acstack 0.48.0 → 0.48.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "acstack",
3
3
  "description": "Agentic coding stack where planning grows only as far as needed: a map of decisions, tasks, checks, and handoffs behind a pluggable tracker.",
4
- "version": "0.48.0",
4
+ "version": "0.48.1",
5
5
  "author": {
6
6
  "name": "ac8318740"
7
7
  },
@@ -236,6 +236,19 @@ pi:
236
236
  # retro. Needs the agentic-retro plugin installed first, and apply says so
237
237
  # when it is not.
238
238
  agentic_retro: true
239
+ # Zentui supplies the accent-rail editor and Starship footer. The footer
240
+ # format keeps model/provider/thinking visible on the right, including in
241
+ # compact mode.
242
+ zentui: true
243
+ # Hide pi-lens's LSP status footer and diagnostics widget rows.
244
+ hide_lens_lsp_status: true
245
+ # Extra pi-lens tools installed onto PATH for ecosystems used across repos.
246
+ # These are machine-wide, not project dependencies.
247
+ lens_extra_tools:
248
+ python: true # black, mypy, pyright
249
+ docker: true # hadolint, Dockerfile language server
250
+ github_actions: true # actionlint, zizmor
251
+ terraform: true # terraform-ls, tflint, terragrunt
239
252
  # Written into ~/.pi/agent/settings.json. Empty leaves pi's own value alone.
240
253
  # Every other key in that file stays as you set it, and disable puts back
241
254
  # any value of yours that apply replaced.
@@ -247,8 +260,9 @@ pi:
247
260
  mermaid: streaming
248
261
  # pi's anonymous install and update reporting. Kept off by this setup.
249
262
  install_telemetry: false
250
- # A pi theme name. "system" takes its colours from the terminal.
251
- theme: system
263
+ # A pi theme name. alex-mid-grey is written by this setup and shades
264
+ # user messages.
265
+ theme: alex-mid-grey
252
266
 
253
267
  neovim:
254
268
  # The one component that is off by default, because it writes into a neovim
@@ -38,7 +38,13 @@ PI_SUBAGENT_CFG="$PI_DIR/extensions/subagent/config.json"
38
38
  PI_RETRO_EXT="${PI_HOME:-$HOME/.pi}/agent/extensions/agentic-retro.ts"
39
39
  PI_ACSTACK="npm:acstack"
40
40
  PI_BILLION="npm:billion-context"
41
+ BILLION_CONFIG="$HOME/.config/billion-context/billion-context.json"
41
42
  PI_SLASH_ANYWHERE="npm:pi-slash-anywhere"
43
+ PI_ZENTUI="npm:pi-zentui"
44
+ PI_ZENTUI_CFG="$PI_DIR/zentui.json"
45
+ PI_THEME_DIR="$PI_DIR/themes"
46
+ PI_THEME="$PI_THEME_DIR/alex-mid-grey.json"
47
+ PI_LENS_CFG="$HOME/.pi-lens/config.json"
42
48
  # A line starting with # is a heading in markdown, and the model reads it. An
43
49
  # HTML comment carries the same marker and renders as nothing.
44
50
  PI_MD_BEGIN="<!-- >>> acstack terminal-workspace >>> -->"
@@ -180,6 +186,57 @@ PY
180
186
  say "$name installed to $BIN/$name"
181
187
  }
182
188
 
189
+ install_plain_github_binary() { # install_plain_github_binary <name> <repo> <asset-match>
190
+ local name="$1" repo="$2" match="$3"
191
+ if [ "${INSTALL_FORCE:-0}" != "1" ] && have "$name"; then
192
+ say "$name already installed"; return 0
193
+ fi
194
+ local json url tmp
195
+ json=$(curl -sSf "https://api.github.com/repos/$repo/releases/latest" 2>/dev/null)
196
+ [ -z "$json" ] && { say "$name: could not reach the GitHub release API"; return 1; }
197
+ url=$(MAPFLOW_JSON="$json" py "$match" <<'PY'
198
+ import json, os, sys
199
+ try:
200
+ d = json.loads(os.environ["MAPFLOW_JSON"])
201
+ except ValueError:
202
+ raise SystemExit
203
+ for a in d.get("assets", []):
204
+ if sys.argv[1] in a["name"]:
205
+ print(a["browser_download_url"]); break
206
+ PY
207
+ )
208
+ [ -z "$url" ] && { say "$name: no matching release asset, install manually"; return 1; }
209
+ tmp=$(mktemp)
210
+ if ! curl -fsSL "$url" -o "$tmp"; then
211
+ rm -f "$tmp"; say "$name: download failed"; return 1
212
+ fi
213
+ mkdir -p "$BIN"; cp "$tmp" "$BIN/$name"; chmod +x "$BIN/$name"
214
+ rm -f "$tmp"
215
+ say "$name installed to $BIN/$name"
216
+ }
217
+
218
+ install_npm_tool() { # install_npm_tool <command> <package>
219
+ local command="$1" package="$2"
220
+ if have "$command"; then say "$command already installed"; return 0; fi
221
+ have npm || { say "$command: npm not installed, skipping"; return 0; }
222
+ if npm install -g "$package" >/dev/null 2>&1; then
223
+ say "$command installed with npm"
224
+ else
225
+ say "$command install failed, run: npm install -g $package"
226
+ fi
227
+ }
228
+
229
+ install_uv_tool() { # install_uv_tool <command> <package>
230
+ local command="$1" package="$2"
231
+ if have "$command"; then say "$command already installed"; return 0; fi
232
+ have uv || { say "$command: uv not installed, skipping"; return 0; }
233
+ if uv tool install "$package" >/dev/null 2>&1; then
234
+ say "$command installed with uv"
235
+ else
236
+ say "$command install failed, run: uv tool install $package"
237
+ fi
238
+ }
239
+
183
240
  write_helpers() {
184
241
  mkdir -p "$BIN"
185
242
  # Helpers this script used to write and no longer does. Upgrading otherwise
@@ -3184,6 +3241,30 @@ apply_harlequin() {
3184
3241
  || say "harlequin install failed, run: uv tool install harlequin"
3185
3242
  }
3186
3243
 
3244
+ apply_lens_extra_tools() {
3245
+ # A small machine-wide pi-lens toolbelt for common project ecosystems.
3246
+ # Pi-lens can fall back to npx or prompt for some tools, but putting these
3247
+ # on PATH makes them available in any repo without per-project setup.
3248
+ if [ "$(cfg_get pi.lens_extra_tools.python false)" = "true" ]; then
3249
+ install_uv_tool black black
3250
+ install_uv_tool mypy mypy
3251
+ install_npm_tool pyright pyright
3252
+ fi
3253
+ if [ "$(cfg_get pi.lens_extra_tools.docker false)" = "true" ]; then
3254
+ install_plain_github_binary hadolint hadolint/hadolint Linux-x86_64
3255
+ install_npm_tool docker-langserver dockerfile-language-server-nodejs
3256
+ fi
3257
+ if [ "$(cfg_get pi.lens_extra_tools.github_actions false)" = "true" ]; then
3258
+ install_binary actionlint rhysd/actionlint linux_amd64
3259
+ install_uv_tool zizmor zizmor
3260
+ fi
3261
+ if [ "$(cfg_get pi.lens_extra_tools.terraform false)" = "true" ]; then
3262
+ install_binary terraform-ls hashicorp/terraform-ls linux_amd64
3263
+ install_binary tflint terraform-linters/tflint linux_amd64
3264
+ install_plain_github_binary terragrunt gruntwork-io/terragrunt linux_amd64
3265
+ fi
3266
+ }
3267
+
3187
3268
  apply_ghdash() {
3188
3269
  have gh || { say "gh not installed, skipping dashboard"; return 0; }
3189
3270
  gh extension list 2>/dev/null | grep -q gh-dash || gh extension install dlvhdr/gh-dash >/dev/null 2>&1
@@ -3495,7 +3576,7 @@ s = ((c.get("pi") or {}).get("settings") or {})
3495
3576
  KEYS = {"hide_thinking_block": ("hideThinkingBlock", True, (True, False)),
3496
3577
  "mermaid": ("markdown.mermaid", "streaming", ("off", "final", "streaming")),
3497
3578
  "install_telemetry": ("enableInstallTelemetry", False, (True, False)),
3498
- "theme": ("theme", "system", None)}
3579
+ "theme": ("theme", "alex-mid-grey", None)}
3499
3580
  out = {}
3500
3581
  for k, (key, default, allowed) in KEYS.items():
3501
3582
  v = s.get(k, default)
@@ -3514,6 +3595,153 @@ print(json.dumps(out))
3514
3595
  PY
3515
3596
  }
3516
3597
 
3598
+ pi_zentui_wanted() {
3599
+ cat <<'JSON'
3600
+ {
3601
+ "components.editor.enabled": true,
3602
+ "components.editor.style": "accent-rail",
3603
+ "components.editor.styles.accent-rail.transparent": false,
3604
+ "components.userMessages.enabled": false,
3605
+ "components.footer.style": "starship",
3606
+ "components.footer.modelLabel": "id",
3607
+ "components.footer.styles.starship.segments.modelInfo": true,
3608
+ "components.footer.styles.starship.extensionStatuses.defaultPlacement": "off",
3609
+ "components.extensionStatuses.defaultVisibility": "hide",
3610
+ "components.extensionStatuses.visibility.pi-lens": "hide",
3611
+ "components.extensionStatuses.visibility.pi-lens-lsp": "hide",
3612
+ "components.extensionStatuses.visibility.ponytail": "hide",
3613
+ "components.footer.styles.starship.responsive": true,
3614
+ "components.footer.styles.starship.format": "$cwd( in $session_name)( on $git_branch)( $git_status)( via $runtime)$fill$model($sep$provider)($sep$thinkingLevel)($sep$context)( $auto_compaction)($sep$tokens)( $cache_read)( $cache_write)($sep$cost)( $subscription)",
3615
+ "components.footer.styles.starship.compactFormat": "$cwd$wrap(in $session_name)$wrap(on $git_branch) $git_status$fill$model$wrap_sep($provider)$wrap_sep($thinkingLevel)$wrap_sep$context$wrap_sep$tokens"
3616
+ }
3617
+ JSON
3618
+ }
3619
+
3620
+ pi_zentui_release() {
3621
+ cat <<'JSON'
3622
+ {
3623
+ "components.editor.enabled": null,
3624
+ "components.editor.style": null,
3625
+ "components.editor.styles.accent-rail.transparent": null,
3626
+ "components.userMessages.enabled": null,
3627
+ "components.footer.style": null,
3628
+ "components.footer.modelLabel": null,
3629
+ "components.footer.styles.starship.segments.modelInfo": null,
3630
+ "components.footer.styles.starship.extensionStatuses.defaultPlacement": null,
3631
+ "components.extensionStatuses.defaultVisibility": null,
3632
+ "components.extensionStatuses.visibility.pi-lens": null,
3633
+ "components.extensionStatuses.visibility.pi-lens-lsp": null,
3634
+ "components.extensionStatuses.visibility.ponytail": null,
3635
+ "components.footer.styles.starship.responsive": null,
3636
+ "components.footer.styles.starship.format": null,
3637
+ "components.footer.styles.starship.compactFormat": null
3638
+ }
3639
+ JSON
3640
+ }
3641
+
3642
+ pi_write_theme() {
3643
+ if [ -e "$PI_THEME" ] && ! pi_state owns file "$PI_THEME"; then
3644
+ say "pi: $PI_THEME already exists, so the Mapflow theme was left alone"
3645
+ return 0
3646
+ fi
3647
+ MAPFLOW_PI_THEME="$PI_THEME" py <<'PY'
3648
+ import json, os
3649
+
3650
+ target = os.environ["MAPFLOW_PI_THEME"]
3651
+ theme = {
3652
+ "$schema": "https://pi.dev/schemas/theme.schema.json",
3653
+ "name": "alex-mid-grey",
3654
+ "vars": {
3655
+ "surfaceMuted": "#21262D",
3656
+ "border": "#30363D",
3657
+ "borderSubtle": "#21262D",
3658
+ "text": "#C9D1D9",
3659
+ "textBright": "#E6EDF3",
3660
+ "muted": "#8B949E",
3661
+ "dim": "#6E7681",
3662
+ "toolStatus": "#59616B",
3663
+ "blue": "#58A6FF",
3664
+ "blueSoft": "#79C0FF",
3665
+ "inlineCode": "#D2A8FF",
3666
+ "green": "#3FB950",
3667
+ "red": "#F85149",
3668
+ "yellow": "#E0A052",
3669
+ "orange": "#FFA657",
3670
+ "purple": "#D2A8FF",
3671
+ "toolAccent": "#95A7FF",
3672
+ "toolName": "#8B85D6",
3673
+ "toolText": "#98A1AC",
3674
+ "pink": "#FF7B72",
3675
+ "userPromptBg": "#24272e",
3676
+ },
3677
+ "colors": {
3678
+ "accent": "toolAccent",
3679
+ "border": "border",
3680
+ "borderAccent": "toolStatus",
3681
+ "borderMuted": "borderSubtle",
3682
+ "success": "green",
3683
+ "error": "red",
3684
+ "warning": "yellow",
3685
+ "muted": "muted",
3686
+ "dim": "dim",
3687
+ "text": "text",
3688
+ "thinkingText": "toolText",
3689
+ "selectedBg": "surfaceMuted",
3690
+ "searchMatchBg": "#3B2E10",
3691
+ "searchMatchText": "textBright",
3692
+ "userMessageBg": "userPromptBg",
3693
+ "userMessageText": "textBright",
3694
+ "customMessageBg": "",
3695
+ "customMessageText": "text",
3696
+ "customMessageLabel": "muted",
3697
+ "toolPendingBg": "",
3698
+ "toolSuccessBg": "",
3699
+ "toolErrorBg": "#211414",
3700
+ "toolTitle": "toolName",
3701
+ "toolOutput": "toolText",
3702
+ "scrollbarTrack": "borderSubtle",
3703
+ "scrollbarThumb": "border",
3704
+ "mdHeading": "textBright",
3705
+ "mdLink": "blue",
3706
+ "mdLinkUrl": "dim",
3707
+ "mdCode": "inlineCode",
3708
+ "mdCodeBlock": "text",
3709
+ "mdCodeBlockBorder": "#0D1117",
3710
+ "mdQuote": "toolText",
3711
+ "mdQuoteBorder": "border",
3712
+ "mdHr": "borderSubtle",
3713
+ "mdListBullet": "muted",
3714
+ "toolDiffAdded": "#45B85A",
3715
+ "toolDiffRemoved": "#E45D57",
3716
+ "toolDiffContext": "dim",
3717
+ "syntaxComment": "dim",
3718
+ "syntaxKeyword": "pink",
3719
+ "syntaxFunction": "purple",
3720
+ "syntaxVariable": "text",
3721
+ "syntaxString": "blueSoft",
3722
+ "syntaxNumber": "orange",
3723
+ "syntaxType": "orange",
3724
+ "syntaxOperator": "muted",
3725
+ "syntaxPunctuation": "muted",
3726
+ "thinkingOff": "borderSubtle",
3727
+ "thinkingMinimal": "border",
3728
+ "thinkingLow": "dim",
3729
+ "thinkingMedium": "muted",
3730
+ "thinkingHigh": "blue",
3731
+ "thinkingXhigh": "blueSoft",
3732
+ "thinkingMax": "#9FCFF8",
3733
+ "bashMode": "green",
3734
+ },
3735
+ "export": {"pageBg": "#0D1117", "cardBg": "#161B22", "infoBg": "#1C2128"},
3736
+ }
3737
+ os.makedirs(os.path.dirname(target), exist_ok=True)
3738
+ json.dump(theme, open(target, "w"), indent=2, ensure_ascii=False)
3739
+ open(target, "a").write("\n")
3740
+ PY
3741
+ pi_state own file "$PI_THEME"
3742
+ say "pi: Mapflow theme written to $PI_THEME"
3743
+ }
3744
+
3517
3745
  # npm installs pi into its global bin, which a fresh shell may not have on
3518
3746
  # PATH. Asking npm costs a process, so only when pi is not already found.
3519
3747
  find_pi() {
@@ -3762,10 +3990,16 @@ apply_pi() {
3762
3990
  bili_client remove codex
3763
3991
  bili_client remove claude
3764
3992
  if [ "$(cfg_get pi.billion_context true)" = "true" ]; then
3765
- pi_add "$PI_BILLION" "$PI_BILLION"
3766
- bili_client install pi
3993
+ pi_state merge "$BILLION_CONFIG" billion '{"pi.subagents": false}'
3994
+ if [ "$(json_field "$BILLION_CONFIG" pi.subagents)" = "False" ]; then
3995
+ pi_add "$PI_BILLION" "$PI_BILLION"
3996
+ bili_client install pi
3997
+ else
3998
+ say "pi: billion-context acp_delegate could not be disabled, so billion-context setup was skipped"
3999
+ fi
3767
4000
  else
3768
4001
  bili_client remove pi
4002
+ pi_state merge "$BILLION_CONFIG" billion '{"pi.subagents": null}'
3769
4003
  pi_drop "$PI_BILLION"
3770
4004
  fi
3771
4005
  if [ "$(cfg_get pi.question_tool true)" = "true" ]; then
@@ -3803,6 +4037,20 @@ apply_pi() {
3803
4037
  else
3804
4038
  pi_drop npm:pi-compact-tools
3805
4039
  fi
4040
+ [ "$(cfg_get pi.settings.theme alex-mid-grey)" = "alex-mid-grey" ] && pi_write_theme
4041
+ if [ "$(cfg_get pi.zentui true)" = "true" ]; then
4042
+ pi_add "$PI_ZENTUI" "$PI_ZENTUI"
4043
+ pi_state merge "$PI_ZENTUI_CFG" zentui "$(pi_zentui_wanted)"
4044
+ else
4045
+ pi_state merge "$PI_ZENTUI_CFG" zentui "$(pi_zentui_release)"
4046
+ pi_drop "$PI_ZENTUI"
4047
+ fi
4048
+ if [ "$(cfg_get pi.hide_lens_lsp_status true)" = "true" ]; then
4049
+ pi_state merge "$PI_LENS_CFG" pi_lens '{"ui.hideLspStatus": true, "widget.visible": false}'
4050
+ else
4051
+ pi_state merge "$PI_LENS_CFG" pi_lens '{"ui.hideLspStatus": null, "widget.visible": null}'
4052
+ fi
4053
+ apply_lens_extra_tools
3806
4054
 
3807
4055
  # agentic-retro writes its own extension file. This setup only asks it to,
3808
4056
  # and owns the file only when it was not there before.
@@ -3850,6 +4098,7 @@ remove_pi() {
3850
4098
  pi_drop npm:pi-plugin-manager
3851
4099
  pi_drop npm:@juicesharp/rpiv-todo
3852
4100
  pi_drop npm:pi-compact-tools
4101
+ pi_drop "$PI_ZENTUI"
3853
4102
  pi_drop npm:@juicesharp/rpiv-ask-user-question
3854
4103
  pi_drop npm:pi-web-access
3855
4104
  pi_drop npm:pi-lens
@@ -3858,6 +4107,7 @@ remove_pi() {
3858
4107
  else
3859
4108
  say "pi: $PI_SETTINGS does not parse, so its packages were left installed"
3860
4109
  fi
4110
+ pi_state merge "$BILLION_CONFIG" billion '{"pi.subagents": null}'
3861
4111
  if pi_state owns file "$PI_RETRO_EXT"; then
3862
4112
  rm -f "$PI_RETRO_EXT"
3863
4113
  pi_state disown file "$PI_RETRO_EXT"
@@ -3865,6 +4115,13 @@ remove_pi() {
3865
4115
  fi
3866
4116
  pi_state merge "$PI_SETTINGS" settings \
3867
4117
  '{"hideThinkingBlock": null, "markdown.mermaid": null, "enableInstallTelemetry": null, "theme": null}'
4118
+ pi_state merge "$PI_ZENTUI_CFG" zentui "$(pi_zentui_release)"
4119
+ pi_state merge "$PI_LENS_CFG" pi_lens '{"ui.hideLspStatus": null, "widget.visible": null}'
4120
+ if pi_state owns file "$PI_THEME"; then
4121
+ rm -f "$PI_THEME"
4122
+ pi_state disown file "$PI_THEME"
4123
+ say "pi: Mapflow theme removed"
4124
+ fi
3868
4125
  pi_state merge "$PI_KEYBINDINGS" keybindings '{"app.message.dequeue": null}'
3869
4126
  pi_state merge "$PI_SUBAGENT_CFG" subagent '{"toolActivation": null}'
3870
4127
  pi_state md strip
@@ -3899,6 +4156,12 @@ pi-web-access pi npm pi-web-access -
3899
4156
  pi-lens pi npm pi-lens -
3900
4157
  pi-slash-anywhere pi npm pi-slash-anywhere -
3901
4158
  pi-compact-tools pi npm pi-compact-tools -
4159
+ pi-zentui pi npm pi-zentui -
4160
+ actionlint pi github rhysd/actionlint linux_amd64
4161
+ hadolint pi github hadolint/hadolint Linux-x86_64
4162
+ terraform-ls pi github hashicorp/terraform-ls linux_amd64
4163
+ tflint pi github terraform-linters/tflint linux_amd64
4164
+ terragrunt pi github gruntwork-io/terragrunt linux_amd64
3902
4165
  T
3903
4166
  }
3904
4167
 
@@ -3929,8 +4192,18 @@ installed_version() { # installed_version <name> -> a bare version, or nothing
3929
4192
  harlequin) v=$(uv tool list 2>/dev/null | awk '$1=="harlequin"{print $2}') ;;
3930
4193
  # 1.1.0
3931
4194
  pi) find_pi; v=$(pi --version 2>/dev/null | awk '{print $NF}') ;;
4195
+ # actionlint version 1.7.7
4196
+ actionlint) v=$(actionlint -version 2>/dev/null | awk '{print $2}') ;;
4197
+ # Haskell Dockerfile Linter 2.12.0
4198
+ hadolint) v=$(hadolint --version 2>/dev/null | awk '{print $NF}') ;;
4199
+ # terraform-ls version 0.36.5
4200
+ terraform-ls) v=$(terraform-ls version 2>/dev/null | awk '{print $2}') ;;
4201
+ # TFLint version 0.59.1
4202
+ tflint) v=$(tflint --version 2>/dev/null | awk 'NR==1{print $3}') ;;
4203
+ # terragrunt version v0.91.2
4204
+ terragrunt) v=$(terragrunt --version 2>/dev/null | awk '{print $NF}') ;;
3932
4205
  # pi installs npm packages under its own directory, not npm's global one.
3933
- pi-subagents|pi-compact-tools|pi-web-access|pi-lens|pi-slash-anywhere|@juicesharp/rpiv-ask-user-question|billion-context|@juicesharp/rpiv-todo|pi-plugin-manager)
4206
+ pi-subagents|pi-compact-tools|pi-zentui|pi-web-access|pi-lens|pi-slash-anywhere|@juicesharp/rpiv-ask-user-question|billion-context|@juicesharp/rpiv-todo|pi-plugin-manager)
3934
4207
  v=$(json_field "$PI_DIR/npm/node_modules/$1/package.json" version) ;;
3935
4208
  esac
3936
4209
  printf '%s' "${v#v}"
@@ -4080,7 +4353,7 @@ upgrade_one() { # upgrade_one <name>
4080
4353
  else
4081
4354
  say "pi update failed"
4082
4355
  fi ;;
4083
- pi-subagents|pi-compact-tools|pi-web-access|pi-lens|pi-slash-anywhere|@juicesharp/rpiv-ask-user-question|billion-context|@juicesharp/rpiv-todo|pi-plugin-manager)
4356
+ pi-subagents|pi-compact-tools|pi-zentui|pi-web-access|pi-lens|pi-slash-anywhere|@juicesharp/rpiv-ask-user-question|billion-context|@juicesharp/rpiv-todo|pi-plugin-manager)
4084
4357
  if pi_npm_pin "$1"; then
4085
4358
  say "$1 is pinned to $(pi_state pkg "npm:$1") in pi, which pi update never moves"
4086
4359
  return 0
@@ -4097,6 +4370,10 @@ upgrade_one() { # upgrade_one <name>
4097
4370
  # here rather than behind a table lookup that will never find it.
4098
4371
  mermaid-ascii)
4099
4372
  INSTALL_FORCE=1 install_binary mermaid-ascii AlexanderGrooff/mermaid-ascii Linux_x86_64 ;;
4373
+ hadolint)
4374
+ INSTALL_FORCE=1 install_plain_github_binary hadolint hadolint/hadolint Linux-x86_64 ;;
4375
+ terragrunt)
4376
+ INSTALL_FORCE=1 install_plain_github_binary terragrunt gruntwork-io/terragrunt linux_amd64 ;;
4100
4377
  # The fork build comes from a local checkout, so a release archive would
4101
4378
  # replace it with the stock binary and silently drop the patches it carries.
4102
4379
  # outdated already skips it; naming it here has to skip it too.
@@ -4165,7 +4442,7 @@ case "$ACTION" in
4165
4442
  if have pi || [ "$(cfg_get pi.enabled false)" = "true" ]; then
4166
4443
  echo
4167
4444
  have pi && say "pi: $(installed_version pi)" || say "pi: not installed"
4168
- for id in npm:pi-subagents "$PI_ACSTACK" "$PI_BILLION" npm:@juicesharp/rpiv-todo npm:pi-plugin-manager npm:@juicesharp/rpiv-ask-user-question npm:pi-web-access npm:pi-lens "$PI_SLASH_ANYWHERE" npm:pi-compact-tools; do
4445
+ for id in npm:pi-subagents "$PI_ACSTACK" "$PI_BILLION" npm:@juicesharp/rpiv-todo npm:pi-plugin-manager npm:@juicesharp/rpiv-ask-user-question npm:pi-web-access npm:pi-lens "$PI_SLASH_ANYWHERE" npm:pi-compact-tools "$PI_ZENTUI"; do
4169
4446
  src=$(pi_state pkg "$id") && say "pi package: $src" || say "pi package: $id not installed"
4170
4447
  done
4171
4448
  [ -e "$PI_RETRO_EXT" ] && say "pi agentic-retro extension: present" \
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acstack-cli",
3
- "version": "0.48.0",
3
+ "version": "0.48.1",
4
4
  "description": "CLI for the ACStack agentic coding stack",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/docs/workflows.md CHANGED
@@ -121,7 +121,7 @@ Nodes live in a tracker. That is the storage layer, and you can swap it.
121
121
 
122
122
  - `/acstack:implement` claims `afk` work nodes from the frontier when a map exists
123
123
  - It treats the request itself as the work item when none does
124
- - Either way, parallel explorer subagents run over the relevant code before anyone writes anything
124
+ - Either way, parallel read-only codebase-search subagents run over the relevant code before anyone writes anything
125
125
  - The command resolves paths at claim time
126
126
  - A node describes behaviour
127
127
  - It can sit unstarted for weeks
@@ -55,7 +55,7 @@ ACStack runs on Claude Code, Codex, and pi. The skills name each capability once
55
55
  - **Launch a subagent** – start a child agent with its own context, by its ACStack name (`test-writer`, `task-executor`, `task-checker`, `frontend-verifier`)
56
56
  - Claude Code: the Agent tool, with `subagent_type` set to that name
57
57
  - pi: the `subagent` tool from `pi-subagents`
58
- - `Explore` names a read-only codebase-search agent: Claude Code's `Explore`, pi's `scout`
58
+ - **Read-only codebase-search subagent** – Claude Code's `Explore`, pi's `scout`
59
59
  - `debugger` names a debugging agent
60
60
  - On a host without a matching built-in agent, launch a general subagent with that brief
61
61
  - **Agent team** – parallel full sessions, each owning one scope and launching its own subagents
@@ -502,7 +502,7 @@ Launch each agent by the name below (see Host tools for how your host takes it).
502
502
  | Implement task | `task-executor` | CANNOT modify tests |
503
503
  | Verify implementation | `task-checker` | Mock skepticism, full regression, TDD isolation |
504
504
  | Verify frontend UI | `frontend-verifier` | Real browser, screenshots, non-negotiable |
505
- | Find/locate something | `Explore` | Built-in codebase search |
505
+ | Find/locate something | `Explore` / `scout` | Read-only codebase search; `scout` on pi |
506
506
  | Debug/investigate | `debugger` | Built-in debugging agent |
507
507
 
508
508
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acstack",
3
- "version": "0.48.0",
3
+ "version": "0.48.1",
4
4
  "private": false,
5
5
  "description": "Agentic coding stack with first-class Claude Code and pi support: skills, orchestrator instructions, and verification agents.",
6
6
  "license": "MIT",
@@ -41,7 +41,7 @@ Your job is to:
41
41
 
42
42
  ## Phase 1: independent exploration (passes 1-3)
43
43
 
44
- Launch **3 parallel `Explore` subagents** at once. Each gets a different exploration strategy:
44
+ Launch **3 parallel read-only codebase-search subagents** at once. Use `Explore` on Claude Code and `scout` on pi. Each gets a different exploration strategy:
45
45
 
46
46
  ### Pass 1 – entry points strategy
47
47
 
@@ -90,7 +90,7 @@ Launch a single subagent to merge the 3 independent drafts per domain. It resolv
90
90
 
91
91
  ## Phase 3: validation (passes 5-7)
92
92
 
93
- Launch 3 parallel Explore subagents to validate ALL domains independently – each checks that FRs match actual code behavior.
93
+ Launch 3 parallel read-only codebase-search subagents to validate ALL domains independently. Each checks that FRs match actual code behavior.
94
94
 
95
95
  ## Phase 4: correction merge (pass 8)
96
96
 
@@ -29,9 +29,11 @@ session.
29
29
  nothing else. Never ask a question whose answer depends on one still open.
30
30
 
31
31
  2. **Facts are your job, never the user's.** Skip a question that an
32
- environment fact would answer. Dispatch parallel `Explore` subagents, one
33
- per distinct place to look, not a fixed count. Fold what they find into
34
- the round.
32
+ environment fact would answer.
33
+
34
+ Dispatch parallel read-only codebase-search subagents, one per distinct
35
+ place to look, not a fixed count. Use `Explore` on Claude Code and `scout`
36
+ on pi. Fold what they find into the round.
35
37
 
36
38
  3. **Run it before you ask it.** Check whether running something could answer
37
39
  the question – behaviour, timing, output, or performance. If it could,
@@ -106,8 +108,8 @@ the tracker's `update` and `create` operations:
106
108
  Write the answer per the `writing` skill.
107
109
 
108
110
  2. Create a node for each new question the answer surfaced, with `answers`
109
- naming the node that surfaced it. Questions that can be stated precisely
110
- are `open`; the rest are `fog`.
111
+ naming the node that surfaced it. Mark precise questions `open`; mark the
112
+ rest `fog`.
111
113
 
112
114
  3. Invoke the `record-context` skill for each resolution – it decides whether
113
115
  the decision earns an ADR, a glossary term, both, or neither.
@@ -98,9 +98,11 @@ Claim each node as you start it:
98
98
 
99
99
  Nodes describe behaviour, not paths. **Resolve paths at claim time**.
100
100
 
101
- Before any code change, dispatch parallel explorer subagents over the
102
- relevant code. Use as many as there are distinct places to look, not a fixed
103
- count. Act on what they find.
101
+ Before any code change, dispatch parallel read-only codebase-search subagents
102
+ over the relevant code. Use `Explore` on Claude Code and `scout` on pi.
103
+
104
+ Use as many as there are distinct places to look, not a fixed count. Act on
105
+ what they find.
104
106
 
105
107
  ### 5. Execute within the claim
106
108
 
@@ -219,8 +221,9 @@ diff.
219
221
  - **The tracker is the progress record** – claim on start, resolve on pass,
220
222
  release on stall. No checkbox files, no phase fields.
221
223
 
222
- - **Explore before writing** – parallel explorer subagents over the relevant
223
- code, sized to the node, before any edit.
224
+ - **Explore before writing** – parallel read-only codebase-search subagents
225
+ over the relevant code, sized to the node, before any edit. Use `Explore` on
226
+ Claude Code and `scout` on pi.
224
227
 
225
228
  - **Resume is a query** – picking up where you left off means asking the
226
229
  tracker what is workable now, not reconstructing history. A fresh session
@@ -182,8 +182,8 @@ the list, marked `skip: <reason>`.
182
182
  - `hitl` nodes: run grilling rounds over them. A round is the whole hitl
183
183
  frontier.
184
184
  - `afk` nodes: claim and run without the user. Unlimited, and in parallel
185
- when independent. Research goes to `Explore` subagents, with the findings
186
- written into the resolution.
185
+ when independent. Research goes to read-only codebase-search subagents:
186
+ `Explore` on Claude Code and `scout` on pi.
187
187
 
188
188
  Implementation work follows `/acstack:implement`'s procedure – it owns
189
189
  the claim-execute-resolve flow, the explorer dispatch, and the TDD
@@ -73,7 +73,8 @@ If no meaningful code changes exist (only docs, config, assets), report "No code
73
73
 
74
74
  ## Step 2: DISCOVER – Learn project conventions (parallel)
75
75
 
76
- Launch **3 parallel Explore subagents** to build a dynamic understanding of the project's current conventions.
76
+ Launch **3 parallel read-only codebase-search subagents** to build a dynamic understanding of the project's current conventions.
77
+ Use `Explore` on Claude Code and `scout` on pi.
77
78
  Do NOT hardcode conventions – discover them fresh each time.
78
79
 
79
80
  **Subagent A – Living Specs & Architecture:**
@@ -177,7 +178,8 @@ Add each finding to the review as `SHOULD | PROSE`, at the `file:line` it names.
177
178
 
178
179
  ## Step 4: ADJACENT – Check surrounding code for rot
179
180
 
180
- For each file with findings from Step 3, launch an **Explore subagent** (up to 3 in parallel).
181
+ For each file with findings from Step 3, launch a **read-only codebase-search subagent**.
182
+ Use up to 3 subagents in parallel.
181
183
  Each subagent examines the rest of that file and its immediate imports for the same categories of issue.
182
184
 
183
185
  ```
@@ -39,7 +39,7 @@ If the user provided an error message or stack trace, note the key details (file
39
39
 
40
40
  **Do NOT skip this step.** Delegate exploration to subagents:
41
41
 
42
- 1. **Locate the code**: Launch an Explore subagent to find the files involved. Read only a file you can already name.
42
+ 1. **Locate the code**: Launch a read-only codebase-search subagent to find the files involved. Use `Explore` on Claude Code and `scout` on pi. Read only a file you can already name.
43
43
 
44
44
  2. **Read the integration points**: Have the subagent read the broken function's callers and callees, not just the function. Understand:
45
45
  - What calls this code? (upstream)
@@ -50,9 +50,11 @@ frontend and has not turned browser verification on. Turn it on:
50
50
  read it. Without it, every path through spec sync skips in silence, and the
51
51
  living specs never update.
52
52
 
53
- Launch an **Explore subagent** over `directories.source` to propose domains. Ask
54
- it for the top-level functional areas. Ask for a kebab-case name, the paths that
55
- belong to it, and one line saying what it owns.
53
+ Launch a **read-only codebase-search subagent** over `directories.source` to propose domains.
54
+ Use `Explore` on Claude Code and `scout` on pi.
55
+
56
+ Ask it for the top-level functional areas.
57
+ Ask for a kebab-case name, the paths that belong to it, and one line saying what it owns.
56
58
 
57
59
  Guidance for the subagent:
58
60