acstack 0.47.0 → 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.
@@ -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.47.0",
4
+ "version": "0.47.1",
5
5
  "author": {
6
6
  "name": "ac8318740"
7
7
  },
@@ -1,5 +1,6 @@
1
1
  # ACStack terminal workspace.
2
- # Copy to ~/.config/acstack/terminal-workspace.yaml, edit, then run setup.sh apply.
2
+ # Copy to ~/.config/acstack/terminal-workspace.yaml, edit, then run
3
+ # setup.sh apply.
3
4
  #
4
5
  # This is machine-level, not per-project. It installs binaries and writes
5
6
  # keybindings for your user, so it does not belong in acstack/project.yaml.
@@ -225,6 +226,10 @@ pi:
225
226
  # pi.settings.install_telemetry, which controls pi's anonymous install and
226
227
  # update reporting and stays false in this example.
227
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
228
233
  # pi-slash-anywhere, which lets slash commands run from any prompt.
229
234
  slash_anywhere: true
230
235
  # agentic-retro's pi extension, which queues each finished pi session for a
@@ -3520,6 +3520,79 @@ find_pi() {
3520
3520
  have pi || ! have npm || PATH="$PATH:$(npm prefix -g 2>/dev/null)/bin"
3521
3521
  }
3522
3522
 
3523
+
3524
+ # Pi's bundled TUI does not always receive Windows Terminal paste as plain
3525
+ # text. Through Herdr and ConPTY it can arrive as Win32 input records such as
3526
+ # ESC[13;28;13;1;0;1_. Patch the installed bundle after install or update.
3527
+ patch_pi_win32_paste() {
3528
+ have pi || return 0
3529
+ local pi_bin pi_real root chunks
3530
+ pi_bin=$(command -v pi) || return 0
3531
+ pi_real=$(readlink -f "$pi_bin" 2>/dev/null || printf '%s' "$pi_bin")
3532
+ case "$pi_real" in
3533
+ */dist/bundle/cli.js) root=${pi_real%/dist/bundle/cli.js} ;;
3534
+ *)
3535
+ root="${pi_real%/bin/pi}/lib/node_modules/@earendil-works/pi-coding-agent"
3536
+ if [ ! -d "$root/dist/bundle/chunks" ]; then
3537
+ have npm || { say "pi: npm not on PATH, so Win32 paste patch was skipped"; return 0; }
3538
+ root="$(npm root -g 2>/dev/null)/@earendil-works/pi-coding-agent"
3539
+ fi
3540
+ ;;
3541
+ esac
3542
+ chunks="$root/dist/bundle/chunks"
3543
+ [ -d "$chunks" ] || { say "pi: bundle chunks not found, so Win32 paste patch was skipped"; return 0; }
3544
+ SPECHUB_PI_CHUNKS="$chunks" py <<'PI_PATCH_PY'
3545
+ import os, pathlib
3546
+ chunks = pathlib.Path(os.environ["SPECHUB_PI_CHUNKS"])
3547
+ target = None
3548
+ for p in chunks.glob("*.js"):
3549
+ try:
3550
+ s = p.read_text()
3551
+ except (OSError, UnicodeDecodeError):
3552
+ continue
3553
+ if "function decodePrintableKey(data)" in s and "handlePaste(pastedText)" in s:
3554
+ target = p
3555
+ break
3556
+ if target is None:
3557
+ print(" pi: TUI bundle not found, so Win32 paste patch was skipped")
3558
+ raise SystemExit
3559
+ s = target.read_text()
3560
+ changed = False
3561
+ repls = [
3562
+ ('function isKeyRelease(data){return data.includes("\\x1B[200~")?!1:!!(data.includes(":3u")||data.includes(":3~")||data.includes(":3A")||data.includes(":3B")||data.includes(":3C")||data.includes(":3D")||data.includes(":3H")||data.includes(":3F"))}',
3563
+ 'function isKeyRelease(data){let win32=parseWin32InputRecord(data);if(win32)return!win32.keyDown;return data.includes("\\x1B[200~")?!1:!!(data.includes(":3u")||data.includes(":3~")||data.includes(":3A")||data.includes(":3B")||data.includes(":3C")||data.includes(":3D")||data.includes(":3H")||data.includes(":3F"))}'),
3564
+ ('function parseModifyOtherKeysSequence(data){let match=data.match(/^\\x1b\\[27;(\\d+);(\\d+)~$/);if(!match)return null;let modValue=parseInt(match[1],10);return{codepoint:parseInt(match[2],10),modifier:modValue-1}}',
3565
+ 'function parseModifyOtherKeysSequence(data){let match=data.match(/^\\x1b\\[27;(\\d+);(\\d+)~$/);if(!match)return null;let modValue=parseInt(match[1],10);return{codepoint:parseInt(match[2],10),modifier:modValue-1}}function parseWin32InputRecord(data){let match=data.match(/^\\x1b\\[(\\d+);(\\d+);(-?\\d+);([01]);(\\d+);(\\d+)_$/);if(!match)return null;let virtualKey=Number.parseInt(match[1],10),scanCode=Number.parseInt(match[2],10),codepoint=Number.parseInt(match[3],10),keyDown=match[4]==="1",controlState=Number.parseInt(match[5],10),modifier=0;if((controlState&16)!==0)modifier|=MODIFIERS.shift;if((controlState&(1|2))!==0)modifier|=MODIFIERS.alt;if((controlState&(4|8))!==0)modifier|=MODIFIERS.ctrl;if((controlState&32)!==0)modifier|=128;if((controlState&128)!==0)modifier|=64;return{virtualKey,scanCode,codepoint,keyDown,modifier}}'),
3566
+ ('function parseKey(data){let kitty=parseKittySequence(data);',
3567
+ 'function parseKey(data){let win32=parseWin32InputRecord(data);if(win32){_lastEventType=win32.keyDown?"press":"release";if(!win32.keyDown)return;let codepoint=win32.codepoint;if(win32.virtualKey===46)codepoint=FUNCTIONAL_CODEPOINTS.delete;else if(win32.virtualKey===45)codepoint=FUNCTIONAL_CODEPOINTS.insert;else if(win32.virtualKey===33)codepoint=FUNCTIONAL_CODEPOINTS.pageUp;else if(win32.virtualKey===34)codepoint=FUNCTIONAL_CODEPOINTS.pageDown;else if(win32.virtualKey===35)codepoint=FUNCTIONAL_CODEPOINTS.end;else if(win32.virtualKey===36)codepoint=FUNCTIONAL_CODEPOINTS.home;else if(win32.virtualKey===37)codepoint=ARROW_CODEPOINTS.left;else if(win32.virtualKey===38)codepoint=ARROW_CODEPOINTS.up;else if(win32.virtualKey===39)codepoint=ARROW_CODEPOINTS.right;else if(win32.virtualKey===40)codepoint=ARROW_CODEPOINTS.down;else if(win32.virtualKey===8)codepoint=CODEPOINTS.backspace;else if(win32.virtualKey===9)codepoint=CODEPOINTS.tab;else if(win32.virtualKey===13)codepoint=CODEPOINTS.enter;else if(win32.virtualKey===27)codepoint=CODEPOINTS.escape;return formatParsedKey(codepoint,win32.modifier)}let kitty=parseKittySequence(data);'),
3568
+ ('function decodeModifyOtherKeysPrintable(data){let parsed=parseModifyOtherKeysSequence(data);if(!(!parsed||(parsed.modifier&~LOCK_MASK&~MODIFIERS.shift)!==0)&&!(!Number.isFinite(parsed.codepoint)||parsed.codepoint<32))try{return String.fromCodePoint(parsed.codepoint)}catch{return}}function decodePrintableKey(data){return decodeKittyPrintable(data)??decodeModifyOtherKeysPrintable(data)}',
3569
+ 'function decodeModifyOtherKeysPrintable(data){let parsed=parseModifyOtherKeysSequence(data);if(!(!parsed||(parsed.modifier&~LOCK_MASK&~MODIFIERS.shift)!==0)&&!(!Number.isFinite(parsed.codepoint)||parsed.codepoint<32))try{return String.fromCodePoint(parsed.codepoint)}catch{return}}function decodeWin32Printable(data){let parsed=parseWin32InputRecord(data);if(!parsed||!parsed.keyDown)return;let modifier=parsed.modifier&~LOCK_MASK;if((modifier&(MODIFIERS.ctrl|MODIFIERS.alt|MODIFIERS.super))!==0)return;if(!Number.isFinite(parsed.codepoint)||parsed.codepoint<32)return;try{return String.fromCodePoint(parsed.codepoint)}catch{return}}function decodePrintableKey(data){return decodeKittyPrintable(data)??decodeModifyOtherKeysPrintable(data)??decodeWin32Printable(data)}'),
3570
+ ('let decodedText=pastedText.replace(/\\x1b\\[(\\d+);5u/g,(match,code)=>{let cp=Number(code);return cp>=97&&cp<=122?String.fromCharCode(cp-96):cp>=65&&cp<=90?String.fromCharCode(cp-64):match}),filteredText=this.normalizeText(decodedText).split("").filter(char=>char===`\n`||char.charCodeAt(0)>=32).join("");',
3571
+ 'let decodedText=pastedText.replace(/\\x1b\\[(\\d+);(\\d+);(-?\\d+);([01]);(\\d+);(\\d+)_/g,(match,vk,sc,uc,kd)=>{if(kd!=="1")return "";let cp=Number(uc),key=Number(vk);if(key===13||cp===13)return `\n`;if(cp>=32)try{return String.fromCodePoint(cp)}catch{return ""}return ""}).replace(/\\x1b\\[(\\d+);5u/g,(match,code)=>{let cp=Number(code);return cp>=97&&cp<=122?String.fromCharCode(cp-96):cp>=65&&cp<=90?String.fromCharCode(cp-64):match}),filteredText=this.normalizeText(decodedText).split("").filter(char=>char===`\n`||char.charCodeAt(0)>=32).join("");')]
3572
+ missing = []
3573
+ for old, new in repls:
3574
+ if new in s:
3575
+ continue
3576
+ if old not in s:
3577
+ missing.append(old[:80])
3578
+ continue
3579
+ s = s.replace(old, new, 1)
3580
+ changed = True
3581
+ if missing:
3582
+ print(" pi: Win32 paste patch did not match this Pi bundle")
3583
+ raise SystemExit
3584
+ if changed:
3585
+ try:
3586
+ target.write_text(s)
3587
+ except OSError as exc:
3588
+ print(f" pi: could not patch {target} ({exc})")
3589
+ raise SystemExit
3590
+ print(" pi: Win32 paste patch applied")
3591
+ else:
3592
+ print(" pi: Win32 paste patch already applied")
3593
+ PI_PATCH_PY
3594
+ }
3595
+
3523
3596
  # The ACStack version running this script, which is the one pi should pin.
3524
3597
  acstack_version() {
3525
3598
  json_field "$(dirname "$0")/../../.claude-plugin/plugin.json" version
@@ -3670,6 +3743,8 @@ apply_pi() {
3670
3743
  return 0
3671
3744
  fi
3672
3745
 
3746
+ patch_pi_win32_paste
3747
+
3673
3748
  # ACStack cannot launch a subagent on pi without pi-subagents.
3674
3749
  pi_add npm:pi-subagents npm:pi-subagents
3675
3750
  if [ "$(cfg_get pi.todo true)" = "true" ]; then
@@ -3703,6 +3778,11 @@ apply_pi() {
3703
3778
  else
3704
3779
  pi_drop npm:pi-web-access
3705
3780
  fi
3781
+ if [ "$(cfg_get pi.lens false)" = "true" ]; then
3782
+ pi_add npm:pi-lens npm:pi-lens
3783
+ else
3784
+ pi_drop npm:pi-lens
3785
+ fi
3706
3786
  if [ "$(cfg_get pi.slash_anywhere true)" = "true" ]; then
3707
3787
  pi_add "$PI_SLASH_ANYWHERE" "$PI_SLASH_ANYWHERE"
3708
3788
  else
@@ -3749,6 +3829,7 @@ apply_pi() {
3749
3829
  pi_state merge "$PI_KEYBINDINGS" keybindings '{"app.message.dequeue": "ctrl+enter"}'
3750
3830
  pi_state merge "$PI_SUBAGENT_CFG" subagent '{"toolActivation": "eager"}'
3751
3831
  pi_state md write
3832
+ patch_pi_win32_paste
3752
3833
 
3753
3834
  # Logging in is interactive and stores a credential, so it stays the user's.
3754
3835
  [ -f "$PI_DIR/auth.json" ] || say "pi: not logged in yet. Run pi, then /login"
@@ -3771,6 +3852,7 @@ remove_pi() {
3771
3852
  pi_drop npm:pi-compact-tools
3772
3853
  pi_drop npm:@juicesharp/rpiv-ask-user-question
3773
3854
  pi_drop npm:pi-web-access
3855
+ pi_drop npm:pi-lens
3774
3856
  pi_drop "$PI_SLASH_ANYWHERE"
3775
3857
  pi_drop npm:pi-subagents
3776
3858
  else
@@ -3814,6 +3896,7 @@ billion-context pi npm billion-context -
3814
3896
  pi-plugin-manager pi npm pi-plugin-manager -
3815
3897
  @juicesharp/rpiv-ask-user-question pi npm @juicesharp/rpiv-ask-user-question -
3816
3898
  pi-web-access pi npm pi-web-access -
3899
+ pi-lens pi npm pi-lens -
3817
3900
  pi-slash-anywhere pi npm pi-slash-anywhere -
3818
3901
  pi-compact-tools pi npm pi-compact-tools -
3819
3902
  T
@@ -3847,7 +3930,7 @@ installed_version() { # installed_version <name> -> a bare version, or nothing
3847
3930
  # 1.1.0
3848
3931
  pi) find_pi; v=$(pi --version 2>/dev/null | awk '{print $NF}') ;;
3849
3932
  # pi installs npm packages under its own directory, not npm's global one.
3850
- pi-subagents|pi-compact-tools|pi-web-access|pi-slash-anywhere|@juicesharp/rpiv-ask-user-question|billion-context|@juicesharp/rpiv-todo|pi-plugin-manager)
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)
3851
3934
  v=$(json_field "$PI_DIR/npm/node_modules/$1/package.json" version) ;;
3852
3935
  esac
3853
3936
  printf '%s' "${v#v}"
@@ -3991,9 +4074,13 @@ upgrade_one() { # upgrade_one <name>
3991
4074
  harlequin) uv tool upgrade harlequin >/dev/null 2>&1 \
3992
4075
  && say "harlequin upgraded" || say "harlequin upgrade failed" ;;
3993
4076
  # With no target, pi update updates pi itself and no package.
3994
- pi) pi update >/dev/null 2>&1 \
3995
- && say "pi updated" || say "pi update failed" ;;
3996
- pi-subagents|pi-compact-tools|pi-web-access|pi-slash-anywhere|@juicesharp/rpiv-ask-user-question|billion-context|@juicesharp/rpiv-todo|pi-plugin-manager)
4077
+ pi) if pi update >/dev/null 2>&1; then
4078
+ say "pi updated"
4079
+ patch_pi_win32_paste
4080
+ else
4081
+ say "pi update failed"
4082
+ 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)
3997
4084
  if pi_npm_pin "$1"; then
3998
4085
  say "$1 is pinned to $(pi_state pkg "npm:$1") in pi, which pi update never moves"
3999
4086
  return 0
@@ -4078,7 +4165,7 @@ case "$ACTION" in
4078
4165
  if have pi || [ "$(cfg_get pi.enabled false)" = "true" ]; then
4079
4166
  echo
4080
4167
  have pi && say "pi: $(installed_version pi)" || say "pi: not installed"
4081
- 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 "$PI_SLASH_ANYWHERE" npm:pi-compact-tools; do
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
4082
4169
  src=$(pi_state pkg "$id") && say "pi package: $src" || say "pi package: $id not installed"
4083
4170
  done
4084
4171
  [ -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.47.0",
3
+ "version": "0.47.1",
4
4
  "description": "CLI for the ACStack agentic coding stack",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -370,6 +370,9 @@ What `apply` does:
370
370
  - It adds web search, URL fetching, GitHub repo cloning, PDF extraction and media understanding tools to pi
371
371
  - This is separate from pi's anonymous install and update reporting; `pi.settings.install_telemetry: false` still controls that telemetry setting
372
372
  - Set `pi.web_access: false` to leave it out, or to remove it later if this setup installed it
373
+ - It can install `pi-lens` for diagnostics and code-intelligence tools
374
+ - It is off by default because it is a heavier dev-tool layer
375
+ - Set `pi.lens: true` to install it, or `pi.lens: false` to remove it later if this setup installed it
373
376
  - It installs `pi-slash-anywhere` by default, so slash commands work from any prompt
374
377
  - Set `pi.slash_anywhere: false` to leave it out, or to remove it later if this setup installed it
375
378
  - It installs `pi-compact-tools` and runs `agentic-retro install-pi`, each behind its own toggle
@@ -380,6 +383,8 @@ What `apply` does:
380
383
  - It sets `"toolActivation": "eager"` in `~/.pi/agent/extensions/subagent/config.json`
381
384
  - It binds `"app.message.dequeue"` to `"ctrl+enter"` in `~/.pi/agent/keybindings.json`
382
385
  - It writes the four keys under `pi.settings` into `~/.pi/agent/settings.json`
386
+ - It patches pi's bundled TUI so Windows Terminal Win32 input records paste as text
387
+ - `pi update` can replace the bundle, so `setup.sh upgrade pi` reapplies the patch
383
388
 
384
389
  How it treats pi's files:
385
390
 
@@ -396,7 +401,7 @@ How it treats pi's files:
396
401
  - `apply` never touches `auth.json`
397
402
  - Run `pi`, then `/login`, yourself
398
403
  - `setup.sh status` reports pi's version, its packages, the delegation line, `toolActivation`, and whether `auth.json` exists
399
- - `setup.sh outdated` and `setup.sh upgrade` include `billion-context`, `@juicesharp/rpiv-todo`, `pi-plugin-manager`, `@juicesharp/rpiv-ask-user-question`, `pi-web-access`, and `pi-slash-anywhere` like pi's other npm packages
404
+ - `setup.sh outdated` and `setup.sh upgrade` include `billion-context`, `@juicesharp/rpiv-todo`, `pi-plugin-manager`, `@juicesharp/rpiv-ask-user-question`, `pi-web-access`, `pi-lens`, and `pi-slash-anywhere` like pi's other npm packages
400
405
 
401
406
  ## 4. How you attach
402
407
 
@@ -472,6 +477,7 @@ The two paths deliver keys by different routes.
472
477
  - The two routes do not carry the same set
473
478
  - The `ctrl+alt` family survives the escape-sequence route and dies on the native one
474
479
  - `ctrl+<digit>` does the opposite
480
+
475
481
  - Either way herdr accepts the binding and then never sees the key
476
482
  - A chord that does not cross is therefore silently dead, and herdr reports no error
477
483
  - `alt+<key>` is the only family that crossed both
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acstack",
3
- "version": "0.47.0",
3
+ "version": "0.47.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",