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,161 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "async"
4
- require "pastel"
5
-
6
- module Space::Core
7
- class Terminal
8
- SPINNER_FRAMES = %w[⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏].freeze
9
-
10
- attr_reader :stdout, :stderr, :config
11
-
12
- def initialize(config:, stdout: $stdout, stderr: $stderr, color_mode: "auto")
13
- @config = config
14
- @stdout = stdout
15
- @stderr = stderr
16
- @color_mode = color_mode.to_s.downcase
17
- color_mode
18
- end
19
-
20
- def interactive?
21
- stderr.tty?
22
- end
23
-
24
- def pastel
25
- @pastel ||= Pastel.new(enabled: colors_enabled?)
26
- end
27
-
28
- def say(message = "")
29
- stdout.puts(message)
30
- end
31
-
32
- def error(message)
33
- stderr.puts(colors_enabled? ? pastel.red(message) : message)
34
- end
35
-
36
- def success(message)
37
- say pastel.green(message)
38
- end
39
-
40
- def table(headers, rows)
41
- column_widths = headers.each_index.map do |index|
42
- ([headers[index]] + rows.map { |row| row[index].to_s }).map(&:length).max
43
- end
44
-
45
- ([headers] + rows).each_with_index.map do |row, row_index|
46
- table_row(headers, row, column_widths, header: row_index.zero?)
47
- end.join("\n")
48
- end
49
-
50
- def path(path)
51
- Paths.contract(path, env: config.env)
52
- end
53
-
54
- def with_spinner(message)
55
- return yield unless interactive?
56
-
57
- Async do |task|
58
- spinner_task = start_spinner(task, message)
59
- yield
60
- ensure
61
- spinner_task&.stop
62
- begin
63
- spinner_task&.wait
64
- rescue StandardError
65
- nil
66
- end
67
- clear_spinner
68
- end.wait
69
- end
70
-
71
- def style_skill_action(action)
72
- case action.to_s
73
- when "installed", "updated"
74
- pastel.green(action)
75
- when "would_install", "would_update"
76
- pastel.cyan(action)
77
- when "unchanged"
78
- pastel.bright_black(action)
79
- when "conflict"
80
- pastel.yellow(action)
81
- else
82
- action.to_s
83
- end
84
- end
85
-
86
- private
87
-
88
- def color_mode
89
- return @color_mode if %w[auto always never].include?(@color_mode)
90
-
91
- raise Error, "Invalid color mode '#{@color_mode}'. Expected one of: auto, always, never"
92
- end
93
-
94
- def colors_enabled?
95
- case color_mode
96
- when "always"
97
- true
98
- when "never"
99
- false
100
- else
101
- stdout.tty?
102
- end
103
- end
104
-
105
- def table_row(headers, row, column_widths, header: false)
106
- row.each_with_index.map do |cell, index|
107
- raw = cell.to_s
108
- styled = header ? pastel.bold(raw) : style_table_cell(headers[index], raw)
109
- "#{styled}#{' ' * (column_widths[index] - raw.length)}"
110
- end.join(" ").rstrip
111
- end
112
-
113
- def style_table_cell(header, value)
114
- case header
115
- when "Status"
116
- style_status(value)
117
- when "Date"
118
- pastel.dim(value)
119
- when "Path"
120
- pastel.cyan(value)
121
- else
122
- value
123
- end
124
- end
125
-
126
- def style_status(status)
127
- case status
128
- when "active"
129
- pastel.green(status)
130
- when "paused"
131
- pastel.yellow(status)
132
- when "done"
133
- pastel.blue(status)
134
- when "archived"
135
- pastel.bright_black(status)
136
- else
137
- status
138
- end
139
- end
140
-
141
- def start_spinner(task, message)
142
- task.async do |spinner|
143
- frame_index = 0
144
-
145
- loop do
146
- text = message.respond_to?(:call) ? message.call : message.to_s
147
- frame = SPINNER_FRAMES[frame_index % SPINNER_FRAMES.length]
148
- stderr.print "\r\e[2K#{pastel.cyan(frame)} #{text}"
149
- stderr.flush
150
- frame_index += 1
151
- spinner.sleep(0.1)
152
- end
153
- end
154
- end
155
-
156
- def clear_spinner
157
- stderr.print "\r\e[2K"
158
- stderr.flush
159
- end
160
- end
161
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Space::Core
4
- module Warnings
5
- module_function
6
-
7
- def disable_experimental!
8
- return unless Warning.respond_to?(:[]) && Warning.respond_to?(:[]=)
9
-
10
- Warning[:experimental] = false
11
- end
12
- end
13
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pathname"
4
-
5
- module Space::Core
6
- module XDG
7
- module_function
8
-
9
- def config_home(env: ENV)
10
- Pathname.new(env.fetch("XDG_CONFIG_HOME", File.join(home(env: env), ".config")))
11
- end
12
-
13
- def state_home(env: ENV)
14
- Pathname.new(env.fetch("XDG_STATE_HOME", File.join(home(env: env), ".local", "state")))
15
- end
16
-
17
- def home(env: ENV)
18
- env.fetch("HOME", Dir.home)
19
- end
20
-
21
- def expand_user(path, env: ENV)
22
- value = path.to_s
23
-
24
- if value == "~"
25
- home(env: env)
26
- elsif value.start_with?("~/")
27
- File.join(home(env: env), value[2..])
28
- else
29
- File.expand_path(value)
30
- end
31
- end
32
- end
33
- end
data/lib/space_core.rb DELETED
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "space_core/version"
4
-
5
- module Space::Core
6
- end
7
-
8
- require "space_core/errors"
9
- require "space_core/warnings"
10
- Space::Core::Warnings.disable_experimental!
11
- require "space_core/atomic_write"
12
- require "space_core/xdg"
13
- require "space_core/paths"
14
- require "space_core/commands"
15
- require "space_core/config"
16
- require "space_core/state"
17
- require "space_core/slugger"
18
- require "space_core/space"
19
- require "space_core/repo_reference"
20
- require "space_core/repo_resolver"
21
- require "space_core/git_client"
22
- require "space_core/mise_client"
23
- require "space_core/space_store"
24
- require "space_core/shell_integration"
25
- require "space_core/terminal"
26
- require "space_core/cli"
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pastel"
4
- require "dry/monads"
5
- require "space_src/cli/options"
6
- require "space_src/cloner"
7
-
8
- module Space::Src
9
- module CLI
10
- # `clone` command: APFS COW copy of an evergreen repo into a working dir.
11
- # Resolves each NAME against config.base_dir and copies via cp -Rc.
12
- # Multiple names are processed independently; a per-name failure is
13
- # reported on err and does not abort the others. Exit code is 1 if any
14
- # name failed, else 0.
15
- class Clone < Dry::CLI::Command
16
- include GlobalOptions
17
-
18
- desc "Clone evergreen repo(s) into a working directory (APFS COW copy)"
19
- argument :names, type: :array, required: true,
20
- desc: "Repo name(s): bare name, owner/name, or host/owner/name"
21
- option :into, default: ".",
22
- desc: "Destination parent directory (default: current working directory)"
23
-
24
- def call(names:, into: ".", plain: nil, json: nil, no_color: nil, quiet: nil, **)
25
- mode = UI::Mode.resolve(
26
- flags: {plain: plain, json: json, no_color: no_color, quiet: quiet},
27
- env: CLI.env,
28
- out: out
29
- )
30
- pastel = Pastel.new(enabled: mode.color)
31
-
32
- paths = CLI.make_paths
33
- config = Config::Store.load(paths.config_file).success
34
- cloner = Cloner.new(base_dir: config.base_dir)
35
-
36
- any_failure = false
37
- names.each do |name|
38
- result = cloner.call(name: name, into: into)
39
- if result.success?
40
- dest = result.success
41
- out.puts pastel.green("cloned: #{name} → #{dest}")
42
- else
43
- err.puts result.failure
44
- any_failure = true
45
- end
46
- end
47
-
48
- CLI.record_outcome(Outcome.new(exit_code: any_failure ? 1 : 0))
49
- end
50
- end
51
- end
52
- end
53
-
54
- Space::Src::CLI::Registry.register "clone", Space::Src::CLI::Clone
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pastel"
4
- require "yaml"
5
- require "space_src/ui/mode"
6
- require "space_src/cli/options"
7
-
8
- module Space::Src
9
- module CLI
10
- # `config` command group: path / show.
11
- module ConfigCmd
12
- class Path < Dry::CLI::Command
13
- include GlobalOptions
14
-
15
- desc "Print the resolved config file path (honors $XDG_CONFIG_HOME)"
16
-
17
- def call(plain: nil, json: nil, no_color: nil, quiet: nil, **)
18
- mode = UI::Mode.resolve(
19
- flags: {plain: plain, json: json, no_color: no_color, quiet: quiet},
20
- env: CLI.env,
21
- out: out
22
- )
23
- pastel = Pastel.new(enabled: mode.color)
24
-
25
- paths = CLI.make_paths
26
- out.puts pastel.cyan(paths.config_file)
27
- CLI.record_outcome(Outcome.new(exit_code: 0))
28
- end
29
- end
30
-
31
- class Show < Dry::CLI::Command
32
- include GlobalOptions
33
-
34
- desc "Print the effective (validated, defaults-applied) config as YAML"
35
-
36
- def call(plain: nil, json: nil, no_color: nil, quiet: nil, **)
37
- mode = UI::Mode.resolve(
38
- flags: {plain: plain, json: json, no_color: no_color, quiet: quiet},
39
- env: CLI.env,
40
- out: out
41
- )
42
- pastel = Pastel.new(enabled: mode.color)
43
-
44
- paths = CLI.make_paths
45
- config = Config::Store.load(paths.config_file).success
46
- # Emit via the store's own emit() so the format matches
47
- # what `config.yaml` looks like on disk (stable key order
48
- # per Slice 1's emit implementation). This makes
49
- # `config show` a faithful round-trip preview.
50
- out.puts pastel.cyan(Config::Store.emit(config.to_h).chomp)
51
- CLI.record_outcome(Outcome.new(exit_code: 0))
52
- end
53
- end
54
- end
55
- end
56
- end
57
-
58
- # `config` is the name of both our config-store module (Config) and
59
- # the CLI command group. We register under a different module name
60
- # (ConfigCmd) to avoid the constant clash, then alias the
61
- # registration key as "config".
62
- Space::Src::CLI::Registry.register "config" do |prefix|
63
- prefix.register "path", Space::Src::CLI::ConfigCmd::Path
64
- prefix.register "show", Space::Src::CLI::ConfigCmd::Show
65
- end