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
@@ -1,117 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dry/monads"
4
-
5
- module SpaceArchitect
6
- module CLI
7
- module Helpers
8
- include Dry::Monads[:result]
9
- def project_config
10
- @project_config ||= SpaceArchitect::Config.load
11
- end
12
-
13
- def state
14
- @state ||= SpaceArchitect::State.load
15
- end
16
-
17
- def store
18
- @store ||= SpaceArchitect::SpaceStore.new(config: project_config, state: state)
19
- end
20
-
21
- def terminal
22
- @terminal
23
- end
24
-
25
- def setup_terminal(color: "auto", colors: nil)
26
- @terminal = Terminal.new(
27
- config: project_config,
28
- stdout: out,
29
- stderr: err,
30
- color_mode: colors || color || "auto"
31
- )
32
- end
33
-
34
- def display_date(space)
35
- id_date = space.id.match(/\A(\d{4})(\d{2})(\d{2})/)
36
- return "#{id_date[1]}-#{id_date[2]}-#{id_date[3]}" if id_date
37
-
38
- space.data["created_at"].to_s[0, 10]
39
- end
40
-
41
- def handle_errors
42
- yield
43
- rescue SpaceArchitect::Error => e
44
- if terminal
45
- terminal.error(e.message)
46
- else
47
- err.puts e.message
48
- end
49
- CLI.record_outcome(Outcome.new(exit_code: 1, message: e.message))
50
- end
51
-
52
- def render(result)
53
- case result
54
- when Dry::Monads::Result::Success
55
- yield result.value! if block_given?
56
- when Dry::Monads::Result::Failure
57
- error = result.failure
58
- message = error.respond_to?(:message) ? error.message : error.to_s
59
- terminal ? terminal.error(message) : err.puts(message)
60
- CLI.record_outcome(Outcome.new(exit_code: 1, message: message))
61
- end
62
- end
63
- end
64
-
65
- class RepoProgress
66
- def initialize(total)
67
- @total = total
68
- @statuses = {}
69
- end
70
-
71
- def start(addition)
72
- source = addition[:src_source]
73
- @statuses[addition.fetch(:reference).full_name] = source&.directory? ? :copying : :cloning
74
- end
75
-
76
- def trust(addition)
77
- @statuses[addition.fetch(:reference).full_name] = :trusting
78
- end
79
-
80
- def finish(addition)
81
- @statuses[addition.fetch(:reference).full_name] = :done
82
- end
83
-
84
- def fail(addition)
85
- @statuses[addition.fetch(:reference).full_name] = :failed
86
- end
87
-
88
- def message
89
- done = @statuses.count { |_repo, status| status == :done }
90
- failed = @statuses.count { |_repo, status| status == :failed }
91
- copying = @statuses.select { |_repo, status| status == :copying }.keys
92
- cloning = @statuses.select { |_repo, status| status == :cloning }.keys
93
- trusting = @statuses.select { |_repo, status| status == :trusting }.keys
94
-
95
- if @total == 1
96
- copying_repo = copying.first
97
- cloning_repo = cloning.first
98
- trusting_repo = trusting.first
99
- return "Copying #{copying_repo}" if copying_repo
100
- return "Cloning #{cloning_repo}" if cloning_repo
101
- return "Trusting #{trusting_repo}" if trusting_repo
102
- return "Fetch failed" if failed.positive?
103
-
104
- "Preparing repos"
105
- else
106
- active = []
107
- active << "copying #{copying.join(', ')}" unless copying.empty?
108
- active << "cloning #{cloning.join(', ')}" unless cloning.empty?
109
- active << "trusting #{trusting.join(', ')}" unless trusting.empty?
110
- suffix = active.empty? ? nil : ": #{active.join('; ')}"
111
- failed_text = failed.positive? ? ", #{failed} failed" : ""
112
- "Fetching repos #{done}/#{@total}#{failed_text}#{suffix}"
113
- end
114
- end
115
- end
116
- end
117
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpaceArchitect
4
- module CLI
5
- class Init < Dry::CLI::Command
6
- include GlobalOptions
7
- include Helpers
8
-
9
- desc "Create default XDG config and state files"
10
- option :force, type: :boolean, default: false, desc: "Overwrite existing config and state files"
11
-
12
- def call(force: false, **opts)
13
- setup_terminal(**opts.slice(:color, :colors))
14
- handle_errors do
15
- if force
16
- @project_config = SpaceArchitect::Config.new
17
- @state = SpaceArchitect::State.new
18
- project_config.save
19
- state.save
20
- else
21
- project_config.ensure_exists!
22
- state.ensure_exists!
23
- end
24
-
25
- FileUtils.mkdir_p(project_config.spaces_dir)
26
- terminal.success "Config: #{terminal.path(project_config.path)}"
27
- terminal.success "State: #{terminal.path(state.path)}"
28
- terminal.success "Spaces: #{terminal.path(project_config.spaces_dir)}"
29
- CLI.record_outcome(Outcome.new(exit_code: 0))
30
- end
31
- end
32
- end
33
- end
34
- end
35
-
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpaceArchitect
4
- module CLI
5
- class List < Dry::CLI::Command
6
- include GlobalOptions
7
- include Helpers
8
-
9
- desc "List spaces"
10
-
11
- def call(**opts)
12
- setup_terminal(**opts.slice(:color, :colors))
13
- handle_errors do
14
- spaces = store.list
15
- if spaces.empty?
16
- terminal.say "No spaces found in #{terminal.path(project_config.spaces_dir)}"
17
- next
18
- end
19
-
20
- rows = spaces.map do |space|
21
- [space.status, display_date(space), space.title, terminal.path(space.path)]
22
- end
23
- terminal.say terminal.table(%w[Status Date Title Path], rows)
24
- CLI.record_outcome(Outcome.new(exit_code: 0))
25
- end
26
- end
27
- end
28
- end
29
- end
30
-
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpaceArchitect
4
- module CLI
5
- class New < Dry::CLI::Command
6
- include GlobalOptions
7
- include Helpers
8
-
9
- desc "Create a new project space"
10
- argument :title, required: true, desc: "Space title"
11
- argument :repos, type: :array, required: false, desc: "Repo refs to clone"
12
- option :git, type: :boolean, default: true, desc: "Initialize the space as a Git repository (use --no-git to skip)"
13
-
14
- def call(title:, repos: [], git: true, **opts)
15
- setup_terminal(**opts.slice(:color, :colors))
16
- result = store.create(title, git: git).bind do |space|
17
- terminal.success "Created #{space.id}"
18
-
19
- repo_specs = Array(repos).compact
20
- repo_specs.each { |spec| terminal.say "Queued #{spec}" }
21
-
22
- next Success(space) if repo_specs.empty?
23
-
24
- progress = RepoProgress.new(repo_specs.length)
25
- terminal.with_spinner(-> { progress.message }) do
26
- store.add_repos_to(space, repo_specs, reporter: progress)
27
- end.fmap do |results|
28
- results.each do |r|
29
- terminal.success "Added #{r.fetch(:repo).fetch('full_name')}"
30
- terminal.say terminal.path(r.fetch(:path))
31
- end
32
- space
33
- end
34
- end
35
- render(result) do |space|
36
- terminal.say terminal.path(space.path)
37
- CLI.record_outcome(Outcome.new(exit_code: 0))
38
- end
39
- end
40
- end
41
- end
42
- end
43
-
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpaceArchitect
4
- module CLI
5
- module GlobalOptions
6
- def self.included(base)
7
- base.option :color, type: :string, default: "auto", desc: "Color output: auto, always, never"
8
- base.option :colors, type: :string, desc: "Alias for --color"
9
- end
10
- end
11
- end
12
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpaceArchitect
4
- module CLI
5
- class Path < Dry::CLI::Command
6
- include GlobalOptions
7
- include Helpers
8
-
9
- desc "Print the path for a space or the current space"
10
- argument :identifier, required: false, desc: "Space ID or title slug"
11
-
12
- def call(identifier: nil, **opts)
13
- setup_terminal(**opts.slice(:color, :colors))
14
- render(store.path_for(identifier)) do |path|
15
- terminal.say terminal.path(path)
16
- CLI.record_outcome(Outcome.new(exit_code: 0))
17
- end
18
- end
19
- end
20
- end
21
- end
22
-
@@ -1,88 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpaceArchitect
4
- module CLI
5
- module Repo
6
- class Add < Dry::CLI::Command
7
- include GlobalOptions
8
- include Helpers
9
-
10
- desc "Clone repos into the current space"
11
- argument :repos, type: :array, required: false, desc: "REPO [REPO...]"
12
-
13
- def call(repos: [], **opts)
14
- setup_terminal(**opts.slice(:color, :colors))
15
- handle_errors do
16
- specs = Array(repos).compact
17
- if specs.empty?
18
- terminal.error("Usage: space repo add REPO [REPO...]")
19
- CLI.record_outcome(Outcome.new(exit_code: 1))
20
- next
21
- end
22
-
23
- progress = RepoProgress.new(specs.length)
24
- add_result = terminal.with_spinner(-> { progress.message }) do
25
- store.add_repos(specs, reporter: progress)
26
- end
27
- render(add_result) do |results|
28
- results.each do |result|
29
- terminal.success "Added #{result.fetch(:repo).fetch('full_name')}"
30
- terminal.say terminal.path(result.fetch(:path))
31
- end
32
- CLI.record_outcome(Outcome.new(exit_code: 0))
33
- end
34
- end
35
- end
36
- end
37
-
38
- class RepoList < Dry::CLI::Command
39
- include GlobalOptions
40
- include Helpers
41
-
42
- desc "List repos in the current space"
43
-
44
- def call(**opts)
45
- setup_terminal(**opts.slice(:color, :colors))
46
- handle_errors do
47
- render(store.repos) do |repos|
48
- if repos.empty?
49
- id = store.find.fmap(&:id).value_or("(unknown space)")
50
- terminal.say "No repos found in #{id}"
51
- next
52
- end
53
-
54
- rows = repos.map { |repo| [repo.fetch("full_name", repo["name"]), repo.fetch("path", "")] }
55
- terminal.say terminal.table(["Repo", "Path"], rows)
56
- CLI.record_outcome(Outcome.new(exit_code: 0))
57
- end
58
- end
59
- end
60
- end
61
-
62
- class Resolve < Dry::CLI::Command
63
- include GlobalOptions
64
- include Helpers
65
-
66
- desc "Resolve repo refs without cloning"
67
- argument :repos, type: :array, required: false, desc: "REPO [REPO...]"
68
-
69
- def call(repos: [], **opts)
70
- setup_terminal(**opts.slice(:color, :colors))
71
- handle_errors do
72
- specs = Array(repos).compact
73
- if specs.empty?
74
- terminal.error("Usage: space repo resolve REPO [REPO...]")
75
- CLI.record_outcome(Outcome.new(exit_code: 1))
76
- next
77
- end
78
-
79
- references = specs.map { |spec| RepoResolver.new(project_config).resolve(spec) }
80
- terminal.say terminal.table(["Repo", "Clone URL"], references.map { |ref| [ref.full_name, ref.clone_url] })
81
- CLI.record_outcome(Outcome.new(exit_code: 0))
82
- end
83
- end
84
- end
85
- end
86
- end
87
- end
88
-
@@ -1,137 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpaceArchitect
4
- module CLI
5
- module Shell
6
- class ShellInit < Dry::CLI::Command
7
- include GlobalOptions
8
- include Helpers
9
-
10
- desc "Print shell integration script"
11
- argument :shell_name, required: true, desc: "Shell name (e.g. fish)"
12
-
13
- def call(shell_name:, **opts)
14
- setup_terminal(**opts.slice(:color, :colors))
15
- handle_errors do
16
- terminal.say ShellIntegration.for(shell_name)
17
- CLI.record_outcome(Outcome.new(exit_code: 0))
18
- end
19
- end
20
- end
21
-
22
- class Fish < Dry::CLI::Command
23
- include GlobalOptions
24
- include Helpers
25
-
26
- desc "Manage fish shell integration: install, uninstall, path"
27
- argument :subcommand, required: false, desc: "install, uninstall, or path (default: install)"
28
- option :force, type: :boolean, default: false, desc: "Overwrite or remove existing shell files"
29
-
30
- def call(subcommand: "install", force: false, **opts)
31
- setup_terminal(**opts.slice(:color, :colors))
32
- handle_errors do
33
- case subcommand
34
- when "install"
35
- result = ShellIntegration.install("fish", env: project_config.env, force: force)
36
- terminal.success fish_install_message(result.fetch(:action), result.fetch(:path))
37
- terminal.success fish_completions_install_message(result.fetch(:completions_action), result.fetch(:completions_path))
38
- terminal.say "Restart fish to load the integration in this terminal: exec fish"
39
- when "uninstall"
40
- result = ShellIntegration.uninstall("fish", env: project_config.env, force: force)
41
- terminal.success fish_uninstall_message(result.fetch(:action), result.fetch(:path))
42
- terminal.success fish_completions_uninstall_message(result.fetch(:completions_action), result.fetch(:completions_path))
43
- when "path"
44
- terminal.say "Function: #{terminal.path(ShellIntegration.path_for('fish', env: project_config.env))}"
45
- terminal.say "Completions: #{terminal.path(ShellIntegration.completions_path_for('fish', env: project_config.env))}"
46
- else
47
- err.puts "Usage: space shell fish [install|uninstall|path]"
48
- CLI.record_outcome(Outcome.new(exit_code: 1))
49
- next
50
- end
51
- CLI.record_outcome(Outcome.new(exit_code: 0))
52
- end
53
- end
54
-
55
- private
56
-
57
- def fish_install_message(action, path)
58
- case action
59
- when :unchanged then "Fish integration already installed: #{terminal.path(path)}"
60
- when :updated then "Updated fish integration: #{terminal.path(path)}"
61
- else "Installed fish integration: #{terminal.path(path)}"
62
- end
63
- end
64
-
65
- def fish_uninstall_message(action, path)
66
- case action
67
- when :missing then "Fish integration was not installed: #{terminal.path(path)}"
68
- else "Removed fish integration: #{terminal.path(path)}"
69
- end
70
- end
71
-
72
- def fish_completions_install_message(action, path)
73
- case action
74
- when :unchanged then "Fish completions already installed: #{terminal.path(path)}"
75
- when :updated then "Updated fish completions: #{terminal.path(path)}"
76
- else "Installed fish completions: #{terminal.path(path)}"
77
- end
78
- end
79
-
80
- def fish_completions_uninstall_message(action, path)
81
- case action
82
- when :missing then "Fish completions were not installed: #{terminal.path(path)}"
83
- else "Removed fish completions: #{terminal.path(path)}"
84
- end
85
- end
86
- end
87
-
88
- class Complete < Dry::CLI::Command
89
- include GlobalOptions
90
- include Helpers
91
-
92
- desc "Print completion candidates"
93
- argument :kind, required: true, desc: "Completion kind"
94
- argument :extra, type: :array, required: false, desc: "Extra args for completion"
95
-
96
- def call(kind:, extra: [], **opts)
97
- setup_terminal(**opts.slice(:color, :colors))
98
- handle_errors do
99
- completion_candidates(kind, Array(extra)).each { |c| terminal.say c }
100
- CLI.record_outcome(Outcome.new(exit_code: 0))
101
- end
102
- end
103
-
104
- private
105
-
106
- def completion_candidates(kind, args)
107
- case kind
108
- when "spaces" then store.list.map { |space| "#{space.id}\t#{space.title}" }
109
- when "statuses" then Space::VALID_STATUSES
110
- when "config-keys" then SpaceArchitect::Config::EDITABLE_KEYS
111
- when "config-values" then completion_values_for_config_key(args.first)
112
- when "shells" then ["fish"]
113
- when "color-modes" then %w[auto always never]
114
- when "repo-subcommands" then %w[add list ls resolve]
115
- when "config-subcommands" then %w[show path set]
116
- when "fish-subcommands" then %w[install uninstall path]
117
- else
118
- raise SpaceArchitect::Error, "Usage: space shell complete #{completion_kinds.join('|')}"
119
- end
120
- end
121
-
122
- def completion_values_for_config_key(key)
123
- case key
124
- when "git_clone_protocol" then SpaceArchitect::Config::VALID_GIT_CLONE_PROTOCOLS
125
- when "default_provider" then %w[github.com gitlab.com]
126
- else []
127
- end
128
- end
129
-
130
- def completion_kinds
131
- %w[spaces statuses config-keys config-values shells color-modes repo-subcommands config-subcommands fish-subcommands]
132
- end
133
- end
134
- end
135
- end
136
- end
137
-
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpaceArchitect
4
- module CLI
5
- class Show < Dry::CLI::Command
6
- include GlobalOptions
7
- include Helpers
8
-
9
- desc "Show metadata for a space or the current space"
10
- argument :identifier, required: false, desc: "Space ID or title slug"
11
-
12
- def call(identifier: nil, **opts)
13
- setup_terminal(**opts.slice(:color, :colors))
14
- render(store.find(identifier)) do |space|
15
- terminal.say "ID: #{space.id}"
16
- terminal.say "Title: #{space.title}"
17
- terminal.say "Status: #{space.status}"
18
- terminal.say "Path: #{terminal.path(space.path)}"
19
- terminal.say "Created: #{space.data['created_at']}"
20
- terminal.say "Updated: #{space.data['updated_at']}"
21
- CLI.record_outcome(Outcome.new(exit_code: 0))
22
- end
23
- end
24
- end
25
- end
26
- end
27
-
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpaceArchitect
4
- module CLI
5
- class Status < Dry::CLI::Command
6
- include GlobalOptions
7
- include Helpers
8
-
9
- desc "Set a space status: active, paused, done, archived"
10
- argument :rest, type: :array, required: false, desc: "[SPACE] STATUS"
11
-
12
- def call(rest: [], **opts)
13
- setup_terminal(**opts.slice(:color, :colors))
14
- handle_errors do
15
- identifier, status_value = parse_status_args(Array(rest))
16
- render(store.find(identifier)) do |space|
17
- space.update_status(status_value)
18
- terminal.success "#{space.id} is #{space.status}"
19
- CLI.record_outcome(Outcome.new(exit_code: 0))
20
- end
21
- end
22
- end
23
-
24
- private
25
-
26
- def parse_status_args(args)
27
- case args.length
28
- when 1
29
- [nil, args.first]
30
- when 2
31
- args
32
- else
33
- raise SpaceArchitect::Error, "Usage: space status [SPACE] STATUS"
34
- end
35
- end
36
- end
37
- end
38
- end
39
-
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpaceArchitect
4
- module CLI
5
- class Use < Dry::CLI::Command
6
- include GlobalOptions
7
- include Helpers
8
-
9
- desc "Remember a space in recent state and print its path"
10
- argument :identifier, required: true, desc: "Space ID or title slug"
11
-
12
- def call(identifier:, **opts)
13
- setup_terminal(**opts.slice(:color, :colors))
14
- render(store.use(identifier)) do |space|
15
- terminal.success "Recent space: #{space.id}"
16
- terminal.say terminal.path(space.path)
17
- CLI.record_outcome(Outcome.new(exit_code: 0))
18
- end
19
- end
20
- end
21
- end
22
- end
23
-
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpaceArchitect
4
- VERSION = "1.3.0"
5
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "space_architect/pristine/version"
4
-
5
- # repo-tender — keep local git clones evergreen.
6
- # (clean · on the remote's default branch · fetched within refresh_interval)
7
- #
8
- # Slice 1 surface: Paths, Shell, Config::{Model,Contract,Store},
9
- # State::Store, SCM::{Client,Git,Status}, Forge::{Client,GitHub}.
10
- # Later slices build sync orchestration, CLI, and launchd on top.
11
-
12
- module SpaceArchitect::Pristine
13
- end
14
-
15
- require "space_architect/pristine/paths"
16
- require "space_architect/pristine/shell"
17
- require "space_architect/pristine/config/model"
18
- require "space_architect/pristine/config/contract"
19
- require "space_architect/pristine/config/store"
20
- require "space_architect/pristine/state/store"
21
- require "space_architect/pristine/state/lock"
22
- require "space_architect/pristine/scm/client"
23
- require "space_architect/pristine/scm/status"
24
- require "space_architect/pristine/scm/git"
25
- require "space_architect/pristine/forge/client"
26
- require "space_architect/pristine/forge/github"
27
- require "space_architect/pristine/sync/repo_plan"
28
- require "space_architect/pristine/sync/engine"
29
- require "space_architect/pristine/config/duration"
30
- require "space_architect/pristine/log_rotator"
31
- require "space_architect/pristine/launchd/plist"
32
- require "space_architect/pristine/launchd/agent"
33
- require "space_architect/pristine/ui/reporter"
34
- require "space_architect/pristine/ui/mode"
35
- require "space_architect/pristine/ui/plain_reporter"
36
- require "space_architect/pristine/ui/json_reporter"
37
- require "space_architect/pristine/cli"
38
- require "space_architect/pristine/cli/repo"
39
- require "space_architect/pristine/cli/org"
40
- require "space_architect/pristine/cli/sync"
41
- require "space_architect/pristine/cli/status"
42
- require "space_architect/pristine/cli/config"
43
- require "space_architect/pristine/cli/daemon"
44
- require "space_architect/pristine/cli/clone"