schwarm-cli 0.1.7 → 0.1.9
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/lib/schwarm_cli/client/secret_files.rb +2 -2
- data/lib/schwarm_cli/client/skill_files.rb +25 -4
- data/lib/schwarm_cli/client.rb +5 -3
- data/lib/schwarm_cli/commands/agent_subscriptions.rb +107 -0
- data/lib/schwarm_cli/commands/agents.rb +40 -49
- data/lib/schwarm_cli/commands/base.rb +28 -1
- data/lib/schwarm_cli/commands/main.rb +1 -5
- data/lib/schwarm_cli/commands/recurring.rb +20 -13
- data/lib/schwarm_cli/commands/repo_skills.rb +9 -6
- data/lib/schwarm_cli/commands/secrets.rb +4 -1
- data/lib/schwarm_cli/commands/skill_files.rb +36 -28
- data/lib/schwarm_cli/commands/templates.rb +8 -6
- data/lib/schwarm_cli/version.rb +1 -1
- data/schwarm-skill.md +9 -4
- metadata +16 -2
- data/lib/schwarm_cli/commands/shared_agents.rb +0 -84
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6eb34f4bf3e7d00ae361c8f3e2e92543b70a3cf607bca49c2621db6308ff7347
|
|
4
|
+
data.tar.gz: '081ef605e386eef7aa68d0f41646a16c270cf5760bfeb1b5f7270f4b3b5ecf7e'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 259bfe442c01d5d6a7906fcbf474927013374d8283f7083552b5e30137603e94ae94d2f92c3211c173807236d37b188de25e45594179c108fb49562af521a903
|
|
7
|
+
data.tar.gz: 118a0a5418bd88cc5f17e6f06bd80221830fdc2d8b5a4e9a83276894cd64779bdc1b9015012f5e5f1068ee1805800260f8f9dbafbde9327247bf1853cd78b0d6
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module SchwarmCli
|
|
4
4
|
class Client
|
|
5
5
|
class SecretFiles < Resource
|
|
6
|
-
def list(repository_id: nil, page: nil, per_page: nil)
|
|
7
|
-
params = { repository_id:, page:, per_page: }.compact
|
|
6
|
+
def list(repository_id: nil, query: nil, page: nil, per_page: nil)
|
|
7
|
+
params = { repository_id:, q: query, page:, per_page: }.compact
|
|
8
8
|
get("/api/v2/secret_files", params).body
|
|
9
9
|
end
|
|
10
10
|
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "faraday/multipart"
|
|
4
|
+
|
|
3
5
|
module SchwarmCli
|
|
4
6
|
class Client
|
|
7
|
+
# The skill_files endpoint expects path as a top-level form param plus a
|
|
8
|
+
# multipart-attached `file` (Active Storage), NOT a JSON body wrapped in
|
|
9
|
+
# `{skill_file: {...}}`. Build a multipart form for create/update.
|
|
5
10
|
class SkillFiles < Resource
|
|
6
11
|
def list(skill_id:, page: nil, per_page: nil)
|
|
7
12
|
params = { page:, per_page: }.compact
|
|
@@ -12,18 +17,34 @@ module SchwarmCli
|
|
|
12
17
|
get("/api/v2/skills/#{skill_id}/files/#{id}").body
|
|
13
18
|
end
|
|
14
19
|
|
|
15
|
-
def create(skill_id:,
|
|
16
|
-
post("/api/v2/skills/#{skill_id}/files",
|
|
20
|
+
def create(skill_id:, path:, content:)
|
|
21
|
+
post("/api/v2/skills/#{skill_id}/files", multipart_body(path:, content:)).body
|
|
17
22
|
end
|
|
18
23
|
|
|
19
|
-
def update(skill_id:, id:,
|
|
20
|
-
|
|
24
|
+
def update(skill_id:, id:, path: nil, content: nil)
|
|
25
|
+
body = {}
|
|
26
|
+
body[:path] = path if path
|
|
27
|
+
body[:file] = file_part(path:, content:) if content
|
|
28
|
+
patch("/api/v2/skills/#{skill_id}/files/#{id}", body).body
|
|
21
29
|
end
|
|
22
30
|
|
|
23
31
|
def destroy(skill_id:, id:)
|
|
24
32
|
delete("/api/v2/skills/#{skill_id}/files/#{id}")
|
|
25
33
|
nil
|
|
26
34
|
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def multipart_body(path:, content:)
|
|
39
|
+
{ path:, file: file_part(path:, content:) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Active Storage needs a real IO with a filename. Wrap the in-memory
|
|
43
|
+
# content in a StringIO and hand it to Faraday::Multipart::FilePart.
|
|
44
|
+
def file_part(path:, content:)
|
|
45
|
+
io = StringIO.new(content.to_s)
|
|
46
|
+
Faraday::Multipart::FilePart.new(io, "application/octet-stream", File.basename(path.to_s))
|
|
47
|
+
end
|
|
27
48
|
end
|
|
28
49
|
end
|
|
29
50
|
end
|
data/lib/schwarm_cli/client.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "faraday"
|
|
4
|
+
require "faraday/multipart"
|
|
4
5
|
require "faraday/net_http_persistent"
|
|
5
6
|
require "faraday/retry"
|
|
6
7
|
|
|
@@ -21,7 +22,7 @@ require_relative "client/user_messages"
|
|
|
21
22
|
module SchwarmCli
|
|
22
23
|
class Client
|
|
23
24
|
attr_reader :tasks, :repositories, :skills, :skill_files, :templates,
|
|
24
|
-
:recurring, :agents, :
|
|
25
|
+
:recurring, :agents, :subscriptions, :secrets, :sessions,
|
|
25
26
|
:repo_skills, :messages
|
|
26
27
|
|
|
27
28
|
def get(...) = conn.get(...)
|
|
@@ -52,8 +53,8 @@ module SchwarmCli
|
|
|
52
53
|
@skill_files = SkillFiles.new(client: self)
|
|
53
54
|
@templates = TaskTemplates.new(client: self)
|
|
54
55
|
@recurring = RecurringTasks.new(client: self)
|
|
55
|
-
@agents =
|
|
56
|
-
@
|
|
56
|
+
@agents = SharedAgents.new(client: self)
|
|
57
|
+
@subscriptions = RepositoryAgents.new(client: self)
|
|
57
58
|
@secrets = SecretFiles.new(client: self)
|
|
58
59
|
@sessions = AgentSessions.new(client: self)
|
|
59
60
|
@repo_skills = RepositorySkills.new(client: self)
|
|
@@ -62,6 +63,7 @@ module SchwarmCli
|
|
|
62
63
|
|
|
63
64
|
def conn
|
|
64
65
|
@conn ||= Faraday.new(url:, headers:, **faraday_options) do |f|
|
|
66
|
+
f.request :multipart
|
|
65
67
|
f.request :json
|
|
66
68
|
f.request :authorization, "Bearer", api_key
|
|
67
69
|
f.request :retry, retry_options
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SchwarmCli
|
|
4
|
+
module Commands
|
|
5
|
+
class AgentSubscriptions < Base
|
|
6
|
+
desc "list", "List repository subscriptions"
|
|
7
|
+
option :repo, type: :string, desc: "Filter by repository ID, owner/repo, or GitHub URL"
|
|
8
|
+
option :query, type: :string, desc: "Search by name"
|
|
9
|
+
pagination_options
|
|
10
|
+
def list
|
|
11
|
+
handle_errors do
|
|
12
|
+
data = fetch_paged do |page_params|
|
|
13
|
+
client.subscriptions.list(repository_id: resolve_repo(options[:repo]), query: options[:query],
|
|
14
|
+
**page_params)
|
|
15
|
+
end
|
|
16
|
+
output_list(data, columns: [%w[ID id], %w[NAME name], %w[REPO github_repository_name],
|
|
17
|
+
%w[ENABLED enabled], %w[SCHEDULE schedule]])
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
desc "show ID", "Show subscription details"
|
|
22
|
+
def show(id)
|
|
23
|
+
handle_errors do
|
|
24
|
+
data = client.subscriptions.find(id)
|
|
25
|
+
output_record(data, fields: {
|
|
26
|
+
"ID" => "id", "Name" => "name", "Repository" => "github_repository_name",
|
|
27
|
+
"Agent" => "shared_agent_id", "Enabled" => "enabled",
|
|
28
|
+
"Schedule" => "schedule",
|
|
29
|
+
"Additional Instructions" => "additional_instructions",
|
|
30
|
+
"Created" => "created_at", "Updated" => "updated_at"
|
|
31
|
+
})
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc "create", "Subscribe an agent to a repository"
|
|
36
|
+
option :repo, type: :string, required: true, desc: "Repository ID, owner/repo, or GitHub URL"
|
|
37
|
+
option :agent, type: :string, required: true,
|
|
38
|
+
desc: "Agent ID to subscribe to this repository"
|
|
39
|
+
option :additional_instructions, type: :string,
|
|
40
|
+
desc: "Per-repository additional instructions appended to the shared prompt"
|
|
41
|
+
option :enabled, type: :boolean, default: false,
|
|
42
|
+
desc: "Whether the subscription is active (default: false; toggle to enable)"
|
|
43
|
+
def create
|
|
44
|
+
handle_errors do
|
|
45
|
+
data = client.subscriptions.create(**create_attrs)
|
|
46
|
+
output_record(data, fields: { "ID" => "id", "Name" => "name", "Enabled" => "enabled" })
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
desc "update ID", "Update a subscription"
|
|
51
|
+
option :additional_instructions, type: :string,
|
|
52
|
+
desc: "Per-repository additional instructions"
|
|
53
|
+
option :enabled, type: :boolean, desc: "Whether the subscription is active"
|
|
54
|
+
def update(id)
|
|
55
|
+
handle_errors do
|
|
56
|
+
data = client.subscriptions.update(id, **update_attrs)
|
|
57
|
+
output_record(data, fields: { "ID" => "id", "Name" => "name", "Enabled" => "enabled" })
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
desc "delete ID", "Delete a subscription"
|
|
62
|
+
def delete(id)
|
|
63
|
+
handle_errors do
|
|
64
|
+
client.subscriptions.destroy(id)
|
|
65
|
+
puts "Subscription #{id} deleted."
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
desc "toggle ID", "Toggle subscription enabled/disabled"
|
|
70
|
+
def toggle(id)
|
|
71
|
+
handle_errors do
|
|
72
|
+
data = client.subscriptions.toggle(id)
|
|
73
|
+
status = data.dig("data", "enabled") ? "enabled" : "disabled"
|
|
74
|
+
puts "Subscription #{id} #{status}."
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
desc "run-now ID", "Trigger subscription to run immediately"
|
|
79
|
+
map "run-now" => :run_now
|
|
80
|
+
def run_now(id)
|
|
81
|
+
handle_errors do
|
|
82
|
+
client.subscriptions.run_now(id)
|
|
83
|
+
puts "Subscription #{id} triggered."
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def create_attrs
|
|
90
|
+
attrs = {
|
|
91
|
+
shared_agent_id: options[:agent],
|
|
92
|
+
github_repository_id: resolve_repo(options[:repo]),
|
|
93
|
+
enabled: options[:enabled]
|
|
94
|
+
}
|
|
95
|
+
attrs[:additional_instructions] = options[:additional_instructions] if options[:additional_instructions]
|
|
96
|
+
attrs
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def update_attrs
|
|
100
|
+
{}.tap do |attrs|
|
|
101
|
+
attrs[:additional_instructions] = options[:additional_instructions] if options[:additional_instructions]
|
|
102
|
+
attrs[:enabled] = options[:enabled] unless options[:enabled].nil?
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "agent_subscriptions"
|
|
4
|
+
|
|
3
5
|
module SchwarmCli
|
|
4
6
|
module Commands
|
|
5
7
|
class Agents < Base
|
|
6
|
-
desc "
|
|
7
|
-
|
|
8
|
+
desc "subscriptions SUBCOMMAND", "Manage repository subscriptions"
|
|
9
|
+
subcommand "subscriptions", AgentSubscriptions
|
|
10
|
+
|
|
11
|
+
desc "list", "List agents"
|
|
8
12
|
option :query, type: :string, desc: "Search by name"
|
|
9
13
|
pagination_options
|
|
10
14
|
def list
|
|
11
15
|
handle_errors do
|
|
12
16
|
data = fetch_paged do |page_params|
|
|
13
|
-
client.agents.list(
|
|
17
|
+
client.agents.list(query: options[:query], **page_params)
|
|
14
18
|
end
|
|
15
|
-
output_list(data, columns: [%w[ID id], %w[NAME name], %w[
|
|
16
|
-
%w[
|
|
19
|
+
output_list(data, columns: [%w[ID id], %w[NAME name], %w[ENABLED enabled],
|
|
20
|
+
%w[SCHEDULE cron_expression]])
|
|
17
21
|
end
|
|
18
22
|
end
|
|
19
23
|
|
|
@@ -21,43 +25,40 @@ module SchwarmCli
|
|
|
21
25
|
def show(id)
|
|
22
26
|
handle_errors do
|
|
23
27
|
data = client.agents.find(id)
|
|
24
|
-
output_record(data, fields:
|
|
25
|
-
"ID" => "id", "Name" => "name", "Repository" => "github_repository_name",
|
|
26
|
-
"Shared Agent" => "shared_agent_id", "Enabled" => "enabled",
|
|
27
|
-
"Schedule" => "schedule",
|
|
28
|
-
"Additional Instructions" => "additional_instructions",
|
|
29
|
-
"Created" => "created_at", "Updated" => "updated_at"
|
|
30
|
-
})
|
|
28
|
+
output_record(data, fields: agent_fields)
|
|
31
29
|
end
|
|
32
30
|
end
|
|
33
31
|
|
|
34
|
-
desc "create", "Create
|
|
35
|
-
option :
|
|
36
|
-
option :
|
|
37
|
-
|
|
38
|
-
option :
|
|
39
|
-
|
|
40
|
-
option :enabled, type: :boolean, default: true,
|
|
41
|
-
desc: "Whether the agent runs (default: true)"
|
|
32
|
+
desc "create", "Create an agent"
|
|
33
|
+
option :name, type: :string, required: true, desc: "Agent name"
|
|
34
|
+
option :prompt, type: :string, required: true, desc: "Agent prompt"
|
|
35
|
+
option :schedule, type: :string, required: true, desc: "Cron schedule (e.g. \"0 0 * * *\")"
|
|
36
|
+
option :enabled, type: :boolean, default: false,
|
|
37
|
+
desc: "Whether the agent runs (default: false; toggle to enable)"
|
|
42
38
|
def create
|
|
43
39
|
handle_errors do
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
attrs = {
|
|
41
|
+
name: options[:name], prompt: options[:prompt],
|
|
42
|
+
cron_expression: options[:schedule], enabled: options[:enabled]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
data = client.agents.create(**attrs)
|
|
46
|
+
output_record(data, fields: agent_fields)
|
|
46
47
|
end
|
|
47
48
|
end
|
|
48
49
|
|
|
49
|
-
desc "update ID", "Update
|
|
50
|
-
option :
|
|
51
|
-
|
|
52
|
-
option :
|
|
50
|
+
desc "update ID", "Update an agent"
|
|
51
|
+
option :name, type: :string, desc: "Agent name"
|
|
52
|
+
option :prompt, type: :string, desc: "Agent prompt"
|
|
53
|
+
option :schedule, type: :string, desc: "Cron schedule"
|
|
53
54
|
def update(id)
|
|
54
55
|
handle_errors do
|
|
55
56
|
data = client.agents.update(id, **update_attrs)
|
|
56
|
-
output_record(data, fields:
|
|
57
|
+
output_record(data, fields: agent_fields)
|
|
57
58
|
end
|
|
58
59
|
end
|
|
59
60
|
|
|
60
|
-
desc "delete ID", "Delete
|
|
61
|
+
desc "delete ID", "Delete an agent"
|
|
61
62
|
def delete(id)
|
|
62
63
|
handle_errors do
|
|
63
64
|
client.agents.destroy(id)
|
|
@@ -74,33 +75,23 @@ module SchwarmCli
|
|
|
74
75
|
end
|
|
75
76
|
end
|
|
76
77
|
|
|
77
|
-
desc "run-now ID", "Trigger agent to run immediately"
|
|
78
|
-
map "run-now" => :run_now
|
|
79
|
-
def run_now(id)
|
|
80
|
-
handle_errors do
|
|
81
|
-
client.agents.run_now(id)
|
|
82
|
-
puts "Agent #{id} triggered."
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
78
|
private
|
|
87
79
|
|
|
88
|
-
def create_attrs
|
|
89
|
-
attrs = {
|
|
90
|
-
shared_agent_id: options[:shared_agent],
|
|
91
|
-
github_repository_id: resolve_repo(options[:repo]),
|
|
92
|
-
enabled: options[:enabled]
|
|
93
|
-
}
|
|
94
|
-
attrs[:additional_instructions] = options[:additional_instructions] if options[:additional_instructions]
|
|
95
|
-
attrs
|
|
96
|
-
end
|
|
97
|
-
|
|
98
80
|
def update_attrs
|
|
99
81
|
{}.tap do |attrs|
|
|
100
|
-
attrs[:
|
|
101
|
-
attrs[:
|
|
82
|
+
attrs[:name] = options[:name] if options[:name]
|
|
83
|
+
attrs[:prompt] = options[:prompt] if options[:prompt]
|
|
84
|
+
attrs[:cron_expression] = options[:schedule] if options[:schedule]
|
|
102
85
|
end
|
|
103
86
|
end
|
|
87
|
+
|
|
88
|
+
def agent_fields
|
|
89
|
+
{
|
|
90
|
+
"ID" => "id", "Name" => "name", "Enabled" => "enabled",
|
|
91
|
+
"Schedule" => "cron_expression", "Prompt" => "prompt",
|
|
92
|
+
"Created" => "created_at", "Updated" => "updated_at"
|
|
93
|
+
}
|
|
94
|
+
end
|
|
104
95
|
end
|
|
105
96
|
end
|
|
106
97
|
end
|
|
@@ -5,12 +5,39 @@ require "thor"
|
|
|
5
5
|
module SchwarmCli
|
|
6
6
|
module Commands
|
|
7
7
|
class Base < Thor
|
|
8
|
-
|
|
8
|
+
# No `default: false` on :json — we need to distinguish "not provided"
|
|
9
|
+
# (nil) from "explicitly negated" (--no-json => false) so nested
|
|
10
|
+
# subcommands can opt out of an inherited --json (see options below).
|
|
11
|
+
class_option :json, type: :boolean, desc: "Output as JSON"
|
|
9
12
|
class_option :url, type: :string, desc: "Schwarm server URL (overrides config)"
|
|
10
13
|
class_option :token, type: :string, desc: "API key (overrides config)"
|
|
11
14
|
|
|
12
15
|
def self.exit_on_failure? = true
|
|
13
16
|
|
|
17
|
+
# Thor parses class_options at each subcommand level independently, so
|
|
18
|
+
# `--json` at a doubly-nested invocation (`agents subscriptions list
|
|
19
|
+
# --json`) is consumed by the outer class and never reaches the inner.
|
|
20
|
+
# Merge parent_options for global flags (json, url, token) so they
|
|
21
|
+
# propagate transparently into nested subcommands.
|
|
22
|
+
#
|
|
23
|
+
# We can't use Hash#merge with a block because Thor returns a
|
|
24
|
+
# HashWithIndifferentAccess whose `merge` ignores the block argument
|
|
25
|
+
# (lib/thor/core_ext/hash_with_indifferent_access.rb:53). Walk the
|
|
26
|
+
# inherited keys explicitly so an explicit child value (including
|
|
27
|
+
# `false` from --no-json) wins over an inherited parent value.
|
|
28
|
+
INHERITED_OPTIONS = %w[json url token].freeze
|
|
29
|
+
|
|
30
|
+
no_commands do
|
|
31
|
+
def options
|
|
32
|
+
merged = super.dup
|
|
33
|
+
parent = parent_options || {}
|
|
34
|
+
INHERITED_OPTIONS.each do |key|
|
|
35
|
+
merged[key] = parent[key] if merged[key].nil? && !parent[key].nil?
|
|
36
|
+
end
|
|
37
|
+
merged
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
14
41
|
DEFAULT_LIST_LIMIT = 20
|
|
15
42
|
|
|
16
43
|
# Declares --limit, --page, --all options for the next method.
|
|
@@ -7,7 +7,6 @@ require_relative "templates"
|
|
|
7
7
|
require_relative "skills"
|
|
8
8
|
require_relative "skill_files"
|
|
9
9
|
require_relative "agents"
|
|
10
|
-
require_relative "shared_agents"
|
|
11
10
|
require_relative "recurring"
|
|
12
11
|
require_relative "secrets"
|
|
13
12
|
require_relative "sessions"
|
|
@@ -34,12 +33,9 @@ module SchwarmCli
|
|
|
34
33
|
desc "skill-files SUBCOMMAND", "Manage skill files"
|
|
35
34
|
subcommand "skill_files", SkillFilesCmd
|
|
36
35
|
|
|
37
|
-
desc "agents SUBCOMMAND", "Manage repository
|
|
36
|
+
desc "agents SUBCOMMAND", "Manage agents and their repository subscriptions"
|
|
38
37
|
subcommand "agents", Agents
|
|
39
38
|
|
|
40
|
-
desc "shared-agents SUBCOMMAND", "Manage shared agents"
|
|
41
|
-
subcommand "shared_agents", SharedAgentsCmd
|
|
42
|
-
|
|
43
39
|
desc "recurring SUBCOMMAND", "Manage recurring tasks"
|
|
44
40
|
subcommand "recurring", Recurring
|
|
45
41
|
|
|
@@ -21,11 +21,7 @@ module SchwarmCli
|
|
|
21
21
|
def show(id)
|
|
22
22
|
handle_errors do
|
|
23
23
|
data = client.recurring.find(id)
|
|
24
|
-
output_record(data, fields:
|
|
25
|
-
"ID" => "id", "Name" => "name", "Repository" => "github_repository_id",
|
|
26
|
-
"Enabled" => "enabled", "Schedule" => "cron_expression", "Prompt" => "prompt",
|
|
27
|
-
"Created" => "created_at", "Updated" => "updated_at"
|
|
28
|
-
})
|
|
24
|
+
output_record(data, fields: recurring_fields)
|
|
29
25
|
end
|
|
30
26
|
end
|
|
31
27
|
|
|
@@ -34,15 +30,12 @@ module SchwarmCli
|
|
|
34
30
|
option :repo, type: :string, required: true, desc: "Repository ID, owner/repo, or GitHub URL"
|
|
35
31
|
option :prompt, type: :string, required: true, desc: "Task prompt"
|
|
36
32
|
option :schedule, type: :string, required: true, desc: "Cron schedule"
|
|
33
|
+
option :enabled, type: :boolean, default: false,
|
|
34
|
+
desc: "Whether the recurring task fires (default: false; toggle to enable)"
|
|
37
35
|
def create
|
|
38
36
|
handle_errors do
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
prompt: options[:prompt], cron_expression: options[:schedule]
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
data = client.recurring.create(**attrs)
|
|
45
|
-
output_record(data, fields: { "ID" => "id", "Name" => "name", "Schedule" => "cron_expression" })
|
|
37
|
+
data = client.recurring.create(**create_attrs)
|
|
38
|
+
output_record(data, fields: recurring_fields)
|
|
46
39
|
end
|
|
47
40
|
end
|
|
48
41
|
|
|
@@ -53,7 +46,7 @@ module SchwarmCli
|
|
|
53
46
|
def update(id)
|
|
54
47
|
handle_errors do
|
|
55
48
|
data = client.recurring.update(id, **update_attrs)
|
|
56
|
-
output_record(data, fields:
|
|
49
|
+
output_record(data, fields: recurring_fields)
|
|
57
50
|
end
|
|
58
51
|
end
|
|
59
52
|
|
|
@@ -76,6 +69,20 @@ module SchwarmCli
|
|
|
76
69
|
|
|
77
70
|
private
|
|
78
71
|
|
|
72
|
+
def create_attrs
|
|
73
|
+
{
|
|
74
|
+
name: options[:name], github_repository_id: resolve_repo(options[:repo]),
|
|
75
|
+
prompt: options[:prompt], cron_expression: options[:schedule],
|
|
76
|
+
enabled: options[:enabled]
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def recurring_fields
|
|
81
|
+
{ "ID" => "id", "Name" => "name", "Repository" => "github_repository_id",
|
|
82
|
+
"Enabled" => "enabled", "Schedule" => "cron_expression", "Prompt" => "prompt",
|
|
83
|
+
"Created" => "created_at", "Updated" => "updated_at" }
|
|
84
|
+
end
|
|
85
|
+
|
|
79
86
|
def update_attrs
|
|
80
87
|
{}.tap do |attrs|
|
|
81
88
|
attrs[:name] = options[:name] if options[:name]
|
|
@@ -23,10 +23,7 @@ module SchwarmCli
|
|
|
23
23
|
def show(id)
|
|
24
24
|
handle_errors do
|
|
25
25
|
data = client.repo_skills.find(id)
|
|
26
|
-
output_record(data, fields:
|
|
27
|
-
"ID" => "id", "Repository" => "github_repository_id",
|
|
28
|
-
"Skill" => "skill_id", "Created" => "created_at"
|
|
29
|
-
})
|
|
26
|
+
output_record(data, fields: repo_skill_fields)
|
|
30
27
|
end
|
|
31
28
|
end
|
|
32
29
|
|
|
@@ -38,8 +35,7 @@ module SchwarmCli
|
|
|
38
35
|
data = client.repo_skills.create(
|
|
39
36
|
github_repository_id: resolve_repo(options[:repo]), skill_id: options[:skill]
|
|
40
37
|
)
|
|
41
|
-
output_record(data, fields:
|
|
42
|
-
"Skill" => "skill_id" })
|
|
38
|
+
output_record(data, fields: repo_skill_fields)
|
|
43
39
|
end
|
|
44
40
|
end
|
|
45
41
|
|
|
@@ -50,6 +46,13 @@ module SchwarmCli
|
|
|
50
46
|
puts "Repository-skill association #{id} deleted."
|
|
51
47
|
end
|
|
52
48
|
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def repo_skill_fields
|
|
53
|
+
{ "ID" => "id", "Repository" => "github_repository_name",
|
|
54
|
+
"Skill" => "skill_name", "Created" => "created_at" }
|
|
55
|
+
end
|
|
53
56
|
end
|
|
54
57
|
end
|
|
55
58
|
end
|
|
@@ -5,11 +5,14 @@ module SchwarmCli
|
|
|
5
5
|
class Secrets < Base
|
|
6
6
|
desc "list", "List secret files"
|
|
7
7
|
option :repo, type: :string, desc: "Filter by repository ID, owner/repo, or GitHub URL"
|
|
8
|
+
option :query, type: :string, desc: "Search by path"
|
|
8
9
|
pagination_options
|
|
9
10
|
def list
|
|
10
11
|
handle_errors do
|
|
11
12
|
data = fetch_paged do |page_params|
|
|
12
|
-
client.secrets.list(
|
|
13
|
+
client.secrets.list(
|
|
14
|
+
repository_id: resolve_repo(options[:repo]), query: options[:query], **page_params
|
|
15
|
+
)
|
|
13
16
|
end
|
|
14
17
|
output_list(data, columns: [%w[ID id], %w[REPO github_repository_name], %w[PATH path]])
|
|
15
18
|
end
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
module SchwarmCli
|
|
4
4
|
module Commands
|
|
5
5
|
class SkillFilesCmd < Base
|
|
6
|
-
class_option :skill, type: :string, required: true, desc: "Skill ID"
|
|
7
|
-
|
|
8
6
|
desc "list", "List skill files"
|
|
7
|
+
option :skill, type: :string, required: true, desc: "Skill ID"
|
|
9
8
|
pagination_options
|
|
10
9
|
def list
|
|
11
10
|
handle_errors do
|
|
@@ -17,39 +16,46 @@ module SchwarmCli
|
|
|
17
16
|
end
|
|
18
17
|
|
|
19
18
|
desc "show ID", "Show skill file details"
|
|
19
|
+
option :skill, type: :string, required: true, desc: "Skill ID"
|
|
20
20
|
def show(id)
|
|
21
21
|
handle_errors do
|
|
22
22
|
data = client.skill_files.find(skill_id: options[:skill], id:)
|
|
23
|
-
output_record(data, fields:
|
|
24
|
-
"ID" => "id", "Path" => "path", "Content" => "content", "Created" => "created_at"
|
|
25
|
-
})
|
|
23
|
+
output_record(data, fields: skill_file_fields)
|
|
26
24
|
end
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
desc "create", "Create a skill file"
|
|
30
|
-
option :
|
|
31
|
-
option :
|
|
32
|
-
option :
|
|
28
|
+
option :skill, type: :string, required: true, desc: "Skill ID"
|
|
29
|
+
option :path, type: :string, required: true, desc: "File path within the skill (e.g. SKILL.md)"
|
|
30
|
+
option :content, type: :string, desc: "File content (mutually exclusive with --file)"
|
|
31
|
+
option :file, type: :string, desc: "Read content from file (mutually exclusive with --content)"
|
|
33
32
|
def create
|
|
34
33
|
handle_errors do
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
data = client.skill_files.create(
|
|
35
|
+
skill_id: options[:skill], path: options[:path],
|
|
36
|
+
content: read_content(required: true)
|
|
37
|
+
)
|
|
38
|
+
output_record(data, fields: skill_file_fields)
|
|
38
39
|
end
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
desc "update ID", "Update a skill file"
|
|
43
|
+
option :skill, type: :string, required: true, desc: "Skill ID"
|
|
42
44
|
option :path, type: :string, desc: "File path"
|
|
43
|
-
option :content, type: :string, desc: "File content"
|
|
44
|
-
option :file, type: :string, desc: "Read content from file"
|
|
45
|
+
option :content, type: :string, desc: "File content (mutually exclusive with --file)"
|
|
46
|
+
option :file, type: :string, desc: "Read content from file (mutually exclusive with --content)"
|
|
45
47
|
def update(id)
|
|
46
48
|
handle_errors do
|
|
47
|
-
data = client.skill_files.update(
|
|
48
|
-
|
|
49
|
+
data = client.skill_files.update(
|
|
50
|
+
skill_id: options[:skill], id:,
|
|
51
|
+
path: options[:path], content: read_content(required: false)
|
|
52
|
+
)
|
|
53
|
+
output_record(data, fields: skill_file_fields)
|
|
49
54
|
end
|
|
50
55
|
end
|
|
51
56
|
|
|
52
57
|
desc "delete ID", "Delete a skill file"
|
|
58
|
+
option :skill, type: :string, required: true, desc: "Skill ID"
|
|
53
59
|
def delete(id)
|
|
54
60
|
handle_errors do
|
|
55
61
|
client.skill_files.destroy(skill_id: options[:skill], id:)
|
|
@@ -59,21 +65,23 @@ module SchwarmCli
|
|
|
59
65
|
|
|
60
66
|
private
|
|
61
67
|
|
|
62
|
-
def
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
attrs[:content] = read_content if options[:content] || options[:file]
|
|
66
|
-
end
|
|
68
|
+
def skill_file_fields
|
|
69
|
+
{ "ID" => "id", "Path" => "path", "Skill" => "skill_id",
|
|
70
|
+
"Created" => "created_at", "Updated" => "updated_at" }
|
|
67
71
|
end
|
|
68
72
|
|
|
69
|
-
def read_content
|
|
70
|
-
if options[:file]
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
def read_content(required:)
|
|
74
|
+
abort "Error: --content and --file are mutually exclusive." if options[:content] && options[:file]
|
|
75
|
+
return options[:content] if options[:content]
|
|
76
|
+
return read_file(options[:file]) if options[:file]
|
|
77
|
+
|
|
78
|
+
abort "Error: one of --content or --file is required." if required
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def read_file(path)
|
|
82
|
+
expanded = File.expand_path(path)
|
|
83
|
+
abort "Error: File not found: #{expanded}" unless File.exist?(expanded)
|
|
84
|
+
File.read(expanded)
|
|
77
85
|
end
|
|
78
86
|
end
|
|
79
87
|
end
|
|
@@ -19,10 +19,7 @@ module SchwarmCli
|
|
|
19
19
|
def show(id)
|
|
20
20
|
handle_errors do
|
|
21
21
|
data = client.templates.find(id)
|
|
22
|
-
output_record(data, fields:
|
|
23
|
-
"ID" => "id", "Name" => "name", "Description" => "description",
|
|
24
|
-
"Prompt" => "prompt_prefix", "Created" => "created_at"
|
|
25
|
-
})
|
|
22
|
+
output_record(data, fields: template_fields)
|
|
26
23
|
end
|
|
27
24
|
end
|
|
28
25
|
|
|
@@ -36,7 +33,7 @@ module SchwarmCli
|
|
|
36
33
|
attrs[:description] = options[:description] if options[:description]
|
|
37
34
|
|
|
38
35
|
data = client.templates.create(**attrs)
|
|
39
|
-
output_record(data, fields:
|
|
36
|
+
output_record(data, fields: template_fields)
|
|
40
37
|
end
|
|
41
38
|
end
|
|
42
39
|
|
|
@@ -47,7 +44,7 @@ module SchwarmCli
|
|
|
47
44
|
def update(id)
|
|
48
45
|
handle_errors do
|
|
49
46
|
data = client.templates.update(id, **update_attrs)
|
|
50
|
-
output_record(data, fields:
|
|
47
|
+
output_record(data, fields: template_fields)
|
|
51
48
|
end
|
|
52
49
|
end
|
|
53
50
|
|
|
@@ -61,6 +58,11 @@ module SchwarmCli
|
|
|
61
58
|
|
|
62
59
|
private
|
|
63
60
|
|
|
61
|
+
def template_fields
|
|
62
|
+
{ "ID" => "id", "Name" => "name", "Description" => "description",
|
|
63
|
+
"Prompt" => "prompt_prefix", "Created" => "created_at", "Updated" => "updated_at" }
|
|
64
|
+
end
|
|
65
|
+
|
|
64
66
|
def update_attrs
|
|
65
67
|
{}.tap do |attrs|
|
|
66
68
|
attrs[:name] = options[:name] if options[:name]
|
data/lib/schwarm_cli/version.rb
CHANGED
data/schwarm-skill.md
CHANGED
|
@@ -266,13 +266,18 @@ for tasks in a repository.
|
|
|
266
266
|
```bash
|
|
267
267
|
schwarm repo-skills create --repo <REPO_ID> --skill <SKILL_ID>
|
|
268
268
|
```
|
|
269
|
-
3. Create
|
|
269
|
+
3. Create an agent (a reusable definition with a name, prompt, and optional schedule):
|
|
270
270
|
```bash
|
|
271
|
-
schwarm agents create --
|
|
271
|
+
schwarm agents create --name "Code style" --prompt "Always follow the project's eslint config"
|
|
272
272
|
```
|
|
273
|
-
4.
|
|
273
|
+
4. Subscribe an agent to a repository so it runs against that repo:
|
|
274
274
|
```bash
|
|
275
|
-
schwarm agents
|
|
275
|
+
schwarm agents subscriptions create --repo <REPO_ID> --agent <AGENT_ID>
|
|
276
|
+
```
|
|
277
|
+
5. List subscriptions for a repo, or all agents:
|
|
278
|
+
```bash
|
|
279
|
+
schwarm agents subscriptions list --repo <REPO_ID>
|
|
280
|
+
schwarm agents list
|
|
276
281
|
```
|
|
277
282
|
|
|
278
283
|
## Tips
|
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.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vincent Garrigues
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '2.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: faraday-multipart
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: faraday-net_http_persistent
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -103,6 +117,7 @@ files:
|
|
|
103
117
|
- lib/schwarm_cli/client/task_templates.rb
|
|
104
118
|
- lib/schwarm_cli/client/tasks.rb
|
|
105
119
|
- lib/schwarm_cli/client/user_messages.rb
|
|
120
|
+
- lib/schwarm_cli/commands/agent_subscriptions.rb
|
|
106
121
|
- lib/schwarm_cli/commands/agents.rb
|
|
107
122
|
- lib/schwarm_cli/commands/base.rb
|
|
108
123
|
- lib/schwarm_cli/commands/configure.rb
|
|
@@ -112,7 +127,6 @@ files:
|
|
|
112
127
|
- lib/schwarm_cli/commands/repos.rb
|
|
113
128
|
- lib/schwarm_cli/commands/secrets.rb
|
|
114
129
|
- lib/schwarm_cli/commands/sessions.rb
|
|
115
|
-
- lib/schwarm_cli/commands/shared_agents.rb
|
|
116
130
|
- lib/schwarm_cli/commands/skill_files.rb
|
|
117
131
|
- lib/schwarm_cli/commands/skills.rb
|
|
118
132
|
- lib/schwarm_cli/commands/tasks.rb
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SchwarmCli
|
|
4
|
-
module Commands
|
|
5
|
-
class SharedAgentsCmd < Base
|
|
6
|
-
desc "list", "List shared agents"
|
|
7
|
-
option :query, type: :string, desc: "Search by name"
|
|
8
|
-
pagination_options
|
|
9
|
-
def list
|
|
10
|
-
handle_errors do
|
|
11
|
-
data = fetch_paged do |page_params|
|
|
12
|
-
client.shared_agents.list(query: options[:query], **page_params)
|
|
13
|
-
end
|
|
14
|
-
output_list(data, columns: [%w[ID id], %w[NAME name], %w[ENABLED enabled],
|
|
15
|
-
%w[SCHEDULE schedule]])
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
desc "show ID", "Show shared agent details"
|
|
20
|
-
def show(id)
|
|
21
|
-
handle_errors do
|
|
22
|
-
data = client.shared_agents.find(id)
|
|
23
|
-
output_record(data, fields: {
|
|
24
|
-
"ID" => "id", "Name" => "name", "Enabled" => "enabled",
|
|
25
|
-
"Schedule" => "schedule", "Prompt" => "prompt",
|
|
26
|
-
"Created" => "created_at", "Updated" => "updated_at"
|
|
27
|
-
})
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
desc "create", "Create a shared agent"
|
|
32
|
-
option :name, type: :string, required: true, desc: "Agent name"
|
|
33
|
-
option :prompt, type: :string, required: true, desc: "Agent prompt"
|
|
34
|
-
option :schedule, type: :string, desc: "Cron schedule"
|
|
35
|
-
def create
|
|
36
|
-
handle_errors do
|
|
37
|
-
attrs = { name: options[:name], prompt: options[:prompt] }
|
|
38
|
-
attrs[:schedule] = options[:schedule] if options[:schedule]
|
|
39
|
-
|
|
40
|
-
data = client.shared_agents.create(**attrs)
|
|
41
|
-
output_record(data, fields: { "ID" => "id", "Name" => "name", "Enabled" => "enabled" })
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
desc "update ID", "Update a shared agent"
|
|
46
|
-
option :name, type: :string, desc: "Agent name"
|
|
47
|
-
option :prompt, type: :string, desc: "Agent prompt"
|
|
48
|
-
option :schedule, type: :string, desc: "Cron schedule"
|
|
49
|
-
def update(id)
|
|
50
|
-
handle_errors do
|
|
51
|
-
data = client.shared_agents.update(id, **update_attrs)
|
|
52
|
-
output_record(data, fields: { "ID" => "id", "Name" => "name", "Enabled" => "enabled" })
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
desc "delete ID", "Delete a shared agent"
|
|
57
|
-
def delete(id)
|
|
58
|
-
handle_errors do
|
|
59
|
-
client.shared_agents.destroy(id)
|
|
60
|
-
puts "Shared agent #{id} deleted."
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
desc "toggle ID", "Toggle shared agent enabled/disabled"
|
|
65
|
-
def toggle(id)
|
|
66
|
-
handle_errors do
|
|
67
|
-
data = client.shared_agents.toggle(id)
|
|
68
|
-
status = data.dig("data", "enabled") ? "enabled" : "disabled"
|
|
69
|
-
puts "Shared agent #{id} #{status}."
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
private
|
|
74
|
-
|
|
75
|
-
def update_attrs
|
|
76
|
-
{}.tap do |attrs|
|
|
77
|
-
attrs[:name] = options[:name] if options[:name]
|
|
78
|
-
attrs[:prompt] = options[:prompt] if options[:prompt]
|
|
79
|
-
attrs[:schedule] = options[:schedule] if options[:schedule]
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|