space-architect 1.2.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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +103 -0
  3. data/README.md +248 -146
  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 +107 -121
  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 +109 -0
  23. data/lib/space_architect.rb +5 -19
  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 +16 -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 +40 -18
  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 +329 -0
  97. data/skill/architect/dispatch.md +308 -0
  98. data/skill/architect/research.md +98 -0
  99. data/skill/architect-research/SKILL.md +165 -0
  100. data/skill/architect-research/lanes.md +191 -0
  101. data/skill/architect-vocabulary/SKILL.md +141 -0
  102. metadata +122 -67
  103. data/lib/space_architect/cli/config.rb +0 -61
  104. data/lib/space_architect/cli/current.rb +0 -22
  105. data/lib/space_architect/cli/helpers.rb +0 -117
  106. data/lib/space_architect/cli/init.rb +0 -35
  107. data/lib/space_architect/cli/list.rb +0 -30
  108. data/lib/space_architect/cli/new.rb +0 -43
  109. data/lib/space_architect/cli/options.rb +0 -12
  110. data/lib/space_architect/cli/path.rb +0 -22
  111. data/lib/space_architect/cli/repo.rb +0 -88
  112. data/lib/space_architect/cli/shell.rb +0 -137
  113. data/lib/space_architect/cli/show.rb +0 -27
  114. data/lib/space_architect/cli/status.rb +0 -39
  115. data/lib/space_architect/cli/use.rb +0 -23
  116. data/lib/space_architect/version.rb +0 -5
  117. data/vendor/repo-tender/lib/space_architect/pristine.rb +0 -37
@@ -1,12 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module SpaceArchitect
3
+ module Space::Architect
4
4
  module CLI
5
5
  module Architect
6
- class Init < Dry::CLI::Command
7
- include GlobalOptions
8
- include Helpers
9
-
6
+ class Init < BaseCommand
10
7
  desc "Scaffold architect mission memory in the current space"
11
8
  argument :space, required: false, desc: "Space identifier (default: $PWD)"
12
9
 
@@ -23,10 +20,7 @@ module SpaceArchitect
23
20
  end
24
21
  end
25
22
 
26
- class New < Dry::CLI::Command
27
- include GlobalOptions
28
- include Helpers
29
-
23
+ class New < BaseCommand
30
24
  desc "Scaffold the next iteration file (architecture/I<NN>-<iteration>.md)"
31
25
  argument :iteration, required: true, desc: "Iteration name (kebab-case)"
32
26
  argument :space, required: false, desc: "Space identifier (default: $PWD)"
@@ -44,10 +38,7 @@ module SpaceArchitect
44
38
  end
45
39
  end
46
40
 
47
- class Status < Dry::CLI::Command
48
- include GlobalOptions
49
- include Helpers
50
-
41
+ class Status < BaseCommand
51
42
  desc "Show architect mission state (read-only)"
52
43
  argument :space, required: false, desc: "Space identifier (default: $PWD)"
53
44
 
@@ -92,10 +83,7 @@ module SpaceArchitect
92
83
  end
93
84
  end
94
85
 
95
- class Freeze < Dry::CLI::Command
96
- include GlobalOptions
97
- include Helpers
98
-
86
+ class Freeze < BaseCommand
99
87
  desc "Freeze gates for an iteration"
100
88
  argument :iteration, required: true, desc: "Iteration name"
101
89
  argument :space, required: false, desc: "Space identifier (default: $PWD)"
@@ -119,10 +107,7 @@ module SpaceArchitect
119
107
  end
120
108
  end
121
109
 
122
- class Verify < Dry::CLI::Command
123
- include GlobalOptions
124
- include Helpers
125
-
110
+ class Verify < BaseCommand
126
111
  desc "Post-flight mechanical checks for an iteration (reports only, no judgment)"
127
112
  argument :iteration, required: true, desc: "Iteration name"
128
113
  argument :space, required: false, desc: "Space identifier (default: $PWD)"
@@ -168,43 +153,54 @@ module SpaceArchitect
168
153
  end
169
154
  end
170
155
 
171
- class Dispatch < Dry::CLI::Command
172
- include GlobalOptions
173
- include Helpers
174
-
156
+ class Dispatch < BaseCommand
175
157
  desc "Dispatch a builder for a lane (streams to build/<id>-<lane>/run.jsonl)"
176
158
  argument :iteration, required: true, desc: "Iteration name"
177
159
  argument :lane, required: true, desc: "Lane name"
178
160
  argument :space, required: false, desc: "Space identifier (default: $PWD)"
179
- option :model, default: nil, desc: "Model to use (default: lane entry or claude-sonnet-4-6)"
180
- option :max_turns, default: "200", desc: "Max turns for the builder"
181
- option :harness, default: nil, desc: "Harness override (claude-code, opencode)"
182
- option :effort, default: nil, desc: "Reasoning effort override (opencode only; sets reasoningEffort in the model config)"
161
+ option :model, default: nil, desc: "Model to use (default: lane entry or claude-sonnet-4-6)"
162
+ option :max_turns, default: "200", desc: "Max turns for the builder"
163
+ option :harness, default: nil, desc: "Harness override (claude-code, opencode)"
164
+ option :effort, default: nil, desc: "Reasoning effort override (opencode only; sets reasoningEffort in the model config)"
165
+ option :detach, type: :boolean, default: false, desc: "Detach the builder process (returns immediately with PID; poll report for completion)"
166
+ option :push_url, default: nil, desc: "HTTP endpoint for streaming push (POST body to this URL)"
167
+ option :push_token, default: nil, desc: "Bearer token for push endpoint authorization"
168
+ option :push_host, default: nil, desc: "Base URL of the ingest server; the CLI creates a run via POST <host>/runs and streams to /runs/<id>/ingest (requires --push-token)"
183
169
 
184
170
  def call(iteration:, lane:, space: nil, model: nil,
185
- max_turns: "200", harness: nil, effort: nil, **opts)
171
+ max_turns: "200", harness: nil, effort: nil, detach: false,
172
+ push_url: nil, push_token: nil, push_host: nil, **opts)
186
173
  setup_terminal(**opts.slice(:color, :colors))
187
174
  handle_errors do
188
175
  render(store.find(space)) do |sp|
189
176
  mission = ArchitectMission.new(space: sp)
190
- kwargs = { max_turns: max_turns.to_i }
191
- kwargs[:model] = model if model
192
- kwargs[:harness] = harness if harness
193
- kwargs[:effort] = effort if effort
177
+ kwargs = { max_turns: max_turns.to_i, detach: detach }
178
+ kwargs[:model] = model if model
179
+ kwargs[:harness] = harness if harness
180
+ kwargs[:effort] = effort if effort
181
+ kwargs[:push_url] = push_url if push_url
182
+ kwargs[:push_token] = push_token if push_token
183
+ kwargs[:push_host] = push_host if push_host
194
184
  res = mission.dispatch(iteration, lane, **kwargs)
195
- terminal.say "Run log: #{terminal.path(res[:run_log])}"
196
- terminal.say "Report: #{terminal.path(res[:report])}"
197
- terminal.say "Builder exited with status #{res[:exit_code]}"
198
- CLI.record_outcome(Outcome.new(exit_code: res[:exit_code]))
185
+ if detach
186
+ terminal.say "PID: #{res[:pid]}"
187
+ terminal.say "Run log: #{terminal.path(res[:run_log])}"
188
+ terminal.say "Report: #{terminal.path(res[:report])}"
189
+ terminal.say "Dispatched detached — poll #{terminal.path(res[:report])} for completion"
190
+ CLI.record_outcome(Outcome.new(exit_code: 0))
191
+ else
192
+ terminal.say "Run log: #{terminal.path(res[:run_log])}"
193
+ terminal.say "Report: #{terminal.path(res[:report])}"
194
+ terminal.say "Ingest URL: #{res[:push_url]}" if res[:push_url]
195
+ terminal.say "Builder exited with status #{res[:exit_code]}"
196
+ CLI.record_outcome(Outcome.new(exit_code: res[:exit_code]))
197
+ end
199
198
  end
200
199
  end
201
200
  end
202
201
  end
203
202
 
204
- class Section < Dry::CLI::Command
205
- include GlobalOptions
206
- include Helpers
207
-
203
+ class Section < BaseCommand
208
204
  desc "Write a section of the iteration file and commit it (one call)"
209
205
  argument :iteration, required: true, desc: "Iteration name"
210
206
  argument :section, required: true, desc: "Section: grounds, specification, prompt, verdict"
@@ -239,14 +235,11 @@ module SpaceArchitect
239
235
  return File.read(from) if from
240
236
  return body if body
241
237
  return $stdin.read if stdin
242
- raise SpaceArchitect::Error, "provide the section body via --from <file>, --body <text>, or --stdin"
238
+ raise Space::Core::Error, "provide the section body via --from <file>, --body <text>, or --stdin"
243
239
  end
244
240
  end
245
241
 
246
- class Evidence < Dry::CLI::Command
247
- include GlobalOptions
248
- include Helpers
249
-
242
+ class Evidence < BaseCommand
250
243
  desc "Transcribe a lane's scratch report VERBATIM into Builder Report and commit"
251
244
  argument :iteration, required: true, desc: "Iteration name"
252
245
  argument :space, required: false, desc: "Space identifier (default: $PWD)"
@@ -267,10 +260,7 @@ module SpaceArchitect
267
260
  end
268
261
  end
269
262
 
270
- class Merge < Dry::CLI::Command
271
- include GlobalOptions
272
- include Helpers
273
-
263
+ class Merge < BaseCommand
274
264
  desc "Integrate ONE judged-passing lane (merges --no-ff; runs no gates, makes no verdict)"
275
265
  argument :iteration, required: true, desc: "Iteration name"
276
266
  argument :lane, required: true, desc: "Lane name (architect-judged passing)"
@@ -292,10 +282,7 @@ module SpaceArchitect
292
282
  end
293
283
  end
294
284
 
295
- class Integrate < Dry::CLI::Command
296
- include GlobalOptions
297
- include Helpers
298
-
285
+ class Integrate < BaseCommand
299
286
  desc "Integrate the architect-supplied set of passing lanes, in order (stops on conflict)"
300
287
  argument :iteration, required: true, desc: "Iteration name"
301
288
  argument :space, required: false, desc: "Space identifier (default: $PWD)"
@@ -319,10 +306,7 @@ module SpaceArchitect
319
306
  end
320
307
  end
321
308
 
322
- class Gate < Dry::CLI::Command
323
- include GlobalOptions
324
- include Helpers
325
-
309
+ class Gate < BaseCommand
326
310
  desc "Run the frozen Acceptance Criteria gate commands and stream raw output (no PASS/FAIL)"
327
311
  argument :iteration, required: true, desc: "Iteration name"
328
312
  argument :lane, required: false, desc: "Run in a lane worktree (default: the integration repo)"
@@ -348,34 +332,30 @@ module SpaceArchitect
348
332
  end
349
333
  end
350
334
 
351
- module Brief
352
- class New < Dry::CLI::Command
353
- include GlobalOptions
354
- include Helpers
335
+ class InstallSkills < BaseCommand
336
+ desc "Install bundled skills (architect, architect-research, architect-vocabulary) for a harness"
337
+ option :provider, default: "claude", desc: "Harness: claude, codex, opencode, pi"
338
+ option :project, type: :boolean, default: false, desc: "Install to CWD instead of global"
339
+ option :force, type: :boolean, default: false, desc: "Overwrite existing skills that differ"
340
+ option :dry_run, type: :boolean, default: false, desc: "Print what would happen without writing files"
355
341
 
356
- desc "Scaffold the durable mission brief (architecture/BRIEF.md)"
357
- argument :space, required: false, desc: "Space identifier (default: $PWD)"
358
- option :force, type: :boolean, default: false, desc: "Overwrite an existing BRIEF.md"
359
-
360
- def call(space: nil, force: false, **opts)
361
- setup_terminal(**opts.slice(:color, :colors))
362
- handle_errors do
363
- render(store.find(space)) do |sp|
364
- mission = ArchitectMission.new(space: sp)
365
- path = mission.brief_new!(force: force)
366
- terminal.say "Brief ready: #{terminal.path(path)}"
367
- CLI.record_outcome(Outcome.new(exit_code: 0))
368
- end
342
+ def call(provider: "claude", project: false, force: false, dry_run: false, **opts)
343
+ setup_terminal(**opts.slice(:color, :colors))
344
+ handle_errors do
345
+ result = SkillInstaller.install(provider, project: project, force: force,
346
+ env: project_config.env, dry_run: dry_run)
347
+ verb = dry_run ? "Would install" : "Installed"
348
+ terminal.say "#{verb} skills for #{provider} → #{terminal.path(result[:dest_root])}"
349
+ result[:skills].each do |s|
350
+ terminal.say " #{s[:name]}: #{terminal.style_skill_action(s[:action])} (#{terminal.path(s[:path])})"
369
351
  end
352
+ CLI.record_outcome(Outcome.new(exit_code: 0))
370
353
  end
371
354
  end
372
355
  end
373
356
 
374
357
  module Worktree
375
- class Add < Dry::CLI::Command
376
- include GlobalOptions
377
- include Helpers
378
-
358
+ class Add < BaseCommand
379
359
  desc "Create a worktree for a lane"
380
360
  argument :repo, required: true, desc: "Repo name (under repos/)"
381
361
  argument :iteration, required: true, desc: "Iteration name"
@@ -402,10 +382,7 @@ module SpaceArchitect
402
382
  end
403
383
  end
404
384
 
405
- class Remove < Dry::CLI::Command
406
- include GlobalOptions
407
- include Helpers
408
-
385
+ class Remove < BaseCommand
409
386
  desc "Remove a lane worktree"
410
387
  argument :iteration, required: true, desc: "Iteration name"
411
388
  argument :lane, required: true, desc: "Lane name"
@@ -423,10 +400,7 @@ module SpaceArchitect
423
400
  end
424
401
  end
425
402
 
426
- class List < Dry::CLI::Command
427
- include GlobalOptions
428
- include Helpers
429
-
403
+ class List < BaseCommand
430
404
  desc "List active architect worktrees"
431
405
 
432
406
  def call(**opts)
@@ -448,10 +422,7 @@ module SpaceArchitect
448
422
  end
449
423
 
450
424
  module Variant
451
- class Add < Dry::CLI::Command
452
- include GlobalOptions
453
- include Helpers
454
-
425
+ class Add < BaseCommand
455
426
  desc "Create a variant set (competing lanes over one frozen spec)"
456
427
  argument :repo, required: true, desc: "Repo name (under repos/)"
457
428
  argument :iteration, required: true, desc: "Iteration name"
@@ -481,10 +452,7 @@ module SpaceArchitect
481
452
  end
482
453
  end
483
454
 
484
- class Promote < Dry::CLI::Command
485
- include GlobalOptions
486
- include Helpers
487
-
455
+ class Promote < BaseCommand
488
456
  desc "Promote one variant of a variant set as the winner"
489
457
  argument :iteration, required: true, desc: "Iteration name"
490
458
  argument :winner, required: true, desc: "Variant lane name to promote (e.g. v02)"
@@ -507,10 +475,7 @@ module SpaceArchitect
507
475
  end
508
476
  end
509
477
 
510
- class Compare < Dry::CLI::Command
511
- include GlobalOptions
512
- include Helpers
513
-
478
+ class Compare < BaseCommand
514
479
  desc "Compare variants of an iteration's variant set (read-only)"
515
480
  argument :iteration, required: true, desc: "Iteration name"
516
481
  argument :space, required: false, desc: "Space identifier (default: $PWD)"
@@ -544,31 +509,52 @@ module SpaceArchitect
544
509
  end
545
510
  end
546
511
  end
512
+
513
+ module Brief
514
+ class New < BaseCommand
515
+ desc "Scaffold the durable mission brief (architecture/BRIEF.md)"
516
+ argument :space, required: false, desc: "Space identifier (default: $PWD)"
517
+ option :force, type: :boolean, default: false, desc: "Overwrite an existing BRIEF.md"
518
+
519
+ def call(space: nil, force: false, **opts)
520
+ setup_terminal(**opts.slice(:color, :colors))
521
+ handle_errors do
522
+ render(store.find(space)) do |sp|
523
+ mission = ArchitectMission.new(space: sp)
524
+ path = mission.brief_new!(force: force)
525
+ terminal.say "Brief ready: #{terminal.path(path)}"
526
+ CLI.record_outcome(Outcome.new(exit_code: 0))
527
+ end
528
+ end
529
+ end
530
+ end
531
+ end
547
532
  end
548
533
  end
549
534
  end
550
535
 
551
- SpaceArchitect::CLI::Registry.register "init", SpaceArchitect::CLI::Architect::Init
552
- SpaceArchitect::CLI::Registry.register "new", SpaceArchitect::CLI::Architect::New
553
- SpaceArchitect::CLI::Registry.register "status", SpaceArchitect::CLI::Architect::Status
554
- SpaceArchitect::CLI::Registry.register "freeze", SpaceArchitect::CLI::Architect::Freeze
555
- SpaceArchitect::CLI::Registry.register "verify", SpaceArchitect::CLI::Architect::Verify
556
- SpaceArchitect::CLI::Registry.register "dispatch", SpaceArchitect::CLI::Architect::Dispatch
557
- SpaceArchitect::CLI::Registry.register "section", SpaceArchitect::CLI::Architect::Section
558
- SpaceArchitect::CLI::Registry.register "evidence", SpaceArchitect::CLI::Architect::Evidence
559
- SpaceArchitect::CLI::Registry.register "merge", SpaceArchitect::CLI::Architect::Merge
560
- SpaceArchitect::CLI::Registry.register "integrate", SpaceArchitect::CLI::Architect::Integrate
561
- SpaceArchitect::CLI::Registry.register "gate", SpaceArchitect::CLI::Architect::Gate
562
- SpaceArchitect::CLI::Registry.register "brief" do |b|
563
- b.register "new", SpaceArchitect::CLI::Architect::Brief::New
536
+ Space::Architect::CLI::Registry.register "init", Space::Architect::CLI::Architect::Init
537
+ Space::Architect::CLI::Registry.register "new", Space::Architect::CLI::Architect::New
538
+ Space::Architect::CLI::Registry.register "status", Space::Architect::CLI::Architect::Status
539
+ Space::Architect::CLI::Registry.register "freeze", Space::Architect::CLI::Architect::Freeze
540
+ Space::Architect::CLI::Registry.register "verify", Space::Architect::CLI::Architect::Verify
541
+ Space::Architect::CLI::Registry.register "dispatch", Space::Architect::CLI::Architect::Dispatch
542
+ Space::Architect::CLI::Registry.register "section", Space::Architect::CLI::Architect::Section
543
+ Space::Architect::CLI::Registry.register "evidence", Space::Architect::CLI::Architect::Evidence
544
+ Space::Architect::CLI::Registry.register "merge", Space::Architect::CLI::Architect::Merge
545
+ Space::Architect::CLI::Registry.register "integrate", Space::Architect::CLI::Architect::Integrate
546
+ Space::Architect::CLI::Registry.register "gate", Space::Architect::CLI::Architect::Gate
547
+ Space::Architect::CLI::Registry.register "install-skills", Space::Architect::CLI::Architect::InstallSkills
548
+ Space::Architect::CLI::Registry.register "brief" do |b|
549
+ b.register "new", Space::Architect::CLI::Architect::Brief::New
564
550
  end
565
- SpaceArchitect::CLI::Registry.register "worktree" do |wt|
566
- wt.register "add", SpaceArchitect::CLI::Architect::Worktree::Add
567
- wt.register "remove", SpaceArchitect::CLI::Architect::Worktree::Remove
568
- wt.register "list", SpaceArchitect::CLI::Architect::Worktree::List
551
+ Space::Architect::CLI::Registry.register "worktree" do |wt|
552
+ wt.register "add", Space::Architect::CLI::Architect::Worktree::Add
553
+ wt.register "remove", Space::Architect::CLI::Architect::Worktree::Remove
554
+ wt.register "list", Space::Architect::CLI::Architect::Worktree::List
569
555
  end
570
- SpaceArchitect::CLI::Registry.register "variant" do |v|
571
- v.register "add", SpaceArchitect::CLI::Architect::Variant::Add
572
- v.register "promote", SpaceArchitect::CLI::Architect::Variant::Promote
573
- v.register "compare", SpaceArchitect::CLI::Architect::Variant::Compare
556
+ Space::Architect::CLI::Registry.register "variant" do |v|
557
+ v.register "add", Space::Architect::CLI::Architect::Variant::Add
558
+ v.register "promote", Space::Architect::CLI::Architect::Variant::Promote
559
+ v.register "compare", Space::Architect::CLI::Architect::Variant::Compare
574
560
  end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Space::Architect
4
+ module CLI
5
+ module Architect
6
+ module Research
7
+ class Dispatch < BaseCommand
8
+ desc "Dispatch detached read-only research lanes (one per prompt file)"
9
+ argument :prompts, required: true,
10
+ desc: "Prompt file(s) to dispatch (space-separated paths)"
11
+ option :model, default: nil, desc: "Model override (default: claude-sonnet-4-6)"
12
+ option :max_turns, default: "40", desc: "Max turns per researcher"
13
+
14
+ def call(prompts:, model: nil, max_turns: "40", **opts)
15
+ setup_terminal(**opts.slice(:color, :colors))
16
+ handle_errors do
17
+ render(store.find(opts[:space])) do |sp|
18
+ paths = Array(prompts)
19
+ supervisor = Space::Architect::Research::Supervisor.new(space: sp)
20
+ kwargs = { max_turns: max_turns.to_i }
21
+ kwargs[:model] = model if model
22
+ runs = supervisor.dispatch(paths, **kwargs)
23
+ runs.each do |run|
24
+ terminal.say "dispatched #{run.id} (pid #{run.pid}) → #{terminal.path(run.run_log_path)}"
25
+ end
26
+ CLI.record_outcome(Outcome.new(exit_code: 0))
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ class Status < BaseCommand
33
+ desc "Show status of dispatched research runs"
34
+ argument :space, required: false, desc: "Space identifier (default: $PWD)"
35
+
36
+ def call(space: nil, **opts)
37
+ setup_terminal(**opts.slice(:color, :colors))
38
+ handle_errors do
39
+ render(store.find(space)) do |sp|
40
+ supervisor = Space::Architect::Research::Supervisor.new(space: sp)
41
+ entries = supervisor.status
42
+ if entries.empty?
43
+ terminal.say "No research runs registered"
44
+ else
45
+ rows = entries.map do |e|
46
+ run = e[:run]
47
+ [run.id, run.pid.to_s, e[:state].to_s, run.model, e[:tail].last.to_s.slice(0, 60)]
48
+ end
49
+ terminal.say terminal.table(%w[ID PID State Model LastLine], rows)
50
+ end
51
+ CLI.record_outcome(Outcome.new(exit_code: 0))
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ class Wait < BaseCommand
58
+ desc "Wait for all dispatched research runs to complete"
59
+ argument :space, required: false, desc: "Space identifier (default: $PWD)"
60
+ option :quiet, type: :boolean, default: false,
61
+ desc: "L0: suppress all output; exit status only"
62
+ option :level, type: :integer, default: 1,
63
+ desc: "Verbosity level: 1=lifecycle 2=+text 3=+tools 4=+io"
64
+ option :thinking, type: :boolean, default: false,
65
+ desc: "Show assistant thinking blocks"
66
+ option :jsonl, type: :boolean, default: false,
67
+ desc: "Emit raw lane-tagged JSONL (mutually exclusive with level/quiet)"
68
+
69
+ def call(space: nil, quiet: false, level: 1, thinking: false, jsonl: false, **opts)
70
+ setup_terminal(**opts.slice(:color, :colors))
71
+ handle_errors do
72
+ render(store.find(space)) do |sp|
73
+ supervisor = Space::Architect::Research::Supervisor.new(space: sp)
74
+ result = supervisor.wait(
75
+ quiet: quiet,
76
+ level: level.to_i,
77
+ thinking: thinking,
78
+ jsonl: jsonl
79
+ )
80
+ CLI.record_outcome(Outcome.new(exit_code: result == :ok ? 0 : 1))
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ Space::Architect::CLI::Registry.register "research" do |r|
91
+ r.register "dispatch", Space::Architect::CLI::Architect::Research::Dispatch
92
+ r.register "status", Space::Architect::CLI::Architect::Research::Status
93
+ r.register "wait", Space::Architect::CLI::Architect::Research::Wait
94
+ end
@@ -1,35 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- SpaceArchitect::CLI::Registry.register "space" do |prefix|
4
- prefix.register "init", SpaceArchitect::CLI::Init
5
- prefix.register "new", SpaceArchitect::CLI::New
6
- prefix.register "list", SpaceArchitect::CLI::List
7
- prefix.register "ls", SpaceArchitect::CLI::List
8
- prefix.register "show", SpaceArchitect::CLI::Show
9
- prefix.register "path", SpaceArchitect::CLI::Path
10
- prefix.register "use", SpaceArchitect::CLI::Use
11
- prefix.register "current", SpaceArchitect::CLI::Current
12
- prefix.register "status", SpaceArchitect::CLI::Status
13
- prefix.register "config" do |c|
14
- c.register "show", SpaceArchitect::CLI::Config::Show
15
- c.register "path", SpaceArchitect::CLI::Config::ConfigPath
16
- c.register "set", SpaceArchitect::CLI::Config::Set
17
- end
18
- prefix.register "repo" do |r|
19
- r.register "add", SpaceArchitect::CLI::Repo::Add
20
- r.register "list", SpaceArchitect::CLI::Repo::RepoList
21
- r.register "ls", SpaceArchitect::CLI::Repo::RepoList
22
- r.register "resolve", SpaceArchitect::CLI::Repo::Resolve
23
- end
24
- prefix.register "repos" do |r|
25
- r.register "add", SpaceArchitect::CLI::Repo::Add
26
- r.register "list", SpaceArchitect::CLI::Repo::RepoList
27
- r.register "ls", SpaceArchitect::CLI::Repo::RepoList
28
- r.register "resolve", SpaceArchitect::CLI::Repo::Resolve
29
- end
30
- prefix.register "shell" do |s|
31
- s.register "init", SpaceArchitect::CLI::Shell::ShellInit
32
- s.register "fish", SpaceArchitect::CLI::Shell::Fish
33
- s.register "complete", SpaceArchitect::CLI::Shell::Complete
3
+ require "space_core/cli"
4
+
5
+ module Space::Architect
6
+ module CLI
7
+ # Exit-code bridge to the Space::Core CLI engine.
8
+ # `architect space <args>` (and `exe/space <args>` via Space::Core::CLI directly)
9
+ # hands the raw remainder to Space::Core's own dry-cli registry and translates
10
+ # Space::Core's recorded Outcome into the host exit code.
11
+ # Space::Core has its own Registry, Outcome, and :space_core_cli_outcome thread-local;
12
+ # this is the seam between the two registries (NOT a re-registration).
13
+ def self.dispatch_space(rest, out = $stdout, err = $stderr)
14
+ if ::Space::Core::CLI::TOP_LEVEL_HELP.include?(rest)
15
+ out.puts Dry::CLI::Usage.call(::Space::Core::CLI::Registry.get([]))
16
+ return 0
17
+ end
18
+
19
+ if ::Space::Core::CLI::VERSION_REQUEST.include?(rest)
20
+ out.puts ::Space::Core::VERSION
21
+ return 0
22
+ end
23
+
24
+ Thread.current[:space_core_cli_outcome] = nil
25
+ Dry::CLI.new(::Space::Core::CLI::Registry).call(arguments: rest, out: out, err: err)
26
+ ::Space::Core::CLI.last_outcome&.exit_code || 0
27
+ end
34
28
  end
35
29
  end
@@ -1,32 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "space_architect/pristine/cli"
3
+ require "space_src/cli"
4
4
 
5
- module SpaceArchitect
5
+ module Space::Architect
6
6
  module CLI
7
- # Exit-code bridge to the vendored Pristine (repo-tender) CLI engine.
8
- # `architect src <args>` hands the raw remainder to Pristine's own dry-cli
9
- # registry and translates Pristine's recorded Outcome into the host exit code.
10
- # Pristine has its own Registry, Outcome, and :repo_tender_cli_* thread-locals;
11
- # this is the seam between the two registries (NOT a re-registration). Pristine's
7
+ # Exit-code bridge to the Space::Src CLI engine.
8
+ # `architect src <args>` hands the raw remainder to Space::Src's own dry-cli
9
+ # registry and translates Space::Src's recorded Outcome into the host exit code.
10
+ # Space::Src has its own Registry, Outcome, and :space_src_cli_* thread-locals;
11
+ # this is the seam between the two registries (NOT a re-registration). Space::Src's
12
12
  # top-level help/version interceptors call Kernel.exit, so we reproduce that
13
13
  # interception here against the injected IO instead of delegating to them.
14
14
  # dry-cli's internal exit on a bare group / unknown command propagates as
15
15
  # SystemExit — same as the host's own bare groups (e.g. `space repo`) — and is
16
16
  # intentionally NOT rescued (accepted behavior change).
17
17
  def self.dispatch_src(rest, out = $stdout, err = $stderr)
18
- if Pristine::CLI::TOP_LEVEL_HELP.include?(rest)
19
- out.puts Dry::CLI::Usage.call(Pristine::CLI::Registry.get([]))
18
+ if ::Space::Src::CLI::TOP_LEVEL_HELP.include?(rest)
19
+ out.puts Dry::CLI::Usage.call(::Space::Src::CLI::Registry.get([]))
20
20
  return 0
21
21
  end
22
- if Pristine::CLI::VERSION_REQUEST.include?(rest)
23
- out.puts SpaceArchitect::Pristine::VERSION
22
+ if ::Space::Src::CLI::VERSION_REQUEST.include?(rest)
23
+ out.puts ::Space::Src::VERSION
24
24
  return 0
25
25
  end
26
26
 
27
- Thread.current[:repo_tender_cli_outcome] = nil
28
- Dry::CLI.new(Pristine::CLI::Registry).call(arguments: rest, out: out, err: err)
29
- Pristine::CLI.last_outcome&.exit_code || 0
27
+ if ::Space::Src::CLI.bare_query?(rest)
28
+ paths = ::Space::Src::CLI.make_paths
29
+ config = ::Space::Src::Config::Store.load(paths.config_file).success
30
+ return ::Space::Src::Nav.dispatch(rest[0], out, err, config.base_dir)
31
+ end
32
+
33
+ Thread.current[:space_src_cli_outcome] = nil
34
+ Dry::CLI.new(::Space::Src::CLI::Registry).call(arguments: rest, out: out, err: err)
35
+ ::Space::Src::CLI.last_outcome&.exit_code || 0
30
36
  end
31
37
  end
32
38
  end