acstack 0.0.0-stage → 0.47.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.
Files changed (143) hide show
  1. package/.claude-plugin/plugin.json +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +389 -2
  4. package/THIRD_PARTY_NOTICES +14 -0
  5. package/TROUBLESHOOTING.md +225 -0
  6. package/agents/codex/frontend-verifier.toml +352 -0
  7. package/agents/codex/task-checker.toml +345 -0
  8. package/agents/codex/task-executor.toml +157 -0
  9. package/agents/codex/test-writer.toml +149 -0
  10. package/agents/frontend-verifier.md +351 -0
  11. package/agents/task-checker.md +344 -0
  12. package/agents/task-executor.md +156 -0
  13. package/agents/test-writer.md +148 -0
  14. package/assets/playwriter-bridge/build-launcher.ps1 +32 -0
  15. package/assets/playwriter-bridge/doctor.ps1 +450 -0
  16. package/assets/playwriter-bridge/launcher-src.cs +131 -0
  17. package/assets/playwriter-bridge/opener.js +330 -0
  18. package/assets/playwriter-bridge/opener.ps1 +63 -0
  19. package/assets/playwriter-bridge/register-tasks.ps1 +328 -0
  20. package/assets/playwriter-bridge/relay.ps1 +50 -0
  21. package/assets/playwriter-bridge/stop.ps1 +125 -0
  22. package/assets/playwriter-bridge/sync.ps1 +271 -0
  23. package/assets/playwriter-bridge/tunnel.ps1 +325 -0
  24. package/assets/playwriter-bridge/vm-free-port.sh +166 -0
  25. package/assets/terminal-workspace/client-keybindings.md +87 -0
  26. package/assets/terminal-workspace/config.example.yaml +329 -0
  27. package/assets/terminal-workspace/setup.sh +4317 -0
  28. package/cli/README.md +47 -0
  29. package/cli/bin/acstack.js +3 -0
  30. package/cli/dist/index.js +25984 -0
  31. package/cli/dist/index.js.map +7 -0
  32. package/cli/package.json +59 -0
  33. package/docs/adr/0001-ste-writing-standard.md +16 -0
  34. package/docs/adr/0002-version-gate-not-auto-bump.md +35 -0
  35. package/docs/adr/0003-handoff-ack-command.md +19 -0
  36. package/docs/adr/0004-dev-setup-per-host.md +13 -0
  37. package/docs/adr/0005-two-orchestrators-per-host.md +25 -0
  38. package/docs/adr/0006-document-opener-service.md +89 -0
  39. package/docs/adr/0007-lead-check-reads-the-transcript.md +87 -0
  40. package/docs/adr/0008-cli-audits-skill-interviews.md +37 -0
  41. package/docs/adr/0009-verify-the-write-not-the-writer.md +51 -0
  42. package/docs/adr/0010-promote-dev-to-main-by-fast-forward.md +115 -0
  43. package/docs/adr/0011-node-header-is-the-edge-encoding.md +20 -0
  44. package/docs/adr/0012-node-kind-is-a-closed-set-of-five.md +31 -0
  45. package/docs/adr/0013-the-diagram-renderer-is-pure.md +23 -0
  46. package/docs/adr/0014-reading-a-node-hides-its-generated-diagram.md +29 -0
  47. package/docs/adr/0015-a-node-id-links-to-its-issue-through-an-anchor.md +37 -0
  48. package/docs/adr/0016-the-map-root-carries-no-status-of-its-own.md +21 -0
  49. package/docs/adr/0017-call-impeccable-copy-only-its-questions.md +13 -0
  50. package/docs/adr/index.md +17 -0
  51. package/docs/config-reference.md +572 -0
  52. package/docs/dev-setups.md +164 -0
  53. package/docs/migrate-0.8.md +28 -0
  54. package/docs/playwriter-bridge-windows.md +15 -0
  55. package/docs/terminal-workspace-keys.md +137 -0
  56. package/docs/terminal-workspace.md +1774 -0
  57. package/docs/workflows.md +309 -0
  58. package/hooks/ac-writing-style-reminder.sh +86 -0
  59. package/hooks/context-pressure.sh +583 -0
  60. package/hooks/hooks.json +50 -0
  61. package/hooks/session-start-handoff.sh +165 -0
  62. package/hooks/session-start.sh +214 -0
  63. package/orchestrator/AGENTS.md +532 -0
  64. package/output-styles/ac-writing-style.md +286 -0
  65. package/package.json +50 -4
  66. package/pi/agents/frontend-verifier.md +357 -0
  67. package/pi/agents/task-checker.md +348 -0
  68. package/pi/agents/task-executor.md +160 -0
  69. package/pi/agents/test-writer.md +154 -0
  70. package/pi/extension.ts +723 -0
  71. package/profiles/fullstack-python.yaml +29 -0
  72. package/profiles/node-typescript.yaml +23 -0
  73. package/profiles/python.yaml +24 -0
  74. package/skills/archive/SKILL.md +160 -0
  75. package/skills/bootstrap/SKILL.md +109 -0
  76. package/skills/bridge/HANDOFF.md +93 -0
  77. package/skills/bridge/SKILL-VM.md +292 -0
  78. package/skills/bridge/SKILL-WINDOWS.md +526 -0
  79. package/skills/bridge/SKILL.md +121 -0
  80. package/skills/browser-verify/SKILL.md +229 -0
  81. package/skills/code-review/SKILL.md +44 -0
  82. package/skills/commit/SKILL.md +106 -0
  83. package/skills/compact-and-continue/SKILL.md +241 -0
  84. package/skills/explore/SKILL.md +149 -0
  85. package/skills/grilling/SKILL.md +134 -0
  86. package/skills/handoff/SKILL.md +250 -0
  87. package/skills/handoff/launch-herdr.md +197 -0
  88. package/skills/handoff/launch-without-herdr.md +30 -0
  89. package/skills/handoff/lead-check.md +59 -0
  90. package/skills/handoff/outcomes.md +162 -0
  91. package/skills/handoff/running-agent.md +50 -0
  92. package/skills/handoff/watch.md +115 -0
  93. package/skills/host/SKILL.md +247 -0
  94. package/skills/host/detect-host.sh +297 -0
  95. package/skills/host/herdr.md +79 -0
  96. package/skills/host/install-herdr-block.sh +467 -0
  97. package/skills/host/install-orca.sh +512 -0
  98. package/skills/host/notes.md +97 -0
  99. package/skills/host/orca.md +160 -0
  100. package/skills/host/questions.md +163 -0
  101. package/skills/host/report.md +42 -0
  102. package/skills/implement/SKILL.md +230 -0
  103. package/skills/implement/decision-log.md +62 -0
  104. package/skills/implement/design-pass.md +32 -0
  105. package/skills/implement/relaxed-tdd.md +28 -0
  106. package/skills/map/SKILL.md +250 -0
  107. package/skills/map/frontend.md +227 -0
  108. package/skills/map/trackers/files.md +74 -0
  109. package/skills/map/trackers/github.md +180 -0
  110. package/skills/map/visuals.md +387 -0
  111. package/skills/new-worktree/SKILL.md +155 -0
  112. package/skills/new-worktree/detect-orchestrator.sh +328 -0
  113. package/skills/new-worktree/detect-orchestrator.test.sh +894 -0
  114. package/skills/new-worktree/herdr.md +84 -0
  115. package/skills/new-worktree/orca.md +101 -0
  116. package/skills/pre-commit-review/SKILL.md +242 -0
  117. package/skills/pre-commit-review/auto-fix.md +19 -0
  118. package/skills/pre-commit-review/interactive.md +35 -0
  119. package/skills/quick-fix/SKILL.md +125 -0
  120. package/skills/record-context/SKILL.md +106 -0
  121. package/skills/setup/SKILL.md +250 -0
  122. package/skills/setup/browser.md +123 -0
  123. package/skills/setup/design-review.md +97 -0
  124. package/skills/setup/first-run.md +114 -0
  125. package/skills/setup/report.md +68 -0
  126. package/skills/setup/row-fixes.md +196 -0
  127. package/skills/setup/terminal-workspace.md +153 -0
  128. package/skills/sync/SKILL.md +171 -0
  129. package/skills/teardown-worktree/SKILL.md +245 -0
  130. package/skills/teardown-worktree/detector.md +39 -0
  131. package/skills/teardown-worktree/herdr.md +113 -0
  132. package/skills/teardown-worktree/orca.md +61 -0
  133. package/skills/terminal-workspace/SKILL.md +249 -0
  134. package/skills/terminal-workspace/components.md +36 -0
  135. package/skills/terminal-workspace/config-choices.md +69 -0
  136. package/skills/terminal-workspace/emulator-keys.md +48 -0
  137. package/skills/terminal-workspace/headless-clipboard.md +175 -0
  138. package/skills/terminal-workspace/turn-off-and-update.md +104 -0
  139. package/skills/test-conventions/SKILL.md +59 -0
  140. package/skills/visual-docs/SKILL.md +246 -0
  141. package/skills/writing/SKILL.md +231 -0
  142. package/skills/writing/examples.md +153 -0
  143. package/skills/writing/vocabulary.md +96 -0
@@ -0,0 +1,247 @@
1
+ ---
2
+ name: host
3
+ description: Declare the dev setup of the machine you are on – which agent orchestrators are installed to host terminal panes and git worktrees, which browser-verification modes work here, and the optional extras. Interviews you for every axis and writes the answers to the ACStack CLI's global config under host.*. Run it once per machine.
4
+ disable-model-invocation: true
5
+ allowed-tools: AskUserQuestion, Read, Bash, Glob, Grep
6
+ ---
7
+
8
+ # Host Setup
9
+
10
+ Declare what this machine can do, so the skills that need a browser, a terminal
11
+ pane or a git worktree stop guessing.
12
+
13
+ Three terms, before they get used:
14
+
15
+ - A **dev setup** is the set of machine-level tools an ACStack session runs
16
+ inside. It covers which agent orchestrators host the terminal panes and git
17
+ worktrees. It also covers which browser-verification modes work on this
18
+ machine, plus the optional extras, such as publishing the dev server to a
19
+ private network.
20
+
21
+ - An **orchestrator**, in this skill, means a tool that owns terminal panes and
22
+ git worktrees. There are two of them, `herdr` and `orca`, and they are
23
+ declared separately – one yes-or-no answer each – because a machine can have
24
+ both installed, one, or neither. A machine with neither uses plain git
25
+ worktrees under `.claude/worktrees` in the repository.
26
+
27
+ - An **axis** is one setting of the dev setup, recorded as one `host.*` key.
28
+
29
+ ## Why this is per machine, not per project
30
+
31
+ The answers go to the ACStack CLI's global config, never to
32
+ `acstack/project.yaml`. Read [`notes.md`](notes.md) when the user asks why.
33
+
34
+ ## What this skill installs
35
+
36
+ This skill declares a setup, and it installs two parts of one. It offers to
37
+ install Orca when Orca is missing here. It offers to write the managed block
38
+ that points herdr at its worktree directory.
39
+
40
+ Both offers live in Step 5. Each one plans first, shows you the steps, and waits
41
+ for your approval.
42
+
43
+ Linux is the only target both installs support. On macOS, on Windows and on the
44
+ Windows Subsystem for Linux, each script says it does not support the install
45
+ there, then stops.
46
+
47
+ This skill does not install a project, and it does not install herdr itself.
48
+ Logging in to Tailscale, connecting the Playwriter bridge and pairing a client
49
+ to Orca stay with you.
50
+
51
+ One axis this skill never asks: `host.terminal_workspace`. The `setup` skill
52
+ offers the terminal workspace in its last step, because that workspace installs
53
+ binaries and the offer belongs after the project works. Leave the key alone
54
+ here, and never ask about it.
55
+
56
+ ## Step 1: Detect what is already here
57
+
58
+ Run the detection script that ships beside this file:
59
+
60
+ ```bash
61
+ plugin_root=$(dirname "$(dirname "$(dirname "$(readlink -f ~/.claude/acstack/bin/acstack)")")")
62
+ bash "$plugin_root/skills/host/detect-host.sh"
63
+ ```
64
+
65
+ Read [`notes.md`](notes.md) when the user asks why the path resolution
66
+ above derives the plugin root from a symlink.
67
+
68
+ If that symlink is missing, the SessionStart hook did not run. Tell the user to
69
+ restart their agent session. If a restart does not fix it, point them at
70
+ `TROUBLESHOOTING.md` in the plugin root.
71
+
72
+ The script is read-only. It prints one JSON object to standard output and exits
73
+ 0 even when it finds nothing at all. Missing tooling is a finding, not an error,
74
+ so a bare machine is a successful run.
75
+
76
+ Read [`questions.md`](questions.md) before asking anything. It says what
77
+ each detection field is evidence for, and holds the exact wording of every
78
+ question in Steps 2 and 3.
79
+
80
+ Then read what this machine already declares:
81
+
82
+ ```bash
83
+ ~/.claude/acstack/bin/acstack config get host
84
+ ```
85
+
86
+ Exit code 0 prints the declared axes. Exit code 2 means this machine declares
87
+ nothing yet. The message goes to standard error and names the unset key.
88
+
89
+ Before asking anything, print a short summary to the user: one line per axis,
90
+ naming the current declaration and what the script detected. Re-running this
91
+ skill is safe. It re-asks every axis and uses the current answer as the starting
92
+ point, so a second run loses nothing.
93
+
94
+ ## Step 2: The required axes – detection never decides them
95
+
96
+ The rule for this whole step is simple. **Auto-detection may pre-fill the
97
+ recommendation, but it never decides a required axis**.
98
+
99
+ A detected fact belongs in an option's description – "detected: this session is
100
+ running in a herdr pane" – and never in a silent write. The user answers, and
101
+ the detection only makes the answer easy.
102
+
103
+ ### 2a. Orchestrators
104
+
105
+ There are two orchestrators, and they are two separate questions rather than one
106
+ choice between them. A machine can have both installed, one, or neither, so an
107
+ answer about one says nothing about the other.
108
+
109
+ Ask both in one call to the question tool (AskUserQuestion on Claude Code),
110
+ one question per orchestrator. There is no skip: every machine has an answer
111
+ to each.
112
+
113
+ Answering no to both is a real answer. It declares that this machine has no
114
+ orchestrator. The worktree skills then fall back to plain git worktrees under
115
+ `.claude/worktrees`.
116
+
117
+ The two questions, and the rules for filling their evidence placeholders,
118
+ are in [`questions.md`](questions.md).
119
+
120
+ ### 2b. Browser-verification modes
121
+
122
+ There are three modes, and they are not alternatives to each other – a machine
123
+ can support any combination:
124
+
125
+ - **remote** drives a real browser on the developer's own machine, over the
126
+ Playwriter bridge. That bridge forwards the browser's debugging port to this
127
+ machine on port 19988.
128
+
129
+ - **headless** launches headless Chromium on this machine, meaning a browser
130
+ with no visible window.
131
+
132
+ - **local** launches a visible browser on this machine, which needs a graphical
133
+ display.
134
+
135
+ Ask them as one multi-select question – [`questions.md`](questions.md) holds
136
+ the wording, the "none of these" and "Decide later" rules, and when you
137
+ must ask it.
138
+
139
+ ## Step 3: The optional axes – with skip
140
+
141
+ Ask these in one question-tool call. It carries a third question only when you
142
+ answered yes to Orca in Step 2. Otherwise omit that question rather than asking
143
+ it and discarding the answer.
144
+
145
+ The three questions – preview publishing, element picker and Orca topology –
146
+ are in [`questions.md`](questions.md).
147
+
148
+ A skipped axis and an axis answered `none` or `false` are different things, and
149
+ the difference matters:
150
+
151
+ - **Skipped** means unset. This skill writes nothing for that axis. Note what
152
+ that costs at the command line.
153
+
154
+ Run `acstack config get` on an unset axis. It writes its message to standard
155
+ error and exits 2 for required and optional axes alike. Only the
156
+ `(required)` or `(optional)` qualifier inside that message tells the two
157
+ apart.
158
+
159
+ The difference that actually matters belongs to `acstack config check`, the
160
+ health check. It lists an unset optional axis as informational, and fails only
161
+ on an unset required one.
162
+
163
+ - **`none` and `false`** are real declarations – the user has said this machine
164
+ does not have the thing.
165
+
166
+ Never write a value to represent a skip. If the user skips, write nothing for
167
+ that axis.
168
+
169
+ ## Step 4: Write the answers
170
+
171
+ One `config set` call per answered axis:
172
+
173
+ ```bash
174
+ ~/.claude/acstack/bin/acstack config set host.orchestrators.herdr <true|false>
175
+ ~/.claude/acstack/bin/acstack config set host.orchestrators.orca <true|false>
176
+ ~/.claude/acstack/bin/acstack config set host.browser.remote <true|false>
177
+ ~/.claude/acstack/bin/acstack config set host.browser.headless <true|false>
178
+ ~/.claude/acstack/bin/acstack config set host.browser.local <true|false>
179
+ ~/.claude/acstack/bin/acstack config set host.preview.tailscale_serve <true|false>
180
+ ~/.claude/acstack/bin/acstack config set host.element_picker <stagewise|orca-design-mode|none>
181
+ ~/.claude/acstack/bin/acstack config set host.orca.topology <local|remote>
182
+ ```
183
+
184
+ The rules for this step:
185
+
186
+ - Write nothing for a skipped axis.
187
+ - A success prints `Set <key> = <value>`, with the value rendered as JSON rather
188
+ than bare – so a string axis comes back quoted, `Set host.element_picker =
189
+ "stagewise"`, and a boolean comes back unquoted, `Set host.browser.local =
190
+ true`. A non-zero exit has two causes, and the message tells them apart.
191
+
192
+ - On a non-zero exit, read [`notes.md`](notes.md) – it covers both causes
193
+ and how to recover from each
194
+
195
+ - Setting `host.orca.topology` while `host.orchestrators.orca` is not true is
196
+ accepted, and the CLI warns that nothing reads it, so do not write it in that
197
+ case at all.
198
+
199
+ ## Step 5: Follow-up for each orchestrator declared installed
200
+
201
+ Run the follow-up for every orchestrator answered yes in Step 2. Two yes answers
202
+ means both follow-ups run, in either order. Two no answers means neither runs,
203
+ and the last section applies instead.
204
+
205
+ ### What both installers share
206
+
207
+ Both scripts take `--plan`, `--apply` and `--help`. `--help` prints the usage
208
+ and the options.
209
+
210
+ | Exit code | Meaning |
211
+ | --- | --- |
212
+ | 0 | the run succeeded |
213
+ | 1 | a step failed, or the write failed |
214
+ | 3 | the script does not support this platform, and it changed nothing |
215
+ | 4 | the script cannot safely edit the config file (`install-herdr-block.sh` only) |
216
+ | 64 | the script rejected an argument |
217
+
218
+ `install-orca.sh` checks `--pairing-address` when it parses the arguments. It
219
+ takes a plain host name or address, and rejects anything else with exit 64. A
220
+ value holding a space or a slash never reaches the unit.
221
+
222
+ ### When you declared Orca installed
223
+
224
+ Read [`orca.md`](orca.md) when the user declared Orca installed in Step 2.
225
+ It registers this repository, states the Claude settings rewrite, and
226
+ offers to install Orca when it is missing.
227
+
228
+ ### When you declared herdr installed
229
+
230
+ Read [`herdr.md`](herdr.md) when the user declared herdr installed in
231
+ Step 2. It offers to write the managed block in herdr's config.
232
+
233
+ ### When you declared neither installed
234
+
235
+ There is nothing to provision. The worktree skills fall back to plain git
236
+ worktrees under `.claude/worktrees` in the repository.
237
+
238
+ ## Step 6: Report
239
+
240
+ Close with the report in [`report.md`](report.md). Read it once every answer is
241
+ written and every follow-up has run.
242
+
243
+ ## Notes
244
+
245
+ Read [`notes.md`](notes.md) when a question comes up about what a
246
+ declaration means, or how `host.browser.*` relates to
247
+ `frontend.browser.mode`.
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env bash
2
+ # ACStack host detection.
3
+ #
4
+ # Reports what dev-setup tooling exists on this machine, as one JSON object on
5
+ # stdout. Nothing else is printed, and the exit code is 0 whenever detection
6
+ # ran - a bare machine with none of the tooling installed is a perfectly valid
7
+ # answer, not a failure. Missing tooling is a finding, never an error.
8
+ #
9
+ # The script is strictly read-only. It never writes a file, never changes a
10
+ # setting and never installs anything. Callers - a skill deciding how to set up
11
+ # browser verification, say - use the facts here to choose, and any change is
12
+ # made by the caller afterwards, with the user watching.
13
+ #
14
+ # Every tool is looked up on PATH with `command -v` and every home path derives
15
+ # from $HOME. Nothing is hardcoded to an absolute location. That is what makes
16
+ # this testable: tests/test-host-detect.sh runs the script under `env -i` with a
17
+ # PATH of fake executables and a temporary HOME, so the output is entirely
18
+ # determined by what the test wired up. Reaching past PATH or $HOME to a real
19
+ # location would defeat that, so don't.
20
+ #
21
+ # Output is built by hand rather than with jq, because jq is one of the things a
22
+ # bare machine may not have, and needing a JSON tool to report that you have no
23
+ # tools would be a poor joke.
24
+
25
+ set -u
26
+
27
+ usage() {
28
+ cat <<'USAGE'
29
+ detect-host.sh - report this machine's dev-setup tooling as JSON.
30
+
31
+ Usage:
32
+ detect-host.sh Detect and print one JSON object to stdout.
33
+ detect-host.sh --help Print this help and exit.
34
+
35
+ The script is read-only: it inspects PATH, environment variables, $HOME and the
36
+ current git repository, and changes nothing. It exits 0 whenever detection ran,
37
+ including on a machine where nothing is installed - absent tooling is reported
38
+ in the JSON, not as an error.
39
+
40
+ Reported sections: orchestrator, browser, preview, element_picker,
41
+ orca_topology, claude_settings, project.
42
+ USAGE
43
+ }
44
+
45
+ case "${1-}" in
46
+ "") ;;
47
+ --help|-h) usage; exit 0 ;;
48
+ *) echo "detect-host.sh: unknown argument: $1" >&2
49
+ echo "detect-host.sh: run with --help for usage." >&2
50
+ exit 64 ;;
51
+ esac
52
+
53
+ # --- helpers ---------------------------------------------------------------
54
+
55
+ # A JSON string literal for $1, or the bare literal null when $1 is empty. Paths
56
+ # come from the filesystem, so a backslash or a quote in one is unlikely - but a
57
+ # single unescaped quote would make the whole object unparseable, and the caller
58
+ # would see a tool crash instead of a finding.
59
+ json_str() {
60
+ local s="$1"
61
+ if [ -z "$s" ]; then printf 'null'; return; fi
62
+ s="${s//\\/\\\\}"
63
+ s="${s//\"/\\\"}"
64
+ printf '"%s"' "$s"
65
+ }
66
+
67
+ # Absolute path of $1 if it is on PATH, empty string otherwise. The `|| true`
68
+ # matters: a missing tool is the normal case here, not a reason to stop.
69
+ which_path() {
70
+ command -v "$1" 2>/dev/null || true
71
+ }
72
+
73
+ # Some of the tools below talk to a background daemon over a local socket, and a
74
+ # wedged daemon makes them wait for ever rather than fail. This script is run
75
+ # from a skill with a user watching, so a hang there is worse than a missing
76
+ # finding: bound every such call to three seconds, the same bound the curl probe
77
+ # already uses.
78
+ #
79
+ # `timeout` is itself just a command on PATH, and it is not on every machine, so
80
+ # resolve it once and treat its absence as normal. When it is missing the call
81
+ # still runs, just unbounded - a slightly worse outcome than a bounded call, and
82
+ # a much better one than not detecting at all.
83
+ timeout_bin="$(which_path timeout)"
84
+
85
+ # Run "$@" with the three-second bound when `timeout` is available, and plainly
86
+ # when it is not. The exit status and output are the command's own either way;
87
+ # a call that is cut short simply produces no output, which every caller here
88
+ # already reads as "no evidence".
89
+ run_bounded() {
90
+ if [ -n "$timeout_bin" ]; then
91
+ "$timeout_bin" 3 "$@"
92
+ else
93
+ "$@"
94
+ fi
95
+ }
96
+
97
+ # --- orchestrator ----------------------------------------------------------
98
+
99
+ herdr_bin="$(which_path herdr)"
100
+
101
+ # Orca ships its executable as `orca-ide`; older installs put it on PATH as
102
+ # plain `orca`. Prefer the current name and fall back.
103
+ orca_bin="$(which_path orca-ide)"
104
+ [ -n "$orca_bin" ] || orca_bin="$(which_path orca)"
105
+
106
+ # Which orchestrator is hosting the session we are running inside, judged by the
107
+ # variable each one exports into its panes. Orca wins when both are set: a herdr
108
+ # pane can host an orca session, but not the other way round.
109
+ if [ -n "${ORCA_PANE_KEY:-}" ]; then
110
+ hosting="orca"
111
+ elif [ -n "${HERDR_ENV:-}" ]; then
112
+ hosting="herdr"
113
+ else
114
+ hosting="none"
115
+ fi
116
+
117
+ # --- browser ---------------------------------------------------------------
118
+
119
+ agent_browser_bin="$(which_path agent-browser)"
120
+
121
+ # The Playwriter bridge forwards a real browser's DevTools port to localhost
122
+ # 19988. If something answers there, remote verification is available. The
123
+ # timeout is short and deliberate: a host that has gone away should cost us
124
+ # three seconds, not hang a skill the user is waiting on.
125
+ bridge_answers="false"
126
+ if [ -n "$(which_path curl)" ]; then
127
+ if curl -fsS --max-time 3 "http://localhost:19988/json/version" >/dev/null 2>&1; then
128
+ bridge_answers="true"
129
+ fi
130
+ fi
131
+
132
+ # Chromium-family browsers, in preference order. Any one of them can drive
133
+ # headless verification, so we report all we find and let the caller pick.
134
+ chromium_json=""
135
+ chromium_found="false"
136
+ for browser in chromium chromium-browser google-chrome google-chrome-stable \
137
+ brave-browser microsoft-edge; do
138
+ p="$(which_path "$browser")"
139
+ [ -n "$p" ] || continue
140
+ [ -z "$chromium_json" ] || chromium_json="${chromium_json},"
141
+ chromium_json="${chromium_json}$(json_str "$p")"
142
+ chromium_found="true"
143
+ done
144
+
145
+ # A graphical session of either flavour. Without one, a browser can still run
146
+ # headless, but nobody can watch it.
147
+ if [ -n "${DISPLAY:-}" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; then
148
+ display="true"
149
+ else
150
+ display="false"
151
+ fi
152
+
153
+ browser_remote="$bridge_answers"
154
+ browser_headless="$chromium_found"
155
+ if [ "$chromium_found" = "true" ] && [ "$display" = "true" ]; then
156
+ browser_local="true"
157
+ else
158
+ browser_local="false"
159
+ fi
160
+
161
+ # --- preview (tailscale) ---------------------------------------------------
162
+
163
+ tailscale_bin="$(which_path tailscale)"
164
+
165
+ # An installed tailscale that nobody has logged in is no use for sharing a
166
+ # preview URL, so the two facts are reported separately.
167
+ tailscale_logged_in="false"
168
+ if [ -n "$tailscale_bin" ]; then
169
+ # Bounded: `tailscale status` asks the local tailscaled daemon over a socket
170
+ # and never returns on its own if that daemon is wedged.
171
+ if run_bounded "$tailscale_bin" status --json 2>/dev/null \
172
+ | grep -q '"BackendState"[[:space:]]*:[[:space:]]*"Running"'; then
173
+ tailscale_logged_in="true"
174
+ fi
175
+ fi
176
+
177
+ if [ -n "$tailscale_bin" ] && [ "$tailscale_logged_in" = "true" ]; then
178
+ preview_rec="true"
179
+ else
180
+ preview_rec="false"
181
+ fi
182
+
183
+ # --- orca topology ---------------------------------------------------------
184
+
185
+ # Orca can serve a UI from this machine to a browser elsewhere. A running
186
+ # user-level `orca` unit with no local display is the shape of a remote setup:
187
+ # the machine serves, somebody else looks. Anything else is local.
188
+ serve_unit_active="false"
189
+ if [ -n "$(which_path systemctl)" ]; then
190
+ # The unit name `orca` is an assumption, not a fact: whatever provisions the
191
+ # server chooses that name, and nothing pins it yet, so a unit installed under
192
+ # a different name reads here as "no server". Bounded too, because
193
+ # `systemctl --user` waits on the user message bus and blocks when that bus is
194
+ # not available - on a machine with no user session, say.
195
+ if [ "$(run_bounded systemctl --user is-active orca 2>/dev/null)" = "active" ]; then
196
+ serve_unit_active="true"
197
+ fi
198
+ fi
199
+
200
+ if [ -z "$orca_bin" ]; then
201
+ orca_topology_rec="" # emitted as null: no Orca, so no topology
202
+ elif [ "$serve_unit_active" = "true" ] && [ "$display" = "false" ]; then
203
+ orca_topology_rec="remote"
204
+ else
205
+ orca_topology_rec="local"
206
+ fi
207
+
208
+ # --- element picker --------------------------------------------------------
209
+
210
+ stagewise_bin="$(which_path stagewise)"
211
+
212
+ # Stagewise is the dedicated tool, so it wins outright. Orca's built-in design
213
+ # mode is the fallback, but only when Orca is driving a local browser - in a
214
+ # remote topology the picker has no window to pick in.
215
+ if [ -n "$stagewise_bin" ]; then
216
+ picker_rec="stagewise"
217
+ elif [ -n "$orca_bin" ] && [ "$orca_topology_rec" = "local" ]; then
218
+ picker_rec="orca-design-mode"
219
+ else
220
+ picker_rec="none"
221
+ fi
222
+
223
+ # --- claude settings -------------------------------------------------------
224
+
225
+ # Whether Orca has already wired hooks into the user's Claude settings, and
226
+ # whether a backup of that file exists. A caller about to edit settings.json
227
+ # wants to know both before it touches anything.
228
+ settings_file="${HOME}/.claude/settings.json"
229
+ orca_hooks_present="false"
230
+ # Deliberately loose: any mention of "orca" anywhere in the file counts, so an
231
+ # unrelated path or permission entry containing that word sets this too. It is
232
+ # evidence that Orca may have touched the file, not proof that it did, and
233
+ # callers must word it that way.
234
+ if [ -f "$settings_file" ] && grep -qi orca "$settings_file" 2>/dev/null; then
235
+ orca_hooks_present="true"
236
+ fi
237
+
238
+ backup_exists="false"
239
+ [ -f "${settings_file}.bak" ] && backup_exists="true"
240
+
241
+ # --- project ---------------------------------------------------------------
242
+
243
+ project_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
244
+
245
+ # A top-level `frontend:` key in the project config is what turns on frontend
246
+ # verification, so we look for it at column zero only - `frontend:` nested under
247
+ # some other key means something else entirely.
248
+ has_frontend="false"
249
+ if [ -n "$project_root" ] && [ -f "${project_root}/acstack/project.yaml" ]; then
250
+ if grep -q '^frontend:' "${project_root}/acstack/project.yaml" 2>/dev/null; then
251
+ has_frontend="true"
252
+ fi
253
+ fi
254
+
255
+ # --- output ----------------------------------------------------------------
256
+
257
+ cat <<JSON
258
+ {
259
+ "orchestrator": {
260
+ "herdr_binary": $(json_str "$herdr_bin"),
261
+ "orca_binary": $(json_str "$orca_bin"),
262
+ "hosting_this_session": $(json_str "$hosting")
263
+ },
264
+ "browser": {
265
+ "agent_browser_binary": $(json_str "$agent_browser_bin"),
266
+ "bridge_port_answers": ${bridge_answers},
267
+ "chromium_binaries": [${chromium_json}],
268
+ "display": ${display},
269
+ "recommended": {
270
+ "remote": ${browser_remote},
271
+ "headless": ${browser_headless},
272
+ "local": ${browser_local}
273
+ }
274
+ },
275
+ "preview": {
276
+ "tailscale_binary": $(json_str "$tailscale_bin"),
277
+ "tailscale_logged_in": ${tailscale_logged_in},
278
+ "recommended": ${preview_rec}
279
+ },
280
+ "element_picker": {
281
+ "stagewise_binary": $(json_str "$stagewise_bin"),
282
+ "recommended": $(json_str "$picker_rec")
283
+ },
284
+ "orca_topology": {
285
+ "serve_unit_active": ${serve_unit_active},
286
+ "recommended": $(json_str "$orca_topology_rec")
287
+ },
288
+ "claude_settings": {
289
+ "orca_hooks_present": ${orca_hooks_present},
290
+ "backup_exists": ${backup_exists}
291
+ },
292
+ "project": {
293
+ "root": $(json_str "$project_root"),
294
+ "has_frontend": ${has_frontend}
295
+ }
296
+ }
297
+ JSON
@@ -0,0 +1,79 @@
1
+ # herdr follow-up
2
+
3
+ Read this file when the user declared herdr installed in Step 2.
4
+
5
+ - `plugin_root` comes from Step 1 in `SKILL.md`
6
+ - Step 5 in `SKILL.md` lists the exit codes both installers share
7
+
8
+ ### When you declared herdr installed
9
+
10
+ **Write the managed block.** herdr keeps its config at
11
+ `~/.config/herdr/config.toml`. ACStack needs one setting in that file: the
12
+ directory herdr creates worktrees under. Offer to write it.
13
+
14
+ The installer is `install-herdr-block.sh`, beside this file, and Step 1 in
15
+ `SKILL.md` resolves the same `plugin_root`.
16
+
17
+ Plan it first:
18
+
19
+ ```bash
20
+ bash "$plugin_root/skills/host/install-herdr-block.sh" --plan
21
+ ```
22
+
23
+ Two options change the defaults. `--config PATH` points at a config file
24
+ elsewhere, and `--worktree-dir PATH` changes the directory. The defaults are
25
+ `~/.config/herdr/config.toml` and `~/.herdr/worktrees`.
26
+
27
+ Show the printed step list verbatim, then ask with the question tool (AskUserQuestion on Claude Code). Declining is
28
+ a real answer, and nothing runs. On approval, run the same command with
29
+ `--apply`.
30
+
31
+ This is the block it writes, and it writes nothing else:
32
+
33
+ ```
34
+ # >>> acstack terminal-workspace >>>
35
+ [worktrees]
36
+ directory = "~/.herdr/worktrees"
37
+ # <<< acstack terminal-workspace <<<
38
+ ```
39
+
40
+ It creates the file when the file is absent. It appends the block when the block
41
+ is absent. It replaces the block in place when the block is there and differs.
42
+
43
+ It refuses and changes nothing whenever the config file is not in a state it can
44
+ safely edit. Editing one of these would corrupt the file, or throw away
45
+ somebody's config:
46
+
47
+ - a path that exists and is not a regular file
48
+ - a file that already holds more than one block with those markers
49
+ - a start marker count and an end marker count that differ
50
+ - a start marker that the file never closes
51
+ - a `[worktrees]` table defined outside any managed block
52
+ - a block between the markers that defines a table besides `[worktrees]`
53
+
54
+ It exits 4 and says which case it hit. Tell the user to fix the config by hand,
55
+ then run the plan again.
56
+
57
+ **What this skill ships, and what is personal taste.** This skill ships the
58
+ managed block and nothing else. The block tells herdr to put worktrees under
59
+ `~/.herdr/worktrees`. The worktree skills read that path, so it is the one
60
+ setting they depend on.
61
+
62
+ The rest of a herdr setup is personal taste. This skill installs none of it: the
63
+ keymap, the popup key bindings, the `acstack.herdr-numbers` plugin, and the
64
+ `acstack-*` helper binaries in `~/.local/bin`.
65
+
66
+ Copy your own if you want them. Nothing in ACStack needs any of them.
67
+
68
+ **The terminal-workspace skill writes the same block, wider.**
69
+ `assets/terminal-workspace/setup.sh apply` fences `[keys]`, one
70
+ `[[keys.command]]` per binding, and `[worktrees]` between these same two
71
+ markers. It takes the directory from `herdr.worktrees_directory` in
72
+ `~/.config/acstack/terminal-workspace.yaml`. A machine that has run it needs
73
+ nothing from this installer.
74
+
75
+ The installer therefore leaves that block alone. It reports the step as skipped
76
+ when the directory already agrees. It refuses with exit 4 when it does not.
77
+
78
+ Change the directory in `terminal-workspace.yaml` and run `setup.sh apply`
79
+ again, never this script.