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.
- moai_adk/cli/commands/init.py +14 -2
- moai_adk/cli/commands/update.py +229 -60
- moai_adk/core/config/migration.py +1 -1
- moai_adk/core/issue_creator.py +313 -0
- moai_adk/core/project/detector.py +201 -12
- moai_adk/core/project/initializer.py +62 -1
- moai_adk/core/project/phase_executor.py +48 -6
- moai_adk/core/tags/__init__.py +86 -0
- moai_adk/core/tags/ci_validator.py +463 -0
- moai_adk/core/tags/cli.py +283 -0
- moai_adk/core/tags/generator.py +109 -0
- moai_adk/core/tags/inserter.py +99 -0
- moai_adk/core/tags/mapper.py +126 -0
- moai_adk/core/tags/parser.py +76 -0
- moai_adk/core/tags/pre_commit_validator.py +393 -0
- moai_adk/core/tags/reporter.py +956 -0
- moai_adk/core/tags/tags.py +149 -0
- moai_adk/core/tags/validator.py +897 -0
- moai_adk/core/template_engine.py +268 -0
- moai_adk/templates/.claude/agents/alfred/backend-expert.md +319 -0
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +25 -2
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +24 -12
- moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +20 -13
- moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
- moai_adk/templates/.claude/agents/alfred/git-manager.md +47 -16
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +95 -15
- moai_adk/templates/.claude/agents/alfred/project-manager.md +78 -12
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +28 -5
- moai_adk/templates/.claude/agents/alfred/skill-factory.md +30 -2
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +133 -13
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +104 -8
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +133 -16
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +27 -4
- moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +571 -0
- moai_adk/templates/.claude/commands/alfred/0-project.md +466 -125
- moai_adk/templates/.claude/commands/alfred/1-plan.md +208 -71
- moai_adk/templates/.claude/commands/alfred/2-run.md +276 -55
- moai_adk/templates/.claude/commands/alfred/3-sync.md +439 -53
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +149 -0
- moai_adk/templates/.claude/hooks/alfred/core/project.py +361 -29
- moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
- moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
- moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +14 -6
- moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +94 -0
- moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +100 -0
- moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +94 -0
- moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +94 -0
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/__init__.py +2 -2
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/checkpoint.py +3 -3
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/context.py +5 -5
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +749 -0
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/tags.py +55 -23
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py +21 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +154 -0
- moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/session.py +28 -15
- moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/tool.py +3 -6
- moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/user.py +19 -0
- moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +112 -0
- moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
- moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
- moai_adk/templates/.claude/settings.json +5 -5
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
- moai_adk/templates/{.moai/memory/CLAUDE-AGENTS-GUIDE.md → .claude/skills/moai-alfred-agent-guide/reference.md} +34 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +56 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/SKILL.md +74 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/reference.md +269 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/reference.md +150 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/SKILL.md +198 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/examples.md +431 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/reference.md +141 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
- moai_adk/templates/.claude/skills/moai-alfred-reporting/SKILL.md +273 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/README.md +137 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/SKILL.md +219 -0
- moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples/validate-spec.sh +3 -3
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples.md +541 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/reference.md +622 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/SKILL.md +115 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/reference.md +348 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
- moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/examples.md +4 -0
- moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL.md +3 -3
- moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
- moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
- moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +17 -13
- moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +9 -6
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +15 -12
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +14 -12
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +14 -11
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +10 -8
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +15 -12
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +13 -11
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +16 -10
- moai_adk/templates/.claude/skills/moai-project-documentation.md +622 -0
- moai_adk/templates/.git-hooks/pre-push +143 -0
- moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
- moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
- moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/moai-gitflow.yml +166 -3
- moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +188 -0
- moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
- moai_adk/templates/.github/workflows/release.yml +118 -0
- moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +206 -35
- moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/tag-report.yml +269 -0
- moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
- moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
- moai_adk/templates/.moai/config.json +21 -2
- moai_adk/templates/CLAUDE.md +972 -78
- moai_adk/templates/workflows/go-tag-validation.yml +30 -0
- moai_adk/templates/workflows/javascript-tag-validation.yml +41 -0
- moai_adk/templates/workflows/python-tag-validation.yml +42 -0
- moai_adk/templates/workflows/typescript-tag-validation.yml +31 -0
- moai_adk/utils/banner.py +5 -5
- {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/METADATA +1518 -161
- {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/RECORD +183 -100
- 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/handlers/notification.py +0 -25
- 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-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.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/.moai/memory/DEVELOPMENT-GUIDE.md +0 -344
- 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/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/templates/{.moai/memory/CONFIG-SCHEMA.md → .claude/skills/moai-alfred-config-schema/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/CLAUDE-PRACTICES.md → .claude/skills/moai-alfred-practices/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/CLAUDE-RULES.md → .claude/skills/moai-alfred-rules/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/SKILLS-DESCRIPTION-POLICY.md → .claude/skills/moai-cc-skill-descriptions/reference.md} +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/CHECKLIST.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/EXAMPLES.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/INTERACTIVE-DISCOVERY.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/METADATA.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PARALLEL-ANALYSIS-REPORT.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PYTHON-VERSION-MATRIX.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-FACTORY-WORKFLOW.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-UPDATE-ADVISOR.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STEP-BY-STEP-GUIDE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STRUCTURE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/WEB-RESEARCH.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/reference.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/generate-structure.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/validate-skill.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/SKILL_TEMPLATE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/examples-template.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/reference-template.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/scripts-template.sh +0 -0
- {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/WHEEL +0 -0
- {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/entry_points.txt +0 -0
- {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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
340
|
-
|
|
413
|
+
Returns version check settings with sensible defaults.
|
|
414
|
+
Supports frequency-based cache TTL configuration.
|
|
341
415
|
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
-
|
|
354
|
-
-
|
|
355
|
-
-
|
|
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
|
-
|
|
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":
|
|
658
|
+
"current": current_version,
|
|
363
659
|
"latest": "unknown",
|
|
364
660
|
"update_available": False,
|
|
365
|
-
"upgrade_command": ""
|
|
661
|
+
"upgrade_command": "",
|
|
366
662
|
}
|
|
367
663
|
|
|
368
|
-
#
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
result
|
|
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
|
-
#
|
|
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
|
-
|
|
383
|
-
result["latest"] =
|
|
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
|
-
#
|
|
386
|
-
|
|
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
|
]
|