space-architect 8.0.0 → 9.0.0
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/CHANGELOG.md +54 -0
- data/README.md +53 -243
- data/lib/space_architect/bug_report.rb +2 -1
- data/lib/space_architect/cli/architect.rb +20 -7
- data/lib/space_architect/cli.rb +9 -14
- data/lib/space_architect/session_sync/bin_path.rb +1 -1
- data/lib/space_architect/session_sync/cursor.rb +1 -1
- data/lib/space_architect/session_sync/plist.rb +1 -1
- data/lib/{space_core → space_architect}/version.rb +2 -2
- data/lib/space_architect.rb +1 -1
- data/skill/architect/SKILL.md +3 -2
- data/skill/architect-vocabulary/SKILL.md +2 -2
- metadata +18 -177
- data/exe/space +0 -13
- data/exe/src +0 -13
- data/lib/space_architect/cli/space.rb +0 -29
- data/lib/space_architect/cli/src.rb +0 -38
- data/lib/space_core/atomic_write.rb +0 -21
- data/lib/space_core/cli/base_command.rb +0 -19
- data/lib/space_core/cli/build.rb +0 -27
- data/lib/space_core/cli/config.rb +0 -49
- data/lib/space_core/cli/current.rb +0 -16
- data/lib/space_core/cli/help.rb +0 -195
- data/lib/space_core/cli/helpers.rb +0 -115
- data/lib/space_core/cli/init.rb +0 -29
- data/lib/space_core/cli/list.rb +0 -24
- data/lib/space_core/cli/loop_status.rb +0 -47
- data/lib/space_core/cli/new.rb +0 -38
- data/lib/space_core/cli/pack.rb +0 -29
- data/lib/space_core/cli/path.rb +0 -16
- data/lib/space_core/cli/repeatable_options.rb +0 -84
- data/lib/space_core/cli/repo.rb +0 -76
- data/lib/space_core/cli/run.rb +0 -44
- data/lib/space_core/cli/shell.rb +0 -125
- data/lib/space_core/cli/show.rb +0 -21
- data/lib/space_core/cli/status.rb +0 -74
- data/lib/space_core/cli/use.rb +0 -17
- data/lib/space_core/cli.rb +0 -177
- data/lib/space_core/commands.rb +0 -51
- data/lib/space_core/config.rb +0 -152
- data/lib/space_core/errors.rb +0 -14
- data/lib/space_core/git_client.rb +0 -49
- data/lib/space_core/mise_client.rb +0 -37
- data/lib/space_core/oci_builder.rb +0 -56
- data/lib/space_core/oci_packer.rb +0 -103
- data/lib/space_core/oci_runner.rb +0 -89
- data/lib/space_core/paths.rb +0 -58
- data/lib/space_core/repo_reference.rb +0 -19
- data/lib/space_core/repo_resolver.rb +0 -167
- data/lib/space_core/shell_integration.rb +0 -438
- data/lib/space_core/slugger.rb +0 -16
- data/lib/space_core/space.rb +0 -174
- data/lib/space_core/space_store.rb +0 -319
- data/lib/space_core/state.rb +0 -86
- data/lib/space_core/templates/oci/dockerfile.erb +0 -73
- data/lib/space_core/templates/oci/dockerignore.erb +0 -17
- data/lib/space_core/templates/oci/entrypoint.sh.erb +0 -16
- data/lib/space_core/terminal.rb +0 -161
- data/lib/space_core/warnings.rb +0 -13
- data/lib/space_core/xdg.rb +0 -33
- data/lib/space_core.rb +0 -26
- data/lib/space_src/cli/clone.rb +0 -54
- data/lib/space_src/cli/config.rb +0 -65
- data/lib/space_src/cli/daemon.rb +0 -362
- data/lib/space_src/cli/options.rb +0 -21
- data/lib/space_src/cli/org.rb +0 -199
- data/lib/space_src/cli/repo.rb +0 -169
- data/lib/space_src/cli/shell.rb +0 -121
- data/lib/space_src/cli/status.rb +0 -75
- data/lib/space_src/cli/sync.rb +0 -161
- data/lib/space_src/cli.rb +0 -159
- data/lib/space_src/cloner.rb +0 -77
- data/lib/space_src/config/contract.rb +0 -54
- data/lib/space_src/config/duration.rb +0 -79
- data/lib/space_src/config/model.rb +0 -49
- data/lib/space_src/config/store.rb +0 -156
- data/lib/space_src/forge/client.rb +0 -31
- data/lib/space_src/forge/github.rb +0 -98
- data/lib/space_src/launchd/agent.rb +0 -195
- data/lib/space_src/launchd/plist.rb +0 -129
- data/lib/space_src/log_rotator.rb +0 -46
- data/lib/space_src/migration.rb +0 -43
- data/lib/space_src/nav.rb +0 -99
- data/lib/space_src/paths.rb +0 -72
- data/lib/space_src/scm/client.rb +0 -87
- data/lib/space_src/scm/git.rb +0 -232
- data/lib/space_src/scm/status.rb +0 -24
- data/lib/space_src/shell.rb +0 -90
- data/lib/space_src/shell_integration.rb +0 -321
- data/lib/space_src/state/lock.rb +0 -59
- data/lib/space_src/state/store.rb +0 -140
- data/lib/space_src/sync/engine.rb +0 -503
- data/lib/space_src/sync/repo_plan.rb +0 -215
- data/lib/space_src/sync/report.rb +0 -18
- data/lib/space_src/ui/interactive_reporter.rb +0 -280
- data/lib/space_src/ui/json_reporter.rb +0 -39
- data/lib/space_src/ui/mode.rb +0 -68
- data/lib/space_src/ui/plain_reporter.rb +0 -53
- data/lib/space_src/ui/reporter.rb +0 -48
- data/lib/space_src/version.rb +0 -7
- data/lib/space_src.rb +0 -38
data/lib/space_src/cli/org.rb
DELETED
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "pastel"
|
|
4
|
-
require "dry/monads"
|
|
5
|
-
require "space_src/ui/mode"
|
|
6
|
-
require "space_src/cli/options"
|
|
7
|
-
|
|
8
|
-
module Space::Src
|
|
9
|
-
module CLI
|
|
10
|
-
# `org` command group: add / remove / list tracked orgs.
|
|
11
|
-
# Same shape as `repo` but against `Config::OrgRef` (host + name
|
|
12
|
-
# + two bool flags). The host defaults to "github.com" per
|
|
13
|
-
# PRD §3.1 when omitted (so `org add socketry` works as well as
|
|
14
|
-
# `org add github.com/socketry`).
|
|
15
|
-
module Org
|
|
16
|
-
module Helpers
|
|
17
|
-
module_function
|
|
18
|
-
|
|
19
|
-
# Parse an org ref string. Accepts "github.com/socketry" or
|
|
20
|
-
# bare "socketry" (defaults host to github.com). Anything
|
|
21
|
-
# with 3+ parts is rejected (org has only host + name).
|
|
22
|
-
def parse_ref(name, include_archived: false, include_forks: false, ignored_repos: [])
|
|
23
|
-
parts = name.to_s.split("/")
|
|
24
|
-
case parts.length
|
|
25
|
-
when 1
|
|
26
|
-
org = parts[0]
|
|
27
|
-
return Dry::Monads::Failure("invalid org reference: empty name in #{name.inspect}") if org.empty?
|
|
28
|
-
Dry::Monads::Success(Config::OrgRef.new(
|
|
29
|
-
host: Config::DEFAULT_HOST,
|
|
30
|
-
name: org,
|
|
31
|
-
include_archived: include_archived,
|
|
32
|
-
include_forks: include_forks,
|
|
33
|
-
ignored_repos: ignored_repos
|
|
34
|
-
))
|
|
35
|
-
when 2
|
|
36
|
-
host, n = parts
|
|
37
|
-
return Dry::Monads::Failure("invalid org reference: empty host in #{name.inspect}") if host.empty?
|
|
38
|
-
return Dry::Monads::Failure("invalid org reference: empty name in #{name.inspect}") if n.empty?
|
|
39
|
-
Dry::Monads::Success(Config::OrgRef.new(
|
|
40
|
-
host: host,
|
|
41
|
-
name: n,
|
|
42
|
-
include_archived: include_archived,
|
|
43
|
-
include_forks: include_forks,
|
|
44
|
-
ignored_repos: ignored_repos
|
|
45
|
-
))
|
|
46
|
-
else
|
|
47
|
-
Dry::Monads::Failure("invalid org reference: #{name.inspect} (expected \"<name>\" or \"<host>/<name>\")")
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def same_org?(a, b) = a.host == b.host && a.name == b.name
|
|
52
|
-
def format_ref(o) = "#{o.host}/#{o.name}"
|
|
53
|
-
def format_failure(f) = f.is_a?(Hash) ? f.inspect : f.to_s
|
|
54
|
-
|
|
55
|
-
def format_ignored(o)
|
|
56
|
-
return "" if o.ignored_repos.empty?
|
|
57
|
-
" ignored_repos=#{o.ignored_repos.inspect}"
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def fail_with(cmd, msg)
|
|
61
|
-
cmd.send(:err).puts msg
|
|
62
|
-
Space::Src::CLI.record_outcome(Outcome.new(exit_code: 1, message: msg))
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
class Add < Dry::CLI::Command
|
|
67
|
-
include Helpers
|
|
68
|
-
include GlobalOptions
|
|
69
|
-
|
|
70
|
-
desc "Add a tracked org (idempotent on host/name)"
|
|
71
|
-
argument :name, required: true,
|
|
72
|
-
desc: "Org identity as <name> or <host>/<name> (host defaults to github.com)"
|
|
73
|
-
option :include_archived, type: :boolean, default: false,
|
|
74
|
-
desc: "Include archived repos when expanding the org"
|
|
75
|
-
option :include_forks, type: :boolean, default: false,
|
|
76
|
-
desc: "Include forks when expanding the org"
|
|
77
|
-
option :ignored_repos, type: :array, default: [],
|
|
78
|
-
desc: "Repos to exclude from expansion (bare name or owner/name)"
|
|
79
|
-
|
|
80
|
-
def call(name:, include_archived: false, include_forks: false, ignored_repos: [], plain: nil, json: nil, no_color: nil, quiet: nil, **)
|
|
81
|
-
mode = UI::Mode.resolve(
|
|
82
|
-
flags: {plain: plain, json: json, no_color: no_color, quiet: quiet},
|
|
83
|
-
env: CLI.env,
|
|
84
|
-
out: out
|
|
85
|
-
)
|
|
86
|
-
pastel = Pastel.new(enabled: mode.color)
|
|
87
|
-
|
|
88
|
-
parsed = parse_ref(name,
|
|
89
|
-
include_archived: include_archived,
|
|
90
|
-
include_forks: include_forks,
|
|
91
|
-
ignored_repos: ignored_repos)
|
|
92
|
-
return fail_with(self, parsed.failure) if parsed.failure?
|
|
93
|
-
|
|
94
|
-
new_ref = parsed.success
|
|
95
|
-
paths = CLI.make_paths
|
|
96
|
-
config = Config::Store.load(paths.config_file).success
|
|
97
|
-
|
|
98
|
-
if config.orgs.any? { |o| same_org?(o, new_ref) }
|
|
99
|
-
out.puts pastel.yellow("already tracked: #{format_ref(new_ref)}" \
|
|
100
|
-
" (include_archived=#{new_ref.include_archived}, include_forks=#{new_ref.include_forks})" \
|
|
101
|
-
"#{format_ignored(new_ref)}")
|
|
102
|
-
return CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
result = Config::Store.update(paths.config_file) do |c|
|
|
106
|
-
Config::Store.with(c, orgs: c.orgs + [new_ref])
|
|
107
|
-
end
|
|
108
|
-
if result.failure?
|
|
109
|
-
return fail_with(self, "failed to update config: #{format_failure(result.failure)}")
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
out.puts pastel.green("added: #{format_ref(new_ref)}" \
|
|
113
|
-
" (include_archived=#{new_ref.include_archived}, include_forks=#{new_ref.include_forks})" \
|
|
114
|
-
"#{format_ignored(new_ref)}")
|
|
115
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
class Remove < Dry::CLI::Command
|
|
120
|
-
include Helpers
|
|
121
|
-
include GlobalOptions
|
|
122
|
-
|
|
123
|
-
desc "Remove a tracked org (host/name)"
|
|
124
|
-
argument :name, required: true,
|
|
125
|
-
desc: "Org identity as <name> or <host>/<name> (host defaults to github.com)"
|
|
126
|
-
|
|
127
|
-
def call(name:, plain: nil, json: nil, no_color: nil, quiet: nil, **)
|
|
128
|
-
# The flags don't affect the identity match for remove; we
|
|
129
|
-
# match on (host, name) only. This matches the user's
|
|
130
|
-
# expectation that "remove" targets the org, not the flag
|
|
131
|
-
# combination they added it with.
|
|
132
|
-
mode = UI::Mode.resolve(
|
|
133
|
-
flags: {plain: plain, json: json, no_color: no_color, quiet: quiet},
|
|
134
|
-
env: CLI.env,
|
|
135
|
-
out: out
|
|
136
|
-
)
|
|
137
|
-
pastel = Pastel.new(enabled: mode.color)
|
|
138
|
-
|
|
139
|
-
parsed = parse_ref(name)
|
|
140
|
-
return fail_with(self, parsed.failure) if parsed.failure?
|
|
141
|
-
|
|
142
|
-
target = parsed.success
|
|
143
|
-
paths = CLI.make_paths
|
|
144
|
-
config = Config::Store.load(paths.config_file).success
|
|
145
|
-
|
|
146
|
-
kept = config.orgs.reject { |o| same_org?(o, target) }
|
|
147
|
-
if kept.size == config.orgs.size
|
|
148
|
-
return fail_with(self, "not tracked: #{format_ref(target)}")
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
result = Config::Store.update(paths.config_file) do |c|
|
|
152
|
-
Config::Store.with(c, orgs: kept)
|
|
153
|
-
end
|
|
154
|
-
if result.failure?
|
|
155
|
-
return fail_with(self, "failed to update config: #{format_failure(result.failure)}")
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
out.puts pastel.green("removed: #{format_ref(target)}")
|
|
159
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
class List < Dry::CLI::Command
|
|
164
|
-
include Helpers
|
|
165
|
-
include GlobalOptions
|
|
166
|
-
|
|
167
|
-
desc "List tracked orgs"
|
|
168
|
-
|
|
169
|
-
def call(plain: nil, json: nil, no_color: nil, quiet: nil, **)
|
|
170
|
-
mode = UI::Mode.resolve(
|
|
171
|
-
flags: {plain: plain, json: json, no_color: no_color, quiet: quiet},
|
|
172
|
-
env: CLI.env,
|
|
173
|
-
out: out
|
|
174
|
-
)
|
|
175
|
-
pastel = Pastel.new(enabled: mode.color)
|
|
176
|
-
|
|
177
|
-
paths = CLI.make_paths
|
|
178
|
-
config = Config::Store.load(paths.config_file).success
|
|
179
|
-
if config.orgs.empty?
|
|
180
|
-
out.puts pastel.dim("(no tracked orgs)")
|
|
181
|
-
else
|
|
182
|
-
config.orgs.each do |o|
|
|
183
|
-
out.puts pastel.cyan("#{o.host}/#{o.name}" \
|
|
184
|
-
" (include_archived=#{o.include_archived}, include_forks=#{o.include_forks})" \
|
|
185
|
-
"#{format_ignored(o)}")
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
Space::Src::CLI::Registry.register "org" do |prefix|
|
|
196
|
-
prefix.register "add", Space::Src::CLI::Org::Add
|
|
197
|
-
prefix.register "remove", Space::Src::CLI::Org::Remove
|
|
198
|
-
prefix.register "list", Space::Src::CLI::Org::List
|
|
199
|
-
end
|
data/lib/space_src/cli/repo.rb
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "pastel"
|
|
4
|
-
require "dry/monads"
|
|
5
|
-
require "space_src/ui/mode"
|
|
6
|
-
require "space_src/cli/options"
|
|
7
|
-
|
|
8
|
-
module Space::Src
|
|
9
|
-
module CLI
|
|
10
|
-
# `repo` command group: add / remove / list tracked repos.
|
|
11
|
-
# Backed by Config::Store (the on-disk config.yaml is the
|
|
12
|
-
# source of truth — the CLI is just the CRUD interface).
|
|
13
|
-
module Repo
|
|
14
|
-
# Shared ref parser + formatters used by Add and Remove. Kept
|
|
15
|
-
# in a module-level Helpers so the parsing rules live in one
|
|
16
|
-
# place (the "invalid repo reference" message must be identical
|
|
17
|
-
# across add/remove for G3 consistency).
|
|
18
|
-
module Helpers
|
|
19
|
-
module_function
|
|
20
|
-
|
|
21
|
-
def parse_ref(ref)
|
|
22
|
-
parts = ref.to_s.split("/")
|
|
23
|
-
return Dry::Monads::Failure("invalid repo reference: #{ref.inspect} (expected host/owner/name)") if parts.length != 3
|
|
24
|
-
host, owner, name = parts
|
|
25
|
-
return Dry::Monads::Failure("invalid repo reference: empty host in #{ref.inspect}") if host.empty?
|
|
26
|
-
return Dry::Monads::Failure("invalid repo reference: empty owner in #{ref.inspect}") if owner.empty?
|
|
27
|
-
return Dry::Monads::Failure("invalid repo reference: empty name in #{ref.inspect}") if name.empty?
|
|
28
|
-
Dry::Monads::Success(Config::RepoRef.new(host: host, owner: owner, name: name))
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def same_repo?(a, b)
|
|
32
|
-
a.host == b.host && a.owner == b.owner && a.name == b.name
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def format_ref(r) = "#{r.host}/#{r.owner}/#{r.name}"
|
|
36
|
-
|
|
37
|
-
def format_failure(f) = f.is_a?(Hash) ? f.inspect : f.to_s
|
|
38
|
-
|
|
39
|
-
def fail_with(cmd, msg)
|
|
40
|
-
cmd.send(:err).puts msg
|
|
41
|
-
Space::Src::CLI.record_outcome(Outcome.new(exit_code: 1, message: msg))
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Add a tracked repo. Idempotent on the (host, owner, name)
|
|
46
|
-
# triple: adding the same ref twice prints "already tracked"
|
|
47
|
-
# and exits 0 (does NOT write a duplicate).
|
|
48
|
-
class Add < Dry::CLI::Command
|
|
49
|
-
include Helpers
|
|
50
|
-
include GlobalOptions
|
|
51
|
-
|
|
52
|
-
desc "Add a tracked repo (idempotent on host/owner/name)"
|
|
53
|
-
argument :ref, required: true,
|
|
54
|
-
desc: "Repo identity as host/owner/name (e.g. github.com/ruby/ruby)"
|
|
55
|
-
|
|
56
|
-
def call(ref:, plain: nil, json: nil, no_color: nil, quiet: nil, **)
|
|
57
|
-
mode = UI::Mode.resolve(
|
|
58
|
-
flags: {plain: plain, json: json, no_color: no_color, quiet: quiet},
|
|
59
|
-
env: CLI.env,
|
|
60
|
-
out: out
|
|
61
|
-
)
|
|
62
|
-
pastel = Pastel.new(enabled: mode.color)
|
|
63
|
-
|
|
64
|
-
parsed = parse_ref(ref)
|
|
65
|
-
return fail_with(self, parsed.failure) if parsed.failure?
|
|
66
|
-
|
|
67
|
-
new_ref = parsed.success
|
|
68
|
-
paths = CLI.make_paths
|
|
69
|
-
config = Config::Store.load(paths.config_file).success
|
|
70
|
-
|
|
71
|
-
if config.repos.any? { |r| same_repo?(r, new_ref) }
|
|
72
|
-
out.puts pastel.yellow("already tracked: #{format_ref(new_ref)}")
|
|
73
|
-
return CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
result = Config::Store.update(paths.config_file) do |c|
|
|
77
|
-
Config::Store.with(c, repos: c.repos + [new_ref])
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
if result.failure?
|
|
81
|
-
return fail_with(self, "failed to update config: #{format_failure(result.failure)}")
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
out.puts pastel.green("added: #{format_ref(new_ref)}")
|
|
85
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Remove a tracked repo. Exits 0 with a clear message if the
|
|
90
|
-
# ref was present; exits 1 with "not tracked" if it wasn't
|
|
91
|
-
# (and the config is untouched in that case).
|
|
92
|
-
class Remove < Dry::CLI::Command
|
|
93
|
-
include Helpers
|
|
94
|
-
include GlobalOptions
|
|
95
|
-
|
|
96
|
-
desc "Remove a tracked repo (host/owner/name)"
|
|
97
|
-
argument :ref, required: true,
|
|
98
|
-
desc: "Repo identity as host/owner/name (e.g. github.com/ruby/ruby)"
|
|
99
|
-
|
|
100
|
-
def call(ref:, plain: nil, json: nil, no_color: nil, quiet: nil, **)
|
|
101
|
-
mode = UI::Mode.resolve(
|
|
102
|
-
flags: {plain: plain, json: json, no_color: no_color, quiet: quiet},
|
|
103
|
-
env: CLI.env,
|
|
104
|
-
out: out
|
|
105
|
-
)
|
|
106
|
-
pastel = Pastel.new(enabled: mode.color)
|
|
107
|
-
|
|
108
|
-
parsed = parse_ref(ref)
|
|
109
|
-
return fail_with(self, parsed.failure) if parsed.failure?
|
|
110
|
-
|
|
111
|
-
target = parsed.success
|
|
112
|
-
paths = CLI.make_paths
|
|
113
|
-
config = Config::Store.load(paths.config_file).success
|
|
114
|
-
|
|
115
|
-
kept = config.repos.reject { |r| same_repo?(r, target) }
|
|
116
|
-
if kept.size == config.repos.size
|
|
117
|
-
return fail_with(self, "not tracked: #{format_ref(target)}")
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
result = Config::Store.update(paths.config_file) do |c|
|
|
121
|
-
Config::Store.with(c, repos: kept)
|
|
122
|
-
end
|
|
123
|
-
if result.failure?
|
|
124
|
-
return fail_with(self, "failed to update config: #{format_failure(result.failure)}")
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
out.puts pastel.green("removed: #{format_ref(target)}")
|
|
128
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# List tracked repos. One per line: "host/owner/name".
|
|
133
|
-
class List < Dry::CLI::Command
|
|
134
|
-
include GlobalOptions
|
|
135
|
-
|
|
136
|
-
desc "List tracked repos"
|
|
137
|
-
|
|
138
|
-
def call(plain: nil, json: nil, no_color: nil, quiet: nil, **)
|
|
139
|
-
mode = UI::Mode.resolve(
|
|
140
|
-
flags: {plain: plain, json: json, no_color: no_color, quiet: quiet},
|
|
141
|
-
env: CLI.env,
|
|
142
|
-
out: out
|
|
143
|
-
)
|
|
144
|
-
pastel = Pastel.new(enabled: mode.color)
|
|
145
|
-
|
|
146
|
-
paths = CLI.make_paths
|
|
147
|
-
config = Config::Store.load(paths.config_file).success
|
|
148
|
-
if config.repos.empty?
|
|
149
|
-
out.puts pastel.dim("(no tracked repos)")
|
|
150
|
-
else
|
|
151
|
-
config.repos.each do |r|
|
|
152
|
-
out.puts pastel.cyan("#{r.host}/#{r.owner}/#{r.name}")
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
# Register the `repo` group + its subcommands. The block's `prefix`
|
|
163
|
-
# is a Dry::CLI::Registry::Prefix proxy that namespaces the
|
|
164
|
-
# subcommand names under "repo".
|
|
165
|
-
Space::Src::CLI::Registry.register "repo" do |prefix|
|
|
166
|
-
prefix.register "add", Space::Src::CLI::Repo::Add
|
|
167
|
-
prefix.register "remove", Space::Src::CLI::Repo::Remove
|
|
168
|
-
prefix.register "list", Space::Src::CLI::Repo::List
|
|
169
|
-
end
|
data/lib/space_src/cli/shell.rb
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "space_src/shell_integration"
|
|
4
|
-
|
|
5
|
-
module Space::Src
|
|
6
|
-
module CLI
|
|
7
|
-
module Shell
|
|
8
|
-
class Init < Dry::CLI::Command
|
|
9
|
-
desc "Print shell integration script"
|
|
10
|
-
argument :shell_name, required: true, desc: "Shell name (e.g. fish)"
|
|
11
|
-
|
|
12
|
-
def call(shell_name:, **)
|
|
13
|
-
out.puts ShellIntegration.for(shell_name)
|
|
14
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
15
|
-
rescue => e
|
|
16
|
-
err.puts "src shell init: #{e.message}"
|
|
17
|
-
CLI.record_outcome(Outcome.new(exit_code: 1))
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
class Fish < Dry::CLI::Command
|
|
22
|
-
desc "Manage fish shell integration: install, uninstall, path"
|
|
23
|
-
argument :subcommand, required: false, desc: "install, uninstall, or path (default: install)"
|
|
24
|
-
option :force, type: :boolean, default: false, desc: "Overwrite or remove existing shell files"
|
|
25
|
-
|
|
26
|
-
def call(subcommand: "install", force: false, **)
|
|
27
|
-
case subcommand
|
|
28
|
-
when "install"
|
|
29
|
-
result = ShellIntegration.install("fish", env: CLI.env, force: force)
|
|
30
|
-
out.puts fish_install_message(result.fetch(:action), result.fetch(:path))
|
|
31
|
-
out.puts fish_completions_install_message(result.fetch(:completions_action), result.fetch(:completions_path))
|
|
32
|
-
out.puts "Restart fish to load the integration in this terminal: exec fish"
|
|
33
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
34
|
-
when "uninstall"
|
|
35
|
-
result = ShellIntegration.uninstall("fish", env: CLI.env, force: force)
|
|
36
|
-
out.puts fish_uninstall_message(result.fetch(:action), result.fetch(:path))
|
|
37
|
-
out.puts fish_completions_uninstall_message(result.fetch(:completions_action), result.fetch(:completions_path))
|
|
38
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
39
|
-
when "path"
|
|
40
|
-
out.puts "Function: #{ShellIntegration.path_for("fish", env: CLI.env)}"
|
|
41
|
-
out.puts "Completions: #{ShellIntegration.completions_path_for("fish", env: CLI.env)}"
|
|
42
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
43
|
-
else
|
|
44
|
-
err.puts "Usage: src shell fish [install|uninstall|path]"
|
|
45
|
-
CLI.record_outcome(Outcome.new(exit_code: 1))
|
|
46
|
-
end
|
|
47
|
-
rescue => e
|
|
48
|
-
err.puts "src shell fish: #{e.message}"
|
|
49
|
-
CLI.record_outcome(Outcome.new(exit_code: 1))
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
private
|
|
53
|
-
|
|
54
|
-
def fish_install_message(action, path)
|
|
55
|
-
case action
|
|
56
|
-
when :unchanged then "Fish integration already installed: #{path}"
|
|
57
|
-
when :updated then "Updated fish integration: #{path}"
|
|
58
|
-
else "Installed fish integration: #{path}"
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def fish_uninstall_message(action, path)
|
|
63
|
-
case action
|
|
64
|
-
when :missing then "Fish integration was not installed: #{path}"
|
|
65
|
-
else "Removed fish integration: #{path}"
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def fish_completions_install_message(action, path)
|
|
70
|
-
case action
|
|
71
|
-
when :unchanged then "Fish completions already installed: #{path}"
|
|
72
|
-
when :updated then "Updated fish completions: #{path}"
|
|
73
|
-
else "Installed fish completions: #{path}"
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def fish_completions_uninstall_message(action, path)
|
|
78
|
-
case action
|
|
79
|
-
when :missing then "Fish completions were not installed: #{path}"
|
|
80
|
-
else "Removed fish completions: #{path}"
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
class Complete < Dry::CLI::Command
|
|
86
|
-
desc "Print completion candidates"
|
|
87
|
-
argument :kind, required: true, desc: "Completion kind"
|
|
88
|
-
argument :extra, type: :array, required: false, desc: "Extra args for completion"
|
|
89
|
-
|
|
90
|
-
def call(kind:, extra: [], **)
|
|
91
|
-
case kind
|
|
92
|
-
when "checkouts"
|
|
93
|
-
begin
|
|
94
|
-
paths = CLI.make_paths
|
|
95
|
-
config = Config::Store.load(paths.config_file).success
|
|
96
|
-
Nav.scan(config.base_dir).each { |e| out.puts e[:target] }
|
|
97
|
-
rescue
|
|
98
|
-
# Never raise on a completion call — missing/unparseable config is fine.
|
|
99
|
-
end
|
|
100
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
101
|
-
when "shells"
|
|
102
|
-
out.puts "fish"
|
|
103
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
104
|
-
else
|
|
105
|
-
err.puts "Usage: src shell complete checkouts|shells"
|
|
106
|
-
CLI.record_outcome(Outcome.new(exit_code: 1))
|
|
107
|
-
end
|
|
108
|
-
rescue => e
|
|
109
|
-
err.puts "src shell complete: #{e.message}"
|
|
110
|
-
CLI.record_outcome(Outcome.new(exit_code: 1))
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
Space::Src::CLI::Registry.register "shell" do |prefix|
|
|
118
|
-
prefix.register "init", Space::Src::CLI::Shell::Init
|
|
119
|
-
prefix.register "fish", Space::Src::CLI::Shell::Fish
|
|
120
|
-
prefix.register "complete", Space::Src::CLI::Shell::Complete
|
|
121
|
-
end
|
data/lib/space_src/cli/status.rb
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "pastel"
|
|
4
|
-
require "space_src/ui/mode"
|
|
5
|
-
require "space_src/cli/options"
|
|
6
|
-
|
|
7
|
-
module Space::Src
|
|
8
|
-
module CLI
|
|
9
|
-
# `status` command: render a per-repo evergreen table from
|
|
10
|
-
# State::Store. Per G5, the rows must include each repo key
|
|
11
|
-
# with status, last_synced_at, and default_branch.
|
|
12
|
-
module Status
|
|
13
|
-
class Show < Dry::CLI::Command
|
|
14
|
-
include GlobalOptions
|
|
15
|
-
|
|
16
|
-
STATUS_COLORS = {
|
|
17
|
-
"clean" => :green,
|
|
18
|
-
"dirty" => :yellow,
|
|
19
|
-
"diverged" => :yellow,
|
|
20
|
-
"wrong_branch" => :yellow,
|
|
21
|
-
"detached" => :yellow,
|
|
22
|
-
"error" => :red
|
|
23
|
-
}.freeze
|
|
24
|
-
|
|
25
|
-
desc "Show the per-repo evergreen status table (from $XDG_STATE_HOME/space-src/state.yaml)"
|
|
26
|
-
|
|
27
|
-
def call(plain: nil, json: nil, no_color: nil, quiet: nil, **)
|
|
28
|
-
mode = UI::Mode.resolve(
|
|
29
|
-
flags: {plain: plain, json: json, no_color: no_color, quiet: quiet},
|
|
30
|
-
env: CLI.env,
|
|
31
|
-
out: out
|
|
32
|
-
)
|
|
33
|
-
pastel = Pastel.new(enabled: mode.color)
|
|
34
|
-
|
|
35
|
-
paths = CLI.make_paths
|
|
36
|
-
state = State::Store.load(paths.state_file).success
|
|
37
|
-
|
|
38
|
-
if state.repos.empty?
|
|
39
|
-
out.puts "(no repos in state — run `src sync` to populate)"
|
|
40
|
-
return CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Tab-separated columns. The G5 assertion is on captured
|
|
44
|
-
# stdout containing each repo key and its status string —
|
|
45
|
-
# tab-separated is the most assertion-friendly (no width
|
|
46
|
-
# padding complexity, no ANSI codes).
|
|
47
|
-
out.puts ["REPO", "STATUS", "DEFAULT_BRANCH", "LAST_SYNCED_AT", "LAST_FETCH_AT"].join("\t")
|
|
48
|
-
state.repos.keys.sort.each do |key|
|
|
49
|
-
r = state.repos[key]
|
|
50
|
-
status_str = r.status.to_s
|
|
51
|
-
color = STATUS_COLORS[status_str]
|
|
52
|
-
styled_status = color ? pastel.decorate(status_str, color) : status_str
|
|
53
|
-
out.puts [
|
|
54
|
-
key,
|
|
55
|
-
styled_status,
|
|
56
|
-
r.default_branch.to_s,
|
|
57
|
-
format_time(r.last_synced_at),
|
|
58
|
-
format_time(r.last_fetch_at)
|
|
59
|
-
].join("\t")
|
|
60
|
-
end
|
|
61
|
-
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
private
|
|
65
|
-
|
|
66
|
-
def format_time(t)
|
|
67
|
-
return "" if t.nil?
|
|
68
|
-
t.respond_to?(:iso8601) ? t.iso8601 : t.to_s
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
Space::Src::CLI::Registry.register "status", Space::Src::CLI::Status::Show
|