space-architect 8.0.0 → 9.1.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.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +54 -0
  3. data/README.md +39 -464
  4. data/lib/space_architect/architect_project.rb +90 -39
  5. data/lib/space_architect/bug_report.rb +2 -1
  6. data/lib/space_architect/cli/architect.rb +189 -11
  7. data/lib/space_architect/cli/research.rb +1 -1
  8. data/lib/space_architect/cli.rb +9 -14
  9. data/lib/space_architect/loop_cost.rb +592 -0
  10. data/lib/space_architect/research/supervisor.rb +9 -2
  11. data/lib/space_architect/routing.rb +149 -0
  12. data/lib/space_architect/session_sync/bin_path.rb +1 -1
  13. data/lib/space_architect/session_sync/cursor.rb +1 -1
  14. data/lib/space_architect/session_sync/plist.rb +1 -1
  15. data/lib/{space_core → space_architect}/version.rb +2 -2
  16. data/lib/space_architect.rb +2 -1
  17. data/skill/architect/SKILL.md +3 -2
  18. data/skill/architect-vocabulary/SKILL.md +2 -2
  19. metadata +20 -177
  20. data/exe/space +0 -13
  21. data/exe/src +0 -13
  22. data/lib/space_architect/cli/space.rb +0 -29
  23. data/lib/space_architect/cli/src.rb +0 -38
  24. data/lib/space_core/atomic_write.rb +0 -21
  25. data/lib/space_core/cli/base_command.rb +0 -19
  26. data/lib/space_core/cli/build.rb +0 -27
  27. data/lib/space_core/cli/config.rb +0 -49
  28. data/lib/space_core/cli/current.rb +0 -16
  29. data/lib/space_core/cli/help.rb +0 -195
  30. data/lib/space_core/cli/helpers.rb +0 -115
  31. data/lib/space_core/cli/init.rb +0 -29
  32. data/lib/space_core/cli/list.rb +0 -24
  33. data/lib/space_core/cli/loop_status.rb +0 -47
  34. data/lib/space_core/cli/new.rb +0 -38
  35. data/lib/space_core/cli/pack.rb +0 -29
  36. data/lib/space_core/cli/path.rb +0 -16
  37. data/lib/space_core/cli/repeatable_options.rb +0 -84
  38. data/lib/space_core/cli/repo.rb +0 -76
  39. data/lib/space_core/cli/run.rb +0 -44
  40. data/lib/space_core/cli/shell.rb +0 -125
  41. data/lib/space_core/cli/show.rb +0 -21
  42. data/lib/space_core/cli/status.rb +0 -74
  43. data/lib/space_core/cli/use.rb +0 -17
  44. data/lib/space_core/cli.rb +0 -177
  45. data/lib/space_core/commands.rb +0 -51
  46. data/lib/space_core/config.rb +0 -152
  47. data/lib/space_core/errors.rb +0 -14
  48. data/lib/space_core/git_client.rb +0 -49
  49. data/lib/space_core/mise_client.rb +0 -37
  50. data/lib/space_core/oci_builder.rb +0 -56
  51. data/lib/space_core/oci_packer.rb +0 -103
  52. data/lib/space_core/oci_runner.rb +0 -89
  53. data/lib/space_core/paths.rb +0 -58
  54. data/lib/space_core/repo_reference.rb +0 -19
  55. data/lib/space_core/repo_resolver.rb +0 -167
  56. data/lib/space_core/shell_integration.rb +0 -438
  57. data/lib/space_core/slugger.rb +0 -16
  58. data/lib/space_core/space.rb +0 -174
  59. data/lib/space_core/space_store.rb +0 -319
  60. data/lib/space_core/state.rb +0 -86
  61. data/lib/space_core/templates/oci/dockerfile.erb +0 -73
  62. data/lib/space_core/templates/oci/dockerignore.erb +0 -17
  63. data/lib/space_core/templates/oci/entrypoint.sh.erb +0 -16
  64. data/lib/space_core/terminal.rb +0 -161
  65. data/lib/space_core/warnings.rb +0 -13
  66. data/lib/space_core/xdg.rb +0 -33
  67. data/lib/space_core.rb +0 -26
  68. data/lib/space_src/cli/clone.rb +0 -54
  69. data/lib/space_src/cli/config.rb +0 -65
  70. data/lib/space_src/cli/daemon.rb +0 -362
  71. data/lib/space_src/cli/options.rb +0 -21
  72. data/lib/space_src/cli/org.rb +0 -199
  73. data/lib/space_src/cli/repo.rb +0 -169
  74. data/lib/space_src/cli/shell.rb +0 -121
  75. data/lib/space_src/cli/status.rb +0 -75
  76. data/lib/space_src/cli/sync.rb +0 -161
  77. data/lib/space_src/cli.rb +0 -159
  78. data/lib/space_src/cloner.rb +0 -77
  79. data/lib/space_src/config/contract.rb +0 -54
  80. data/lib/space_src/config/duration.rb +0 -79
  81. data/lib/space_src/config/model.rb +0 -49
  82. data/lib/space_src/config/store.rb +0 -156
  83. data/lib/space_src/forge/client.rb +0 -31
  84. data/lib/space_src/forge/github.rb +0 -98
  85. data/lib/space_src/launchd/agent.rb +0 -195
  86. data/lib/space_src/launchd/plist.rb +0 -129
  87. data/lib/space_src/log_rotator.rb +0 -46
  88. data/lib/space_src/migration.rb +0 -43
  89. data/lib/space_src/nav.rb +0 -99
  90. data/lib/space_src/paths.rb +0 -72
  91. data/lib/space_src/scm/client.rb +0 -87
  92. data/lib/space_src/scm/git.rb +0 -232
  93. data/lib/space_src/scm/status.rb +0 -24
  94. data/lib/space_src/shell.rb +0 -90
  95. data/lib/space_src/shell_integration.rb +0 -321
  96. data/lib/space_src/state/lock.rb +0 -59
  97. data/lib/space_src/state/store.rb +0 -140
  98. data/lib/space_src/sync/engine.rb +0 -503
  99. data/lib/space_src/sync/repo_plan.rb +0 -215
  100. data/lib/space_src/sync/report.rb +0 -18
  101. data/lib/space_src/ui/interactive_reporter.rb +0 -280
  102. data/lib/space_src/ui/json_reporter.rb +0 -39
  103. data/lib/space_src/ui/mode.rb +0 -68
  104. data/lib/space_src/ui/plain_reporter.rb +0 -53
  105. data/lib/space_src/ui/reporter.rb +0 -48
  106. data/lib/space_src/version.rb +0 -7
  107. data/lib/space_src.rb +0 -38
@@ -1,167 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "uri"
4
-
5
- module Space::Core
6
- class RepoResolver
7
- SCP_LIKE_PATTERN = /\A(?:[^@\/]+@)?(?<provider>[^:\/]+):(?<path>.+)\z/
8
- URL_PATTERN = %r{\A[A-Za-z][A-Za-z0-9+\-.]*://}
9
-
10
- attr_reader :config
11
-
12
- def initialize(config)
13
- @config = config
14
- end
15
-
16
- def resolve(spec)
17
- value = spec.to_s.strip
18
- raise RepoResolutionError, "Repo cannot be blank" if value.empty?
19
-
20
- if url_like?(value)
21
- resolve_url(value)
22
- elsif (match = value.match(SCP_LIKE_PATTERN))
23
- reference_from_parts(
24
- provider: match[:provider],
25
- path_parts: split_repo_path(match[:path]),
26
- clone_url: value,
27
- source: value
28
- )
29
- else
30
- resolve_shorthand(value)
31
- end
32
- end
33
-
34
- private
35
-
36
- def resolve_url(value)
37
- uri = URI.parse(value)
38
- provider = uri.host
39
- path = uri.path.to_s.delete_prefix("/")
40
- raise RepoResolutionError, "Could not determine provider from '#{value}'" if provider.to_s.empty?
41
-
42
- reference_from_parts(
43
- provider: provider,
44
- path_parts: split_repo_path(path),
45
- clone_url: value,
46
- source: value
47
- )
48
- rescue URI::InvalidURIError
49
- raise RepoResolutionError, "Could not parse repo URL '#{value}'"
50
- end
51
-
52
- def resolve_shorthand(value)
53
- parts = split_repo_path(value)
54
-
55
- if parts.length == 1
56
- resolve_default_organization_repo(parts.first, value)
57
- elsif provider_like?(parts.first) && parts.length >= 3
58
- reference_from_parts(
59
- provider: parts.first,
60
- path_parts: parts[1..],
61
- clone_url: nil,
62
- source: value
63
- )
64
- else
65
- resolve_default_provider_repo(parts, value)
66
- end
67
- end
68
-
69
- def resolve_default_organization_repo(name, source)
70
- provider = require_default_provider(source)
71
- owner = config.default_organization
72
- unless owner
73
- raise RepoResolutionError,
74
- "Repo '#{source}' needs an organization. Set one with: space config set default_organization ORG"
75
- end
76
-
77
- reference(provider:, owner:, name:, clone_url: nil, source:)
78
- end
79
-
80
- def resolve_default_provider_repo(parts, source)
81
- provider = require_default_provider(source)
82
- reference_from_parts(provider:, path_parts: parts, clone_url: nil, source:)
83
- end
84
-
85
- def reference_from_parts(provider:, path_parts:, clone_url:, source:)
86
- if path_parts.length < 2
87
- raise RepoResolutionError, "Repo '#{source}' must include an organization and repo name"
88
- end
89
-
90
- name = path_parts.last
91
- owner = path_parts[0...-1].join("/")
92
- reference(provider:, owner:, name:, clone_url:, source:)
93
- end
94
-
95
- def reference(provider:, owner:, name:, clone_url:, source:)
96
- normalized_provider = normalize_provider(provider)
97
- normalized_owner = normalize_path_part(owner)
98
- normalized_name = normalize_repo_name(name)
99
-
100
- RepoReference.new(
101
- provider: normalized_provider,
102
- owner: normalized_owner,
103
- name: normalized_name,
104
- clone_url: clone_url || clone_url_for(normalized_provider, normalized_owner, normalized_name),
105
- source: source
106
- )
107
- end
108
-
109
- def clone_url_for(provider, owner, name)
110
- case config.git_clone_protocol
111
- when "ssh"
112
- "git@#{provider}:#{owner}/#{name}.git"
113
- when "https"
114
- "https://#{provider}/#{owner}/#{name}.git"
115
- end
116
- end
117
-
118
- def split_repo_path(value)
119
- normalized = value.to_s.strip.delete_prefix("/").delete_suffix("/")
120
- normalized = normalized.delete_suffix(".git")
121
- parts = normalized.split("/").reject(&:empty?)
122
- raise RepoResolutionError, "Repo '#{value}' must include a repo name" if parts.empty?
123
-
124
- parts
125
- end
126
-
127
- def require_default_provider(source)
128
- config.default_provider || raise(
129
- RepoResolutionError,
130
- "Repo '#{source}' needs a provider. Set one with: space config set default_provider PROVIDER"
131
- )
132
- end
133
-
134
- def normalize_provider(value)
135
- normalized = value.to_s.strip
136
- normalized = normalized.delete_prefix("https://")
137
- normalized = normalized.delete_prefix("http://")
138
- normalized = normalized.delete_prefix("ssh://")
139
- normalized = normalized.delete_suffix("/")
140
- raise RepoResolutionError, "Provider cannot be blank" if normalized.empty?
141
-
142
- normalized
143
- end
144
-
145
- def normalize_path_part(value)
146
- normalized = value.to_s.strip.delete_prefix("/").delete_suffix("/")
147
- raise RepoResolutionError, "Organization cannot be blank" if normalized.empty?
148
-
149
- normalized
150
- end
151
-
152
- def normalize_repo_name(value)
153
- normalized = value.to_s.strip.delete_suffix(".git")
154
- raise RepoResolutionError, "Repo name cannot be blank" if normalized.empty?
155
-
156
- normalized
157
- end
158
-
159
- def url_like?(value)
160
- value.match?(URL_PATTERN)
161
- end
162
-
163
- def provider_like?(value)
164
- value.include?(".") || value.include?(":") || value == "localhost"
165
- end
166
- end
167
- end
@@ -1,438 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fileutils"
4
-
5
- module Space::Core
6
- module ShellIntegration
7
- FISH_TEMPLATE = <<~'FISH'
8
- # Generated by space-architect. Do not edit by hand.
9
-
10
- function __space_architect_command
11
- set -l __ps_index 1
12
-
13
- while test $__ps_index -le (count $argv)
14
- set -l __ps_arg $argv[$__ps_index]
15
-
16
- switch "$__ps_arg"
17
- case "--"
18
- set __ps_index (math $__ps_index + 1)
19
- if test $__ps_index -le (count $argv)
20
- echo $argv[$__ps_index]
21
- end
22
- return 0
23
- case "--color" "--colors"
24
- set __ps_index (math $__ps_index + 2)
25
- continue
26
- case "--color=*" "--colors=*"
27
- set __ps_index (math $__ps_index + 1)
28
- continue
29
- case "-*"
30
- return 0
31
- case "*"
32
- echo $__ps_arg
33
- return 0
34
- end
35
- end
36
- end
37
-
38
- function __space_architect_has_color_option
39
- set -l __ps_index 1
40
-
41
- while test $__ps_index -le (count $argv)
42
- set -l __ps_arg $argv[$__ps_index]
43
-
44
- switch "$__ps_arg"
45
- case "--color" "--colors" "--color=*" "--colors=*"
46
- return 0
47
- case "--"
48
- return 1
49
- end
50
-
51
- set __ps_index (math $__ps_index + 1)
52
- end
53
-
54
- return 1
55
- end
56
-
57
- function space --wraps space --description "Create and manage project spaces"
58
- if not set -q __space_architect_compat_checked
59
- set -g __space_architect_compat_checked 1
60
- set -l __space_architect_installed_version __SPACE_ARCHITECT_VERSION__
61
- set -l __space_architect_binary_version (command space --version 2>/dev/null)
62
- if test "$__space_architect_binary_version" != "$__space_architect_installed_version"
63
- echo "space-architect: shell integration version $__space_architect_installed_version does not match binary version $__space_architect_binary_version; re-run 'space shell fish install'" >&2
64
- end
65
- end
66
-
67
- set -l __space_command (__space_architect_command $argv)
68
- set -l __space_args $argv
69
-
70
- if test -t 1; and not __space_architect_has_color_option $argv
71
- set __space_args --color=always $__space_args
72
- end
73
-
74
- switch "$__space_command"
75
- case new use
76
- set -l __space_output (command space $__space_args)
77
- set -l __space_status $status
78
-
79
- if test (count $__space_output) -gt 0
80
- printf "%s\n" $__space_output
81
- end
82
-
83
- if test $__space_status -eq 0
84
- set -l __space_target $__space_output[-1]
85
- set __space_target (string replace -r "^~(?=/|\$)" $HOME -- $__space_target)
86
- if test -d "$__space_target"
87
- cd "$__space_target"
88
- end
89
- end
90
-
91
- return $__space_status
92
- case "*"
93
- command space $__space_args
94
- return $status
95
- end
96
- end
97
- FISH
98
-
99
- FISH_COMPLETIONS = <<~'FISH'
100
- # Generated by space-architect. Do not edit by hand.
101
-
102
- function __space_architect_complete_command
103
- set -l tokens (commandline -opc)
104
- set -l index 2
105
-
106
- while test $index -le (count $tokens)
107
- set -l token $tokens[$index]
108
-
109
- switch "$token"
110
- case "--"
111
- set index (math $index + 1)
112
- if test $index -le (count $tokens)
113
- echo $tokens[$index]
114
- end
115
- return 0
116
- case "--color" "--colors"
117
- set index (math $index + 2)
118
- continue
119
- case "--color=*" "--colors=*"
120
- set index (math $index + 1)
121
- continue
122
- case "-*"
123
- set index (math $index + 1)
124
- continue
125
- case "*"
126
- echo $token
127
- return 0
128
- end
129
- end
130
- end
131
-
132
- function __space_architect_complete_needs_command
133
- test -z "$(__space_architect_complete_command)"
134
- end
135
-
136
- function __space_architect_complete_using_command
137
- contains -- (__space_architect_complete_command) $argv
138
- end
139
-
140
- function __space_architect_complete_first_argument_after
141
- set -l commands $argv
142
- set -l tokens (commandline -opc)
143
- set -l index 2
144
- set -l matched_command 0
145
-
146
- while test $index -le (count $tokens)
147
- set -l token $tokens[$index]
148
-
149
- switch "$token"
150
- case "--color" "--colors"
151
- set index (math $index + 2)
152
- continue
153
- case "--color=*" "--colors=*" "-*"
154
- set index (math $index + 1)
155
- continue
156
- end
157
-
158
- if test $matched_command -eq 0
159
- if contains -- "$token" $commands
160
- set matched_command 1
161
- else
162
- return 1
163
- end
164
- else
165
- echo $token
166
- return 0
167
- end
168
-
169
- set index (math $index + 1)
170
- end
171
-
172
- return 1
173
- end
174
-
175
- function __space_architect_complete_has_first_argument_after
176
- set -q argv[1]; or return 1
177
- set -l first_argument (__space_architect_complete_first_argument_after $argv)
178
- test -n "$first_argument"
179
- end
180
-
181
- function __space_architect_complete_second_argument_after
182
- set -q argv[1]; or return 1
183
- set -l command $argv[1]
184
- set -l tokens (commandline -opc)
185
- set -l index 2
186
- set -l matched_command 0
187
- set -l matched_first_argument 0
188
-
189
- while test $index -le (count $tokens)
190
- set -l token $tokens[$index]
191
-
192
- switch "$token"
193
- case "--color" "--colors"
194
- set index (math $index + 2)
195
- continue
196
- case "--color=*" "--colors=*" "-*"
197
- set index (math $index + 1)
198
- continue
199
- end
200
-
201
- if test $matched_command -eq 0
202
- test "$token" = "$command"; or return 1
203
- set matched_command 1
204
- else if test $matched_first_argument -eq 0
205
- set matched_first_argument 1
206
- else
207
- echo $token
208
- return 0
209
- end
210
-
211
- set index (math $index + 1)
212
- end
213
-
214
- return 1
215
- end
216
-
217
- function __space_architect_complete_has_second_argument_after
218
- set -q argv[1]; or return 1
219
- set -l second_argument (__space_architect_complete_second_argument_after $argv)
220
- test -n "$second_argument"
221
- end
222
-
223
- function __space_architect_complete_first_argument_is
224
- set -q argv[1]; or return 1
225
- set -l expected $argv[1]
226
- set -e argv[1]
227
- test "$(__space_architect_complete_first_argument_after $argv)" = "$expected"
228
- end
229
-
230
- function __space_architect_complete_spaces
231
- command space shell complete spaces 2>/dev/null
232
- end
233
-
234
- function __space_architect_complete_statuses
235
- command space shell complete statuses 2>/dev/null
236
- end
237
-
238
- function __space_architect_complete_config_keys
239
- command space shell complete config-keys 2>/dev/null
240
- end
241
-
242
- function __space_architect_complete_config_set_key
243
- set -l tokens (commandline -opc)
244
- set -l index 2
245
- set -l matched_config 0
246
- set -l matched_set 0
247
-
248
- while test $index -le (count $tokens)
249
- set -l token $tokens[$index]
250
-
251
- switch "$token"
252
- case "--color" "--colors"
253
- set index (math $index + 2)
254
- continue
255
- case "--color=*" "--colors=*" "-*"
256
- set index (math $index + 1)
257
- continue
258
- end
259
-
260
- if test $matched_config -eq 0
261
- test "$token" = "config"; or return 1
262
- set matched_config 1
263
- else if test $matched_set -eq 0
264
- test "$token" = "set"; or return 1
265
- set matched_set 1
266
- else
267
- echo $token
268
- return 0
269
- end
270
-
271
- set index (math $index + 1)
272
- end
273
-
274
- return 1
275
- end
276
-
277
- function __space_architect_complete_config_set_has_key
278
- test -n "$(__space_architect_complete_config_set_key)"
279
- end
280
-
281
- function __space_architect_complete_config_set_key_is
282
- test "$(__space_architect_complete_config_set_key)" = "$argv[1]"
283
- end
284
-
285
- complete -c space -f -l color -x -a "auto always never" -d "Color output"
286
- complete -c space -f -l colors -x -a "auto always never" -d "Color output"
287
- complete -c space -f -n "__space_architect_complete_using_command init shell" -l force -d "Overwrite existing files"
288
- complete -c space -f -n "__space_architect_complete_using_command new" -s r -l repo -x -d "Clone a repo into the new space"
289
-
290
- complete -c space -f -n "__space_architect_complete_needs_command" -a init -d "Create default XDG config and state files"
291
- complete -c space -f -n "__space_architect_complete_needs_command" -a new -d "Create a new project space"
292
- complete -c space -f -n "__space_architect_complete_needs_command" -a list -d "List spaces"
293
- complete -c space -f -n "__space_architect_complete_needs_command" -a ls -d "List spaces"
294
- complete -c space -f -n "__space_architect_complete_needs_command" -a show -d "Show space metadata"
295
- complete -c space -f -n "__space_architect_complete_needs_command" -a path -d "Print a space path"
296
- complete -c space -f -n "__space_architect_complete_needs_command" -a use -d "Select and cd to a space with fish integration"
297
- complete -c space -f -n "__space_architect_complete_needs_command" -a current -d "Show the current space"
298
- complete -c space -f -n "__space_architect_complete_needs_command" -a status -d "Report or set a space status"
299
- complete -c space -f -n "__space_architect_complete_needs_command" -a config -d "Show or update config"
300
- complete -c space -f -n "__space_architect_complete_needs_command" -a repo -d "Manage repos in the current space"
301
- complete -c space -f -n "__space_architect_complete_needs_command" -a repos -d "Manage repos in the current space"
302
- complete -c space -f -n "__space_architect_complete_needs_command" -a shell -d "Manage shell integration"
303
-
304
- complete -c space -f -n "__space_architect_complete_using_command show path use" -a "(__space_architect_complete_spaces)" -d "Space"
305
- complete -c space -f -n "__space_architect_complete_using_command status" -a "(__space_architect_complete_spaces)" -d "Space"
306
- complete -c space -f -n "__space_architect_complete_using_command status" -a "(__space_architect_complete_statuses)" -d "Status"
307
- complete -c space -f -n "__space_architect_complete_first_argument_is init shell; and not __space_architect_complete_has_second_argument_after shell" -a fish -d "Fish shell"
308
-
309
- complete -c space -f -n "__space_architect_complete_using_command repo repos; and not __space_architect_complete_has_first_argument_after repo repos" -a add -d "Clone repos into the current space"
310
- complete -c space -f -n "__space_architect_complete_using_command repo repos; and not __space_architect_complete_has_first_argument_after repo repos" -a list -d "List repos in the current space"
311
- complete -c space -f -n "__space_architect_complete_using_command repo repos; and not __space_architect_complete_has_first_argument_after repo repos" -a ls -d "List repos in the current space"
312
- complete -c space -f -n "__space_architect_complete_using_command repo repos; and not __space_architect_complete_has_first_argument_after repo repos" -a resolve -d "Resolve repo names without cloning"
313
-
314
- complete -c space -f -n "__space_architect_complete_using_command config; and not __space_architect_complete_has_first_argument_after config" -a show -d "Show config"
315
- complete -c space -f -n "__space_architect_complete_using_command config; and not __space_architect_complete_has_first_argument_after config" -a path -d "Print config path"
316
- complete -c space -f -n "__space_architect_complete_using_command config; and not __space_architect_complete_has_first_argument_after config" -a set -d "Set a config value"
317
- complete -c space -f -n "__space_architect_complete_first_argument_is set config; and not __space_architect_complete_config_set_has_key" -a "(__space_architect_complete_config_keys)" -d "Config key"
318
- complete -c space -f -n "__space_architect_complete_config_set_key_is git_clone_protocol" -a "ssh https" -d "Clone protocol"
319
- complete -c space -f -n "__space_architect_complete_config_set_key_is default_provider" -a "github.com gitlab.com" -d "Git provider"
320
-
321
- complete -c space -f -n "__space_architect_complete_using_command shell; and not __space_architect_complete_has_first_argument_after shell" -a init -d "Print shell integration"
322
- complete -c space -f -n "__space_architect_complete_using_command shell; and not __space_architect_complete_has_first_argument_after shell" -a fish -d "Manage fish integration and completions"
323
- complete -c space -f -n "__space_architect_complete_using_command shell; and not __space_architect_complete_has_first_argument_after shell" -a complete -d "Print completion candidates"
324
- complete -c space -f -n "__space_architect_complete_first_argument_is fish shell; and not __space_architect_complete_has_second_argument_after shell" -a install -d "Install fish integration and completions"
325
- complete -c space -f -n "__space_architect_complete_first_argument_is fish shell; and not __space_architect_complete_has_second_argument_after shell" -a uninstall -d "Remove fish integration and completions"
326
- complete -c space -f -n "__space_architect_complete_first_argument_is fish shell; and not __space_architect_complete_has_second_argument_after shell" -a path -d "Print fish integration paths"
327
- complete -c space -f -n "__space_architect_complete_first_argument_is complete shell; and not __space_architect_complete_has_second_argument_after shell" -a "spaces statuses config-keys config-values shells color-modes repo-subcommands config-subcommands fish-subcommands" -d "Completion kind"
328
- FISH
329
-
330
- def self.for(shell)
331
- case shell.to_s
332
- when "fish"
333
- FISH_TEMPLATE.gsub("__SPACE_ARCHITECT_VERSION__", VERSION)
334
- else
335
- raise Error, "Unsupported shell '#{shell}'. Expected: fish"
336
- end
337
- end
338
-
339
- def self.completions_for(shell)
340
- case shell.to_s
341
- when "fish"
342
- FISH_COMPLETIONS
343
- else
344
- raise Error, "Unsupported shell '#{shell}'. Expected: fish"
345
- end
346
- end
347
-
348
- def self.path_for(shell, env: ENV)
349
- case shell.to_s
350
- when "fish"
351
- XDG.config_home(env: env).join("fish", "functions", "space.fish")
352
- else
353
- raise Error, "Unsupported shell '#{shell}'. Expected: fish"
354
- end
355
- end
356
-
357
- def self.completions_path_for(shell, env: ENV)
358
- case shell.to_s
359
- when "fish"
360
- XDG.config_home(env: env).join("fish", "completions", "space.fish")
361
- else
362
- raise Error, "Unsupported shell '#{shell}'. Expected: fish"
363
- end
364
- end
365
-
366
- def self.install(shell, env: ENV, force: false)
367
- function_result = write_managed_file(
368
- path: path_for(shell, env:),
369
- content: self.for(shell),
370
- force: force,
371
- description: "fish function"
372
- )
373
- completions_result = write_managed_file(
374
- path: completions_path_for(shell, env:),
375
- content: completions_for(shell),
376
- force: force,
377
- description: "fish completions"
378
- )
379
-
380
- function_result.merge(
381
- completions_action: completions_result.fetch(:action),
382
- completions_path: completions_result.fetch(:path)
383
- )
384
- end
385
-
386
- def self.uninstall(shell, env: ENV, force: false)
387
- function_result = remove_managed_file(
388
- path: path_for(shell, env:),
389
- content: self.for(shell),
390
- force: force,
391
- description: "fish function"
392
- )
393
- completions_result = remove_managed_file(
394
- path: completions_path_for(shell, env:),
395
- content: completions_for(shell),
396
- force: force,
397
- description: "fish completions"
398
- )
399
-
400
- function_result.merge(
401
- completions_action: completions_result.fetch(:action),
402
- completions_path: completions_result.fetch(:path)
403
- )
404
- end
405
-
406
- def self.managed_fish?(content)
407
- content.include?("Generated by space-architect") ||
408
- (content.include?("function __space_architect_command") && content.include?("function space --wraps space"))
409
- end
410
-
411
- def self.write_managed_file(path:, content:, force:, description:)
412
- existing = path.read if path.exist?
413
-
414
- if existing && existing != content && !force && !managed_fish?(existing)
415
- raise Error, "Refusing to overwrite existing #{description} at #{path}. Re-run with --force."
416
- end
417
-
418
- if existing == content
419
- { action: :unchanged, path: path }
420
- else
421
- AtomicWrite.write(path, content)
422
- { action: existing ? :updated : :installed, path: path }
423
- end
424
- end
425
-
426
- def self.remove_managed_file(path:, content:, force:, description:)
427
- return { action: :missing, path: path } unless path.exist?
428
-
429
- existing = path.read
430
- if existing != content && !force && !managed_fish?(existing)
431
- raise Error, "Refusing to remove existing #{description} at #{path}. Re-run with --force."
432
- end
433
-
434
- FileUtils.rm_f(path)
435
- { action: :removed, path: path }
436
- end
437
- end
438
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Space::Core
4
- module Slugger
5
- module_function
6
-
7
- def slug(value)
8
- slug = value.to_s.downcase.strip
9
- .gsub(/[^a-z0-9]+/, "-")
10
- .gsub(/\A-+|-+\z/, "")
11
- .gsub(/-+/, "-")
12
-
13
- slug.empty? ? "space" : slug
14
- end
15
- end
16
- end