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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +103 -0
- data/README.md +267 -158
- data/exe/architect +1 -1
- data/exe/space +2 -2
- data/exe/src +13 -0
- data/lib/space_architect/{architect_mission.rb → architect_project.rb} +475 -125
- data/lib/space_architect/cli/architect.rb +248 -178
- data/lib/space_architect/cli/research.rb +94 -0
- data/lib/space_architect/cli/space.rb +25 -31
- data/lib/space_architect/cli/src.rb +20 -14
- data/lib/space_architect/cli.rb +22 -22
- data/lib/space_architect/dispatcher.rb +5 -1
- data/lib/space_architect/gate_evaluator.rb +65 -0
- data/lib/space_architect/gate_lint.rb +140 -0
- data/lib/space_architect/harness.rb +145 -17
- data/lib/space_architect/research/mux.rb +127 -0
- data/lib/space_architect/research/registry.rb +70 -0
- data/lib/space_architect/research/renderer.rb +101 -0
- data/lib/space_architect/research/run.rb +7 -0
- data/lib/space_architect/research/supervisor.rb +108 -0
- data/lib/space_architect/research.rb +13 -0
- data/lib/space_architect/run_creator.rb +53 -0
- data/lib/space_architect/skill_installer.rb +81 -79
- data/lib/space_architect/templates/architect.md.erb +15 -4
- data/lib/space_architect/templates/brief.md.erb +5 -5
- data/lib/space_architect/templates/iteration.md.erb +17 -6
- data/lib/space_architect.rb +8 -21
- data/lib/{space_architect → space_core}/atomic_write.rb +1 -1
- data/lib/space_core/cli/base_command.rb +19 -0
- data/lib/space_core/cli/build.rb +27 -0
- data/lib/space_core/cli/config.rb +49 -0
- data/lib/space_core/cli/current.rb +16 -0
- data/lib/space_core/cli/help.rb +123 -0
- data/lib/space_core/cli/helpers.rb +115 -0
- data/lib/space_core/cli/init.rb +29 -0
- data/lib/space_core/cli/list.rb +24 -0
- data/lib/space_core/cli/new.rb +38 -0
- data/lib/space_core/cli/pack.rb +29 -0
- data/lib/space_core/cli/path.rb +16 -0
- data/lib/space_core/cli/repeatable_options.rb +75 -0
- data/lib/space_core/cli/repo.rb +76 -0
- data/lib/space_core/cli/run.rb +29 -0
- data/lib/space_core/cli/shell.rb +125 -0
- data/lib/space_core/cli/show.rb +21 -0
- data/lib/space_core/cli/status.rb +33 -0
- data/lib/space_core/cli/use.rb +17 -0
- data/lib/space_core/cli.rb +177 -0
- data/lib/{space_architect → space_core}/config.rb +1 -1
- data/lib/{space_architect → space_core}/errors.rb +1 -1
- data/lib/{space_architect → space_core}/git_client.rb +1 -1
- data/lib/{space_architect → space_core}/mise_client.rb +1 -1
- data/lib/space_core/oci_builder.rb +56 -0
- data/lib/space_core/oci_packer.rb +99 -0
- data/lib/space_core/oci_runner.rb +73 -0
- data/lib/{space_architect → space_core}/repo_reference.rb +1 -1
- data/lib/{space_architect → space_core}/repo_resolver.rb +1 -1
- data/lib/{space_architect → space_core}/shell_integration.rb +1 -1
- data/lib/{space_architect → space_core}/slugger.rb +1 -1
- data/lib/{space_architect → space_core}/space.rb +11 -3
- data/lib/{space_architect → space_core}/space_store.rb +13 -13
- data/lib/{space_architect → space_core}/state.rb +1 -1
- data/lib/space_core/templates/oci/dockerfile.erb +63 -0
- data/lib/space_core/templates/oci/dockerignore.erb +17 -0
- data/lib/space_core/templates/oci/entrypoint.sh.erb +10 -0
- data/lib/{space_architect → space_core}/terminal.rb +1 -1
- data/lib/space_core/version.rb +7 -0
- data/lib/{space_architect → space_core}/warnings.rb +1 -1
- data/lib/{space_architect → space_core}/xdg.rb +1 -1
- data/lib/space_core.rb +24 -0
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/clone.rb +5 -5
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/config.rb +7 -7
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/daemon.rb +46 -30
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/options.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/org.rb +9 -9
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/repo.rb +9 -9
- data/lib/space_src/cli/shell.rb +122 -0
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/status.rb +7 -7
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/sync.rb +17 -17
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli.rb +42 -11
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cloner.rb +3 -3
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/contract.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/duration.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/model.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/store.rb +5 -5
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/forge/client.rb +2 -2
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/forge/github.rb +4 -4
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/launchd/agent.rb +5 -5
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/launchd/plist.rb +3 -3
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/log_rotator.rb +1 -1
- data/lib/space_src/migration.rb +43 -0
- data/lib/space_src/nav.rb +98 -0
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/paths.rb +2 -2
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/scm/client.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/scm/git.rb +4 -4
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/scm/status.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/shell.rb +1 -1
- data/lib/space_src/shell_integration.rb +321 -0
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/state/lock.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/state/store.rb +2 -2
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/sync/engine.rb +12 -12
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/sync/repo_plan.rb +3 -3
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/interactive_reporter.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/json_reporter.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/mode.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/plain_reporter.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/reporter.rb +1 -1
- data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/version.rb +2 -2
- data/lib/space_src.rb +37 -0
- data/skill/architect/SKILL.md +110 -54
- data/skill/architect/dispatch.md +147 -39
- data/skill/architect/research.md +46 -37
- data/skill/architect-research/SKILL.md +2 -2
- data/skill/architect-vocabulary/SKILL.md +24 -21
- metadata +127 -68
- data/lib/space_architect/cli/config.rb +0 -61
- data/lib/space_architect/cli/current.rb +0 -22
- data/lib/space_architect/cli/helpers.rb +0 -117
- data/lib/space_architect/cli/init.rb +0 -35
- data/lib/space_architect/cli/list.rb +0 -30
- data/lib/space_architect/cli/new.rb +0 -43
- data/lib/space_architect/cli/options.rb +0 -12
- data/lib/space_architect/cli/path.rb +0 -22
- data/lib/space_architect/cli/repo.rb +0 -88
- data/lib/space_architect/cli/shell.rb +0 -137
- data/lib/space_architect/cli/show.rb +0 -27
- data/lib/space_architect/cli/status.rb +0 -39
- data/lib/space_architect/cli/use.rb +0 -23
- data/lib/space_architect/version.rb +0 -5
- 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
|
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 "
|
|
6
|
-
require "
|
|
7
|
-
require "
|
|
5
|
+
require "space_src/cli"
|
|
6
|
+
require "space_src/cli/options"
|
|
7
|
+
require "space_src/cloner"
|
|
8
8
|
|
|
9
|
-
module
|
|
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
|
-
|
|
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 "
|
|
6
|
-
require "
|
|
7
|
-
require "
|
|
5
|
+
require "space_src/cli"
|
|
6
|
+
require "space_src/ui/mode"
|
|
7
|
+
require "space_src/cli/options"
|
|
8
8
|
|
|
9
|
-
module
|
|
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
|
-
|
|
64
|
-
prefix.register "path",
|
|
65
|
-
prefix.register "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 "
|
|
7
|
-
require "
|
|
8
|
-
require "
|
|
9
|
-
require "
|
|
10
|
-
require "
|
|
11
|
-
|
|
12
|
-
|
|
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 `
|
|
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>/
|
|
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
|
-
|
|
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
|
|
287
|
-
#
|
|
288
|
-
#
|
|
289
|
-
#
|
|
290
|
-
#
|
|
291
|
-
class
|
|
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["
|
|
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["
|
|
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["
|
|
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>/
|
|
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, "
|
|
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", "
|
|
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("
|
|
352
|
+
Gem.bin_path("space-architect", "src")
|
|
337
353
|
end
|
|
338
354
|
end
|
|
339
355
|
|
|
340
|
-
|
|
341
|
-
prefix.register "install",
|
|
342
|
-
prefix.register "uninstall",
|
|
343
|
-
prefix.register "start",
|
|
344
|
-
prefix.register "stop",
|
|
345
|
-
prefix.register "restart",
|
|
346
|
-
prefix.register "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
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require "pastel"
|
|
4
4
|
require "dry/monads"
|
|
5
|
-
require "
|
|
6
|
-
require "
|
|
7
|
-
require "
|
|
5
|
+
require "space_src/cli"
|
|
6
|
+
require "space_src/ui/mode"
|
|
7
|
+
require "space_src/cli/options"
|
|
8
8
|
|
|
9
|
-
module
|
|
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
|
-
|
|
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
|
-
|
|
197
|
-
prefix.register "add",
|
|
198
|
-
prefix.register "remove",
|
|
199
|
-
prefix.register "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 "
|
|
6
|
-
require "
|
|
7
|
-
require "
|
|
5
|
+
require "space_src/cli"
|
|
6
|
+
require "space_src/ui/mode"
|
|
7
|
+
require "space_src/cli/options"
|
|
8
8
|
|
|
9
|
-
module
|
|
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
|
-
|
|
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
|
-
|
|
167
|
-
prefix.register "add",
|
|
168
|
-
prefix.register "remove",
|
|
169
|
-
prefix.register "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 "
|
|
5
|
-
require "
|
|
6
|
-
require "
|
|
4
|
+
require "space_src/cli"
|
|
5
|
+
require "space_src/ui/mode"
|
|
6
|
+
require "space_src/cli/options"
|
|
7
7
|
|
|
8
|
-
module
|
|
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/
|
|
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 `
|
|
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
|
-
|
|
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 "
|
|
5
|
-
require "
|
|
6
|
-
require "
|
|
7
|
-
require "
|
|
8
|
-
require "
|
|
9
|
-
require "
|
|
10
|
-
require "
|
|
11
|
-
|
|
12
|
-
module
|
|
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 =
|
|
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
|
-
|
|
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 `
|
|
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
|
-
|
|
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
|
-
# `
|
|
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["
|
|
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 "
|
|
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
|
-
|
|
149
|
+
Space::Src::CLI::Registry.register "sync", Space::Src::CLI::Sync::Run
|