space-architect 1.3.0 → 2.0.0.rc2

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +103 -0
  3. data/README.md +267 -158
  4. data/exe/architect +1 -1
  5. data/exe/space +2 -2
  6. data/exe/src +13 -0
  7. data/lib/space_architect/{architect_mission.rb → architect_project.rb} +475 -125
  8. data/lib/space_architect/cli/architect.rb +248 -178
  9. data/lib/space_architect/cli/research.rb +94 -0
  10. data/lib/space_architect/cli/space.rb +25 -31
  11. data/lib/space_architect/cli/src.rb +20 -14
  12. data/lib/space_architect/cli.rb +22 -22
  13. data/lib/space_architect/dispatcher.rb +5 -1
  14. data/lib/space_architect/gate_evaluator.rb +65 -0
  15. data/lib/space_architect/gate_lint.rb +140 -0
  16. data/lib/space_architect/harness.rb +145 -17
  17. data/lib/space_architect/research/mux.rb +127 -0
  18. data/lib/space_architect/research/registry.rb +70 -0
  19. data/lib/space_architect/research/renderer.rb +101 -0
  20. data/lib/space_architect/research/run.rb +7 -0
  21. data/lib/space_architect/research/supervisor.rb +108 -0
  22. data/lib/space_architect/research.rb +13 -0
  23. data/lib/space_architect/run_creator.rb +53 -0
  24. data/lib/space_architect/skill_installer.rb +81 -79
  25. data/lib/space_architect/templates/architect.md.erb +15 -4
  26. data/lib/space_architect/templates/brief.md.erb +5 -5
  27. data/lib/space_architect/templates/iteration.md.erb +17 -6
  28. data/lib/space_architect.rb +8 -21
  29. data/lib/{space_architect → space_core}/atomic_write.rb +1 -1
  30. data/lib/space_core/cli/base_command.rb +19 -0
  31. data/lib/space_core/cli/build.rb +27 -0
  32. data/lib/space_core/cli/config.rb +49 -0
  33. data/lib/space_core/cli/current.rb +16 -0
  34. data/lib/space_core/cli/help.rb +123 -0
  35. data/lib/space_core/cli/helpers.rb +115 -0
  36. data/lib/space_core/cli/init.rb +29 -0
  37. data/lib/space_core/cli/list.rb +24 -0
  38. data/lib/space_core/cli/new.rb +38 -0
  39. data/lib/space_core/cli/pack.rb +29 -0
  40. data/lib/space_core/cli/path.rb +16 -0
  41. data/lib/space_core/cli/repeatable_options.rb +75 -0
  42. data/lib/space_core/cli/repo.rb +76 -0
  43. data/lib/space_core/cli/run.rb +29 -0
  44. data/lib/space_core/cli/shell.rb +125 -0
  45. data/lib/space_core/cli/show.rb +21 -0
  46. data/lib/space_core/cli/status.rb +33 -0
  47. data/lib/space_core/cli/use.rb +17 -0
  48. data/lib/space_core/cli.rb +177 -0
  49. data/lib/{space_architect → space_core}/config.rb +1 -1
  50. data/lib/{space_architect → space_core}/errors.rb +1 -1
  51. data/lib/{space_architect → space_core}/git_client.rb +1 -1
  52. data/lib/{space_architect → space_core}/mise_client.rb +1 -1
  53. data/lib/space_core/oci_builder.rb +56 -0
  54. data/lib/space_core/oci_packer.rb +99 -0
  55. data/lib/space_core/oci_runner.rb +73 -0
  56. data/lib/{space_architect → space_core}/repo_reference.rb +1 -1
  57. data/lib/{space_architect → space_core}/repo_resolver.rb +1 -1
  58. data/lib/{space_architect → space_core}/shell_integration.rb +1 -1
  59. data/lib/{space_architect → space_core}/slugger.rb +1 -1
  60. data/lib/{space_architect → space_core}/space.rb +11 -3
  61. data/lib/{space_architect → space_core}/space_store.rb +13 -13
  62. data/lib/{space_architect → space_core}/state.rb +1 -1
  63. data/lib/space_core/templates/oci/dockerfile.erb +63 -0
  64. data/lib/space_core/templates/oci/dockerignore.erb +17 -0
  65. data/lib/space_core/templates/oci/entrypoint.sh.erb +10 -0
  66. data/lib/{space_architect → space_core}/terminal.rb +1 -1
  67. data/lib/space_core/version.rb +7 -0
  68. data/lib/{space_architect → space_core}/warnings.rb +1 -1
  69. data/lib/{space_architect → space_core}/xdg.rb +1 -1
  70. data/lib/space_core.rb +24 -0
  71. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/clone.rb +5 -5
  72. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/config.rb +7 -7
  73. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/daemon.rb +46 -30
  74. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/options.rb +1 -1
  75. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/org.rb +9 -9
  76. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/repo.rb +9 -9
  77. data/lib/space_src/cli/shell.rb +122 -0
  78. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/status.rb +7 -7
  79. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/sync.rb +17 -17
  80. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli.rb +42 -11
  81. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cloner.rb +3 -3
  82. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/contract.rb +1 -1
  83. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/duration.rb +1 -1
  84. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/model.rb +1 -1
  85. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/store.rb +5 -5
  86. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/forge/client.rb +2 -2
  87. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/forge/github.rb +4 -4
  88. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/launchd/agent.rb +5 -5
  89. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/launchd/plist.rb +3 -3
  90. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/log_rotator.rb +1 -1
  91. data/lib/space_src/migration.rb +43 -0
  92. data/lib/space_src/nav.rb +98 -0
  93. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/paths.rb +2 -2
  94. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/scm/client.rb +1 -1
  95. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/scm/git.rb +4 -4
  96. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/scm/status.rb +1 -1
  97. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/shell.rb +1 -1
  98. data/lib/space_src/shell_integration.rb +321 -0
  99. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/state/lock.rb +1 -1
  100. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/state/store.rb +2 -2
  101. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/sync/engine.rb +12 -12
  102. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/sync/repo_plan.rb +3 -3
  103. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/interactive_reporter.rb +1 -1
  104. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/json_reporter.rb +1 -1
  105. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/mode.rb +1 -1
  106. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/plain_reporter.rb +1 -1
  107. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/reporter.rb +1 -1
  108. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/version.rb +2 -2
  109. data/lib/space_src.rb +37 -0
  110. data/skill/architect/SKILL.md +110 -54
  111. data/skill/architect/dispatch.md +147 -39
  112. data/skill/architect/research.md +46 -37
  113. data/skill/architect-research/SKILL.md +2 -2
  114. data/skill/architect-vocabulary/SKILL.md +24 -21
  115. metadata +127 -68
  116. data/lib/space_architect/cli/config.rb +0 -61
  117. data/lib/space_architect/cli/current.rb +0 -22
  118. data/lib/space_architect/cli/helpers.rb +0 -117
  119. data/lib/space_architect/cli/init.rb +0 -35
  120. data/lib/space_architect/cli/list.rb +0 -30
  121. data/lib/space_architect/cli/new.rb +0 -43
  122. data/lib/space_architect/cli/options.rb +0 -12
  123. data/lib/space_architect/cli/path.rb +0 -22
  124. data/lib/space_architect/cli/repo.rb +0 -88
  125. data/lib/space_architect/cli/shell.rb +0 -137
  126. data/lib/space_architect/cli/show.rb +0 -27
  127. data/lib/space_architect/cli/status.rb +0 -39
  128. data/lib/space_architect/cli/use.rb +0 -23
  129. data/lib/space_architect/version.rb +0 -5
  130. data/vendor/repo-tender/lib/space_architect/pristine.rb +0 -44
@@ -0,0 +1,17 @@
1
+ # Build scratch: worktrees, harness output, builder artefacts
2
+ build/
3
+
4
+ # Temp workspace
5
+ tmp/
6
+ notes/tmp/
7
+
8
+ # Secrets — NEVER bake credentials in image layers
9
+ .env
10
+ **/.env
11
+ *.key
12
+ *.pem
13
+ *.p12
14
+ *.pfx
15
+ id_rsa
16
+ id_ecdsa
17
+ id_ed25519
@@ -0,0 +1,10 @@
1
+ #!/bin/bash
2
+ set -e
3
+ git config --global --add safe.directory '*'
4
+ git config --global --get user.name >/dev/null 2>&1 || git config --global user.name 'space-architect'
5
+ git config --global --get user.email >/dev/null 2>&1 || git config --global user.email 'architect@localhost'
6
+ if [ "$#" -eq 0 ]; then
7
+ exec bash --login
8
+ else
9
+ exec "$@"
10
+ fi
@@ -3,7 +3,7 @@
3
3
  require "async"
4
4
  require "pastel"
5
5
 
6
- module SpaceArchitect
6
+ module Space::Core
7
7
  class Terminal
8
8
  SPINNER_FRAMES = %w[⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏].freeze
9
9
 
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Space
4
+ module Core
5
+ VERSION = "2.0.0.rc2"
6
+ end
7
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module SpaceArchitect
3
+ module Space::Core
4
4
  module Warnings
5
5
  module_function
6
6
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "pathname"
4
4
 
5
- module SpaceArchitect
5
+ module Space::Core
6
6
  module XDG
7
7
  module_function
8
8
 
data/lib/space_core.rb ADDED
@@ -0,0 +1,24 @@
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/config"
14
+ require "space_core/state"
15
+ require "space_core/slugger"
16
+ require "space_core/space"
17
+ require "space_core/repo_reference"
18
+ require "space_core/repo_resolver"
19
+ require "space_core/git_client"
20
+ require "space_core/mise_client"
21
+ require "space_core/space_store"
22
+ require "space_core/shell_integration"
23
+ require "space_core/terminal"
24
+ require "space_core/cli"
@@ -2,11 +2,11 @@
2
2
 
3
3
  require "pastel"
4
4
  require "dry/monads"
5
- require "space_architect/pristine/cli"
6
- require "space_architect/pristine/cli/options"
7
- require "space_architect/pristine/cloner"
5
+ require "space_src/cli"
6
+ require "space_src/cli/options"
7
+ require "space_src/cloner"
8
8
 
9
- module SpaceArchitect::Pristine
9
+ module Space::Src
10
10
  module CLI
11
11
  # `clone` command: APFS COW copy of an evergreen repo into a working dir.
12
12
  # Resolves each NAME against config.base_dir and copies via cp -Rc.
@@ -52,4 +52,4 @@ module SpaceArchitect::Pristine
52
52
  end
53
53
  end
54
54
 
55
- SpaceArchitect::Pristine::CLI::Registry.register "clone", SpaceArchitect::Pristine::CLI::Clone
55
+ Space::Src::CLI::Registry.register "clone", Space::Src::CLI::Clone
@@ -2,11 +2,11 @@
2
2
 
3
3
  require "pastel"
4
4
  require "yaml"
5
- require "space_architect/pristine/cli"
6
- require "space_architect/pristine/ui/mode"
7
- require "space_architect/pristine/cli/options"
5
+ require "space_src/cli"
6
+ require "space_src/ui/mode"
7
+ require "space_src/cli/options"
8
8
 
9
- module SpaceArchitect::Pristine
9
+ module Space::Src
10
10
  module CLI
11
11
  # `config` command group: path / show.
12
12
  module ConfigCmd
@@ -60,7 +60,7 @@ end
60
60
  # the CLI command group. We register under a different module name
61
61
  # (ConfigCmd) to avoid the constant clash, then alias the
62
62
  # registration key as "config".
63
- SpaceArchitect::Pristine::CLI::Registry.register "config" do |prefix|
64
- prefix.register "path", SpaceArchitect::Pristine::CLI::ConfigCmd::Path
65
- prefix.register "show", SpaceArchitect::Pristine::CLI::ConfigCmd::Show
63
+ Space::Src::CLI::Registry.register "config" do |prefix|
64
+ prefix.register "path", Space::Src::CLI::ConfigCmd::Path
65
+ prefix.register "show", Space::Src::CLI::ConfigCmd::Show
66
66
  end
@@ -3,17 +3,18 @@
3
3
  require "pastel"
4
4
  require "fileutils"
5
5
  require "dry/monads"
6
- require "space_architect/pristine/cli"
7
- require "space_architect/pristine/ui/mode"
8
- require "space_architect/pristine/cli/options"
9
- require "space_architect/pristine/launchd/agent"
10
- require "space_architect/pristine/launchd/plist"
11
-
12
- module SpaceArchitect::Pristine
6
+ require "space_src/cli"
7
+ require "space_src/ui/mode"
8
+ require "space_src/cli/options"
9
+ require "space_src/launchd/agent"
10
+ require "space_src/launchd/plist"
11
+ require "space_src/migration"
12
+
13
+ module Space::Src
13
14
  module CLI
14
15
  # `daemon` command group: install / uninstall / start / stop
15
16
  # / restart / status. Installs a per-user launchd agent
16
- # (`gui/<UID>`) that fires `repo-tender sync` on a
17
+ # (`gui/<UID>`) that fires `src sync` on a
17
18
  # `StartInterval`. The launchctl side is exercised ONLY
18
19
  # through an injected command runner (slice-4 gates G2–G4);
19
20
  # the live domain is proven by the manual real-Mac checklist
@@ -57,7 +58,7 @@ module SpaceArchitect::Pristine
57
58
  # toolchain-resolved ruby; pinned via mise.toml).
58
59
  # * `bin_path` — `RbConfig.ruby` + the script path
59
60
  # (we use `__dir__` of this file's caller; for the
60
- # gem install, this is `<gem>/bin/repo-tender`).
61
+ # gem install, this is `<gem>/exe/src`).
61
62
  #
62
63
  # In tests, we inject these via the `Resolve` object
63
64
  # (see below) — never call out to the shell.
@@ -78,7 +79,7 @@ module SpaceArchitect::Pristine
78
79
 
79
80
  def fail_with(cmd, msg)
80
81
  cmd.send(:err).puts msg
81
- SpaceArchitect::Pristine::CLI.record_outcome(Outcome.new(exit_code: 1, message: msg))
82
+ Space::Src::CLI.record_outcome(Outcome.new(exit_code: 1, message: msg))
82
83
  end
83
84
 
84
85
  # Bundle of resolved absolute paths the plist needs.
@@ -113,6 +114,15 @@ module SpaceArchitect::Pristine
113
114
  label = Launchd::Agent::DEFAULT_LABEL
114
115
  pp = plist_path(paths, label)
115
116
 
117
+ # Relabel: if old-identity plist exists, bootout (benign-failure-tolerant)
118
+ # and remove it before bootstrapping the new-label plist.
119
+ old_pp = plist_path(paths, Migration::OLD_LABEL)
120
+ if File.exist?(old_pp)
121
+ old_agent = make_agent(label: Migration::OLD_LABEL)
122
+ old_agent.uninstall
123
+ File.delete(old_pp) if File.exist?(old_pp)
124
+ end
125
+
116
126
  resolve = Resolve.detect(repo_root: Dir.pwd)
117
127
  xml = build_plist(resolve: resolve, config: config, paths: paths, label: label)
118
128
  FileUtils.mkdir_p(File.dirname(pp))
@@ -264,6 +274,12 @@ module SpaceArchitect::Pristine
264
274
  )
265
275
  pastel = Pastel.new(enabled: mode.color)
266
276
 
277
+ paths = CLI.make_paths
278
+ old_pp = plist_path(paths, Migration::OLD_LABEL)
279
+ if File.exist?(old_pp)
280
+ err.puts pastel.yellow("warning: stale agent #{Migration::OLD_LABEL} detected; run `src daemon install` to upgrade")
281
+ end
282
+
267
283
  label = Launchd::Agent::DEFAULT_LABEL
268
284
  agent = make_agent
269
285
  result = agent.status
@@ -283,12 +299,12 @@ module SpaceArchitect::Pristine
283
299
  end
284
300
  end
285
301
 
286
- # Detect the runtime paths the plist needs. The repo-tender
287
- # install path matters because the plist stores an absolute
288
- # `bin_path` — that is the script launchd invokes. We resolve
289
- # `bin_path` from the on-disk gem layout if we can, else fall
290
- # back to the directory the daemon command was run from.
291
- class SpaceArchitect::Pristine::CLI::Daemon::Helpers::Resolve
302
+ # Detect the runtime paths the plist needs. The src install path
303
+ # matters because the plist stores an absolute `bin_path` — that
304
+ # is the script launchd invokes. We resolve `bin_path` from the
305
+ # on-disk gem layout if we can, else fall back to the directory
306
+ # the daemon command was run from.
307
+ class Space::Src::CLI::Daemon::Helpers::Resolve
292
308
  # @param repo_root [String] absolute path of the working directory (where mise.toml is expected)
293
309
  # @return [Resolve]
294
310
  def self.detect(repo_root:)
@@ -300,7 +316,7 @@ class SpaceArchitect::Pristine::CLI::Daemon::Helpers::Resolve
300
316
  end
301
317
 
302
318
  def self.detect_mise_bin
303
- path = ENV["REPO_TENDER_MISE_BIN"]
319
+ path = ENV["SPACE_SRC_MISE_BIN"]
304
320
  return path if path && !path.empty?
305
321
  require "open3"
306
322
  out, _e, st = Open3.capture3("which", "mise")
@@ -308,7 +324,7 @@ class SpaceArchitect::Pristine::CLI::Daemon::Helpers::Resolve
308
324
  end
309
325
 
310
326
  def self.detect_ruby_bin(repo_root, mise_bin)
311
- path = ENV["REPO_TENDER_RUBY_BIN"]
327
+ path = ENV["SPACE_SRC_RUBY_BIN"]
312
328
  return path if path && !path.empty?
313
329
  # `mise exec -- which ruby` — but we avoid spawning in tests;
314
330
  # production path goes through here.
@@ -321,27 +337,27 @@ class SpaceArchitect::Pristine::CLI::Daemon::Helpers::Resolve
321
337
  end
322
338
 
323
339
  def self.detect_bin_path(repo_root)
324
- path = ENV["REPO_TENDER_BIN_PATH"]
340
+ path = ENV["SPACE_SRC_BIN_PATH"]
325
341
  return path if path && !path.empty?
326
- # Prefer the on-disk dev bin at `<repo_root>/bin/repo-tender`
342
+ # Prefer the on-disk dev bin at `<repo_root>/exe/src`
327
343
  # — it's what the human runs during testing, and the gem is
328
344
  # typically not `gem install`ed in a source checkout.
329
- dev = File.join(repo_root, "bin", "repo-tender")
345
+ dev = File.join(repo_root, "exe", "src")
330
346
  return dev if File.exist?(dev)
331
347
  # Next, an installed binary on PATH.
332
348
  require "open3"
333
- out, _e, st = Open3.capture3("which", "repo-tender")
349
+ out, _e, st = Open3.capture3("which", "src")
334
350
  return out.strip if st.success? && !out.strip.empty?
335
351
  # Last resort: the installed gem's bin (raises if not installed).
336
- Gem.bin_path("repo-tender", "repo-tender")
352
+ Gem.bin_path("space-architect", "src")
337
353
  end
338
354
  end
339
355
 
340
- SpaceArchitect::Pristine::CLI::Registry.register "daemon" do |prefix|
341
- prefix.register "install", SpaceArchitect::Pristine::CLI::Daemon::Install
342
- prefix.register "uninstall", SpaceArchitect::Pristine::CLI::Daemon::Uninstall
343
- prefix.register "start", SpaceArchitect::Pristine::CLI::Daemon::Start
344
- prefix.register "stop", SpaceArchitect::Pristine::CLI::Daemon::Stop
345
- prefix.register "restart", SpaceArchitect::Pristine::CLI::Daemon::Restart
346
- prefix.register "status", SpaceArchitect::Pristine::CLI::Daemon::Status
356
+ Space::Src::CLI::Registry.register "daemon" do |prefix|
357
+ prefix.register "install", Space::Src::CLI::Daemon::Install
358
+ prefix.register "uninstall", Space::Src::CLI::Daemon::Uninstall
359
+ prefix.register "start", Space::Src::CLI::Daemon::Start
360
+ prefix.register "stop", Space::Src::CLI::Daemon::Stop
361
+ prefix.register "restart", Space::Src::CLI::Daemon::Restart
362
+ prefix.register "status", Space::Src::CLI::Daemon::Status
347
363
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module SpaceArchitect::Pristine
3
+ module Space::Src
4
4
  module CLI
5
5
  # Shared output mode flags. Include in any command class to add:
6
6
  # --plain force plain text output (one line per event, ANSI-free)
@@ -2,11 +2,11 @@
2
2
 
3
3
  require "pastel"
4
4
  require "dry/monads"
5
- require "space_architect/pristine/cli"
6
- require "space_architect/pristine/ui/mode"
7
- require "space_architect/pristine/cli/options"
5
+ require "space_src/cli"
6
+ require "space_src/ui/mode"
7
+ require "space_src/cli/options"
8
8
 
9
- module SpaceArchitect::Pristine
9
+ module Space::Src
10
10
  module CLI
11
11
  # `org` command group: add / remove / list tracked orgs.
12
12
  # Same shape as `repo` but against `Config::OrgRef` (host + name
@@ -60,7 +60,7 @@ module SpaceArchitect::Pristine
60
60
 
61
61
  def fail_with(cmd, msg)
62
62
  cmd.send(:err).puts msg
63
- SpaceArchitect::Pristine::CLI.record_outcome(Outcome.new(exit_code: 1, message: msg))
63
+ Space::Src::CLI.record_outcome(Outcome.new(exit_code: 1, message: msg))
64
64
  end
65
65
  end
66
66
 
@@ -193,8 +193,8 @@ module SpaceArchitect::Pristine
193
193
  end
194
194
  end
195
195
 
196
- SpaceArchitect::Pristine::CLI::Registry.register "org" do |prefix|
197
- prefix.register "add", SpaceArchitect::Pristine::CLI::Org::Add
198
- prefix.register "remove", SpaceArchitect::Pristine::CLI::Org::Remove
199
- prefix.register "list", SpaceArchitect::Pristine::CLI::Org::List
196
+ Space::Src::CLI::Registry.register "org" do |prefix|
197
+ prefix.register "add", Space::Src::CLI::Org::Add
198
+ prefix.register "remove", Space::Src::CLI::Org::Remove
199
+ prefix.register "list", Space::Src::CLI::Org::List
200
200
  end
@@ -2,11 +2,11 @@
2
2
 
3
3
  require "pastel"
4
4
  require "dry/monads"
5
- require "space_architect/pristine/cli"
6
- require "space_architect/pristine/ui/mode"
7
- require "space_architect/pristine/cli/options"
5
+ require "space_src/cli"
6
+ require "space_src/ui/mode"
7
+ require "space_src/cli/options"
8
8
 
9
- module SpaceArchitect::Pristine
9
+ module Space::Src
10
10
  module CLI
11
11
  # `repo` command group: add / remove / list tracked repos.
12
12
  # Backed by Config::Store (the on-disk config.yaml is the
@@ -39,7 +39,7 @@ module SpaceArchitect::Pristine
39
39
 
40
40
  def fail_with(cmd, msg)
41
41
  cmd.send(:err).puts msg
42
- SpaceArchitect::Pristine::CLI.record_outcome(Outcome.new(exit_code: 1, message: msg))
42
+ Space::Src::CLI.record_outcome(Outcome.new(exit_code: 1, message: msg))
43
43
  end
44
44
  end
45
45
 
@@ -163,8 +163,8 @@ end
163
163
  # Register the `repo` group + its subcommands. The block's `prefix`
164
164
  # is a Dry::CLI::Registry::Prefix proxy that namespaces the
165
165
  # subcommand names under "repo".
166
- SpaceArchitect::Pristine::CLI::Registry.register "repo" do |prefix|
167
- prefix.register "add", SpaceArchitect::Pristine::CLI::Repo::Add
168
- prefix.register "remove", SpaceArchitect::Pristine::CLI::Repo::Remove
169
- prefix.register "list", SpaceArchitect::Pristine::CLI::Repo::List
166
+ Space::Src::CLI::Registry.register "repo" do |prefix|
167
+ prefix.register "add", Space::Src::CLI::Repo::Add
168
+ prefix.register "remove", Space::Src::CLI::Repo::Remove
169
+ prefix.register "list", Space::Src::CLI::Repo::List
170
170
  end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "space_src/cli"
4
+ require "space_src/shell_integration"
5
+
6
+ module Space::Src
7
+ module CLI
8
+ module Shell
9
+ class Init < Dry::CLI::Command
10
+ desc "Print shell integration script"
11
+ argument :shell_name, required: true, desc: "Shell name (e.g. fish)"
12
+
13
+ def call(shell_name:, **)
14
+ out.puts ShellIntegration.for(shell_name)
15
+ CLI.record_outcome(Outcome.new(exit_code: 0))
16
+ rescue => e
17
+ err.puts "src shell init: #{e.message}"
18
+ CLI.record_outcome(Outcome.new(exit_code: 1))
19
+ end
20
+ end
21
+
22
+ class Fish < Dry::CLI::Command
23
+ desc "Manage fish shell integration: install, uninstall, path"
24
+ argument :subcommand, required: false, desc: "install, uninstall, or path (default: install)"
25
+ option :force, type: :boolean, default: false, desc: "Overwrite or remove existing shell files"
26
+
27
+ def call(subcommand: "install", force: false, **)
28
+ case subcommand
29
+ when "install"
30
+ result = ShellIntegration.install("fish", env: CLI.env, force: force)
31
+ out.puts fish_install_message(result.fetch(:action), result.fetch(:path))
32
+ out.puts fish_completions_install_message(result.fetch(:completions_action), result.fetch(:completions_path))
33
+ out.puts "Restart fish to load the integration in this terminal: exec fish"
34
+ CLI.record_outcome(Outcome.new(exit_code: 0))
35
+ when "uninstall"
36
+ result = ShellIntegration.uninstall("fish", env: CLI.env, force: force)
37
+ out.puts fish_uninstall_message(result.fetch(:action), result.fetch(:path))
38
+ out.puts fish_completions_uninstall_message(result.fetch(:completions_action), result.fetch(:completions_path))
39
+ CLI.record_outcome(Outcome.new(exit_code: 0))
40
+ when "path"
41
+ out.puts "Function: #{ShellIntegration.path_for("fish", env: CLI.env)}"
42
+ out.puts "Completions: #{ShellIntegration.completions_path_for("fish", env: CLI.env)}"
43
+ CLI.record_outcome(Outcome.new(exit_code: 0))
44
+ else
45
+ err.puts "Usage: src shell fish [install|uninstall|path]"
46
+ CLI.record_outcome(Outcome.new(exit_code: 1))
47
+ end
48
+ rescue => e
49
+ err.puts "src shell fish: #{e.message}"
50
+ CLI.record_outcome(Outcome.new(exit_code: 1))
51
+ end
52
+
53
+ private
54
+
55
+ def fish_install_message(action, path)
56
+ case action
57
+ when :unchanged then "Fish integration already installed: #{path}"
58
+ when :updated then "Updated fish integration: #{path}"
59
+ else "Installed fish integration: #{path}"
60
+ end
61
+ end
62
+
63
+ def fish_uninstall_message(action, path)
64
+ case action
65
+ when :missing then "Fish integration was not installed: #{path}"
66
+ else "Removed fish integration: #{path}"
67
+ end
68
+ end
69
+
70
+ def fish_completions_install_message(action, path)
71
+ case action
72
+ when :unchanged then "Fish completions already installed: #{path}"
73
+ when :updated then "Updated fish completions: #{path}"
74
+ else "Installed fish completions: #{path}"
75
+ end
76
+ end
77
+
78
+ def fish_completions_uninstall_message(action, path)
79
+ case action
80
+ when :missing then "Fish completions were not installed: #{path}"
81
+ else "Removed fish completions: #{path}"
82
+ end
83
+ end
84
+ end
85
+
86
+ class Complete < Dry::CLI::Command
87
+ desc "Print completion candidates"
88
+ argument :kind, required: true, desc: "Completion kind"
89
+ argument :extra, type: :array, required: false, desc: "Extra args for completion"
90
+
91
+ def call(kind:, extra: [], **)
92
+ case kind
93
+ when "checkouts"
94
+ begin
95
+ paths = CLI.make_paths
96
+ config = Config::Store.load(paths.config_file).success
97
+ Nav.scan(config.base_dir).each { |e| out.puts e[:target] }
98
+ rescue
99
+ # Never raise on a completion call — missing/unparseable config is fine.
100
+ end
101
+ CLI.record_outcome(Outcome.new(exit_code: 0))
102
+ when "shells"
103
+ out.puts "fish"
104
+ CLI.record_outcome(Outcome.new(exit_code: 0))
105
+ else
106
+ err.puts "Usage: src shell complete checkouts|shells"
107
+ CLI.record_outcome(Outcome.new(exit_code: 1))
108
+ end
109
+ rescue => e
110
+ err.puts "src shell complete: #{e.message}"
111
+ CLI.record_outcome(Outcome.new(exit_code: 1))
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ Space::Src::CLI::Registry.register "shell" do |prefix|
119
+ prefix.register "init", Space::Src::CLI::Shell::Init
120
+ prefix.register "fish", Space::Src::CLI::Shell::Fish
121
+ prefix.register "complete", Space::Src::CLI::Shell::Complete
122
+ end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "pastel"
4
- require "space_architect/pristine/cli"
5
- require "space_architect/pristine/ui/mode"
6
- require "space_architect/pristine/cli/options"
4
+ require "space_src/cli"
5
+ require "space_src/ui/mode"
6
+ require "space_src/cli/options"
7
7
 
8
- module SpaceArchitect::Pristine
8
+ module Space::Src
9
9
  module CLI
10
10
  # `status` command: render a per-repo evergreen table from
11
11
  # State::Store. Per G5, the rows must include each repo key
@@ -23,7 +23,7 @@ module SpaceArchitect::Pristine
23
23
  "error" => :red
24
24
  }.freeze
25
25
 
26
- desc "Show the per-repo evergreen status table (from $XDG_STATE_HOME/repo-tender/state.yaml)"
26
+ desc "Show the per-repo evergreen status table (from $XDG_STATE_HOME/space-src/state.yaml)"
27
27
 
28
28
  def call(plain: nil, json: nil, no_color: nil, quiet: nil, **)
29
29
  mode = UI::Mode.resolve(
@@ -37,7 +37,7 @@ module SpaceArchitect::Pristine
37
37
  state = State::Store.load(paths.state_file).success
38
38
 
39
39
  if state.repos.empty?
40
- out.puts "(no repos in state — run `repo-tender sync` to populate)"
40
+ out.puts "(no repos in state — run `src sync` to populate)"
41
41
  return CLI.record_outcome(Outcome.new(exit_code: 0))
42
42
  end
43
43
 
@@ -73,4 +73,4 @@ module SpaceArchitect::Pristine
73
73
  end
74
74
  end
75
75
 
76
- SpaceArchitect::Pristine::CLI::Registry.register "status", SpaceArchitect::Pristine::CLI::Status::Show
76
+ Space::Src::CLI::Registry.register "status", Space::Src::CLI::Status::Show
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dry/monads"
4
- require "space_architect/pristine/cli"
5
- require "space_architect/pristine/cli/repo" # for Repo::Helpers.parse_ref
6
- require "space_architect/pristine/cli/options"
7
- require "space_architect/pristine/ui/mode"
8
- require "space_architect/pristine/ui/plain_reporter"
9
- require "space_architect/pristine/ui/json_reporter"
10
- require "space_architect/pristine/ui/interactive_reporter"
11
-
12
- module SpaceArchitect::Pristine
4
+ require "space_src/cli"
5
+ require "space_src/cli/repo" # for Repo::Helpers.parse_ref
6
+ require "space_src/cli/options"
7
+ require "space_src/ui/mode"
8
+ require "space_src/ui/plain_reporter"
9
+ require "space_src/ui/json_reporter"
10
+ require "space_src/ui/interactive_reporter"
11
+
12
+ module Space::Src
13
13
  module CLI
14
14
  # `sync` command: invoke Sync::Engine over the full config, or
15
15
  # scope to a single repo with --repo.
@@ -74,7 +74,7 @@ module SpaceArchitect::Pristine
74
74
  UI::PlainReporter.new(out, mode: mode)
75
75
  end
76
76
 
77
- result = SpaceArchitect::Pristine::Sync::Engine.new(reporter: reporter).call(config: config, paths: paths)
77
+ result = Space::Src::Sync::Engine.new(reporter: reporter).call(config: config, paths: paths)
78
78
  if result.failure?
79
79
  return fail_with(self, "sync failed: #{format_failure(result.failure)}")
80
80
  end
@@ -95,11 +95,11 @@ module SpaceArchitect::Pristine
95
95
 
96
96
  def fail_with(cmd, msg)
97
97
  cmd.send(:err).puts msg
98
- SpaceArchitect::Pristine::CLI.record_outcome(Outcome.new(exit_code: 1, message: msg))
98
+ Space::Src::CLI.record_outcome(Outcome.new(exit_code: 1, message: msg))
99
99
  end
100
100
 
101
101
  # Default log-rotation threshold: 10 MiB. Tunable via the
102
- # env var `REPO_TENDER_LOG_MAX_BYTES` (introspection /
102
+ # env var `SPACE_SRC_LOG_MAX_BYTES` (introspection /
103
103
  # ops escape hatch). The LogRotator itself is unit-tested
104
104
  # with an injected threshold (gate G5).
105
105
  DEFAULT_LOG_MAX_BYTES = 10 * 1024 * 1024
@@ -109,12 +109,12 @@ module SpaceArchitect::Pristine
109
109
  label = Launchd::Agent::DEFAULT_LABEL
110
110
  [File.join(paths.log_dir, "#{label}.out.log"),
111
111
  File.join(paths.log_dir, "#{label}.err.log")].each do |p|
112
- SpaceArchitect::Pristine::LogRotator.call(p, threshold_bytes: threshold)
112
+ Space::Src::LogRotator.call(p, threshold_bytes: threshold)
113
113
  end
114
114
  end
115
115
 
116
116
  # CF6 (Slice 5): defensively parse the
117
- # `REPO_TENDER_LOG_MAX_BYTES` env var so a malformed
117
+ # `SPACE_SRC_LOG_MAX_BYTES` env var so a malformed
118
118
  # operator value (e.g. `"10MB"`) falls back to the
119
119
  # 10 MiB default instead of raising `ArgumentError`
120
120
  # and crashing the entire `sync` run before any repo
@@ -131,13 +131,13 @@ module SpaceArchitect::Pristine
131
131
  # tests can pass arbitrary values without mutating
132
132
  # the real `ENV`; production callers invoke with
133
133
  # no args and the method reads `ENV` itself.
134
- def log_max_bytes(env_value = ENV["REPO_TENDER_LOG_MAX_BYTES"])
134
+ def log_max_bytes(env_value = ENV["SPACE_SRC_LOG_MAX_BYTES"])
135
135
  return DEFAULT_LOG_MAX_BYTES if env_value.nil? || env_value.strip.empty?
136
136
 
137
137
  parsed = Integer(env_value, 10, exception: false)
138
138
  return parsed if parsed.is_a?(Integer) && parsed.positive?
139
139
 
140
- warn "repo-tender: REPO_TENDER_LOG_MAX_BYTES=#{env_value.inspect} is invalid; " \
140
+ warn "src: SPACE_SRC_LOG_MAX_BYTES=#{env_value.inspect} is invalid; " \
141
141
  "falling back to #{DEFAULT_LOG_MAX_BYTES} bytes"
142
142
  DEFAULT_LOG_MAX_BYTES
143
143
  end
@@ -146,4 +146,4 @@ module SpaceArchitect::Pristine
146
146
  end
147
147
  end
148
148
 
149
- SpaceArchitect::Pristine::CLI::Registry.register "sync", SpaceArchitect::Pristine::CLI::Sync::Run
149
+ Space::Src::CLI::Registry.register "sync", Space::Src::CLI::Sync::Run