Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.25.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: Go 1.25.0 has not been released. Latest stable is 1.23.x. Change to "1.23" or "stable"

Suggested change
go-version: "1.25.0"
go-version: "1.23"
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/ci.yml
Line: 19:19

Comment:
**syntax:** Go 1.25.0 has not been released. Latest stable is 1.23.x. Change to `"1.23"` or `"stable"`

```suggestion
          go-version: "1.23"
```

How can I resolve this? If you propose a fix, please make it concise.


- name: Run golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: latest

format:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.25.0"

- name: Check formatting
run: |
if [ -n "$(gofmt -l .)" ]; then
echo "The following files are not formatted:"
gofmt -l .
exit 1
fi

test:
name: Test
runs-on: ubuntu-latest
needs: [lint, format]
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.25.0"

- name: Run tests
run: go test -v ./...
35 changes: 35 additions & 0 deletions .github/workflows/release-dispatch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Release Dispatch

on:
workflow_dispatch:
inputs:
version:
description: "Semantic version (e.g., v1.0.0)"
required: true
type: string

jobs:
dispatch:
name: Trigger Release
runs-on: ubuntu-latest
steps:
- name: Validate version format
run: |
if [[ ! "${{ inputs.version }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Version must be in format v*.*.* (e.g., v1.0.0)"
exit 1
fi

- name: Trigger release workflow
uses: actions/github-script@v7
with:
script: |
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'release.yml',
ref: 'main',
inputs: {
version: '${{ inputs.version }}'
}
})
132 changes: 132 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: Release

on:
workflow_dispatch:
inputs:
version:
description: "Semantic version (e.g., v1.0.0)"
required: true
type: string
push:
tags:
- "v*"

permissions:
contents: write

jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
include:
- goos: linux
goarch: amd64
- goos: linux
goarch: arm64
- goos: darwin
goarch: amd64
- goos: darwin
goarch: arm64
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.25.0"

- name: Determine version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
else
echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
fi

- name: Build binary
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0
run: |
BINARY_NAME="ccmanager-${{ matrix.goos }}-${{ matrix.goarch }}"
go build -ldflags="-s -w -X main.version=${{ steps.version.outputs.version }}" -o "$BINARY_NAME" .

- name: Compress binary
run: |
BINARY_NAME="ccmanager-${{ matrix.goos }}-${{ matrix.goarch }}"
tar -czvf "${BINARY_NAME}.tar.gz" "$BINARY_NAME"

- name: Generate checksum
run: |
BINARY_NAME="ccmanager-${{ matrix.goos }}-${{ matrix.goarch }}"
sha256sum "${BINARY_NAME}.tar.gz" > "${BINARY_NAME}.tar.gz.sha256"

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ccmanager-${{ matrix.goos }}-${{ matrix.goarch }}
path: |
ccmanager-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
ccmanager-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz.sha256

release:
name: Release
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Determine version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
else
echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
fi

- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: Collect release files
run: |
mkdir -p release
find artifacts -type f \( -name "*.tar.gz" -o -name "*.sha256" \) -exec mv {} release/ \;

- name: Generate changelog
id: changelog
run: |
PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -n "$PREVIOUS_TAG" ]; then
CHANGELOG=$(git log --pretty=format:"- %s" "$PREVIOUS_TAG"..HEAD)
else
CHANGELOG=$(git log --pretty=format:"- %s" HEAD~10..HEAD 2>/dev/null || git log --pretty=format:"- %s")
fi
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.version }}
name: Release ${{ steps.version.outputs.version }}
body: |
## Changelog
${{ steps.changelog.outputs.changelog }}

## Checksums
```
$(cat release/*.sha256)
```
files: release/*
draft: false
prerelease: false
50 changes: 50 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# CCManager - Claude Code Session Manager

Go CLI application with Bubble Tea TUI for managing Claude Code sessions with gamification features.

## Development Commands

```bash
make build # Build binary to bin/ccmanager
make test # Run all tests
make lint # Run golangci-lint
make deps # Run go mod tidy
make dev # Build and run
make run-debug # Run with CCMANAGER_DEBUG=1
```

## Code Style

- Follow Go conventions and existing patterns in the codebase
- Use standard library where possible
- Keep functions focused and small
- Error handling: return errors, don't panic

## Project Structure

```
cmd/ccmanager/ # Main entry point
internal/
app/ # Application orchestration
claude/ # Claude Code process detection
config/ # Configuration management
daemon/ # Background monitoring
game/ # Gamification (streaks, pomodoro, control groups)
store/ # SQLite persistence
tmux/ # Tmux integration
tui/ # Bubble Tea UI components
usage/ # Usage tracking and parsing
workspace/ # Workspace/project detection (git, jj)
```

## Before Committing

1. Run tests: `make test`
2. Run linter: `make lint`
3. Ensure build passes: `make build`

## Testing

- Tests are in `*_test.go` files alongside source
- Run specific package: `go test -v ./internal/game/...`
- Run with coverage: `go test -coverprofile=coverage.out ./...`
2 changes: 1 addition & 1 deletion cmd/ccmanager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func main() {
fmt.Fprintf(os.Stderr, "Error initializing: %v\n", err)
os.Exit(1)
}
defer application.Close()
defer func() { _ = application.Close() }()

if err := application.Run(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/valentindosimont/ccmanager

go 1.25.0
go 1.24.0

require (
github.com/charmbracelet/bubbles v0.21.0
Expand Down
4 changes: 2 additions & 2 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func (a *App) saveState() {
score, lastScoreDate, pomodoroState, pomodoroRemaining := a.engine.State()

now := time.Now()
a.store.UpdateGameState(&store.GameState{
_ = a.store.UpdateGameState(&store.GameState{
CurrentScore: score,
LastScoreDate: lastScoreDate,
LastActionAt: &now,
Expand All @@ -157,6 +157,6 @@ func (a *App) saveState() {

// Save control groups
for groupNum, session := range a.engine.ControlGroups().All() {
a.store.SetControlGroup(groupNum, session)
_ = a.store.SetControlGroup(groupNum, session)
}
}
28 changes: 5 additions & 23 deletions internal/claude/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import (
type SessionState int

const (
StateUnknown SessionState = iota
StateIdle // Prompt visible, waiting for input
StateActive // User recently typed
StateThinking // Claude is thinking
StateUrgent // Needs immediate input
StateUnknown SessionState = iota
StateIdle // Prompt visible, waiting for input
StateActive // User recently typed
StateThinking // Claude is thinking
StateUrgent // Needs immediate input
)

func (s SessionState) String() string {
Expand Down Expand Up @@ -203,21 +203,3 @@ func (d *Detector) getLastNonEmptyLine(content string) string {
}
return ""
}

func (d *Detector) endsWithPrompt(content string) bool {
trimmed := strings.TrimSpace(content)
if len(trimmed) == 0 {
return false
}
checkLen := 20
if len(trimmed) < checkLen {
checkLen = len(trimmed)
}
suffix := trimmed[len(trimmed)-checkLen:]
for _, pattern := range d.idlePatterns {
if pattern.MatchString(suffix) {
return true
}
}
return false
}
Loading