acstack 0.0.0-stage → 0.47.1

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 +334 -0
  27. package/assets/terminal-workspace/setup.sh +4404 -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 +1780 -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,166 @@
1
+ #!/usr/bin/env bash
2
+ # vm-free-port.sh – VM-side cleanup for a stuck reverse-forward port.
3
+ #
4
+ # When the Windows tunnel repeatedly hits "remote port forwarding failed
5
+ # for listen port 19988", this script identifies what holds 127.0.0.1:19988
6
+ # on the VM and – if safe – kills it.
7
+ #
8
+ # Guardrail: refuses to kill if the socket is held by the current user's
9
+ # own interactive SSH session. Killing that would drop the shell running
10
+ # the script.
11
+ #
12
+ # Guardrail: refuses to kill anything that still answers HTTP on the port. A
13
+ # forward that is serving is the bridge, not litter, and the operator who runs
14
+ # this out of habit would be taking the browser away from whoever is using it.
15
+ #
16
+ # Scoped strictly to the two ports this setup owns: 19988 (the bridge's CDP
17
+ # port) and 19989 (the document opener). Any other port is refused. This is an
18
+ # allowlist rather than a free --port flag on purpose - the guardrails below
19
+ # reason about what a holder is, and that reasoning is only valid for sockets
20
+ # we put there. Exits non-zero with a clear message on any ambiguous or unsafe
21
+ # situation.
22
+ #
23
+ # Intended to be invoked via the handoff block produced by Windows-side
24
+ # doctor.ps1. Safe to run at any time; no-ops if the port is already free.
25
+
26
+ set -u
27
+
28
+ PORT=19988
29
+ # 64 for a bad argument (the conventional EX_USAGE), because 2 already means
30
+ # something specific here: the port is held by something this script may not
31
+ # kill. Reusing it would make "you typed it wrong" indistinguishable from "the
32
+ # port is genuinely stuck".
33
+ case "${1:-}" in
34
+ --port)
35
+ case "${2:-}" in
36
+ 19988|19989) PORT="$2" ;;
37
+ *) printf 'ERROR: --port accepts only 19988 (bridge) or 19989 (opener), not "%s".\n' "${2:-}" >&2; exit 64 ;;
38
+ esac
39
+ ;;
40
+ "") ;;
41
+ *) printf 'usage: vm-free-port.sh [--port 19988|19989]\n' >&2; exit 64 ;;
42
+ esac
43
+
44
+ log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }
45
+ err() { log "ERROR: $*" >&2; }
46
+
47
+ # Inspect 127.0.0.1:$PORT – print owner PID, process name, and parent PID.
48
+ find_holder() {
49
+ ss -H -lntp "sport = :$PORT" 2>/dev/null | head -n1
50
+ }
51
+
52
+ line=$(find_holder)
53
+ if [ -z "$line" ]; then
54
+ log "port $PORT is already free. Nothing to do."
55
+ exit 0
56
+ fi
57
+
58
+ log "current holder: $line"
59
+
60
+ # Guardrail: is the forward still carrying traffic? A holder on this port is not
61
+ # garbage by default - while the tunnel is up it is the bridge, and this is the
62
+ # socket agent-browser drives Chrome through. The question is asked of the port
63
+ # rather than of the process, and asked first, because a non-root ss cannot name
64
+ # the owner of someone else's socket: the checks below would give up at "could
65
+ # not parse PID" and never reach the one guardrail that matters most. doctor.ps1 prints this
66
+ # script as the remedy for an amber "Tunnel logs" row, and that row trails for
67
+ # five minutes after the tunnel has already healed itself, so running the
68
+ # clearer against a healthy forward is the ordinary mistake, not the rare one.
69
+ #
70
+ # Any HTTP answer counts as alive. A 401 or a 404 comes from a CDP endpoint
71
+ # that is serving and merely guarded, or asked for a path it does not have, so
72
+ # --fail is deliberately absent: it would read those as dead and kill them. The
73
+ # probe is bounded at 3 seconds, because a genuinely wedged socket is exactly
74
+ # the one that would otherwise hang the clearer called to free it.
75
+ #
76
+ # There is no override flag for this refusal, on purpose. A live tunnel is
77
+ # released by closing the session holding it, and a switch that skipped the
78
+ # probe would just be the habit the probe exists to stop.
79
+ if curl -s -o /dev/null -m 3 "http://127.0.0.1:$PORT/" 2>/dev/null; then
80
+ err "port $PORT is carrying a live tunnel: something answered HTTP on it just now."
81
+ err "Killing the holder would take the browser away from whoever is using it."
82
+ err "If the tunnel really is finished with, close the SSH session holding the"
83
+ err "forward - on the laptop that is stop.ps1 - and this port frees itself."
84
+ exit 7
85
+ fi
86
+
87
+ # ss -lntp line format includes users:(("<name>",pid=<pid>,fd=<fd>))
88
+ pid=$(printf '%s' "$line" | grep -oE 'pid=[0-9]+' | head -n1 | cut -d= -f2)
89
+ if [ -z "${pid:-}" ]; then
90
+ err "could not parse PID from ss output. Run: sudo ss -lntp 'sport = :$PORT'"
91
+ exit 2
92
+ fi
93
+
94
+ pname=$(ps -o comm= -p "$pid" 2>/dev/null || true)
95
+ ppid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ' || true)
96
+ log "PID $pid ($pname), parent PID $ppid"
97
+
98
+ # Guardrail: is this process part of the current user's active SSH session?
99
+ # If the holder (or any ancestor) is our own sshd session leader, refuse.
100
+ current_sid=$(loginctl show-session "$(loginctl | awk -v u="$(id -un)" '$3==u {print $1; exit}')" -p Leader --value 2>/dev/null || true)
101
+
102
+ # Walk ancestors of the holder. If we meet $$ (this script's own shell) or
103
+ # its session leader, it's us.
104
+ own_tree=0
105
+ cursor=$pid
106
+ for _ in 1 2 3 4 5 6 7 8; do
107
+ [ -z "$cursor" ] && break
108
+ [ "$cursor" = "1" ] && break
109
+ if [ "$cursor" = "$$" ] || [ "$cursor" = "$PPID" ]; then
110
+ own_tree=1
111
+ break
112
+ fi
113
+ if [ -n "${current_sid:-}" ] && [ "$cursor" = "$current_sid" ]; then
114
+ own_tree=1
115
+ break
116
+ fi
117
+ cursor=$(ps -o ppid= -p "$cursor" 2>/dev/null | tr -d ' ' || true)
118
+ done
119
+
120
+ if [ "$own_tree" = "1" ]; then
121
+ err "port $PORT is held by your own interactive session (PID $pid, ancestor of this shell)."
122
+ err "Killing it would drop the SSH session running this script."
123
+ err "Exit this SSH session from another shell, or close the client, then retry."
124
+ exit 3
125
+ fi
126
+
127
+ # Prefer not to kill anything that is not sshd – surface it for review.
128
+ if [ "$pname" != "sshd" ] && [ "$pname" != "sshd-session" ]; then
129
+ err "holder is '$pname', not sshd. Refusing to kill a non-sshd process on port $PORT."
130
+ err "If this is expected (e.g. a local test server), stop it manually."
131
+ exit 4
132
+ fi
133
+
134
+ # Check parent liveness: an orphan sshd forward channel typically has a
135
+ # parent that has exited or has been re-parented.
136
+ if [ -n "${ppid:-}" ] && [ "$ppid" != "1" ]; then
137
+ if ! kill -0 "$ppid" 2>/dev/null; then
138
+ log "parent PID $ppid is gone – holder is an orphan sshd forward channel."
139
+ else
140
+ log "parent PID $ppid is alive – holder is an active sshd session, likely a stale tunnel that has not yet been reaped by ClientAlive."
141
+ fi
142
+ fi
143
+
144
+ log "killing PID $pid ..."
145
+ if kill "$pid" 2>/dev/null; then
146
+ :
147
+ else
148
+ log "kill failed without privilege; retrying with sudo."
149
+ if ! sudo -n kill "$pid" 2>/dev/null; then
150
+ err "could not kill PID $pid. Re-run with sudo, or ask the admin to kill it."
151
+ exit 5
152
+ fi
153
+ fi
154
+
155
+ # Give the kernel a moment to release the socket.
156
+ for _ in 1 2 3 4 5; do
157
+ sleep 1
158
+ if [ -z "$(find_holder)" ]; then
159
+ log "port $PORT is now free."
160
+ exit 0
161
+ fi
162
+ done
163
+
164
+ err "port $PORT still held after kill. Current state:"
165
+ ss -lntp "sport = :$PORT" >&2 || true
166
+ exit 6
@@ -0,0 +1,87 @@
1
+ # Free the keys your terminal emulator swallows
2
+
3
+ Hand this to a coding agent running **on the machine you type on** – the
4
+ Windows, macOS, or Linux desktop you SSH *from*. You cannot do this from the
5
+ remote host. The local terminal emulator intercepts the keys before they ever
6
+ reach the other end.
7
+
8
+ ## 1. The emulator claims the same alt chords the remote host binds
9
+
10
+ On the remote host the user runs herdr, a terminal multiplexer. One program
11
+ holds many terminal sessions and keeps them running after the user
12
+ disconnects. Its setup binds `alt` key combinations, called chords, for panes,
13
+ tabs and popups.
14
+
15
+ Some terminal emulators bind the same chords locally. They swallow the
16
+ keypress, or they act on it **and** forward it, so one press does two things.
17
+
18
+ The known collision is **`alt+shift+d`**, which Windows Terminal binds to
19
+ "duplicate pane" by default. It splits the tab on your own machine and never
20
+ forwards the key, so the remote host sees nothing.
21
+
22
+ The remote setup avoids this one by putting its diff viewer on `alt+f` and
23
+ `alt+shift+f`. Other emulators claim other chords, so the table below still
24
+ has to be free.
25
+
26
+ To tell an emulator collision from a remote misconfiguration, run `cat -v` in
27
+ any pane and press the chord. One that arrives prints an escape sequence such
28
+ as `^[Z`. One that prints nothing never left your machine.
29
+
30
+ ## 2. Unbind, in the terminal emulator only
31
+
32
+ Work in the terminal emulator config only. Unbind every chord in the table
33
+ below that the emulator currently claims. Change nothing on the remote host,
34
+ and do not touch the remote configuration.
35
+
36
+ | Chord | Used remotely for |
37
+ |---|---|
38
+ | `alt+f` / `alt+shift+f` | diff viewer, popup and tab |
39
+ | `alt+x` / `alt+shift+x` | diff comparison picker, popup and tab |
40
+ | `alt+i` / `alt+shift+i` | pull request dashboard, popup and tab |
41
+ | `alt+y` / `alt+shift+y` | file manager, popup and tab |
42
+ | `alt+g` / `alt+shift+g` | git TUI, popup and tab |
43
+ | `alt+h` `alt+j` `alt+k` `alt+l` | move between panes |
44
+ | `alt+left` / `alt+right` | previous and next tab |
45
+ | `alt+up` / `alt+down` | previous and next workspace |
46
+ | `alt+1` ... `alt+9` | jump to an agent |
47
+ | `alt+c` `alt+e` `alt+minus` `alt+z` `alt+s` `alt+a` `alt+n` `alt+u` `alt+w` `alt+r` | new tab, splits, zoom, sidebar, last pane, agent and workspace navigation |
48
+
49
+ A workspace is one herdr container holding tabs and panes.
50
+
51
+ ### Windows Terminal
52
+
53
+ Settings, then "Open JSON file". Add to the `actions` array:
54
+
55
+ ```json
56
+ { "command": "unbound", "keys": "alt+shift+d" }
57
+ ```
58
+
59
+ Windows Terminal binds three more chords from the same family. Check
60
+ `alt+shift+plus`, `alt+shift+minus` and `alt+shift+<arrow>` in the same pass.
61
+
62
+ ### Other emulators
63
+
64
+ - **WezTerm**: `keys` in `.wezterm.lua`, with `action = wezterm.action.DisableDefaultAssignment`
65
+ - **iTerm2**: Preferences, Keys, Key Bindings, or the profile's own Keys tab
66
+ - **kitty**: `map alt+shift+d no_op` in `kitty.conf`
67
+ - **Ghostty**: `keybind = alt+shift+d=ignore` in the config
68
+ - **GNOME Terminal / Konsole**: check the shortcut editor for the chord
69
+
70
+ ## 3. What never to change
71
+
72
+ - **Back up the config before editing**, and show the change before saving.
73
+ - Unbind only. Do not remap these chords to something else, and do not touch
74
+ bindings unrelated to the table above.
75
+
76
+ - Leave a chord alone when the terminal emulator does not bind it locally.
77
+
78
+ ## 4. Confirm exactly one thing happens
79
+
80
+ Reload or restart the terminal, then SSH in and press `alt+shift+f`. Exactly
81
+ one thing should happen.
82
+
83
+ A tab opens on the remote host. A local split, or both at once, means the
84
+ unbind did not take.
85
+
86
+ Report which chords the terminal emulator had bound, what changed, and how to
87
+ undo it.
@@ -0,0 +1,334 @@
1
+ # ACStack terminal workspace.
2
+ # Copy to ~/.config/acstack/terminal-workspace.yaml, edit, then run
3
+ # setup.sh apply.
4
+ #
5
+ # This is machine-level, not per-project. It installs binaries and writes
6
+ # keybindings for your user, so it does not belong in acstack/project.yaml.
7
+ #
8
+ # What each component is, every key, and the traps worth knowing:
9
+ # docs/terminal-workspace.md in the ACStack plugin.
10
+ #
11
+ # A key here never carries a hyphen, so gh-dash is gh_dash. The last two
12
+ # blocks name a feature rather than a binary: markdown is acstack-md, and
13
+ # remote is acstack-clip plus acstack-open.
14
+
15
+ # Master switch. With this false, apply exits without installing anything.
16
+ # It removes nothing either. Use setup.sh uninstall for that.
17
+ enabled: true
18
+
19
+ herdr:
20
+ enabled: true
21
+ # A terminal multiplexer. One program holds many terminal sessions and keeps
22
+ # them running after you disconnect.
23
+ #
24
+ # A chord is one key combination, such as alt+f. herdr normally wants a
25
+ # prefix key first. You press the prefix, release it, then press the second
26
+ # key. A chord modifier replaces those two steps with one combination.
27
+ #
28
+ # Keep "alt". It is the only family measured to work on both attach paths.
29
+ # ctrl+alt does reach herdr when you SSH in and run it on the remote host.
30
+ # But a Windows client attaching with `herdr --remote` never delivers it, so
31
+ # those chords go silently dead.
32
+ # Set "none" to keep herdr's default prefix-only keymap.
33
+ chord_modifier: alt
34
+ # A pane's scrollbar costs it a column. herdr counts that column inside the
35
+ # pane rect but tells the program in the pane it has the full width, so a
36
+ # full-screen app wraps one column early and every row drifts one column
37
+ # left of the row above it. Set true to take the scrollbar back; scrolling
38
+ # itself is unaffected either way, because the mouse wheel and copy mode do
39
+ # not use it.
40
+ pane_scrollbars: false
41
+ # A pane border carries the name of the agent running in it. Several agents
42
+ # in several panes is what this workspace is for, and an unlabelled pane does
43
+ # not say which agent it holds. Set false to take the labels off.
44
+ agent_labels_on_pane_borders: true
45
+ # How the agent panel orders its rows: "priority" puts the agents waiting on
46
+ # you at the top. Empty leaves herdr's own order alone.
47
+ agent_panel_sort: ""
48
+ # Where herdr draws a toast, its own short status message. "herdr" draws it
49
+ # in the terminal itself, which is the only route that survives SSH. Empty
50
+ # leaves herdr's own choice alone.
51
+ toast_delivery: ""
52
+ theme:
53
+ # The colour theme herdr paints. Empty writes no theme at all, so herdr
54
+ # keeps whatever it already uses.
55
+ name: ""
56
+ # One herdr server, two devices, two backgrounds. auto_switch asks the
57
+ # attached client for its background colour over OSC 11 - an escape
58
+ # sequence a terminal answers with the colour it is painting - and picks
59
+ # the dark or the light theme from the answer. So a dark terminal on one
60
+ # device and a light one on another, such as an e-ink panel, each get the
61
+ # right theme without either changing the other.
62
+ #
63
+ # Both keys below are read only when `light` names a theme. Without one
64
+ # there is nothing to switch to, so `name` is written on its own.
65
+ auto_switch: true
66
+ # The theme a light client gets. `name` is what a dark one gets, and what
67
+ # a client that answers nothing at all falls back to.
68
+ light: ""
69
+ worktrees_directory: "~/.herdr/worktrees"
70
+ # Agent state hook, so herdr reads state from the agent instead of the
71
+ # screen. One of: claude, codex, opencode, copilot, none.
72
+ integration: claude
73
+ # Link a small herdr plugin that keeps the sidebar numbers matching the keys
74
+ # you press for them. Opening or closing a workspace or a worktree moves the
75
+ # rows, so without this the number beside a row stops being the number that
76
+ # jumps to it.
77
+ renumber_plugin: true
78
+
79
+ gh_dash:
80
+ enabled: true
81
+ # A pull request dashboard. It lists pull requests in the sections you
82
+ # define below, and hands the selected one to tuicr or to an agent.
83
+ popup_key: "alt+i"
84
+ tab_key: "alt+shift+i"
85
+ sections:
86
+ - title: Mine
87
+ filters: "is:open author:@me"
88
+ - title: Needs review
89
+ filters: "is:open review-requested:@me"
90
+ - title: Failing
91
+ filters: "is:open author:@me status:failure"
92
+ # Local clones. Needed for checkout and for {{.RepoPath}} in keybindings.
93
+ repo_paths: {}
94
+ # Bind PageUp and PageDown to scroll the preview. gh-dash uses ctrl+u and
95
+ # ctrl+d by default, which is fine in vim and awkward everywhere else.
96
+ page_keys: true
97
+ keybindings:
98
+ # Both keys below are what apply binds anyway with no config file present.
99
+ # Change either to move it, or set it to "" to leave the key unbound.
100
+ #
101
+ # Open the selected pull request in tuicr, which reviews it in place.
102
+ review: "D"
103
+ # Hand the selected pull request to an agent. Empty disables it.
104
+ agent_review: "S"
105
+
106
+ diffnav:
107
+ enabled: true
108
+ # A diff viewer with a file tree. The popup compares the branch you are on
109
+ # against origin/dev, committed work only, and names both sides in a banner.
110
+ # Repos without a dev branch fall back to the default branch.
111
+ popup_key: "alt+f"
112
+ # Same comparison, a new tab instead of a popup.
113
+ tab_key: "alt+shift+f"
114
+ # An fzf menu of comparisons: against dev or the default branch, with or
115
+ # without uncommitted work, one commit, or any branch against any branch.
116
+ pick_key: "alt+x"
117
+ # Same menu, a new tab instead of a popup.
118
+ pick_tab_key: "alt+shift+x"
119
+
120
+ delta:
121
+ enabled: true
122
+ # The diff renderer git pages through. Sets core.pager and
123
+ # interactive.diffFilter. This does not change what agents see, because git
124
+ # only pages to a terminal.
125
+ set_git_pager: true
126
+
127
+ tuicr:
128
+ enabled: true
129
+ # Reviews a pull request inside the terminal. gh-dash hands pull requests to
130
+ # it. It is not the file manager, which is yazi below.
131
+ # Starting width of the file list, as a percentage of the content area.
132
+ file_list_width: 30
133
+ # Per-file +added -removed counts in the tree.
134
+ show_file_line_stats: true
135
+ # "dark", "light", or empty to let tuicr work it out for itself.
136
+ #
137
+ # Detection asks the terminal for its background colour over OSC 11 - an
138
+ # escape sequence a terminal answers with the colour it is painting. Under
139
+ # `herdr --remote` nothing answers, so tuicr falls back to a desktop setting
140
+ # that a headless machine reports as light, then paints dark text on a black
141
+ # terminal. Pin the value on any machine you reach over the network.
142
+ appearance: ""
143
+ #
144
+ # TEMPORARY. Two upstream pull requests are still open, plus one local fix
145
+ # with none yet:
146
+ # agavra/tuicr#607 configurable file line stats (antonio2368)
147
+ # agavra/tuicr#633 <leader>L / <leader>H resize (ours)
148
+ # (none yet) blank +/- counts in pull request review mode (ours)
149
+ # Until both merge, the two settings above only exist in a fork build.
150
+ # `setup.sh status` checks both and tells you when to switch this off.
151
+ # With it false, the stock release installs and ignores those two settings.
152
+ build_from_fork: false
153
+ fork: "https://github.com/ac8318740/tuicr"
154
+ fork_branch: "local/daily"
155
+
156
+ lazygit:
157
+ enabled: true
158
+ # A git TUI: stage with space, commit with c, amend with A, push with P.
159
+ # g is reserved for it, so herdr's goto sits on prefix+t instead.
160
+ popup_key: "alt+g"
161
+ # Same tool, a new tab instead of a popup.
162
+ tab_key: "alt+shift+g"
163
+
164
+ harlequin:
165
+ enabled: true
166
+ # A SQL editor in the terminal. Write a query on the left, read the rows on
167
+ # the right, and browse the schema in a tree beside both.
168
+ #
169
+ # It is the one component that is not a downloaded binary. harlequin is a
170
+ # python program, so uv installs it into an environment of its own and puts
171
+ # one command on your PATH. A machine without uv skips this component and
172
+ # apply says so.
173
+ popup_key: "alt+q"
174
+ # Same editor, a new tab instead of a popup.
175
+ tab_key: "alt+shift+q"
176
+ # The databases harlequin can talk to. Each name here installs the
177
+ # harlequin-<name> package alongside it. Adding one and running apply again
178
+ # reinstalls harlequin with the new set.
179
+ adapters: [postgres, snowflake]
180
+ # Your connection profiles. acstack writes a starter file here the first time
181
+ # you press the key, and never writes it again - what is in it is yours.
182
+ #
183
+ # default_profile = "local"
184
+ #
185
+ # [profiles.local]
186
+ # adapter = "postgres"
187
+ # conn_str = "postgres://localhost:5432/postgres"
188
+ #
189
+ # A snowflake profile names an account, user, role, warehouse, database and
190
+ # schema, or a single connection_name pointing at a connection in
191
+ # ~/.snowflake/connections.toml. Prefer the second: it keeps the secret out
192
+ # of this file.
193
+ config_path: "~/.config/acstack/harlequin.toml"
194
+
195
+ pi:
196
+ # Off by default, because it writes into pi's own config in ~/.pi/agent.
197
+ # pi is a coding agent in the terminal. Turning this on installs it with npm
198
+ # if it is missing, then installs ACStack into it, pinned to this plugin's
199
+ # version, with pi-subagents, which ACStack needs to launch a subagent.
200
+ #
201
+ # It also writes one line into pi's context file in ~/.pi/agent (AGENTS.md
202
+ # by default) that lets pi delegate when an ACStack skill says to.
203
+ # It sets "toolActivation": "eager" in
204
+ # ~/.pi/agent/extensions/subagent/config.json. It binds
205
+ # "app.message.dequeue" to "ctrl+enter" in ~/.pi/agent/keybindings.json.
206
+ # These changes sit beside your own settings, which stay.
207
+ #
208
+ # Logging in is yours: run pi, then /login.
209
+ enabled: false
210
+ # pi-compact-tools, which shortens how pi draws each tool call.
211
+ compact_tools: true
212
+ # billion-context, which adds context compression to pi. When bili is on
213
+ # PATH, apply also runs bili's native plugin setup for pi. Claude Code and
214
+ # Codex stay off bili.
215
+ billion_context: true
216
+ # Pi's question tool. rpiv-ask-user-question is used because it can show
217
+ # markdown, code and artifact previews beside a decision, accepts freeform
218
+ # answers and notes, and does not require a full-event telemetry env var.
219
+ question_tool: true
220
+ # rpiv-todo, which adds a todo tool to pi.
221
+ todo: true
222
+ # pi-plugin-manager, which adds pi plugin management commands.
223
+ plugin_manager: true
224
+ # pi-web-access, which adds web search, URL fetching, GitHub repo cloning,
225
+ # PDF extraction and media understanding tools to pi. This is separate from
226
+ # pi.settings.install_telemetry, which controls pi's anonymous install and
227
+ # update reporting and stays false in this example.
228
+ web_access: true
229
+ # pi-lens, which adds diagnostics, code-intelligence tools and local
230
+ # observability logs to pi. Off by default because it is a heavier dev-tool
231
+ # layer and may download helper tools when pi-lens needs them.
232
+ lens: false
233
+ # pi-slash-anywhere, which lets slash commands run from any prompt.
234
+ slash_anywhere: true
235
+ # agentic-retro's pi extension, which queues each finished pi session for a
236
+ # retro. Needs the agentic-retro plugin installed first, and apply says so
237
+ # when it is not.
238
+ agentic_retro: true
239
+ # Written into ~/.pi/agent/settings.json. Empty leaves pi's own value alone.
240
+ # Every other key in that file stays as you set it, and disable puts back
241
+ # any value of yours that apply replaced.
242
+ settings:
243
+ # Hide the model's thinking in the transcript.
244
+ hide_thinking_block: true
245
+ # How pi draws a mermaid diagram: "off", "final", or "streaming".
246
+ # Quote it, because YAML reads a bare off as false.
247
+ mermaid: streaming
248
+ # pi's anonymous install and update reporting. Kept off by this setup.
249
+ install_telemetry: false
250
+ # A pi theme name. "system" takes its colours from the terminal.
251
+ theme: system
252
+
253
+ neovim:
254
+ # The one component that is off by default, because it writes into a neovim
255
+ # config you built yourself. Every other one owns its files outright.
256
+ enabled: false
257
+ # LazyVim marks an unsaved buffer by recolouring the filename and nothing
258
+ # else, so a glance at the statusline cannot tell you a buffer is modified.
259
+ # Turning this on writes ~/.config/nvim/lua/plugins/acstack.lua, a whole
260
+ # file of acstack's own, which appends a dot to the statusline for a
261
+ # modified buffer, and makes copy work over SSH (osc52_clipboard below).
262
+ # It never edits a file you wrote, and
263
+ # `setup.sh disable neovim` deletes it again.
264
+ # Skipped on a machine with no LazyVim config, because both parts rely on
265
+ # LazyVim.
266
+ #
267
+ # The colour of the dot. The default is the catppuccin red.
268
+ unsaved_dot_color: "#f38ba8"
269
+ # Over SSH, :%y+ fails because neovim finds no clipboard. This sends every
270
+ # copy to the machine you sit at over OSC 52. Section 3.5 of
271
+ # docs/terminal-workspace.md covers paste. Left out when a file of yours
272
+ # already sets vim.g.clipboard.
273
+ osc52_clipboard: true
274
+
275
+ yazi:
276
+ enabled: true
277
+ # A file manager. Markdown renders live in the preview pane through
278
+ # acstack-md, so a document draws as the cursor moves over it. Enter opens
279
+ # the same renderer full width, where more of a diagram fits.
280
+ # A popup, leaving the tab layout alone.
281
+ popup_key: "alt+y"
282
+ # Opens a real tab instead.
283
+ tab_key: "alt+shift+y"
284
+ # Opens the hovered file in $EDITOR. tuicr puts the editor on the same key
285
+ # in both its panels, so one key means edit across the workspace.
286
+ edit_key: "e"
287
+ # Sends the hovered file to your own browser.
288
+ browser_key: "b"
289
+ # Previews markdown as source, with line numbers down the left.
290
+ line_numbers_key: "#"
291
+ # Sends the hovered file to the machine you sit at, over Tailscale Taildrop.
292
+ # Set it to that machine's Tailscale node name, as `tailscale status` on this
293
+ # machine spells it. Empty writes no key at all.
294
+ # Both machines must sit on one tailnet under one Tailscale user account,
295
+ # because Taildrop refuses to cross either boundary.
296
+ download_target: ""
297
+ # The key that sends it. Only read when download_target is set.
298
+ download_key: "D"
299
+ show_hidden: true
300
+
301
+ markdown:
302
+ enabled: true
303
+ # This component installs three tools: acstack-md, mermaid-ascii and glow.
304
+ # acstack-md draws a markdown file in the terminal, calling glow for the
305
+ # prose and mermaid-ascii for the diagrams. A mermaid diagram is a diagram
306
+ # written as text in the file itself. acstack-md can also serve the same
307
+ # file to a real browser, for the diagrams worth studying.
308
+ #
309
+ # Text rather than inline images is deliberate. herdr emits the kitty
310
+ # graphics protocol, and no terminal reachable over SSH from Windows or
311
+ # Android renders it, so an image never arrives. e-ink panels also render
312
+ # text far better than bitmaps. The fast refresh modes an interactive
313
+ # terminal needs are the ones that drop the greyscale depth an image needs.
314
+ #
315
+ # acstack-md --serve takes its port from $ACSTACK_MD_PORT and falls back to
316
+ # 6419. Forward whichever port you serve on, or the link --serve prints is
317
+ # unreachable from your laptop.
318
+
319
+ remote:
320
+ enabled: true
321
+ # Copy and open, on a machine you reach over SSH.
322
+ #
323
+ # The dev machine – the remote machine your agents run on, a virtual machine
324
+ # in this setup – has no display and no clipboard. Anything that shells out
325
+ # to xclip fails, and xdg-open exits 1. Two programs close the gap.
326
+ # acstack-clip sends text to your own terminal's clipboard over OSC 52, an
327
+ # escape sequence a terminal reads as "put this text on the clipboard of the
328
+ # machine I am running on". acstack-open finds whatever browser is actually
329
+ # reachable.
330
+ #
331
+ # Install an xclip stand-in backed by acstack-clip. Programs that only know
332
+ # how to shell out – gh-dash's y and Y among them – then copy correctly.
333
+ # Skipped when the machine has a real xclip or a display of its own.
334
+ clipboard_shim: true