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.
- package/.claude-plugin/plugin.json +19 -0
- package/LICENSE +21 -0
- package/README.md +389 -2
- package/THIRD_PARTY_NOTICES +14 -0
- package/TROUBLESHOOTING.md +225 -0
- package/agents/codex/frontend-verifier.toml +352 -0
- package/agents/codex/task-checker.toml +345 -0
- package/agents/codex/task-executor.toml +157 -0
- package/agents/codex/test-writer.toml +149 -0
- package/agents/frontend-verifier.md +351 -0
- package/agents/task-checker.md +344 -0
- package/agents/task-executor.md +156 -0
- package/agents/test-writer.md +148 -0
- package/assets/playwriter-bridge/build-launcher.ps1 +32 -0
- package/assets/playwriter-bridge/doctor.ps1 +450 -0
- package/assets/playwriter-bridge/launcher-src.cs +131 -0
- package/assets/playwriter-bridge/opener.js +330 -0
- package/assets/playwriter-bridge/opener.ps1 +63 -0
- package/assets/playwriter-bridge/register-tasks.ps1 +328 -0
- package/assets/playwriter-bridge/relay.ps1 +50 -0
- package/assets/playwriter-bridge/stop.ps1 +125 -0
- package/assets/playwriter-bridge/sync.ps1 +271 -0
- package/assets/playwriter-bridge/tunnel.ps1 +325 -0
- package/assets/playwriter-bridge/vm-free-port.sh +166 -0
- package/assets/terminal-workspace/client-keybindings.md +87 -0
- package/assets/terminal-workspace/config.example.yaml +329 -0
- package/assets/terminal-workspace/setup.sh +4317 -0
- package/cli/README.md +47 -0
- package/cli/bin/acstack.js +3 -0
- package/cli/dist/index.js +25984 -0
- package/cli/dist/index.js.map +7 -0
- package/cli/package.json +59 -0
- package/docs/adr/0001-ste-writing-standard.md +16 -0
- package/docs/adr/0002-version-gate-not-auto-bump.md +35 -0
- package/docs/adr/0003-handoff-ack-command.md +19 -0
- package/docs/adr/0004-dev-setup-per-host.md +13 -0
- package/docs/adr/0005-two-orchestrators-per-host.md +25 -0
- package/docs/adr/0006-document-opener-service.md +89 -0
- package/docs/adr/0007-lead-check-reads-the-transcript.md +87 -0
- package/docs/adr/0008-cli-audits-skill-interviews.md +37 -0
- package/docs/adr/0009-verify-the-write-not-the-writer.md +51 -0
- package/docs/adr/0010-promote-dev-to-main-by-fast-forward.md +115 -0
- package/docs/adr/0011-node-header-is-the-edge-encoding.md +20 -0
- package/docs/adr/0012-node-kind-is-a-closed-set-of-five.md +31 -0
- package/docs/adr/0013-the-diagram-renderer-is-pure.md +23 -0
- package/docs/adr/0014-reading-a-node-hides-its-generated-diagram.md +29 -0
- package/docs/adr/0015-a-node-id-links-to-its-issue-through-an-anchor.md +37 -0
- package/docs/adr/0016-the-map-root-carries-no-status-of-its-own.md +21 -0
- package/docs/adr/0017-call-impeccable-copy-only-its-questions.md +13 -0
- package/docs/adr/index.md +17 -0
- package/docs/config-reference.md +572 -0
- package/docs/dev-setups.md +164 -0
- package/docs/migrate-0.8.md +28 -0
- package/docs/playwriter-bridge-windows.md +15 -0
- package/docs/terminal-workspace-keys.md +137 -0
- package/docs/terminal-workspace.md +1774 -0
- package/docs/workflows.md +309 -0
- package/hooks/ac-writing-style-reminder.sh +86 -0
- package/hooks/context-pressure.sh +583 -0
- package/hooks/hooks.json +50 -0
- package/hooks/session-start-handoff.sh +165 -0
- package/hooks/session-start.sh +214 -0
- package/orchestrator/AGENTS.md +532 -0
- package/output-styles/ac-writing-style.md +286 -0
- package/package.json +50 -4
- package/pi/agents/frontend-verifier.md +357 -0
- package/pi/agents/task-checker.md +348 -0
- package/pi/agents/task-executor.md +160 -0
- package/pi/agents/test-writer.md +154 -0
- package/pi/extension.ts +723 -0
- package/profiles/fullstack-python.yaml +29 -0
- package/profiles/node-typescript.yaml +23 -0
- package/profiles/python.yaml +24 -0
- package/skills/archive/SKILL.md +160 -0
- package/skills/bootstrap/SKILL.md +109 -0
- package/skills/bridge/HANDOFF.md +93 -0
- package/skills/bridge/SKILL-VM.md +292 -0
- package/skills/bridge/SKILL-WINDOWS.md +526 -0
- package/skills/bridge/SKILL.md +121 -0
- package/skills/browser-verify/SKILL.md +229 -0
- package/skills/code-review/SKILL.md +44 -0
- package/skills/commit/SKILL.md +106 -0
- package/skills/compact-and-continue/SKILL.md +241 -0
- package/skills/explore/SKILL.md +149 -0
- package/skills/grilling/SKILL.md +134 -0
- package/skills/handoff/SKILL.md +250 -0
- package/skills/handoff/launch-herdr.md +197 -0
- package/skills/handoff/launch-without-herdr.md +30 -0
- package/skills/handoff/lead-check.md +59 -0
- package/skills/handoff/outcomes.md +162 -0
- package/skills/handoff/running-agent.md +50 -0
- package/skills/handoff/watch.md +115 -0
- package/skills/host/SKILL.md +247 -0
- package/skills/host/detect-host.sh +297 -0
- package/skills/host/herdr.md +79 -0
- package/skills/host/install-herdr-block.sh +467 -0
- package/skills/host/install-orca.sh +512 -0
- package/skills/host/notes.md +97 -0
- package/skills/host/orca.md +160 -0
- package/skills/host/questions.md +163 -0
- package/skills/host/report.md +42 -0
- package/skills/implement/SKILL.md +230 -0
- package/skills/implement/decision-log.md +62 -0
- package/skills/implement/design-pass.md +32 -0
- package/skills/implement/relaxed-tdd.md +28 -0
- package/skills/map/SKILL.md +250 -0
- package/skills/map/frontend.md +227 -0
- package/skills/map/trackers/files.md +74 -0
- package/skills/map/trackers/github.md +180 -0
- package/skills/map/visuals.md +387 -0
- package/skills/new-worktree/SKILL.md +155 -0
- package/skills/new-worktree/detect-orchestrator.sh +328 -0
- package/skills/new-worktree/detect-orchestrator.test.sh +894 -0
- package/skills/new-worktree/herdr.md +84 -0
- package/skills/new-worktree/orca.md +101 -0
- package/skills/pre-commit-review/SKILL.md +242 -0
- package/skills/pre-commit-review/auto-fix.md +19 -0
- package/skills/pre-commit-review/interactive.md +35 -0
- package/skills/quick-fix/SKILL.md +125 -0
- package/skills/record-context/SKILL.md +106 -0
- package/skills/setup/SKILL.md +250 -0
- package/skills/setup/browser.md +123 -0
- package/skills/setup/design-review.md +97 -0
- package/skills/setup/first-run.md +114 -0
- package/skills/setup/report.md +68 -0
- package/skills/setup/row-fixes.md +196 -0
- package/skills/setup/terminal-workspace.md +153 -0
- package/skills/sync/SKILL.md +171 -0
- package/skills/teardown-worktree/SKILL.md +245 -0
- package/skills/teardown-worktree/detector.md +39 -0
- package/skills/teardown-worktree/herdr.md +113 -0
- package/skills/teardown-worktree/orca.md +61 -0
- package/skills/terminal-workspace/SKILL.md +249 -0
- package/skills/terminal-workspace/components.md +36 -0
- package/skills/terminal-workspace/config-choices.md +69 -0
- package/skills/terminal-workspace/emulator-keys.md +48 -0
- package/skills/terminal-workspace/headless-clipboard.md +175 -0
- package/skills/terminal-workspace/turn-off-and-update.md +104 -0
- package/skills/test-conventions/SKILL.md +59 -0
- package/skills/visual-docs/SKILL.md +246 -0
- package/skills/writing/SKILL.md +231 -0
- package/skills/writing/examples.md +153 -0
- 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.
|