Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f4beb1e
refactor: simplify setup with global install, remove per-project init
zippoxer Jan 20, 2026
1194564
test: add setup UX e2e coverage
zippoxer Jan 20, 2026
b36abfe
fix: prevent false merge detection for tasks with no commits
zippoxer Jan 20, 2026
b10837d
test: add thorough layout migration tests
zippoxer Jan 21, 2026
0a8f34f
Remove broken plugin install, add migration for legacy users
zippoxer Jan 21, 2026
77afa95
fix: make --guide output agent-readable instructions
zippoxer Jan 21, 2026
2a23eb7
fix: make --guide output agent-readable instructions
zippoxer Jan 21, 2026
1df9ae3
feat: improve install UX with flags, dynamic guide, and consistent ou…
zippoxer Jan 21, 2026
cc144fd
Add validation and e2e tests for install UX flags
zippoxer Jan 21, 2026
709ca6d
fix(install): improve scope wizard text clarity
zippoxer Jan 21, 2026
7c0fb56
refactor: extract config merge/validate logic into pure functions
zippoxer Jan 21, 2026
c178a10
feat(release): add interactive release flow with beta support
zippoxer Jan 21, 2026
47717b7
improve "task is still working" error message
zippoxer Jan 21, 2026
8380733
Update README.md
zippoxer Jan 22, 2026
dbf2d6f
Update README.md
zippoxer Jan 22, 2026
ad43ed6
Update README.md
zippoxer Jan 22, 2026
e62a8ba
Update README.md
zippoxer Jan 22, 2026
eb52071
Update README.md
zippoxer Jan 22, 2026
137191a
Update installation instructions in README.md
zippoxer Jan 22, 2026
e202112
improve install guide and README
zippoxer Jan 22, 2026
cfef833
feat(review): add --base flag for PR-style branch diff reviews
zippoxer Jan 22, 2026
7fc8e3e
docs: add git redesign spec and requirements
zippoxer Jan 23, 2026
7380d34
Fix Codex harness returning transient errors on success
zippoxer Jan 24, 2026
4662808
Use model aliases for Claude harness defaults
zippoxer Jan 24, 2026
f6a62b1
Git redesign: PR-style diffs, applied detection, frozen stats, migration
zippoxer Jan 24, 2026
2d4efc5
Fix migration corrupting sort order by using commit dates for zero ti…
zippoxer Jan 25, 2026
9b7d866
Warn when project-scoped skill is outdated
zippoxer Jan 26, 2026
4b03bf7
Merge main into dev: fix Codex transient errors on success
zippoxer Jan 28, 2026
6cb1f1b
Add send --async and wait command
zippoxer Jan 28, 2026
6c80523
Print 'interrupted' to stderr on signal in send
zippoxer Jan 28, 2026
819533e
Fix merge-tree conflict detection
zippoxer Jan 28, 2026
68d354d
Update SKILL.md
zippoxer Jan 28, 2026
55c2af8
Update .gitignore
zippoxer Jan 28, 2026
5c7cdc9
Allow subtask merge with uncommitted changes in base worktree
zippoxer Jan 28, 2026
17cae96
Update SKILL.md
zippoxer Jan 28, 2026
97e1a37
Update SKILL.md
zippoxer Jan 28, 2026
d561dce
Add global marker to skip gitredesign migration after first run
zippoxer Jan 28, 2026
65bd829
Revert async send/wait feature
zippoxer Jan 29, 2026
b2c882c
Build review prompts on subtask side instead of passing flags to codex
zippoxer Jan 29, 2026
7b60a4c
Fix async revert: proper concurrency guards, optimal ordering, e2e tests
zippoxer Jan 29, 2026
57782f4
Add early development note to README
zippoxer Jan 29, 2026
591f654
Merge remote-tracking branch 'origin/main' into dev
zippoxer Jan 29, 2026
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
81 changes: 59 additions & 22 deletions .claude/commands/release.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,49 @@
---
allowed-tools: Bash(git:*), Bash(gh:*)
argument-hint: major | minor | patch
argument-hint: [optional - will prompt interactively]
description: Create and publish a new release
---

## Context

- Current version: !`git describe --tags --abbrev=0 2>/dev/null || echo "no tags yet"`
- Latest stable: !`git tag -l 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | grep -v '-' | head -1 || echo "none"`
- Latest beta: !`git tag -l 'v*-beta*' --sort=-v:refname | head -1 || echo "none"`
- Current branch: !`git branch --show-current`
- Git status: !`git status --short`
- Commits since last tag: !`git log $(git describe --tags --abbrev=0 2>/dev/null)..HEAD --oneline 2>/dev/null || git log --oneline -5`

## Task

Create a new **$1** release (major | minor | patch).
Create a new release.

### Steps

1. **Validate** argument is one of: major, minor, patch. If missing or invalid, ask.

2. **Check prerequisites**:
- On `main` branch
1. **Show current state** to the user:
- Latest stable version
- Latest beta version (if any)
- What versions each release type would create

2. **Ask user what release to create** using AskUserQuestion:
- Calculate and show concrete version numbers for each option
- Example options (adjust based on current state):
- "v0.2.0 (minor)" — requires `main` branch
- "v0.1.2 (patch)" — requires `main` branch
- "v1.0.0 (major)" — requires `main` branch
- "v0.2.0-beta.1 (new beta)" — requires `dev` branch
- "v0.2.0-beta.2 (next beta)" — only if beta exists, requires `dev` branch
- If argument was provided (e.g., `/release patch`), skip the question and use it

3. **Check prerequisites**:
- Working directory is clean
- Tests pass: `go test ./...`

3. **Calculate new version** using semver:
- major: bump X in vX.Y.Z, reset Y and Z to 0
- minor: bump Y, reset Z to 0
- patch: bump Z
- **IMPORTANT - Branch rules:**
- **Stable releases (major/minor/patch)**: MUST be on `main` branch
- **Beta releases**: MUST be on `dev` branch
- If on wrong branch, stop and tell the user to switch branches first

4. **Create and push tag**:
```bash
VERSION=vX.Y.Z
VERSION=vX.Y.Z # or vX.Y.Z-beta.N for beta
git tag "$VERSION"
git push origin "$VERSION"
```
Expand All @@ -41,13 +53,19 @@ Create a new **$1** release (major | minor | patch).
gh run watch --workflow release.yml --interval 10
```

6. **Verify release**:
6. **Mark as prerelease** (beta only):
```bash
gh release edit "$VERSION" --prerelease
```
This ensures the beta won't be picked up by auto-update (which uses `/releases/latest`).

7. **Verify release**:
```bash
gh release view "$VERSION"
gh release view "$VERSION" --json assets --jq '.assets[].name'
```

7. **Add release notes**:
8. **Add release notes**:
- Read the commit history since the last release
- Group changes by type (Features, Fixes, Improvements, etc.)
- Write a concise summary highlighting the most important changes
Expand All @@ -72,29 +90,48 @@ Create a new **$1** release (major | minor | patch).
)"
```

8. **Verify Homebrew tap updated**:
9. **Verify Homebrew tap updated** (stable releases only - skip for beta):
```bash
gh api "repos/zippoxer/homebrew-tap/contents/Formula/subtask.rb?ref=main" --jq .content \
| base64 --decode \
| rg "version|url|sha256" -n
```

9. **Test Homebrew install** (without installing):
```bash
brew fetch --force zippoxer/tap/subtask
```
This downloads the tarball and verifies the checksum matches the formula.
10. **Test Homebrew install** (stable releases only - skip for beta):
```bash
brew fetch --force zippoxer/tap/subtask
```
This downloads the tarball and verifies the checksum matches the formula.

Note: Do NOT update the local installation. The user tests with local builds (`go install ./cmd/subtask`), not Homebrew.

### Beta Release Notes

For beta releases, keep notes concise and focused on what to test:
```bash
gh release edit "$VERSION" --prerelease --notes "$(cat <<'EOF'
## Beta Release

This is a **prerelease** for testing. Not recommended for production.

### Changes
- Change 1
- Change 2

### Testing
Please report issues at https://github.com/zippoxer/subtask/issues
EOF
)"
```

### Troubleshooting

If release workflow fails:
```bash
gh run list --workflow release.yml --limit 5
gh run view --log-failed <run-id>
```
Fix the issue on main, then create a NEW tag (don't reuse).
Fix the issue, then create a NEW tag (don't reuse).

To undo a bad release:
```bash
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ coverage.out
# Personal notes
ISSUES.md
TODO.md
/docs/issues
29 changes: 16 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ Task status is what users care about. Worker status is operational detail. Works

| Command | Description |
|---------|-------------|
| `subtask init` | One-time setup: project config, workspace limit |
| `subtask install` | One-time global install + configuration wizard |
| `subtask config` | Edit user defaults or project overrides |
| `subtask draft <task>` | Create a task without running it |
| `subtask send <task>` | Send a message (starts or resumes task) |
| `subtask stage <task> <stage>` | Advance workflow stage |
Expand All @@ -152,18 +153,20 @@ Task status is what users care about. Worker status is operational detail. Works
## Storage

```
.subtask/ # per-project, gitignored
├── config.json # harness, model, workspace limit
├── tasks/<name>/ # task folder (portable, syncable)
│ ├── TASK.md # description + schema version in frontmatter
│ ├── PLAN.md # optional plan
│ ├── PROGRESS.json # worker progress tracking
│ └── history.jsonl # source of truth: messages + lifecycle events
├── internal/<name>/ # runtime only (not portable)
│ └── state.json # session ID, workspace path
└── index.db # SQLite cache (rebuilt from history)

~/.subtask/workspaces/ # global worktree pool (created on demand)
~/.subtask/
├── config.json # global defaults (from install/config)
├── workspaces/<escaped-git-root>--<id>/ # worktrees (created on demand)
└── projects/<escaped-git-root>/ # per-project runtime state (machine-local)
├── internal/ # session IDs, workspace assignments, locks
└── index.db # SQLite cache (rebuildable)

<repo>/.subtask/
├── config.json # optional per-project overrides
└── tasks/<name>/ # task folder (portable, syncable)
├── TASK.md # description + schema version in frontmatter
├── PLAN.md # optional plan
├── PROGRESS.json # worker progress tracking
└── history.jsonl # source of truth: messages + lifecycle events
```

### Portability Contract
Expand Down
36 changes: 22 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ Run `subtask` in your terminal to see everything:
</tr>
</table>

## Install
> [!NOTE]
## Setup

> [!NOTE]
> Subtask is in early development. Upcoming releases will simplify installation, solve known bugs, and improve Claude's proficiency.

### Get the CLI
### Install the CLI

#### Mac/Linux

Expand Down Expand Up @@ -88,26 +89,33 @@ go install github.com/zippoxer/subtask/cmd/subtask@latest

</details>

### Install the Claude Code Skill
### Install the Skill

Tell Claude Code:
```md
Setup Subtask with `subtask install --guide`.
```
Claude will install the Subtask skill at `~/.claude/skills`, and ask you whether subagents should run Claude, Codex or OpenCode.

<details>
<summary>Or install manually…</summary>

```bash
subtask install

# Tip: Uninstall later with `subtask uninstall`.
```

> *This asks whether to install to user-scope (`~/.claude/skills`) or project-scope.*
>
> *You can skip installing the plugin, it isn't working yet.*

Restart Claude Code.
</details>

### Setup Subtask in your Repo
### Install the Plugin (Optional)

```bash
cd your-repo
subtask init
In Claude Code:
```
/plugin marketplace add zippoxer/subtask
/plugin install subtask@subtask
```
This reminds Claude to use the Subtask skill when it invokes the CLI.

## Use

Expand All @@ -133,7 +141,7 @@ subtask update
- I use Claude Code to lead the development (i talk, it creates tasks and tracks everything)
- I use Codex for subagents (just preference, Claude Code works too)
- ~60 tasks merged in the past week
- Proof: https://github.com/user-attachments/assets/6c71e34f-b3c6-4372-ac25-dd3eea15932e
- [Proof](https://github.com/user-attachments/assets/6c71e34f-b3c6-4372-ac25-dd3eea15932e)


## License
Expand Down
2 changes: 1 addition & 1 deletion cmd/subtask/ask.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (c *AskCmd) Run() error {
// Load config for harness
cfg, err := workspace.LoadConfig()
if err != nil {
return fmt.Errorf("subtask not initialized\n\nRun: subtask init")
return err
}
if err := workspace.ValidateReasoningFlag(cfg.Harness, c.Reasoning); err != nil {
return err
Expand Down
34 changes: 15 additions & 19 deletions cmd/subtask/auto_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,36 @@ package main

import (
"os"
"path/filepath"

"github.com/zippoxer/subtask/internal/homedir"
"github.com/zippoxer/subtask/pkg/install"
"github.com/zippoxer/subtask/pkg/task"
)

func runAutoUpdate() {
if os.Getenv(autoUpdateEnvVar) == "1" {
return
}

userBase, _, err := baseDirForScope(install.ScopeUser)
if err != nil || userBase == "" {
return
}
projectBase, _, err := baseDirForScope(install.ScopeProject)
if err != nil || projectBase == "" {
return
homeDir, err := homedir.Dir()
if err == nil && homeDir != "" {
res, err := install.AutoUpdateIfInstalled(homeDir)
if err == nil && res.UpdatedSkill {
printSuccess("Updated skill to latest version")
}
}

userRes, err := install.AutoUpdateIfInstalled(install.ScopeUser, userBase)
if err != nil {
repoRoot, err := task.GitRootAbs()
if err != nil || repoRoot == "" {
return
}
projectRes, err := install.AutoUpdateIfInstalled(install.ScopeProject, projectBase)

st, err := install.GetSkillStatusFor(repoRoot)
if err != nil {
return
}

skillUpdated := userRes.UpdatedSkill || projectRes.UpdatedSkill
pluginUpdated := userRes.UpdatedPlugin || projectRes.UpdatedPlugin

if skillUpdated {
printSuccess("Updated skill to latest version")
}
if pluginUpdated {
printSuccess("Updated plugin to latest version")
if st.Installed && !st.UpToDate {
printWarning("Project skill at " + filepath.Join(".claude", "skills", "subtask", "SKILL.md") + " is outdated. Run `subtask install --scope project` to update.")
}
}
Loading
Loading