moai-adk 0.8.0__py3-none-any.whl → 0.15.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.

Potentially problematic release.


This version of moai-adk might be problematic. Click here for more details.

Files changed (207) hide show
  1. moai_adk/cli/commands/init.py +14 -2
  2. moai_adk/cli/commands/update.py +229 -60
  3. moai_adk/core/config/migration.py +1 -1
  4. moai_adk/core/issue_creator.py +313 -0
  5. moai_adk/core/project/detector.py +201 -12
  6. moai_adk/core/project/initializer.py +62 -1
  7. moai_adk/core/project/phase_executor.py +48 -6
  8. moai_adk/core/tags/__init__.py +86 -0
  9. moai_adk/core/tags/ci_validator.py +463 -0
  10. moai_adk/core/tags/cli.py +283 -0
  11. moai_adk/core/tags/generator.py +109 -0
  12. moai_adk/core/tags/inserter.py +99 -0
  13. moai_adk/core/tags/mapper.py +126 -0
  14. moai_adk/core/tags/parser.py +76 -0
  15. moai_adk/core/tags/pre_commit_validator.py +393 -0
  16. moai_adk/core/tags/reporter.py +956 -0
  17. moai_adk/core/tags/tags.py +149 -0
  18. moai_adk/core/tags/validator.py +897 -0
  19. moai_adk/core/template_engine.py +268 -0
  20. moai_adk/templates/.claude/agents/alfred/backend-expert.md +319 -0
  21. moai_adk/templates/.claude/agents/alfred/cc-manager.md +25 -2
  22. moai_adk/templates/.claude/agents/alfred/debug-helper.md +24 -12
  23. moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
  24. moai_adk/templates/.claude/agents/alfred/doc-syncer.md +20 -13
  25. moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
  26. moai_adk/templates/.claude/agents/alfred/git-manager.md +47 -16
  27. moai_adk/templates/.claude/agents/alfred/implementation-planner.md +95 -15
  28. moai_adk/templates/.claude/agents/alfred/project-manager.md +78 -12
  29. moai_adk/templates/.claude/agents/alfred/quality-gate.md +28 -5
  30. moai_adk/templates/.claude/agents/alfred/skill-factory.md +30 -2
  31. moai_adk/templates/.claude/agents/alfred/spec-builder.md +133 -13
  32. moai_adk/templates/.claude/agents/alfred/tag-agent.md +104 -8
  33. moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +133 -16
  34. moai_adk/templates/.claude/agents/alfred/trust-checker.md +27 -4
  35. moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +571 -0
  36. moai_adk/templates/.claude/commands/alfred/0-project.md +466 -125
  37. moai_adk/templates/.claude/commands/alfred/1-plan.md +208 -71
  38. moai_adk/templates/.claude/commands/alfred/2-run.md +276 -55
  39. moai_adk/templates/.claude/commands/alfred/3-sync.md +439 -53
  40. moai_adk/templates/.claude/commands/alfred/9-feedback.md +149 -0
  41. moai_adk/templates/.claude/hooks/alfred/core/project.py +361 -29
  42. moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
  43. moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
  44. moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
  45. moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +14 -6
  46. moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +94 -0
  47. moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +100 -0
  48. moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +94 -0
  49. moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +94 -0
  50. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/__init__.py +2 -2
  51. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/checkpoint.py +3 -3
  52. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/context.py +5 -5
  53. moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +749 -0
  54. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/tags.py +55 -23
  55. moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
  56. moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py +21 -0
  57. moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +154 -0
  58. moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/session.py +28 -15
  59. moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/tool.py +3 -6
  60. moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/user.py +19 -0
  61. moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +112 -0
  62. moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
  63. moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
  64. moai_adk/templates/.claude/settings.json +5 -5
  65. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
  66. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
  67. moai_adk/templates/{.moai/memory/CLAUDE-AGENTS-GUIDE.md → .claude/skills/moai-alfred-agent-guide/reference.md} +34 -0
  68. moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +56 -0
  69. moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
  70. moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
  71. moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
  72. moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
  73. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
  74. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
  75. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
  76. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
  77. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
  78. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
  79. moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/SKILL.md +74 -0
  80. moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/examples.md +4 -0
  81. moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/reference.md +269 -0
  82. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +19 -0
  83. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
  84. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/reference.md +150 -0
  85. moai_adk/templates/.claude/skills/moai-alfred-persona-roles/SKILL.md +198 -0
  86. moai_adk/templates/.claude/skills/moai-alfred-persona-roles/examples.md +431 -0
  87. moai_adk/templates/.claude/skills/moai-alfred-persona-roles/reference.md +141 -0
  88. moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
  89. moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
  90. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
  91. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
  92. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
  93. moai_adk/templates/.claude/skills/moai-alfred-reporting/SKILL.md +273 -0
  94. moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
  95. moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
  96. moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +19 -0
  97. moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
  98. moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
  99. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/README.md +137 -0
  100. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/SKILL.md +219 -0
  101. moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples/validate-spec.sh +3 -3
  102. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples.md +541 -0
  103. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/reference.md +622 -0
  104. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/SKILL.md +115 -0
  105. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/examples.md +4 -0
  106. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/reference.md +348 -0
  107. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
  108. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
  109. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
  110. moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
  111. moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/SKILL.md +19 -0
  112. moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/examples.md +4 -0
  113. moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL.md +3 -3
  114. moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
  115. moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
  116. moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
  117. moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +17 -13
  118. moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +9 -6
  119. moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +15 -12
  120. moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +14 -12
  121. moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +14 -11
  122. moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +10 -8
  123. moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +15 -12
  124. moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +13 -11
  125. moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +16 -10
  126. moai_adk/templates/.claude/skills/moai-project-documentation.md +622 -0
  127. moai_adk/templates/.git-hooks/pre-push +143 -0
  128. moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
  129. moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
  130. moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
  131. moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
  132. moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
  133. moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
  134. moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
  135. moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
  136. moai_adk/templates/.github/workflows/moai-gitflow.yml +166 -3
  137. moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
  138. moai_adk/templates/.github/workflows/moai-release-pipeline.yml +188 -0
  139. moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
  140. moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
  141. moai_adk/templates/.github/workflows/release.yml +118 -0
  142. moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
  143. moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
  144. moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
  145. moai_adk/templates/.github/workflows/spec-issue-sync.yml +206 -35
  146. moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
  147. moai_adk/templates/.github/workflows/tag-report.yml +269 -0
  148. moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
  149. moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
  150. moai_adk/templates/.moai/config.json +21 -2
  151. moai_adk/templates/CLAUDE.md +972 -78
  152. moai_adk/templates/workflows/go-tag-validation.yml +30 -0
  153. moai_adk/templates/workflows/javascript-tag-validation.yml +41 -0
  154. moai_adk/templates/workflows/python-tag-validation.yml +42 -0
  155. moai_adk/templates/workflows/typescript-tag-validation.yml +31 -0
  156. moai_adk/utils/banner.py +5 -5
  157. {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/METADATA +1518 -161
  158. {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/RECORD +183 -100
  159. moai_adk/templates/.claude/hooks/alfred/HOOK_SCHEMA_VALIDATION.md +0 -313
  160. moai_adk/templates/.claude/hooks/alfred/README.md +0 -230
  161. moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -174
  162. moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +0 -25
  163. moai_adk/templates/.claude/hooks/alfred/test_hook_output.py +0 -175
  164. moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -640
  165. moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -696
  166. moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -474
  167. moai_adk/templates/.claude/skills/moai-spec-authoring/README.md +0 -137
  168. moai_adk/templates/.claude/skills/moai-spec-authoring/SKILL.md +0 -218
  169. moai_adk/templates/.claude/skills/moai-spec-authoring/examples.md +0 -541
  170. moai_adk/templates/.claude/skills/moai-spec-authoring/reference.md +0 -622
  171. moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +0 -176
  172. moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
  173. moai_adk/templates/.moai/memory/DEVELOPMENT-GUIDE.md +0 -344
  174. moai_adk/templates/.moai/memory/GITFLOW-PROTECTION-POLICY.md +0 -220
  175. moai_adk/templates/.moai/memory/SPEC-METADATA.md +0 -356
  176. moai_adk/templates/.moai/memory/config-schema.md +0 -444
  177. moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
  178. moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
  179. moai_adk/templates/.moai/project/product.md +0 -161
  180. moai_adk/templates/.moai/project/structure.md +0 -156
  181. moai_adk/templates/.moai/project/tech.md +0 -227
  182. moai_adk/templates/__init__.py +0 -2
  183. /moai_adk/templates/{.moai/memory/CONFIG-SCHEMA.md → .claude/skills/moai-alfred-config-schema/reference.md} +0 -0
  184. /moai_adk/templates/{.moai/memory/CLAUDE-PRACTICES.md → .claude/skills/moai-alfred-practices/reference.md} +0 -0
  185. /moai_adk/templates/{.moai/memory/CLAUDE-RULES.md → .claude/skills/moai-alfred-rules/reference.md} +0 -0
  186. /moai_adk/templates/{.moai/memory/SKILLS-DESCRIPTION-POLICY.md → .claude/skills/moai-cc-skill-descriptions/reference.md} +0 -0
  187. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/CHECKLIST.md +0 -0
  188. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/EXAMPLES.md +0 -0
  189. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/INTERACTIVE-DISCOVERY.md +0 -0
  190. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/METADATA.md +0 -0
  191. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PARALLEL-ANALYSIS-REPORT.md +0 -0
  192. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PYTHON-VERSION-MATRIX.md +0 -0
  193. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-FACTORY-WORKFLOW.md +0 -0
  194. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-UPDATE-ADVISOR.md +0 -0
  195. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STEP-BY-STEP-GUIDE.md +0 -0
  196. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STRUCTURE.md +0 -0
  197. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/WEB-RESEARCH.md +0 -0
  198. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/reference.md +0 -0
  199. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/generate-structure.sh +0 -0
  200. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/validate-skill.sh +0 -0
  201. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/SKILL_TEMPLATE.md +0 -0
  202. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/examples-template.md +0 -0
  203. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/reference-template.md +0 -0
  204. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/scripts-template.sh +0 -0
  205. {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/WHEEL +0 -0
  206. {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/entry_points.txt +0 -0
  207. {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: alfred:9-feedback
3
+ description: "Create GitHub issues interactively"
4
+ allowed-tools:
5
+ - Bash(gh:*)
6
+ - Task
7
+ - AskUserQuestion
8
+ ---
9
+
10
+ # 🎯 MoAI-ADK Alfred 9-Feedback: Interactive GitHub Issue Creation
11
+
12
+ > **Purpose**: Create GitHub Issues through an interactive multi-step dialog. Simple command → guided questions → automatic issue creation.
13
+
14
+ ## 📋 Command Purpose
15
+
16
+ Enable developers to instantly report bugs, request features, suggest improvements, and ask questions through conversational dialogs. No command arguments needed—just run `/alfred:9-feedback` and answer questions.
17
+
18
+ **Command Format**:
19
+ ```bash
20
+ /alfred:9-feedback
21
+ ```
22
+
23
+ That's it! Alfred guides you through the rest.
24
+
25
+ ---
26
+
27
+ ## 🚀 Interactive Execution Flow
28
+
29
+ ### Step 1: Start Command
30
+ ```bash
31
+ /alfred:9-feedback
32
+ ```
33
+
34
+ Alfred responds and proceeds to Step 2.
35
+
36
+ ---
37
+
38
+ ### Step 2: Select Issue Type (AskUserQuestion)
39
+
40
+ **Question**: "What type of issue do you want to create?"
41
+
42
+ **Options**:
43
+ ```
44
+ [ ] 🐛 Bug Report - Something isn't working
45
+ [ ] ✨ Feature Request - Suggest new functionality
46
+ [ ] ⚡ Improvement - Enhance existing features
47
+ [ ] ❓ Question/Discussion - Ask the team
48
+ ```
49
+
50
+ **User Selection**: Selects one (e.g., 🐛 Bug Report)
51
+
52
+ ---
53
+
54
+ ### Step 3: Enter Issue Title (AskUserQuestion)
55
+
56
+ **Question**: "What is the issue title? (Be concise)"
57
+
58
+ **Example Input**:
59
+ ```
60
+ Login button on homepage not responding to clicks
61
+ ```
62
+
63
+ ---
64
+
65
+ ### Step 4: Enter Description (AskUserQuestion)
66
+
67
+ **Question**: "Provide a detailed description (optional—press Enter to skip)"
68
+
69
+ **Example Input**:
70
+ ```
71
+ When I click the login button on the homepage, nothing happens.
72
+ Tested on Chrome 120.0 on macOS 14.2.
73
+ Expected: Login modal should appear
74
+ Actual: No response
75
+ ```
76
+
77
+ Or just press Enter to skip.
78
+
79
+ ---
80
+
81
+ ### Step 5: Select Priority (AskUserQuestion)
82
+
83
+ **Question**: "What's the priority level?"
84
+
85
+ **Options**:
86
+ ```
87
+ [ ] 🔴 Critical - System down, data loss, security breach
88
+ [ ] 🟠 High - Major feature broken, significant impact
89
+ [✓] 🟡 Medium - Normal priority (default)
90
+ [ ] 🟢 Low - Minor issues, nice-to-have
91
+ ```
92
+
93
+ **User Selection**: Selects priority (e.g., 🟠 High)
94
+
95
+ ---
96
+
97
+ ### Step 6: Create Issue (Automatic)
98
+
99
+ Alfred automatically:
100
+ 1. Formats title with emoji: "🐛 [BUG] Login button not responding..."
101
+ 2. Prepares body with user description + metadata
102
+ 3. Assigns labels: bug, reported, priority-high
103
+ 4. Executes: `gh issue create --title ... --body ... --label ...`
104
+ 5. Parses issue number from response
105
+
106
+ **Success Output**:
107
+ ```
108
+ ✅ GitHub Issue #234 created successfully!
109
+
110
+ 📋 Title: 🐛 [BUG] Login button not responding to clicks
111
+ 🔴 Priority: High
112
+ 🏷️ Labels: bug, reported, priority-high
113
+ 🔗 URL: https://github.com/owner/repo/issues/234
114
+
115
+ 💡 Next: Reference this issue in your commits or link to a SPEC document
116
+ ```
117
+
118
+ ---
119
+
120
+ ## ⚠️ Important Rules
121
+
122
+ ### ✅ What to Do
123
+
124
+ - ✅ Ask all 4 questions in sequence (type → title → description → priority)
125
+ - ✅ Preserve exact user wording in title and description
126
+ - ✅ Use AskUserQuestion for all user inputs
127
+ - ✅ Allow skipping description (optional field)
128
+ - ✅ Show issue URL after creation
129
+
130
+ ### ❌ What NOT to Do
131
+
132
+ - ❌ Accept command arguments (`/alfred:9-feedback --bug` is wrong—just use `/alfred:9-feedback`)
133
+ - ❌ Skip questions or change order
134
+ - ❌ Rephrase user's input
135
+ - ❌ Create issues without labels
136
+
137
+ ---
138
+
139
+ ## 💡 Key Benefits
140
+
141
+ 1. **🚀 No Arguments Needed**: Just `/alfred:9-feedback`
142
+ 2. **💬 Conversational**: Intuitive step-by-step dialog
143
+ 3. **🏷️ Auto-labeled**: Labels applied automatically
144
+ 4. **🔗 Team Visible**: Issues immediately visible
145
+ 5. **⏱️ Fast**: Create issues in 30 seconds
146
+
147
+ ---
148
+
149
+ **Supported since**: MoAI-ADK v0.7.0+
@@ -1,19 +1,81 @@
1
1
  #!/usr/bin/env python3
2
+ # @CODE:OFFLINE-001 | SPEC: SPEC-OFFLINE-SUPPORT-001 | TEST: tests/unit/test_network_detection.py
2
3
  """Project metadata utilities
3
4
 
4
5
  Project information inquiry (language, Git, SPEC progress, etc.)
6
+
7
+ Network detection and caching support:
8
+ - is_network_available(): Check network connectivity with timeout
9
+ - get_package_version_info(): Get package version with offline cache support
5
10
  """
6
11
 
7
12
  import json
8
13
  import signal
14
+ import socket
9
15
  import subprocess
10
16
  from contextlib import contextmanager
11
17
  from pathlib import Path
12
18
  from typing import Any
13
19
 
20
+ # Cache directory for version check results
21
+ CACHE_DIR_NAME = ".moai/cache"
22
+
23
+
24
+ def find_project_root(start_path: str | Path = ".") -> Path:
25
+ """Find MoAI-ADK project root by searching upward for .moai/config.json
26
+
27
+ Traverses up the directory tree until it finds .moai/config.json or CLAUDE.md,
28
+ which indicates the project root. This ensures cache and other files are
29
+ always created in the correct location, regardless of where hooks execute.
30
+
31
+ Args:
32
+ start_path: Starting directory (default: current directory)
33
+
34
+ Returns:
35
+ Project root Path. If not found, returns start_path as absolute path.
36
+
37
+ Examples:
38
+ >>> find_project_root(".")
39
+ Path("/Users/user/my-project")
40
+ >>> find_project_root(".claude/hooks/alfred")
41
+ Path("/Users/user/my-project") # Found root 3 levels up
42
+
43
+ Notes:
44
+ - Searches for .moai/config.json first (most reliable)
45
+ - Falls back to CLAUDE.md if config.json not found
46
+ - Max depth: 10 levels up (prevent infinite loop)
47
+ - Returns absolute path for consistency
48
+
49
+ TDD History:
50
+ - RED: 4 test scenarios (root, nested, not found, symlinks)
51
+ - GREEN: Minimal upward search with .moai/config.json detection
52
+ - REFACTOR: Add CLAUDE.md fallback, max depth limit, absolute path return
53
+ """
54
+ current = Path(start_path).resolve()
55
+ max_depth = 10 # Prevent infinite loop
56
+
57
+ for _ in range(max_depth):
58
+ # Check for .moai/config.json (primary indicator)
59
+ if (current / ".moai" / "config.json").exists():
60
+ return current
61
+
62
+ # Check for CLAUDE.md (secondary indicator)
63
+ if (current / "CLAUDE.md").exists():
64
+ return current
65
+
66
+ # Move up one level
67
+ parent = current.parent
68
+ if parent == current: # Reached filesystem root
69
+ break
70
+ current = parent
71
+
72
+ # Not found - return start_path as absolute
73
+ return Path(start_path).resolve()
74
+
14
75
 
15
76
  class TimeoutError(Exception):
16
77
  """Signal-based timeout exception"""
78
+
17
79
  pass
18
80
 
19
81
 
@@ -30,6 +92,7 @@ def timeout_handler(seconds: int):
30
92
  Raises:
31
93
  TimeoutError: If operation exceeds timeout
32
94
  """
95
+
33
96
  def _handle_timeout(signum, frame):
34
97
  raise TimeoutError(f"Operation timed out after {seconds} seconds")
35
98
 
@@ -242,7 +305,7 @@ def count_specs(cwd: str) -> dict[str, int]:
242
305
  Counts the number of SPECs with status: completed.
243
306
 
244
307
  Args:
245
- cwd: Project root directory path
308
+ cwd: Project root directory path (or any subdirectory, will search upward)
246
309
 
247
310
  Returns:
248
311
  SPEC progress dictionary. Includes the following keys:
@@ -260,15 +323,19 @@ def count_specs(cwd: str) -> dict[str, int]:
260
323
 
261
324
  Notes:
262
325
  - SPEC File Location: .moai/specs/SPEC-{ID}/spec.md
263
- - Completion condition: Include status: completed in YAML front matter
326
+ - Completion condition: Include "status: completed" in YAML front matter
264
327
  - If parsing fails, the SPEC is considered incomplete.
328
+ - Automatically finds project root to locate .moai/specs/
265
329
 
266
330
  TDD History:
267
331
  - RED: 5 items scenario test (0/0, 2/5, 5/5, no directory, parsing error)
268
332
  - GREEN: SPEC search with Path.iterdir(), YAML parsing implementation
269
333
  - REFACTOR: Strengthened exception handling, improved percentage calculation safety
334
+ - UPDATE: Add project root detection for consistent path resolution
270
335
  """
271
- specs_dir = Path(cwd) / ".moai" / "specs"
336
+ # Find project root to ensure we read specs from correct location
337
+ project_root = find_project_root(cwd)
338
+ specs_dir = project_root / ".moai" / "specs"
272
339
 
273
340
  if not specs_dir.exists():
274
341
  return {"completed": 0, "total": 0, "percentage": 0}
@@ -312,7 +379,7 @@ def get_project_language(cwd: str) -> str:
312
379
  """Determine the primary project language (prefers config.json).
313
380
 
314
381
  Args:
315
- cwd: Project root directory.
382
+ cwd: Project root directory (or any subdirectory, will search upward).
316
383
 
317
384
  Returns:
318
385
  Language string in lower-case.
@@ -320,8 +387,11 @@ def get_project_language(cwd: str) -> str:
320
387
  Notes:
321
388
  - Reads ``.moai/config.json`` first for a quick answer.
322
389
  - Falls back to ``detect_language`` if configuration is missing.
390
+ - Automatically finds project root to locate .moai/config.json
323
391
  """
324
- config_path = Path(cwd) / ".moai" / "config.json"
392
+ # Find project root to ensure we read config from correct location
393
+ project_root = find_project_root(cwd)
394
+ config_path = project_root / ".moai" / "config.json"
325
395
  if config_path.exists():
326
396
  try:
327
397
  config = json.loads(config_path.read_text())
@@ -332,15 +402,177 @@ def get_project_language(cwd: str) -> str:
332
402
  # Fall back to detection on parse errors
333
403
  pass
334
404
 
335
- # Fall back to the original language detection routine
336
- return detect_language(cwd)
405
+ # Fall back to the original language detection routine (use project root)
406
+ return detect_language(str(project_root))
407
+
337
408
 
409
+ # @CODE:CONFIG-INTEGRATION-001
410
+ def get_version_check_config(cwd: str) -> dict[str, Any]:
411
+ """Read version check configuration from .moai/config.json
338
412
 
339
- def get_package_version_info() -> dict[str, Any]:
340
- """Check MoAI-ADK current and latest version from PyPI
413
+ Returns version check settings with sensible defaults.
414
+ Supports frequency-based cache TTL configuration.
341
415
 
342
- Compares the installed version with the latest version available on PyPI.
343
- Returns version information for SessionStart hook to display update recommendations.
416
+ Args:
417
+ cwd: Project root directory path
418
+
419
+ Returns:
420
+ dict with keys:
421
+ - "enabled": Boolean (default: True)
422
+ - "frequency": "always" | "daily" | "weekly" | "never" (default: "daily")
423
+ - "cache_ttl_hours": TTL in hours based on frequency
424
+
425
+ Frequency to TTL mapping:
426
+ - "always": 0 hours (no caching)
427
+ - "daily": 24 hours
428
+ - "weekly": 168 hours (7 days)
429
+ - "never": infinity (never check)
430
+
431
+ TDD History:
432
+ - RED: 8 test scenarios (defaults, custom, disabled, TTL, etc.)
433
+ - GREEN: Minimal config reading with defaults
434
+ - REFACTOR: Add validation and error handling
435
+ """
436
+ # TTL mapping by frequency
437
+ ttl_by_frequency = {"always": 0, "daily": 24, "weekly": 168, "never": float("inf")}
438
+
439
+ # Default configuration
440
+ defaults = {"enabled": True, "frequency": "daily", "cache_ttl_hours": 24}
441
+
442
+ # Find project root to ensure we read config from correct location
443
+ project_root = find_project_root(cwd)
444
+ config_path = project_root / ".moai" / "config.json"
445
+ if not config_path.exists():
446
+ return defaults
447
+
448
+ try:
449
+ config = json.loads(config_path.read_text())
450
+
451
+ # Extract moai.version_check section
452
+ moai_config = config.get("moai", {})
453
+ version_check_config = moai_config.get("version_check", {})
454
+
455
+ # Read enabled flag (default: True)
456
+ enabled = version_check_config.get("enabled", defaults["enabled"])
457
+
458
+ # Read frequency (default: "daily")
459
+ frequency = moai_config.get("update_check_frequency", defaults["frequency"])
460
+
461
+ # Validate frequency
462
+ if frequency not in ttl_by_frequency:
463
+ frequency = defaults["frequency"]
464
+
465
+ # Calculate TTL from frequency
466
+ cache_ttl_hours = ttl_by_frequency[frequency]
467
+
468
+ # Allow explicit cache_ttl_hours override
469
+ if "cache_ttl_hours" in version_check_config:
470
+ cache_ttl_hours = version_check_config["cache_ttl_hours"]
471
+
472
+ return {"enabled": enabled, "frequency": frequency, "cache_ttl_hours": cache_ttl_hours}
473
+
474
+ except (OSError, json.JSONDecodeError, KeyError):
475
+ # Config read or parse error - return defaults
476
+ return defaults
477
+
478
+
479
+ # @CODE:NETWORK-DETECT-001
480
+ def is_network_available(timeout_seconds: float = 0.1) -> bool:
481
+ """Quick network availability check using socket.
482
+
483
+ Does NOT check PyPI specifically, just basic connectivity.
484
+ Returns immediately on success (< 50ms typically).
485
+ Returns False on any error without raising exceptions.
486
+
487
+ Args:
488
+ timeout_seconds: Socket timeout in seconds (default 0.1s)
489
+
490
+ Returns:
491
+ True if network appears available, False otherwise
492
+
493
+ Examples:
494
+ >>> is_network_available()
495
+ True # Network is available
496
+ >>> is_network_available(timeout_seconds=0.001)
497
+ False # Timeout too short, returns False
498
+
499
+ TDD History:
500
+ - RED: 3 test scenarios (success, failure, timeout)
501
+ - GREEN: Minimal socket.create_connection implementation
502
+ - REFACTOR: Add error handling for all exception types
503
+ """
504
+ try:
505
+ # Try connecting to Google's public DNS server (8.8.8.8:53)
506
+ # This is a reliable host that's typically reachable
507
+ connection = socket.create_connection(("8.8.8.8", 53), timeout=timeout_seconds)
508
+ connection.close()
509
+ return True
510
+ except (socket.timeout, OSError, Exception):
511
+ # Any connection error means network is unavailable
512
+ # This includes: timeout, connection refused, network unreachable, etc.
513
+ return False
514
+
515
+
516
+ # @CODE:VERSION-DETECT-MAJOR-001
517
+ def is_major_version_change(current: str, latest: str) -> bool:
518
+ """Detect if version change is a major version bump.
519
+
520
+ A major version change is when the first (major) component increases:
521
+ - 0.8.1 → 1.0.0: True (0 → 1)
522
+ - 1.2.3 → 2.0.0: True (1 → 2)
523
+ - 0.8.1 → 0.9.0: False (0 → 0, minor changed)
524
+ - 1.2.3 → 1.3.0: False (1 → 1)
525
+
526
+ Args:
527
+ current: Current version string (e.g., "0.8.1")
528
+ latest: Latest version string (e.g., "1.0.0")
529
+
530
+ Returns:
531
+ True if major version increased, False otherwise
532
+
533
+ Examples:
534
+ >>> is_major_version_change("0.8.1", "1.0.0")
535
+ True
536
+ >>> is_major_version_change("0.8.1", "0.9.0")
537
+ False
538
+ >>> is_major_version_change("dev", "1.0.0")
539
+ False # Invalid versions return False
540
+
541
+ TDD History:
542
+ - RED: 4 test scenarios (0→1, 1→2, minor, invalid)
543
+ - GREEN: Minimal version parsing and comparison
544
+ - REFACTOR: Improve error handling for invalid versions
545
+ """
546
+ try:
547
+ # Parse version strings into integer components
548
+ current_parts = [int(x) for x in current.split(".")]
549
+ latest_parts = [int(x) for x in latest.split(".")]
550
+
551
+ # Compare major version (first component)
552
+ if len(current_parts) >= 1 and len(latest_parts) >= 1:
553
+ return latest_parts[0] > current_parts[0]
554
+
555
+ # If parsing succeeds but empty, no major change
556
+ return False
557
+
558
+ except (ValueError, AttributeError, IndexError):
559
+ # Invalid version format - return False (no exception)
560
+ return False
561
+
562
+
563
+ # @CODE:VERSION-CACHE-INTEGRATION-001
564
+ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
565
+ """Check MoAI-ADK current and latest version with caching and offline support.
566
+
567
+ Execution flow:
568
+ 1. Try to load from cache (< 50ms)
569
+ 2. If cache invalid, check network
570
+ 3. If network available, query PyPI
571
+ 4. If network unavailable, return current version only
572
+ 5. Save result to cache for next time
573
+
574
+ Args:
575
+ cwd: Project root directory (for cache location)
344
576
 
345
577
  Returns:
346
578
  dict with keys:
@@ -348,44 +580,128 @@ def get_package_version_info() -> dict[str, Any]:
348
580
  - "latest": Latest version available on PyPI
349
581
  - "update_available": Boolean indicating if update is available
350
582
  - "upgrade_command": Recommended upgrade command (if update available)
583
+ - "release_notes_url": URL to release notes (Phase 3)
584
+ - "is_major_update": Boolean indicating major version change (Phase 3)
351
585
 
352
586
  Note:
353
- - Has 1-second timeout to avoid blocking SessionStart
354
- - Returns graceful fallback if PyPI check fails
355
- - Handles version parsing gracefully
587
+ - Cache hit (< 24 hours): Returns in ~20ms, no network access
588
+ - Cache miss + online: Query PyPI (1s timeout), cache result
589
+ - Cache miss + offline: Return current version only (~100ms)
590
+ - Offline + cached: Return from cache in ~20ms
591
+
592
+ TDD History:
593
+ - RED: 5 test scenarios (network detection, cache integration, offline mode)
594
+ - GREEN: Integrate VersionCache with network detection
595
+ - REFACTOR: Extract cache directory constant, improve error handling
596
+ - Phase 3: Add release_notes_url and is_major_update fields (@CODE:VERSION-INTEGRATE-FIELDS-001)
356
597
  """
357
- from importlib.metadata import version, PackageNotFoundError
358
- import urllib.request
598
+ import importlib.util
359
599
  import urllib.error
600
+ import urllib.request
601
+ from importlib.metadata import PackageNotFoundError, version
360
602
 
603
+ # Import VersionCache from the same directory (using dynamic import for testing compatibility)
604
+ try:
605
+ version_cache_path = Path(__file__).parent / "version_cache.py"
606
+ spec = importlib.util.spec_from_file_location("version_cache", version_cache_path)
607
+ if spec and spec.loader:
608
+ version_cache_module = importlib.util.module_from_spec(spec)
609
+ spec.loader.exec_module(version_cache_module)
610
+ version_cache_class = version_cache_module.VersionCache
611
+ else:
612
+ # Skip caching if module can't be loaded
613
+ version_cache_class = None
614
+ except (ImportError, OSError):
615
+ # Graceful degradation: skip caching on import errors
616
+ version_cache_class = None
617
+
618
+ # 1. Find project root (ensure cache is always in correct location)
619
+ # This prevents creating .moai/cache in wrong locations when hooks run
620
+ # from subdirectories like .claude/hooks/alfred/
621
+ project_root = find_project_root(cwd)
622
+
623
+ # 2. Initialize cache (skip if VersionCache couldn't be imported)
624
+ cache_dir = project_root / CACHE_DIR_NAME
625
+ version_cache = version_cache_class(cache_dir) if version_cache_class else None
626
+
627
+ # 2. Get current installed version first (needed for cache validation)
628
+ current_version = "unknown"
629
+ try:
630
+ current_version = version("moai-adk")
631
+ except PackageNotFoundError:
632
+ current_version = "dev"
633
+ # Dev mode - skip cache and return immediately
634
+ return {
635
+ "current": "dev",
636
+ "latest": "unknown",
637
+ "update_available": False,
638
+ "upgrade_command": "",
639
+ }
640
+
641
+ # 3. Try to load from cache (fast path with version validation)
642
+ if version_cache and version_cache.is_valid():
643
+ cached_info = version_cache.load()
644
+ if cached_info:
645
+ # Only use cache if the cached version matches current installed version
646
+ # This prevents stale cache when package is upgraded locally
647
+ if cached_info.get("current") == current_version:
648
+ # Ensure new fields exist for backward compatibility
649
+ if "release_notes_url" not in cached_info:
650
+ # Add missing fields to old cached data
651
+ cached_info.setdefault("release_notes_url", None)
652
+ cached_info.setdefault("is_major_update", False)
653
+ return cached_info
654
+ # else: cache is stale (version changed), fall through to re-check
655
+
656
+ # 4. Cache miss or stale - need to query PyPI
361
657
  result = {
362
- "current": "unknown",
658
+ "current": current_version,
363
659
  "latest": "unknown",
364
660
  "update_available": False,
365
- "upgrade_command": ""
661
+ "upgrade_command": "",
366
662
  }
367
663
 
368
- # Get current version
369
- try:
370
- result["current"] = version("moai-adk")
371
- except PackageNotFoundError:
372
- result["current"] = "dev"
664
+ # 5. Check if version check is enabled in config
665
+ config = get_version_check_config(cwd)
666
+ if not config["enabled"]:
667
+ # Version check disabled - return only current version
668
+ return result
669
+
670
+ # 6. Check network before PyPI query
671
+ if not is_network_available():
672
+ # Offline mode - return current version only
373
673
  return result
374
674
 
375
- # Get latest version from PyPI (with 1-second timeout)
675
+ # 7. Network available - query PyPI
676
+ pypi_data = None
376
677
  try:
377
678
  with timeout_handler(1):
378
679
  url = "https://pypi.org/pypi/moai-adk/json"
379
680
  headers = {"Accept": "application/json"}
380
681
  req = urllib.request.Request(url, headers=headers)
381
682
  with urllib.request.urlopen(req, timeout=0.8) as response:
382
- data = json.load(response)
383
- result["latest"] = data.get("info", {}).get("version", "unknown")
683
+ pypi_data = json.load(response)
684
+ result["latest"] = pypi_data.get("info", {}).get("version", "unknown")
685
+
686
+ # Extract release notes URL from project_urls
687
+ try:
688
+ project_urls = pypi_data.get("info", {}).get("project_urls", {})
689
+ release_url = project_urls.get("Changelog", "")
690
+ if not release_url:
691
+ # Fallback to GitHub releases URL pattern
692
+ release_url = (
693
+ f"https://github.com/modu-ai/moai-adk/releases/tag/v{result['latest']}"
694
+ )
695
+ result["release_notes_url"] = release_url
696
+ except (KeyError, AttributeError, TypeError):
697
+ result["release_notes_url"] = None
698
+
384
699
  except (urllib.error.URLError, TimeoutError, Exception):
385
- # Network error or timeout - return with unknown latest version
386
- return result
700
+ # PyPI query failed - return current version
701
+ result["release_notes_url"] = None
702
+ pass
387
703
 
388
- # Compare versions (simple comparison)
704
+ # 7. Compare versions (simple comparison)
389
705
  if result["current"] != "unknown" and result["latest"] != "unknown":
390
706
  try:
391
707
  # Parse versions for comparison
@@ -400,17 +716,33 @@ def get_package_version_info() -> dict[str, Any]:
400
716
  if latest_parts > current_parts:
401
717
  result["update_available"] = True
402
718
  result["upgrade_command"] = f"uv pip install --upgrade moai-adk>={result['latest']}"
719
+
720
+ # Detect major version change
721
+ result["is_major_update"] = is_major_version_change(
722
+ result["current"], result["latest"]
723
+ )
724
+ else:
725
+ result["is_major_update"] = False
403
726
  except (ValueError, AttributeError):
404
727
  # Version parsing failed - skip comparison
728
+ result["is_major_update"] = False
405
729
  pass
406
730
 
731
+ # 8. Save result to cache (if caching is available)
732
+ if version_cache:
733
+ version_cache.save(result)
734
+
407
735
  return result
408
736
 
409
737
 
410
738
  __all__ = [
739
+ "find_project_root",
411
740
  "detect_language",
412
741
  "get_git_info",
413
742
  "count_specs",
414
743
  "get_project_language",
744
+ "get_version_check_config",
745
+ "is_network_available",
746
+ "is_major_version_change",
415
747
  "get_package_version_info",
416
748
  ]