schwarm-cli 0.1.3 → 0.1.4
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/commands/agents.rb +13 -7
- data/lib/schwarm_cli/commands/base.rb +7 -0
- data/lib/schwarm_cli/commands/recurring.rb +4 -4
- data/lib/schwarm_cli/commands/repo_skills.rb +6 -4
- data/lib/schwarm_cli/commands/secrets.rb +7 -4
- data/lib/schwarm_cli/commands/tasks.rb +8 -8
- data/lib/schwarm_cli/hands_off_task.rb +8 -19
- data/lib/schwarm_cli/repository_resolver.rb +65 -0
- data/lib/schwarm_cli/version.rb +1 -1
- data/lib/schwarm_cli.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fda5a7c0a0a68a6e07573a5509cadb8ea7ff600f94a3feabf065317c332a9b40
|
|
4
|
+
data.tar.gz: bfd4c31bca63d5862c9df68514c8ab3bdeefb0a1ebbd944c316aeed4522a626d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cf2aac68753184cd3ef7fcd28ff24eb75010b84237f2b661e76a00e1582ead6ad07b0789941f9d3c518bd40eadc34bea14d463bc83e7d69dfb62f68f165920a3
|
|
7
|
+
data.tar.gz: 9bdf3cbb26adecc1f40e90d681e9cb722bd2ae477cd7faa1ac258f3bf690cc77aa1374cb2744a8fe9bb6583b7b328b242f81e80f19ab79afa2dedad23c5d6b55
|
|
@@ -4,13 +4,13 @@ module SchwarmCli
|
|
|
4
4
|
module Commands
|
|
5
5
|
class Agents < Base
|
|
6
6
|
desc "list", "List repository agents"
|
|
7
|
-
option :repo, type: :string, desc: "Filter by repository ID"
|
|
7
|
+
option :repo, type: :string, desc: "Filter by repository ID, owner/repo, or GitHub URL"
|
|
8
8
|
option :query, type: :string, desc: "Search by name"
|
|
9
9
|
pagination_options
|
|
10
10
|
def list
|
|
11
11
|
handle_errors do
|
|
12
12
|
data = fetch_paged do |page_params|
|
|
13
|
-
client.agents.list(repository_id: options[:repo], query: options[:query], **page_params)
|
|
13
|
+
client.agents.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
16
|
%w[ENABLED enabled], %w[SCHEDULE schedule]])
|
|
@@ -31,15 +31,12 @@ module SchwarmCli
|
|
|
31
31
|
|
|
32
32
|
desc "create", "Create a repository agent"
|
|
33
33
|
option :name, type: :string, required: true, desc: "Agent name"
|
|
34
|
-
option :repo, type: :string, required: true, desc: "Repository ID"
|
|
34
|
+
option :repo, type: :string, required: true, desc: "Repository ID, owner/repo, or GitHub URL"
|
|
35
35
|
option :prompt, type: :string, required: true, desc: "Agent prompt"
|
|
36
36
|
option :schedule, type: :string, desc: "Cron schedule"
|
|
37
37
|
def create
|
|
38
38
|
handle_errors do
|
|
39
|
-
|
|
40
|
-
attrs[:schedule] = options[:schedule] if options[:schedule]
|
|
41
|
-
|
|
42
|
-
data = client.agents.create(**attrs)
|
|
39
|
+
data = client.agents.create(**create_attrs)
|
|
43
40
|
output_record(data, fields: { "ID" => "id", "Name" => "name", "Enabled" => "enabled" })
|
|
44
41
|
end
|
|
45
42
|
end
|
|
@@ -83,6 +80,15 @@ module SchwarmCli
|
|
|
83
80
|
|
|
84
81
|
private
|
|
85
82
|
|
|
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
|
+
|
|
86
92
|
def update_attrs
|
|
87
93
|
{}.tap do |attrs|
|
|
88
94
|
attrs[:name] = options[:name] if options[:name]
|
|
@@ -25,6 +25,13 @@ module SchwarmCli
|
|
|
25
25
|
@client ||= SchwarmCli::Client.new(url: options[:url], api_key: options[:token])
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
# Resolves --repo input (ID, owner/repo, or GitHub URL) to a schwarm repo ID.
|
|
29
|
+
def resolve_repo(ref)
|
|
30
|
+
SchwarmCli::RepositoryResolver.new(client: client).resolve(ref)
|
|
31
|
+
rescue SchwarmCli::RepositoryResolver::ResolutionError => e
|
|
32
|
+
abort "Error: #{e.message}"
|
|
33
|
+
end
|
|
34
|
+
|
|
28
35
|
def output_list(data, columns:)
|
|
29
36
|
if options[:json]
|
|
30
37
|
Formatters::Json.format(data)
|
|
@@ -4,13 +4,13 @@ module SchwarmCli
|
|
|
4
4
|
module Commands
|
|
5
5
|
class Recurring < Base
|
|
6
6
|
desc "list", "List recurring tasks"
|
|
7
|
-
option :repo, type: :string, desc: "Filter by repository ID"
|
|
7
|
+
option :repo, type: :string, desc: "Filter by repository ID, owner/repo, or GitHub URL"
|
|
8
8
|
option :query, type: :string, desc: "Search by name"
|
|
9
9
|
pagination_options
|
|
10
10
|
def list
|
|
11
11
|
handle_errors do
|
|
12
12
|
data = fetch_paged do |page_params|
|
|
13
|
-
client.recurring.list(repository_id: options[:repo], query: options[:query], **page_params)
|
|
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
16
|
%w[ENABLED enabled], %w[SCHEDULE schedule]])
|
|
@@ -31,13 +31,13 @@ module SchwarmCli
|
|
|
31
31
|
|
|
32
32
|
desc "create", "Create a recurring task"
|
|
33
33
|
option :name, type: :string, required: true, desc: "Task name"
|
|
34
|
-
option :repo, type: :string, required: true, desc: "Repository ID"
|
|
34
|
+
option :repo, type: :string, required: true, desc: "Repository ID, owner/repo, or GitHub URL"
|
|
35
35
|
option :prompt, type: :string, required: true, desc: "Task prompt"
|
|
36
36
|
option :schedule, type: :string, required: true, desc: "Cron schedule"
|
|
37
37
|
def create
|
|
38
38
|
handle_errors do
|
|
39
39
|
attrs = {
|
|
40
|
-
name: options[:name], github_repository_id: options[:repo],
|
|
40
|
+
name: options[:name], github_repository_id: resolve_repo(options[:repo]),
|
|
41
41
|
prompt: options[:prompt], schedule: options[:schedule]
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -4,13 +4,15 @@ module SchwarmCli
|
|
|
4
4
|
module Commands
|
|
5
5
|
class RepoSkills < Base
|
|
6
6
|
desc "list", "List repository-skill associations"
|
|
7
|
-
option :repo, type: :string, desc: "Filter by repository ID"
|
|
7
|
+
option :repo, type: :string, desc: "Filter by repository ID, owner/repo, or GitHub URL"
|
|
8
8
|
option :skill, type: :string, desc: "Filter by skill ID"
|
|
9
9
|
pagination_options
|
|
10
10
|
def list
|
|
11
11
|
handle_errors do
|
|
12
12
|
data = fetch_paged do |page_params|
|
|
13
|
-
client.repo_skills.list(
|
|
13
|
+
client.repo_skills.list(
|
|
14
|
+
repository_id: resolve_repo(options[:repo]), skill_id: options[:skill], **page_params
|
|
15
|
+
)
|
|
14
16
|
end
|
|
15
17
|
output_list(data, columns: [%w[ID id], %w[REPO github_repository_name],
|
|
16
18
|
%w[SKILL skill_name]])
|
|
@@ -29,12 +31,12 @@ module SchwarmCli
|
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
desc "create", "Associate a skill with a repository"
|
|
32
|
-
option :repo, type: :string, required: true, desc: "Repository ID"
|
|
34
|
+
option :repo, type: :string, required: true, desc: "Repository ID, owner/repo, or GitHub URL"
|
|
33
35
|
option :skill, type: :string, required: true, desc: "Skill ID"
|
|
34
36
|
def create
|
|
35
37
|
handle_errors do
|
|
36
38
|
data = client.repo_skills.create(
|
|
37
|
-
github_repository_id: options[:repo], skill_id: options[:skill]
|
|
39
|
+
github_repository_id: resolve_repo(options[:repo]), skill_id: options[:skill]
|
|
38
40
|
)
|
|
39
41
|
output_record(data, fields: { "ID" => "id", "Repository" => "github_repository_id",
|
|
40
42
|
"Skill" => "skill_id" })
|
|
@@ -4,12 +4,12 @@ module SchwarmCli
|
|
|
4
4
|
module Commands
|
|
5
5
|
class Secrets < Base
|
|
6
6
|
desc "list", "List secret files"
|
|
7
|
-
option :repo, type: :string, desc: "Filter by repository ID"
|
|
7
|
+
option :repo, type: :string, desc: "Filter by repository ID, owner/repo, or GitHub URL"
|
|
8
8
|
pagination_options
|
|
9
9
|
def list
|
|
10
10
|
handle_errors do
|
|
11
11
|
data = fetch_paged do |page_params|
|
|
12
|
-
client.secrets.list(repository_id: options[:repo], **page_params)
|
|
12
|
+
client.secrets.list(repository_id: resolve_repo(options[:repo]), **page_params)
|
|
13
13
|
end
|
|
14
14
|
output_list(data, columns: [%w[ID id], %w[NAME name], %w[REPO github_repository_name],
|
|
15
15
|
%w[PATH path]])
|
|
@@ -29,14 +29,17 @@ module SchwarmCli
|
|
|
29
29
|
|
|
30
30
|
desc "create", "Create a secret file"
|
|
31
31
|
option :name, type: :string, required: true, desc: "Secret name"
|
|
32
|
-
option :repo, type: :string, required: true, desc: "Repository ID"
|
|
32
|
+
option :repo, type: :string, required: true, desc: "Repository ID, owner/repo, or GitHub URL"
|
|
33
33
|
option :path, type: :string, required: true, desc: "File path in workspace"
|
|
34
34
|
option :content, type: :string, desc: "Secret content"
|
|
35
35
|
option :file, type: :string, desc: "Read content from file"
|
|
36
36
|
def create
|
|
37
37
|
handle_errors do
|
|
38
38
|
content = read_content
|
|
39
|
-
attrs = {
|
|
39
|
+
attrs = {
|
|
40
|
+
name: options[:name], github_repository_id: resolve_repo(options[:repo]),
|
|
41
|
+
path: options[:path], content:
|
|
42
|
+
}
|
|
40
43
|
|
|
41
44
|
data = client.secrets.create(**attrs)
|
|
42
45
|
output_record(data, fields: { "ID" => "id", "Name" => "name", "Path" => "path" })
|
|
@@ -5,7 +5,7 @@ module SchwarmCli
|
|
|
5
5
|
class Tasks < Base
|
|
6
6
|
desc "list", "List tasks (excludes archived by default)"
|
|
7
7
|
option :status, type: :string, desc: "Filter by status"
|
|
8
|
-
option :repo, type: :string, desc: "Filter by repository ID"
|
|
8
|
+
option :repo, type: :string, desc: "Filter by repository ID, owner/repo, or GitHub URL"
|
|
9
9
|
option :query, type: :string, desc: "Search by name"
|
|
10
10
|
pagination_options
|
|
11
11
|
def list
|
|
@@ -33,7 +33,7 @@ module SchwarmCli
|
|
|
33
33
|
option :name, type: :string, required: true, desc: "Task name"
|
|
34
34
|
option :prompt, type: :string, desc: "Task prompt"
|
|
35
35
|
option :prompt_file, type: :string, desc: "Read prompt from file"
|
|
36
|
-
option :repo, type: :string, desc: "Repository ID"
|
|
36
|
+
option :repo, type: :string, desc: "Repository ID, owner/repo, or GitHub URL"
|
|
37
37
|
option :status, type: :string, desc: "Initial status (draft/waiting)"
|
|
38
38
|
option :template, type: :string, desc: "Template ID"
|
|
39
39
|
option :depends_on, type: :array, desc: "Dependency task IDs"
|
|
@@ -48,7 +48,7 @@ module SchwarmCli
|
|
|
48
48
|
option :name, type: :string, desc: "Task name"
|
|
49
49
|
option :prompt, type: :string, desc: "Task prompt"
|
|
50
50
|
option :prompt_file, type: :string, desc: "Read prompt from file"
|
|
51
|
-
option :repo, type: :string, desc: "Repository ID"
|
|
51
|
+
option :repo, type: :string, desc: "Repository ID, owner/repo, or GitHub URL"
|
|
52
52
|
option :depends_on, type: :array, desc: "Dependency task IDs"
|
|
53
53
|
def update(id)
|
|
54
54
|
handle_errors do
|
|
@@ -117,7 +117,7 @@ module SchwarmCli
|
|
|
117
117
|
desc "handoff", "Hand off the current local branch to schwarm as a ready task"
|
|
118
118
|
option :prompt, type: :string, desc: "Task prompt"
|
|
119
119
|
option :prompt_file, type: :string, desc: "Read prompt from file"
|
|
120
|
-
option :repo, type: :string, desc: "Repository ID (skips origin auto-detect)"
|
|
120
|
+
option :repo, type: :string, desc: "Repository ID, owner/repo, or GitHub URL (skips origin auto-detect)"
|
|
121
121
|
option :name, type: :string, desc: "Task name (defaults to the branch name)"
|
|
122
122
|
def handoff
|
|
123
123
|
prompt = read_prompt
|
|
@@ -125,7 +125,7 @@ module SchwarmCli
|
|
|
125
125
|
|
|
126
126
|
handle_errors do
|
|
127
127
|
result = SchwarmCli::HandsOffTask.new(client: client).call(
|
|
128
|
-
prompt: prompt, repo_override: options[:repo], name_override: options[:name]
|
|
128
|
+
prompt: prompt, repo_override: resolve_repo(options[:repo]), name_override: options[:name]
|
|
129
129
|
)
|
|
130
130
|
print_handoff_result(result)
|
|
131
131
|
end
|
|
@@ -136,7 +136,7 @@ module SchwarmCli
|
|
|
136
136
|
def list_attrs
|
|
137
137
|
exclude_archived = options[:status].nil? && !options[:all]
|
|
138
138
|
{
|
|
139
|
-
status: options[:status], repository_id: options[:repo], query: options[:query],
|
|
139
|
+
status: options[:status], repository_id: resolve_repo(options[:repo]), query: options[:query],
|
|
140
140
|
archived: exclude_archived ? false : nil
|
|
141
141
|
}
|
|
142
142
|
end
|
|
@@ -154,14 +154,14 @@ module SchwarmCli
|
|
|
154
154
|
def create_attrs
|
|
155
155
|
{
|
|
156
156
|
name: options[:name], prompt: read_prompt,
|
|
157
|
-
github_repository_id: options[:repo], status: options[:status],
|
|
157
|
+
github_repository_id: resolve_repo(options[:repo]), status: options[:status],
|
|
158
158
|
task_template_id: options[:template], dependency_ids: options[:depends_on]
|
|
159
159
|
}.compact
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
def update_attrs
|
|
163
163
|
{
|
|
164
|
-
name: options[:name], github_repository_id: options[:repo],
|
|
164
|
+
name: options[:name], github_repository_id: resolve_repo(options[:repo]),
|
|
165
165
|
dependency_ids: options[:depends_on]
|
|
166
166
|
}.compact.tap do |attrs|
|
|
167
167
|
attrs[:prompt] = read_prompt if options[:prompt] || options[:prompt_file]
|
|
@@ -84,28 +84,17 @@ module SchwarmCli
|
|
|
84
84
|
end
|
|
85
85
|
|
|
86
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
|
|
87
|
+
return find_by_id(override) if override
|
|
99
88
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
89
|
+
RepositoryResolver.new(client: @client).resolve_record(origin)
|
|
90
|
+
rescue RepositoryResolver::ResolutionError
|
|
91
|
+
nil
|
|
103
92
|
end
|
|
104
93
|
|
|
105
|
-
def
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
def find_by_id(id)
|
|
95
|
+
@client.repositories.find(id)["data"]
|
|
96
|
+
rescue Faraday::ResourceNotFound
|
|
97
|
+
nil
|
|
109
98
|
end
|
|
110
99
|
|
|
111
100
|
def create_task(repo:, branch:, prompt:, name_override:)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SchwarmCli
|
|
4
|
+
# Turns a user-provided `--repo` reference into a schwarm repository ID.
|
|
5
|
+
#
|
|
6
|
+
# Accepted inputs:
|
|
7
|
+
# - a schwarm repository ID (returned as-is)
|
|
8
|
+
# - "owner/repo"
|
|
9
|
+
# - "https://github.com/owner/repo[.git]"
|
|
10
|
+
# - "git@github.com:owner/repo[.git]"
|
|
11
|
+
class RepositoryResolver
|
|
12
|
+
class ResolutionError < StandardError; end
|
|
13
|
+
|
|
14
|
+
SLUG_PATTERNS = [
|
|
15
|
+
%r{\Ahttps?://github\.com/([^/\s]+/[^/\s]+?)(?:\.git)?/?\z},
|
|
16
|
+
%r{\Agit@github\.com:([^/\s:]+/[^/\s:]+?)(?:\.git)?\z},
|
|
17
|
+
%r{\A([^/\s:]+/[^/\s:]+?)(?:\.git)?\z}
|
|
18
|
+
].freeze
|
|
19
|
+
|
|
20
|
+
def initialize(client:)
|
|
21
|
+
@client = client
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def resolve(ref)
|
|
25
|
+
return nil if blank?(ref)
|
|
26
|
+
|
|
27
|
+
record = resolve_record(ref)
|
|
28
|
+
record ? record["id"] : ref
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Like `resolve`, but returns the full repo record when the ref is looked up
|
|
32
|
+
# via slug/URL. Returns nil when `ref` is blank or already a bare ID (caller
|
|
33
|
+
# should `find` by id directly in that case).
|
|
34
|
+
def resolve_record(ref)
|
|
35
|
+
return nil if blank?(ref)
|
|
36
|
+
|
|
37
|
+
slug = extract_slug(ref.strip)
|
|
38
|
+
slug ? lookup_by_slug(slug, ref) : nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def blank?(ref)
|
|
44
|
+
ref.nil? || ref.to_s.strip.empty?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def extract_slug(ref)
|
|
48
|
+
SLUG_PATTERNS.filter_map { |pat| ref.match(pat)&.[](1) }.first
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def lookup_by_slug(slug, original)
|
|
52
|
+
response = @client.repositories.list(query: slug)
|
|
53
|
+
match = Array(response["data"]).find { |r| slug_from_url(r["url"]) == slug }
|
|
54
|
+
raise ResolutionError, "no schwarm repository matches `#{original}`" if match.nil?
|
|
55
|
+
|
|
56
|
+
match
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def slug_from_url(url)
|
|
60
|
+
return nil if url.nil?
|
|
61
|
+
|
|
62
|
+
url.sub(%r{\Ahttps?://github\.com/}, "").sub(/\Agit@github\.com:/, "").sub(/\.git\z/, "")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/schwarm_cli/version.rb
CHANGED
data/lib/schwarm_cli.rb
CHANGED
|
@@ -4,6 +4,7 @@ require_relative "schwarm_cli/version"
|
|
|
4
4
|
require_relative "schwarm_cli/config"
|
|
5
5
|
require_relative "schwarm_cli/client"
|
|
6
6
|
require_relative "schwarm_cli/git"
|
|
7
|
+
require_relative "schwarm_cli/repository_resolver"
|
|
7
8
|
require_relative "schwarm_cli/hands_off_task"
|
|
8
9
|
require_relative "schwarm_cli/formatters/json"
|
|
9
10
|
require_relative "schwarm_cli/formatters/table"
|
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.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vincent Garrigues
|
|
@@ -122,6 +122,7 @@ files:
|
|
|
122
122
|
- lib/schwarm_cli/formatters/table.rb
|
|
123
123
|
- lib/schwarm_cli/git.rb
|
|
124
124
|
- lib/schwarm_cli/hands_off_task.rb
|
|
125
|
+
- lib/schwarm_cli/repository_resolver.rb
|
|
125
126
|
- lib/schwarm_cli/version.rb
|
|
126
127
|
- schwarm-skill.md
|
|
127
128
|
homepage: https://github.com/getdexter/schwarm
|