Compare commits
51 Commits
develop
...
v2.7.2-beta.6
| Author | SHA1 | Date | |
|---|---|---|---|
| c0a02a453d | |||
| 086429cb49 | |||
| d9fb8f29d5 | |||
| d0b0b3df0d | |||
| 937a60f8bd | |||
| 7a51cbd5e5 | |||
| 26beefe39d | |||
| 8416f3076a | |||
| 217249c8a3 | |||
| 8bb3df917e | |||
| 5106c6e9b2 | |||
| 3ff612742f | |||
| 7f19c2e1f3 | |||
| d98e28305d | |||
| 0b874d4b33 | |||
| 50dd10788a | |||
| f1cc5a09f2 | |||
| b005fa5c86 | |||
| d79f2da411 | |||
| 5ac566e2a2 | |||
| f49d4817b1 | |||
| 1e1d7d9b68 | |||
| e74a3dffd2 | |||
| 6ac8250b5b | |||
| a2cee6941f | |||
| d4cad80a73 | |||
| 596e95137b | |||
| d42041c5b5 | |||
| a3f87540c6 | |||
| f843811292 | |||
| 5e8c53080f | |||
| 348de6dfe7 | |||
| 0f7d6e0530 | |||
| 5ccdb6abc5 | |||
| 6ec8549f63 | |||
| 53527293cd | |||
| 02bef954f3 | |||
| f168bdc3ac | |||
| e3eec68aab | |||
| 407a0bee5e | |||
| 8f766ad16e | |||
| ced2ad479f | |||
| 05f5d3038b | |||
| 6951251b33 | |||
| 30e7536b63 | |||
| 220faf0fb4 | |||
| f96c6301f4 | |||
| ebd8340d82 | |||
| df779530e7 | |||
| 0adaddacaa | |||
| 91f7051dda |
@@ -0,0 +1,65 @@
|
||||
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
|
||||
|
||||
# CodeRabbit Configuration
|
||||
# Documentation: https://docs.coderabbit.ai/reference/configuration
|
||||
|
||||
language: "en-US"
|
||||
|
||||
reviews:
|
||||
# Review profile: "chill" for fewer comments, "assertive" for more thorough feedback
|
||||
profile: "assertive"
|
||||
|
||||
# Generate high-level summary in PR description
|
||||
high_level_summary: true
|
||||
|
||||
# Automatic review settings
|
||||
auto_review:
|
||||
enabled: true
|
||||
auto_incremental_review: true
|
||||
# Target branches for review (in addition to default branch)
|
||||
base_branches:
|
||||
- develop
|
||||
- "release/*"
|
||||
- "hotfix/*"
|
||||
# Skip review for PRs with these title keywords (case-insensitive)
|
||||
ignore_title_keywords:
|
||||
- "[WIP]"
|
||||
- "WIP:"
|
||||
- "DO NOT MERGE"
|
||||
# Don't review draft PRs
|
||||
drafts: false
|
||||
|
||||
# Path filters - exclude generated/vendor files
|
||||
path_filters:
|
||||
- "!**/node_modules/**"
|
||||
- "!**/.venv/**"
|
||||
- "!**/dist/**"
|
||||
- "!**/build/**"
|
||||
- "!**/*.lock"
|
||||
- "!**/package-lock.json"
|
||||
- "!**/*.min.js"
|
||||
- "!**/*.min.css"
|
||||
|
||||
# Path-specific review instructions
|
||||
path_instructions:
|
||||
- path: "apps/backend/**/*.py"
|
||||
instructions: |
|
||||
Focus on Python best practices, type hints, and async patterns.
|
||||
Check for proper error handling and security considerations.
|
||||
Verify compatibility with Python 3.12+.
|
||||
- path: "apps/frontend/**/*.{ts,tsx}"
|
||||
instructions: |
|
||||
Review React patterns and TypeScript type safety.
|
||||
Check for proper state management and component composition.
|
||||
- path: "tests/**"
|
||||
instructions: |
|
||||
Ensure tests are comprehensive and follow pytest conventions.
|
||||
Check for proper mocking and test isolation.
|
||||
|
||||
chat:
|
||||
auto_reply: true
|
||||
|
||||
knowledge_base:
|
||||
opt_out: false
|
||||
learnings:
|
||||
scope: "auto"
|
||||
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: AndyMik90
|
||||
@@ -1,75 +1,27 @@
|
||||
name: Bug Report
|
||||
description: Report a bug or unexpected behavior
|
||||
labels: ["bug", "triage"]
|
||||
name: 🐛 Bug Report
|
||||
description: Something isn't working
|
||||
labels: ["bug", "needs-triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to report a bug! Please fill out the sections below.
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Bug Description
|
||||
description: A clear and concise description of the bug.
|
||||
placeholder: What happened?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: What did you expect to happen?
|
||||
placeholder: What should have happened?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: Steps to reproduce the behavior.
|
||||
placeholder: |
|
||||
1. Run command '...'
|
||||
2. Click on '...'
|
||||
3. See error
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Error Messages / Logs
|
||||
description: If applicable, paste any error messages or logs.
|
||||
render: shell
|
||||
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If applicable, add screenshots to help explain the problem.
|
||||
label: Checklist
|
||||
options:
|
||||
- label: I searched existing issues and this hasn't been reported
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: component
|
||||
id: area
|
||||
attributes:
|
||||
label: Component
|
||||
description: Which part of Auto Claude is affected?
|
||||
label: Area
|
||||
options:
|
||||
- Python Backend (auto-claude/)
|
||||
- Electron UI (auto-claude-ui/)
|
||||
- Both
|
||||
- Frontend
|
||||
- Backend
|
||||
- Fullstack
|
||||
- Not sure
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Auto Claude Version
|
||||
description: What version are you running? (check package.json or git tag)
|
||||
placeholder: "v2.0.1"
|
||||
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
@@ -78,26 +30,47 @@ body:
|
||||
- macOS
|
||||
- Windows
|
||||
- Linux
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: python-version
|
||||
id: version
|
||||
attributes:
|
||||
label: Python Version
|
||||
description: Output of `python --version`
|
||||
placeholder: "3.12.0"
|
||||
|
||||
- type: input
|
||||
id: node-version
|
||||
attributes:
|
||||
label: Node.js Version (for UI issues)
|
||||
description: Output of `node --version`
|
||||
placeholder: "20.10.0"
|
||||
label: Version
|
||||
placeholder: "e.g., 2.5.5"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional
|
||||
id: description
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Any other context about the problem.
|
||||
label: What happened?
|
||||
placeholder: Describe the bug clearly and concisely. Include any error messages you encountered.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
placeholder: |
|
||||
1. Run command '...' or click on '...'
|
||||
2. Observe behavior '...'
|
||||
3. See error or unexpected result
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
placeholder: What did you expect to happen instead? Describe the correct behavior.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Logs / Screenshots
|
||||
description: Required for UI bugs. Attach relevant logs, screenshots, or error output.
|
||||
render: shell
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Questions & Discussions
|
||||
- name: 💡 Feature Request
|
||||
url: https://github.com/AndyMik90/Auto-Claude/discussions
|
||||
about: Ask questions and discuss ideas with the community
|
||||
- name: Documentation
|
||||
url: https://github.com/AndyMik90/Auto-Claude#readme
|
||||
about: Check the documentation before opening an issue
|
||||
about: Suggest new features in GitHub Discussions
|
||||
- name: 💬 Discord Community
|
||||
url: https://discord.gg/QhRnz9m5HE
|
||||
about: Questions and discussions - join our Discord!
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
name: 📚 Documentation
|
||||
description: Improvements or additions to documentation
|
||||
labels: ["documentation", "needs-triage", "help wanted"]
|
||||
body:
|
||||
- type: dropdown
|
||||
id: type
|
||||
attributes:
|
||||
label: Type
|
||||
options:
|
||||
- Missing documentation
|
||||
- Incorrect/outdated info
|
||||
- Improvement suggestion
|
||||
- Typo/grammar fix
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: location
|
||||
attributes:
|
||||
label: Location
|
||||
description: Which file or page?
|
||||
placeholder: "e.g., README.md or guides/setup.md"
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: What needs to change?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: contribute
|
||||
attributes:
|
||||
label: Contribution
|
||||
options:
|
||||
- label: I'm willing to submit a PR for this
|
||||
@@ -1,70 +0,0 @@
|
||||
name: Feature Request
|
||||
description: Suggest a new feature or enhancement
|
||||
labels: ["enhancement", "triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for suggesting a feature! Please describe your idea below.
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Problem Statement
|
||||
description: What problem does this feature solve? Is this related to a frustration?
|
||||
placeholder: I'm always frustrated when...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Proposed Solution
|
||||
description: Describe the solution you'd like to see.
|
||||
placeholder: I would like Auto Claude to...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives Considered
|
||||
description: Have you considered any alternative solutions or workarounds?
|
||||
placeholder: I've tried...
|
||||
|
||||
- type: dropdown
|
||||
id: component
|
||||
attributes:
|
||||
label: Component
|
||||
description: Which part of Auto Claude would this affect?
|
||||
options:
|
||||
- Python Backend (auto-claude/)
|
||||
- Electron UI (auto-claude-ui/)
|
||||
- Both
|
||||
- New component
|
||||
- Not sure
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: How important is this feature to you?
|
||||
options:
|
||||
- Nice to have
|
||||
- Important for my workflow
|
||||
- Critical / Blocking my use
|
||||
|
||||
- type: checkboxes
|
||||
id: contribution
|
||||
attributes:
|
||||
label: Contribution
|
||||
description: Would you be willing to help implement this?
|
||||
options:
|
||||
- label: I'm willing to submit a PR for this feature
|
||||
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any other context, mockups, or screenshots about the feature request.
|
||||
@@ -0,0 +1,61 @@
|
||||
name: ❓ Question
|
||||
description: Needs clarification
|
||||
labels: ["question", "needs-triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Before asking:** Check [Discord](https://discord.gg/QhRnz9m5HE) - your question may already be answered there!
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Checklist
|
||||
options:
|
||||
- label: I searched existing issues and Discord for similar questions
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: area
|
||||
attributes:
|
||||
label: Area
|
||||
options:
|
||||
- Setup/Installation
|
||||
- Frontend
|
||||
- Backend
|
||||
- Configuration
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you using?
|
||||
placeholder: "e.g., 2.7.1"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: question
|
||||
attributes:
|
||||
label: Question
|
||||
placeholder: "Describe your question in detail..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Context
|
||||
description: What are you trying to achieve?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: attempts
|
||||
attributes:
|
||||
label: What have you already tried?
|
||||
description: What steps have you taken to resolve this?
|
||||
placeholder: "e.g., I tried reading the docs, searched for..."
|
||||
@@ -1,44 +1,76 @@
|
||||
## Summary
|
||||
## Base Branch
|
||||
|
||||
<!-- Brief description of what this PR does -->
|
||||
- [ ] This PR targets the `develop` branch (required for all feature/fix PRs)
|
||||
- [ ] This PR targets `main` (hotfix only - maintainers)
|
||||
|
||||
## Description
|
||||
|
||||
<!-- What does this PR do? 2-3 sentences -->
|
||||
|
||||
## Related Issue
|
||||
|
||||
Closes #
|
||||
|
||||
## Type of Change
|
||||
|
||||
- [ ] Bug fix (non-breaking change that fixes an issue)
|
||||
- [ ] New feature (non-breaking change that adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
- [ ] Documentation update
|
||||
- [ ] Refactoring (no functional changes)
|
||||
- [ ] Tests (adding or updating tests)
|
||||
- [ ] 🐛 Bug fix
|
||||
- [ ] ✨ New feature
|
||||
- [ ] 📚 Documentation
|
||||
- [ ] ♻️ Refactor
|
||||
- [ ] 🧪 Test
|
||||
|
||||
## Related Issues
|
||||
## Area
|
||||
|
||||
<!-- Link any related issues: Fixes #123, Closes #456 -->
|
||||
- [ ] Frontend
|
||||
- [ ] Backend
|
||||
- [ ] Fullstack
|
||||
|
||||
## Changes Made
|
||||
## Commit Message Format
|
||||
|
||||
<!-- List the main changes in this PR -->
|
||||
Follow conventional commits: `<type>: <subject>`
|
||||
|
||||
-
|
||||
-
|
||||
-
|
||||
**Types:** feat, fix, docs, style, refactor, test, chore
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!-- If applicable, add screenshots for UI changes -->
|
||||
**Example:** `feat: add user authentication system`
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have run `pre-commit run --all-files` and fixed any issues
|
||||
- [ ] I have added tests for my changes (if applicable)
|
||||
- [ ] All existing tests pass locally
|
||||
- [ ] I have updated documentation (if applicable)
|
||||
- [ ] My code follows the project's code style
|
||||
- [ ] I've synced with `develop` branch
|
||||
- [ ] I've tested my changes locally
|
||||
- [ ] I've followed the code principles (SOLID, DRY, KISS)
|
||||
- [ ] My PR is small and focused (< 400 lines ideally)
|
||||
|
||||
## Testing
|
||||
## CI/Testing Requirements
|
||||
|
||||
<!-- Describe how you tested these changes -->
|
||||
- [ ] All CI checks pass
|
||||
- [ ] All existing tests pass
|
||||
- [ ] New features include test coverage
|
||||
- [ ] Bug fixes include regression tests
|
||||
|
||||
## Additional Notes
|
||||
## Screenshots
|
||||
|
||||
<!-- Any additional context or notes for reviewers -->
|
||||
<!-- Required for UI changes. Delete if not applicable. -->
|
||||
|
||||
| Before | After |
|
||||
|--------|-------|
|
||||
| | |
|
||||
|
||||
## Feature Toggle
|
||||
|
||||
<!-- If feature is incomplete or experimental, how is it hidden from users? -->
|
||||
<!-- This ensures incomplete work can be merged without affecting production. -->
|
||||
|
||||
- [ ] Behind localStorage flag: `use_feature_name`
|
||||
- [ ] Behind settings toggle
|
||||
- [ ] Behind environment variable/config
|
||||
- [ ] N/A - Feature is complete and ready for all users
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
<!-- Does this PR introduce breaking changes? If yes, describe what breaks and migration steps. -->
|
||||
<!-- Delete this section if not applicable. -->
|
||||
|
||||
**Breaking:** Yes / No
|
||||
|
||||
**Details:**
|
||||
<!-- What breaks? What do users/developers need to change? -->
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Python dependencies
|
||||
- package-ecosystem: pip
|
||||
directory: /apps/backend
|
||||
schedule:
|
||||
interval: weekly
|
||||
open-pull-requests-limit: 5
|
||||
labels:
|
||||
- dependencies
|
||||
- python
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
|
||||
# npm dependencies
|
||||
- package-ecosystem: npm
|
||||
directory: /apps/frontend
|
||||
schedule:
|
||||
interval: weekly
|
||||
open-pull-requests-limit: 5
|
||||
labels:
|
||||
- dependencies
|
||||
- javascript
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
|
||||
# GitHub Actions
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
open-pull-requests-limit: 5
|
||||
labels:
|
||||
- dependencies
|
||||
- ci
|
||||
commit-message:
|
||||
prefix: "ci(deps)"
|
||||
@@ -0,0 +1,409 @@
|
||||
name: Beta Release
|
||||
|
||||
# Manual trigger for beta releases from develop branch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Beta version (e.g., 2.8.0-beta.1)'
|
||||
required: true
|
||||
type: string
|
||||
dry_run:
|
||||
description: 'Test build without creating release'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
validate-version:
|
||||
name: Validate beta version format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Validate version format
|
||||
run: |
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
|
||||
# Check if version matches beta semver pattern
|
||||
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+-(beta|alpha|rc)\.[0-9]+$ ]]; then
|
||||
echo "::error::Invalid version format: $VERSION"
|
||||
echo "Version must match pattern: X.Y.Z-beta.N (e.g., 2.8.0-beta.1)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Valid beta version: $VERSION"
|
||||
|
||||
create-tag:
|
||||
name: Create beta tag
|
||||
needs: validate-version
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
version: ${{ github.event.inputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: develop
|
||||
|
||||
- name: Create and push tag
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
run: |
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git tag -a "v$VERSION" -m "Beta release v$VERSION"
|
||||
git push origin "v$VERSION"
|
||||
echo "Created tag v$VERSION"
|
||||
|
||||
# Intel build on Intel runner for native compilation
|
||||
build-macos-intel:
|
||||
needs: create-tag
|
||||
runs-on: macos-15-intel
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Use tag for real releases, develop branch for dry runs
|
||||
ref: ${{ github.event.inputs.dry_run == 'true' && 'develop' || format('v{0}', needs.create-tag.outputs.version) }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.npm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm-
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd apps/frontend && npm ci
|
||||
|
||||
- name: Cache bundled Python
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/frontend/python-runtime
|
||||
key: python-bundle-${{ runner.os }}-x64-3.12.8
|
||||
restore-keys: |
|
||||
python-bundle-${{ runner.os }}-x64-
|
||||
|
||||
- name: Build application
|
||||
run: cd apps/frontend && npm run build
|
||||
|
||||
- name: Package macOS (Intel)
|
||||
run: |
|
||||
VERSION="${{ needs.create-tag.outputs.version }}"
|
||||
cd apps/frontend && npm run package:mac -- --x64 --config.extraMetadata.version="$VERSION"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.MAC_CERTIFICATE }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }}
|
||||
|
||||
- name: Notarize macOS Intel app
|
||||
env:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
run: |
|
||||
if [ -z "$APPLE_ID" ]; then
|
||||
echo "Skipping notarization: APPLE_ID not configured"
|
||||
exit 0
|
||||
fi
|
||||
cd apps/frontend
|
||||
for dmg in dist/*.dmg; do
|
||||
echo "Notarizing $dmg..."
|
||||
xcrun notarytool submit "$dmg" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
|
||||
--team-id "$APPLE_TEAM_ID" \
|
||||
--wait
|
||||
xcrun stapler staple "$dmg"
|
||||
echo "Successfully notarized and stapled $dmg"
|
||||
done
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-intel-builds
|
||||
path: |
|
||||
apps/frontend/dist/*.dmg
|
||||
apps/frontend/dist/*.zip
|
||||
|
||||
# Apple Silicon build on ARM64 runner for native compilation
|
||||
build-macos-arm64:
|
||||
needs: create-tag
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Use tag for real releases, develop branch for dry runs
|
||||
ref: ${{ github.event.inputs.dry_run == 'true' && 'develop' || format('v{0}', needs.create-tag.outputs.version) }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.npm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm-
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd apps/frontend && npm ci
|
||||
|
||||
- name: Cache bundled Python
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/frontend/python-runtime
|
||||
key: python-bundle-${{ runner.os }}-arm64-3.12.8
|
||||
restore-keys: |
|
||||
python-bundle-${{ runner.os }}-arm64-
|
||||
|
||||
- name: Build application
|
||||
run: cd apps/frontend && npm run build
|
||||
|
||||
- name: Package macOS (Apple Silicon)
|
||||
run: |
|
||||
VERSION="${{ needs.create-tag.outputs.version }}"
|
||||
cd apps/frontend && npm run package:mac -- --arm64 --config.extraMetadata.version="$VERSION"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.MAC_CERTIFICATE }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }}
|
||||
|
||||
- name: Notarize macOS ARM64 app
|
||||
env:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
run: |
|
||||
if [ -z "$APPLE_ID" ]; then
|
||||
echo "Skipping notarization: APPLE_ID not configured"
|
||||
exit 0
|
||||
fi
|
||||
cd apps/frontend
|
||||
for dmg in dist/*.dmg; do
|
||||
echo "Notarizing $dmg..."
|
||||
xcrun notarytool submit "$dmg" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
|
||||
--team-id "$APPLE_TEAM_ID" \
|
||||
--wait
|
||||
xcrun stapler staple "$dmg"
|
||||
echo "Successfully notarized and stapled $dmg"
|
||||
done
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-arm64-builds
|
||||
path: |
|
||||
apps/frontend/dist/*.dmg
|
||||
apps/frontend/dist/*.zip
|
||||
|
||||
build-windows:
|
||||
needs: create-tag
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Use tag for real releases, develop branch for dry runs
|
||||
ref: ${{ github.event.inputs.dry_run == 'true' && 'develop' || format('v{0}', needs.create-tag.outputs.version) }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
shell: bash
|
||||
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.npm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm-
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd apps/frontend && npm ci
|
||||
|
||||
- name: Cache bundled Python
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/frontend/python-runtime
|
||||
key: python-bundle-${{ runner.os }}-x64-3.12.8
|
||||
restore-keys: |
|
||||
python-bundle-${{ runner.os }}-x64-
|
||||
|
||||
- name: Build application
|
||||
run: cd apps/frontend && npm run build
|
||||
|
||||
- name: Package Windows
|
||||
shell: bash
|
||||
run: |
|
||||
VERSION="${{ needs.create-tag.outputs.version }}"
|
||||
cd apps/frontend && npm run package:win -- --config.extraMetadata.version="$VERSION"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.WIN_CERTIFICATE }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.WIN_CERTIFICATE_PASSWORD }}
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-builds
|
||||
path: |
|
||||
apps/frontend/dist/*.exe
|
||||
|
||||
build-linux:
|
||||
needs: create-tag
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Use tag for real releases, develop branch for dry runs
|
||||
ref: ${{ github.event.inputs.dry_run == 'true' && 'develop' || format('v{0}', needs.create-tag.outputs.version) }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.npm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm-
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd apps/frontend && npm ci
|
||||
|
||||
- name: Cache bundled Python
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/frontend/python-runtime
|
||||
key: python-bundle-${{ runner.os }}-x64-3.12.8
|
||||
restore-keys: |
|
||||
python-bundle-${{ runner.os }}-x64-
|
||||
|
||||
- name: Build application
|
||||
run: cd apps/frontend && npm run build
|
||||
|
||||
- name: Package Linux
|
||||
run: |
|
||||
VERSION="${{ needs.create-tag.outputs.version }}"
|
||||
cd apps/frontend && npm run package:linux -- --config.extraMetadata.version="$VERSION"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-builds
|
||||
path: |
|
||||
apps/frontend/dist/*.AppImage
|
||||
apps/frontend/dist/*.deb
|
||||
|
||||
create-release:
|
||||
needs: [create-tag, build-macos-intel, build-macos-arm64, build-windows, build-linux]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: v${{ needs.create-tag.outputs.version }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist
|
||||
|
||||
- name: Flatten and validate artifacts
|
||||
run: |
|
||||
mkdir -p release-assets
|
||||
find dist -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" \) -exec cp {} release-assets/ \;
|
||||
|
||||
# Validate that at least one artifact was copied
|
||||
artifact_count=$(find release-assets -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" \) | wc -l)
|
||||
if [ "$artifact_count" -eq 0 ]; then
|
||||
echo "::error::No build artifacts found! Expected .dmg, .zip, .exe, .AppImage, or .deb files."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found $artifact_count artifact(s):"
|
||||
ls -la release-assets/
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
cd release-assets
|
||||
sha256sum ./* > checksums.sha256
|
||||
cat checksums.sha256
|
||||
|
||||
- name: Create Beta Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: v${{ needs.create-tag.outputs.version }}
|
||||
name: v${{ needs.create-tag.outputs.version }} (Beta)
|
||||
body: |
|
||||
## Beta Release v${{ needs.create-tag.outputs.version }}
|
||||
|
||||
This is a **beta release** for testing new features. It may contain bugs or incomplete functionality.
|
||||
|
||||
### How to opt-in to beta updates
|
||||
1. Open Auto Claude
|
||||
2. Go to Settings > Updates
|
||||
3. Enable "Beta Updates" toggle
|
||||
|
||||
### Reporting Issues
|
||||
Please report any issues at https://github.com/AndyMik90/Auto-Claude/issues
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: https://github.com/${{ github.repository }}/compare/main...v${{ needs.create-tag.outputs.version }}
|
||||
files: release-assets/*
|
||||
draft: false
|
||||
prerelease: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
dry-run-summary:
|
||||
needs: [create-tag, build-macos-intel, build-macos-arm64, build-windows, build-linux]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.inputs.dry_run == 'true' }}
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist
|
||||
|
||||
- name: Dry run summary
|
||||
run: |
|
||||
echo "## Beta Release Dry Run Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Version:** ${{ needs.create-tag.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Build artifacts created successfully:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
find dist -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" \) >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "To create a real release, run this workflow again with dry_run unchecked." >> $GITHUB_STEP_SUMMARY
|
||||
@@ -32,22 +32,17 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
node-version: '24'
|
||||
|
||||
- name: Install Visual Studio Build Tools
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Install node-pty and rebuild for Electron
|
||||
working-directory: auto-claude-ui
|
||||
working-directory: apps/frontend
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Install only node-pty
|
||||
pnpm add node-pty@1.1.0-beta42
|
||||
npm install node-pty@1.1.0-beta42
|
||||
|
||||
# Get Electron ABI version
|
||||
$electronAbi = (npx electron-abi $env:ELECTRON_VERSION)
|
||||
@@ -57,7 +52,7 @@ jobs:
|
||||
npx @electron/rebuild --version $env:ELECTRON_VERSION --module-dir node_modules/node-pty --arch ${{ matrix.arch }}
|
||||
|
||||
- name: Package prebuilt binaries
|
||||
working-directory: auto-claude-ui
|
||||
working-directory: apps/frontend
|
||||
shell: pwsh
|
||||
run: |
|
||||
$electronAbi = (npx electron-abi $env:ELECTRON_VERSION)
|
||||
@@ -83,7 +78,7 @@ jobs:
|
||||
Get-ChildItem $prebuildDir
|
||||
|
||||
- name: Create archive
|
||||
working-directory: auto-claude-ui
|
||||
working-directory: apps/frontend
|
||||
shell: pwsh
|
||||
run: |
|
||||
$electronAbi = (npx electron-abi $env:ELECTRON_VERSION)
|
||||
@@ -98,14 +93,14 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: node-pty-win32-${{ matrix.arch }}
|
||||
path: auto-claude-ui/node-pty-*.zip
|
||||
path: apps/frontend/node-pty-*.zip
|
||||
retention-days: 90
|
||||
|
||||
- name: Upload to release
|
||||
if: github.event_name == 'release'
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: auto-claude-ui/node-pty-*.zip
|
||||
files: apps/frontend/node-pty-*.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
+35
-31
@@ -2,9 +2,13 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
branches: [main, develop]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
# Python tests
|
||||
@@ -29,30 +33,35 @@ jobs:
|
||||
version: "latest"
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: auto-claude
|
||||
working-directory: apps/backend
|
||||
run: |
|
||||
uv venv
|
||||
uv pip install -r requirements.txt
|
||||
uv pip install -r ../tests/requirements-test.txt
|
||||
uv pip install -r ../../tests/requirements-test.txt
|
||||
|
||||
- name: Run tests
|
||||
working-directory: auto-claude
|
||||
if: matrix.python-version != '3.12'
|
||||
working-directory: apps/backend
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}/apps/backend
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
pytest ../tests/ -v --tb=short -x
|
||||
pytest ../../tests/ -v --tb=short -x
|
||||
|
||||
- name: Run tests with coverage
|
||||
if: matrix.python-version == '3.12'
|
||||
working-directory: auto-claude
|
||||
working-directory: apps/backend
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}/apps/backend
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
pytest ../tests/ -v --cov=. --cov-report=xml --cov-report=term-missing
|
||||
pytest ../../tests/ -v --cov=. --cov-report=xml --cov-report=term-missing --cov-fail-under=20
|
||||
|
||||
- name: Upload coverage reports
|
||||
if: matrix.python-version == '3.12'
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./auto-claude/coverage.xml
|
||||
file: ./apps/backend/coverage.xml
|
||||
fail_ci_if_error: false
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -67,39 +76,34 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '24'
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
run: echo "dir=$(pnpm store path)" >> "$GITHUB_OUTPUT"
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
run: echo "dir=$(npm config get cache)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: ${{ runner.os }}-pnpm-
|
||||
path: ${{ steps.npm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm-
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: auto-claude-ui
|
||||
run: pnpm install --frozen-lockfile --ignore-scripts
|
||||
working-directory: apps/frontend
|
||||
run: npm ci --ignore-scripts
|
||||
|
||||
- name: Lint
|
||||
working-directory: auto-claude-ui
|
||||
run: pnpm run lint
|
||||
working-directory: apps/frontend
|
||||
run: npm run lint
|
||||
|
||||
- name: Type check
|
||||
working-directory: auto-claude-ui
|
||||
run: pnpm run typecheck
|
||||
working-directory: apps/frontend
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Run tests
|
||||
working-directory: auto-claude-ui
|
||||
run: pnpm run test
|
||||
working-directory: apps/frontend
|
||||
run: npm run test
|
||||
|
||||
- name: Build
|
||||
working-directory: auto-claude-ui
|
||||
run: pnpm run build
|
||||
working-directory: apps/frontend
|
||||
run: npm run build
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
name: Issue Auto Label
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
label-area:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Add area label from form
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const issue = context.payload.issue;
|
||||
const body = issue.body || '';
|
||||
|
||||
console.log(`Processing issue #${issue.number}: ${issue.title}`);
|
||||
|
||||
// Map form selection to label
|
||||
const areaMap = {
|
||||
'Frontend': 'area/frontend',
|
||||
'Backend': 'area/backend',
|
||||
'Fullstack': 'area/fullstack'
|
||||
};
|
||||
|
||||
const labels = [];
|
||||
|
||||
for (const [key, label] of Object.entries(areaMap)) {
|
||||
if (body.includes(key)) {
|
||||
console.log(`Found area: ${key}, adding label: ${label}`);
|
||||
labels.push(label);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (labels.length > 0) {
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: labels
|
||||
});
|
||||
console.log(`Successfully added labels: ${labels.join(', ')}`);
|
||||
} catch (error) {
|
||||
core.setFailed(`Failed to add labels: ${error.message}`);
|
||||
}
|
||||
} else {
|
||||
console.log('No matching area found in issue body');
|
||||
}
|
||||
@@ -2,9 +2,9 @@ name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
branches: [main, develop]
|
||||
|
||||
jobs:
|
||||
# Python linting
|
||||
@@ -23,36 +23,8 @@ jobs:
|
||||
run: pip install ruff
|
||||
|
||||
- name: Run ruff check
|
||||
run: ruff check auto-claude/ --output-format=github
|
||||
run: ruff check apps/backend/ --output-format=github
|
||||
|
||||
- name: Run ruff format check
|
||||
run: ruff format auto-claude/ --check --diff
|
||||
run: ruff format apps/backend/ --check --diff
|
||||
|
||||
# TypeScript/React linting
|
||||
frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: auto-claude-ui
|
||||
run: pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
- name: Run ESLint
|
||||
working-directory: auto-claude-ui
|
||||
run: pnpm lint
|
||||
|
||||
- name: Run TypeScript check
|
||||
working-directory: auto-claude-ui
|
||||
run: pnpm typecheck
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
name: PR Auto Label
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
# Cancel in-progress runs for the same PR
|
||||
concurrency:
|
||||
group: pr-auto-label-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
label:
|
||||
name: Auto Label PR
|
||||
runs-on: ubuntu-latest
|
||||
# Don't run on fork PRs (they can't write labels)
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Auto-label PR
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
retries: 3
|
||||
retry-exempt-status-codes: 400,401,403,404,422
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
const pr = context.payload.pull_request;
|
||||
const prNumber = pr.number;
|
||||
const title = pr.title;
|
||||
|
||||
console.log(`::group::PR #${prNumber} - Auto-labeling`);
|
||||
console.log(`Title: ${title}`);
|
||||
|
||||
const labelsToAdd = new Set();
|
||||
const labelsToRemove = new Set();
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// TYPE LABELS (from PR title - Conventional Commits)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
const typeMap = {
|
||||
'feat': 'feature',
|
||||
'fix': 'bug',
|
||||
'docs': 'documentation',
|
||||
'refactor': 'refactor',
|
||||
'test': 'test',
|
||||
'ci': 'ci',
|
||||
'chore': 'chore',
|
||||
'perf': 'performance',
|
||||
'style': 'style',
|
||||
'build': 'build'
|
||||
};
|
||||
|
||||
const typeMatch = title.match(/^(\w+)(\(.+?\))?(!)?:/);
|
||||
if (typeMatch) {
|
||||
const type = typeMatch[1].toLowerCase();
|
||||
const isBreaking = typeMatch[3] === '!';
|
||||
|
||||
if (typeMap[type]) {
|
||||
labelsToAdd.add(typeMap[type]);
|
||||
console.log(` 📝 Type: ${type} → ${typeMap[type]}`);
|
||||
}
|
||||
|
||||
if (isBreaking) {
|
||||
labelsToAdd.add('breaking-change');
|
||||
console.log(` ⚠️ Breaking change detected`);
|
||||
}
|
||||
} else {
|
||||
console.log(` ⚠️ No conventional commit prefix found in title`);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// AREA LABELS (from changed files)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
let files = [];
|
||||
try {
|
||||
const { data } = await github.rest.pulls.listFiles({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: prNumber,
|
||||
per_page: 100
|
||||
});
|
||||
files = data;
|
||||
} catch (e) {
|
||||
console.log(` ⚠️ Could not fetch files: ${e.message}`);
|
||||
}
|
||||
|
||||
const areas = {
|
||||
frontend: false,
|
||||
backend: false,
|
||||
ci: false,
|
||||
docs: false,
|
||||
tests: false
|
||||
};
|
||||
|
||||
for (const file of files) {
|
||||
const path = file.filename;
|
||||
if (path.startsWith('apps/frontend/')) areas.frontend = true;
|
||||
if (path.startsWith('apps/backend/')) areas.backend = true;
|
||||
if (path.startsWith('.github/')) areas.ci = true;
|
||||
if (path.endsWith('.md') || path.startsWith('docs/')) areas.docs = true;
|
||||
if (path.startsWith('tests/') || path.includes('.test.') || path.includes('.spec.')) areas.tests = true;
|
||||
}
|
||||
|
||||
// Determine area label (mutually exclusive)
|
||||
const areaLabels = ['area/frontend', 'area/backend', 'area/fullstack', 'area/ci'];
|
||||
|
||||
if (areas.frontend && areas.backend) {
|
||||
labelsToAdd.add('area/fullstack');
|
||||
areaLabels.filter(l => l !== 'area/fullstack').forEach(l => labelsToRemove.add(l));
|
||||
console.log(` 📁 Area: fullstack (${files.length} files)`);
|
||||
} else if (areas.frontend) {
|
||||
labelsToAdd.add('area/frontend');
|
||||
areaLabels.filter(l => l !== 'area/frontend').forEach(l => labelsToRemove.add(l));
|
||||
console.log(` 📁 Area: frontend (${files.length} files)`);
|
||||
} else if (areas.backend) {
|
||||
labelsToAdd.add('area/backend');
|
||||
areaLabels.filter(l => l !== 'area/backend').forEach(l => labelsToRemove.add(l));
|
||||
console.log(` 📁 Area: backend (${files.length} files)`);
|
||||
} else if (areas.ci) {
|
||||
labelsToAdd.add('area/ci');
|
||||
areaLabels.filter(l => l !== 'area/ci').forEach(l => labelsToRemove.add(l));
|
||||
console.log(` 📁 Area: ci (${files.length} files)`);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// SIZE LABELS (from lines changed)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
const additions = pr.additions || 0;
|
||||
const deletions = pr.deletions || 0;
|
||||
const totalLines = additions + deletions;
|
||||
|
||||
const sizeLabels = ['size/XS', 'size/S', 'size/M', 'size/L', 'size/XL'];
|
||||
let sizeLabel;
|
||||
|
||||
if (totalLines < 10) sizeLabel = 'size/XS';
|
||||
else if (totalLines < 100) sizeLabel = 'size/S';
|
||||
else if (totalLines < 500) sizeLabel = 'size/M';
|
||||
else if (totalLines < 1000) sizeLabel = 'size/L';
|
||||
else sizeLabel = 'size/XL';
|
||||
|
||||
labelsToAdd.add(sizeLabel);
|
||||
sizeLabels.filter(l => l !== sizeLabel).forEach(l => labelsToRemove.add(l));
|
||||
console.log(` 📏 Size: ${sizeLabel} (+${additions}/-${deletions} = ${totalLines} lines)`);
|
||||
|
||||
console.log('::endgroup::');
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// APPLY LABELS
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
console.log(`::group::Applying labels`);
|
||||
|
||||
// Remove old labels (in parallel)
|
||||
const removeArray = [...labelsToRemove].filter(l => !labelsToAdd.has(l));
|
||||
if (removeArray.length > 0) {
|
||||
const removePromises = removeArray.map(async (label) => {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
name: label
|
||||
});
|
||||
console.log(` ✓ Removed: ${label}`);
|
||||
} catch (e) {
|
||||
if (e.status !== 404) {
|
||||
console.log(` ⚠ Could not remove ${label}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
await Promise.all(removePromises);
|
||||
}
|
||||
|
||||
// Add new labels
|
||||
const addArray = [...labelsToAdd];
|
||||
if (addArray.length > 0) {
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
labels: addArray
|
||||
});
|
||||
console.log(` ✓ Added: ${addArray.join(', ')}`);
|
||||
} catch (e) {
|
||||
// Some labels might not exist
|
||||
if (e.status === 404) {
|
||||
core.warning(`Some labels do not exist. Please create them in repository settings.`);
|
||||
// Try adding one by one
|
||||
for (const label of addArray) {
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
labels: [label]
|
||||
});
|
||||
} catch (e2) {
|
||||
console.log(` ⚠ Label '${label}' does not exist`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('::endgroup::');
|
||||
|
||||
// Summary
|
||||
console.log(`✅ PR #${prNumber} labeled: ${addArray.join(', ')}`);
|
||||
|
||||
// Write job summary
|
||||
core.summary
|
||||
.addHeading(`PR #${prNumber} Auto-Labels`, 3)
|
||||
.addTable([
|
||||
[{data: 'Category', header: true}, {data: 'Label', header: true}],
|
||||
['Type', typeMatch ? typeMap[typeMatch[1].toLowerCase()] || 'none' : 'none'],
|
||||
['Area', areas.frontend && areas.backend ? 'fullstack' : areas.frontend ? 'frontend' : areas.backend ? 'backend' : 'other'],
|
||||
['Size', sizeLabel]
|
||||
])
|
||||
.addRaw(`\n**Files changed:** ${files.length}\n`)
|
||||
.addRaw(`**Lines:** +${additions} / -${deletions}\n`);
|
||||
await core.summary.write();
|
||||
@@ -0,0 +1,72 @@
|
||||
name: PR Status Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
# Cancel in-progress runs for the same PR
|
||||
concurrency:
|
||||
group: pr-status-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
mark-checking:
|
||||
name: Set Checking Status
|
||||
runs-on: ubuntu-latest
|
||||
# Don't run on fork PRs (they can't write labels)
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Update PR status label
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
retries: 3
|
||||
retry-exempt-status-codes: 400,401,403,404,422
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const statusLabels = ['🔄 Checking', '✅ Ready for Review', '❌ Checks Failed'];
|
||||
|
||||
console.log(`::group::PR #${prNumber} - Setting status to Checking`);
|
||||
|
||||
// Remove old status labels (parallel for speed)
|
||||
const removePromises = statusLabels.map(async (label) => {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
name: label
|
||||
});
|
||||
console.log(` ✓ Removed: ${label}`);
|
||||
} catch (e) {
|
||||
if (e.status !== 404) {
|
||||
console.log(` ⚠ Could not remove ${label}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(removePromises);
|
||||
|
||||
// Add checking label
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
labels: ['🔄 Checking']
|
||||
});
|
||||
console.log(` ✓ Added: 🔄 Checking`);
|
||||
} catch (e) {
|
||||
// Label might not exist - create helpful error
|
||||
if (e.status === 404) {
|
||||
core.warning(`Label '🔄 Checking' does not exist. Please create it in repository settings.`);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
console.log('::endgroup::');
|
||||
console.log(`✅ PR #${prNumber} marked as checking`);
|
||||
@@ -0,0 +1,195 @@
|
||||
name: PR Status Gate
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [CI, Lint, Quality Security, Quality DCO, Quality Commit Lint]
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
checks: read
|
||||
|
||||
jobs:
|
||||
update-status:
|
||||
name: Update PR Status
|
||||
runs-on: ubuntu-latest
|
||||
# Only run if this workflow_run is associated with a PR
|
||||
if: github.event.workflow_run.pull_requests[0] != null
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Check all required checks and update label
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
retries: 3
|
||||
retry-exempt-status-codes: 400,401,403,404,422
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
const prNumber = context.payload.workflow_run.pull_requests[0].number;
|
||||
const headSha = context.payload.workflow_run.head_sha;
|
||||
const triggerWorkflow = context.payload.workflow_run.name;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// REQUIRED CHECK RUNS - Job-level checks (not workflow-level)
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// Format: "{Workflow Name} / {Job Name} (pull_request)"
|
||||
//
|
||||
// To find check names: Go to PR → Checks tab → copy exact name
|
||||
// To update: Edit this list when workflow jobs are added/renamed/removed
|
||||
//
|
||||
// Last validated: 2025-12-26
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
const requiredChecks = [
|
||||
// CI workflow (ci.yml) - 3 checks
|
||||
'CI / test-frontend (pull_request)',
|
||||
'CI / test-python (3.12) (pull_request)',
|
||||
'CI / test-python (3.13) (pull_request)',
|
||||
// Lint workflow (lint.yml) - 1 check
|
||||
'Lint / python (pull_request)',
|
||||
// Quality Security workflow (quality-security.yml) - 4 checks
|
||||
'Quality Security / CodeQL (javascript-typescript) (pull_request)',
|
||||
'Quality Security / CodeQL (python) (pull_request)',
|
||||
'Quality Security / Python Security (Bandit) (pull_request)',
|
||||
'Quality Security / Security Summary (pull_request)',
|
||||
// Quality DCO workflow (quality-dco.yml) - 1 check
|
||||
'Quality DCO / DCO Check (pull_request)',
|
||||
// Quality Commit Lint workflow (quality-commit-lint.yml) - 1 check
|
||||
'Quality Commit Lint / Conventional Commits (pull_request)'
|
||||
];
|
||||
|
||||
const statusLabels = {
|
||||
checking: '🔄 Checking',
|
||||
passed: '✅ Ready for Review',
|
||||
failed: '❌ Checks Failed'
|
||||
};
|
||||
|
||||
console.log(`::group::PR #${prNumber} - Checking required checks`);
|
||||
console.log(`Triggered by: ${triggerWorkflow}`);
|
||||
console.log(`Head SHA: ${headSha}`);
|
||||
console.log(`Required checks: ${requiredChecks.length}`);
|
||||
console.log('');
|
||||
|
||||
// Fetch all check runs for this commit
|
||||
let allCheckRuns = [];
|
||||
try {
|
||||
const { data } = await github.rest.checks.listForRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: headSha,
|
||||
per_page: 100
|
||||
});
|
||||
allCheckRuns = data.check_runs;
|
||||
console.log(`Found ${allCheckRuns.length} total check runs`);
|
||||
} catch (error) {
|
||||
// Add warning annotation so maintainers are alerted
|
||||
core.warning(`Failed to fetch check runs for PR #${prNumber}: ${error.message}. PR label may be outdated.`);
|
||||
console.log(`::error::Failed to fetch check runs: ${error.message}`);
|
||||
console.log('::endgroup::');
|
||||
return;
|
||||
}
|
||||
|
||||
let allComplete = true;
|
||||
let anyFailed = false;
|
||||
const results = [];
|
||||
|
||||
// Check each required check
|
||||
for (const checkName of requiredChecks) {
|
||||
const check = allCheckRuns.find(c => c.name === checkName);
|
||||
|
||||
if (!check) {
|
||||
results.push({ name: checkName, status: '⏳ Pending', complete: false });
|
||||
allComplete = false;
|
||||
} else if (check.status !== 'completed') {
|
||||
results.push({ name: checkName, status: '🔄 Running', complete: false });
|
||||
allComplete = false;
|
||||
} else if (check.conclusion === 'success') {
|
||||
results.push({ name: checkName, status: '✅ Passed', complete: true });
|
||||
} else if (check.conclusion === 'skipped') {
|
||||
// Skipped checks are treated as passed (e.g., path filters, conditional jobs)
|
||||
results.push({ name: checkName, status: '⏭️ Skipped', complete: true, skipped: true });
|
||||
} else {
|
||||
results.push({ name: checkName, status: '❌ Failed', complete: true, failed: true });
|
||||
anyFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Print results table
|
||||
console.log('');
|
||||
console.log('Check Status:');
|
||||
console.log('─'.repeat(70));
|
||||
for (const r of results) {
|
||||
const shortName = r.name.length > 55 ? r.name.substring(0, 52) + '...' : r.name;
|
||||
console.log(` ${r.status.padEnd(12)} ${shortName}`);
|
||||
}
|
||||
console.log('─'.repeat(70));
|
||||
console.log('::endgroup::');
|
||||
|
||||
// Only update label if all required checks are complete
|
||||
if (!allComplete) {
|
||||
const pending = results.filter(r => !r.complete).length;
|
||||
console.log(`⏳ ${pending}/${requiredChecks.length} checks still pending - keeping current label`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine final label
|
||||
const newLabel = anyFailed ? statusLabels.failed : statusLabels.passed;
|
||||
|
||||
console.log(`::group::Updating PR #${prNumber} label`);
|
||||
|
||||
// Remove old status labels
|
||||
for (const label of Object.values(statusLabels)) {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
name: label
|
||||
});
|
||||
console.log(` ✓ Removed: ${label}`);
|
||||
} catch (e) {
|
||||
if (e.status !== 404) {
|
||||
console.log(` ⚠ Could not remove ${label}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add final status label
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
labels: [newLabel]
|
||||
});
|
||||
console.log(` ✓ Added: ${newLabel}`);
|
||||
} catch (e) {
|
||||
if (e.status === 404) {
|
||||
core.warning(`Label '${newLabel}' does not exist. Please create it in repository settings.`);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
console.log('::endgroup::');
|
||||
|
||||
// Summary
|
||||
const passedCount = results.filter(r => r.status === '✅ Passed').length;
|
||||
const skippedCount = results.filter(r => r.skipped).length;
|
||||
const failedCount = results.filter(r => r.failed).length;
|
||||
|
||||
if (anyFailed) {
|
||||
console.log(`❌ PR #${prNumber} has ${failedCount} failing check(s)`);
|
||||
core.summary.addRaw(`## ❌ PR #${prNumber} - Checks Failed\n\n`);
|
||||
core.summary.addRaw(`**${failedCount}** of **${requiredChecks.length}** required checks failed.\n\n`);
|
||||
} else {
|
||||
const skippedNote = skippedCount > 0 ? ` (${skippedCount} skipped)` : '';
|
||||
const totalSuccessful = passedCount + skippedCount;
|
||||
console.log(`✅ PR #${prNumber} is ready for review (${totalSuccessful}/${requiredChecks.length} checks succeeded${skippedNote})`);
|
||||
core.summary.addRaw(`## ✅ PR #${prNumber} - Ready for Review\n\n`);
|
||||
core.summary.addRaw(`All **${requiredChecks.length}** required checks succeeded${skippedNote}.\n\n`);
|
||||
}
|
||||
|
||||
// Add results to summary
|
||||
core.summary.addTable([
|
||||
[{data: 'Check', header: true}, {data: 'Status', header: true}],
|
||||
...results.map(r => [r.name, r.status])
|
||||
]);
|
||||
await core.summary.write();
|
||||
@@ -0,0 +1,109 @@
|
||||
name: Prepare Release
|
||||
|
||||
# Triggers when code is pushed to main (e.g., merging develop → main)
|
||||
# If package.json version is newer than the latest tag, creates a new tag
|
||||
# which then triggers the release.yml workflow
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'apps/frontend/package.json'
|
||||
- 'package.json'
|
||||
|
||||
jobs:
|
||||
check-and-tag:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
should_release: ${{ steps.check.outputs.should_release }}
|
||||
new_version: ${{ steps.check.outputs.new_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get package version
|
||||
id: package
|
||||
run: |
|
||||
VERSION=$(node -p "require('./apps/frontend/package.json').version")
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Package version: $VERSION"
|
||||
|
||||
- name: Get latest tag version
|
||||
id: latest_tag
|
||||
run: |
|
||||
# Get the latest version tag (v*)
|
||||
LATEST_TAG=$(git tag -l 'v*' --sort=-version:refname | head -n1)
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
echo "No existing tags found"
|
||||
echo "version=0.0.0" >> $GITHUB_OUTPUT
|
||||
else
|
||||
# Remove 'v' prefix
|
||||
LATEST_VERSION=${LATEST_TAG#v}
|
||||
echo "version=$LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Latest tag: $LATEST_TAG (version: $LATEST_VERSION)"
|
||||
fi
|
||||
|
||||
- name: Check if release needed
|
||||
id: check
|
||||
run: |
|
||||
PACKAGE_VERSION="${{ steps.package.outputs.version }}"
|
||||
LATEST_VERSION="${{ steps.latest_tag.outputs.version }}"
|
||||
|
||||
echo "Comparing: package=$PACKAGE_VERSION vs latest_tag=$LATEST_VERSION"
|
||||
|
||||
# Use sort -V for version comparison
|
||||
HIGHER=$(printf '%s\n%s' "$PACKAGE_VERSION" "$LATEST_VERSION" | sort -V | tail -n1)
|
||||
|
||||
if [ "$HIGHER" = "$PACKAGE_VERSION" ] && [ "$PACKAGE_VERSION" != "$LATEST_VERSION" ]; then
|
||||
echo "should_release=true" >> $GITHUB_OUTPUT
|
||||
echo "new_version=$PACKAGE_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "✅ New release needed: v$PACKAGE_VERSION"
|
||||
else
|
||||
echo "should_release=false" >> $GITHUB_OUTPUT
|
||||
echo "⏭️ No release needed (package version not newer than latest tag)"
|
||||
fi
|
||||
|
||||
- name: Create and push tag
|
||||
if: steps.check.outputs.should_release == 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.check.outputs.new_version }}"
|
||||
TAG="v$VERSION"
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
echo "Creating tag: $TAG"
|
||||
git tag -a "$TAG" -m "Release $TAG"
|
||||
git push origin "$TAG"
|
||||
|
||||
echo "✅ Tag $TAG created and pushed"
|
||||
echo "🚀 This will trigger the release workflow"
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
if [ "${{ steps.check.outputs.should_release }}" = "true" ]; then
|
||||
echo "## 🚀 Release Triggered" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Version:** v${{ steps.check.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "The release workflow has been triggered and will:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "1. Build binaries for all platforms" >> $GITHUB_STEP_SUMMARY
|
||||
echo "2. Generate changelog from PRs" >> $GITHUB_STEP_SUMMARY
|
||||
echo "3. Create GitHub release" >> $GITHUB_STEP_SUMMARY
|
||||
echo "4. Update README with new version" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "## ⏭️ No Release Needed" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Package version:** ${{ steps.package.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Latest tag:** v${{ steps.latest_tag.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "The package version is not newer than the latest tag." >> $GITHUB_STEP_SUMMARY
|
||||
echo "To trigger a release, bump the version using:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
|
||||
echo "node scripts/bump-version.js patch # or minor/major" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -0,0 +1,115 @@
|
||||
name: Quality Commit Lint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
types: [opened, edited, synchronize, reopened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Conventional Commits
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Validate PR title
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
retries: 3
|
||||
script: |
|
||||
const pr = context.payload.pull_request;
|
||||
const title = pr.title;
|
||||
// Sanitize title for safe markdown interpolation (prevent injection)
|
||||
const sanitizedTitle = title.replace(/`/g, "'").replace(/\[/g, '\\[').replace(/\]/g, '\\]');
|
||||
|
||||
console.log(`::group::PR #${pr.number} - Validating PR title`);
|
||||
console.log(`Title: ${title}`);
|
||||
|
||||
// Conventional Commits pattern for PR title
|
||||
// type(scope)?: description (max 100 chars)
|
||||
// Optional ! for breaking changes: feat!: or feat(scope)!:
|
||||
// Scope allows: letters, numbers, hyphens, underscores, slashes, dots
|
||||
const pattern = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-zA-Z0-9_\-\/\.]+\))?!?: .{1,100}$/;
|
||||
|
||||
const isValid = pattern.test(title);
|
||||
console.log(`Valid: ${isValid}`);
|
||||
console.log('::endgroup::');
|
||||
|
||||
if (!isValid) {
|
||||
// Log helpful error message to console (visible in workflow logs)
|
||||
console.log('');
|
||||
console.log('❌ PR title does not follow Conventional Commits format');
|
||||
console.log('');
|
||||
console.log('Expected format: type(scope): description');
|
||||
console.log('');
|
||||
console.log('Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert');
|
||||
console.log('');
|
||||
console.log('Examples of valid PR titles:');
|
||||
console.log(' ✓ feat(auth): add OAuth2 login support');
|
||||
console.log(' ✓ fix(api): handle null response correctly');
|
||||
console.log(' ✓ docs: update README installation steps');
|
||||
console.log(' ✓ chore: update dependencies');
|
||||
console.log('');
|
||||
console.log(`Your title: "${title}"`);
|
||||
console.log('');
|
||||
console.log('Suggested fix for your title:');
|
||||
// Try to suggest a fix based on the title
|
||||
const lowerTitle = title.toLowerCase();
|
||||
const placeholder = '[add description here]';
|
||||
if (lowerTitle.includes('fix') || lowerTitle.includes('bug')) {
|
||||
const cleaned = title.replace(/^(fix(ed|es|ing)?|bug)[:\s]*/i, '').trim();
|
||||
console.log(` → fix: ${cleaned || placeholder}`);
|
||||
} else if (lowerTitle.includes('add') || lowerTitle.includes('new') || lowerTitle.includes('feature')) {
|
||||
const cleaned = title.replace(/^(add(ed|s|ing)?|new|features?)[:\s]*/i, '').trim();
|
||||
console.log(` → feat: ${cleaned || placeholder}`);
|
||||
} else if (lowerTitle.includes('update') || lowerTitle.includes('change')) {
|
||||
const cleaned = title.replace(/^(update[ds]?|chang(ed|es|ing)?)[:\s]*/i, '').trim();
|
||||
console.log(` → chore: ${cleaned || placeholder}`);
|
||||
} else if (lowerTitle.includes('doc') || lowerTitle.includes('readme')) {
|
||||
const cleaned = title.replace(/^(docs?|readme)[:\s]*/i, '').trim();
|
||||
console.log(` → docs: ${cleaned || placeholder}`);
|
||||
} else {
|
||||
console.log(` → feat: ${title}`);
|
||||
console.log(` → fix: ${title}`);
|
||||
console.log(` → chore: ${title}`);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
let errorMsg = '## ❌ PR Title Validation Failed\n\n';
|
||||
errorMsg += `Your PR title does not follow [Conventional Commits](https://www.conventionalcommits.org/) format:\n\n`;
|
||||
errorMsg += `> \`${sanitizedTitle}\`\n\n`;
|
||||
errorMsg += '### Expected Format\n\n';
|
||||
errorMsg += '```\ntype(scope): description\n```\n\n';
|
||||
errorMsg += '| Type | Description |\n';
|
||||
errorMsg += '|------|-------------|\n';
|
||||
errorMsg += '| `feat` | New feature |\n';
|
||||
errorMsg += '| `fix` | Bug fix |\n';
|
||||
errorMsg += '| `docs` | Documentation only |\n';
|
||||
errorMsg += '| `style` | Code style (formatting, etc.) |\n';
|
||||
errorMsg += '| `refactor` | Code refactoring |\n';
|
||||
errorMsg += '| `perf` | Performance improvement |\n';
|
||||
errorMsg += '| `test` | Adding/updating tests |\n';
|
||||
errorMsg += '| `build` | Build system changes |\n';
|
||||
errorMsg += '| `ci` | CI/CD changes |\n';
|
||||
errorMsg += '| `chore` | Maintenance tasks |\n';
|
||||
errorMsg += '| `revert` | Reverting changes |\n\n';
|
||||
errorMsg += '### Examples\n\n';
|
||||
errorMsg += '```\nfeat(auth): add OAuth2 login support\nfix(api/users): handle null response correctly\nfix(package.json): update dependencies\ndocs: update README installation steps\nci: add automated release workflow\n```\n\n';
|
||||
errorMsg += '### How to Fix\n\n';
|
||||
errorMsg += 'Edit your PR title to follow the format above.\n';
|
||||
|
||||
core.summary.addRaw(errorMsg);
|
||||
await core.summary.write();
|
||||
|
||||
core.setFailed('PR title does not follow Conventional Commits format');
|
||||
} else {
|
||||
console.log(`✅ PR title follows Conventional Commits format`);
|
||||
|
||||
core.summary
|
||||
.addHeading('✅ PR Title Valid', 3)
|
||||
.addRaw(`PR title follows Conventional Commits format: \`${sanitizedTitle}\``);
|
||||
await core.summary.write();
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
name: Quality DCO
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: DCO Check
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Check DCO Sign-off
|
||||
uses: dcoapp/dco-check@v1
|
||||
with:
|
||||
require-signoff: true
|
||||
|
||||
- name: DCO Help on Failure
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const helpMsg = `## ❌ DCO Sign-off Required
|
||||
|
||||
This project requires all commits to be signed off with the Developer Certificate of Origin (DCO).
|
||||
|
||||
### How to Fix
|
||||
|
||||
**Option 1: Sign off your last commit**
|
||||
\`\`\`bash
|
||||
git commit --amend --signoff
|
||||
git push --force-with-lease
|
||||
\`\`\`
|
||||
|
||||
**Option 2: Sign off all commits in this PR**
|
||||
\`\`\`bash
|
||||
git rebase HEAD~N --signoff # Replace N with number of commits
|
||||
git push --force-with-lease
|
||||
\`\`\`
|
||||
|
||||
**Option 3: Configure git to always sign off**
|
||||
\`\`\`bash
|
||||
git config --global format.signoff true
|
||||
\`\`\`
|
||||
|
||||
### What is DCO?
|
||||
|
||||
The [Developer Certificate of Origin](https://developercertificate.org/) is a lightweight way for contributors to certify that they wrote or have the right to submit the code they are contributing.
|
||||
|
||||
By signing off, you agree to the DCO terms:
|
||||
- The contribution was created by you
|
||||
- You have the right to submit it under the project's license
|
||||
- You understand the contribution is public and recorded
|
||||
|
||||
### Sign-off Format
|
||||
|
||||
Your commit message should end with:
|
||||
\`\`\`
|
||||
Signed-off-by: Your Name <your.email@example.com>
|
||||
\`\`\`
|
||||
|
||||
This line is automatically added when you use \`git commit -s\` or \`git commit --signoff\`.
|
||||
`;
|
||||
|
||||
core.summary.addRaw(helpMsg);
|
||||
await core.summary.write();
|
||||
@@ -0,0 +1,178 @@
|
||||
name: Quality Security
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
schedule:
|
||||
- cron: '0 0 * * 1' # Weekly on Monday at midnight UTC
|
||||
|
||||
# Cancel in-progress runs for the same branch/PR
|
||||
concurrency:
|
||||
group: security-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
codeql:
|
||||
name: CodeQL (${{ matrix.language }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [python, javascript-typescript]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-extended,security-and-quality
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
|
||||
python-security:
|
||||
name: Python Security (Bandit)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install Bandit
|
||||
run: pip install bandit
|
||||
|
||||
- name: Run Bandit security scan
|
||||
id: bandit
|
||||
run: |
|
||||
echo "::group::Running Bandit security scan"
|
||||
# Run Bandit; exit code 1 means issues found (expected), other codes are errors
|
||||
# Flags: -r=recursive, -ll=severity LOW+, -ii=confidence LOW+, -f=format, -o=output
|
||||
bandit -r apps/backend/ -ll -ii -f json -o bandit-report.json || BANDIT_EXIT=$?
|
||||
if [ "${BANDIT_EXIT:-0}" -gt 1 ]; then
|
||||
echo "::error::Bandit scan failed with exit code $BANDIT_EXIT"
|
||||
exit 1
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Analyze Bandit results
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
// Check if report exists
|
||||
if (!fs.existsSync('bandit-report.json')) {
|
||||
core.setFailed('Bandit report not found - scan may have failed');
|
||||
return;
|
||||
}
|
||||
|
||||
const report = JSON.parse(fs.readFileSync('bandit-report.json', 'utf8'));
|
||||
const results = report.results || [];
|
||||
|
||||
// Categorize by severity
|
||||
const high = results.filter(r => r.issue_severity === 'HIGH');
|
||||
const medium = results.filter(r => r.issue_severity === 'MEDIUM');
|
||||
const low = results.filter(r => r.issue_severity === 'LOW');
|
||||
|
||||
console.log(`::group::Bandit Security Scan Results`);
|
||||
console.log(`Found ${results.length} issues:`);
|
||||
console.log(` 🔴 HIGH: ${high.length}`);
|
||||
console.log(` 🟡 MEDIUM: ${medium.length}`);
|
||||
console.log(` 🟢 LOW: ${low.length}`);
|
||||
console.log('');
|
||||
|
||||
// Print high severity issues
|
||||
if (high.length > 0) {
|
||||
console.log('High Severity Issues:');
|
||||
console.log('─'.repeat(60));
|
||||
for (const issue of high) {
|
||||
console.log(` ${issue.filename}:${issue.line_number}`);
|
||||
console.log(` ${issue.issue_text}`);
|
||||
console.log(` Test: ${issue.test_id} (${issue.test_name})`);
|
||||
console.log('');
|
||||
}
|
||||
}
|
||||
console.log('::endgroup::');
|
||||
|
||||
// Build summary
|
||||
let summary = `## 🔒 Python Security Scan (Bandit)\n\n`;
|
||||
summary += `| Severity | Count |\n`;
|
||||
summary += `|----------|-------|\n`;
|
||||
summary += `| 🔴 High | ${high.length} |\n`;
|
||||
summary += `| 🟡 Medium | ${medium.length} |\n`;
|
||||
summary += `| 🟢 Low | ${low.length} |\n\n`;
|
||||
|
||||
if (high.length > 0) {
|
||||
summary += `### High Severity Issues\n\n`;
|
||||
for (const issue of high) {
|
||||
summary += `- **${issue.filename}:${issue.line_number}**\n`;
|
||||
summary += ` - ${issue.issue_text}\n`;
|
||||
summary += ` - Test: \`${issue.test_id}\` (${issue.test_name})\n\n`;
|
||||
}
|
||||
}
|
||||
|
||||
core.summary.addRaw(summary);
|
||||
await core.summary.write();
|
||||
|
||||
// Fail if high severity issues found
|
||||
if (high.length > 0) {
|
||||
core.setFailed(`Found ${high.length} high severity security issue(s)`);
|
||||
} else {
|
||||
console.log('✅ No high severity security issues found');
|
||||
}
|
||||
|
||||
# Summary job that waits for all security checks
|
||||
security-summary:
|
||||
name: Security Summary
|
||||
runs-on: ubuntu-latest
|
||||
needs: [codeql, python-security]
|
||||
if: always()
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Check security results
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const codeql = '${{ needs.codeql.result }}';
|
||||
const bandit = '${{ needs.python-security.result }}';
|
||||
|
||||
console.log('Security Check Results:');
|
||||
console.log(` CodeQL: ${codeql}`);
|
||||
console.log(` Bandit: ${bandit}`);
|
||||
|
||||
// Only 'failure' is a real failure; 'skipped' is acceptable (e.g., path filters)
|
||||
const acceptable = ['success', 'skipped'];
|
||||
const codeqlOk = acceptable.includes(codeql);
|
||||
const banditOk = acceptable.includes(bandit);
|
||||
const allPassed = codeqlOk && banditOk;
|
||||
|
||||
if (allPassed) {
|
||||
console.log('\n✅ All security checks passed');
|
||||
core.summary.addRaw('## ✅ Security Checks Passed\n\nAll security scans completed successfully.');
|
||||
} else {
|
||||
console.log('\n❌ Some security checks failed');
|
||||
core.summary.addRaw('## ❌ Security Checks Failed\n\nOne or more security scans found issues.');
|
||||
core.setFailed('Security checks failed');
|
||||
}
|
||||
|
||||
await core.summary.write();
|
||||
+150
-69
@@ -20,34 +20,42 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '24'
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
run: echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-x64-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: ${{ runner.os }}-x64-pnpm-
|
||||
path: ${{ steps.npm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm-
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd auto-claude-ui && pnpm install --frozen-lockfile
|
||||
run: cd apps/frontend && npm ci
|
||||
|
||||
- name: Cache bundled Python
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/frontend/python-runtime
|
||||
key: python-bundle-${{ runner.os }}-x64-3.12.8
|
||||
restore-keys: |
|
||||
python-bundle-${{ runner.os }}-x64-
|
||||
|
||||
- name: Build application
|
||||
run: cd auto-claude-ui && pnpm run build
|
||||
run: cd apps/frontend && npm run build
|
||||
|
||||
- name: Package macOS (Intel)
|
||||
run: cd auto-claude-ui && pnpm run package:mac -- --arch=x64
|
||||
run: cd apps/frontend && npm run package:mac -- --x64
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.MAC_CERTIFICATE }}
|
||||
@@ -63,7 +71,7 @@ jobs:
|
||||
echo "Skipping notarization: APPLE_ID not configured"
|
||||
exit 0
|
||||
fi
|
||||
cd auto-claude-ui
|
||||
cd apps/frontend
|
||||
for dmg in dist/*.dmg; do
|
||||
echo "Notarizing $dmg..."
|
||||
xcrun notarytool submit "$dmg" \
|
||||
@@ -80,8 +88,8 @@ jobs:
|
||||
with:
|
||||
name: macos-intel-builds
|
||||
path: |
|
||||
auto-claude-ui/dist/*.dmg
|
||||
auto-claude-ui/dist/*.zip
|
||||
apps/frontend/dist/*.dmg
|
||||
apps/frontend/dist/*.zip
|
||||
|
||||
# Apple Silicon build on ARM64 runner for native compilation
|
||||
build-macos-arm64:
|
||||
@@ -89,34 +97,42 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '24'
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
run: echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-arm64-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: ${{ runner.os }}-arm64-pnpm-
|
||||
path: ${{ steps.npm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm-
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd auto-claude-ui && pnpm install --frozen-lockfile
|
||||
run: cd apps/frontend && npm ci
|
||||
|
||||
- name: Cache bundled Python
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/frontend/python-runtime
|
||||
key: python-bundle-${{ runner.os }}-arm64-3.12.8
|
||||
restore-keys: |
|
||||
python-bundle-${{ runner.os }}-arm64-
|
||||
|
||||
- name: Build application
|
||||
run: cd auto-claude-ui && pnpm run build
|
||||
run: cd apps/frontend && npm run build
|
||||
|
||||
- name: Package macOS (Apple Silicon)
|
||||
run: cd auto-claude-ui && pnpm run package:mac -- --arch=arm64
|
||||
run: cd apps/frontend && npm run package:mac -- --arm64
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.MAC_CERTIFICATE }}
|
||||
@@ -132,7 +148,7 @@ jobs:
|
||||
echo "Skipping notarization: APPLE_ID not configured"
|
||||
exit 0
|
||||
fi
|
||||
cd auto-claude-ui
|
||||
cd apps/frontend
|
||||
for dmg in dist/*.dmg; do
|
||||
echo "Notarizing $dmg..."
|
||||
xcrun notarytool submit "$dmg" \
|
||||
@@ -149,43 +165,51 @@ jobs:
|
||||
with:
|
||||
name: macos-arm64-builds
|
||||
path: |
|
||||
auto-claude-ui/dist/*.dmg
|
||||
auto-claude-ui/dist/*.zip
|
||||
apps/frontend/dist/*.dmg
|
||||
apps/frontend/dist/*.zip
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '24'
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
shell: bash
|
||||
run: echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: ${{ runner.os }}-pnpm-
|
||||
path: ${{ steps.npm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm-
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd auto-claude-ui && pnpm install --frozen-lockfile
|
||||
run: cd apps/frontend && npm ci
|
||||
|
||||
- name: Cache bundled Python
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/frontend/python-runtime
|
||||
key: python-bundle-${{ runner.os }}-x64-3.12.8
|
||||
restore-keys: |
|
||||
python-bundle-${{ runner.os }}-x64-
|
||||
|
||||
- name: Build application
|
||||
run: cd auto-claude-ui && pnpm run build
|
||||
run: cd apps/frontend && npm run build
|
||||
|
||||
- name: Package Windows
|
||||
run: cd auto-claude-ui && pnpm run package:win
|
||||
run: cd apps/frontend && npm run package:win
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.WIN_CERTIFICATE }}
|
||||
@@ -196,41 +220,49 @@ jobs:
|
||||
with:
|
||||
name: windows-builds
|
||||
path: |
|
||||
auto-claude-ui/dist/*.exe
|
||||
apps/frontend/dist/*.exe
|
||||
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '24'
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
run: echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: ${{ runner.os }}-pnpm-
|
||||
path: ${{ steps.npm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm-
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd auto-claude-ui && pnpm install --frozen-lockfile
|
||||
run: cd apps/frontend && npm ci
|
||||
|
||||
- name: Cache bundled Python
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/frontend/python-runtime
|
||||
key: python-bundle-${{ runner.os }}-x64-3.12.8
|
||||
restore-keys: |
|
||||
python-bundle-${{ runner.os }}-x64-
|
||||
|
||||
- name: Build application
|
||||
run: cd auto-claude-ui && pnpm run build
|
||||
run: cd apps/frontend && npm run build
|
||||
|
||||
- name: Package Linux
|
||||
run: cd auto-claude-ui && pnpm run package:linux
|
||||
run: cd apps/frontend && npm run package:linux
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -239,8 +271,8 @@ jobs:
|
||||
with:
|
||||
name: linux-builds
|
||||
path: |
|
||||
auto-claude-ui/dist/*.AppImage
|
||||
auto-claude-ui/dist/*.deb
|
||||
apps/frontend/dist/*.AppImage
|
||||
apps/frontend/dist/*.deb
|
||||
|
||||
create-release:
|
||||
needs: [build-macos-intel, build-macos-arm64, build-windows, build-linux]
|
||||
@@ -451,3 +483,52 @@ jobs:
|
||||
prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Update README with new version after successful release
|
||||
update-readme:
|
||||
needs: [create-release]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.dry_run != true) }}
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract version from tag
|
||||
id: version
|
||||
run: |
|
||||
# Extract version from tag (v2.7.2 -> 2.7.2)
|
||||
VERSION=${GITHUB_REF_NAME#v}
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Updating README to version: $VERSION"
|
||||
|
||||
- name: Update README.md
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
|
||||
# Update version badge: version-X.Y.Z-blue
|
||||
sed -i "s/version-[0-9]*\.[0-9]*\.[0-9]*-blue/version-${VERSION}-blue/g" README.md
|
||||
|
||||
# Update download links: Auto-Claude-X.Y.Z
|
||||
sed -i "s/Auto-Claude-[0-9]*\.[0-9]*\.[0-9]*/Auto-Claude-${VERSION}/g" README.md
|
||||
|
||||
echo "README.md updated to version $VERSION"
|
||||
grep -E "(version-|Auto-Claude-)" README.md | head -10
|
||||
|
||||
- name: Commit and push README update
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Check if there are changes to commit
|
||||
if git diff --quiet README.md; then
|
||||
echo "No changes to README.md, skipping commit"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git add README.md
|
||||
git commit -m "docs: update README to v${{ steps.version.outputs.version }} [skip ci]"
|
||||
git push origin main
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Stale Issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Every Sunday
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
stale-issue-message: |
|
||||
This issue has been inactive for 60 days. It will be closed in 14 days if there's no activity.
|
||||
|
||||
- If this is still relevant, please comment or update the issue
|
||||
- If you're working on this, add the `in-progress` label
|
||||
close-issue-message: 'Closed due to inactivity. Feel free to reopen if still relevant.'
|
||||
stale-issue-label: 'stale'
|
||||
days-before-stale: 60
|
||||
days-before-close: 14
|
||||
exempt-issue-labels: 'priority/critical,priority/high,in-progress,blocked'
|
||||
@@ -28,17 +28,19 @@ jobs:
|
||||
version: "latest"
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: auto-claude
|
||||
working-directory: apps/backend
|
||||
run: |
|
||||
uv venv
|
||||
uv pip install -r requirements.txt
|
||||
uv pip install -r ../tests/requirements-test.txt
|
||||
uv pip install -r ../../tests/requirements-test.txt
|
||||
|
||||
- name: Run tests
|
||||
working-directory: auto-claude
|
||||
working-directory: apps/backend
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}/apps/backend
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
pytest ../tests/ -v --tb=short
|
||||
pytest ../../tests/ -v --tb=short
|
||||
|
||||
# Frontend tests
|
||||
test-frontend:
|
||||
@@ -50,17 +52,12 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
node-version: '24'
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: auto-claude-ui
|
||||
run: pnpm install --frozen-lockfile --ignore-scripts
|
||||
working-directory: apps/frontend
|
||||
run: npm ci --ignore-scripts
|
||||
|
||||
- name: Run tests
|
||||
working-directory: auto-claude-ui
|
||||
run: pnpm test
|
||||
working-directory: apps/frontend
|
||||
run: npm run test
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
id: package_version
|
||||
run: |
|
||||
# Read version from package.json
|
||||
PACKAGE_VERSION=$(node -p "require('./auto-claude-ui/package.json').version")
|
||||
PACKAGE_VERSION=$(node -p "require('./apps/frontend/package.json').version")
|
||||
echo "version=$PACKAGE_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Package.json version: $PACKAGE_VERSION"
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
name: Welcome
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
welcome:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/first-interaction@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-message: |
|
||||
👋 Thanks for opening your first issue!
|
||||
|
||||
A maintainer will triage this soon. In the meantime:
|
||||
- Make sure you've provided all the requested info
|
||||
- Join our [Discord](https://discord.gg/QhRnz9m5HE) for faster help
|
||||
pr-message: |
|
||||
🎉 Thanks for your first PR!
|
||||
|
||||
A maintainer will review it soon. Please make sure:
|
||||
- Your branch is synced with `develop`
|
||||
- CI checks pass
|
||||
- You've followed our [contribution guide](https://github.com/AndyMik90/Auto-Claude/blob/develop/CONTRIBUTING.md)
|
||||
|
||||
Welcome to the Auto Claude community!
|
||||
+112
-36
@@ -1,31 +1,69 @@
|
||||
# OS
|
||||
# ===========================
|
||||
# OS Files
|
||||
# ===========================
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Environment files (contain API keys)
|
||||
# ===========================
|
||||
# Security - Environment & Secrets
|
||||
# ===========================
|
||||
.env
|
||||
.env.local
|
||||
.env.*
|
||||
!.env.example
|
||||
*.pem
|
||||
*.key
|
||||
*.crt
|
||||
*.p12
|
||||
*.pfx
|
||||
.secrets
|
||||
secrets/
|
||||
credentials/
|
||||
|
||||
# Git worktrees (used by auto-build parallel mode)
|
||||
.worktrees/
|
||||
|
||||
# IDE
|
||||
# ===========================
|
||||
# IDE & Editors
|
||||
# ===========================
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*.sublime-workspace
|
||||
*.sublime-project
|
||||
.project
|
||||
.classpath
|
||||
.settings/
|
||||
|
||||
# ===========================
|
||||
# Logs
|
||||
# ===========================
|
||||
logs/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Personal notes
|
||||
OPUS_ANALYSIS_AND_IDEAS.md
|
||||
# ===========================
|
||||
# Git Worktrees (parallel builds)
|
||||
# ===========================
|
||||
.worktrees/
|
||||
|
||||
# Documentation
|
||||
docs/
|
||||
# ===========================
|
||||
# Auto Claude Generated
|
||||
# ===========================
|
||||
.auto-claude/
|
||||
.auto-build-security.json
|
||||
.auto-claude-security.json
|
||||
.auto-claude-status
|
||||
.claude_settings.json
|
||||
.update-metadata.json
|
||||
|
||||
# Python
|
||||
# ===========================
|
||||
# Python (apps/backend)
|
||||
# ===========================
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
@@ -33,25 +71,19 @@ __pycache__/
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
/lib/
|
||||
/lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
ENV/
|
||||
env/
|
||||
.conda/
|
||||
|
||||
# Testing
|
||||
.pytest_cache/
|
||||
@@ -64,26 +96,70 @@ coverage.xml
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
|
||||
# mypy
|
||||
# Type checking
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
.pytype/
|
||||
.pyre/
|
||||
|
||||
# Auto-build generated files
|
||||
.auto-build-security.json
|
||||
.auto-claude-security.json
|
||||
.auto-claude-status
|
||||
.claude_settings.json
|
||||
.update-metadata.json
|
||||
# ===========================
|
||||
# Node.js (apps/frontend)
|
||||
# ===========================
|
||||
node_modules/
|
||||
.npm
|
||||
.yarn/
|
||||
.pnp.*
|
||||
|
||||
# Development of Auto Build with Auto Build
|
||||
# Build output
|
||||
dist/
|
||||
out/
|
||||
*.tsbuildinfo
|
||||
apps/frontend/python-runtime/
|
||||
|
||||
# Cache
|
||||
.cache/
|
||||
.parcel-cache/
|
||||
.turbo/
|
||||
.eslintcache
|
||||
.prettiercache
|
||||
|
||||
# ===========================
|
||||
# Electron
|
||||
# ===========================
|
||||
apps/frontend/dist/
|
||||
apps/frontend/out/
|
||||
*.asar
|
||||
*.blockmap
|
||||
*.snap
|
||||
*.deb
|
||||
*.rpm
|
||||
*.AppImage
|
||||
*.dmg
|
||||
*.exe
|
||||
*.msi
|
||||
|
||||
# ===========================
|
||||
# Testing
|
||||
# ===========================
|
||||
coverage/
|
||||
.nyc_output/
|
||||
test-results/
|
||||
playwright-report/
|
||||
playwright/.cache/
|
||||
|
||||
# ===========================
|
||||
# Misc
|
||||
# ===========================
|
||||
*.local
|
||||
*.bak
|
||||
*.tmp
|
||||
*.temp
|
||||
|
||||
# Development
|
||||
dev/
|
||||
|
||||
.auto-claude/
|
||||
|
||||
_bmad/
|
||||
_bmad-output/
|
||||
.claude/
|
||||
/docs
|
||||
|
||||
_bmad
|
||||
_bmad-output
|
||||
|
||||
.claude
|
||||
OPUS_ANALYSIS_AND_IDEAS.md
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Commit message validation
|
||||
# Enforces conventional commit format: type(scope): description
|
||||
#
|
||||
# Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
|
||||
# Examples:
|
||||
# feat(tasks): add drag and drop support
|
||||
# fix(terminal): resolve scroll position issue
|
||||
# docs: update README with setup instructions
|
||||
# chore: update dependencies
|
||||
|
||||
commit_msg_file=$1
|
||||
commit_msg=$(cat "$commit_msg_file")
|
||||
|
||||
# Regex for conventional commits
|
||||
# Format: type(optional-scope): description
|
||||
pattern="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9-]+\))?: .{1,100}$"
|
||||
|
||||
# Allow merge commits
|
||||
if echo "$commit_msg" | grep -qE "^Merge "; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Allow revert commits
|
||||
if echo "$commit_msg" | grep -qE "^Revert "; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check first line against pattern
|
||||
first_line=$(echo "$commit_msg" | head -n 1)
|
||||
|
||||
if ! echo "$first_line" | grep -qE "$pattern"; then
|
||||
echo ""
|
||||
echo "ERROR: Invalid commit message format!"
|
||||
echo ""
|
||||
echo "Your message: $first_line"
|
||||
echo ""
|
||||
echo "Expected format: type(scope): description"
|
||||
echo ""
|
||||
echo "Valid types:"
|
||||
echo " feat - A new feature"
|
||||
echo " fix - A bug fix"
|
||||
echo " docs - Documentation changes"
|
||||
echo " style - Code style changes (formatting, semicolons, etc.)"
|
||||
echo " refactor - Code refactoring (no feature/fix)"
|
||||
echo " perf - Performance improvements"
|
||||
echo " test - Adding or updating tests"
|
||||
echo " build - Build system or dependencies"
|
||||
echo " ci - CI/CD configuration"
|
||||
echo " chore - Other changes (maintenance)"
|
||||
echo " revert - Reverting a previous commit"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " feat(tasks): add drag and drop support"
|
||||
echo " fix(terminal): resolve scroll position issue"
|
||||
echo " docs: update README"
|
||||
echo " chore: update dependencies"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check description length (max 100 chars for first line)
|
||||
if [ ${#first_line} -gt 100 ]; then
|
||||
echo ""
|
||||
echo "ERROR: Commit message first line is too long!"
|
||||
echo "Maximum: 100 characters"
|
||||
echo "Current: ${#first_line} characters"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
+155
-3
@@ -1,6 +1,158 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Run lint-staged in auto-claude-ui if there are staged files there
|
||||
if git diff --cached --name-only | grep -q "^auto-claude-ui/"; then
|
||||
cd auto-claude-ui && pnpm exec lint-staged
|
||||
echo "Running pre-commit checks..."
|
||||
|
||||
# =============================================================================
|
||||
# VERSION SYNC - Keep all version references in sync with root package.json
|
||||
# =============================================================================
|
||||
|
||||
# Check if package.json is staged
|
||||
if git diff --cached --name-only | grep -q "^package.json$"; then
|
||||
echo "package.json changed, syncing version to all files..."
|
||||
|
||||
# Extract version from root package.json
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
|
||||
if [ -n "$VERSION" ]; then
|
||||
# Sync to apps/frontend/package.json
|
||||
if [ -f "apps/frontend/package.json" ]; then
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const pkg = require('./apps/frontend/package.json');
|
||||
if (pkg.version !== '$VERSION') {
|
||||
pkg.version = '$VERSION';
|
||||
fs.writeFileSync('./apps/frontend/package.json', JSON.stringify(pkg, null, 2) + '\n');
|
||||
console.log(' Updated apps/frontend/package.json to $VERSION');
|
||||
}
|
||||
"
|
||||
git add apps/frontend/package.json
|
||||
fi
|
||||
|
||||
# Sync to apps/backend/__init__.py
|
||||
if [ -f "apps/backend/__init__.py" ]; then
|
||||
sed -i.bak "s/__version__ = \"[^\"]*\"/__version__ = \"$VERSION\"/" apps/backend/__init__.py
|
||||
rm -f apps/backend/__init__.py.bak
|
||||
git add apps/backend/__init__.py
|
||||
echo " Updated apps/backend/__init__.py to $VERSION"
|
||||
fi
|
||||
|
||||
# Sync to README.md
|
||||
if [ -f "README.md" ]; then
|
||||
# Update version badge
|
||||
sed -i.bak "s/version-[0-9]*\.[0-9]*\.[0-9]*-blue/version-$VERSION-blue/g" README.md
|
||||
# Update download links
|
||||
sed -i.bak "s/Auto-Claude-[0-9]*\.[0-9]*\.[0-9]*/Auto-Claude-$VERSION/g" README.md
|
||||
rm -f README.md.bak
|
||||
git add README.md
|
||||
echo " Updated README.md to $VERSION"
|
||||
fi
|
||||
|
||||
echo "Version sync complete: $VERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# BACKEND CHECKS (Python) - Run first, before frontend
|
||||
# =============================================================================
|
||||
|
||||
# Check if there are staged Python files in apps/backend
|
||||
if git diff --cached --name-only | grep -q "^apps/backend/.*\.py$"; then
|
||||
echo "Python changes detected, running backend checks..."
|
||||
|
||||
# Determine ruff command (venv or global)
|
||||
RUFF=""
|
||||
if [ -f "apps/backend/.venv/bin/ruff" ]; then
|
||||
RUFF="apps/backend/.venv/bin/ruff"
|
||||
elif [ -f "apps/backend/.venv/Scripts/ruff.exe" ]; then
|
||||
RUFF="apps/backend/.venv/Scripts/ruff.exe"
|
||||
elif command -v ruff >/dev/null 2>&1; then
|
||||
RUFF="ruff"
|
||||
fi
|
||||
|
||||
if [ -n "$RUFF" ]; then
|
||||
# Run ruff linting (auto-fix)
|
||||
echo "Running ruff lint..."
|
||||
$RUFF check apps/backend/ --fix
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Ruff lint failed. Please fix Python linting errors before committing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run ruff format (auto-fix)
|
||||
echo "Running ruff format..."
|
||||
$RUFF format apps/backend/
|
||||
|
||||
# Stage any files that were auto-fixed by ruff (POSIX-compliant)
|
||||
find apps/backend -name "*.py" -type f -exec git add {} + 2>/dev/null || true
|
||||
else
|
||||
echo "Warning: ruff not found, skipping Python linting. Install with: uv pip install ruff"
|
||||
fi
|
||||
|
||||
# Run pytest (skip slow/integration tests and Windows-incompatible tests for pre-commit speed)
|
||||
echo "Running Python tests..."
|
||||
cd apps/backend
|
||||
# Tests to skip: graphiti (external deps), merge_file_tracker/service_orchestrator/worktree/workspace (Windows path/git issues)
|
||||
IGNORE_TESTS="--ignore=../../tests/test_graphiti.py --ignore=../../tests/test_merge_file_tracker.py --ignore=../../tests/test_service_orchestrator.py --ignore=../../tests/test_worktree.py --ignore=../../tests/test_workspace.py"
|
||||
if [ -d ".venv" ]; then
|
||||
# Use venv if it exists
|
||||
if [ -f ".venv/bin/pytest" ]; then
|
||||
PYTHONPATH=. .venv/bin/pytest ../../tests/ -v --tb=short -x -m "not slow and not integration" $IGNORE_TESTS
|
||||
elif [ -f ".venv/Scripts/pytest.exe" ]; then
|
||||
# Windows
|
||||
PYTHONPATH=. .venv/Scripts/pytest.exe ../../tests/ -v --tb=short -x -m "not slow and not integration" $IGNORE_TESTS
|
||||
else
|
||||
PYTHONPATH=. python -m pytest ../../tests/ -v --tb=short -x -m "not slow and not integration" $IGNORE_TESTS
|
||||
fi
|
||||
else
|
||||
PYTHONPATH=. python -m pytest ../../tests/ -v --tb=short -x -m "not slow and not integration" $IGNORE_TESTS
|
||||
fi
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Python tests failed. Please fix failing tests before committing."
|
||||
exit 1
|
||||
fi
|
||||
cd ../..
|
||||
|
||||
echo "Backend checks passed!"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# FRONTEND CHECKS (TypeScript/React)
|
||||
# =============================================================================
|
||||
|
||||
# Check if there are staged files in apps/frontend
|
||||
if git diff --cached --name-only | grep -q "^apps/frontend/"; then
|
||||
echo "Frontend changes detected, running frontend checks..."
|
||||
cd apps/frontend
|
||||
|
||||
# Run lint-staged (handles staged .ts/.tsx files)
|
||||
npm exec lint-staged
|
||||
|
||||
# Run TypeScript type check
|
||||
echo "Running type check..."
|
||||
npm run typecheck
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Type check failed. Please fix TypeScript errors before committing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run linting
|
||||
echo "Running lint..."
|
||||
npm run lint
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Lint failed. Run 'npm run lint:fix' to auto-fix issues."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for vulnerabilities (only high severity)
|
||||
echo "Checking for vulnerabilities..."
|
||||
npm audit --audit-level=high
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "High severity vulnerabilities found. Run 'npm audit fix' to resolve."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd ../..
|
||||
echo "Frontend checks passed!"
|
||||
fi
|
||||
|
||||
echo "All pre-commit checks passed!"
|
||||
|
||||
+41
-8
@@ -1,29 +1,62 @@
|
||||
repos:
|
||||
# Python linting (auto-claude/)
|
||||
# Version sync - propagate root package.json version to all files
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: version-sync
|
||||
name: Version Sync
|
||||
entry: bash -c '
|
||||
VERSION=$(node -p "require(\"./package.json\").version");
|
||||
if [ -n "$VERSION" ]; then
|
||||
# Sync to apps/frontend/package.json
|
||||
node -e "const fs=require(\"fs\");const p=require(\"./apps/frontend/package.json\");if(p.version!==\"$VERSION\"){p.version=\"$VERSION\";fs.writeFileSync(\"./apps/frontend/package.json\",JSON.stringify(p,null,2)+\"\n\");}";
|
||||
# Sync to apps/backend/__init__.py
|
||||
sed -i.bak "s/__version__ = \"[^\"]*\"/__version__ = \"$VERSION\"/" apps/backend/__init__.py && rm -f apps/backend/__init__.py.bak;
|
||||
# Sync to README.md
|
||||
sed -i.bak "s/version-[0-9]*\.[0-9]*\.[0-9]*-blue/version-$VERSION-blue/g" README.md;
|
||||
sed -i.bak "s/Auto-Claude-[0-9]*\.[0-9]*\.[0-9]*/Auto-Claude-$VERSION/g" README.md && rm -f README.md.bak;
|
||||
git add apps/frontend/package.json apps/backend/__init__.py README.md 2>/dev/null || true;
|
||||
fi
|
||||
'
|
||||
language: system
|
||||
files: ^package\.json$
|
||||
pass_filenames: false
|
||||
|
||||
# Python linting (apps/backend/)
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.8.3
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix]
|
||||
files: ^auto-claude/
|
||||
files: ^apps/backend/
|
||||
- id: ruff-format
|
||||
files: ^auto-claude/
|
||||
files: ^apps/backend/
|
||||
|
||||
# Frontend linting (auto-claude-ui/)
|
||||
# Python tests (apps/backend/) - skip slow/integration tests for pre-commit speed
|
||||
# Tests to skip: graphiti (external deps), merge_file_tracker/service_orchestrator/worktree/workspace (Windows path/git issues)
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: pytest
|
||||
name: Python Tests
|
||||
entry: bash -c 'cd apps/backend && PYTHONPATH=. python -m pytest ../../tests/ -v --tb=short -x -m "not slow and not integration" --ignore=../../tests/test_graphiti.py --ignore=../../tests/test_merge_file_tracker.py --ignore=../../tests/test_service_orchestrator.py --ignore=../../tests/test_worktree.py --ignore=../../tests/test_workspace.py'
|
||||
language: system
|
||||
files: ^(apps/backend/.*\.py$|tests/.*\.py$)
|
||||
pass_filenames: false
|
||||
|
||||
# Frontend linting (apps/frontend/)
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: eslint
|
||||
name: ESLint
|
||||
entry: bash -c 'cd auto-claude-ui && pnpm lint'
|
||||
entry: bash -c 'cd apps/frontend && npm run lint'
|
||||
language: system
|
||||
files: ^auto-claude-ui/.*\.(ts|tsx|js|jsx)$
|
||||
files: ^apps/frontend/.*\.(ts|tsx|js|jsx)$
|
||||
pass_filenames: false
|
||||
|
||||
- id: typecheck
|
||||
name: TypeScript Check
|
||||
entry: bash -c 'cd auto-claude-ui && pnpm typecheck'
|
||||
entry: bash -c 'cd apps/frontend && npm run typecheck'
|
||||
language: system
|
||||
files: ^auto-claude-ui/.*\.(ts|tsx)$
|
||||
files: ^apps/frontend/.*\.(ts|tsx)$
|
||||
pass_filenames: false
|
||||
|
||||
# General checks
|
||||
|
||||
@@ -4,94 +4,150 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project Overview
|
||||
|
||||
Auto Claude is a multi-agent autonomous coding framework that builds software through coordinated AI agent sessions. It uses the Claude Code SDK to run agents in isolated workspaces with security controls.
|
||||
Auto Claude is a multi-agent autonomous coding framework that builds software through coordinated AI agent sessions. It uses the Claude Agent SDK to run agents in isolated workspaces with security controls.
|
||||
|
||||
**CRITICAL: All AI interactions use the Claude Agent SDK (`claude-agent-sdk` package), NOT the Anthropic API directly.**
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
autonomous-coding/
|
||||
├── apps/
|
||||
│ ├── backend/ # Python backend/CLI - ALL agent logic lives here
|
||||
│ │ ├── core/ # Client, auth, security
|
||||
│ │ ├── agents/ # Agent implementations
|
||||
│ │ ├── spec_agents/ # Spec creation agents
|
||||
│ │ ├── integrations/ # Graphiti, Linear, GitHub
|
||||
│ │ └── prompts/ # Agent system prompts
|
||||
│ └── frontend/ # Electron desktop UI
|
||||
├── guides/ # Documentation
|
||||
├── tests/ # Test suite
|
||||
└── scripts/ # Build and utility scripts
|
||||
```
|
||||
|
||||
**When working with AI/LLM code:**
|
||||
- Look in `apps/backend/core/client.py` for the Claude SDK client setup
|
||||
- Reference `apps/backend/agents/` for working agent implementations
|
||||
- Check `apps/backend/spec_agents/` for spec creation agent examples
|
||||
- NEVER use `anthropic.Anthropic()` directly - always use `create_client()` from `core.client`
|
||||
|
||||
**Frontend (Electron Desktop App):**
|
||||
- Built with Electron, React, TypeScript
|
||||
- AI agents can perform E2E testing using the Electron MCP server
|
||||
- When bug fixing or implementing features, use the Electron MCP server for automated testing
|
||||
- See "End-to-End Testing" section below for details
|
||||
|
||||
## Commands
|
||||
|
||||
### Setup
|
||||
|
||||
**Requirements:**
|
||||
- Python 3.12+ (required for backend)
|
||||
- Node.js (for frontend)
|
||||
|
||||
```bash
|
||||
# Install dependencies (from auto-claude/)
|
||||
uv venv && uv pip install -r requirements.txt
|
||||
# Or: python3 -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt
|
||||
# Install all dependencies from root
|
||||
npm run install:all
|
||||
|
||||
# Or install separately:
|
||||
# Backend (from apps/backend/)
|
||||
cd apps/backend && uv venv && uv pip install -r requirements.txt
|
||||
|
||||
# Frontend (from apps/frontend/)
|
||||
cd apps/frontend && npm install
|
||||
|
||||
# Set up OAuth token
|
||||
claude setup-token
|
||||
# Add to auto-claude/.env: CLAUDE_CODE_OAUTH_TOKEN=your-token
|
||||
# Add to apps/backend/.env: CLAUDE_CODE_OAUTH_TOKEN=your-token
|
||||
```
|
||||
|
||||
### Creating and Running Specs
|
||||
```bash
|
||||
cd apps/backend
|
||||
|
||||
# Create a spec interactively
|
||||
python auto-claude/spec_runner.py --interactive
|
||||
python spec_runner.py --interactive
|
||||
|
||||
# Create spec from task description
|
||||
python auto-claude/spec_runner.py --task "Add user authentication"
|
||||
python spec_runner.py --task "Add user authentication"
|
||||
|
||||
# Force complexity level (simple/standard/complex)
|
||||
python auto-claude/spec_runner.py --task "Fix button" --complexity simple
|
||||
python spec_runner.py --task "Fix button" --complexity simple
|
||||
|
||||
# Run autonomous build
|
||||
python auto-claude/run.py --spec 001
|
||||
python run.py --spec 001
|
||||
|
||||
# List all specs
|
||||
python auto-claude/run.py --list
|
||||
python run.py --list
|
||||
```
|
||||
|
||||
### Workspace Management
|
||||
```bash
|
||||
cd apps/backend
|
||||
|
||||
# Review changes in isolated worktree
|
||||
python auto-claude/run.py --spec 001 --review
|
||||
python run.py --spec 001 --review
|
||||
|
||||
# Merge completed build into project
|
||||
python auto-claude/run.py --spec 001 --merge
|
||||
python run.py --spec 001 --merge
|
||||
|
||||
# Discard build
|
||||
python auto-claude/run.py --spec 001 --discard
|
||||
python run.py --spec 001 --discard
|
||||
```
|
||||
|
||||
### QA Validation
|
||||
```bash
|
||||
cd apps/backend
|
||||
|
||||
# Run QA manually
|
||||
python auto-claude/run.py --spec 001 --qa
|
||||
python run.py --spec 001 --qa
|
||||
|
||||
# Check QA status
|
||||
python auto-claude/run.py --spec 001 --qa-status
|
||||
python run.py --spec 001 --qa-status
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# Install test dependencies (required first time)
|
||||
cd auto-claude && uv pip install -r ../tests/requirements-test.txt
|
||||
cd apps/backend && uv pip install -r ../../tests/requirements-test.txt
|
||||
|
||||
# Run all tests (use virtual environment pytest)
|
||||
auto-claude/.venv/bin/pytest tests/ -v
|
||||
apps/backend/.venv/bin/pytest tests/ -v
|
||||
|
||||
# Run single test file
|
||||
auto-claude/.venv/bin/pytest tests/test_security.py -v
|
||||
apps/backend/.venv/bin/pytest tests/test_security.py -v
|
||||
|
||||
# Run specific test
|
||||
auto-claude/.venv/bin/pytest tests/test_security.py::test_bash_command_validation -v
|
||||
apps/backend/.venv/bin/pytest tests/test_security.py::test_bash_command_validation -v
|
||||
|
||||
# Skip slow tests
|
||||
auto-claude/.venv/bin/pytest tests/ -m "not slow"
|
||||
apps/backend/.venv/bin/pytest tests/ -m "not slow"
|
||||
|
||||
# Or from root
|
||||
npm run test:backend
|
||||
```
|
||||
|
||||
### Spec Validation
|
||||
```bash
|
||||
python auto-claude/validate_spec.py --spec-dir auto-claude/specs/001-feature --checkpoint all
|
||||
python apps/backend/validate_spec.py --spec-dir apps/backend/specs/001-feature --checkpoint all
|
||||
```
|
||||
|
||||
### Releases
|
||||
```bash
|
||||
# Automated version bump and release (recommended)
|
||||
node scripts/bump-version.js patch # 2.5.5 -> 2.5.6
|
||||
node scripts/bump-version.js minor # 2.5.5 -> 2.6.0
|
||||
node scripts/bump-version.js major # 2.5.5 -> 3.0.0
|
||||
node scripts/bump-version.js 2.6.0 # Set specific version
|
||||
# 1. Bump version on your branch (creates commit, no tag)
|
||||
node scripts/bump-version.js patch # 2.8.0 -> 2.8.1
|
||||
node scripts/bump-version.js minor # 2.8.0 -> 2.9.0
|
||||
node scripts/bump-version.js major # 2.8.0 -> 3.0.0
|
||||
|
||||
# Then push to trigger GitHub release workflows
|
||||
git push origin main
|
||||
git push origin v2.6.0
|
||||
# 2. Push and create PR to main
|
||||
git push origin your-branch
|
||||
gh pr create --base main
|
||||
|
||||
# 3. Merge PR → GitHub Actions automatically:
|
||||
# - Creates tag
|
||||
# - Builds all platforms
|
||||
# - Creates release with changelog
|
||||
# - Updates README
|
||||
```
|
||||
|
||||
See [RELEASE.md](RELEASE.md) for detailed release process documentation.
|
||||
@@ -108,21 +164,43 @@ See [RELEASE.md](RELEASE.md) for detailed release process documentation.
|
||||
**Implementation (run.py → agent.py)** - Multi-session build:
|
||||
1. Planner Agent creates subtask-based implementation plan
|
||||
2. Coder Agent implements subtasks (can spawn subagents for parallel work)
|
||||
3. QA Reviewer validates acceptance criteria
|
||||
4. QA Fixer resolves issues in a loop
|
||||
3. QA Reviewer validates acceptance criteria (can perform E2E testing via Electron MCP for frontend changes)
|
||||
4. QA Fixer resolves issues in a loop (with E2E testing to verify fixes)
|
||||
|
||||
### Key Components
|
||||
### Key Components (apps/backend/)
|
||||
|
||||
- **client.py** - Claude SDK client with security hooks and tool permissions
|
||||
- **security.py** + **project_analyzer.py** - Dynamic command allowlisting based on detected project stack
|
||||
- **worktree.py** - Git worktree isolation for safe feature development
|
||||
- **memory.py** - File-based session memory (primary, always-available storage)
|
||||
- **graphiti_memory.py** - Optional graph-based cross-session memory with semantic search
|
||||
- **graphiti_providers.py** - Multi-provider factory for Graphiti (OpenAI, Anthropic, Azure, Ollama, Google AI)
|
||||
**Core Infrastructure:**
|
||||
- **core/client.py** - Claude Agent SDK client factory with security hooks and tool permissions
|
||||
- **core/security.py** - Dynamic command allowlisting based on detected project stack
|
||||
- **core/auth.py** - OAuth token management for Claude SDK authentication
|
||||
- **agents/** - Agent implementations (planner, coder, qa_reviewer, qa_fixer)
|
||||
- **spec_agents/** - Spec creation agents (gatherer, researcher, writer, critic)
|
||||
|
||||
**Memory & Context:**
|
||||
- **integrations/graphiti/** - Graphiti memory system (mandatory)
|
||||
- `queries_pkg/graphiti.py` - Main GraphitiMemory class
|
||||
- `queries_pkg/client.py` - LadybugDB client wrapper
|
||||
- `queries_pkg/queries.py` - Graph query operations
|
||||
- `queries_pkg/search.py` - Semantic search logic
|
||||
- `queries_pkg/schema.py` - Graph schema definitions
|
||||
- **graphiti_config.py** - Configuration and validation for Graphiti integration
|
||||
- **linear_updater.py** - Optional Linear integration for progress tracking
|
||||
- **graphiti_providers.py** - Multi-provider factory (OpenAI, Anthropic, Azure, Ollama, Google AI)
|
||||
- **agents/memory_manager.py** - Session memory orchestration
|
||||
|
||||
### Agent Prompts (auto-claude/prompts/)
|
||||
**Workspace & Security:**
|
||||
- **cli/worktree.py** - Git worktree isolation for safe feature development
|
||||
- **context/project_analyzer.py** - Project stack detection for dynamic tooling
|
||||
- **auto_claude_tools.py** - Custom MCP tools integration
|
||||
|
||||
**Integrations:**
|
||||
- **linear_updater.py** - Optional Linear integration for progress tracking
|
||||
- **runners/github/** - GitHub Issues & PRs automation
|
||||
- **Electron MCP** - E2E testing integration for QA agents (Chrome DevTools Protocol)
|
||||
- Enabled with `ELECTRON_MCP_ENABLED=true` in `.env`
|
||||
- Allows QA agents to interact with running Electron app
|
||||
- See "End-to-End Testing" section for details
|
||||
|
||||
### Agent Prompts (apps/backend/prompts/)
|
||||
|
||||
| Prompt | Purpose |
|
||||
|--------|---------|
|
||||
@@ -139,7 +217,7 @@ See [RELEASE.md](RELEASE.md) for detailed release process documentation.
|
||||
|
||||
### Spec Directory Structure
|
||||
|
||||
Each spec in `auto-claude/specs/XXX-name/` contains:
|
||||
Each spec in `.auto-claude/specs/XXX-name/` contains:
|
||||
- `spec.md` - Feature specification
|
||||
- `requirements.json` - Structured user requirements
|
||||
- `context.json` - Discovered codebase context
|
||||
@@ -179,44 +257,225 @@ Three-layer defense:
|
||||
|
||||
Security profile cached in `.auto-claude-security.json`.
|
||||
|
||||
### Claude Agent SDK Integration
|
||||
|
||||
**CRITICAL: Auto Claude uses the Claude Agent SDK for ALL AI interactions. Never use the Anthropic API directly.**
|
||||
|
||||
**Client Location:** `apps/backend/core/client.py`
|
||||
|
||||
The `create_client()` function creates a configured `ClaudeSDKClient` instance with:
|
||||
- Multi-layered security (sandbox, permissions, security hooks)
|
||||
- Agent-specific tool permissions (planner, coder, qa_reviewer, qa_fixer)
|
||||
- Dynamic MCP server integration based on project capabilities
|
||||
- Extended thinking token budget control
|
||||
|
||||
**Example usage in agents:**
|
||||
```python
|
||||
from core.client import create_client
|
||||
|
||||
# Create SDK client (NOT raw Anthropic API client)
|
||||
client = create_client(
|
||||
project_dir=project_dir,
|
||||
spec_dir=spec_dir,
|
||||
model="claude-sonnet-4-5-20250929",
|
||||
agent_type="coder",
|
||||
max_thinking_tokens=None # or 5000/10000/16000
|
||||
)
|
||||
|
||||
# Run agent session
|
||||
response = client.create_agent_session(
|
||||
name="coder-agent-session",
|
||||
starting_message="Implement the authentication feature"
|
||||
)
|
||||
```
|
||||
|
||||
**Why use the SDK:**
|
||||
- Pre-configured security (sandbox, allowlists, hooks)
|
||||
- Automatic MCP server integration (Context7, Linear, Graphiti, Electron, Puppeteer)
|
||||
- Tool permissions based on agent role
|
||||
- Session management and recovery
|
||||
- Unified API across all agent types
|
||||
|
||||
**Where to find working examples:**
|
||||
- `apps/backend/agents/planner.py` - Planner agent
|
||||
- `apps/backend/agents/coder.py` - Coder agent
|
||||
- `apps/backend/agents/qa_reviewer.py` - QA reviewer
|
||||
- `apps/backend/agents/qa_fixer.py` - QA fixer
|
||||
- `apps/backend/spec_agents/` - Spec creation agents
|
||||
|
||||
### Memory System
|
||||
|
||||
Dual-layer memory architecture:
|
||||
**Graphiti Memory (Mandatory)** - `integrations/graphiti/`
|
||||
|
||||
**File-Based Memory (Primary)** - `memory.py`
|
||||
- Zero dependencies, always available
|
||||
- Human-readable files in `specs/XXX/memory/`
|
||||
- Session insights, patterns, gotchas, codebase map
|
||||
Auto Claude uses Graphiti as its primary memory system with embedded LadybugDB (no Docker required):
|
||||
|
||||
**Graphiti Memory (Optional Enhancement)** - `graphiti_memory.py`
|
||||
- Graph database with semantic search (LadybugDB - embedded, no Docker)
|
||||
- Cross-session context retrieval
|
||||
- Requires Python 3.12+
|
||||
- Multi-provider support:
|
||||
- **Graph database with semantic search** - Knowledge graph for cross-session context
|
||||
- **Session insights** - Patterns, gotchas, discoveries automatically extracted
|
||||
- **Multi-provider support:**
|
||||
- LLM: OpenAI, Anthropic, Azure OpenAI, Ollama, Google AI (Gemini)
|
||||
- Embedders: OpenAI, Voyage AI, Azure OpenAI, Ollama, Google AI
|
||||
- **Modular architecture:** (`integrations/graphiti/queries_pkg/`)
|
||||
- `graphiti.py` - Main GraphitiMemory class
|
||||
- `client.py` - LadybugDB client wrapper
|
||||
- `queries.py` - Graph query operations
|
||||
- `search.py` - Semantic search logic
|
||||
- `schema.py` - Graph schema definitions
|
||||
|
||||
```bash
|
||||
# Setup (requires Python 3.12+)
|
||||
pip install real_ladybug graphiti-core
|
||||
**Configuration:**
|
||||
- Set provider credentials in `apps/backend/.env` (see `.env.example`)
|
||||
- Required env vars: `GRAPHITI_ENABLED=true`, `ANTHROPIC_API_KEY` or other provider keys
|
||||
- Memory data stored in `.auto-claude/specs/XXX/graphiti/`
|
||||
|
||||
**Usage in agents:**
|
||||
```python
|
||||
from integrations.graphiti.memory import get_graphiti_memory
|
||||
|
||||
memory = get_graphiti_memory(spec_dir, project_dir)
|
||||
context = memory.get_context_for_session("Implementing feature X")
|
||||
memory.add_session_insight("Pattern: use React hooks for state")
|
||||
```
|
||||
|
||||
Enable with: `GRAPHITI_ENABLED=true` + provider credentials. See `.env.example`.
|
||||
## Development Guidelines
|
||||
|
||||
## Project Structure
|
||||
### Frontend Internationalization (i18n)
|
||||
|
||||
Auto Claude can be used in two ways:
|
||||
**CRITICAL: Always use i18n translation keys for all user-facing text in the frontend.**
|
||||
|
||||
**As a standalone CLI tool** (original project):
|
||||
```bash
|
||||
python auto-claude/run.py --spec 001
|
||||
The frontend uses `react-i18next` for internationalization. All labels, buttons, messages, and user-facing text MUST use translation keys.
|
||||
|
||||
**Translation file locations:**
|
||||
- `apps/frontend/src/shared/i18n/locales/en/*.json` - English translations
|
||||
- `apps/frontend/src/shared/i18n/locales/fr/*.json` - French translations
|
||||
|
||||
**Translation namespaces:**
|
||||
- `common.json` - Shared labels, buttons, common terms
|
||||
- `navigation.json` - Sidebar navigation items, sections
|
||||
- `settings.json` - Settings page content
|
||||
- `dialogs.json` - Dialog boxes and modals
|
||||
- `tasks.json` - Task/spec related content
|
||||
- `onboarding.json` - Onboarding wizard content
|
||||
- `welcome.json` - Welcome screen content
|
||||
|
||||
**Usage pattern:**
|
||||
```tsx
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// In component
|
||||
const { t } = useTranslation(['navigation', 'common']);
|
||||
|
||||
// Use translation keys, NOT hardcoded strings
|
||||
<span>{t('navigation:items.githubPRs')}</span> // ✅ CORRECT
|
||||
<span>GitHub PRs</span> // ❌ WRONG
|
||||
```
|
||||
|
||||
**With the optional Electron frontend** (`auto-claude-ui/`):
|
||||
- Provides a GUI for task management and progress tracking
|
||||
- Wraps the CLI commands - the backend works independently
|
||||
**When adding new UI text:**
|
||||
1. Add the translation key to ALL language files (at minimum: `en/*.json` and `fr/*.json`)
|
||||
2. Use `namespace:section.key` format (e.g., `navigation:items.githubPRs`)
|
||||
3. Never use hardcoded strings in JSX/TSX files
|
||||
|
||||
**Directory layout:**
|
||||
- `auto-claude/` - Python backend/CLI (the framework code)
|
||||
- `auto-claude-ui/` - Optional Electron frontend
|
||||
- `.auto-claude/specs/` - Per-project data (specs, plans, QA reports) - gitignored
|
||||
### End-to-End Testing (Electron App)
|
||||
|
||||
**IMPORTANT: When bug fixing or implementing new features in the frontend, AI agents can perform automated E2E testing using the Electron MCP server.**
|
||||
|
||||
The Electron MCP server allows QA agents to interact with the running Electron app via Chrome DevTools Protocol:
|
||||
|
||||
**Setup:**
|
||||
1. Start the Electron app with remote debugging enabled:
|
||||
```bash
|
||||
npm run dev # Already configured with --remote-debugging-port=9222
|
||||
```
|
||||
|
||||
2. Enable Electron MCP in `apps/backend/.env`:
|
||||
```bash
|
||||
ELECTRON_MCP_ENABLED=true
|
||||
ELECTRON_DEBUG_PORT=9222 # Default port
|
||||
```
|
||||
|
||||
**Available Testing Capabilities:**
|
||||
|
||||
QA agents (`qa_reviewer` and `qa_fixer`) automatically get access to Electron MCP tools:
|
||||
|
||||
1. **Window Management**
|
||||
- `mcp__electron__get_electron_window_info` - Get info about running windows
|
||||
- `mcp__electron__take_screenshot` - Capture screenshots for visual verification
|
||||
|
||||
2. **UI Interaction**
|
||||
- `mcp__electron__send_command_to_electron` with commands:
|
||||
- `click_by_text` - Click buttons/links by visible text
|
||||
- `click_by_selector` - Click elements by CSS selector
|
||||
- `fill_input` - Fill form fields by placeholder or selector
|
||||
- `select_option` - Select dropdown options
|
||||
- `send_keyboard_shortcut` - Send keyboard shortcuts (Enter, Ctrl+N, etc.)
|
||||
- `navigate_to_hash` - Navigate to hash routes (#settings, #create, etc.)
|
||||
|
||||
3. **Page Inspection**
|
||||
- `get_page_structure` - Get organized overview of page elements
|
||||
- `debug_elements` - Get debugging info about buttons and forms
|
||||
- `verify_form_state` - Check form state and validation
|
||||
- `eval` - Execute custom JavaScript code
|
||||
|
||||
4. **Logging**
|
||||
- `mcp__electron__read_electron_logs` - Read console logs for debugging
|
||||
|
||||
**Example E2E Test Flow:**
|
||||
|
||||
```python
|
||||
# 1. Agent takes screenshot to see current state
|
||||
agent: "Take a screenshot to see the current UI"
|
||||
# Uses: mcp__electron__take_screenshot
|
||||
|
||||
# 2. Agent inspects page structure
|
||||
agent: "Get page structure to find available buttons"
|
||||
# Uses: mcp__electron__send_command_to_electron (command: "get_page_structure")
|
||||
|
||||
# 3. Agent clicks a button to navigate
|
||||
agent: "Click the 'Create New Spec' button"
|
||||
# Uses: mcp__electron__send_command_to_electron (command: "click_by_text", args: {text: "Create New Spec"})
|
||||
|
||||
# 4. Agent fills out a form
|
||||
agent: "Fill the task description field"
|
||||
# Uses: mcp__electron__send_command_to_electron (command: "fill_input", args: {placeholder: "Describe your task", value: "Add login feature"})
|
||||
|
||||
# 5. Agent submits and verifies
|
||||
agent: "Click Submit and verify success"
|
||||
# Uses: click_by_text → take_screenshot → verify result
|
||||
```
|
||||
|
||||
**When to Use E2E Testing:**
|
||||
|
||||
- **Bug Fixes**: Reproduce the bug, apply fix, verify it's resolved
|
||||
- **New Features**: Implement feature, test the UI flow end-to-end
|
||||
- **UI Changes**: Verify visual changes and interactions work correctly
|
||||
- **Form Validation**: Test form submission, validation, error handling
|
||||
|
||||
**Configuration in `core/client.py`:**
|
||||
|
||||
The client automatically enables Electron MCP tools for QA agents when:
|
||||
- Project is detected as Electron (`is_electron` capability)
|
||||
- `ELECTRON_MCP_ENABLED=true` is set
|
||||
- Agent type is `qa_reviewer` or `qa_fixer`
|
||||
|
||||
**Note:** Screenshots are automatically compressed (1280x720, quality 60, JPEG) to stay under Claude SDK's 1MB JSON message buffer limit.
|
||||
|
||||
## Running the Application
|
||||
|
||||
**As a standalone CLI tool**:
|
||||
```bash
|
||||
cd apps/backend
|
||||
python run.py --spec 001
|
||||
```
|
||||
|
||||
**With the Electron frontend**:
|
||||
```bash
|
||||
npm start # Build and run desktop app
|
||||
npm run dev # Run in development mode (includes --remote-debugging-port=9222 for E2E testing)
|
||||
```
|
||||
|
||||
**For E2E Testing with QA Agents:**
|
||||
1. Start the Electron app: `npm run dev`
|
||||
2. Enable Electron MCP in `apps/backend/.env`: `ELECTRON_MCP_ENABLED=true`
|
||||
3. Run QA: `python run.py --spec 001 --qa`
|
||||
4. QA agents will automatically interact with the running app for testing
|
||||
|
||||
**Project data storage:**
|
||||
- `.auto-claude/specs/` - Per-project data (specs, plans, QA reports, memory) - gitignored
|
||||
|
||||
+285
-59
@@ -5,6 +5,7 @@ Thank you for your interest in contributing to Auto Claude! This document provid
|
||||
## Table of Contents
|
||||
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Development Setup](#development-setup)
|
||||
- [Python Backend](#python-backend)
|
||||
- [Electron Frontend](#electron-frontend)
|
||||
@@ -14,7 +15,13 @@ Thank you for your interest in contributing to Auto Claude! This document provid
|
||||
- [Testing](#testing)
|
||||
- [Continuous Integration](#continuous-integration)
|
||||
- [Git Workflow](#git-workflow)
|
||||
- [Branch Overview](#branch-overview)
|
||||
- [Main Branches](#main-branches)
|
||||
- [Supporting Branches](#supporting-branches)
|
||||
- [Branch Naming](#branch-naming)
|
||||
- [Where to Branch From](#where-to-branch-from)
|
||||
- [Pull Request Targets](#pull-request-targets)
|
||||
- [Release Process](#release-process-maintainers)
|
||||
- [Commit Messages](#commit-messages)
|
||||
- [Pull Request Process](#pull-request-process)
|
||||
- [Issue Reporting](#issue-reporting)
|
||||
@@ -24,37 +31,77 @@ Thank you for your interest in contributing to Auto Claude! This document provid
|
||||
|
||||
Before contributing, ensure you have the following installed:
|
||||
|
||||
- **Python 3.8+** - For the backend framework
|
||||
- **Node.js 18+** - For the Electron frontend
|
||||
- **pnpm** - Package manager for the frontend (`npm install -g pnpm`)
|
||||
- **Python 3.12+** - For the backend framework
|
||||
- **Node.js 24+** - For the Electron frontend
|
||||
- **npm 10+** - Package manager for the frontend (comes with Node.js)
|
||||
- **uv** (recommended) or **pip** - Python package manager
|
||||
- **Git** - Version control
|
||||
|
||||
### Installing Python 3.12
|
||||
|
||||
**Windows:**
|
||||
```bash
|
||||
winget install Python.Python.3.12
|
||||
```
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
brew install python@3.12
|
||||
```
|
||||
|
||||
**Linux (Ubuntu/Debian):**
|
||||
```bash
|
||||
sudo apt install python3.12 python3.12-venv
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
The fastest way to get started:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/AndyMik90/Auto-Claude.git
|
||||
cd Auto-Claude
|
||||
|
||||
# Install all dependencies (cross-platform)
|
||||
npm run install:all
|
||||
|
||||
# Run in development mode
|
||||
npm run dev
|
||||
|
||||
# Or build and run production
|
||||
npm start
|
||||
```
|
||||
|
||||
## Development Setup
|
||||
|
||||
The project consists of two main components:
|
||||
|
||||
1. **Python Backend** (`auto-claude/`) - The core autonomous coding framework
|
||||
2. **Electron Frontend** (`auto-claude-ui/`) - Optional desktop UI
|
||||
1. **Python Backend** (`apps/backend/`) - The core autonomous coding framework
|
||||
2. **Electron Frontend** (`apps/frontend/`) - Optional desktop UI
|
||||
|
||||
### Python Backend
|
||||
|
||||
The recommended way is to use `npm run install:backend`, but you can also set up manually:
|
||||
|
||||
```bash
|
||||
# Navigate to the auto-claude directory
|
||||
cd auto-claude
|
||||
# Navigate to the backend directory
|
||||
cd apps/backend
|
||||
|
||||
# Create virtual environment (using uv - recommended)
|
||||
uv venv
|
||||
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
||||
uv pip install -r requirements.txt
|
||||
# Create virtual environment
|
||||
# Windows:
|
||||
py -3.12 -m venv .venv
|
||||
.venv\Scripts\activate
|
||||
|
||||
# Or using standard Python
|
||||
python3 -m venv .venv
|
||||
# macOS/Linux:
|
||||
python3.12 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Install test dependencies
|
||||
pip install -r ../tests/requirements-test.txt
|
||||
pip install -r ../../tests/requirements-test.txt
|
||||
|
||||
# Set up environment
|
||||
cp .env.example .env
|
||||
@@ -64,31 +111,31 @@ cp .env.example .env
|
||||
### Electron Frontend
|
||||
|
||||
```bash
|
||||
# Navigate to the UI directory
|
||||
cd auto-claude-ui
|
||||
# Navigate to the frontend directory
|
||||
cd apps/frontend
|
||||
|
||||
# Install dependencies
|
||||
pnpm install
|
||||
npm install
|
||||
|
||||
# Start development server
|
||||
pnpm dev
|
||||
npm run dev
|
||||
|
||||
# Build for production
|
||||
pnpm build
|
||||
npm run build
|
||||
|
||||
# Package for distribution
|
||||
pnpm package
|
||||
npm run package
|
||||
```
|
||||
|
||||
## Running from Source
|
||||
|
||||
If you want to run Auto Claude from source (for development or testing unreleased features), follow these steps:
|
||||
|
||||
### Step 1: Clone and Set Up Python Backend
|
||||
### Step 1: Clone and Set Up
|
||||
|
||||
```bash
|
||||
git clone https://github.com/AndyMik90/Auto-Claude.git
|
||||
cd Auto-Claude/auto-claude
|
||||
cd Auto-Claude/apps/backend
|
||||
|
||||
# Using uv (recommended)
|
||||
uv venv && uv pip install -r requirements.txt
|
||||
@@ -99,6 +146,7 @@ source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Set up environment
|
||||
cd apps/backend
|
||||
cp .env.example .env
|
||||
# Edit .env and add your CLAUDE_CODE_OAUTH_TOKEN (get it via: claude setup-token)
|
||||
```
|
||||
@@ -106,16 +154,16 @@ cp .env.example .env
|
||||
### Step 2: Run the Desktop UI
|
||||
|
||||
```bash
|
||||
cd ../auto-claude-ui
|
||||
cd ../frontend
|
||||
|
||||
# Install dependencies
|
||||
pnpm install
|
||||
npm install
|
||||
|
||||
# Development mode (hot reload)
|
||||
pnpm dev
|
||||
npm run dev
|
||||
|
||||
# Or production build
|
||||
pnpm run build && pnpm run start
|
||||
npm run build && npm run start
|
||||
```
|
||||
|
||||
<details>
|
||||
@@ -126,7 +174,7 @@ Auto Claude automatically downloads prebuilt binaries for Windows. If prebuilts
|
||||
1. Download [Visual Studio Build Tools 2022](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
|
||||
2. Select "Desktop development with C++" workload
|
||||
3. In "Individual Components", add "MSVC v143 - VS 2022 C++ x64/x86 Spectre-mitigated libs"
|
||||
4. Restart terminal and run `pnpm install` again
|
||||
4. Restart terminal and run `npm install` again
|
||||
|
||||
</details>
|
||||
|
||||
@@ -152,10 +200,10 @@ When you commit, the following checks run automatically:
|
||||
|
||||
| Check | Scope | Description |
|
||||
|-------|-------|-------------|
|
||||
| **ruff** | `auto-claude/` | Python linter with auto-fix |
|
||||
| **ruff-format** | `auto-claude/` | Python code formatter |
|
||||
| **eslint** | `auto-claude-ui/` | TypeScript/React linter |
|
||||
| **typecheck** | `auto-claude-ui/` | TypeScript type checking |
|
||||
| **ruff** | `apps/backend/` | Python linter with auto-fix |
|
||||
| **ruff-format** | `apps/backend/` | Python code formatter |
|
||||
| **eslint** | `apps/frontend/` | TypeScript/React linter |
|
||||
| **typecheck** | `apps/frontend/` | TypeScript type checking |
|
||||
| **trailing-whitespace** | All files | Removes trailing whitespace |
|
||||
| **end-of-file-fixer** | All files | Ensures files end with newline |
|
||||
| **check-yaml** | All files | Validates YAML syntax |
|
||||
@@ -212,7 +260,7 @@ def gnc(sd):
|
||||
### TypeScript/React
|
||||
|
||||
- Use TypeScript strict mode
|
||||
- Follow the existing component patterns in `auto-claude-ui/src/`
|
||||
- Follow the existing component patterns in `apps/frontend/src/`
|
||||
- Use functional components with hooks
|
||||
- Prefer named exports over default exports
|
||||
- Use the UI components from `src/renderer/components/ui/`
|
||||
@@ -242,20 +290,25 @@ export default function(props) {
|
||||
### Python Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
pytest tests/ -v
|
||||
# Run all tests (from repository root)
|
||||
npm run test:backend
|
||||
|
||||
# Or manually with pytest
|
||||
cd apps/backend
|
||||
.venv/Scripts/pytest.exe ../tests -v # Windows
|
||||
.venv/bin/pytest ../tests -v # macOS/Linux
|
||||
|
||||
# Run a specific test file
|
||||
pytest tests/test_security.py -v
|
||||
npm run test:backend -- tests/test_security.py -v
|
||||
|
||||
# Run a specific test
|
||||
pytest tests/test_security.py::test_bash_command_validation -v
|
||||
npm run test:backend -- tests/test_security.py::test_bash_command_validation -v
|
||||
|
||||
# Skip slow tests
|
||||
pytest tests/ -m "not slow"
|
||||
npm run test:backend -- -m "not slow"
|
||||
|
||||
# Run with coverage
|
||||
pytest tests/ --cov=auto-claude --cov-report=html
|
||||
pytest tests/ --cov=apps/backend --cov-report=html
|
||||
```
|
||||
|
||||
Test configuration is in `tests/pytest.ini`.
|
||||
@@ -263,26 +316,26 @@ Test configuration is in `tests/pytest.ini`.
|
||||
### Frontend Tests
|
||||
|
||||
```bash
|
||||
cd auto-claude-ui
|
||||
cd apps/frontend
|
||||
|
||||
# Run unit tests
|
||||
pnpm test
|
||||
npm test
|
||||
|
||||
# Run tests in watch mode
|
||||
pnpm test:watch
|
||||
npm run test:watch
|
||||
|
||||
# Run with coverage
|
||||
pnpm test:coverage
|
||||
npm run test:coverage
|
||||
|
||||
# Run E2E tests (requires built app)
|
||||
pnpm build
|
||||
pnpm test:e2e
|
||||
npm run build
|
||||
npm run test:e2e
|
||||
|
||||
# Run linting
|
||||
pnpm lint
|
||||
npm run lint
|
||||
|
||||
# Run type checking
|
||||
pnpm typecheck
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
### Testing Requirements
|
||||
@@ -320,19 +373,50 @@ Before a PR can be merged:
|
||||
|
||||
```bash
|
||||
# Python tests
|
||||
cd auto-claude
|
||||
cd apps/backend
|
||||
source .venv/bin/activate
|
||||
pytest ../tests/ -v
|
||||
pytest ../../tests/ -v
|
||||
|
||||
# Frontend tests
|
||||
cd auto-claude-ui
|
||||
pnpm test
|
||||
pnpm lint
|
||||
pnpm typecheck
|
||||
cd apps/frontend
|
||||
npm test
|
||||
npm run lint
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
## Git Workflow
|
||||
|
||||
We use a **Git Flow** branching strategy to manage releases and parallel development.
|
||||
|
||||
### Branch Overview
|
||||
|
||||
```
|
||||
main (stable) ← Only released, tested code (tagged versions)
|
||||
│
|
||||
develop ← Integration branch - all PRs merge here first
|
||||
│
|
||||
├── feature/xxx ← New features
|
||||
├── fix/xxx ← Bug fixes
|
||||
├── release/vX.Y.Z ← Release preparation
|
||||
└── hotfix/xxx ← Emergency production fixes
|
||||
```
|
||||
|
||||
### Main Branches
|
||||
|
||||
| Branch | Purpose | Protected |
|
||||
|--------|---------|-----------|
|
||||
| `main` | Production-ready code. Only receives merges from `release/*` or `hotfix/*` branches. Every merge is tagged (v2.7.0, v2.8.0, etc.) | ✅ Yes |
|
||||
| `develop` | Integration branch where all features and fixes are combined. This is the default target for all PRs. | ✅ Yes |
|
||||
|
||||
### Supporting Branches
|
||||
|
||||
| Branch Type | Branch From | Merge To | Purpose |
|
||||
|-------------|-------------|----------|---------|
|
||||
| `feature/*` | `develop` | `develop` | New features and enhancements |
|
||||
| `fix/*` | `develop` | `develop` | Bug fixes (non-critical) |
|
||||
| `release/*` | `develop` | `main` + `develop` | Release preparation and final testing |
|
||||
| `hotfix/*` | `main` | `main` + `develop` | Critical production bug fixes |
|
||||
|
||||
### Branch Naming
|
||||
|
||||
Use descriptive branch names with a prefix indicating the type of change:
|
||||
@@ -341,10 +425,146 @@ Use descriptive branch names with a prefix indicating the type of change:
|
||||
|--------|---------|---------|
|
||||
| `feature/` | New feature | `feature/add-dark-mode` |
|
||||
| `fix/` | Bug fix | `fix/memory-leak-in-worker` |
|
||||
| `hotfix/` | Urgent production fix | `hotfix/critical-crash-fix` |
|
||||
| `docs/` | Documentation | `docs/update-readme` |
|
||||
| `refactor/` | Code refactoring | `refactor/simplify-auth-flow` |
|
||||
| `test/` | Test additions/fixes | `test/add-integration-tests` |
|
||||
| `chore/` | Maintenance tasks | `chore/update-dependencies` |
|
||||
| `release/` | Release preparation | `release/v2.8.0` |
|
||||
| `hotfix/` | Emergency fixes | `hotfix/critical-auth-bug` |
|
||||
|
||||
### Where to Branch From
|
||||
|
||||
```bash
|
||||
# For features and bug fixes - ALWAYS branch from develop
|
||||
git checkout develop
|
||||
git pull origin develop
|
||||
git checkout -b feature/my-new-feature
|
||||
|
||||
# For hotfixes only - branch from main
|
||||
git checkout main
|
||||
git pull origin main
|
||||
git checkout -b hotfix/critical-fix
|
||||
```
|
||||
|
||||
### Pull Request Targets
|
||||
|
||||
> ⚠️ **Important:** All PRs should target `develop`, NOT `main`!
|
||||
|
||||
| Your Branch Type | Target Branch |
|
||||
|------------------|---------------|
|
||||
| `feature/*` | `develop` |
|
||||
| `fix/*` | `develop` |
|
||||
| `docs/*` | `develop` |
|
||||
| `refactor/*` | `develop` |
|
||||
| `test/*` | `develop` |
|
||||
| `chore/*` | `develop` |
|
||||
| `hotfix/*` | `main` (maintainers only) |
|
||||
| `release/*` | `main` (maintainers only) |
|
||||
|
||||
### Release Process (Maintainers)
|
||||
|
||||
When ready to release a new version:
|
||||
|
||||
```bash
|
||||
# 1. Create release branch from develop
|
||||
git checkout develop
|
||||
git pull origin develop
|
||||
git checkout -b release/v2.8.0
|
||||
|
||||
# 2. Update version numbers, CHANGELOG, final fixes only
|
||||
# No new features allowed in release branches!
|
||||
|
||||
# 3. Merge to main and tag
|
||||
git checkout main
|
||||
git merge release/v2.8.0
|
||||
git tag v2.8.0
|
||||
git push origin main --tags
|
||||
|
||||
# 4. Merge back to develop (important!)
|
||||
git checkout develop
|
||||
git merge release/v2.8.0
|
||||
git push origin develop
|
||||
|
||||
# 5. Delete release branch
|
||||
git branch -d release/v2.8.0
|
||||
git push origin --delete release/v2.8.0
|
||||
```
|
||||
|
||||
### Beta Release Process (Maintainers)
|
||||
|
||||
Beta releases allow users to test new features before they're included in a stable release. Beta releases are published from the `develop` branch.
|
||||
|
||||
**Creating a Beta Release:**
|
||||
|
||||
1. Go to **Actions** → **Beta Release** workflow in GitHub
|
||||
2. Click **Run workflow**
|
||||
3. Enter the beta version (e.g., `2.8.0-beta.1`)
|
||||
4. Optionally enable dry run to test without publishing
|
||||
5. Click **Run workflow**
|
||||
|
||||
The workflow will:
|
||||
- Validate the version format
|
||||
- Update `package.json` on develop
|
||||
- Create and push a tag (e.g., `v2.8.0-beta.1`)
|
||||
- Build installers for all platforms
|
||||
- Create a GitHub pre-release
|
||||
|
||||
**Version Format:**
|
||||
```
|
||||
X.Y.Z-beta.N (e.g., 2.8.0-beta.1, 2.8.0-beta.2)
|
||||
X.Y.Z-alpha.N (e.g., 2.8.0-alpha.1)
|
||||
X.Y.Z-rc.N (e.g., 2.8.0-rc.1)
|
||||
```
|
||||
|
||||
**For Users:**
|
||||
Users can opt into beta updates in Settings → Updates → "Beta Updates" toggle. When enabled, the app will check for and install beta versions. Users can switch back to stable at any time.
|
||||
|
||||
### Hotfix Workflow
|
||||
|
||||
For urgent production fixes that can't wait for the normal release cycle:
|
||||
|
||||
**1. Create hotfix from main**
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git pull origin main
|
||||
git checkout -b hotfix/150-critical-fix
|
||||
```
|
||||
|
||||
**2. Fix the issue**
|
||||
|
||||
```bash
|
||||
# ... make changes ...
|
||||
git commit -m "hotfix: fix critical crash on startup"
|
||||
```
|
||||
|
||||
**3. Open PR to main (fast-track review)**
|
||||
|
||||
```bash
|
||||
gh pr create --base main --title "hotfix: fix critical crash on startup"
|
||||
```
|
||||
|
||||
**4. After merge to main, sync to develop**
|
||||
|
||||
```bash
|
||||
git checkout develop
|
||||
git pull origin develop
|
||||
git merge main
|
||||
git push origin develop
|
||||
```
|
||||
|
||||
```
|
||||
main ─────●─────●─────●─────●───── (production)
|
||||
↑ ↑ ↑ ↑
|
||||
develop ──●─────●─────●─────●───── (integration)
|
||||
↑ ↑ ↑
|
||||
feature/123 ────●
|
||||
feature/124 ──────────●
|
||||
hotfix/125 ─────────────────●───── (from main, merge to both)
|
||||
```
|
||||
|
||||
> **Note:** Hotfixes branch FROM `main` and merge TO `main` first, then sync back to `develop` to keep branches aligned.
|
||||
|
||||
### Commit Messages
|
||||
|
||||
@@ -378,17 +598,23 @@ git commit -m "WIP"
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
1. **Fork the repository** and create your branch from `main`
|
||||
1. **Fork the repository** and create your branch from `develop` (not main!)
|
||||
|
||||
```bash
|
||||
git checkout develop
|
||||
git pull origin develop
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
|
||||
2. **Make your changes** following the code style guidelines
|
||||
|
||||
3. **Test thoroughly**:
|
||||
```bash
|
||||
# Python
|
||||
pytest tests/ -v
|
||||
# Python (from repository root)
|
||||
npm run test:backend
|
||||
|
||||
# Frontend
|
||||
cd auto-claude-ui && pnpm test && pnpm lint && pnpm typecheck
|
||||
cd apps/frontend && npm test && npm run lint && npm run typecheck
|
||||
```
|
||||
|
||||
4. **Update documentation** if your changes affect:
|
||||
@@ -447,7 +673,7 @@ When requesting a feature:
|
||||
|
||||
Auto Claude consists of two main parts:
|
||||
|
||||
### Python Backend (`auto-claude/`)
|
||||
### Python Backend (`apps/backend/`)
|
||||
|
||||
The core autonomous coding framework:
|
||||
|
||||
@@ -457,9 +683,9 @@ The core autonomous coding framework:
|
||||
- **Memory**: `memory.py` (file-based), `graphiti_memory.py` (graph-based)
|
||||
- **QA**: `qa_loop.py`, `prompts/qa_*.md`
|
||||
|
||||
### Electron Frontend (`auto-claude-ui/`)
|
||||
### Electron Frontend (`apps/frontend/`)
|
||||
|
||||
Optional desktop interface:
|
||||
Desktop interface:
|
||||
|
||||
- **Main Process**: `src/main/` - Electron main process, IPC handlers
|
||||
- **Renderer**: `src/renderer/` - React UI components
|
||||
|
||||
@@ -1,269 +1,222 @@
|
||||
# Auto Claude
|
||||
|
||||
Your AI coding companion. Build features, fix bugs, and ship faster — with autonomous agents that plan, code, and validate for you.
|
||||
**Autonomous multi-agent coding framework that plans, builds, and validates software for you.**
|
||||
|
||||

|
||||
|
||||
[](https://discord.gg/KCXaPBr4Dj)
|
||||
[](https://github.com/AndyMik90/Auto-Claude/releases/latest)
|
||||
[](./agpl-3.0.txt)
|
||||
[](https://discord.gg/KCXaPBr4Dj)
|
||||
[](https://github.com/AndyMik90/Auto-Claude/actions)
|
||||
|
||||
## What It Does ✨
|
||||
---
|
||||
|
||||
**Auto Claude is a desktop app that supercharges your AI coding workflow.** Whether you're a vibe coder just getting started or an experienced developer, Auto Claude meets you where you are.
|
||||
## Download
|
||||
|
||||
- **Autonomous Tasks** — Describe what you want to build, and agents handle planning, coding, and validation while you focus on other work
|
||||
- **Agent Terminals** — Run Claude Code in up to 12 terminals with a clean layout, smart naming based on context, and one-click task context injection
|
||||
- **Safe by Default** — All work happens in git worktrees, keeping your main branch undisturbed until you're ready to merge
|
||||
- **Self-Validating** — Built-in QA agents check their own work before you review
|
||||
Get the latest pre-built release for your platform:
|
||||
|
||||
**The result?** 10x your output while maintaining code quality.
|
||||
| Platform | Download | Notes |
|
||||
|----------|----------|-------|
|
||||
| **Windows** | [Auto-Claude-2.7.2.exe](https://github.com/AndyMik90/Auto-Claude/releases/latest) | Installer (NSIS) |
|
||||
| **macOS (Apple Silicon)** | [Auto-Claude-2.7.2-arm64.dmg](https://github.com/AndyMik90/Auto-Claude/releases/latest) | M1/M2/M3 Macs |
|
||||
| **macOS (Intel)** | [Auto-Claude-2.7.2-x64.dmg](https://github.com/AndyMik90/Auto-Claude/releases/latest) | Intel Macs |
|
||||
| **Linux** | [Auto-Claude-2.7.2.AppImage](https://github.com/AndyMik90/Auto-Claude/releases/latest) | Universal |
|
||||
| **Linux (Debian)** | [Auto-Claude-2.7.2.deb](https://github.com/AndyMik90/Auto-Claude/releases/latest) | Ubuntu/Debian |
|
||||
|
||||
## Key Features
|
||||
> All releases include SHA256 checksums and VirusTotal scan results for security verification.
|
||||
|
||||
- **Parallel Agents**: Run multiple builds simultaneously while you focus on other work
|
||||
- **Context Engineering**: Agents understand your codebase structure before writing code
|
||||
- **Self-Validating**: Built-in QA loop catches issues before you review
|
||||
- **Isolated Workspaces**: All work happens in git worktrees — your code stays safe
|
||||
- **AI Merge Resolution**: Intelligent conflict resolution when merging back to main — no manual conflict fixing
|
||||
- **Cross-Platform**: Desktop app runs on Mac, Windows, and Linux
|
||||
- **Any Project Type**: Build web apps, APIs, CLIs — works with any software project
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Claude Pro/Max subscription** - [Get one here](https://claude.ai/upgrade)
|
||||
- **Claude Code CLI** - `npm install -g @anthropic-ai/claude-code`
|
||||
- **Git repository** - Your project must be initialized as a git repo
|
||||
- **Python 3.12+** - Required for the backend and Memory Layer
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Download Auto Claude
|
||||
|
||||
Download the latest release for your platform from [GitHub Releases](https://github.com/AndyMik90/Auto-Claude/releases/latest):
|
||||
|
||||
| Platform | Download |
|
||||
|----------|----------|
|
||||
| **macOS (Apple Silicon M1-M4)** | `*-arm64.dmg` |
|
||||
| **macOS (Intel)** | `*-x64.dmg` |
|
||||
| **Windows** | `*.exe` |
|
||||
| **Linux** | `*.AppImage` or `*.deb` |
|
||||
|
||||
> **Not sure which Mac?** Click the Apple menu () > "About This Mac". Look for "Chip" - M1/M2/M3/M4 = Apple Silicon, otherwise Intel.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before using Auto Claude, you need:
|
||||
|
||||
1. **Claude Subscription** - Requires [Claude Pro or Max](https://claude.ai/upgrade) for Claude Code access
|
||||
2. **Claude Code CLI** - Install with: `npm install -g @anthropic-ai/claude-code`
|
||||
|
||||
### Install and Run
|
||||
|
||||
1. **Download** the installer for your platform from the table above
|
||||
2. **Install**:
|
||||
- **macOS**: Open the `.dmg`, drag Auto Claude to Applications
|
||||
- **Windows**: Run the `.exe` installer (see note below about security warning)
|
||||
- **Linux**: Make the AppImage executable (`chmod +x`) and run it, or install the `.deb`
|
||||
3. **Launch** Auto Claude
|
||||
4. **Add your project** and start building!
|
||||
|
||||
<details>
|
||||
<summary><b>Windows users:</b> Security warning when installing</summary>
|
||||
|
||||
The Windows installer is not yet code-signed, so you may see a "Windows protected your PC" warning from Microsoft Defender SmartScreen.
|
||||
|
||||
**To proceed:**
|
||||
1. Click "More info"
|
||||
2. Click "Run anyway"
|
||||
|
||||
This is safe — all releases are automatically scanned with VirusTotal before publishing. You can verify any installer by checking the **VirusTotal Scan Results** section in each [release's notes](https://github.com/AndyMik90/Auto-Claude/releases).
|
||||
|
||||
We're working on obtaining a code signing certificate for future releases.
|
||||
|
||||
</details>
|
||||
|
||||
> **Want to build from source?** See [CONTRIBUTING.md](CONTRIBUTING.md#running-from-source) for development setup.
|
||||
1. **Download and install** the app for your platform
|
||||
2. **Open your project** - Select a git repository folder
|
||||
3. **Connect Claude** - The app will guide you through OAuth setup
|
||||
4. **Create a task** - Describe what you want to build
|
||||
5. **Watch it work** - Agents plan, code, and validate autonomously
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Features
|
||||
## Features
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| **Autonomous Tasks** | Describe your goal; agents handle planning, implementation, and validation |
|
||||
| **Parallel Execution** | Run multiple builds simultaneously with up to 12 agent terminals |
|
||||
| **Isolated Workspaces** | All changes happen in git worktrees - your main branch stays safe |
|
||||
| **Self-Validating QA** | Built-in quality assurance loop catches issues before you review |
|
||||
| **AI-Powered Merge** | Automatic conflict resolution when integrating back to main |
|
||||
| **Memory Layer** | Agents retain insights across sessions for smarter builds |
|
||||
| **Cross-Platform** | Native desktop apps for Windows, macOS, and Linux |
|
||||
| **Auto-Updates** | App updates automatically when new versions are released |
|
||||
|
||||
---
|
||||
|
||||
## Interface
|
||||
|
||||
### Kanban Board
|
||||
|
||||
Plan tasks and let AI handle the planning, coding, and validation — all in a visual interface. Track progress from "Planning" to "Done" while agents work autonomously.
|
||||
Visual task management from planning through completion. Create tasks and monitor agent progress in real-time.
|
||||
|
||||
### Agent Terminals
|
||||
AI-powered terminals with one-click task context injection. Spawn multiple agents for parallel work.
|
||||
|
||||
Spawn up to 12 AI-powered terminals for hands-on coding. Inject task context with a click, reference files from your project, and work rapidly across multiple sessions.
|
||||
|
||||
**Power users:** Connect multiple Claude Code subscriptions to run even more agents in parallel — perfect for teams or heavy workloads.
|
||||
|
||||

|
||||
|
||||
### Insights
|
||||
|
||||
Have a conversation about your project in a ChatGPT-style interface. Ask questions, get explanations, and explore your codebase through natural dialogue.
|
||||

|
||||
|
||||
### Roadmap
|
||||
AI-assisted feature planning with competitor analysis and audience targeting.
|
||||
|
||||
Based on your target audience, AI anticipates and plans the most impactful features you should focus on. Prioritize what matters most to your users.
|
||||

|
||||
|
||||

|
||||
|
||||
### Ideation
|
||||
|
||||
Let AI help you create a project that shines. Rapidly understand your codebase and discover:
|
||||
- Code improvements and refactoring opportunities
|
||||
- Performance bottlenecks
|
||||
- Security vulnerabilities
|
||||
- Documentation gaps
|
||||
- UI/UX enhancements
|
||||
- Overall code quality issues
|
||||
|
||||
### Changelog
|
||||
|
||||
Write professional changelogs effortlessly. Generate release notes from completed Auto Claude tasks or integrate with GitHub to create masterclass changelogs automatically.
|
||||
|
||||
### Context
|
||||
|
||||
See exactly what Auto Claude understands about your project — the tech stack, file structure, patterns, and insights it uses to write better code.
|
||||
|
||||
### AI Merge Resolution
|
||||
|
||||
When your main branch evolves while a build is in progress, Auto Claude automatically resolves merge conflicts using AI — no manual `<<<<<<< HEAD` fixing required.
|
||||
|
||||
**How it works:**
|
||||
1. **Git Auto-Merge First** — Simple non-conflicting changes merge instantly without AI
|
||||
2. **Conflict-Only AI** — For actual conflicts, AI receives only the specific conflict regions (not entire files), achieving ~98% prompt reduction
|
||||
3. **Parallel Processing** — Multiple conflicting files resolve simultaneously for faster merges
|
||||
4. **Syntax Validation** — Every merge is validated before being applied
|
||||
|
||||
**The result:** A build that was 50+ commits behind main merges in seconds instead of requiring manual conflict resolution.
|
||||
### Additional Features
|
||||
- **Insights** - Chat interface for exploring your codebase
|
||||
- **Ideation** - Discover improvements, performance issues, and vulnerabilities
|
||||
- **Changelog** - Generate release notes from completed tasks
|
||||
|
||||
---
|
||||
|
||||
## CLI Usage (Terminal-Only)
|
||||
|
||||
For terminal-based workflows, headless servers, or CI/CD integration, see **[guides/CLI-USAGE.md](guides/CLI-USAGE.md)**.
|
||||
|
||||
## ⚙️ How It Works
|
||||
|
||||
Auto Claude focuses on three core principles: **context engineering** (understanding your codebase before writing code), **good coding standards** (following best practices and patterns), and **validation logic** (ensuring code works before you see it).
|
||||
|
||||
### The Agent Pipeline
|
||||
|
||||
**Phase 1: Spec Creation** (3-8 phases based on complexity)
|
||||
|
||||
Before any code is written, agents gather context and create a detailed specification:
|
||||
|
||||
1. **Discovery** — Analyzes your project structure and tech stack
|
||||
2. **Requirements** — Gathers what you want to build through interactive conversation
|
||||
3. **Research** — Validates external integrations against real documentation
|
||||
4. **Context Discovery** — Finds relevant files in your codebase
|
||||
5. **Spec Writer** — Creates a comprehensive specification document
|
||||
6. **Spec Critic** — Self-critiques using extended thinking to find issues early
|
||||
7. **Planner** — Breaks work into subtasks with dependencies
|
||||
8. **Validation** — Ensures all outputs are valid before proceeding
|
||||
|
||||
**Phase 2: Implementation**
|
||||
|
||||
With a validated spec, coding agents execute the plan:
|
||||
|
||||
1. **Planner Agent** — Creates subtask-based implementation plan
|
||||
2. **Coder Agent** — Implements subtasks one-by-one with verification
|
||||
3. **QA Reviewer** — Validates all acceptance criteria
|
||||
4. **QA Fixer** — Fixes issues in a self-healing loop (up to 50 iterations)
|
||||
|
||||
Each session runs with a fresh context window. Progress is tracked via `implementation_plan.json` and Git commits.
|
||||
|
||||
**Phase 3: Merge**
|
||||
|
||||
When you're ready to merge, AI handles any conflicts that arose while you were working:
|
||||
|
||||
1. **Conflict Detection** — Identifies files modified in both main and the build
|
||||
2. **3-Tier Resolution** — Git auto-merge → Conflict-only AI → Full-file AI (fallback)
|
||||
3. **Parallel Merge** — Multiple files resolve simultaneously
|
||||
4. **Staged for Review** — Changes are staged but not committed, so you can review before finalizing
|
||||
|
||||
### 🔒 Security Model
|
||||
|
||||
Three-layer defense keeps your code safe:
|
||||
- **OS Sandbox** — Bash commands run in isolation
|
||||
- **Filesystem Restrictions** — Operations limited to project directory
|
||||
- **Command Allowlist** — Only approved commands based on your project's stack
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
your-project/
|
||||
├── .worktrees/ # Created during build (git-ignored)
|
||||
│ └── auto-claude/ # Isolated workspace for AI coding
|
||||
├── .auto-claude/ # Per-project data (specs, plans, QA reports)
|
||||
│ ├── specs/ # Task specifications
|
||||
│ ├── roadmap/ # Project roadmap
|
||||
│ └── ideation/ # Ideas and planning
|
||||
├── auto-claude/ # Python backend (framework code)
|
||||
│ ├── run.py # Build entry point
|
||||
│ ├── spec_runner.py # Spec creation orchestrator
|
||||
│ ├── prompts/ # Agent prompt templates
|
||||
│ └── ...
|
||||
└── auto-claude-ui/ # Electron desktop application
|
||||
└── ...
|
||||
Auto-Claude/
|
||||
├── apps/
|
||||
│ ├── backend/ # Python agents, specs, QA pipeline
|
||||
│ └── frontend/ # Electron desktop application
|
||||
├── guides/ # Additional documentation
|
||||
├── tests/ # Test suite
|
||||
└── scripts/ # Build utilities
|
||||
```
|
||||
|
||||
### Understanding the Folders
|
||||
---
|
||||
|
||||
**You don't create these folders manually** - they serve different purposes:
|
||||
## CLI Usage
|
||||
|
||||
- **`auto-claude/`** - The framework repository itself (clone this once from GitHub)
|
||||
- **`.auto-claude/`** - Created automatically in YOUR project when you run Auto Claude (stores specs, plans, QA reports)
|
||||
- **`.worktrees/`** - Temporary isolated workspaces created during builds (git-ignored, deleted after merge)
|
||||
For headless operation, CI/CD integration, or terminal-only workflows:
|
||||
|
||||
**When using Auto Claude on your project:**
|
||||
```bash
|
||||
cd your-project/ # Your own project directory
|
||||
python /path/to/auto-claude/run.py --spec 001
|
||||
# Auto Claude creates .auto-claude/ automatically in your-project/
|
||||
cd apps/backend
|
||||
|
||||
# Create a spec interactively
|
||||
python spec_runner.py --interactive
|
||||
|
||||
# Run autonomous build
|
||||
python run.py --spec 001
|
||||
|
||||
# Review and merge
|
||||
python run.py --spec 001 --review
|
||||
python run.py --spec 001 --merge
|
||||
```
|
||||
|
||||
**When developing Auto Claude itself:**
|
||||
See [guides/CLI-USAGE.md](guides/CLI-USAGE.md) for complete CLI documentation.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Create `apps/backend/.env` from the example:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yourusername/auto-claude
|
||||
cd auto-claude/ # You're working in the framework repo
|
||||
cp apps/backend/.env.example apps/backend/.env
|
||||
```
|
||||
|
||||
The `.auto-claude/` directory is gitignored and project-specific - you'll have one per project you use Auto Claude on.
|
||||
|
||||
## Environment Variables (CLI Only)
|
||||
|
||||
> **Desktop UI users:** These are configured through the app settings — no manual setup needed.
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `CLAUDE_CODE_OAUTH_TOKEN` | Yes | OAuth token from `claude setup-token` |
|
||||
| `AUTO_BUILD_MODEL` | No | Model override (default: claude-opus-4-5-20251101) |
|
||||
| `GRAPHITI_ENABLED` | No | Enable Memory Layer for cross-session context |
|
||||
| `AUTO_BUILD_MODEL` | No | Override the default Claude model |
|
||||
|
||||
See `auto-claude/.env.example` for complete configuration options.
|
||||
---
|
||||
|
||||
## 💬 Community
|
||||
## Building from Source
|
||||
|
||||
Join our Discord to get help, share what you're building, and connect with other Auto Claude users:
|
||||
For contributors and development:
|
||||
|
||||
[](https://discord.gg/KCXaPBr4Dj)
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/AndyMik90/Auto-Claude.git
|
||||
cd Auto-Claude
|
||||
|
||||
## 🤝 Contributing
|
||||
# Install all dependencies
|
||||
npm run install:all
|
||||
|
||||
We welcome contributions! Whether it's bug fixes, new features, or documentation improvements.
|
||||
# Run in development mode
|
||||
npm run dev
|
||||
|
||||
See **[CONTRIBUTING.md](CONTRIBUTING.md)** for guidelines on how to get started.
|
||||
# Or build and run
|
||||
npm start
|
||||
```
|
||||
|
||||
## Acknowledgments
|
||||
**System requirements for building:**
|
||||
- Node.js 24+
|
||||
- Python 3.12+
|
||||
- npm 10+
|
||||
|
||||
This framework was inspired by Anthropic's [Autonomous Coding Agent](https://github.com/anthropics/claude-quickstarts/tree/main/autonomous-coding). Thank you to the Anthropic team for their innovative work on autonomous coding systems.
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed development setup.
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
Auto Claude uses a three-layer security model:
|
||||
|
||||
1. **OS Sandbox** - Bash commands run in isolation
|
||||
2. **Filesystem Restrictions** - Operations limited to project directory
|
||||
3. **Dynamic Command Allowlist** - Only approved commands based on detected project stack
|
||||
|
||||
All releases are:
|
||||
- Scanned with VirusTotal before publishing
|
||||
- Include SHA256 checksums for verification
|
||||
- Code-signed where applicable (macOS)
|
||||
|
||||
---
|
||||
|
||||
## Available Scripts
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `npm run install:all` | Install backend and frontend dependencies |
|
||||
| `npm start` | Build and run the desktop app |
|
||||
| `npm run dev` | Run in development mode with hot reload |
|
||||
| `npm run package` | Package for current platform |
|
||||
| `npm run package:mac` | Package for macOS |
|
||||
| `npm run package:win` | Package for Windows |
|
||||
| `npm run package:linux` | Package for Linux |
|
||||
| `npm run lint` | Run linter |
|
||||
| `npm test` | Run frontend tests |
|
||||
| `npm run test:backend` | Run backend tests |
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for:
|
||||
- Development setup instructions
|
||||
- Code style guidelines
|
||||
- Testing requirements
|
||||
- Pull request process
|
||||
|
||||
---
|
||||
|
||||
## Community
|
||||
|
||||
- **Discord** - [Join our community](https://discord.gg/KCXaPBr4Dj)
|
||||
- **Issues** - [Report bugs or request features](https://github.com/AndyMik90/Auto-Claude/issues)
|
||||
- **Discussions** - [Ask questions](https://github.com/AndyMik90/Auto-Claude/discussions)
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
**AGPL-3.0** - GNU Affero General Public License v3.0
|
||||
|
||||
This software is licensed under AGPL-3.0, which means:
|
||||
Auto Claude is free to use. If you modify and distribute it, or run it as a service, your code must also be open source under AGPL-3.0.
|
||||
|
||||
- **Attribution Required**: You must give appropriate credit, provide a link to the license, and indicate if changes were made. When using Auto Claude, please credit the project.
|
||||
- **Open Source Required**: If you modify this software and distribute it or run it as a service, you must release your source code under AGPL-3.0.
|
||||
- **Network Use (Copyleft)**: If you run this software as a network service (e.g., SaaS), users interacting with it over a network must be able to receive the source code.
|
||||
- **No Closed-Source Usage**: You cannot use this software in proprietary/closed-source projects without open-sourcing your entire project under AGPL-3.0.
|
||||
|
||||
**In simple terms**: You can use Auto Claude freely, but if you build on it, your code must also be open source under AGPL-3.0 and attribute this project. Closed-source commercial use requires a separate license.
|
||||
|
||||
For commercial licensing inquiries (closed-source usage), please contact the maintainers.
|
||||
Commercial licensing available for closed-source use cases.
|
||||
|
||||
+163
-161
@@ -1,186 +1,188 @@
|
||||
# Release Process
|
||||
|
||||
This document describes how to create a new release of Auto Claude.
|
||||
This document describes how releases are created for Auto Claude.
|
||||
|
||||
## Automated Release Process (Recommended)
|
||||
## Overview
|
||||
|
||||
We provide an automated script that handles version bumping, git commits, and tagging to ensure version consistency.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Clean git working directory (no uncommitted changes)
|
||||
- You're on the branch you want to release from (usually `main`)
|
||||
|
||||
### Steps
|
||||
|
||||
1. **Run the version bump script:**
|
||||
|
||||
```bash
|
||||
# Bump patch version (2.5.5 -> 2.5.6)
|
||||
node scripts/bump-version.js patch
|
||||
|
||||
# Bump minor version (2.5.5 -> 2.6.0)
|
||||
node scripts/bump-version.js minor
|
||||
|
||||
# Bump major version (2.5.5 -> 3.0.0)
|
||||
node scripts/bump-version.js major
|
||||
|
||||
# Set specific version
|
||||
node scripts/bump-version.js 2.6.0
|
||||
```
|
||||
|
||||
This script will:
|
||||
- ✅ Update `auto-claude-ui/package.json` with the new version
|
||||
- ✅ Create a git commit with the version change
|
||||
- ✅ Create a git tag (e.g., `v2.5.6`)
|
||||
- ⚠️ **NOT** push to remote (you control when to push)
|
||||
|
||||
2. **Review the changes:**
|
||||
|
||||
```bash
|
||||
git log -1 # View the commit
|
||||
git show v2.5.6 # View the tag
|
||||
```
|
||||
|
||||
3. **Push to GitHub:**
|
||||
|
||||
```bash
|
||||
# Push the commit
|
||||
git push origin main
|
||||
|
||||
# Push the tag
|
||||
git push origin v2.5.6
|
||||
```
|
||||
|
||||
4. **Create GitHub Release:**
|
||||
|
||||
- Go to [GitHub Releases](https://github.com/AndyMik90/Auto-Claude/releases)
|
||||
- Click "Draft a new release"
|
||||
- Select the tag you just pushed (e.g., `v2.5.6`)
|
||||
- Add release notes (describe what changed)
|
||||
- Click "Publish release"
|
||||
|
||||
5. **Automated builds will trigger:**
|
||||
|
||||
- ✅ Version validation workflow will verify version consistency
|
||||
- ✅ Tests will run (`test-on-tag.yml`)
|
||||
- ✅ Native module prebuilds will be created (`build-prebuilds.yml`)
|
||||
- ✅ Discord notification will be sent (`discord-release.yml`)
|
||||
|
||||
## Manual Release Process (Not Recommended)
|
||||
|
||||
If you need to create a release manually, follow these steps **carefully** to avoid version mismatches:
|
||||
|
||||
1. **Update `auto-claude-ui/package.json`:**
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "2.5.6"
|
||||
}
|
||||
```
|
||||
|
||||
2. **Commit the change:**
|
||||
|
||||
```bash
|
||||
git add auto-claude-ui/package.json
|
||||
git commit -m "chore: bump version to 2.5.6"
|
||||
```
|
||||
|
||||
3. **Create and push tag:**
|
||||
|
||||
```bash
|
||||
git tag -a v2.5.6 -m "Release v2.5.6"
|
||||
git push origin main
|
||||
git push origin v2.5.6
|
||||
```
|
||||
|
||||
4. **Create GitHub Release** (same as step 4 above)
|
||||
|
||||
## Version Validation
|
||||
|
||||
A GitHub Action automatically validates that the version in `package.json` matches the git tag.
|
||||
|
||||
If there's a mismatch, the workflow will **fail** with a clear error message:
|
||||
Auto Claude uses an automated release pipeline that ensures releases are only published after all builds succeed. This prevents version mismatches between documentation and actual releases.
|
||||
|
||||
```
|
||||
❌ ERROR: Version mismatch detected!
|
||||
|
||||
The version in package.json (2.5.0) does not match
|
||||
the git tag version (2.5.5).
|
||||
|
||||
To fix this:
|
||||
1. Delete this tag: git tag -d v2.5.5
|
||||
2. Update package.json version to 2.5.5
|
||||
3. Commit the change
|
||||
4. Recreate the tag: git tag -a v2.5.5 -m 'Release v2.5.5'
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ RELEASE FLOW │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ develop branch main branch │
|
||||
│ ────────────── ─────────── │
|
||||
│ │ │ │
|
||||
│ │ 1. bump-version.js │ │
|
||||
│ │ (creates commit) │ │
|
||||
│ │ │ │
|
||||
│ ▼ │ │
|
||||
│ ┌─────────┐ │ │
|
||||
│ │ v2.8.0 │ 2. Create PR │ │
|
||||
│ │ commit │ ────────────────────► │ │
|
||||
│ └─────────┘ │ │
|
||||
│ │ │
|
||||
│ 3. Merge PR ▼ │
|
||||
│ ┌──────────┐ │
|
||||
│ │ v2.8.0 │ │
|
||||
│ │ on main │ │
|
||||
│ └────┬─────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────────┴───────────────────┐ │
|
||||
│ │ GitHub Actions (automatic) │ │
|
||||
│ ├───────────────────────────────────────┤ │
|
||||
│ │ 4. prepare-release.yml │ │
|
||||
│ │ - Detects version > latest tag │ │
|
||||
│ │ - Creates tag v2.8.0 │ │
|
||||
│ │ │ │
|
||||
│ │ 5. release.yml (triggered by tag) │ │
|
||||
│ │ - Builds macOS (Intel + ARM) │ │
|
||||
│ │ - Builds Windows │ │
|
||||
│ │ - Builds Linux │ │
|
||||
│ │ - Generates changelog │ │
|
||||
│ │ - Creates GitHub release │ │
|
||||
│ │ - Updates README │ │
|
||||
│ └───────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
This validation ensures we never ship a release where the updater shows the wrong version.
|
||||
## For Maintainers: Creating a Release
|
||||
|
||||
## Troubleshooting
|
||||
### Step 1: Bump the Version
|
||||
|
||||
### Version Mismatch Error
|
||||
|
||||
If you see a version mismatch error in GitHub Actions:
|
||||
|
||||
1. **Delete the incorrect tag:**
|
||||
```bash
|
||||
git tag -d v2.5.6 # Delete locally
|
||||
git push origin :refs/tags/v2.5.6 # Delete remotely
|
||||
```
|
||||
|
||||
2. **Use the automated script:**
|
||||
```bash
|
||||
node scripts/bump-version.js 2.5.6
|
||||
git push origin main
|
||||
git push origin v2.5.6
|
||||
```
|
||||
|
||||
### Git Working Directory Not Clean
|
||||
|
||||
If the version bump script fails with "Git working directory is not clean":
|
||||
On your development branch (typically `develop` or a feature branch):
|
||||
|
||||
```bash
|
||||
# Commit or stash your changes first
|
||||
git status
|
||||
git add .
|
||||
git commit -m "your changes"
|
||||
# Navigate to project root
|
||||
cd /path/to/auto-claude
|
||||
|
||||
# Then run the version bump script
|
||||
node scripts/bump-version.js patch
|
||||
# Bump version (choose one)
|
||||
node scripts/bump-version.js patch # 2.7.1 -> 2.7.2 (bug fixes)
|
||||
node scripts/bump-version.js minor # 2.7.1 -> 2.8.0 (new features)
|
||||
node scripts/bump-version.js major # 2.7.1 -> 3.0.0 (breaking changes)
|
||||
node scripts/bump-version.js 2.8.0 # Set specific version
|
||||
```
|
||||
|
||||
## Release Checklist
|
||||
This will:
|
||||
- Update `apps/frontend/package.json`
|
||||
- Update `package.json` (root)
|
||||
- Update `apps/backend/__init__.py`
|
||||
- Create a commit with message `chore: bump version to X.Y.Z`
|
||||
|
||||
Use this checklist when creating a new release:
|
||||
### Step 2: Push and Create PR
|
||||
|
||||
- [ ] All tests passing on main branch
|
||||
- [ ] CHANGELOG updated (if applicable)
|
||||
- [ ] Run `node scripts/bump-version.js <type>`
|
||||
- [ ] Review commit and tag
|
||||
- [ ] Push commit and tag to GitHub
|
||||
- [ ] Create GitHub Release with release notes
|
||||
- [ ] Verify version validation passed
|
||||
- [ ] Verify builds completed successfully
|
||||
- [ ] Test the updater shows correct version
|
||||
```bash
|
||||
# Push your branch
|
||||
git push origin your-branch
|
||||
|
||||
## What Gets Released
|
||||
# Create PR to main (via GitHub UI or gh CLI)
|
||||
gh pr create --base main --title "Release v2.8.0"
|
||||
```
|
||||
|
||||
When you create a release, the following are built and published:
|
||||
### Step 3: Merge to Main
|
||||
|
||||
1. **Native module prebuilds** - Windows node-pty binaries
|
||||
2. **Electron app packages** - Desktop installers (triggered manually or via electron-builder)
|
||||
3. **Discord notification** - Sent to the Auto Claude community
|
||||
Once the PR is approved and merged to `main`, GitHub Actions will automatically:
|
||||
|
||||
1. **Detect the version bump** (`prepare-release.yml`)
|
||||
2. **Create a git tag** (e.g., `v2.8.0`)
|
||||
3. **Trigger the release workflow** (`release.yml`)
|
||||
4. **Build binaries** for all platforms:
|
||||
- macOS Intel (x64) - code signed & notarized
|
||||
- macOS Apple Silicon (arm64) - code signed & notarized
|
||||
- Windows (NSIS installer) - code signed
|
||||
- Linux (AppImage + .deb)
|
||||
5. **Generate changelog** from merged PRs (using release-drafter)
|
||||
6. **Scan binaries** with VirusTotal
|
||||
7. **Create GitHub release** with all artifacts
|
||||
8. **Update README** with new version badge and download links
|
||||
|
||||
### Step 4: Verify
|
||||
|
||||
After merging, check:
|
||||
- [GitHub Actions](https://github.com/AndyMik90/Auto-Claude/actions) - ensure all workflows pass
|
||||
- [Releases](https://github.com/AndyMik90/Auto-Claude/releases) - verify release was created
|
||||
- [README](https://github.com/AndyMik90/Auto-Claude#download) - confirm version updated
|
||||
|
||||
## Version Numbering
|
||||
|
||||
We follow [Semantic Versioning (SemVer)](https://semver.org/):
|
||||
We follow [Semantic Versioning](https://semver.org/):
|
||||
|
||||
- **MAJOR** version (X.0.0) - Breaking changes
|
||||
- **MINOR** version (0.X.0) - New features (backward compatible)
|
||||
- **PATCH** version (0.0.X) - Bug fixes (backward compatible)
|
||||
- **MAJOR** (X.0.0): Breaking changes, incompatible API changes
|
||||
- **MINOR** (0.X.0): New features, backwards compatible
|
||||
- **PATCH** (0.0.X): Bug fixes, backwards compatible
|
||||
|
||||
Examples:
|
||||
- `2.5.5 -> 2.5.6` - Bug fix
|
||||
- `2.5.6 -> 2.6.0` - New feature
|
||||
- `2.6.0 -> 3.0.0` - Breaking change
|
||||
## Changelog Generation
|
||||
|
||||
Changelogs are automatically generated from merged PRs using [Release Drafter](https://github.com/release-drafter/release-drafter).
|
||||
|
||||
### PR Labels for Changelog Categories
|
||||
|
||||
| Label | Category |
|
||||
|-------|----------|
|
||||
| `feature`, `enhancement` | New Features |
|
||||
| `bug`, `fix` | Bug Fixes |
|
||||
| `improvement`, `refactor` | Improvements |
|
||||
| `documentation` | Documentation |
|
||||
| (any other) | Other Changes |
|
||||
|
||||
**Tip:** Add appropriate labels to your PRs for better changelog organization.
|
||||
|
||||
## Workflows
|
||||
|
||||
| Workflow | Trigger | Purpose |
|
||||
|----------|---------|---------|
|
||||
| `prepare-release.yml` | Push to `main` | Detects version bump, creates tag |
|
||||
| `release.yml` | Tag `v*` pushed | Builds binaries, creates release |
|
||||
| `validate-version.yml` | Tag `v*` pushed | Validates tag matches package.json |
|
||||
| `update-readme` (in release.yml) | After release | Updates README with new version |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Release didn't trigger after merge
|
||||
|
||||
1. Check if version in `package.json` is greater than latest tag:
|
||||
```bash
|
||||
git tag -l 'v*' --sort=-version:refname | head -1
|
||||
cat apps/frontend/package.json | grep version
|
||||
```
|
||||
|
||||
2. Ensure the merge commit touched `package.json`:
|
||||
```bash
|
||||
git diff HEAD~1 --name-only | grep package.json
|
||||
```
|
||||
|
||||
### Build failed after tag was created
|
||||
|
||||
- The release won't be published if builds fail
|
||||
- Fix the issue and create a new patch version
|
||||
- Don't reuse failed version numbers
|
||||
|
||||
### README shows wrong version
|
||||
|
||||
- README is only updated after successful release
|
||||
- If release failed, README keeps the previous version (this is intentional)
|
||||
- Once you successfully release, README will update automatically
|
||||
|
||||
## Manual Release (Emergency Only)
|
||||
|
||||
In rare cases where you need to bypass the automated flow:
|
||||
|
||||
```bash
|
||||
# Create tag manually (NOT RECOMMENDED)
|
||||
git tag -a v2.8.0 -m "Release v2.8.0"
|
||||
git push origin v2.8.0
|
||||
|
||||
# This will trigger release.yml directly
|
||||
```
|
||||
|
||||
**Warning:** Only do this if you're certain the version in package.json matches the tag.
|
||||
|
||||
## Security
|
||||
|
||||
- All macOS binaries are code signed with Apple Developer certificate
|
||||
- All macOS binaries are notarized by Apple
|
||||
- Windows binaries are code signed
|
||||
- All binaries are scanned with VirusTotal
|
||||
- SHA256 checksums are generated for all artifacts
|
||||
|
||||
@@ -117,9 +117,9 @@
|
||||
# ELECTRON_DEBUG_PORT=9222
|
||||
|
||||
# =============================================================================
|
||||
# GRAPHITI MEMORY INTEGRATION (OPTIONAL)
|
||||
# GRAPHITI MEMORY INTEGRATION (REQUIRED)
|
||||
# =============================================================================
|
||||
# Enable Graphiti-based persistent memory layer for cross-session context
|
||||
# Graphiti-based persistent memory layer for cross-session context
|
||||
# retention. Uses LadybugDB as the embedded graph database.
|
||||
#
|
||||
# REQUIREMENTS:
|
||||
@@ -133,8 +133,8 @@
|
||||
# - Ollama (local, fully offline)
|
||||
# - Google AI (Gemini)
|
||||
|
||||
# Enable Graphiti integration (default: false)
|
||||
# GRAPHITI_ENABLED=true
|
||||
# Graphiti is enabled by default. Set to false to disable memory features.
|
||||
GRAPHITI_ENABLED=true
|
||||
|
||||
# =============================================================================
|
||||
# GRAPHITI: Database Settings
|
||||
@@ -153,10 +153,10 @@
|
||||
# Choose which providers to use for LLM and embeddings.
|
||||
# Default is "openai" for both.
|
||||
|
||||
# LLM provider: openai | anthropic | azure_openai | ollama | google
|
||||
# LLM provider: openai | anthropic | azure_openai | ollama | google | openrouter
|
||||
# GRAPHITI_LLM_PROVIDER=openai
|
||||
|
||||
# Embedder provider: openai | voyage | azure_openai | ollama | google
|
||||
# Embedder provider: openai | voyage | azure_openai | ollama | google | openrouter
|
||||
# GRAPHITI_EMBEDDER_PROVIDER=openai
|
||||
|
||||
# =============================================================================
|
||||
@@ -221,6 +221,28 @@
|
||||
# Google Embedding Model (default: text-embedding-004)
|
||||
# GOOGLE_EMBEDDING_MODEL=text-embedding-004
|
||||
|
||||
# =============================================================================
|
||||
# GRAPHITI: OpenRouter Provider (Multi-provider aggregator)
|
||||
# =============================================================================
|
||||
# Use OpenRouter to access multiple LLM providers through a single API.
|
||||
# OpenRouter provides access to Anthropic, OpenAI, Google, and many other models.
|
||||
# Get API key from: https://openrouter.ai/keys
|
||||
#
|
||||
# Required: OPENROUTER_API_KEY
|
||||
|
||||
# OpenRouter API Key
|
||||
# OPENROUTER_API_KEY=sk-or-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
||||
# OpenRouter Base URL (default: https://openrouter.ai/api/v1)
|
||||
# OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
|
||||
|
||||
# OpenRouter LLM Model (default: anthropic/claude-3.5-sonnet)
|
||||
# Popular choices: anthropic/claude-3.5-sonnet, openai/gpt-4o, google/gemini-2.0-flash
|
||||
# OPENROUTER_LLM_MODEL=anthropic/claude-3.5-sonnet
|
||||
|
||||
# OpenRouter Embedding Model (default: openai/text-embedding-3-small)
|
||||
# OPENROUTER_EMBEDDING_MODEL=openai/text-embedding-3-small
|
||||
|
||||
# =============================================================================
|
||||
# GRAPHITI: Azure OpenAI Provider
|
||||
# =============================================================================
|
||||
@@ -307,3 +329,11 @@
|
||||
# GRAPHITI_LLM_PROVIDER=google
|
||||
# GRAPHITI_EMBEDDER_PROVIDER=google
|
||||
# GOOGLE_API_KEY=AIzaSyxxxxxxxx
|
||||
#
|
||||
# --- Example 6: OpenRouter (multi-provider aggregator) ---
|
||||
# GRAPHITI_ENABLED=true
|
||||
# GRAPHITI_LLM_PROVIDER=openrouter
|
||||
# GRAPHITI_EMBEDDER_PROVIDER=openrouter
|
||||
# OPENROUTER_API_KEY=sk-or-xxxxxxxx
|
||||
# OPENROUTER_LLM_MODEL=anthropic/claude-3.5-sonnet
|
||||
# OPENROUTER_EMBEDDING_MODEL=openai/text-embedding-3-small
|
||||
@@ -61,3 +61,6 @@ Thumbs.db
|
||||
|
||||
# Tests (development only)
|
||||
tests/
|
||||
|
||||
# Auto Claude data directory
|
||||
.auto-claude/
|
||||
@@ -0,0 +1,120 @@
|
||||
# Auto Claude Backend
|
||||
|
||||
Autonomous coding framework powered by Claude AI. Builds software features through coordinated multi-agent sessions.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### 1. Install
|
||||
|
||||
```bash
|
||||
cd apps/backend
|
||||
python -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 2. Configure
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Set your Claude API token in `.env`:
|
||||
```
|
||||
CLAUDE_CODE_OAUTH_TOKEN=your-token-here
|
||||
```
|
||||
|
||||
Get your token by running: `claude setup-token`
|
||||
|
||||
### 3. Run
|
||||
|
||||
```bash
|
||||
# List available specs
|
||||
python run.py --list
|
||||
|
||||
# Run a spec
|
||||
python run.py --spec 001
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.10+
|
||||
- Claude API token
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `--list` | List all specs |
|
||||
| `--spec 001` | Run spec 001 |
|
||||
| `--spec 001 --isolated` | Run in isolated workspace |
|
||||
| `--spec 001 --direct` | Run directly in repo |
|
||||
| `--spec 001 --merge` | Merge completed build |
|
||||
| `--spec 001 --review` | Review build changes |
|
||||
| `--spec 001 --discard` | Discard build |
|
||||
| `--spec 001 --qa` | Run QA validation |
|
||||
| `--list-worktrees` | List all worktrees |
|
||||
| `--help` | Show all options |
|
||||
|
||||
## Configuration
|
||||
|
||||
Optional `.env` settings:
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `AUTO_BUILD_MODEL` | Override Claude model |
|
||||
| `DEBUG=true` | Enable debug logging |
|
||||
| `LINEAR_API_KEY` | Enable Linear integration |
|
||||
| `GRAPHITI_ENABLED=true` | Enable memory system |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"tree-sitter not available"** - Safe to ignore, uses regex fallback.
|
||||
|
||||
**Missing module errors** - Run `python -m pip install -r requirements.txt`
|
||||
|
||||
**Debug mode** - Set `DEBUG=true DEBUG_LEVEL=2` before running.
|
||||
|
||||
---
|
||||
|
||||
## For Developers
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
backend/
|
||||
├── agents/ # AI agent execution
|
||||
├── analysis/ # Code analysis
|
||||
├── cli/ # Command-line interface
|
||||
├── core/ # Core utilities
|
||||
├── integrations/ # External services (Linear, Graphiti)
|
||||
├── merge/ # Git merge handling
|
||||
├── project/ # Project detection
|
||||
├── prompts/ # Prompt templates
|
||||
├── qa/ # QA validation
|
||||
├── spec/ # Spec management
|
||||
└── ui/ # Terminal UI
|
||||
```
|
||||
|
||||
### Design Principles
|
||||
|
||||
- **SOLID** - Single responsibility, clean interfaces
|
||||
- **DRY** - Shared utilities in `core/`
|
||||
- **KISS** - Simple flat imports via facade modules
|
||||
|
||||
### Import Convention
|
||||
|
||||
```python
|
||||
# Use facade modules for clean imports
|
||||
from debug import debug, debug_error
|
||||
from progress import count_subtasks
|
||||
from workspace import setup_workspace
|
||||
```
|
||||
|
||||
### Adding Features
|
||||
|
||||
1. Create module in appropriate folder
|
||||
2. Export API in `__init__.py`
|
||||
3. Add facade module at root if commonly imported
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0
|
||||
@@ -0,0 +1,23 @@
|
||||
"""
|
||||
Auto Claude Backend - Autonomous Coding Framework
|
||||
==================================================
|
||||
|
||||
Multi-agent autonomous coding framework that builds software through
|
||||
coordinated AI agent sessions.
|
||||
|
||||
This package provides:
|
||||
- Autonomous agent execution for building features from specs
|
||||
- Workspace isolation via git worktrees
|
||||
- QA validation loops
|
||||
- Memory management (Graphiti + file-based)
|
||||
- Linear integration for project management
|
||||
|
||||
Quick Start:
|
||||
python run.py --spec 001 # Run a spec
|
||||
python run.py --list # List all specs
|
||||
|
||||
See README.md for full documentation.
|
||||
"""
|
||||
|
||||
__version__ = "2.7.2"
|
||||
__author__ = "Auto Claude Team"
|
||||
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
Agents Module
|
||||
=============
|
||||
|
||||
Modular agent system for autonomous coding.
|
||||
|
||||
This module provides:
|
||||
- run_autonomous_agent: Main coder agent loop
|
||||
- run_followup_planner: Follow-up planner for completed specs
|
||||
- Memory management (Graphiti + file-based fallback)
|
||||
- Session management and post-processing
|
||||
- Utility functions for git and plan management
|
||||
|
||||
Uses lazy imports to avoid circular dependencies.
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
# Main API
|
||||
"run_autonomous_agent",
|
||||
"run_followup_planner",
|
||||
# Memory
|
||||
"debug_memory_system_status",
|
||||
"get_graphiti_context",
|
||||
"save_session_memory",
|
||||
"save_session_to_graphiti",
|
||||
# Session
|
||||
"run_agent_session",
|
||||
"post_session_processing",
|
||||
# Utils
|
||||
"get_latest_commit",
|
||||
"get_commit_count",
|
||||
"load_implementation_plan",
|
||||
"find_subtask_in_plan",
|
||||
"find_phase_for_subtask",
|
||||
"sync_plan_to_source",
|
||||
# Constants
|
||||
"AUTO_CONTINUE_DELAY_SECONDS",
|
||||
"HUMAN_INTERVENTION_FILE",
|
||||
]
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
"""Lazy imports to avoid circular dependencies."""
|
||||
if name in ("AUTO_CONTINUE_DELAY_SECONDS", "HUMAN_INTERVENTION_FILE"):
|
||||
from .base import AUTO_CONTINUE_DELAY_SECONDS, HUMAN_INTERVENTION_FILE
|
||||
|
||||
return locals()[name]
|
||||
elif name == "run_autonomous_agent":
|
||||
from .coder import run_autonomous_agent
|
||||
|
||||
return run_autonomous_agent
|
||||
elif name in (
|
||||
"debug_memory_system_status",
|
||||
"get_graphiti_context",
|
||||
"save_session_memory",
|
||||
"save_session_to_graphiti",
|
||||
):
|
||||
from .memory_manager import (
|
||||
debug_memory_system_status,
|
||||
get_graphiti_context,
|
||||
save_session_memory,
|
||||
save_session_to_graphiti,
|
||||
)
|
||||
|
||||
return locals()[name]
|
||||
elif name == "run_followup_planner":
|
||||
from .planner import run_followup_planner
|
||||
|
||||
return run_followup_planner
|
||||
elif name in ("post_session_processing", "run_agent_session"):
|
||||
from .session import post_session_processing, run_agent_session
|
||||
|
||||
return locals()[name]
|
||||
elif name in (
|
||||
"find_phase_for_subtask",
|
||||
"find_subtask_in_plan",
|
||||
"get_commit_count",
|
||||
"get_latest_commit",
|
||||
"load_implementation_plan",
|
||||
"sync_plan_to_source",
|
||||
):
|
||||
from .utils import (
|
||||
find_phase_for_subtask,
|
||||
find_subtask_in_plan,
|
||||
get_commit_count,
|
||||
get_latest_commit,
|
||||
load_implementation_plan,
|
||||
sync_plan_to_source,
|
||||
)
|
||||
|
||||
return locals()[name]
|
||||
raise AttributeError(f"module 'agents' has no attribute '{name}'")
|
||||
+6
@@ -77,6 +77,12 @@ def get_allowed_tools(
|
||||
TOOL_GET_SESSION_CONTEXT,
|
||||
],
|
||||
},
|
||||
"pr_reviewer": {
|
||||
# PR reviewers can ONLY read - no bash, no edits, no writes
|
||||
# This prevents the agent from switching branches or making changes
|
||||
"base": BASE_READ_TOOLS,
|
||||
"auto_claude": [], # No auto-claude tools needed for PR review
|
||||
},
|
||||
"qa_fixer": {
|
||||
"base": BASE_READ_TOOLS + BASE_WRITE_TOOLS,
|
||||
"auto_claude": [
|
||||
@@ -6,6 +6,9 @@ Code analysis and project scanning tools.
|
||||
"""
|
||||
|
||||
# Import from analyzers subpackage (these are the modular analyzers)
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .analyzers import (
|
||||
ProjectAnalyzer as ModularProjectAnalyzer,
|
||||
)
|
||||
@@ -27,6 +27,8 @@ This module now serves as a facade to the modular analyzer system in the analyze
|
||||
All actual implementation is in focused submodules for better maintainability.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
+2
@@ -11,6 +11,8 @@ Main exports:
|
||||
- analyze_service: Convenience function for service analysis
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -5,6 +5,8 @@ Base Analyzer Module
|
||||
Provides common constants, utilities, and base functionality shared across all analyzers.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
+2
@@ -5,6 +5,8 @@ Context Analyzer Package
|
||||
Contains specialized detectors for comprehensive project context analysis.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .api_docs_detector import ApiDocsDetector
|
||||
from .auth_detector import AuthDetector
|
||||
from .env_detector import EnvironmentDetector
|
||||
+2
@@ -8,6 +8,8 @@ Detects API documentation tools and configurations:
|
||||
- API documentation endpoints
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
+2
@@ -11,6 +11,8 @@ Detects authentication and authorization patterns:
|
||||
- Auth middleware and decorators
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
+2
@@ -9,6 +9,8 @@ Detects and analyzes environment variables from multiple sources:
|
||||
- Source code (os.getenv, process.env)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
+2
@@ -9,6 +9,8 @@ Detects background job and task queue systems:
|
||||
- Scheduled tasks and cron jobs
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
+2
@@ -10,6 +10,8 @@ Detects database migration tools and configurations:
|
||||
- Prisma
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
+2
@@ -9,6 +9,8 @@ Detects monitoring and observability setup:
|
||||
- Logging infrastructure
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
+2
@@ -13,6 +13,8 @@ Detects external service integrations based on dependencies:
|
||||
- Monitoring tools (Sentry, Datadog, New Relic)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
+2
@@ -14,6 +14,8 @@ Orchestrates comprehensive project context analysis including:
|
||||
This module delegates to specialized detectors for clean separation of concerns.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
+2
@@ -7,6 +7,8 @@ Detects database models and schemas across different ORMs:
|
||||
- JavaScript/TypeScript: Prisma, TypeORM, Drizzle, Mongoose
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
+2
@@ -6,6 +6,8 @@ Detects programming languages, frameworks, and related technologies across diffe
|
||||
Supports Python, Node.js/TypeScript, Go, Rust, and Ruby frameworks.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
+2
@@ -6,6 +6,8 @@ Detects application ports from multiple sources including entry points,
|
||||
environment files, Docker Compose, configuration files, and scripts.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
+2
@@ -5,6 +5,8 @@ Project Analyzer Module
|
||||
Analyzes entire projects, detecting monorepo structures, services, infrastructure, and conventions.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
+2
@@ -9,6 +9,8 @@ Detects API routes and endpoints across different frameworks:
|
||||
- Rust: Axum, Actix
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
+2
@@ -6,6 +6,8 @@ Main ServiceAnalyzer class that coordinates all analysis for a single service/pa
|
||||
Integrates framework detection, route analysis, database models, and context extraction.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
@@ -22,6 +22,8 @@ Usage:
|
||||
print(f"Test Commands: {result.test_commands}")
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
@@ -9,6 +9,8 @@ Uses the Claude Agent SDK (same as the rest of the system) for extraction.
|
||||
Falls back to generic insights if extraction fails (never blocks the build).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
@@ -28,6 +28,9 @@ needed for the detected tech stack, while blocking dangerous operations.
|
||||
"""
|
||||
|
||||
# Re-export all public API from the project module
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from project import (
|
||||
# Command registries
|
||||
BASE_COMMANDS,
|
||||
@@ -21,6 +21,8 @@ Usage:
|
||||
test_types = classifier.get_required_test_types(spec_dir)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
@@ -21,6 +21,8 @@ Usage:
|
||||
print("Security issues found - blocking QA approval")
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from dataclasses import dataclass, field
|
||||
@@ -22,6 +22,8 @@ Usage:
|
||||
print(f"Test command: {result['test_command']}")
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Analyzer facade module.
|
||||
|
||||
Provides backward compatibility for scripts that import from analyzer.py at the root.
|
||||
Actual implementation is in analysis/analyzer.py.
|
||||
"""
|
||||
|
||||
from analysis.analyzer import (
|
||||
ProjectAnalyzer,
|
||||
ServiceAnalyzer,
|
||||
analyze_project,
|
||||
analyze_service,
|
||||
main,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"ServiceAnalyzer",
|
||||
"ProjectAnalyzer",
|
||||
"analyze_project",
|
||||
"analyze_service",
|
||||
"main",
|
||||
]
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
Auto Claude tools module facade.
|
||||
|
||||
Provides MCP tools for agent operations.
|
||||
Re-exports from agents.tools_pkg for clean imports.
|
||||
"""
|
||||
|
||||
from agents.tools_pkg.models import ( # noqa: F401
|
||||
ELECTRON_TOOLS,
|
||||
TOOL_GET_BUILD_PROGRESS,
|
||||
TOOL_GET_SESSION_CONTEXT,
|
||||
TOOL_RECORD_DISCOVERY,
|
||||
TOOL_RECORD_GOTCHA,
|
||||
TOOL_UPDATE_QA_STATUS,
|
||||
TOOL_UPDATE_SUBTASK_STATUS,
|
||||
is_electron_mcp_enabled,
|
||||
)
|
||||
from agents.tools_pkg.permissions import get_allowed_tools # noqa: F401
|
||||
from agents.tools_pkg.registry import ( # noqa: F401
|
||||
create_auto_claude_mcp_server,
|
||||
is_tools_available,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"create_auto_claude_mcp_server",
|
||||
"get_allowed_tools",
|
||||
"is_tools_available",
|
||||
"TOOL_UPDATE_SUBTASK_STATUS",
|
||||
"TOOL_GET_BUILD_PROGRESS",
|
||||
"TOOL_RECORD_DISCOVERY",
|
||||
"TOOL_RECORD_GOTCHA",
|
||||
"TOOL_GET_SESSION_CONTEXT",
|
||||
"TOOL_UPDATE_QA_STATUS",
|
||||
"ELECTRON_TOOLS",
|
||||
"is_electron_mcp_enabled",
|
||||
]
|
||||
@@ -0,0 +1,216 @@
|
||||
"""
|
||||
Batch Task Management Commands
|
||||
==============================
|
||||
|
||||
Commands for creating and managing multiple tasks from batch files.
|
||||
"""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from ui import highlight, print_status
|
||||
|
||||
|
||||
def handle_batch_create_command(batch_file: str, project_dir: str) -> bool:
|
||||
"""
|
||||
Create multiple tasks from a batch JSON file.
|
||||
|
||||
Args:
|
||||
batch_file: Path to JSON file with task definitions
|
||||
project_dir: Project directory
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
batch_path = Path(batch_file)
|
||||
|
||||
if not batch_path.exists():
|
||||
print_status(f"Batch file not found: {batch_file}", "error")
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(batch_path) as f:
|
||||
batch_data = json.load(f)
|
||||
except json.JSONDecodeError as e:
|
||||
print_status(f"Invalid JSON in batch file: {e}", "error")
|
||||
return False
|
||||
|
||||
tasks = batch_data.get("tasks", [])
|
||||
if not tasks:
|
||||
print_status("No tasks found in batch file", "warning")
|
||||
return False
|
||||
|
||||
print_status(f"Creating {len(tasks)} tasks from batch file", "info")
|
||||
print()
|
||||
|
||||
specs_dir = Path(project_dir) / ".auto-claude" / "specs"
|
||||
specs_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Find next spec ID
|
||||
existing_specs = [d.name for d in specs_dir.iterdir() if d.is_dir()]
|
||||
next_id = (
|
||||
max([int(s.split("-")[0]) for s in existing_specs if s[0].isdigit()] or [0]) + 1
|
||||
)
|
||||
|
||||
created_specs = []
|
||||
|
||||
for idx, task in enumerate(tasks, 1):
|
||||
spec_id = f"{next_id:03d}"
|
||||
task_title = task.get("title", f"Task {idx}")
|
||||
task_slug = task_title.lower().replace(" ", "-")[:50]
|
||||
spec_name = f"{spec_id}-{task_slug}"
|
||||
spec_dir = specs_dir / spec_name
|
||||
spec_dir.mkdir(exist_ok=True)
|
||||
|
||||
# Create requirements.json
|
||||
requirements = {
|
||||
"task_description": task.get("description", task_title),
|
||||
"description": task.get("description", task_title),
|
||||
"workflow_type": task.get("workflow_type", "feature"),
|
||||
"services_involved": task.get("services", ["frontend"]),
|
||||
"priority": task.get("priority", 5),
|
||||
"complexity_inferred": task.get("complexity", "standard"),
|
||||
"inferred_from": {},
|
||||
"created_at": Path(spec_dir).stat().st_mtime,
|
||||
"estimate": {
|
||||
"estimated_hours": task.get("estimated_hours", 4.0),
|
||||
"estimated_days": task.get("estimated_days", 0.5),
|
||||
},
|
||||
}
|
||||
|
||||
req_file = spec_dir / "requirements.json"
|
||||
with open(req_file, "w") as f:
|
||||
json.dump(requirements, f, indent=2, default=str)
|
||||
|
||||
created_specs.append(
|
||||
{
|
||||
"id": spec_id,
|
||||
"name": spec_name,
|
||||
"title": task_title,
|
||||
"status": "pending_spec_creation",
|
||||
}
|
||||
)
|
||||
|
||||
print_status(
|
||||
f"[{idx}/{len(tasks)}] Created {spec_id} - {task_title}", "success"
|
||||
)
|
||||
next_id += 1
|
||||
|
||||
print()
|
||||
print_status(f"Created {len(created_specs)} spec(s) successfully", "success")
|
||||
print()
|
||||
|
||||
# Show summary
|
||||
print(highlight("Next steps:"))
|
||||
print(" 1. Generate specs: spec_runner.py --continue <spec_id>")
|
||||
print(" 2. Approve specs and build them")
|
||||
print(" 3. Run: python run.py --spec <id> to execute")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def handle_batch_status_command(project_dir: str) -> bool:
|
||||
"""
|
||||
Show status of all specs in project.
|
||||
|
||||
Args:
|
||||
project_dir: Project directory
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
specs_dir = Path(project_dir) / ".auto-claude" / "specs"
|
||||
|
||||
if not specs_dir.exists():
|
||||
print_status("No specs found in project", "warning")
|
||||
return True
|
||||
|
||||
specs = sorted([d for d in specs_dir.iterdir() if d.is_dir()])
|
||||
|
||||
if not specs:
|
||||
print_status("No specs found", "warning")
|
||||
return True
|
||||
|
||||
print_status(f"Found {len(specs)} spec(s)", "info")
|
||||
print()
|
||||
|
||||
for spec_dir in specs:
|
||||
spec_name = spec_dir.name
|
||||
req_file = spec_dir / "requirements.json"
|
||||
|
||||
status = "unknown"
|
||||
title = spec_name
|
||||
|
||||
if req_file.exists():
|
||||
try:
|
||||
with open(req_file) as f:
|
||||
req = json.load(f)
|
||||
title = req.get("task_description", title)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# Determine status
|
||||
if (spec_dir / "spec.md").exists():
|
||||
status = "spec_created"
|
||||
elif (spec_dir / "implementation_plan.json").exists():
|
||||
status = "building"
|
||||
elif (spec_dir / "qa_report.md").exists():
|
||||
status = "qa_approved"
|
||||
else:
|
||||
status = "pending_spec"
|
||||
|
||||
status_icon = {
|
||||
"pending_spec": "⏳",
|
||||
"spec_created": "📋",
|
||||
"building": "⚙️",
|
||||
"qa_approved": "✅",
|
||||
"unknown": "❓",
|
||||
}.get(status, "❓")
|
||||
|
||||
print(f"{status_icon} {spec_name:<40} {title}")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def handle_batch_cleanup_command(project_dir: str, dry_run: bool = True) -> bool:
|
||||
"""
|
||||
Clean up completed specs and worktrees.
|
||||
|
||||
Args:
|
||||
project_dir: Project directory
|
||||
dry_run: If True, show what would be deleted
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
specs_dir = Path(project_dir) / ".auto-claude" / "specs"
|
||||
worktrees_dir = Path(project_dir) / ".worktrees"
|
||||
|
||||
if not specs_dir.exists():
|
||||
print_status("No specs directory found", "info")
|
||||
return True
|
||||
|
||||
# Find completed specs
|
||||
completed = []
|
||||
for spec_dir in specs_dir.iterdir():
|
||||
if spec_dir.is_dir() and (spec_dir / "qa_report.md").exists():
|
||||
completed.append(spec_dir.name)
|
||||
|
||||
if not completed:
|
||||
print_status("No completed specs to clean up", "info")
|
||||
return True
|
||||
|
||||
print_status(f"Found {len(completed)} completed spec(s)", "info")
|
||||
|
||||
if dry_run:
|
||||
print()
|
||||
print("Would remove:")
|
||||
for spec_name in completed:
|
||||
print(f" - {spec_name}")
|
||||
wt_path = worktrees_dir / spec_name
|
||||
if wt_path.exists():
|
||||
print(f" └─ .worktrees/{spec_name}/")
|
||||
print()
|
||||
print("Run with --no-dry-run to actually delete")
|
||||
|
||||
return True
|
||||
@@ -20,6 +20,11 @@ from ui import (
|
||||
icon,
|
||||
)
|
||||
|
||||
from .batch_commands import (
|
||||
handle_batch_cleanup_command,
|
||||
handle_batch_create_command,
|
||||
handle_batch_status_command,
|
||||
)
|
||||
from .build_commands import handle_build_command
|
||||
from .followup_commands import handle_followup_command
|
||||
from .qa_commands import (
|
||||
@@ -237,6 +242,30 @@ Environment Variables:
|
||||
help="Base branch for creating worktrees (default: auto-detect or current branch)",
|
||||
)
|
||||
|
||||
# Batch task management
|
||||
parser.add_argument(
|
||||
"--batch-create",
|
||||
type=str,
|
||||
default=None,
|
||||
metavar="FILE",
|
||||
help="Create multiple tasks from a batch JSON file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--batch-status",
|
||||
action="store_true",
|
||||
help="Show status of all specs in the project",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--batch-cleanup",
|
||||
action="store_true",
|
||||
help="Clean up completed specs (dry-run by default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-dry-run",
|
||||
action="store_true",
|
||||
help="Actually delete files in cleanup (not just preview)",
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@@ -283,6 +312,19 @@ def main() -> None:
|
||||
handle_cleanup_worktrees_command(project_dir)
|
||||
return
|
||||
|
||||
# Handle batch commands
|
||||
if args.batch_create:
|
||||
handle_batch_create_command(args.batch_create, str(project_dir))
|
||||
return
|
||||
|
||||
if args.batch_status:
|
||||
handle_batch_status_command(str(project_dir))
|
||||
return
|
||||
|
||||
if args.batch_cleanup:
|
||||
handle_batch_cleanup_command(str(project_dir), dry_run=not args.no_dry_run)
|
||||
return
|
||||
|
||||
# Require --spec if not listing
|
||||
if not args.spec:
|
||||
print_banner()
|
||||
@@ -93,14 +93,76 @@ def list_specs(project_dir: Path, dev_mode: bool = False) -> list[dict]:
|
||||
return specs
|
||||
|
||||
|
||||
def print_specs_list(project_dir: Path, dev_mode: bool = False) -> None:
|
||||
"""Print a formatted list of all specs."""
|
||||
def print_specs_list(
|
||||
project_dir: Path, dev_mode: bool = False, auto_create: bool = True
|
||||
) -> None:
|
||||
"""Print a formatted list of all specs.
|
||||
|
||||
Args:
|
||||
project_dir: Project root directory
|
||||
dev_mode: If True, use dev/auto-claude/specs/
|
||||
auto_create: If True and no specs exist, automatically launch spec creation
|
||||
"""
|
||||
import subprocess
|
||||
|
||||
specs = list_specs(project_dir, dev_mode)
|
||||
|
||||
if not specs:
|
||||
print("\nNo specs found.")
|
||||
print("\nCreate your first spec:")
|
||||
print(" claude /spec")
|
||||
|
||||
if auto_create:
|
||||
# Get the backend directory and find spec_runner.py
|
||||
backend_dir = Path(__file__).parent.parent
|
||||
spec_runner = backend_dir / "runners" / "spec_runner.py"
|
||||
|
||||
# Find Python executable - use current interpreter
|
||||
python_path = sys.executable
|
||||
|
||||
if spec_runner.exists() and python_path:
|
||||
# Quick prompt for task description
|
||||
print("\n" + "=" * 60)
|
||||
print(" QUICK START")
|
||||
print("=" * 60)
|
||||
print("\nWhat do you want to build?")
|
||||
print(
|
||||
"(Enter a brief description, or press Enter for interactive mode)\n"
|
||||
)
|
||||
|
||||
try:
|
||||
task = input("> ").strip()
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print("\nCancelled.")
|
||||
return
|
||||
|
||||
if task:
|
||||
# Direct mode: create spec and start building
|
||||
print(f"\nStarting build for: {task}\n")
|
||||
subprocess.run(
|
||||
[
|
||||
python_path,
|
||||
str(spec_runner),
|
||||
"--task",
|
||||
task,
|
||||
"--complexity",
|
||||
"simple",
|
||||
"--auto-approve",
|
||||
],
|
||||
cwd=project_dir,
|
||||
)
|
||||
else:
|
||||
# Interactive mode
|
||||
print("\nLaunching interactive mode...\n")
|
||||
subprocess.run(
|
||||
[python_path, str(spec_runner), "--interactive"],
|
||||
cwd=project_dir,
|
||||
)
|
||||
return
|
||||
else:
|
||||
print("\nCreate your first spec:")
|
||||
print(" python runners/spec_runner.py --interactive")
|
||||
else:
|
||||
print("\nCreate your first spec:")
|
||||
print(" python runners/spec_runner.py --interactive")
|
||||
return
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
@@ -70,19 +70,43 @@ def find_spec(
|
||||
"""
|
||||
specs_dir = get_specs_dir(project_dir, dev_mode)
|
||||
|
||||
if not specs_dir.exists():
|
||||
return None
|
||||
if specs_dir.exists():
|
||||
# Try exact match first
|
||||
exact_path = specs_dir / spec_identifier
|
||||
if exact_path.exists() and (exact_path / "spec.md").exists():
|
||||
return exact_path
|
||||
|
||||
# Try exact match first
|
||||
exact_path = specs_dir / spec_identifier
|
||||
if exact_path.exists() and (exact_path / "spec.md").exists():
|
||||
return exact_path
|
||||
# Try matching by number prefix
|
||||
for spec_folder in specs_dir.iterdir():
|
||||
if spec_folder.is_dir() and spec_folder.name.startswith(
|
||||
spec_identifier + "-"
|
||||
):
|
||||
if (spec_folder / "spec.md").exists():
|
||||
return spec_folder
|
||||
|
||||
# Try matching by number prefix
|
||||
for spec_folder in specs_dir.iterdir():
|
||||
if spec_folder.is_dir() and spec_folder.name.startswith(spec_identifier + "-"):
|
||||
if (spec_folder / "spec.md").exists():
|
||||
return spec_folder
|
||||
# Check worktree specs (for merge-preview, merge, review, discard operations)
|
||||
worktree_base = project_dir / ".worktrees"
|
||||
if worktree_base.exists():
|
||||
# Try exact match in worktree
|
||||
worktree_spec = (
|
||||
worktree_base / spec_identifier / ".auto-claude" / "specs" / spec_identifier
|
||||
)
|
||||
if worktree_spec.exists() and (worktree_spec / "spec.md").exists():
|
||||
return worktree_spec
|
||||
|
||||
# Try matching by prefix in worktrees
|
||||
for worktree_dir in worktree_base.iterdir():
|
||||
if worktree_dir.is_dir() and worktree_dir.name.startswith(
|
||||
spec_identifier + "-"
|
||||
):
|
||||
spec_in_worktree = (
|
||||
worktree_dir / ".auto-claude" / "specs" / worktree_dir.name
|
||||
)
|
||||
if (
|
||||
spec_in_worktree.exists()
|
||||
and (spec_in_worktree / "spec.md").exists()
|
||||
):
|
||||
return spec_in_worktree
|
||||
|
||||
return None
|
||||
|
||||
@@ -185,9 +209,9 @@ def get_project_dir(provided_dir: Path | None) -> Path:
|
||||
|
||||
project_dir = Path.cwd()
|
||||
|
||||
# Auto-detect if running from within auto-claude directory (the source code)
|
||||
if project_dir.name == "auto-claude" and (project_dir / "run.py").exists():
|
||||
# Running from within auto-claude/ source directory, go up 1 level
|
||||
project_dir = project_dir.parent
|
||||
# Auto-detect if running from within apps/backend directory (the source code)
|
||||
if project_dir.name == "backend" and (project_dir / "run.py").exists():
|
||||
# Running from within apps/backend/ source directory, go up 2 levels
|
||||
project_dir = project_dir.parent.parent
|
||||
|
||||
return project_dir
|
||||
@@ -14,7 +14,14 @@ _PARENT_DIR = Path(__file__).parent.parent
|
||||
if str(_PARENT_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(_PARENT_DIR))
|
||||
|
||||
from core.workspace.git_utils import _is_auto_claude_file, is_lock_file
|
||||
from core.workspace.git_utils import (
|
||||
_is_auto_claude_file,
|
||||
apply_path_mapping,
|
||||
detect_file_renames,
|
||||
get_file_content_from_ref,
|
||||
get_merge_base,
|
||||
is_lock_file,
|
||||
)
|
||||
from debug import debug_warning
|
||||
from ui import (
|
||||
Icons,
|
||||
@@ -680,6 +687,58 @@ def handle_merge_preview_command(
|
||||
# but we want to show the user all files that will be merged
|
||||
total_files_from_git = len(all_changed_files)
|
||||
|
||||
# Detect files that need AI merge due to path mappings (file renames)
|
||||
# This happens when the target branch has renamed/moved files that the
|
||||
# worktree modified at their old locations
|
||||
path_mapped_ai_merges: list[dict] = []
|
||||
path_mappings: dict[str, str] = {}
|
||||
|
||||
if git_conflicts["needs_rebase"] and git_conflicts["commits_behind"] > 0:
|
||||
# Get the merge-base between the branches
|
||||
spec_branch = git_conflicts["spec_branch"]
|
||||
base_branch = git_conflicts["base_branch"]
|
||||
merge_base = get_merge_base(project_dir, spec_branch, base_branch)
|
||||
|
||||
if merge_base:
|
||||
# Detect file renames between merge-base and current base branch
|
||||
path_mappings = detect_file_renames(
|
||||
project_dir, merge_base, base_branch
|
||||
)
|
||||
|
||||
if path_mappings:
|
||||
debug(
|
||||
MODULE,
|
||||
f"Detected {len(path_mappings)} file rename(s) between merge-base and target",
|
||||
sample_mappings={
|
||||
k: v for k, v in list(path_mappings.items())[:3]
|
||||
},
|
||||
)
|
||||
|
||||
# Check which changed files have path mappings and need AI merge
|
||||
for file_path in all_changed_files:
|
||||
mapped_path = apply_path_mapping(file_path, path_mappings)
|
||||
if mapped_path != file_path:
|
||||
# File was renamed - check if both versions exist
|
||||
worktree_content = get_file_content_from_ref(
|
||||
project_dir, spec_branch, file_path
|
||||
)
|
||||
target_content = get_file_content_from_ref(
|
||||
project_dir, base_branch, mapped_path
|
||||
)
|
||||
|
||||
if worktree_content and target_content:
|
||||
path_mapped_ai_merges.append(
|
||||
{
|
||||
"oldPath": file_path,
|
||||
"newPath": mapped_path,
|
||||
"reason": "File was renamed/moved and modified in both branches",
|
||||
}
|
||||
)
|
||||
debug(
|
||||
MODULE,
|
||||
f"Path-mapped file needs AI merge: {file_path} -> {mapped_path}",
|
||||
)
|
||||
|
||||
result = {
|
||||
"success": True,
|
||||
# Use git diff files as the authoritative list of files to merge
|
||||
@@ -693,6 +752,9 @@ def handle_merge_preview_command(
|
||||
"commitsBehind": git_conflicts["commits_behind"],
|
||||
"baseBranch": git_conflicts["base_branch"],
|
||||
"specBranch": git_conflicts["spec_branch"],
|
||||
# Path-mapped files that need AI merge due to renames
|
||||
"pathMappedAIMerges": path_mapped_ai_merges,
|
||||
"totalRenames": len(path_mappings),
|
||||
},
|
||||
"summary": {
|
||||
# Use git diff count, not semantic tracker count
|
||||
@@ -702,6 +764,8 @@ def handle_merge_preview_command(
|
||||
"autoMergeable": summary.get("auto_mergeable", 0),
|
||||
"hasGitConflicts": git_conflicts["has_conflicts"]
|
||||
and len(non_lock_conflicting_files) > 0,
|
||||
# Include path-mapped AI merge count for UI display
|
||||
"pathMappedAIMergeCount": len(path_mapped_ai_merges),
|
||||
},
|
||||
# Include lock files info so UI can optionally show them
|
||||
"lockFilesExcluded": lock_files_excluded,
|
||||
@@ -716,6 +780,8 @@ def handle_merge_preview_command(
|
||||
total_conflicts=result["summary"]["totalConflicts"],
|
||||
has_git_conflicts=git_conflicts["has_conflicts"],
|
||||
auto_mergeable=result["summary"]["autoMergeable"],
|
||||
path_mapped_ai_merges=len(path_mapped_ai_merges),
|
||||
total_renames=len(path_mappings),
|
||||
)
|
||||
|
||||
return result
|
||||
@@ -736,5 +802,6 @@ def handle_merge_preview_command(
|
||||
"conflictFiles": 0,
|
||||
"totalConflicts": 0,
|
||||
"autoMergeable": 0,
|
||||
"pathMappedAIMergeCount": 0,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
Claude client module facade.
|
||||
|
||||
Provides Claude API client utilities.
|
||||
Uses lazy imports to avoid circular dependencies.
|
||||
"""
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
"""Lazy import to avoid circular imports with auto_claude_tools."""
|
||||
from core import client as _client
|
||||
|
||||
return getattr(_client, name)
|
||||
|
||||
|
||||
def create_client(*args, **kwargs):
|
||||
"""Create a Claude client instance."""
|
||||
from core.client import create_client as _create_client
|
||||
|
||||
return _create_client(*args, **kwargs)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"create_client",
|
||||
]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user