schwarm-cli 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +68 -0
- data/lib/schwarm_cli/client/user_messages.rb +1 -1
- data/lib/schwarm_cli/commands/main.rb +6 -0
- data/lib/schwarm_cli/commands/tasks.rb +27 -0
- data/lib/schwarm_cli/git.rb +18 -0
- data/lib/schwarm_cli/hands_off_task.rb +147 -0
- data/lib/schwarm_cli/version.rb +1 -1
- data/lib/schwarm_cli.rb +2 -0
- data/schwarm-skill.md +235 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5ad711fe3997e1d3b3c18cee54f6621ecc3d895f01f7c09916e5bb1f217ea6c4
|
|
4
|
+
data.tar.gz: 3c0b5c575b1ee49423a547eeadc5a7310109e5ff294f718ee0e3cbd016f25dda
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a1650f63cca311d44de2f6c6b351a4bb1f81958ec5ce3093ac0a6745f5f1104ae632bcb38386bac177dd03e1274052bb47dd7967f2a0a689de1b9e28dd8227eb
|
|
7
|
+
data.tar.gz: b5696799424c27eb8a7808ff225a461e1573e28ffb4cb2261ecd541687344293e5f115fa743605273b612db9639c61ba1eb1ee70113deaca8f9e5dd26b70dc5d
|
data/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# schwarm-cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for [Schwarm](https://github.com/getdexter/schwarm) — a
|
|
4
|
+
distributed system that orchestrates Claude Code agents working on tasks in
|
|
5
|
+
parallel. Wraps the Schwarm Public API v2 to manage tasks, repositories,
|
|
6
|
+
skills, agents, and more.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Requires Ruby >= 4.0.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
gem install schwarm-cli
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This installs the `schwarm` executable.
|
|
17
|
+
|
|
18
|
+
## Configuration
|
|
19
|
+
|
|
20
|
+
Run the interactive configuration once to point the CLI at your Schwarm
|
|
21
|
+
server and save an API key:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
schwarm configure
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
You'll be prompted for:
|
|
28
|
+
|
|
29
|
+
- **Server URL** (defaults to `https://schwarm.getdexter.net`)
|
|
30
|
+
- **API Key** — create one from your Schwarm server's API Keys page
|
|
31
|
+
|
|
32
|
+
The credentials are saved to `~/.schwarm/config/<env>.json` and the connection
|
|
33
|
+
is tested before saving.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
List top-level commands:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
schwarm help
|
|
41
|
+
schwarm tree # full command tree
|
|
42
|
+
schwarm version
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Common workflows:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
schwarm repos list
|
|
49
|
+
schwarm tasks list
|
|
50
|
+
schwarm tasks create --repo <REPO_ID> --name "Fix bug" --prompt "..." --status ready
|
|
51
|
+
schwarm tasks show <TASK_ID>
|
|
52
|
+
schwarm tasks handoff --repo <REPO_ID> --name "Continue WIP"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For the full set of agent-oriented workflows (dispatching tasks, DAGs,
|
|
56
|
+
handoffs, monitoring, messaging), print the bundled skill:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
schwarm skill
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This is the same document AI agents read to learn how to drive the CLI, and
|
|
63
|
+
it's kept in sync with the gem at [`cli/schwarm-skill.md`](schwarm-skill.md).
|
|
64
|
+
|
|
65
|
+
## Links
|
|
66
|
+
|
|
67
|
+
- [Schwarm repository](https://github.com/getdexter/schwarm)
|
|
68
|
+
- [Source](https://github.com/getdexter/schwarm/tree/main/cli)
|
|
@@ -4,7 +4,7 @@ module SchwarmCli
|
|
|
4
4
|
class Client
|
|
5
5
|
class UserMessages < Resource
|
|
6
6
|
def create(task_id:, content:)
|
|
7
|
-
post("/api/v2/tasks/#{task_id}/messages", {
|
|
7
|
+
post("/api/v2/tasks/#{task_id}/messages", { message: { content: } }).body
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -59,6 +59,12 @@ module SchwarmCli
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
map %w[-v --version] => :version
|
|
62
|
+
|
|
63
|
+
desc "skill", "Print the agent skill documenting how AI agents use the Schwarm CLI"
|
|
64
|
+
def skill
|
|
65
|
+
path = File.expand_path("../../../schwarm-skill.md", __dir__)
|
|
66
|
+
puts File.read(path)
|
|
67
|
+
end
|
|
62
68
|
end
|
|
63
69
|
end
|
|
64
70
|
end
|
|
@@ -114,6 +114,23 @@ module SchwarmCli
|
|
|
114
114
|
end
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
+
desc "handoff", "Hand off the current local branch to schwarm as a ready task"
|
|
118
|
+
option :prompt, type: :string, desc: "Task prompt"
|
|
119
|
+
option :prompt_file, type: :string, desc: "Read prompt from file"
|
|
120
|
+
option :repo, type: :string, desc: "Repository ID (skips origin auto-detect)"
|
|
121
|
+
option :name, type: :string, desc: "Task name (defaults to the branch name)"
|
|
122
|
+
def handoff
|
|
123
|
+
prompt = read_prompt
|
|
124
|
+
abort "Error: --prompt or --prompt-file is required." if prompt.nil? || prompt.strip.empty?
|
|
125
|
+
|
|
126
|
+
handle_errors do
|
|
127
|
+
result = SchwarmCli::HandsOffTask.new(client: client).call(
|
|
128
|
+
prompt: prompt, repo_override: options[:repo], name_override: options[:name]
|
|
129
|
+
)
|
|
130
|
+
print_handoff_result(result)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
117
134
|
private
|
|
118
135
|
|
|
119
136
|
def list_attrs
|
|
@@ -124,6 +141,16 @@ module SchwarmCli
|
|
|
124
141
|
}
|
|
125
142
|
end
|
|
126
143
|
|
|
144
|
+
def print_handoff_result(result)
|
|
145
|
+
if result.success?
|
|
146
|
+
puts "Pushed #{result.branch_name} to origin."
|
|
147
|
+
puts "Task #{result.task_id} created (status: #{result.status})."
|
|
148
|
+
puts " #{result.task_url}"
|
|
149
|
+
else
|
|
150
|
+
abort "Error: #{result.error_message}"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
127
154
|
def create_attrs
|
|
128
155
|
{
|
|
129
156
|
name: options[:name], prompt: read_prompt,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
|
|
5
|
+
module SchwarmCli
|
|
6
|
+
# Minimal git shell-out wrapper.
|
|
7
|
+
#
|
|
8
|
+
# Returns a Result struct for every invocation so callers can pattern-match on
|
|
9
|
+
# +success?+ and surface +stderr+ verbatim when a command fails.
|
|
10
|
+
module Git
|
|
11
|
+
Result = Struct.new(:stdout, :stderr, :success?, keyword_init: true)
|
|
12
|
+
|
|
13
|
+
def self.run(*, chdir: Dir.pwd)
|
|
14
|
+
stdout, stderr, status = Open3.capture3("git", *, chdir: chdir)
|
|
15
|
+
Result.new(stdout: stdout, stderr: stderr, success?: status.success?)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SchwarmCli
|
|
4
|
+
# Runs preflight checks, pushes the current branch, and creates a schwarm
|
|
5
|
+
# task pointing at it. Returns a Result the CLI command prints.
|
|
6
|
+
class HandsOffTask
|
|
7
|
+
Result = Struct.new(:success?, :task_id, :task_url, :branch_name, :status, :error_message,
|
|
8
|
+
keyword_init: true)
|
|
9
|
+
|
|
10
|
+
def initialize(git: SchwarmCli::Git, client: SchwarmCli::Client.new)
|
|
11
|
+
@git = git
|
|
12
|
+
@client = client
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(prompt:, repo_override:, name_override:)
|
|
16
|
+
repo, branch, error = run_preflight(repo_override)
|
|
17
|
+
return error if error
|
|
18
|
+
|
|
19
|
+
push_result = @git.run("push", "-u", "origin", "HEAD")
|
|
20
|
+
return failure(push_result.stderr.strip) unless push_result.success?
|
|
21
|
+
|
|
22
|
+
create_task(repo: repo, branch: branch, prompt: prompt, name_override: name_override)
|
|
23
|
+
rescue Faraday::Error => e
|
|
24
|
+
failure(parse_api_error(e))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def run_preflight(repo_override) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
30
|
+
return [nil, nil, failure("not in a git repository")] unless inside_work_tree?
|
|
31
|
+
|
|
32
|
+
origin = origin_url
|
|
33
|
+
unless github_url?(origin)
|
|
34
|
+
return [nil, nil, failure("handoff requires a GitHub `origin` remote (got `#{origin}`)")]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
repo = resolve_repository(origin: origin, override: repo_override)
|
|
38
|
+
return [nil, nil, failure(missing_repo_message(origin, repo_override))] if repo.nil?
|
|
39
|
+
|
|
40
|
+
branch = current_branch
|
|
41
|
+
if branch.empty?
|
|
42
|
+
return [nil, nil, failure("not on a branch (detached HEAD). Check out a branch before handing off.")]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
base_branch = repo["base_branch"]
|
|
46
|
+
if branch == base_branch
|
|
47
|
+
return [nil, nil,
|
|
48
|
+
failure("refusing to hand off the base branch `#{base_branch}`; create a feature branch first.")]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
dirty = status_porcelain
|
|
52
|
+
return [nil, nil, failure("commit or stash your changes before handing off:\n#{dirty}")] unless dirty.empty?
|
|
53
|
+
|
|
54
|
+
[repo, branch, nil]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def missing_repo_message(origin, repo_override)
|
|
58
|
+
if repo_override
|
|
59
|
+
"no schwarm repository with id `#{repo_override}`."
|
|
60
|
+
else
|
|
61
|
+
"no schwarm repository matches origin `#{origin}`. Pass --repo <id> to override."
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def inside_work_tree?
|
|
66
|
+
@git.run("rev-parse", "--is-inside-work-tree").success?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def origin_url
|
|
70
|
+
result = @git.run("remote", "get-url", "origin")
|
|
71
|
+
result.success? ? result.stdout.strip : ""
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def github_url?(url)
|
|
75
|
+
url.include?("github.com")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def current_branch
|
|
79
|
+
@git.run("symbolic-ref", "--short", "HEAD").stdout.strip
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def status_porcelain
|
|
83
|
+
@git.run("status", "--porcelain").stdout
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def resolve_repository(origin:, override:)
|
|
87
|
+
if override
|
|
88
|
+
begin
|
|
89
|
+
return @client.repositories.find(override)["data"]
|
|
90
|
+
rescue Faraday::ResourceNotFound
|
|
91
|
+
return nil
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
slug = origin_slug(origin)
|
|
96
|
+
response = @client.repositories.list(query: slug)
|
|
97
|
+
Array(response["data"]).find { |r| normalize_url(r["github_url"]) == normalize_url(origin) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def origin_slug(url)
|
|
101
|
+
# Accept both https://github.com/org/repo[.git] and git@github.com:org/repo.git
|
|
102
|
+
url.sub(%r{\Ahttps?://github\.com/}, "").sub(/\Agit@github\.com:/, "").sub(/\.git\z/, "")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def normalize_url(url)
|
|
106
|
+
return nil if url.nil?
|
|
107
|
+
|
|
108
|
+
url.sub(/\.git\z/, "").sub(/\Agit@github\.com:/, "https://github.com/")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def create_task(repo:, branch:, prompt:, name_override:)
|
|
112
|
+
response = @client.tasks.create(
|
|
113
|
+
name: name_override || branch,
|
|
114
|
+
branch_name: branch,
|
|
115
|
+
prompt: prompt,
|
|
116
|
+
status: "ready",
|
|
117
|
+
github_repository_id: repo["id"]
|
|
118
|
+
)
|
|
119
|
+
data = response["data"]
|
|
120
|
+
Result.new(
|
|
121
|
+
success?: true,
|
|
122
|
+
task_id: data["id"],
|
|
123
|
+
task_url: task_url_for(data["id"]),
|
|
124
|
+
branch_name: data["branch_name"],
|
|
125
|
+
status: data["status"]
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def task_url_for(task_id)
|
|
130
|
+
base = ENV["SCHWARM_URL"] || SchwarmCli::Config.new.url
|
|
131
|
+
"#{base.chomp('/')}/tasks/#{task_id}"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def parse_api_error(error)
|
|
135
|
+
body = error.response&.dig(:body)
|
|
136
|
+
return error.message unless body.is_a?(String)
|
|
137
|
+
|
|
138
|
+
JSON.parse(body).dig("error", "message") || error.message
|
|
139
|
+
rescue JSON::ParserError
|
|
140
|
+
error.message
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def failure(message)
|
|
144
|
+
Result.new(success?: false, error_message: message)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
data/lib/schwarm_cli/version.rb
CHANGED
data/lib/schwarm_cli.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require_relative "schwarm_cli/version"
|
|
4
4
|
require_relative "schwarm_cli/config"
|
|
5
5
|
require_relative "schwarm_cli/client"
|
|
6
|
+
require_relative "schwarm_cli/git"
|
|
7
|
+
require_relative "schwarm_cli/hands_off_task"
|
|
6
8
|
require_relative "schwarm_cli/formatters/json"
|
|
7
9
|
require_relative "schwarm_cli/formatters/table"
|
|
8
10
|
require_relative "schwarm_cli/commands/main"
|
data/schwarm-skill.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Schwarm
|
|
2
|
+
|
|
3
|
+
Schwarm is a distributed system that orchestrates Claude Code agents working on
|
|
4
|
+
tasks in parallel. Use the `schwarm` CLI to dispatch work to Schwarm from your
|
|
5
|
+
local machine.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
The `schwarm` CLI is installed and configured (`schwarm configure`).
|
|
10
|
+
|
|
11
|
+
## Key Concepts
|
|
12
|
+
|
|
13
|
+
- **Repository** — a GitHub repo linked to Schwarm. Tasks run against a repo.
|
|
14
|
+
- **Task** — a unit of work. Schwarm creates an isolated git worktree and runs
|
|
15
|
+
Claude Code on it. Tasks have a lifecycle: draft -> ready -> claimed -> archived.
|
|
16
|
+
- **Dependencies** — tasks can depend on other tasks, forming a DAG. A task with
|
|
17
|
+
unresolved dependencies stays in `waiting` until they're all archived.
|
|
18
|
+
- **Node** — a worker machine that picks up `ready` tasks and runs Claude Code.
|
|
19
|
+
|
|
20
|
+
## Workflows
|
|
21
|
+
|
|
22
|
+
### 1. Dispatch a single task
|
|
23
|
+
|
|
24
|
+
**When:** You have a self-contained piece of work to send to Schwarm.
|
|
25
|
+
|
|
26
|
+
**Steps:**
|
|
27
|
+
1. Find the repository ID:
|
|
28
|
+
```bash
|
|
29
|
+
schwarm repos list
|
|
30
|
+
```
|
|
31
|
+
2. Create the task and start it immediately:
|
|
32
|
+
```bash
|
|
33
|
+
schwarm tasks create --repo <REPO_ID> --name "Fix login bug" --prompt "The login form crashes when email is empty. Fix the validation in app/models/user.rb" --status ready
|
|
34
|
+
```
|
|
35
|
+
Use `--prompt-file ./prompt.md` for long prompts.
|
|
36
|
+
3. Check the created task:
|
|
37
|
+
```bash
|
|
38
|
+
schwarm tasks show <TASK_ID>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Watch for:** If you set `--status ready`, the task starts immediately. Omit
|
|
42
|
+
`--status` (defaults to draft) if you want to review before starting.
|
|
43
|
+
|
|
44
|
+
### 1b. Hand off a WIP branch to schwarm
|
|
45
|
+
|
|
46
|
+
**When:** You have work in progress on a local branch and want schwarm to
|
|
47
|
+
continue from where you left off. Schwarm picks up the same branch and
|
|
48
|
+
continues committing to it.
|
|
49
|
+
|
|
50
|
+
**Steps:**
|
|
51
|
+
1. Make sure your working tree is clean (commit or stash local changes).
|
|
52
|
+
2. Hand off from the current branch:
|
|
53
|
+
```bash
|
|
54
|
+
schwarm tasks handoff --prompt "continue the validation logic, add tests"
|
|
55
|
+
```
|
|
56
|
+
Use `--prompt-file ./handoff.md` for long prompts.
|
|
57
|
+
3. Take note of the task ID printed on success; you can monitor it like any
|
|
58
|
+
other task.
|
|
59
|
+
|
|
60
|
+
**Watch for:**
|
|
61
|
+
- The command refuses to hand off the base branch (`main`). Create a feature
|
|
62
|
+
branch first.
|
|
63
|
+
- It refuses if the working tree is dirty — commit or stash first.
|
|
64
|
+
- If another non-archived task already owns this branch name, the command
|
|
65
|
+
will print the conflicting task ID. Archive or reset it first.
|
|
66
|
+
- The repository is auto-detected from `git remote get-url origin`. Use
|
|
67
|
+
`--repo <id>` if you need to override it (e.g. in a fork).
|
|
68
|
+
|
|
69
|
+
### 2. Dispatch a DAG of tasks
|
|
70
|
+
|
|
71
|
+
**When:** You need to break work into multiple steps with ordering constraints.
|
|
72
|
+
|
|
73
|
+
**Steps:**
|
|
74
|
+
1. Create all tasks as drafts first:
|
|
75
|
+
```bash
|
|
76
|
+
schwarm tasks create --repo <REPO_ID> --name "Step 1: Refactor models" --prompt "..."
|
|
77
|
+
schwarm tasks create --repo <REPO_ID> --name "Step 2: Add tests" --prompt "..." --depends-on <STEP1_ID>
|
|
78
|
+
schwarm tasks create --repo <REPO_ID> --name "Step 3: Update docs" --prompt "..." --depends-on <STEP1_ID>
|
|
79
|
+
schwarm tasks create --repo <REPO_ID> --name "Step 4: Integration tests" --prompt "..." --depends-on <STEP2_ID> <STEP3_ID>
|
|
80
|
+
```
|
|
81
|
+
2. Start all tasks — Schwarm cascades automatically:
|
|
82
|
+
```bash
|
|
83
|
+
schwarm tasks start <STEP1_ID>
|
|
84
|
+
schwarm tasks start <STEP2_ID>
|
|
85
|
+
schwarm tasks start <STEP3_ID>
|
|
86
|
+
schwarm tasks start <STEP4_ID>
|
|
87
|
+
```
|
|
88
|
+
Tasks with unresolved dependencies move to `waiting` and become `ready`
|
|
89
|
+
automatically when their dependencies are archived.
|
|
90
|
+
|
|
91
|
+
**Watch for:** You must start all tasks, not just the root. Starting moves
|
|
92
|
+
drafts to `ready` or `waiting` (depending on whether dependencies are resolved).
|
|
93
|
+
Tasks left as `draft` will never run.
|
|
94
|
+
|
|
95
|
+
### 3. Monitor task progress
|
|
96
|
+
|
|
97
|
+
**When:** You've dispatched tasks and want to check on them.
|
|
98
|
+
|
|
99
|
+
**Steps:**
|
|
100
|
+
1. List active tasks (archived excluded by default):
|
|
101
|
+
```bash
|
|
102
|
+
schwarm tasks list
|
|
103
|
+
schwarm tasks list --status claimed
|
|
104
|
+
schwarm tasks list --status ready
|
|
105
|
+
schwarm tasks list --status error
|
|
106
|
+
```
|
|
107
|
+
2. Check a specific task:
|
|
108
|
+
```bash
|
|
109
|
+
schwarm tasks show <TASK_ID>
|
|
110
|
+
```
|
|
111
|
+
Look at Status, Branch, and Error fields.
|
|
112
|
+
3. View agent session logs for debugging:
|
|
113
|
+
```bash
|
|
114
|
+
schwarm sessions list --json
|
|
115
|
+
schwarm sessions show <SESSION_ID>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Watch for:** A task in `claimed` status means a node is actively working on
|
|
119
|
+
it. `ready` means it's waiting for a node to pick it up.
|
|
120
|
+
|
|
121
|
+
### 4. Send a message to a running task
|
|
122
|
+
|
|
123
|
+
**When:** A task is in `claimed` status and you want to give it additional
|
|
124
|
+
instructions or course corrections.
|
|
125
|
+
|
|
126
|
+
**Steps:**
|
|
127
|
+
1. Send the message:
|
|
128
|
+
```bash
|
|
129
|
+
schwarm tasks message <TASK_ID> --content "Also fix the validation for the password field"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Watch for:** Messages are delivered asynchronously via the node's polling loop.
|
|
133
|
+
The agent will see the message on its next check. Only works for `claimed` tasks.
|
|
134
|
+
|
|
135
|
+
### 5. Handle failures
|
|
136
|
+
|
|
137
|
+
**When:** A task has moved to `error` status.
|
|
138
|
+
|
|
139
|
+
**Steps:**
|
|
140
|
+
1. Check what went wrong:
|
|
141
|
+
```bash
|
|
142
|
+
schwarm tasks show <TASK_ID>
|
|
143
|
+
```
|
|
144
|
+
The Error field shows the failure reason.
|
|
145
|
+
2. Decide: retry or reset.
|
|
146
|
+
- **Retry** — re-runs the task from where it left off (same worktree):
|
|
147
|
+
```bash
|
|
148
|
+
schwarm tasks retry <TASK_ID>
|
|
149
|
+
```
|
|
150
|
+
- **Reset** — moves the task back to `draft` for a fresh start:
|
|
151
|
+
```bash
|
|
152
|
+
schwarm tasks reset <TASK_ID>
|
|
153
|
+
schwarm tasks start <TASK_ID>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Watch for:** Use `retry` for transient failures (node crash, timeout). Use
|
|
157
|
+
`reset` when you need to change the prompt or start clean. After reset, you
|
|
158
|
+
must `start` again.
|
|
159
|
+
|
|
160
|
+
### 6. Discover repos and templates
|
|
161
|
+
|
|
162
|
+
**When:** Before creating tasks, you need to know what's available.
|
|
163
|
+
|
|
164
|
+
**Steps:**
|
|
165
|
+
1. List repositories:
|
|
166
|
+
```bash
|
|
167
|
+
schwarm repos list
|
|
168
|
+
```
|
|
169
|
+
2. List task templates (pre-made prompts):
|
|
170
|
+
```bash
|
|
171
|
+
schwarm templates list
|
|
172
|
+
schwarm templates show <TEMPLATE_ID>
|
|
173
|
+
```
|
|
174
|
+
3. Create a task from a template:
|
|
175
|
+
```bash
|
|
176
|
+
schwarm tasks create --repo <REPO_ID> --name "Apply template" --template <TEMPLATE_ID> --status ready
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 7. Manage recurring tasks
|
|
180
|
+
|
|
181
|
+
**When:** You want a task to run on a schedule (e.g., nightly tests).
|
|
182
|
+
|
|
183
|
+
**Steps:**
|
|
184
|
+
1. Create a recurring task:
|
|
185
|
+
```bash
|
|
186
|
+
schwarm recurring create --repo <REPO_ID> --name "Nightly tests" --schedule "0 0 * * *" --prompt "Run the full test suite and fix any failures"
|
|
187
|
+
```
|
|
188
|
+
2. Toggle on/off:
|
|
189
|
+
```bash
|
|
190
|
+
schwarm recurring toggle <ID>
|
|
191
|
+
```
|
|
192
|
+
3. List existing recurring tasks:
|
|
193
|
+
```bash
|
|
194
|
+
schwarm recurring list --repo <REPO_ID>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 8. Configure skills and agents
|
|
198
|
+
|
|
199
|
+
**When:** You want to customize what skills or agent instructions are available
|
|
200
|
+
for tasks in a repository.
|
|
201
|
+
|
|
202
|
+
**Steps:**
|
|
203
|
+
1. List available skills:
|
|
204
|
+
```bash
|
|
205
|
+
schwarm skills list
|
|
206
|
+
```
|
|
207
|
+
2. Attach a skill to a repository:
|
|
208
|
+
```bash
|
|
209
|
+
schwarm repo-skills create --repo <REPO_ID> --skill <SKILL_ID>
|
|
210
|
+
```
|
|
211
|
+
3. Create a repository agent (custom instructions for all tasks in a repo):
|
|
212
|
+
```bash
|
|
213
|
+
schwarm agents create --repo <REPO_ID> --name "Code style" --prompt "Always follow the project's eslint config"
|
|
214
|
+
```
|
|
215
|
+
4. List agents for a repo:
|
|
216
|
+
```bash
|
|
217
|
+
schwarm agents list --repo <REPO_ID>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Tips
|
|
221
|
+
|
|
222
|
+
- Use `--json` when parsing output programmatically.
|
|
223
|
+
- Check task status before taking actions (e.g., only `retry` works on `error`
|
|
224
|
+
tasks, only `start` works on `draft` tasks).
|
|
225
|
+
- When building a DAG, create all tasks as drafts first, then start them all.
|
|
226
|
+
- For long prompts, write them to a file and use `--prompt-file`.
|
|
227
|
+
- Run `schwarm <command> help` for full flag documentation on any command.
|
|
228
|
+
- `schwarm tasks list` excludes archived tasks by default. Use `--all` to
|
|
229
|
+
include them or `--status archived` to see only archived.
|
|
230
|
+
- `schwarm sessions list` shows only active sessions (pending/running) by
|
|
231
|
+
default. Use `--all` to include terminal states.
|
|
232
|
+
- All list commands support `--limit N` and `--page N` for pagination. The
|
|
233
|
+
footer shows "showing N of M · page X of Y".
|
|
234
|
+
- Run `schwarm skill` to print this document — useful for piping into context
|
|
235
|
+
or refreshing your knowledge of the available workflows.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: schwarm-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vincent Garrigues
|
|
@@ -86,6 +86,7 @@ executables:
|
|
|
86
86
|
extensions: []
|
|
87
87
|
extra_rdoc_files: []
|
|
88
88
|
files:
|
|
89
|
+
- README.md
|
|
89
90
|
- bin/schwarm
|
|
90
91
|
- lib/schwarm_cli.rb
|
|
91
92
|
- lib/schwarm_cli/client.rb
|
|
@@ -119,7 +120,10 @@ files:
|
|
|
119
120
|
- lib/schwarm_cli/config.rb
|
|
120
121
|
- lib/schwarm_cli/formatters/json.rb
|
|
121
122
|
- lib/schwarm_cli/formatters/table.rb
|
|
123
|
+
- lib/schwarm_cli/git.rb
|
|
124
|
+
- lib/schwarm_cli/hands_off_task.rb
|
|
122
125
|
- lib/schwarm_cli/version.rb
|
|
126
|
+
- schwarm-skill.md
|
|
123
127
|
homepage: https://github.com/getdexter/schwarm
|
|
124
128
|
licenses:
|
|
125
129
|
- MIT
|