acstack 0.49.0 → 0.49.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 (34) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/assets/terminal-workspace/helpers/acstack-bridge +185 -0
  3. package/assets/terminal-workspace/helpers/acstack-clip +88 -0
  4. package/assets/terminal-workspace/helpers/acstack-dash +25 -0
  5. package/assets/terminal-workspace/helpers/acstack-db +86 -0
  6. package/assets/terminal-workspace/helpers/acstack-diff +268 -0
  7. package/assets/terminal-workspace/helpers/acstack-edit +25 -0
  8. package/assets/terminal-workspace/helpers/acstack-gh +47 -0
  9. package/assets/terminal-workspace/helpers/acstack-herdr-renumber +93 -0
  10. package/assets/terminal-workspace/helpers/acstack-herdr-tab +49 -0
  11. package/assets/terminal-workspace/helpers/acstack-md +401 -0
  12. package/assets/terminal-workspace/helpers/acstack-md-lib +219 -0
  13. package/assets/terminal-workspace/helpers/acstack-open +217 -0
  14. package/assets/terminal-workspace/helpers/acstack-popup +13 -0
  15. package/assets/terminal-workspace/helpers/acstack-view +33 -0
  16. package/assets/terminal-workspace/helpers/xclip +31 -0
  17. package/assets/terminal-workspace/lib/apply-ghdash.sh +78 -0
  18. package/assets/terminal-workspace/lib/apply-herdr-numbers.sh +49 -0
  19. package/assets/terminal-workspace/lib/apply-herdr.sh +308 -0
  20. package/assets/terminal-workspace/lib/apply-tools.sh +293 -0
  21. package/assets/terminal-workspace/lib/apply-tuicr.sh +133 -0
  22. package/assets/terminal-workspace/lib/apply-yazi.sh +389 -0
  23. package/assets/terminal-workspace/lib/commands.sh +296 -0
  24. package/assets/terminal-workspace/lib/core.sh +184 -0
  25. package/assets/terminal-workspace/lib/helper-install.sh +35 -0
  26. package/assets/terminal-workspace/lib/pi-packages.sh +331 -0
  27. package/assets/terminal-workspace/lib/pi-state.sh +479 -0
  28. package/assets/terminal-workspace/lib/versions.sh +254 -0
  29. package/assets/terminal-workspace/setup.sh +24 -4637
  30. package/cli/package.json +1 -1
  31. package/package.json +1 -1
  32. package/skills/handoff/SKILL.md +14 -14
  33. package/skills/handoff/launch-herdr.md +15 -4
  34. package/skills/implement/SKILL.md +5 -0
@@ -0,0 +1,331 @@
1
+ acstack_version() {
2
+ json_field "$SCRIPT_DIR/../../.claude-plugin/plugin.json" version
3
+ }
4
+
5
+ # Installs one pi package, or moves its pin. Ownership is taken only when the
6
+ # package was absent: one the user installed stays theirs, pin and all.
7
+ pi_add() { # pi_add <source> <identity>
8
+ local cur
9
+ cur=$(pi_state pkg "$2")
10
+ if [ -n "$cur" ]; then
11
+ # Unpinned, presence is enough: a pin the user chose stays.
12
+ case "$1" in "$2"@*) ;; *) say "pi: $2 already installed"; return 0 ;; esac
13
+ [ "$cur" = "$1" ] && { say "pi: $1 already installed"; return 0; }
14
+ fi
15
+ if ! pi install "$1" >/dev/null 2>&1; then
16
+ say "pi: install of $1 failed, run: pi install $1"
17
+ return 1
18
+ fi
19
+ if [ -n "$cur" ]; then
20
+ pi_state pin "$2" "$cur" "$1"
21
+ say "pi: $cur moved to $1"
22
+ else
23
+ pi_state own pkg "$2"
24
+ say "pi: $1 installed"
25
+ fi
26
+ }
27
+
28
+ # Puts back a pin apply moved, while the pin is still the one apply set.
29
+ pi_unpin() { # pi_unpin <identity>
30
+ local rec prior ours cur
31
+ rec=$(pi_state pin-get "$1") || return 0
32
+ prior="${rec%%$'\t'*}" ours="${rec#*$'\t'}"
33
+ cur=$(pi_state pkg "$1")
34
+ if [ "$cur" = "$ours" ]; then
35
+ have pi || { say "pi: not on PATH, so $cur stays pinned"; return 0; }
36
+ pi install "$prior" >/dev/null 2>&1 \
37
+ || { say "pi: could not put back $prior, run: pi install $prior"; return 0; }
38
+ say "pi: $cur put back to $prior"
39
+ fi
40
+ pi_state pin-forget "$1"
41
+ }
42
+
43
+ # Removes a package this setup installed. One the user installed is left.
44
+ pi_drop() { # pi_drop <identity>
45
+ local cur
46
+ pi_state owns pkg "$1" || return 0
47
+ cur=$(pi_state pkg "$1")
48
+ if [ -n "$cur" ]; then
49
+ have pi || { say "pi: not on PATH, so $cur stays installed"; return 0; }
50
+ pi remove "$cur" >/dev/null 2>&1 || { say "pi: remove of $cur failed, run: pi remove $cur"; return 0; }
51
+ say "pi: $cur removed"
52
+ fi
53
+ pi_state disown pkg "$1"
54
+ }
55
+
56
+ pi_npm_package() { # pi_npm_package <name> -> npm package identity, or nothing
57
+ version_table | awk -F'\t' -v n="$1" '$1==n && $2=="pi" && $3=="npm"{print "npm:" $4; found=1; exit} END{exit !found}'
58
+ }
59
+
60
+ # A package pi holds at a fixed version, such as npm:pi-subagents@1.0.0.
61
+ pi_npm_pin() { # pi_npm_pin <name>
62
+ local pkg src
63
+ pkg=$(pi_npm_package "$1") || return 1
64
+ src=$(pi_state pkg "$pkg") || return 1
65
+ [ "$src" != "$pkg" ]
66
+ }
67
+
68
+ pi_suggest_todo_discipline() {
69
+ local needle="When using a todo list, keep it current as work completes."
70
+ grep -qsF "$needle" "$PI_DIR"/AGENTS.override.md "$PI_DIR"/AGENTS.md "$PI_DIR"/AGENTS.MD \
71
+ "$PI_DIR"/CLAUDE.md "$PI_DIR"/CLAUDE.MD && return 0
72
+ say "pi: todo tool installed. For stricter todo discipline, ask the agent"
73
+ say "pi: running this setup to hand-edit $PI_DIR/AGENTS.md and add:"
74
+ say "pi: $needle Before reporting a task as done, check the todo list"
75
+ say "pi: and mark every completed step completed. Do not leave stale"
76
+ say "pi: in-progress or pending todos after a commit, PR, merge or workflow gate."
77
+ }
78
+
79
+ # The pin apply would move to, or nothing when the installed one is current
80
+ # or newer. A newer pin is one the user chose, so it is never moved back.
81
+ pi_acstack_behind() {
82
+ local cur ver pin
83
+ ver=$(acstack_version); [ -n "$ver" ] || return 0
84
+ cur=$(pi_state pkg "$PI_ACSTACK") || return 0
85
+ case "$cur" in "$PI_ACSTACK"@*) pin="${cur##*@}" ;; *) return 0 ;; esac
86
+ [ "$pin" = "$ver" ] && return 0
87
+ [ "$(printf '%s\n%s\n' "$pin" "$ver" | sort -V | tail -1)" = "$ver" ] || return 0
88
+ printf '%s\t%s' "$pin" "$ver"
89
+ }
90
+
91
+ bili_bin() {
92
+ if [ -x "$PI_DIR/npm/node_modules/.bin/bili" ]; then
93
+ printf '%s' "$PI_DIR/npm/node_modules/.bin/bili"
94
+ elif have bili; then
95
+ command -v bili
96
+ else
97
+ return 1
98
+ fi
99
+ }
100
+
101
+ bili_status() { # bili_status <bin> <client> -> installed|absent|unknown
102
+ local line
103
+ line=$("$1" plugin list 2>/dev/null | awk -v c="$2" '$1==c{print; exit}') || true
104
+ [ -n "$line" ] || { echo unknown; return 0; }
105
+ case "$line" in *"not installed"*) echo absent ;; *) echo installed ;; esac
106
+ }
107
+
108
+ bili_client() { # bili_client install|remove <client>
109
+ local op="$1" client="$2" bin marker status
110
+ marker="bili:$client"
111
+ if [ "$op" = "install" ]; then
112
+ [ "$client" = "pi" ] || have "$client" || return 0
113
+ pi_state owns file "$marker" && return 0
114
+ if bin=$(bili_bin); then
115
+ status=$(bili_status "$bin" "$client")
116
+ if [ "$status" = "installed" ]; then
117
+ say "pi: billion-context already enabled for $client"
118
+ return 0
119
+ fi
120
+ if "$bin" plugin install "$client" >/dev/null 2>&1; then
121
+ pi_state own file "$marker"
122
+ say "pi: billion-context enabled for $client"
123
+ else
124
+ say "pi: billion-context $client setup failed, run: bili plugin install $client"
125
+ fi
126
+ else
127
+ say "pi: bili command not found, so billion-context $client setup was skipped"
128
+ fi
129
+ elif pi_state owns file "$marker"; then
130
+ if bin=$(bili_bin); then
131
+ if "$bin" plugin remove "$client" >/dev/null 2>&1; then
132
+ pi_state disown file "$marker"
133
+ say "pi: billion-context disabled for $client"
134
+ else
135
+ say "pi: billion-context $client removal failed, run: bili plugin remove $client"
136
+ fi
137
+ else
138
+ say "pi: bili command not found, so billion-context $client setup was left installed"
139
+ fi
140
+ fi
141
+ }
142
+
143
+ apply_pi() {
144
+ find_pi
145
+ if ! have pi; then
146
+ have npm || {
147
+ say "npm not installed, skipping pi"
148
+ say "install Node.js, then run apply again"
149
+ return 0
150
+ }
151
+ npm install -g @earendil-works/pi-coding-agent >/dev/null 2>&1 \
152
+ || { say "pi install failed, run: npm install -g @earendil-works/pi-coding-agent"; return 0; }
153
+ have pi || { say "pi installed, but npm's global bin is not on PATH"; return 0; }
154
+ say "pi installed"
155
+ fi
156
+ # pi rewrites settings.json on every install. Handing it a file it cannot
157
+ # parse risks the user's settings, so concede the whole component instead.
158
+ if ! pi_state parses "$PI_SETTINGS"; then
159
+ say "pi: $PI_SETTINGS does not parse, so nothing was installed into pi"
160
+ say "fix that file, then run apply again"
161
+ return 0
162
+ fi
163
+
164
+ patch_pi_win32_paste
165
+
166
+ # ACStack cannot launch a subagent on pi without pi-subagents.
167
+ pi_add npm:pi-subagents npm:pi-subagents
168
+ if [ "$(cfg_get pi.todo true)" = "true" ]; then
169
+ pi_add npm:@juicesharp/rpiv-todo npm:@juicesharp/rpiv-todo
170
+ pi_suggest_todo_discipline
171
+ else
172
+ pi_drop npm:@juicesharp/rpiv-todo
173
+ fi
174
+ if [ "$(cfg_get pi.plugin_manager true)" = "true" ]; then
175
+ pi_add npm:pi-plugin-manager npm:pi-plugin-manager
176
+ else
177
+ pi_drop npm:pi-plugin-manager
178
+ fi
179
+ # bili's proxy dies with the session that started it, cutting off every
180
+ # other Claude Code or Codex session. Keep it in pi; undo older installs.
181
+ bili_client remove codex
182
+ bili_client remove claude
183
+ if [ "$(cfg_get pi.billion_context true)" = "true" ]; then
184
+ pi_state merge "$BILLION_CONFIG" billion '{"pi.subagents": false}'
185
+ if [ "$(json_field "$BILLION_CONFIG" pi.subagents)" = "False" ]; then
186
+ pi_add "$PI_BILLION" "$PI_BILLION"
187
+ bili_client install pi
188
+ else
189
+ say "pi: billion-context acp_delegate could not be disabled, so billion-context setup was skipped"
190
+ fi
191
+ else
192
+ bili_client remove pi
193
+ pi_state merge "$BILLION_CONFIG" billion '{"pi.subagents": null}'
194
+ pi_drop "$PI_BILLION"
195
+ fi
196
+ if [ "$(cfg_get pi.question_tool true)" = "true" ]; then
197
+ pi_add npm:@juicesharp/rpiv-ask-user-question npm:@juicesharp/rpiv-ask-user-question
198
+ else
199
+ pi_drop npm:@juicesharp/rpiv-ask-user-question
200
+ fi
201
+ if [ "$(cfg_get pi.web_access true)" = "true" ]; then
202
+ pi_add npm:pi-web-access npm:pi-web-access
203
+ else
204
+ pi_drop npm:pi-web-access
205
+ fi
206
+ if [ "$(cfg_get pi.lens false)" = "true" ]; then
207
+ pi_add npm:pi-lens npm:pi-lens
208
+ else
209
+ pi_drop npm:pi-lens
210
+ fi
211
+ if [ "$(cfg_get pi.slash_anywhere true)" = "true" ]; then
212
+ pi_add "$PI_SLASH_ANYWHERE" "$PI_SLASH_ANYWHERE"
213
+ else
214
+ pi_drop "$PI_SLASH_ANYWHERE"
215
+ fi
216
+ local old
217
+ if old=$(pi_state pkg "$PI_ACSTACK_GIT"); then
218
+ say "pi: $old serves old ACStack skills, run: pi remove $old"
219
+ fi
220
+ local ver cur; ver=$(acstack_version)
221
+ if [ -n "$ver" ]; then
222
+ if cur=$(pi_state pkg "$PI_ACSTACK") && [ -z "$(pi_acstack_behind)" ]; then
223
+ say "pi: $cur already installed"
224
+ else
225
+ pi_add "$PI_ACSTACK@$ver" "$PI_ACSTACK"
226
+ fi
227
+ else
228
+ say "pi: no plugin version beside this script, so ACStack was not installed into pi"
229
+ fi
230
+ if [ "$(cfg_get pi.compact_tools true)" = "true" ]; then
231
+ pi_add npm:pi-compact-tools npm:pi-compact-tools
232
+ else
233
+ pi_drop npm:pi-compact-tools
234
+ fi
235
+ [ "$(cfg_get pi.settings.theme alex-mid-grey)" = "alex-mid-grey" ] && pi_write_theme
236
+ if [ "$(cfg_get pi.zentui true)" = "true" ]; then
237
+ pi_add "$PI_ZENTUI" "$PI_ZENTUI"
238
+ pi_state merge "$PI_ZENTUI_CFG" zentui "$(pi_zentui_wanted)"
239
+ else
240
+ pi_state merge "$PI_ZENTUI_CFG" zentui "$(pi_zentui_release)"
241
+ pi_drop "$PI_ZENTUI"
242
+ fi
243
+ if [ "$(cfg_get pi.hide_lens_lsp_status true)" = "true" ]; then
244
+ pi_state merge "$PI_LENS_CFG" pi_lens '{"ui.hideLspStatus": true, "widget.visible": false}'
245
+ else
246
+ pi_state merge "$PI_LENS_CFG" pi_lens '{"ui.hideLspStatus": null, "widget.visible": null}'
247
+ fi
248
+ apply_lens_extra_tools
249
+
250
+ # agentic-retro writes its own extension file. This setup only asks it to,
251
+ # and owns the file only when it was not there before.
252
+ if [ "$(cfg_get pi.agentic_retro true)" = "true" ]; then
253
+ if have agentic-retro; then
254
+ local fresh=0; [ -e "$PI_RETRO_EXT" ] || fresh=1
255
+ if agentic-retro install-pi >/dev/null 2>&1; then
256
+ [ "$fresh" = "1" ] && pi_state own file "$PI_RETRO_EXT"
257
+ say "pi: agentic-retro extension written"
258
+ else
259
+ say "pi: agentic-retro install-pi failed, run it yourself"
260
+ fi
261
+ else
262
+ say "pi: agentic-retro not on PATH, so its pi extension was skipped"
263
+ say "install the agentic-retro plugin first, then run apply again"
264
+ fi
265
+ elif pi_state owns file "$PI_RETRO_EXT"; then
266
+ rm -f "$PI_RETRO_EXT"
267
+ pi_state disown file "$PI_RETRO_EXT"
268
+ say "pi: agentic-retro extension removed"
269
+ fi
270
+
271
+ pi_state merge "$PI_SETTINGS" settings "$(pi_settings_wanted)"
272
+ pi_state merge "$PI_KEYBINDINGS" keybindings '{"app.message.dequeue": "ctrl+enter"}'
273
+ pi_state merge "$PI_SUBAGENT_CFG" subagent '{"toolActivation": "eager"}'
274
+ pi_state md write
275
+ patch_pi_win32_paste
276
+
277
+ # Logging in is interactive and stores a credential, so it stays the user's.
278
+ [ -f "$PI_DIR/auth.json" ] || say "pi: not logged in yet. Run pi, then /login"
279
+ return 0
280
+ }
281
+
282
+ # disable pi and uninstall both come here. It undoes what apply_pi recorded
283
+ # and leaves the pi binary, its logins and its sessions alone.
284
+ remove_pi() {
285
+ find_pi
286
+ if pi_state parses "$PI_SETTINGS"; then
287
+ pi_unpin "$PI_ACSTACK"
288
+ pi_drop "$PI_ACSTACK"
289
+ bili_client remove codex
290
+ bili_client remove claude
291
+ bili_client remove pi
292
+ pi_drop "$PI_BILLION"
293
+ pi_drop npm:pi-plugin-manager
294
+ pi_drop npm:@juicesharp/rpiv-todo
295
+ pi_drop npm:pi-compact-tools
296
+ pi_drop "$PI_ZENTUI"
297
+ pi_drop npm:@juicesharp/rpiv-ask-user-question
298
+ pi_drop npm:pi-web-access
299
+ pi_drop npm:pi-lens
300
+ pi_drop "$PI_SLASH_ANYWHERE"
301
+ pi_drop npm:pi-subagents
302
+ else
303
+ say "pi: $PI_SETTINGS does not parse, so its packages were left installed"
304
+ fi
305
+ pi_state merge "$BILLION_CONFIG" billion '{"pi.subagents": null}'
306
+ if pi_state owns file "$PI_RETRO_EXT"; then
307
+ rm -f "$PI_RETRO_EXT"
308
+ pi_state disown file "$PI_RETRO_EXT"
309
+ say "pi: agentic-retro extension removed"
310
+ fi
311
+ pi_state merge "$PI_SETTINGS" settings \
312
+ '{"hideThinkingBlock": null, "markdown.mermaid": null, "enableInstallTelemetry": null, "theme": null}'
313
+ pi_state merge "$PI_ZENTUI_CFG" zentui "$(pi_zentui_release)"
314
+ pi_state merge "$PI_LENS_CFG" pi_lens '{"ui.hideLspStatus": null, "widget.visible": null}'
315
+ if pi_state owns file "$PI_THEME"; then
316
+ rm -f "$PI_THEME"
317
+ pi_state disown file "$PI_THEME"
318
+ say "pi: ACStack theme removed"
319
+ fi
320
+ pi_state merge "$PI_KEYBINDINGS" keybindings '{"app.message.dequeue": null}'
321
+ pi_state merge "$PI_SUBAGENT_CFG" subagent '{"toolActivation": null}'
322
+ pi_state md strip
323
+ return 0
324
+ }
325
+
326
+ # Every tool with a version worth comparing.
327
+ # name | component key | source | reference | release asset match
328
+ # mermaid-ascii is deliberately absent: it has no version flag at all, so
329
+ # nothing here can read what is installed. `upgrade mermaid-ascii` still works,
330
+ # because upgrade_one carries an arm of its own for it. A tool that is in
331
+ # neither place is one upgrade cannot reach, whatever the docs promise.