space-architect 1.3.0 → 2.0.0.rc1

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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +103 -0
  3. data/README.md +248 -155
  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 +84 -53
  8. data/lib/space_architect/cli/architect.rb +92 -132
  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/harness.rb +123 -16
  15. data/lib/space_architect/research/mux.rb +127 -0
  16. data/lib/space_architect/research/registry.rb +70 -0
  17. data/lib/space_architect/research/renderer.rb +101 -0
  18. data/lib/space_architect/research/run.rb +7 -0
  19. data/lib/space_architect/research/supervisor.rb +108 -0
  20. data/lib/space_architect/research.rb +13 -0
  21. data/lib/space_architect/run_creator.rb +53 -0
  22. data/lib/space_architect/skill_installer.rb +81 -79
  23. data/lib/space_architect.rb +5 -20
  24. data/lib/{space_architect → space_core}/atomic_write.rb +1 -1
  25. data/lib/space_core/cli/base_command.rb +19 -0
  26. data/lib/space_core/cli/config.rb +49 -0
  27. data/lib/space_core/cli/current.rb +16 -0
  28. data/lib/space_core/cli/help.rb +110 -0
  29. data/lib/space_core/cli/helpers.rb +115 -0
  30. data/lib/space_core/cli/init.rb +29 -0
  31. data/lib/space_core/cli/list.rb +24 -0
  32. data/lib/space_core/cli/new.rb +38 -0
  33. data/lib/space_core/cli/path.rb +16 -0
  34. data/lib/space_core/cli/repeatable_options.rb +75 -0
  35. data/lib/space_core/cli/repo.rb +76 -0
  36. data/lib/space_core/cli/shell.rb +125 -0
  37. data/lib/space_core/cli/show.rb +21 -0
  38. data/lib/space_core/cli/status.rb +33 -0
  39. data/lib/space_core/cli/use.rb +17 -0
  40. data/lib/space_core/cli.rb +171 -0
  41. data/lib/{space_architect → space_core}/config.rb +1 -1
  42. data/lib/{space_architect → space_core}/errors.rb +1 -1
  43. data/lib/{space_architect → space_core}/git_client.rb +1 -1
  44. data/lib/{space_architect → space_core}/mise_client.rb +1 -1
  45. data/lib/{space_architect → space_core}/repo_reference.rb +1 -1
  46. data/lib/{space_architect → space_core}/repo_resolver.rb +1 -1
  47. data/lib/{space_architect → space_core}/shell_integration.rb +1 -1
  48. data/lib/{space_architect → space_core}/slugger.rb +1 -1
  49. data/lib/{space_architect → space_core}/space.rb +1 -1
  50. data/lib/{space_architect → space_core}/space_store.rb +12 -12
  51. data/lib/{space_architect → space_core}/state.rb +1 -1
  52. data/lib/{space_architect → space_core}/terminal.rb +1 -1
  53. data/lib/space_core/version.rb +7 -0
  54. data/lib/{space_architect → space_core}/warnings.rb +1 -1
  55. data/lib/{space_architect → space_core}/xdg.rb +1 -1
  56. data/lib/space_core.rb +24 -0
  57. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/clone.rb +5 -5
  58. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/config.rb +7 -7
  59. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/daemon.rb +46 -30
  60. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/options.rb +1 -1
  61. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/org.rb +9 -9
  62. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/repo.rb +9 -9
  63. data/lib/space_src/cli/shell.rb +122 -0
  64. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/status.rb +7 -7
  65. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli/sync.rb +17 -17
  66. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cli.rb +42 -11
  67. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/cloner.rb +3 -3
  68. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/contract.rb +1 -1
  69. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/duration.rb +1 -1
  70. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/model.rb +1 -1
  71. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/config/store.rb +5 -5
  72. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/forge/client.rb +2 -2
  73. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/forge/github.rb +4 -4
  74. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/launchd/agent.rb +5 -5
  75. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/launchd/plist.rb +3 -3
  76. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/log_rotator.rb +1 -1
  77. data/lib/space_src/migration.rb +43 -0
  78. data/lib/space_src/nav.rb +98 -0
  79. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/paths.rb +2 -2
  80. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/scm/client.rb +1 -1
  81. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/scm/git.rb +4 -4
  82. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/scm/status.rb +1 -1
  83. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/shell.rb +1 -1
  84. data/lib/space_src/shell_integration.rb +321 -0
  85. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/state/lock.rb +1 -1
  86. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/state/store.rb +2 -2
  87. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/sync/engine.rb +12 -12
  88. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/sync/repo_plan.rb +3 -3
  89. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/interactive_reporter.rb +1 -1
  90. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/json_reporter.rb +1 -1
  91. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/mode.rb +1 -1
  92. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/plain_reporter.rb +1 -1
  93. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/ui/reporter.rb +1 -1
  94. data/{vendor/repo-tender/lib/space_architect/pristine → lib/space_src}/version.rb +2 -2
  95. data/lib/space_src.rb +37 -0
  96. data/skill/architect/SKILL.md +2 -2
  97. data/skill/architect/research.md +46 -37
  98. metadata +115 -67
  99. data/lib/space_architect/cli/config.rb +0 -61
  100. data/lib/space_architect/cli/current.rb +0 -22
  101. data/lib/space_architect/cli/helpers.rb +0 -117
  102. data/lib/space_architect/cli/init.rb +0 -35
  103. data/lib/space_architect/cli/list.rb +0 -30
  104. data/lib/space_architect/cli/new.rb +0 -43
  105. data/lib/space_architect/cli/options.rb +0 -12
  106. data/lib/space_architect/cli/path.rb +0 -22
  107. data/lib/space_architect/cli/repo.rb +0 -88
  108. data/lib/space_architect/cli/shell.rb +0 -137
  109. data/lib/space_architect/cli/show.rb +0 -27
  110. data/lib/space_architect/cli/status.rb +0 -39
  111. data/lib/space_architect/cli/use.rb +0 -23
  112. data/lib/space_architect/version.rb +0 -5
  113. data/vendor/repo-tender/lib/space_architect/pristine.rb +0 -44
@@ -0,0 +1,321 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "pathname"
5
+
6
+ module Space::Src
7
+ # Generates the standalone `src` fish shell integration and completions.
8
+ # All paths resolved through Space::Src::Paths (no external core deps).
9
+ module ShellIntegration
10
+ FISH_TEMPLATE = <<~'FISH'
11
+ # Generated by space-src. Do not edit by hand.
12
+
13
+ function src --wraps src --description "Navigate source checkouts"
14
+ if not set -q __src_compat_checked
15
+ set -g __src_compat_checked 1
16
+ set -l __src_installed_version __SRC_VERSION__
17
+ set -l __src_binary_version (command src --version 2>/dev/null)
18
+ if test "$__src_binary_version" != "$__src_installed_version"
19
+ echo "space-src: shell integration version $__src_installed_version does not match binary version $__src_binary_version; re-run 'src shell fish install'" >&2
20
+ end
21
+ end
22
+
23
+ switch "$argv[1]"
24
+ case repo org sync status config daemon clone shell
25
+ command src $argv
26
+ return $status
27
+ case '*'
28
+ set -l __src_output (command src $argv)
29
+ set -l __src_status $status
30
+
31
+ if test (count $__src_output) -gt 0
32
+ printf "%s\n" $__src_output
33
+ end
34
+
35
+ if test $__src_status -eq 0
36
+ set -l __src_target $__src_output[-1]
37
+ set __src_target (string replace -r "^~(?=/|$)" $HOME -- $__src_target)
38
+ if test -d "$__src_target"
39
+ cd "$__src_target"
40
+ end
41
+ end
42
+
43
+ return $__src_status
44
+ end
45
+ end
46
+ FISH
47
+
48
+ FISH_COMPLETIONS = <<~'FISH'
49
+ # Generated by space-src. Do not edit by hand.
50
+
51
+ function __src_complete_command
52
+ set -l tokens (commandline -opc)
53
+ set -l index 2
54
+
55
+ while test $index -le (count $tokens)
56
+ set -l token $tokens[$index]
57
+
58
+ switch "$token"
59
+ case "-*"
60
+ set index (math $index + 1)
61
+ continue
62
+ case "*"
63
+ echo $token
64
+ return 0
65
+ end
66
+ end
67
+ end
68
+
69
+ function __src_complete_needs_command
70
+ test -z "(__src_complete_command)"
71
+ end
72
+
73
+ function __src_complete_using_command
74
+ contains -- (__src_complete_command) $argv
75
+ end
76
+
77
+ function __src_complete_first_argument_after
78
+ set -l commands $argv
79
+ set -l tokens (commandline -opc)
80
+ set -l index 2
81
+ set -l matched_command 0
82
+
83
+ while test $index -le (count $tokens)
84
+ set -l token $tokens[$index]
85
+
86
+ switch "$token"
87
+ case "-*"
88
+ set index (math $index + 1)
89
+ continue
90
+ end
91
+
92
+ if test $matched_command -eq 0
93
+ if contains -- "$token" $commands
94
+ set matched_command 1
95
+ else
96
+ return 1
97
+ end
98
+ else
99
+ echo $token
100
+ return 0
101
+ end
102
+
103
+ set index (math $index + 1)
104
+ end
105
+
106
+ return 1
107
+ end
108
+
109
+ function __src_complete_has_first_argument_after
110
+ set -q argv[1]; or return 1
111
+ set -l first_argument (__src_complete_first_argument_after $argv)
112
+ test -n "$first_argument"
113
+ end
114
+
115
+ function __src_complete_second_argument_after
116
+ set -q argv[1]; or return 1
117
+ set -l command $argv[1]
118
+ set -l tokens (commandline -opc)
119
+ set -l index 2
120
+ set -l matched_command 0
121
+ set -l matched_first_argument 0
122
+
123
+ while test $index -le (count $tokens)
124
+ set -l token $tokens[$index]
125
+
126
+ switch "$token"
127
+ case "-*"
128
+ set index (math $index + 1)
129
+ continue
130
+ end
131
+
132
+ if test $matched_command -eq 0
133
+ test "$token" = "$command"; or return 1
134
+ set matched_command 1
135
+ else if test $matched_first_argument -eq 0
136
+ set matched_first_argument 1
137
+ else
138
+ echo $token
139
+ return 0
140
+ end
141
+
142
+ set index (math $index + 1)
143
+ end
144
+
145
+ return 1
146
+ end
147
+
148
+ function __src_complete_has_second_argument_after
149
+ set -q argv[1]; or return 1
150
+ set -l second_argument (__src_complete_second_argument_after $argv)
151
+ test -n "$second_argument"
152
+ end
153
+
154
+ function __src_complete_first_argument_is
155
+ set -q argv[1]; or return 1
156
+ set -l expected $argv[1]
157
+ set -e argv[1]
158
+ test "$(__src_complete_first_argument_after $argv)" = "$expected"
159
+ end
160
+
161
+ function __src_complete_checkouts
162
+ command src shell complete checkouts 2>/dev/null
163
+ end
164
+
165
+ function __src_complete_shells
166
+ command src shell complete shells 2>/dev/null
167
+ end
168
+
169
+ complete -c src -f -n "__src_complete_needs_command" -a clone -d "Clone a repo into the source tree"
170
+ complete -c src -f -n "__src_complete_needs_command" -a config -d "Show or update config"
171
+ complete -c src -f -n "__src_complete_needs_command" -a daemon -d "Manage the background sync daemon"
172
+ complete -c src -f -n "__src_complete_needs_command" -a org -d "Manage tracked GitHub orgs"
173
+ complete -c src -f -n "__src_complete_needs_command" -a repo -d "Manage tracked repos"
174
+ complete -c src -f -n "__src_complete_needs_command" -a shell -d "Manage shell integration"
175
+ complete -c src -f -n "__src_complete_needs_command" -a status -d "Show sync status"
176
+ complete -c src -f -n "__src_complete_needs_command" -a sync -d "Sync all tracked repos"
177
+ complete -c src -f -n "__src_complete_needs_command" -a "(__src_complete_checkouts)" -d "Checkout"
178
+
179
+ complete -c src -f -n "__src_complete_using_command config; and not __src_complete_has_first_argument_after config" -a path -d "Print config path"
180
+ complete -c src -f -n "__src_complete_using_command config; and not __src_complete_has_first_argument_after config" -a show -d "Show config"
181
+
182
+ complete -c src -f -n "__src_complete_using_command org; and not __src_complete_has_first_argument_after org" -a add -d "Add a GitHub org"
183
+ complete -c src -f -n "__src_complete_using_command org; and not __src_complete_has_first_argument_after org" -a remove -d "Remove a GitHub org"
184
+ complete -c src -f -n "__src_complete_using_command org; and not __src_complete_has_first_argument_after org" -a list -d "List tracked orgs"
185
+
186
+ complete -c src -f -n "__src_complete_using_command repo; and not __src_complete_has_first_argument_after repo" -a add -d "Add a repo"
187
+ complete -c src -f -n "__src_complete_using_command repo; and not __src_complete_has_first_argument_after repo" -a remove -d "Remove a repo"
188
+ complete -c src -f -n "__src_complete_using_command repo; and not __src_complete_has_first_argument_after repo" -a list -d "List repos"
189
+
190
+ complete -c src -f -n "__src_complete_using_command daemon; and not __src_complete_has_first_argument_after daemon" -a install -d "Install launchd agent"
191
+ complete -c src -f -n "__src_complete_using_command daemon; and not __src_complete_has_first_argument_after daemon" -a uninstall -d "Uninstall launchd agent"
192
+ complete -c src -f -n "__src_complete_using_command daemon; and not __src_complete_has_first_argument_after daemon" -a start -d "Start the agent"
193
+ complete -c src -f -n "__src_complete_using_command daemon; and not __src_complete_has_first_argument_after daemon" -a stop -d "Stop the agent"
194
+ complete -c src -f -n "__src_complete_using_command daemon; and not __src_complete_has_first_argument_after daemon" -a restart -d "Restart the agent"
195
+ complete -c src -f -n "__src_complete_using_command daemon; and not __src_complete_has_first_argument_after daemon" -a status -d "Show agent status"
196
+
197
+ complete -c src -f -n "__src_complete_using_command shell; and not __src_complete_has_first_argument_after shell" -a init -d "Print shell integration"
198
+ complete -c src -f -n "__src_complete_using_command shell; and not __src_complete_has_first_argument_after shell" -a fish -d "Manage fish integration and completions"
199
+ complete -c src -f -n "__src_complete_using_command shell; and not __src_complete_has_first_argument_after shell" -a complete -d "Print completion candidates"
200
+
201
+ complete -c src -f -n "__src_complete_first_argument_is fish shell; and not __src_complete_has_second_argument_after shell" -a install -d "Install fish integration and completions"
202
+ complete -c src -f -n "__src_complete_first_argument_is fish shell; and not __src_complete_has_second_argument_after shell" -a uninstall -d "Remove fish integration and completions"
203
+ complete -c src -f -n "__src_complete_first_argument_is fish shell; and not __src_complete_has_second_argument_after shell" -a path -d "Print fish integration paths"
204
+
205
+ complete -c src -f -n "__src_complete_first_argument_is init shell; and not __src_complete_has_second_argument_after shell" -a "(__src_complete_shells)" -d "Shell"
206
+ complete -c src -f -n "__src_complete_first_argument_is complete shell; and not __src_complete_has_second_argument_after shell" -a "checkouts shells" -d "Completion kind"
207
+ FISH
208
+
209
+ def self.for(shell)
210
+ case shell.to_s
211
+ when "fish"
212
+ FISH_TEMPLATE.gsub("__SRC_VERSION__", Space::Src::VERSION)
213
+ else
214
+ raise "Unsupported shell '#{shell}'. Expected: fish"
215
+ end
216
+ end
217
+
218
+ def self.completions_for(shell)
219
+ case shell.to_s
220
+ when "fish"
221
+ FISH_COMPLETIONS
222
+ else
223
+ raise "Unsupported shell '#{shell}'. Expected: fish"
224
+ end
225
+ end
226
+
227
+ def self.path_for(shell, env: ENV)
228
+ case shell.to_s
229
+ when "fish"
230
+ Pathname(Paths.new(environment: env).config_home).join("fish", "functions", "src.fish")
231
+ else
232
+ raise "Unsupported shell '#{shell}'. Expected: fish"
233
+ end
234
+ end
235
+
236
+ def self.completions_path_for(shell, env: ENV)
237
+ case shell.to_s
238
+ when "fish"
239
+ Pathname(Paths.new(environment: env).config_home).join("fish", "completions", "src.fish")
240
+ else
241
+ raise "Unsupported shell '#{shell}'. Expected: fish"
242
+ end
243
+ end
244
+
245
+ def self.install(shell, env: ENV, force: false)
246
+ fn_result = write_managed_file(
247
+ path: path_for(shell, env:),
248
+ content: self.for(shell),
249
+ force: force,
250
+ description: "fish function"
251
+ )
252
+ co_result = write_managed_file(
253
+ path: completions_path_for(shell, env:),
254
+ content: completions_for(shell),
255
+ force: force,
256
+ description: "fish completions"
257
+ )
258
+ fn_result.merge(
259
+ completions_action: co_result.fetch(:action),
260
+ completions_path: co_result.fetch(:path)
261
+ )
262
+ end
263
+
264
+ def self.uninstall(shell, env: ENV, force: false)
265
+ fn_result = remove_managed_file(
266
+ path: path_for(shell, env:),
267
+ content: self.for(shell),
268
+ force: force,
269
+ description: "fish function"
270
+ )
271
+ co_result = remove_managed_file(
272
+ path: completions_path_for(shell, env:),
273
+ content: completions_for(shell),
274
+ force: force,
275
+ description: "fish completions"
276
+ )
277
+ fn_result.merge(
278
+ completions_action: co_result.fetch(:action),
279
+ completions_path: co_result.fetch(:path)
280
+ )
281
+ end
282
+
283
+ def self.managed_src?(content)
284
+ content.include?("Generated by space-src")
285
+ end
286
+
287
+ private_class_method def self.write_managed_file(path:, content:, force:, description:)
288
+ existing = File.read(path) if File.exist?(path)
289
+
290
+ if existing && existing != content && !force && !managed_src?(existing)
291
+ raise "Refusing to overwrite existing #{description} at #{path}. Re-run with --force."
292
+ end
293
+
294
+ if existing == content
295
+ {action: :unchanged, path: path}
296
+ else
297
+ FileUtils.mkdir_p(File.dirname(path))
298
+ tmp = "#{path}.tmp.#{Process.pid}"
299
+ begin
300
+ File.write(tmp, content)
301
+ File.rename(tmp, path)
302
+ ensure
303
+ File.delete(tmp) if File.exist?(tmp)
304
+ end
305
+ {action: existing ? :updated : :installed, path: path}
306
+ end
307
+ end
308
+
309
+ private_class_method def self.remove_managed_file(path:, content:, force:, description:)
310
+ return {action: :missing, path: path} unless File.exist?(path)
311
+
312
+ existing = File.read(path)
313
+ if existing != content && !force && !managed_src?(existing)
314
+ raise "Refusing to remove existing #{description} at #{path}. Re-run with --force."
315
+ end
316
+
317
+ FileUtils.rm_f(path)
318
+ {action: :removed, path: path}
319
+ end
320
+ end
321
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "fileutils"
4
4
 
5
- module SpaceArchitect::Pristine
5
+ module Space::Src
6
6
  module State
7
7
  # Advisory process-level lock on a sidecar file derived from the
8
8
  # state file path. Serializes overlapping `sync` runs so the
@@ -5,9 +5,9 @@ require "fileutils"
5
5
  require "time"
6
6
  require "dry/monads"
7
7
 
8
- module SpaceArchitect::Pristine
8
+ module Space::Src
9
9
  module State
10
- # Machine-managed state at $XDG_STATE_HOME/repo-tender/state.yaml.
10
+ # Machine-managed state at $XDG_STATE_HOME/space-src/state.yaml.
11
11
  # Never hand-edited (per PRD §3.2). Per-repo + per-org records with
12
12
  # a fixed status enum; the store validates the enum and timestamp
13
13
  # format on write.
@@ -6,16 +6,16 @@ require "async"
6
6
  require "async/barrier"
7
7
  require "async/semaphore"
8
8
  require "dry/monads"
9
- require "space_architect/pristine/config/model"
10
- require "space_architect/pristine/scm/git"
11
- require "space_architect/pristine/forge/github"
12
- require "space_architect/pristine/state/store"
13
- require "space_architect/pristine/state/lock"
14
- require "space_architect/pristine/paths"
15
- require "space_architect/pristine/sync/repo_plan"
16
- require "space_architect/pristine/ui/reporter"
17
-
18
- module SpaceArchitect::Pristine
9
+ require "space_src/config/model"
10
+ require "space_src/scm/git"
11
+ require "space_src/forge/github"
12
+ require "space_src/state/store"
13
+ require "space_src/state/lock"
14
+ require "space_src/paths"
15
+ require "space_src/sync/repo_plan"
16
+ require "space_src/ui/reporter"
17
+
18
+ module Space::Src
19
19
  module Sync
20
20
  # The sync engine: one run that brings every tracked repo to the
21
21
  # evergreen invariant (PRD §3.3). Splits observation (RepoPlan)
@@ -68,7 +68,7 @@ module SpaceArchitect::Pristine
68
68
 
69
69
  def initialize(scm: SCM::Git.new, forge: Forge::GitHub.new,
70
70
  clock: -> { Time.now }, url_builder: DEFAULT_URL_BUILDER,
71
- reporter: SpaceArchitect::Pristine::UI::NullReporter.new)
71
+ reporter: Space::Src::UI::NullReporter.new)
72
72
  @scm = scm
73
73
  @forge = forge
74
74
  @clock = clock
@@ -160,7 +160,7 @@ module SpaceArchitect::Pristine
160
160
  end
161
161
 
162
162
  if lock_result == State::Lock::NOT_ACQUIRED
163
- warn "repo-tender: skipped — another sync in progress"
163
+ warn "src: skipped — another sync in progress"
164
164
  Dry::Monads::Success(State::Store.load(paths.state_file).success)
165
165
  else
166
166
  lock_result
@@ -2,10 +2,10 @@
2
2
 
3
3
  require "time"
4
4
  require "dry/monads"
5
- require "space_architect/pristine/scm/client"
6
- require "space_architect/pristine/scm/status"
5
+ require "space_src/scm/client"
6
+ require "space_src/scm/status"
7
7
 
8
- module SpaceArchitect::Pristine
8
+ module Space::Src
9
9
  module Sync
10
10
  # Pure-ish evergreen evaluation. Given a RepoRef + on-disk path +
11
11
  # SCM client + refresh_interval, observe the repo and decide an
@@ -3,7 +3,7 @@
3
3
  require "pastel"
4
4
  require "tty-cursor"
5
5
 
6
- module SpaceArchitect::Pristine
6
+ module Space::Src
7
7
  module UI
8
8
  # Compact, single-line live progress renderer for `sync`.
9
9
  # Driven by one render-loop fiber spawned as a child of the engine task
@@ -3,7 +3,7 @@
3
3
  require "json"
4
4
  require "time"
5
5
 
6
- module SpaceArchitect::Pristine
6
+ module Space::Src
7
7
  module UI
8
8
  # Emits one JSON object per event line (12-factor style) to `out`.
9
9
  # Every object carries at minimum: "event", "t" (ISO8601 timestamp),
@@ -3,7 +3,7 @@
3
3
  require "dry/struct"
4
4
  require "dry/types"
5
5
 
6
- module SpaceArchitect::Pristine
6
+ module Space::Src
7
7
  module UI
8
8
  Types = Dry.Types()
9
9
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module SpaceArchitect::Pristine
3
+ module Space::Src
4
4
  module UI
5
5
  # Emits one tab-separated line per terminal repo event, ANSI-free always.
6
6
  # repo_finished → "ref\tstatus"; repo_failed → "ref\tFAILED\terror".
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module SpaceArchitect::Pristine
3
+ module Space::Src
4
4
  module UI
5
5
  # The reporter event interface. Every implementation must respond to:
6
6
  #
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module SpaceArchitect
4
- module Pristine
3
+ module Space
4
+ module Src
5
5
  VERSION = "0.1.0"
6
6
  end
7
7
  end
data/lib/space_src.rb ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "space_src/version"
4
+
5
+ # space-src — 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 Space::Src
13
+ end
14
+
15
+ require "space_src/paths"
16
+ require "space_src/shell"
17
+ require "space_src/config/model"
18
+ require "space_src/config/contract"
19
+ require "space_src/config/store"
20
+ require "space_src/state/store"
21
+ require "space_src/state/lock"
22
+ require "space_src/scm/client"
23
+ require "space_src/scm/status"
24
+ require "space_src/scm/git"
25
+ require "space_src/forge/client"
26
+ require "space_src/forge/github"
27
+ require "space_src/sync/repo_plan"
28
+ require "space_src/sync/engine"
29
+ require "space_src/config/duration"
30
+ require "space_src/log_rotator"
31
+ require "space_src/launchd/plist"
32
+ require "space_src/launchd/agent"
33
+ require "space_src/ui/reporter"
34
+ require "space_src/ui/mode"
35
+ require "space_src/ui/plain_reporter"
36
+ require "space_src/ui/json_reporter"
37
+ require "space_src/cli"
@@ -126,8 +126,8 @@ loop.
126
126
  an iteration index pointing at each iteration file; per-iteration detail lives
127
127
  in the iteration file, never duplicated into the handoff. `architect status`
128
128
  prints mission state (iterations, freeze_shas, lanes, verdicts) at any point.
129
- - **Space setup (first time):** `architect space new "Mission Name" org/repo …`
130
- (repos are variadic positionals after the title), then `architect init` inside
129
+ - **Space setup (first time):** `architect space new "Mission Name" -r org/repo -r …`
130
+ (each repo is a repeatable `-r` flag after the title), then `architect init` inside
131
131
  the space to scaffold `architecture/ARCHITECT.md`.
132
132
  - Scale to the task: trivial fixes don't need the loop — say so and let the
133
133
  human do it inline or in a normal session. The loop is for iteration-sized
@@ -1,10 +1,13 @@
1
1
  # Research fan-out reference
2
2
 
3
- Read this only when a research trigger fires (see SKILL.md step 3). The
4
- fan-out uses `claude -p` (Sonnet 4.6) as parallel web-research subagents
5
- read-only (no `Edit`/`Write`/`Bash`), built-in `WebSearch`/`WebFetch` — and
6
- the architect keeps all judgment: it verifies the load-bearing claims and writes
7
- the iteration's **Grounds** section itself.
3
+ Read this only when a research trigger fires (see SKILL.md step 3). The fan-out
4
+ uses `architect research dispatch` to launch parallel read-only `claude -p`
5
+ researchers (Sonnet 4.6, no Edit/Write/Bash) and `architect research wait` to
6
+ collect their results. The architect keeps all judgment: it verifies
7
+ load-bearing claims and writes the iteration's **Grounds** section itself.
8
+
9
+ **Note:** the `~/.claude/skills/architect-research/` copy of this skill is
10
+ synced separately and is NOT updated by this repo.
8
11
 
9
12
  ## Fan out
10
13
 
@@ -13,41 +16,42 @@ Cover different angles, not the same angle five times — typical split:
13
16
  official docs/reference, changelog/breaking changes, community failure reports,
14
17
  alternatives/comparisons, security/operational constraints.
15
18
 
16
- One fresh `claude -p` per question, each launched as its own **background Bash
17
- tool call** (`run_in_background`) — one call per researcher, not a shell `&`
18
- loop (a `&` launcher orphans the researchers and the harness reaps them all at
19
- once; same trap as builder dispatch). The researcher toolset is read-only
20
- (`Read,Grep,Glob`) plus the web tools (`WebSearch,WebFetch`) — no
21
- `Edit`/`Write`/`Bash`, so it cannot touch the repo. Capture the final report
22
- by redirecting stdout (the report *is* the text result in the default `text`
23
- output format):
19
+ Write each research prompt to its own file, then dispatch and wait:
24
20
 
25
21
  ```bash
26
- claude -p --model claude-sonnet-4-6 \
27
- --allowedTools 'Read,Grep,Glob,WebSearch,WebFetch' \
28
- --max-turns 40 \
29
- < build/research/<NN>-<topic>.prompt.md \
30
- > build/research/<NN>-<topic>.md
22
+ # 1. Write one prompt file per research question (use Write tool — do NOT shell-redirect)
23
+ # Filenames: <NN>-<topic>.prompt.md e.g. 01-official-api.prompt.md
24
+
25
+ # 2. Dispatch all lanes at once (non-blocking — returns PIDs immediately)
26
+ architect research dispatch \
27
+ 01-official-api.prompt.md \
28
+ 02-changelog.prompt.md
29
+
30
+ # 3. Wait for all lanes to complete (tails run.jsonl streams; exits non-zero if any lane fails)
31
+ architect research wait
32
+
33
+ # 4. Read each report
34
+ cat build/research/01-official-api/report.md
35
+ cat build/research/02-changelog/report.md
31
36
  ```
32
37
 
33
- Write each research-prompt to a `.prompt.md` file and feed it on stdin, never
34
- as a shell argument — a quote-mangling shell will corrupt a big prompt; the
35
- stdin redirect injects it verbatim.
36
-
37
- - Read-only by toolset: with only `Read,Grep,Glob,WebSearch,WebFetch` on the
38
- allow list and nothing else granted, any write/bash call is denied — the
39
- researcher can't touch the repo. Its report is the redirected stdout.
40
- - Web comes from the built-in `WebSearch` and `WebFetch` tools — no extension
41
- or key. Launch ONE canary researcher and confirm it actually fetches live URLs
42
- before fanning out. Restrict domains with `WebFetch(domain:…)` allow rules in
43
- prompt-injection-sensitive repos.
44
- - Thinking budget: keep research at a modest level (a plain block, or "think
45
- hard") — research is coverage work; deep thinking buys nothing here. Synthesis
46
- happens on the architect's side.
47
- - Scope each researcher to ≤5 subjects and put hard context rules in the
48
- research-prompt (snippet over page; quote ≤2 sentences; stop the moment you
49
- can answer) a researcher that fills its context window dies without emitting
50
- its report. Bisect and re-dispatch dead lanes; don't re-run as-is.
38
+ The id for each lane is derived from the prompt filename:
39
+ `01-official-api.prompt.md` id `01-official-api`
40
+ directory `build/research/01-official-api/`.
41
+
42
+ Verbosity flags for `wait`:
43
+ - default (L1): per-lane lifecycle + terminal outcome line
44
+ - `--level 2`: + assistant text
45
+ - `--level 3`: + tool call names
46
+ - `--level 4`: + tool call inputs and results
47
+ - `--quiet`: suppress all output; exit status alone signals outcome
48
+ - `--thinking`: reveal assistant thinking blocks
49
+ - `--jsonl`: emit raw lane-tagged JSONL instead of human text
50
+
51
+ The researchers are READ-ONLY by toolset (`Read,Grep,Glob,WebSearch,WebFetch`
52
+ with no Edit/Write/Bash) so they cannot touch the repo. Their final report
53
+ is extracted from the terminal `result` event in the stream-json log and
54
+ written to `build/research/<id>/report.md` automatically by `wait`.
51
55
 
52
56
  ## Research-prompt template
53
57
 
@@ -70,9 +74,14 @@ OUTPUT FORMAT — a markdown report:
70
74
  - End with: the 2-3 findings most likely to change an implementation decision.
71
75
  ```
72
76
 
77
+ Keep each researcher scoped to ≤5 subjects and put hard context rules in the
78
+ prompt (snippet over page; quote ≤2 sentences; stop the moment you can answer)
79
+ — a researcher that fills its context window dies without emitting its report.
80
+ Bisect and re-dispatch dead lanes; don't re-run as-is.
81
+
73
82
  ## Gather (architect — this is your work, not another agent's)
74
83
 
75
- 1. Read every findings file in `build/research/`.
84
+ 1. Read every `build/research/<id>/report.md`.
76
85
  2. Identify the **load-bearing claims** — facts the spec will depend on
77
86
  (an API shape, a version constraint, a limit, a deprecation). Adversarially
78
87
  verify each: cross-check against a second independent source or the live