schwarm-cli 0.1.6 → 0.1.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb4e9030ccc6a70a2d35c94e5c2db414bae4b56550ae21128b2e3f612b56b6cf
4
- data.tar.gz: 4c4bfd79d9b120f01764b4b482200757a5a3f4d1a1e8dba5c344486befebf48d
3
+ metadata.gz: 1ce993c4ccb8c15a7a2743ef5d28c8f2c06e3513dad637c23ad0f7929e3dc8b5
4
+ data.tar.gz: add9e02c84c8b0bb05d96c34c0b07b68210669449bb0c428cda290b4559137a4
5
5
  SHA512:
6
- metadata.gz: 326c318ea026081fb23c05cd552e5a78204af6f35dc96e2ff7d5da6c838d0b48646534ab914cc1bc6afa250844e6a6d2ab3895cf2da37fee44f671308c10fbc4
7
- data.tar.gz: c15ba6b8d00b97e44e8b0642ab88753423043cbb4eafad9593fc8fdba0a22bc3a67168576fe76f5165386316bfe14e505411471d08dca4eb6e474ddea25a9d97
6
+ metadata.gz: 54bde32d06880389d77bf624a431c4f702f58fefd6d97067f0f4b6e042ab4bd6fba7e64f3399a16e19e0a4fb1473580c68f45998ae8248cb9ca8a08d3c5ea327
7
+ data.tar.gz: f2a4b9dd8b1b2061c3dc7d9512b2889e260abce55c640b8f5638caded2c790c0db5f03753a895a418a9075c54a41dc69050f1af09b89750e167e187036742cf2
@@ -21,7 +21,7 @@ require_relative "client/user_messages"
21
21
  module SchwarmCli
22
22
  class Client
23
23
  attr_reader :tasks, :repositories, :skills, :skill_files, :templates,
24
- :recurring, :agents, :shared_agents, :secrets, :sessions,
24
+ :recurring, :agents, :subscriptions, :secrets, :sessions,
25
25
  :repo_skills, :messages
26
26
 
27
27
  def get(...) = conn.get(...)
@@ -52,8 +52,8 @@ module SchwarmCli
52
52
  @skill_files = SkillFiles.new(client: self)
53
53
  @templates = TaskTemplates.new(client: self)
54
54
  @recurring = RecurringTasks.new(client: self)
55
- @agents = RepositoryAgents.new(client: self)
56
- @shared_agents = SharedAgents.new(client: self)
55
+ @agents = SharedAgents.new(client: self)
56
+ @subscriptions = RepositoryAgents.new(client: self)
57
57
  @secrets = SecretFiles.new(client: self)
58
58
  @sessions = AgentSessions.new(client: self)
59
59
  @repo_skills = RepositorySkills.new(client: self)
@@ -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: true,
42
+ desc: "Whether the subscription is active (default: true)"
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 "list", "List repository agents"
7
- option :repo, type: :string, desc: "Filter by repository ID, owner/repo, or GitHub URL"
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(repository_id: resolve_repo(options[:repo]), query: options[:query], **page_params)
17
+ client.agents.list(query: options[:query], **page_params)
14
18
  end
15
- output_list(data, columns: [%w[ID id], %w[NAME name], %w[REPO github_repository_name],
16
- %w[ENABLED enabled], %w[SCHEDULE schedule]])
19
+ output_list(data, columns: [%w[ID id], %w[NAME name], %w[ENABLED enabled],
20
+ %w[SCHEDULE schedule]])
17
21
  end
18
22
  end
19
23
 
@@ -22,26 +26,28 @@ module SchwarmCli
22
26
  handle_errors do
23
27
  data = client.agents.find(id)
24
28
  output_record(data, fields: {
25
- "ID" => "id", "Name" => "name", "Repository" => "github_repository_id",
26
- "Enabled" => "enabled", "Schedule" => "schedule", "Prompt" => "prompt",
29
+ "ID" => "id", "Name" => "name", "Enabled" => "enabled",
30
+ "Schedule" => "schedule", "Prompt" => "prompt",
27
31
  "Created" => "created_at", "Updated" => "updated_at"
28
32
  })
29
33
  end
30
34
  end
31
35
 
32
- desc "create", "Create a repository agent"
36
+ desc "create", "Create an agent"
33
37
  option :name, type: :string, required: true, desc: "Agent name"
34
- option :repo, type: :string, required: true, desc: "Repository ID, owner/repo, or GitHub URL"
35
38
  option :prompt, type: :string, required: true, desc: "Agent prompt"
36
39
  option :schedule, type: :string, desc: "Cron schedule"
37
40
  def create
38
41
  handle_errors do
39
- data = client.agents.create(**create_attrs)
42
+ attrs = { name: options[:name], prompt: options[:prompt] }
43
+ attrs[:schedule] = options[:schedule] if options[:schedule]
44
+
45
+ data = client.agents.create(**attrs)
40
46
  output_record(data, fields: { "ID" => "id", "Name" => "name", "Enabled" => "enabled" })
41
47
  end
42
48
  end
43
49
 
44
- desc "update ID", "Update a repository agent"
50
+ desc "update ID", "Update an agent"
45
51
  option :name, type: :string, desc: "Agent name"
46
52
  option :prompt, type: :string, desc: "Agent prompt"
47
53
  option :schedule, type: :string, desc: "Cron schedule"
@@ -52,7 +58,7 @@ module SchwarmCli
52
58
  end
53
59
  end
54
60
 
55
- desc "delete ID", "Delete a repository agent"
61
+ desc "delete ID", "Delete an agent"
56
62
  def delete(id)
57
63
  handle_errors do
58
64
  client.agents.destroy(id)
@@ -69,26 +75,8 @@ module SchwarmCli
69
75
  end
70
76
  end
71
77
 
72
- desc "run-now ID", "Trigger agent to run immediately"
73
- map "run-now" => :run_now
74
- def run_now(id)
75
- handle_errors do
76
- client.agents.run_now(id)
77
- puts "Agent #{id} triggered."
78
- end
79
- end
80
-
81
78
  private
82
79
 
83
- def create_attrs
84
- attrs = {
85
- name: options[:name], github_repository_id: resolve_repo(options[:repo]),
86
- prompt: options[:prompt]
87
- }
88
- attrs[:schedule] = options[:schedule] if options[:schedule]
89
- attrs
90
- end
91
-
92
80
  def update_attrs
93
81
  {}.tap do |attrs|
94
82
  attrs[:name] = options[:name] if options[:name]
@@ -9,6 +9,8 @@ module SchwarmCli
9
9
  class_option :url, type: :string, desc: "Schwarm server URL (overrides config)"
10
10
  class_option :token, type: :string, desc: "API key (overrides config)"
11
11
 
12
+ def self.exit_on_failure? = true
13
+
12
14
  DEFAULT_LIST_LIMIT = 20
13
15
 
14
16
  # Declares --limit, --page, --all options for the next method.
@@ -87,12 +89,12 @@ module SchwarmCli
87
89
  yield
88
90
  rescue Faraday::UnauthorizedError
89
91
  abort "Error: Unauthorized. Check your API key or run `schwarm configure`."
90
- rescue Faraday::ResourceNotFound
91
- abort "Error: Not found."
92
+ rescue Faraday::ResourceNotFound => e
93
+ abort "Error: #{parse_error_body(e, fallback: 'Not found.')}"
92
94
  rescue Faraday::UnprocessableEntityError => e
93
- abort "Error: #{parse_error_body(e)}"
95
+ abort "Error: #{parse_error_body(e, fallback: 'Validation failed')}"
94
96
  rescue Faraday::ClientError => e
95
- abort "Error: #{e.message}"
97
+ abort "Error: #{parse_error_body(e, fallback: e.message)}"
96
98
  rescue Faraday::ServerError
97
99
  abort "Error: Server error. Try again later."
98
100
  rescue Faraday::ConnectionFailed
@@ -101,14 +103,14 @@ module SchwarmCli
101
103
  abort "Error: Request timed out."
102
104
  end
103
105
 
104
- def parse_error_body(error)
106
+ def parse_error_body(error, fallback:)
105
107
  body = error.response&.dig(:body)
106
- return "Validation failed" unless body.is_a?(String)
108
+ return fallback unless body.is_a?(String) && !body.empty?
107
109
 
108
110
  parsed = JSON.parse(body)
109
- parsed.dig("error", "message") || "Validation failed"
111
+ parsed.dig("error", "message") || fallback
110
112
  rescue JSON::ParserError
111
- "Validation failed"
113
+ fallback
112
114
  end
113
115
  end
114
116
  end
@@ -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"
@@ -17,6 +16,8 @@ require_relative "configure"
17
16
  module SchwarmCli
18
17
  module Commands
19
18
  class Main < Thor
19
+ def self.exit_on_failure? = true
20
+
20
21
  desc "tasks SUBCOMMAND", "Manage tasks"
21
22
  subcommand "tasks", Tasks
22
23
 
@@ -30,14 +31,11 @@ module SchwarmCli
30
31
  subcommand "skills", SkillsCmd
31
32
 
32
33
  desc "skill-files SUBCOMMAND", "Manage skill files"
33
- subcommand "skill-files", SkillFilesCmd
34
+ subcommand "skill_files", SkillFilesCmd
34
35
 
35
- desc "agents SUBCOMMAND", "Manage repository agents"
36
+ desc "agents SUBCOMMAND", "Manage agents and their repository subscriptions"
36
37
  subcommand "agents", Agents
37
38
 
38
- desc "shared-agents SUBCOMMAND", "Manage shared agents"
39
- subcommand "shared-agents", SharedAgentsCmd
40
-
41
39
  desc "recurring SUBCOMMAND", "Manage recurring tasks"
42
40
  subcommand "recurring", Recurring
43
41
 
@@ -48,7 +46,7 @@ module SchwarmCli
48
46
  subcommand "sessions", Sessions
49
47
 
50
48
  desc "repo-skills SUBCOMMAND", "Manage repository-skill associations"
51
- subcommand "repo-skills", RepoSkills
49
+ subcommand "repo_skills", RepoSkills
52
50
 
53
51
  desc "configure", "Set up Schwarm CLI configuration"
54
52
  subcommand "configure", Configure
@@ -13,7 +13,7 @@ module SchwarmCli
13
13
  client.recurring.list(repository_id: resolve_repo(options[:repo]), query: options[:query], **page_params)
14
14
  end
15
15
  output_list(data, columns: [%w[ID id], %w[NAME name], %w[REPO github_repository_name],
16
- %w[ENABLED enabled], %w[SCHEDULE schedule]])
16
+ %w[ENABLED enabled], %w[SCHEDULE cron_expression]])
17
17
  end
18
18
  end
19
19
 
@@ -23,7 +23,7 @@ module SchwarmCli
23
23
  data = client.recurring.find(id)
24
24
  output_record(data, fields: {
25
25
  "ID" => "id", "Name" => "name", "Repository" => "github_repository_id",
26
- "Enabled" => "enabled", "Schedule" => "schedule", "Prompt" => "prompt",
26
+ "Enabled" => "enabled", "Schedule" => "cron_expression", "Prompt" => "prompt",
27
27
  "Created" => "created_at", "Updated" => "updated_at"
28
28
  })
29
29
  end
@@ -38,11 +38,11 @@ module SchwarmCli
38
38
  handle_errors do
39
39
  attrs = {
40
40
  name: options[:name], github_repository_id: resolve_repo(options[:repo]),
41
- prompt: options[:prompt], schedule: options[:schedule]
41
+ prompt: options[:prompt], cron_expression: options[:schedule]
42
42
  }
43
43
 
44
44
  data = client.recurring.create(**attrs)
45
- output_record(data, fields: { "ID" => "id", "Name" => "name", "Schedule" => "schedule" })
45
+ output_record(data, fields: { "ID" => "id", "Name" => "name", "Schedule" => "cron_expression" })
46
46
  end
47
47
  end
48
48
 
@@ -53,7 +53,7 @@ module SchwarmCli
53
53
  def update(id)
54
54
  handle_errors do
55
55
  data = client.recurring.update(id, **update_attrs)
56
- output_record(data, fields: { "ID" => "id", "Name" => "name", "Schedule" => "schedule" })
56
+ output_record(data, fields: { "ID" => "id", "Name" => "name", "Schedule" => "cron_expression" })
57
57
  end
58
58
  end
59
59
 
@@ -80,7 +80,7 @@ module SchwarmCli
80
80
  {}.tap do |attrs|
81
81
  attrs[:name] = options[:name] if options[:name]
82
82
  attrs[:prompt] = options[:prompt] if options[:prompt]
83
- attrs[:schedule] = options[:schedule] if options[:schedule]
83
+ attrs[:cron_expression] = options[:schedule] if options[:schedule]
84
84
  end
85
85
  end
86
86
  end
@@ -64,7 +64,11 @@ module SchwarmCli
64
64
  def pause(id)
65
65
  handle_errors do
66
66
  data = client.repositories.pause(id)
67
- puts "Repository #{id} paused (#{data.dig('data', 'status')})."
67
+ if options[:json]
68
+ output_record(data, fields: {})
69
+ else
70
+ puts "Repository #{id} paused (#{data.dig('data', 'status')})."
71
+ end
68
72
  end
69
73
  end
70
74
 
@@ -72,7 +76,11 @@ module SchwarmCli
72
76
  def resume(id)
73
77
  handle_errors do
74
78
  data = client.repositories.resume(id)
75
- puts "Repository #{id} resumed (#{data.dig('data', 'status')})."
79
+ if options[:json]
80
+ output_record(data, fields: {})
81
+ else
82
+ puts "Repository #{id} resumed (#{data.dig('data', 'status')})."
83
+ end
76
84
  end
77
85
  end
78
86
 
@@ -11,50 +11,46 @@ module SchwarmCli
11
11
  data = fetch_paged do |page_params|
12
12
  client.secrets.list(repository_id: resolve_repo(options[:repo]), **page_params)
13
13
  end
14
- output_list(data, columns: [%w[ID id], %w[NAME name], %w[REPO github_repository_name],
15
- %w[PATH path]])
14
+ output_list(data, columns: [%w[ID id], %w[REPO github_repository_name], %w[PATH path]])
16
15
  end
17
16
  end
18
17
 
19
- desc "show ID", "Show secret file details"
18
+ desc "show ID", "Show secret file details (metadata only — content is never returned)"
20
19
  def show(id)
21
20
  handle_errors do
22
21
  data = client.secrets.find(id)
23
22
  output_record(data, fields: {
24
- "ID" => "id", "Name" => "name", "Repository" => "github_repository_id",
23
+ "ID" => "id", "Repository" => "github_repository_name",
25
24
  "Path" => "path", "Created" => "created_at", "Updated" => "updated_at"
26
25
  })
27
26
  end
28
27
  end
29
28
 
30
29
  desc "create", "Create a secret file"
31
- option :name, type: :string, required: true, desc: "Secret name"
32
30
  option :repo, type: :string, required: true, desc: "Repository ID, owner/repo, or GitHub URL"
33
31
  option :path, type: :string, required: true, desc: "File path in workspace"
34
- option :content, type: :string, desc: "Secret content"
35
- option :file, type: :string, desc: "Read content from file"
32
+ option :content, type: :string, desc: "Secret content (mutually exclusive with --file)"
33
+ option :file, type: :string, desc: "Read content from file (mutually exclusive with --content)"
36
34
  def create
37
35
  handle_errors do
38
- content = read_content
39
36
  attrs = {
40
- name: options[:name], github_repository_id: resolve_repo(options[:repo]),
41
- path: options[:path], content:
37
+ github_repository_id: resolve_repo(options[:repo]),
38
+ path: options[:path], content: read_content(required: true)
42
39
  }
43
40
 
44
41
  data = client.secrets.create(**attrs)
45
- output_record(data, fields: { "ID" => "id", "Name" => "name", "Path" => "path" })
42
+ output_record(data, fields: { "ID" => "id", "Path" => "path" })
46
43
  end
47
44
  end
48
45
 
49
46
  desc "update ID", "Update a secret file"
50
- option :name, type: :string, desc: "Secret name"
51
47
  option :path, type: :string, desc: "File path in workspace"
52
- option :content, type: :string, desc: "Secret content"
53
- option :file, type: :string, desc: "Read content from file"
48
+ option :content, type: :string, desc: "Secret content (mutually exclusive with --file)"
49
+ option :file, type: :string, desc: "Read content from file (mutually exclusive with --content)"
54
50
  def update(id)
55
51
  handle_errors do
56
52
  data = client.secrets.update(id, **update_attrs)
57
- output_record(data, fields: { "ID" => "id", "Name" => "name", "Path" => "path" })
53
+ output_record(data, fields: { "ID" => "id", "Path" => "path" })
58
54
  end
59
55
  end
60
56
 
@@ -69,19 +65,24 @@ module SchwarmCli
69
65
  private
70
66
 
71
67
  def update_attrs
72
- { name: options[:name], path: options[:path] }.compact.tap do |attrs|
73
- attrs[:content] = read_content if options[:content] || options[:file]
68
+ { path: options[:path] }.compact.tap do |attrs|
69
+ content = read_content(required: false)
70
+ attrs[:content] = content if content
74
71
  end
75
72
  end
76
73
 
77
- def read_content
78
- if options[:file]
79
- file_path = File.expand_path(options[:file])
80
- abort "Error: File not found: #{file_path}" unless File.exist?(file_path)
81
- File.read(file_path)
82
- else
83
- options[:content]
84
- end
74
+ def read_content(required:)
75
+ abort "Error: --content and --file are mutually exclusive." if options[:content] && options[:file]
76
+ return options[:content] if options[:content]
77
+ return read_file(options[:file]) if options[:file]
78
+
79
+ abort "Error: one of --content or --file is required." if required
80
+ end
81
+
82
+ def read_file(path)
83
+ expanded = File.expand_path(path)
84
+ abort "Error: File not found: #{expanded}" unless File.exist?(expanded)
85
+ File.read(expanded)
85
86
  end
86
87
  end
87
88
  end
@@ -27,7 +27,8 @@ module SchwarmCli
27
27
  data = client.sessions.find(id)
28
28
  output_record(data, fields: {
29
29
  "ID" => "id", "Source" => "source", "Status" => "status",
30
- "Prompt" => "prompt", "Result" => "result",
30
+ "External ID" => "external_id", "Model" => "model_id",
31
+ "Error" => "error_message",
31
32
  "Created" => "created_at", "Updated" => "updated_at"
32
33
  })
33
34
  end
@@ -21,19 +21,24 @@ module SchwarmCli
21
21
  data = client.skills.find(id)
22
22
  output_record(data, fields: {
23
23
  "ID" => "id", "Name" => "name", "Description" => "description",
24
- "Global" => "global", "Created" => "created_at"
24
+ "Instructions" => "instructions", "Global" => "global",
25
+ "Created" => "created_at"
25
26
  })
26
27
  end
27
28
  end
28
29
 
29
30
  desc "create", "Create a skill"
30
31
  option :name, type: :string, required: true, desc: "Skill name"
31
- option :description, type: :string, desc: "Skill description"
32
+ option :description, type: :string, required: true, desc: "Skill description"
33
+ option :instructions, type: :string, desc: "Skill instructions (inline)"
34
+ option :instructions_file, type: :string, desc: "Read instructions from file"
32
35
  option :global, type: :boolean, default: false, desc: "Make skill global"
33
36
  def create
34
37
  handle_errors do
35
- attrs = { name: options[:name], global: options[:global] }
36
- attrs[:description] = options[:description] if options[:description]
38
+ attrs = {
39
+ name: options[:name], description: options[:description],
40
+ instructions: read_instructions, global: options[:global]
41
+ }
37
42
 
38
43
  data = client.skills.create(**attrs)
39
44
  output_record(data, fields: { "ID" => "id", "Name" => "name", "Global" => "global" })
@@ -43,6 +48,8 @@ module SchwarmCli
43
48
  desc "update ID", "Update a skill"
44
49
  option :name, type: :string, desc: "Skill name"
45
50
  option :description, type: :string, desc: "Skill description"
51
+ option :instructions, type: :string, desc: "Skill instructions (inline)"
52
+ option :instructions_file, type: :string, desc: "Read instructions from file"
46
53
  option :global, type: :boolean, desc: "Make skill global"
47
54
  def update(id)
48
55
  handle_errors do
@@ -62,10 +69,24 @@ module SchwarmCli
62
69
  private
63
70
 
64
71
  def update_attrs
72
+ instructions = read_instructions
65
73
  { name: options[:name], description: options[:description] }.compact.tap do |attrs|
74
+ attrs[:instructions] = instructions if instructions
66
75
  attrs[:global] = options[:global] unless options[:global].nil?
67
76
  end
68
77
  end
78
+
79
+ def read_instructions
80
+ if options[:instructions] && options[:instructions_file]
81
+ abort "Error: --instructions and --instructions-file are mutually exclusive."
82
+ elsif options[:instructions_file]
83
+ file_path = File.expand_path(options[:instructions_file])
84
+ abort "Error: File not found: #{file_path}" unless File.exist?(file_path)
85
+ File.read(file_path)
86
+ else
87
+ options[:instructions]
88
+ end
89
+ end
69
90
  end
70
91
  end
71
92
  end
@@ -20,7 +20,8 @@ module SchwarmCli
20
20
  handle_errors do
21
21
  data = client.templates.find(id)
22
22
  output_record(data, fields: {
23
- "ID" => "id", "Name" => "name", "Prompt" => "prompt", "Created" => "created_at"
23
+ "ID" => "id", "Name" => "name", "Description" => "description",
24
+ "Prompt" => "prompt_prefix", "Created" => "created_at"
24
25
  })
25
26
  end
26
27
  end
@@ -28,9 +29,13 @@ module SchwarmCli
28
29
  desc "create", "Create a task template"
29
30
  option :name, type: :string, required: true, desc: "Template name"
30
31
  option :prompt, type: :string, required: true, desc: "Template prompt"
32
+ option :description, type: :string, desc: "Template description"
31
33
  def create
32
34
  handle_errors do
33
- data = client.templates.create(name: options[:name], prompt: options[:prompt])
35
+ attrs = { name: options[:name], prompt_prefix: options[:prompt] }
36
+ attrs[:description] = options[:description] if options[:description]
37
+
38
+ data = client.templates.create(**attrs)
34
39
  output_record(data, fields: { "ID" => "id", "Name" => "name" })
35
40
  end
36
41
  end
@@ -38,13 +43,10 @@ module SchwarmCli
38
43
  desc "update ID", "Update a task template"
39
44
  option :name, type: :string, desc: "Template name"
40
45
  option :prompt, type: :string, desc: "Template prompt"
46
+ option :description, type: :string, desc: "Template description"
41
47
  def update(id)
42
48
  handle_errors do
43
- attrs = {}
44
- attrs[:name] = options[:name] if options[:name]
45
- attrs[:prompt] = options[:prompt] if options[:prompt]
46
-
47
- data = client.templates.update(id, **attrs)
49
+ data = client.templates.update(id, **update_attrs)
48
50
  output_record(data, fields: { "ID" => "id", "Name" => "name" })
49
51
  end
50
52
  end
@@ -56,6 +58,16 @@ module SchwarmCli
56
58
  puts "Template #{id} deleted."
57
59
  end
58
60
  end
61
+
62
+ private
63
+
64
+ def update_attrs
65
+ {}.tap do |attrs|
66
+ attrs[:name] = options[:name] if options[:name]
67
+ attrs[:prompt_prefix] = options[:prompt] if options[:prompt]
68
+ attrs[:description] = options[:description] if options[:description]
69
+ end
70
+ end
59
71
  end
60
72
  end
61
73
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SchwarmCli
4
- VERSION = "0.1.6"
4
+ VERSION = "0.1.8"
5
5
  end
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 a repository agent (custom instructions for all tasks in a repo):
269
+ 3. Create an agent (a reusable definition with a name, prompt, and optional schedule):
270
270
  ```bash
271
- schwarm agents create --repo <REPO_ID> --name "Code style" --prompt "Always follow the project's eslint config"
271
+ schwarm agents create --name "Code style" --prompt "Always follow the project's eslint config"
272
272
  ```
273
- 4. List agents for a repo:
273
+ 4. Subscribe an agent to a repository so it runs against that repo:
274
274
  ```bash
275
- schwarm agents list --repo <REPO_ID>
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.6
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vincent Garrigues
@@ -103,6 +103,7 @@ files:
103
103
  - lib/schwarm_cli/client/task_templates.rb
104
104
  - lib/schwarm_cli/client/tasks.rb
105
105
  - lib/schwarm_cli/client/user_messages.rb
106
+ - lib/schwarm_cli/commands/agent_subscriptions.rb
106
107
  - lib/schwarm_cli/commands/agents.rb
107
108
  - lib/schwarm_cli/commands/base.rb
108
109
  - lib/schwarm_cli/commands/configure.rb
@@ -112,7 +113,6 @@ files:
112
113
  - lib/schwarm_cli/commands/repos.rb
113
114
  - lib/schwarm_cli/commands/secrets.rb
114
115
  - lib/schwarm_cli/commands/sessions.rb
115
- - lib/schwarm_cli/commands/shared_agents.rb
116
116
  - lib/schwarm_cli/commands/skill_files.rb
117
117
  - lib/schwarm_cli/commands/skills.rb
118
118
  - 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