space-architect 9.4.0 → 9.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/lib/space_architect/agent_installer.rb +127 -0
- data/lib/space_architect/cli/architect.rb +60 -2
- data/lib/space_architect/looplog.rb +27 -7
- data/lib/space_architect/pi/builder-guard.ts +13 -1
- data/lib/space_architect/version.rb +1 -1
- data/lib/space_architect.rb +1 -0
- data/skill/architect-discuss/SKILL.md +8 -6
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4de640b748008dc4484903c88d496366528cb9eb294002abad5bc02547816198
|
|
4
|
+
data.tar.gz: 4aa87deb60eee6dc8d768d6f333204137d7ddb49a3e94640263fce5711623e3e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5e383f34c36693c6ad9290ec9db84815835545676c194d4c06d79d630c6bc4f98ba6b1c7072570c60b4ce3df2b782d0a6e064b0d1ed5d31fdd9baacc6dbe0342
|
|
7
|
+
data.tar.gz: b957f8f1a303a9a2c88ad0c569a8ce8b572740dfffb1da6aa203b0e66fb15f1984d711479549c6d1a35ae7ece15a502f3f48994f5614bf09a06346b3392b129a
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [9.5.0] - 2026-10-17
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`architect install`** — the one-shot machine install: skills
|
|
13
|
+
(`--provider`, default pi), the architect-loop extension, then the role
|
|
14
|
+
playbooks, in that order (install-agents renders from the extension the
|
|
15
|
+
prior step installs). Each step aborts loudly; no partial silent states.
|
|
16
|
+
- **`architect install-agents`** — the role playbooks to
|
|
17
|
+
`~/.pi/agent/agents/`, rendered by the installed architect-loop extension
|
|
18
|
+
(the single owner of the playbook text) with global-routing pins;
|
|
19
|
+
write-if-changed, `--force`, `--dry-run` like install-skills. Loud
|
|
20
|
+
failures name the prerequisite (`install-extensions`) or the cure
|
|
21
|
+
(`routing init --global`).
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **install-skills targets pi by default** (was claude; `--provider` still
|
|
26
|
+
selects claude/codex/opencode).
|
|
27
|
+
- **looplog tags**: the narrator abbreviates to `narr` (it previously
|
|
28
|
+
truncated to "architect-"), and lane-worktree tags carry the iteration
|
|
29
|
+
ordinal plus the lane, resolved against the space's iteration ids —
|
|
30
|
+
`[bld:67-core]` instead of the old clipped
|
|
31
|
+
`[bld:rig-oracle-repin-core]`.
|
|
32
|
+
|
|
8
33
|
## [9.4.0] - 2026-10-17
|
|
9
34
|
|
|
10
35
|
### Added
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
|
|
5
|
+
module Space
|
|
6
|
+
module Architect
|
|
7
|
+
# Installs the role playbooks (architect-coordinator/spec/builder/judge/
|
|
8
|
+
# narrator) to ~/.pi/agent/agents. The playbook text is rendered by the
|
|
9
|
+
# installed architect-loop extension itself — the extension owns that text
|
|
10
|
+
# (it renders the per-space .pi/agents files from the same source), so this
|
|
11
|
+
# installer carries no second copy to drift. Routing pins come from the
|
|
12
|
+
# global architect.yaml (ENV["SPACE_ARCHITECT_GLOBAL_CONFIG"] overrides the
|
|
13
|
+
# path, honored inside the extension): the user-level files are the
|
|
14
|
+
# machine-wide fallback for sessions that cannot see a space's .pi dir;
|
|
15
|
+
# a space's own .pi/agents files always win inside that space.
|
|
16
|
+
module AgentInstaller
|
|
17
|
+
ROLES = %w[coordinator spec builder judge narrator].freeze
|
|
18
|
+
|
|
19
|
+
# Renders each role's agent file and prints it between ===FILE===<role>
|
|
20
|
+
# markers. Dynamic import (node -e runs CJS; import() is legal there) so
|
|
21
|
+
# the .ts source — type-stripped by node — loads with its own package's
|
|
22
|
+
# node_modules. The scratch startDir forces global-only routing.
|
|
23
|
+
RENDER_SCRIPT = <<~JS
|
|
24
|
+
const { pathToFileURL } = require("node:url");
|
|
25
|
+
const fs = require("node:fs"), os = require("node:os"), path = require("node:path");
|
|
26
|
+
import(pathToFileURL(process.argv[1]).href).then(({ renderAgentFile, resolveConfig }) => {
|
|
27
|
+
const config = resolveConfig(fs.mkdtempSync(path.join(os.tmpdir(), "architect-agents-")), {});
|
|
28
|
+
if (!config) {
|
|
29
|
+
console.error("no architect.yaml found — run `architect routing init --global` to scaffold the machine-wide config first");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
for (const role of ["coordinator", "spec", "builder", "judge", "narrator"]) {
|
|
33
|
+
console.log("===FILE===" + role);
|
|
34
|
+
console.log(renderAgentFile(role, config));
|
|
35
|
+
}
|
|
36
|
+
}).catch((error) => {
|
|
37
|
+
console.error(error && error.message ? error.message : String(error));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
|
40
|
+
JS
|
|
41
|
+
|
|
42
|
+
class << self
|
|
43
|
+
def install(env: ENV, force: false, dry_run: false, dest: nil, extension_dir: nil)
|
|
44
|
+
source = extension_dir ? Pathname.new(extension_dir).join("architect-loop.ts") : locate_extension(env)
|
|
45
|
+
unless source&.exist?
|
|
46
|
+
raise Space::Core::Error,
|
|
47
|
+
"architect-loop.ts not found under #{search_root(env)} — run `architect install-extensions` first " \
|
|
48
|
+
"(install-agents renders its playbooks from the installed extension)"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
dest ||= default_dest(env)
|
|
52
|
+
rendered = render_via_node(source, env)
|
|
53
|
+
results = ROLES.map do |role|
|
|
54
|
+
write_agent(dest.join("architect-#{role}.md"), rendered.fetch(role), force: force, dry_run: dry_run)
|
|
55
|
+
end
|
|
56
|
+
{ dest_root: dest, agents: results, dry_run: dry_run }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def default_dest(env)
|
|
62
|
+
Pathname.new(env.fetch("PI_AGENTS_DIR", File.join(pi_agent_dir(env), "agents")))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Same convention as SkillInstaller: PI_CODING_AGENT_DIR or ~/.pi/agent.
|
|
66
|
+
def pi_agent_dir(env)
|
|
67
|
+
Pathname.new(env.fetch("PI_CODING_AGENT_DIR", File.join(Space::Core::XDG.home(env: env), ".pi", "agent")))
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def search_root(env)
|
|
71
|
+
File.join(pi_agent_dir(env), "git", "**")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The installed extension package: pi clones it under
|
|
75
|
+
# <agent dir>/git/<host>/<owner>/pi-extensions/. First match wins;
|
|
76
|
+
# --extension-dir (tests) bypasses discovery.
|
|
77
|
+
def locate_extension(env)
|
|
78
|
+
Dir.glob(File.join(search_root(env), "pi-extensions", "architect-loop.ts")).first # paths:exempt — enumerates pi's own managed install layout (git/<host>/<owner>/pi-extensions), matching exactly the declared package, never user project files
|
|
79
|
+
&.then { |p| Pathname.new(p) }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def render_via_node(source, env)
|
|
83
|
+
out, err, status = Open3.capture3(env, "node", "-e", RENDER_SCRIPT, source.to_s)
|
|
84
|
+
unless status.success?
|
|
85
|
+
raise Space::Core::Error, "rendering playbooks via node failed (exit #{status.exitstatus}):\n#{out + err}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
rendered = {}
|
|
89
|
+
marker = nil
|
|
90
|
+
out.each_line do |line|
|
|
91
|
+
if (m = line.match(/\A===FILE===(\w+)\n?\z/))
|
|
92
|
+
marker = m[1]
|
|
93
|
+
rendered[marker] = +""
|
|
94
|
+
elsif marker
|
|
95
|
+
rendered[marker] << line
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
missing = ROLES - rendered.keys
|
|
99
|
+
raise Space::Core::Error, "rendered output missing roles: #{missing.join(', ')}" if missing.any?
|
|
100
|
+
rendered
|
|
101
|
+
rescue Errno::ENOENT
|
|
102
|
+
raise Space::Core::Error,
|
|
103
|
+
"node: command not found — install-agents renders playbooks through the installed " \
|
|
104
|
+
"architect-loop extension, which needs node on PATH (it ships with pi)."
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def write_agent(dest, content, force:, dry_run:)
|
|
108
|
+
name = dest.basename.to_s
|
|
109
|
+
if dest.exist?
|
|
110
|
+
return { name: name, action: :unchanged, path: dest } if File.read(dest) == content
|
|
111
|
+
return { name: name, action: :conflict, path: dest } if dry_run
|
|
112
|
+
|
|
113
|
+
raise Space::Core::Error,
|
|
114
|
+
"Refusing to overwrite existing playbook at #{dest}. Re-run with --force." unless force
|
|
115
|
+
|
|
116
|
+
File.write(dest, content)
|
|
117
|
+
{ name: name, action: :updated, path: dest }
|
|
118
|
+
else
|
|
119
|
+
FileUtils.mkdir_p(dest.parent)
|
|
120
|
+
File.write(dest, content) unless dry_run
|
|
121
|
+
{ name: name, action: dry_run ? :would_install : :installed, path: dest }
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -6,6 +6,7 @@ require_relative "../loop_cost"
|
|
|
6
6
|
require_relative "../runlog"
|
|
7
7
|
require_relative "../looplog"
|
|
8
8
|
require_relative "../extension_installer"
|
|
9
|
+
require_relative "../agent_installer"
|
|
9
10
|
|
|
10
11
|
module Space::Architect
|
|
11
12
|
module CLI
|
|
@@ -1064,12 +1065,12 @@ module Space::Architect
|
|
|
1064
1065
|
class InstallSkills < BaseCommand
|
|
1065
1066
|
desc "Install bundled skills (architect, architect-discuss, architect-action, architect-status, architect-research) for a harness"
|
|
1066
1067
|
phase 53, "Project"
|
|
1067
|
-
option :provider, default: "
|
|
1068
|
+
option :provider, default: "pi", desc: "Skill install target (default: pi; validated by the installer)"
|
|
1068
1069
|
option :project, type: :boolean, default: false, desc: "Install to CWD instead of global"
|
|
1069
1070
|
option :force, type: :boolean, default: false, desc: "Overwrite existing skills that differ"
|
|
1070
1071
|
option :dry_run, type: :boolean, default: false, desc: "Print what would happen without writing files"
|
|
1071
1072
|
|
|
1072
|
-
def call(provider: "
|
|
1073
|
+
def call(provider: "pi", project: false, force: false, dry_run: false, **opts)
|
|
1073
1074
|
setup_terminal(**opts.slice(:color, :colors))
|
|
1074
1075
|
handle_errors do
|
|
1075
1076
|
result = SkillInstaller.install(provider, project: project, force: force,
|
|
@@ -1102,6 +1103,61 @@ module Space::Architect
|
|
|
1102
1103
|
end
|
|
1103
1104
|
end
|
|
1104
1105
|
|
|
1106
|
+
# Role playbooks to ~/.pi/agent/agents, rendered from the installed
|
|
1107
|
+
# architect-loop extension (the single owner of the playbook text) with
|
|
1108
|
+
# global-routing pins — the machine-wide fallback for sessions that
|
|
1109
|
+
# cannot see a space's .pi dir. Needs install-extensions to have run.
|
|
1110
|
+
class InstallAgents < BaseCommand
|
|
1111
|
+
desc "Install the role playbooks (architect-coordinator/spec/builder/judge/narrator) to ~/.pi/agent/agents, rendered from the installed architect-loop extension"
|
|
1112
|
+
phase 53, "Project"
|
|
1113
|
+
option :force, type: :boolean, default: false, desc: "Overwrite existing playbooks that differ"
|
|
1114
|
+
option :dry_run, type: :boolean, default: false, desc: "Print what would happen without writing files"
|
|
1115
|
+
|
|
1116
|
+
def call(force: false, dry_run: false, **opts)
|
|
1117
|
+
setup_terminal(**opts.slice(:color, :colors))
|
|
1118
|
+
handle_errors do
|
|
1119
|
+
result = AgentInstaller.install(env: project_config.env, force: force, dry_run: dry_run)
|
|
1120
|
+
verb = dry_run ? "Would install" : "Installed"
|
|
1121
|
+
terminal.say "#{verb} role playbooks for pi → #{terminal.path(result[:dest_root])}"
|
|
1122
|
+
result[:agents].each do |a|
|
|
1123
|
+
terminal.say " #{a[:name]}: #{terminal.style_skill_action(a[:action])} (#{terminal.path(a[:path])})"
|
|
1124
|
+
end
|
|
1125
|
+
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
1126
|
+
end
|
|
1127
|
+
end
|
|
1128
|
+
end
|
|
1129
|
+
|
|
1130
|
+
# The one-shot machine install: skills (pi by default), the
|
|
1131
|
+
# architect-loop extension, then the role playbooks — in that order,
|
|
1132
|
+
# because install-agents renders from the extension the prior step just
|
|
1133
|
+
# installed. Each step aborts loudly on failure; there are no partial
|
|
1134
|
+
# silent states to reason about afterwards.
|
|
1135
|
+
class Install < BaseCommand
|
|
1136
|
+
desc "Install everything for this machine: skills (--provider, default pi), the architect-loop extension, then the role playbooks"
|
|
1137
|
+
phase 53, "Project"
|
|
1138
|
+
option :provider, default: "pi", desc: "Skill install target (default: pi; validated by the installer; passed through to install-skills)"
|
|
1139
|
+
option :force, type: :boolean, default: false, desc: "Overwrite existing skills/playbooks that differ (passed through)"
|
|
1140
|
+
|
|
1141
|
+
def call(provider: "pi", force: false, **opts)
|
|
1142
|
+
setup_terminal(**opts.slice(:color, :colors))
|
|
1143
|
+
handle_errors do
|
|
1144
|
+
skills = SkillInstaller.install(provider, project: false, force: force,
|
|
1145
|
+
env: project_config.env)
|
|
1146
|
+
terminal.say "skills → #{terminal.path(skills[:dest_root])}"
|
|
1147
|
+
skills[:skills].each { |s| terminal.say " #{s[:name]}: #{terminal.style_skill_action(s[:action])}" }
|
|
1148
|
+
|
|
1149
|
+
extensions = ExtensionInstaller.install(env: project_config.env)
|
|
1150
|
+
extensions[:steps].each { |s| terminal.say " #{s[:cmd]} (exit #{s[:exit_code]})" }
|
|
1151
|
+
|
|
1152
|
+
agents = AgentInstaller.install(env: project_config.env, force: force)
|
|
1153
|
+
terminal.say "role playbooks → #{terminal.path(agents[:dest_root])}"
|
|
1154
|
+
agents[:agents].each { |a| terminal.say " #{a[:name]}: #{terminal.style_skill_action(a[:action])}" }
|
|
1155
|
+
|
|
1156
|
+
CLI.record_outcome(Outcome.new(exit_code: 0))
|
|
1157
|
+
end
|
|
1158
|
+
end
|
|
1159
|
+
end
|
|
1160
|
+
|
|
1105
1161
|
module Routing
|
|
1106
1162
|
# I09: `architect routing init` — the self-service cure for the
|
|
1107
1163
|
# missing-config wall (default_model_inform's hint names this command).
|
|
@@ -1378,8 +1434,10 @@ Space::Architect::CLI::Registry.register "evidence", Space::Architect::CLI::Arc
|
|
|
1378
1434
|
Space::Architect::CLI::Registry.register "merge", Space::Architect::CLI::Architect::Merge
|
|
1379
1435
|
Space::Architect::CLI::Registry.register "integrate", Space::Architect::CLI::Architect::Integrate
|
|
1380
1436
|
Space::Architect::CLI::Registry.register "gate", Space::Architect::CLI::Architect::Gate
|
|
1437
|
+
Space::Architect::CLI::Registry.register "install", Space::Architect::CLI::Architect::Install
|
|
1381
1438
|
Space::Architect::CLI::Registry.register "install-skills", Space::Architect::CLI::Architect::InstallSkills
|
|
1382
1439
|
Space::Architect::CLI::Registry.register "install-extensions", Space::Architect::CLI::Architect::InstallExtensions
|
|
1440
|
+
Space::Architect::CLI::Registry.register "install-agents", Space::Architect::CLI::Architect::InstallAgents
|
|
1383
1441
|
Space::Architect::CLI::Registry.register "runlog", Space::Architect::CLI::Architect::Runlog
|
|
1384
1442
|
Space::Architect::CLI::Registry.register "looplog", Space::Architect::CLI::Architect::Looplog
|
|
1385
1443
|
Space::Architect::CLI::Registry.register "bug-report", Space::Architect::CLI::Architect::BugReport
|
|
@@ -56,7 +56,8 @@ module Space
|
|
|
56
56
|
"architect-coordinator" => "coord",
|
|
57
57
|
"architect-spec" => "spec",
|
|
58
58
|
"architect-judge" => "judge",
|
|
59
|
-
"architect-builder" => "bld"
|
|
59
|
+
"architect-builder" => "bld",
|
|
60
|
+
"architect-narrator" => "narr"
|
|
60
61
|
}.freeze
|
|
61
62
|
|
|
62
63
|
TAG_COLORS = %w[36 35 33 32 34 95 96 91].freeze
|
|
@@ -166,13 +167,23 @@ module Space
|
|
|
166
167
|
[cwd, task]
|
|
167
168
|
end
|
|
168
169
|
|
|
169
|
-
# [bld:
|
|
170
|
-
|
|
170
|
+
# [bld:67-core] when cwd is a lane worktree — the iteration ordinal
|
|
171
|
+
# plus the lane name, resolved against the space's known iteration ids
|
|
172
|
+
# (architecture/I<NN>-<name>.md) so a hyphenated iteration slug cannot
|
|
173
|
+
# bleed into the lane (I06-editor-markdown-markdown-seam → 06-markdown-seam).
|
|
174
|
+
# An unrecognized dir (no architecture/, or a legacy name) falls back
|
|
175
|
+
# to the whole dir name minus the I<NN>- prefix, clipped.
|
|
176
|
+
def tag_label(agent, cwd, iteration_ids: [])
|
|
171
177
|
role = ROLE_ABBREV.fetch(agent, agent[0, 10])
|
|
172
178
|
return role unless cwd && File.basename(cwd) == "wt"
|
|
173
179
|
|
|
174
|
-
|
|
175
|
-
|
|
180
|
+
dir = File.basename(File.dirname(cwd))
|
|
181
|
+
ordinal = dir[/\A[Ii](\d+)-/, 1]
|
|
182
|
+
return "#{role}:#{dir[0, 26]}" unless ordinal
|
|
183
|
+
|
|
184
|
+
lane = iteration_ids.filter_map { |id| dir.delete_prefix("#{id}-") if dir.start_with?("#{id}-") }.first
|
|
185
|
+
lane ||= dir.sub(/\A[Ii]\d+-/, "")
|
|
186
|
+
"#{role}:#{ordinal}-#{lane[0, 26]}"
|
|
176
187
|
end
|
|
177
188
|
|
|
178
189
|
def paint_tag(label, run_id)
|
|
@@ -350,7 +361,7 @@ module Space
|
|
|
350
361
|
cwd, task = backscan(t.path)
|
|
351
362
|
@io.puts format("%s %-9s %5s %5.0fm %7sK %s %s",
|
|
352
363
|
active ? green("●") : dim("·"),
|
|
353
|
-
tag_label(t.agent, cwd),
|
|
364
|
+
tag_label(t.agent, cwd, iteration_ids: iteration_ids),
|
|
354
365
|
dim(Time.at(t.mtime).strftime("%H:%M")),
|
|
355
366
|
age_min, t.size / 1024,
|
|
356
367
|
dim(t.run_id[0, 8]),
|
|
@@ -390,9 +401,18 @@ module Space
|
|
|
390
401
|
|
|
391
402
|
private
|
|
392
403
|
|
|
404
|
+
# The space's iteration ids (architecture/I<NN>-<name>.md basenames) —
|
|
405
|
+
# the authority for splitting a lane dir name into iteration and lane
|
|
406
|
+
# without guessing at hyphen boundaries.
|
|
407
|
+
def iteration_ids
|
|
408
|
+
# paths:exempt — the I[0-9]*-*.md pattern structurally cannot match a dotfile-prefixed name; one non-hidden directory level, no dotfile hazard
|
|
409
|
+
@iteration_ids ||= Dir.glob(File.join(@space.to_s, "architecture", "I[0-9]*-*.md"))
|
|
410
|
+
.map { |p| File.basename(p, ".md") }
|
|
411
|
+
end
|
|
412
|
+
|
|
393
413
|
def adopt(t)
|
|
394
414
|
cwd, task = backscan(t.path)
|
|
395
|
-
tag = paint_tag(tag_label(t.agent, cwd), t.run_id)
|
|
415
|
+
tag = paint_tag(tag_label(t.agent, cwd, iteration_ids: iteration_ids), t.run_id)
|
|
396
416
|
run = Run.new(t.path, t.run_id, t.agent, tag,
|
|
397
417
|
Renderer.new(limit: @limit, think_limit: @env["RUNLOG_THINK"]&.to_i, color: @color),
|
|
398
418
|
@replay ? 0 : t.size, +"", stamp_now)
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
// and commands bash cannot parse (a parse error at tool time burns a turn).
|
|
5
5
|
// Parse-level only: `bash -n` cannot catch runtime failures — those are the
|
|
6
6
|
// bash tool's own domain.
|
|
7
|
+
//
|
|
8
|
+
// NOTE: a sibling copy of the guard logic lives in jetpks/pi-extensions
|
|
9
|
+
// (imported by architect-loop.ts, which applies the same denials to sessions
|
|
10
|
+
// whose cwd is a lane worktree — coordinator-spawned builders cannot get -e
|
|
11
|
+
// injection). The logic is kept identical across both — change them together.
|
|
7
12
|
import { spawnSync } from "node:child_process";
|
|
8
13
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
9
14
|
|
|
@@ -80,6 +85,13 @@ function parseDenial(command: string): string | null {
|
|
|
80
85
|
`fix the command's syntax — bash said: ${(check.stderr ?? "").trim()}`;
|
|
81
86
|
}
|
|
82
87
|
|
|
88
|
+
// The combined denial for a bash command: why it must not run, or null.
|
|
89
|
+
// Exported pure so architect-loop.ts can apply the same rules to sessions
|
|
90
|
+
// running inside lane worktrees (see the NOTE above).
|
|
91
|
+
export function guardDenial(command: string): string | null {
|
|
92
|
+
return gitWriteDenial(command) ?? parseDenial(command);
|
|
93
|
+
}
|
|
94
|
+
|
|
83
95
|
export default function builderGuard(pi: ExtensionAPI): void {
|
|
84
96
|
pi.on("tool_call", (event) => {
|
|
85
97
|
// bash only: the deny list is shell-command shaped; edit/write tools are
|
|
@@ -90,7 +102,7 @@ export default function builderGuard(pi: ExtensionAPI): void {
|
|
|
90
102
|
|
|
91
103
|
// Test the command string itself, never the JSON-rendered whole input —
|
|
92
104
|
// a payload that merely mentions `git commit` in prose must not match.
|
|
93
|
-
const denial =
|
|
105
|
+
const denial = guardDenial(command);
|
|
94
106
|
if (denial) {
|
|
95
107
|
return {
|
|
96
108
|
block: true,
|
data/lib/space_architect.rb
CHANGED
|
@@ -9,6 +9,7 @@ require_relative "space_architect/gate_lint"
|
|
|
9
9
|
require_relative "space_architect/gate_evaluator"
|
|
10
10
|
require_relative "space_architect/architect_project"
|
|
11
11
|
require_relative "space_architect/skill_installer"
|
|
12
|
+
require_relative "space_architect/agent_installer"
|
|
12
13
|
require_relative "space_architect/bug_report"
|
|
13
14
|
require_relative "space_architect/research"
|
|
14
15
|
require_relative "space_architect/cli"
|
|
@@ -140,11 +140,12 @@ the iteration verdicts, the integrated diff, and the BRIEF — to a fresh
|
|
|
140
140
|
paste-and-run block (cd, `git push -u origin project/<slug>`, `gh pr
|
|
141
141
|
create`). Releases of the space-architect gem itself are tag-driven
|
|
142
142
|
(annotated `vX.Y.Z` tag on main → GitHub Action publishes to rubygems).
|
|
143
|
-
Post-landing rollout for spaces running the loop's own tooling:
|
|
144
|
-
force-
|
|
145
|
-
`architect install-
|
|
146
|
-
|
|
147
|
-
|
|
143
|
+
Post-landing rollout for spaces running the loop's own tooling: once the
|
|
144
|
+
release is out (or rebuilt + force-installed from the project branch), run
|
|
145
|
+
`architect install` — the one-shot machine install: skills (pi by default),
|
|
146
|
+
the architect-loop extension, then the role playbooks. A config that moved
|
|
147
|
+
ahead of the installed CLI rejects every command (the stale-gem trap). No
|
|
148
|
+
push, no `gh` call by you; the human runs the block when ready.
|
|
148
149
|
|
|
149
150
|
## Iteration surgery (interactive path)
|
|
150
151
|
|
|
@@ -239,7 +240,8 @@ architect looplog [--list|--replay|--all|--space PATH]
|
|
|
239
240
|
Follow every active loop run in this space, live (--list: inventory only)
|
|
240
241
|
architect runlog [FILE]
|
|
241
242
|
Pretty-print a pi run.jsonl (FILE or stdin; RUNLOG_LIMIT, NO_COLOR honored)
|
|
242
|
-
architect install-
|
|
243
|
+
architect install one-shot: skills (--provider, default pi) + extension + role playbooks
|
|
244
|
+
architect install-skills bundled skills for a harness (--provider, default pi)
|
|
243
245
|
architect install-extensions reconcile the architect-loop pi extension package
|
|
244
246
|
architect install-agents role playbooks to ~/.pi/agent/agents (pi)
|
|
245
247
|
architect bug-report prefilled GitHub issue template for the CLI
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: space-architect
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 9.
|
|
4
|
+
version: 9.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eric Jacobs
|
|
@@ -151,6 +151,7 @@ files:
|
|
|
151
151
|
- README.md
|
|
152
152
|
- exe/architect
|
|
153
153
|
- lib/space_architect.rb
|
|
154
|
+
- lib/space_architect/agent_installer.rb
|
|
154
155
|
- lib/space_architect/architect_project.rb
|
|
155
156
|
- lib/space_architect/bug_report.rb
|
|
156
157
|
- lib/space_architect/cli.rb
|