{"_id":"pi-link","_rev":"30-face8f512f14b24e2fce7c67b3a92c19","name":"pi-link","dist-tags":{"latest":"0.5.2"},"versions":{"0.1.0":{"name":"pi-link","version":"0.1.0","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.0","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"extensions":["./index.ts"]},"dist":{"shasum":"67627ef50b7f524648582028f2f0044f1619429c","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.0.tgz","fileCount":4,"integrity":"sha512-bYhdJWxf2nXKu6zHpEcEFeWFof9EkDMtboW5ARqTpWCIcb3cvOhxgL+kefH0oZXpqlJg6Zu7SC2ZkNsF2RzSuA==","signatures":[{"sig":"MEUCIHq2C4gw3Zdl7/yMHsKgAZUcpECnZjLr7RyEm0AxhXxSAiEA7QSIyYkovyzuQUqSGPeLhAoAoMJmcpQpp+f0LvQJScA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":53901},"gitHead":"57bda8b78e2f929d1f230f8772a82226f02b1853","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.0_1774242949350_0.2326508605351565","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"pi-link","version":"0.1.1","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.1","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"extensions":["./index.ts"]},"dist":{"shasum":"2ecf01308c961cee73ed465b25d8cca69b9f9964","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.1.tgz","fileCount":4,"integrity":"sha512-lo742FFqlS6H6DwjoOc56fFQi0Y1t625IwREYVZ+Ytv2tccNq5NfGhD4+p0IChgxjN+YXT8a4vK2s68xmq22PQ==","signatures":[{"sig":"MEYCIQDE4haIvniqitcehwW6MKyqSvGQDJCAWgZ2RMfZXZWuMAIhAO9K2kg11ozyOyoc1HfIJko0shmZRrx8p6nvs0hHMgbh","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":53837},"gitHead":"87b394fcdaccd7960de786e21b693e10b74f55a4","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.1_1774244422513_0.6979346164754179","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"pi-link","version":"0.1.2","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.2","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"extensions":["./index.ts"]},"dist":{"shasum":"65bc505b4a90b2560521661101959e288ffedca0","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.2.tgz","fileCount":4,"integrity":"sha512-ZQ2XfB/iYjmXxsJ/1OuKDj6tt0L+pD95gISj3qtrEu8ki+a7noQRVc859OCfMLPsnsr5h9lAfQ9noQNns2x13g==","signatures":[{"sig":"MEUCIQCjr3TjHrcQ6Kej6HwpDS3vTfbhOK85y2gUeSHLlMGYqQIgYMGsSVWdXXlAOP8ccziUnI66TtfjDFfWQNlqt2npHm0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":60310},"gitHead":"116e6e39170b9717a948457a2fd464d51cf3a222","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.2_1774404499814_0.2795835021313047","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"pi-link","version":"0.1.3","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.3","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"extensions":["./index.ts"]},"dist":{"shasum":"9795c53ccd544b0180b1bb501a9cb31bb64f0e67","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.3.tgz","fileCount":4,"integrity":"sha512-B/ffvemByT+XmDpmS0APm0PZe461+vATQoJR/w8TpJpEJZeyIIysCW3KGAIltyrNmKOF2EA5xQJiVGKrqT04ig==","signatures":[{"sig":"MEYCIQCugwwDIUDYPvfYIjy2frXQD148OLBWh9tMnjFKXyjTZwIhAK6h5/c83a+74vktQhug/DYBxpvzs1xKs6D93LN+P4MM","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":64379},"gitHead":"9c297c9415dd2f995f75775be8200b0d635a597a","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.3_1774589698206_0.7938842656901981","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"pi-link","version":"0.1.4","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.4","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"extensions":["./index.ts"]},"dist":{"shasum":"1f7f9491f4e2f3a25eec90d6efb5fee47d8625b9","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.4.tgz","fileCount":5,"integrity":"sha512-55lmJMP2i5yGLEHD0mEb9uBMFR9xlBEVK67VtMgnTsSqRe6WTuK62ZqOI9wh07pHlUtWAHc/kQ+IgGviDBgaSw==","signatures":[{"sig":"MEUCIQDZ7Q4aOz0MmkxC1BFrkvf5XSpuhM6gjl/QbZMGHa/70AIgCG+tw8CRE7189FAmPBMuvFl2tW9G6lUDMbj0jrM8ooM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":74835},"gitHead":"463913ae172f4a01058fd8f7d1625e693105580e","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.4_1774853823964_0.5104170715087246","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"pi-link","version":"0.1.5","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.5","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"extensions":["./index.ts"]},"dist":{"shasum":"96dd6f5b7641743f2f244fe5598388d536a8f603","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.5.tgz","fileCount":6,"integrity":"sha512-rFtHFFuHWfzkX+wr4JCRv9MNm4UT9F36Wze8HM0ETSPLQkThQYc/B3Uh0H48wuj9W9WgNddvIOA9Me13cRcKPw==","signatures":[{"sig":"MEQCIEjFtav1IRrJoU621MiyEb+REeU0iRs3pMsLFXDfvXdEAiBxgXlDiaJ88tTqJgMbfCBTpZi+xsQpxepcXLmJydo9VQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":80458},"gitHead":"8dbbecf08e02b0a792781a1d165e06bf5a826b80","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.5_1775115010240_0.7024185504607723","host":"s3://npm-registry-packages-npm-production"}},"0.1.6":{"name":"pi-link","version":"0.1.6","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.6","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"extensions":["./index.ts"]},"dist":{"shasum":"42e2af11856d5ae9169d87de07c2d0d60fb0e684","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.6.tgz","fileCount":6,"integrity":"sha512-V4GjU/0yvtEpAiN/qMu3vCXRoJ56ZSEh8P4v6wJ9FMAPF8HmIHZmhHdi4LJH+GzTnlLaJlgDCSBcbQVwyariZQ==","signatures":[{"sig":"MEUCIQD8IqMBzsadprFo8cF9beYapdTq42KI61MX2OEbLqE1qQIgDQ2UkKguMCXuSuFKSzIxavyiXZxg7n8rf/jG5k2DYns=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":78306},"gitHead":"f7b8ad24253c3a53356942df3a6af6342fb44142","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.6_1775244367891_0.589974912536557","host":"s3://npm-registry-packages-npm-production"}},"0.1.7":{"name":"pi-link","version":"0.1.7","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.7","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"dist":{"shasum":"697dc87ffb1c95d5b2ae2c917d91a751db4b3bfb","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.7.tgz","fileCount":7,"integrity":"sha512-XUTLsKYQJzpFYUEpjF0m3A239wlMcYjr9OE+JsG4BxzfuXnmp/yxNQJTnDHbE4Kar/vaDbpnRD6BFPTFkLXwiA==","signatures":[{"sig":"MEUCIFCHzCBbHXv0g6E2Q2rG1zxUhT3ArNRhfSqoV1vd99LEAiEAnuEMGVBJuNrsVgIvUKMkA+KZ4Rd943O+vyYqqaMkXLo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":83731},"gitHead":"fbe358d3b521d306c60e850165ae44dfd0bd17f7","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.7_1775749518906_0.5369674827662707","host":"s3://npm-registry-packages-npm-production"}},"0.1.8":{"name":"pi-link","version":"0.1.8","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.8","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"dist":{"shasum":"e6c4eb0e951340a40733120d4e32da2bdc354c9a","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.8.tgz","fileCount":6,"integrity":"sha512-8ft77KQqPhWwypj57uYB0htufDuM/dnFVqfxWjTJK7UwZr//MbFBs4ubQ0EogR6eU1L2fKDusF2NmD3xiWjJeQ==","signatures":[{"sig":"MEUCIDCBkcn44z1hem9JxEHrqLaQT6RJyFKuoijeT+EM0NsFAiEAiYr/V/lWCLKj26T1obvoOwhQF1stQzQ25jie5o17Hag=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":90257},"gitHead":"4e4a1230c3b56f43755032de8dd3c0c3a5a43cda","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.8_1776313185271_0.536039766692199","host":"s3://npm-registry-packages-npm-production"}},"0.1.9":{"name":"pi-link","version":"0.1.9","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.9","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"01870c343ad73a081effa04a382ca576eb975c4c","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.9.tgz","fileCount":7,"integrity":"sha512-CxZs88PljImRMDT9YfBIjdFMKPZjTi6GJuzNugZsPKFb08gR3wl7REz9P9B/HnRsyBQ8VbeQDKL/txRfRyY2Rw==","signatures":[{"sig":"MEUCIQDW4tJsbTcDstFkYi/6l2f2LW+f8+Oij0U0fmnZqdL8VwIgfwkhXHNUCbet5G+pCv+mshz0cbQqZU0Q3Iaxygqud7I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":97633},"gitHead":"014a97f1423ebd63b1f4ce950541a6a8f7aefa91","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.9_1776884443378_0.5543061615566107","host":"s3://npm-registry-packages-npm-production"}},"0.1.10":{"name":"pi-link","version":"0.1.10","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.10","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"e60b93d7c8569c3e916813fad24ba46d296243c1","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.10.tgz","fileCount":7,"integrity":"sha512-DMxE/EGCAWJNQ8bNKLoASKIkz+JqicC31Y820h3G0NBXaIrMyGJ8SXQXgFbK1h4CL8ZWOT1a1w1/GjVdfOk25Q==","signatures":[{"sig":"MEUCIFXym/o+EniH7MQs0vvEoAB+EXjgeZZSG352cO+9nwShAiEAs17OaqTyQQHBEiPwjeX0/Q0AkuXjWQQd2o7OBIF2xiI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":103732},"gitHead":"a7176c6f3d7737d03d9a203b4f3e7019929e72b2","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.10_1777187512238_0.5985823405810469","host":"s3://npm-registry-packages-npm-production"}},"0.1.11":{"name":"pi-link","version":"0.1.11","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.11","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"1071dd89894bc313ed30ccf0d206cc1a946f6236","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.11.tgz","fileCount":7,"integrity":"sha512-FtvbwzYRKCN3GQxAnY/af+A5nB4la5o1/P3PjJJdFJg35Lowmr8SN8IDupC3WgTtUAEnrAwbg9fLmOp8yXrkOg==","signatures":[{"sig":"MEQCIHpu8zywGgxy3KoAkvr+TECLyp2cT48vLxMmEncj7ACzAiALImQiDL1ovuvPaUI5NFrm/95nyjC9uuJBOfVqvH8cew==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":111712},"gitHead":"91a9ecde56837d87a35d44d22ab2b363339761de","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.11_1777279354662_0.6495596244737827","host":"s3://npm-registry-packages-npm-production"}},"0.1.12":{"name":"pi-link","version":"0.1.12","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.12","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"d5b4900e9a7817d30bbeb80877cc5b3c85bc725f","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.12.tgz","fileCount":7,"integrity":"sha512-spFscwwzehpxllmzowgshuSOabj0e/u+MC7DGHfVjnvEz30PxfnpwlgfGBseECEQAGbQHV2TUbpaI3XZNgshMA==","signatures":[{"sig":"MEUCICEduxpasSpcDLA5r6VEV5AFTM0aPdKLYmf9VKIeUc5lAiEAlK5FWWc/6x9QoklDmg10uCs75KJQcpSJsYOSTT4S/J0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":122760},"gitHead":"3bc191246d13440065b61e3cf74201c46e8265b7","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.12_1777795459444_0.598604356298579","host":"s3://npm-registry-packages-npm-production"}},"0.1.13":{"name":"pi-link","version":"0.1.13","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.13","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"b777ab540e5670fb6f23e035c149fcac20cba31a","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.13.tgz","fileCount":7,"integrity":"sha512-8PGKLgwbCYPWy6Vx5DT872WPlfaNkDjl/UImkn/YnxhqBl+0S8UY15XS+kTl9FERdfTRxCu7K7MwUw1Tcpq51Q==","signatures":[{"sig":"MEYCIQDpKFMfAPo+3aCOrg/qAMsUbNROdIaiBsjXOsTxuBLfEQIhAMWDmUVn/PyAK2uaiASsJHdmZxv7Jf3gmeeY175Sziv3","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":123798},"gitHead":"4437f002cf922ee41fc46c5c9fa48af2ecb66319","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.13_1777796261478_0.5088164941370883","host":"s3://npm-registry-packages-npm-production"}},"0.1.14-beta.0":{"name":"pi-link","version":"0.1.14-beta.0","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.14-beta.0","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"35942f6829e2f9f989e5c5635568c79c83ddeb5e","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.14-beta.0.tgz","fileCount":7,"integrity":"sha512-8lglg2ekgjOq3ssoX5LDyT0m1dXw+HgJMXbSS3bzAWi7W/8wMYr7y+9AAHFnKn2dFJkaOJr3khrSQXIwTz2SrQ==","signatures":[{"sig":"MEUCIQDZ4TNWi7vA8LjbRcm4VrGFCm/GyeSpQLcySL9If18FzwIgESeSETUb4+kIgaLMx30EBPF9ol8ntjv5ZhE/SKnYDgM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":127912},"gitHead":"546e54d3d3e74e19dfc0323de6ee3471fb4269d5","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.14-beta.0_1777926346880_0.35515226021267865","host":"s3://npm-registry-packages-npm-production"}},"0.1.14":{"name":"pi-link","version":"0.1.14","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.14","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"d7647a18ca17cace45d4bcaf32d941f8f264b8dc","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.14.tgz","fileCount":7,"integrity":"sha512-3B1WqvczW4Ukv+N4ZMBREAqK9eMQFraCH4bAV2ZoXsjn1gs5L+q/WKrmEHLjKRehgBHVDskQx4gW5Bty5Zybuw==","signatures":[{"sig":"MEQCIFZwFyN3zIuj3mXlcCfOn+u7FxsH+54BOjsFb9kv0aWrAiBj03JDqC58WnqOBfNqkSD5JQeUiYPiLU8aQuENCKj5iw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":127898},"gitHead":"546e54d3d3e74e19dfc0323de6ee3471fb4269d5","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.14_1777935364034_0.6327134648032267","host":"s3://npm-registry-packages-npm-production"}},"0.1.15-beta.0":{"name":"pi-link","version":"0.1.15-beta.0","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.15-beta.0","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"b70880a0ec851d6b46217f8823e603923dce708d","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.15-beta.0.tgz","fileCount":7,"integrity":"sha512-UlhyA15kO5SkfUj8EH01eU4m3Gw/HbqMHig32HD1J9EAZ2oGGigmSSgw+7NTbrZoPcJVrA46mPSwCDcCPyyDsg==","signatures":[{"sig":"MEYCIQDG1JJMhivOYfoznhgLGiCHIbElBcQofh9I1Ye/qD3qZAIhANN8bTZCJZ2UCHrRri58U2aJxe5C+nKUKTIARPZLyMCd","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":138593},"gitHead":"f597b1a89ec3c0254db72d60da597d2fe2cb2318","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"@types/ws":"^8.18.1"},"peerDependencies":{"typebox":"*","@mariozechner/pi-tui":"*","@mariozechner/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.15-beta.0_1779050330322_0.6513913950132637","host":"s3://npm-registry-packages-npm-production"}},"0.1.15-beta.1":{"name":"pi-link","version":"0.1.15-beta.1","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.15-beta.1","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"b0f3b95abe20cbb7d83a02708533b2637985ae0a","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.15-beta.1.tgz","fileCount":7,"integrity":"sha512-xIr41s/O67SCBJGE1YRJ6u3YahjefUaccgyUTDDgLm/cieXtO6xhVDwg4tjZpH9x9dL8BO3ErMCqRs04x+zG4A==","signatures":[{"sig":"MEYCIQC24zLZD4//glHmfkf7ME+z+qO3Zl0e0pjgCBOVnjhosAIhAMtuZ7nXJSgh+AK8T1C5f1EbFI79bPpGVALZeM5rd4WY","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":140334},"gitHead":"720f356223351ee9adbe56bfa7dd294dd273fc33","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"@types/ws":"^8.18.1"},"peerDependencies":{"typebox":"*","@earendil-works/pi-tui":">=0.74.0","@earendil-works/pi-coding-agent":">=0.74.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.15-beta.1_1779055255345_0.7430057560868313","host":"s3://npm-registry-packages-npm-production"}},"0.1.15":{"name":"pi-link","version":"0.1.15","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.15","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"4f50a374ef05fcfacadd33c356ed2507b75cceec","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.15.tgz","fileCount":7,"integrity":"sha512-yrUx4jRPUqg2zf6eAAlYlNnwgzxF9ovVvcLVsUGc1DSKoNPx4mGi6Qv2kMonlKKYtVCRP0DHD+wroZzsMYeiew==","signatures":[{"sig":"MEUCIDRYsfraapgpP5iSUs3kXOWIkx8XAxRfc/vaFfpmuqsPAiEA50YzW84ILKPko9f8Mb2i2vnAxkOiO8v9GkLjHToSUdg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":140024},"gitHead":"7d555180c437b9a322d2455890e47cf81772c6bc","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.10.0","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.15_1779067788129_0.6799315898529006","host":"s3://npm-registry-packages-npm-production"}},"0.1.16":{"name":"pi-link","version":"0.1.16","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.16","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"21a5edc442c3715d6514e8f5dd7917db14894405","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.16.tgz","fileCount":7,"integrity":"sha512-WoHTA31a/+DWwBp9RqqE0V8/dlGT/HKi2lFWb5/ygXPqrEl21qUsRI9NpNNWYXvi5QxjQTwlv5xM/s/nrN1TpA==","signatures":[{"sig":"MEYCIQDBlcNIUjTZqE+QWVN6ImxOx67ga6+a97WzcY2RpduvkgIhAMrPONFvHnOKi65ngI6Kc2sl428xcSJ8k4zkfVO3ObeN","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":160418},"gitHead":"e55ac768bf137180e08eefe53288e74080400131","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.14.1","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.16_1780978225300_0.39897417067758334","host":"s3://npm-registry-packages-npm-production"}},"0.1.17":{"name":"pi-link","version":"0.1.17","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.1.17","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"5894ada538664978de312bd62c93f5162f1b5feb","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.1.17.tgz","fileCount":7,"integrity":"sha512-h4FlFJsvCwLQRdSxaQelFPUt2Uc43P8UJ5YXUCnS1NaSY5uwpSZS1VxxLixYa8O/b7tAHJeaq/jydNij9FKbgg==","signatures":[{"sig":"MEUCIFriotkiUBGyDWuA198Mv4+0DplQpeffGlNXvb8076MHAiEA1d0FKLH6RhXQw4SBVvgRLiGq80ecMyVjjYZzu6y+yyg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":162847},"gitHead":"eca907a31eb0d7ad1222890be454b41533cd90a9","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.14.1","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.1.17_1782927116008_0.9872996211054428","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"pi-link","version":"0.2.0","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.2.0","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"dc73670c57c2d6d0d16eef3bb16cb83fdb0ef52c","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.2.0.tgz","fileCount":7,"integrity":"sha512-LBHfY+EGTbhAoAWz7kEeU/oV+FhzUK0ujMrW3GeisnJjjbsHbVf3NTA0ASiQFwrXFZ2dZas72WYzB2RFP5Mkjg==","signatures":[{"sig":"MEUCIQC+7P94n+gqeiX8DV4agXS670jOFCB6LhYPUebVBlm7UgIgQiYhia8UVbK+MLkClT2OIvhxWRW57cHJZ7hwvDa7ACc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":162492},"gitHead":"91cdc75b36368f7150e9ac45feafb2387358d628","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.14.1","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.2.0_1784349128663_0.5673312586550996","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"pi-link","version":"0.3.0","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.3.0","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"c3225dd8ebd591655fd31b20b8bb9e2df48ab775","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.3.0.tgz","fileCount":7,"integrity":"sha512-6VssWs553Ih6dcfjS2+1tRKQB58RiHevA/QIB2RR+eILheujpA2j/11qpeT8UYrN7VRVL7OO8EtzSXlvrCYeyQ==","signatures":[{"sig":"MEUCIQDCGljXYF1jWFMx4+kQPYpK4l8FVptHeUNgUIuEc3OSDQIgckDBoKr2DcS6otVao+2hWL5gB/nzbHhQc2DxdYBmXZE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":183831},"gitHead":"02217bb1f3bccc2d362e6efaeb02eba7e735cf1e","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.14.1","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.3.0_1787950215631_0.8091051666370346","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"pi-link","version":"0.4.0","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.4.0","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"f8abbd7b0bdddb68502e86b4913bf07f7db3b390","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.4.0.tgz","fileCount":7,"integrity":"sha512-lpFt3N0WRQ0Wy8Fu0KjL49wrFwA4eFOoj7Kq2iOg9IGPJ9NM0rm7ARwrD4phyD0OBZP09yvHmD2iNR4O0kfVsw==","signatures":[{"sig":"MEYCIQCYPZ+0P2L2czotdOE2s7fZBWmmX8xYrGqSiwOtj84bDgIhAJBVjZC5ORNLrFw4lzxoM1pe3UQtr47xjmzmvwEIIxEe","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":205518},"gitHead":"a3c831fc619e8e08139dc883ed3bf9718c91f876","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.14.1","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.4.0_1788504436198_0.5522858809231959","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"pi-link","version":"0.4.1","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.4.1","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"d3bae1ec7c12cb3d29a0726aa406e6670f4818f1","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.4.1.tgz","fileCount":7,"integrity":"sha512-Y5G0huXG31CpVOLmTdybej4P2OoBdFdJPBgG6RDYDcs9xaPXDiLmAmnCo4+IEBp46GhlGvWAGKRG4pw/r9jymw==","signatures":[{"sig":"MEUCIQCr+ACDaFcn0ARsmYJS9S195oEp8vc1Qai32acyB3Y5hQIgcdqtlp4mhyTgExl2CqTUzNt0pK1qwB9W5ogbaDvWuJY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":212114},"gitHead":"ee1e4d3aa5b3a90db85d44ecca47e720bc9f39e3","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.14.1","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.4.1_1788762000929_0.8105662118946724","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"pi-link","version":"0.5.0","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.5.0","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"b596b3b0ae424c98a94f07d78b97c2bc0979b359","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.5.0.tgz","fileCount":7,"integrity":"sha512-jGIXB/2fL2hIRM61Vc7Gh0t9xL9eKbr4D4vEdP0erO6y4v6l/S1u0+yblW+Dv2PLTHuWxMPXJ50gLXjJactU5Q==","signatures":[{"sig":"MEUCIQCpWwX8ZD1+bt7Phe9qAhoVua7JDlcGqGgfVZ/IMlkO+QIgNrXfoUcEKJAhwenntJfU1Ldmkz6Dyl24jh1kh78F68Q=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":220905},"gitHead":"aff9d5a8f9fa43cc101ef6ee75d239c215e36069","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.14.1","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.5.0_1789599514214_0.15661661524498438","host":"s3://npm-registry-packages-npm-production"}},"0.5.1":{"name":"pi-link","version":"0.5.1","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"author":{"name":"alvivar"},"license":"MIT","_id":"pi-link@0.5.1","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"homepage":"https://github.com/alvivar/pi-link#readme","bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"bin":{"pi-link":"bin/pi-link.mjs"},"dist":{"shasum":"efd3e2a7cfc2f7ac8729e2c1a732c325bb20f062","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.5.1.tgz","fileCount":7,"integrity":"sha512-XjVxumQcx4MmEK9JKU0mOzWGnqZoYeLiuEz2pGTim+I10hzS0eqs/4MsCEhPfyyHMSpNDvLBZ93PRlh1IFY2gQ==","signatures":[{"sig":"MEUCIFIz1wu8iKTJArBTUbyPA0tE+lQaGP/E7uell5D0hFDjAiEAkhYIVZ7A9PzBn7ciIX67pQLucUT3ILaywPpsyozUmrk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQDfnFV6vd1KTP7Y4otXK+DBiwJBnGRhXkRx05Pa5QKuAgIgWI+XdzIKcyqFhWLyiCHVFtW5JqTFSRNTbcZH5rcoqzo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":222364},"gitHead":"1c2be023c4054c7ae755539bf15ac1b7d3972689","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.14.1","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"tmp":"tmp/pi-link_0.5.1_1790141334931_0.19889336393476675","host":"s3://npm-registry-packages-npm-production"}},"0.5.2":{"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"_id":"pi-link@0.5.2","bin":{"pi-link":"bin/pi-link.mjs"},"bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"dist":{"shasum":"45c4feb6c8bd124423f9356613678adb32c92d2b","tarball":"https://registry.npmjs.org/pi-link/-/pi-link-0.5.2.tgz","fileCount":7,"integrity":"sha512-bemtmgwhzBQ5ScBWCs5F72v+S33kegNbx/hxnBK+yOC1jRF+aliS1vliV31tToGEu5tDGITzfAhGf6PfLlH7ZA==","signatures":[{"sig":"MEUCIQDtuGTdfsoqFUX9iyUT/t9szXLHqXf4zDOy4VqAcxXebQIgcFbH/jHbA50Uc/QdP3ksv1OCBwKGLJvGMMCnxZC1M/I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCWE5naAn2CzqE2aqQljiPkdBKasTZ3Ns/PR//tZSsV6gIgLaZmDuDfUcAKbEHKRL+i7vJC09P7f1+qo448LDnqkDk="}],"unpackedSize":224246},"name":"pi-link","author":{"name":"alvivar"},"gitHead":"8dcec96c89c3816f6290a68a64c8f40545e0df4a","license":"MIT","version":"0.5.2","_npmUser":{"name":"alvivar","email":"andresalvivar@gmail.com"},"homepage":"https://github.com/alvivar/pi-link#readme","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"_npmVersion":"11.14.1","description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","directories":{},"maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"_nodeVersion":"24.19.0","dependencies":{"ws":"^8.20.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-link_0.5.2_1790723827063_0.04753997331570714"}}},"time":{"created":"2026-03-23T05:15:49.349Z","modified":"2026-09-29T23:17:07.305Z","0.1.0":"2026-03-23T05:15:49.488Z","0.1.1":"2026-03-23T05:40:22.672Z","0.1.2":"2026-03-25T02:08:19.958Z","0.1.3":"2026-03-27T05:34:58.423Z","0.1.4":"2026-03-30T06:57:04.098Z","0.1.5":"2026-04-02T07:30:10.393Z","0.1.6":"2026-04-03T19:26:08.080Z","0.1.7":"2026-04-09T15:45:19.071Z","0.1.8":"2026-04-16T04:19:45.400Z","0.1.9":"2026-04-22T19:00:43.510Z","0.1.10":"2026-04-26T07:11:52.418Z","0.1.11":"2026-04-27T08:42:34.798Z","0.1.12":"2026-05-03T08:04:19.593Z","0.1.13":"2026-05-03T08:17:41.662Z","0.1.14-beta.0":"2026-05-04T20:25:47.021Z","0.1.14":"2026-05-04T22:56:04.166Z","0.1.15-beta.0":"2026-05-17T20:38:50.456Z","0.1.15-beta.1":"2026-05-17T22:00:55.493Z","0.1.15":"2026-05-18T01:29:48.281Z","0.1.16":"2026-06-09T04:10:25.437Z","0.1.17":"2026-07-01T17:31:56.164Z","0.2.0":"2026-07-18T04:32:08.780Z","0.3.0":"2026-08-28T20:50:15.769Z","0.4.0":"2026-09-04T06:47:16.358Z","0.4.1":"2026-09-07T06:20:01.059Z","0.5.0":"2026-09-16T22:58:34.347Z","0.5.1":"2026-09-23T05:28:55.041Z","0.5.2":"2026-09-29T23:17:07.147Z"},"bugs":{"url":"https://github.com/alvivar/pi-link/issues"},"author":{"name":"alvivar"},"license":"MIT","homepage":"https://github.com/alvivar/pi-link#readme","keywords":["pi-package","pi","link","websocket","terminal","multi-agent"],"repository":{"url":"git+https://github.com/alvivar/pi-link.git","type":"git"},"description":"WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.","maintainers":[{"name":"alvivar","email":"andresalvivar@gmail.com"}],"readme":"# pi-link\r\n\r\nRun several Pi agents side by side and let them work as a team. Open two terminals with `pi --link`; they find each other on your machine, and each one can hand the other work, ask it questions, or wait for it to report back — all from a normal prompt.\r\n\r\nOne Pi is powerful. Several, talking to each other, unlock patterns a single terminal cannot:\r\n\r\n- **Research + Build** — one terminal digs through APIs, docs or logs while another writes the code.\r\n- **Parallel work** — split a large task (\"you take the backend, you take the frontend\") and collect the results.\r\n- **Orchestrator / Workers** — one terminal delegates, tracks who reported back, and assembles the whole.\r\n- **Review pipeline** — one writes, another reviews, back and forth until both are satisfied.\r\n\r\nQuestions, ideas? There's a [pi-link thread](https://discord.com/channels/1456806362351669492/1485515696719921183) in the official Pi Discord.\r\n\r\n---\r\n\r\n## Table of Contents\r\n\r\n- [Prerequisites](#prerequisites)\r\n- [Quick Start](#quick-start)\r\n- [Walkthrough](#walkthrough)\r\n- [LLM Tools](#llm-tools)\r\n- [Slash Commands](#slash-commands)\r\n- [CLI: `pi-link`](#cli-pi-link)\r\n- [Configuration](#configuration)\r\n- [Troubleshooting](#troubleshooting)\r\n- [Limitations & Design Decisions](#limitations--design-decisions)\r\n- [Dependencies](#dependencies)\r\n- [Internals](#internals)\r\n\r\n---\r\n\r\n## Prerequisites\r\n\r\n- [Pi coding agent](https://github.com/badlogic/pi-mono), version **0.84.2 or later** (for pi-link 0.3+), stable releases only, reported as `x.y.z` without a prerelease or build suffix. On Pi 0.74–0.84.1, pin `pi-link@0.2.x`; on Pi ≤0.73, pin `pi-link@0.1.14`.\r\n- Node.js (LTS recommended)\r\n\r\nPi's package installation does not check the host version, so `pi install` succeeds on an older Pi. pi-link then refuses to initialize instead of half-running: it throws before registering anything, and Pi reports it under **[Extension issues]** with the required minimum and the version it detected.\r\n\r\n---\r\n\r\n## Quick Start\r\n\r\n### Install\r\n\r\nThe minimum install — enables every in-Pi feature (`/link`, `link_send`, `/link-connect`, `--link` flag, auto-resume, and all LLM tools):\r\n\r\n```bash\r\npi install npm:pi-link\r\n```\r\n\r\nThat's it. For most users this is all you need.\r\n\r\n#### Optional: shell launcher\r\n\r\nIf you also want the `pi-link <name>` shell command to start named sessions from a terminal prompt (e.g. `pi-link builder` in one window, `pi-link reviewer` in another), install the CLI globally as well:\r\n\r\n```bash\r\nnpm i -g pi-link\r\n```\r\n\r\nOr install both in one line:\r\n\r\n```bash\r\npi install npm:pi-link && npm i -g pi-link\r\n```\r\n\r\nPi installs packages into its own npm root (`~/.pi/agent/npm/`), which is not on `PATH`; `npm i -g pi-link` is what puts the `pi-link` command there.\r\n\r\nThe shell launcher is convenience-only — you can always reach the same functionality from inside Pi via `/link-connect` and `/link-name <name>`.\r\n\r\n### Uninstall\r\n\r\n```bash\r\npi uninstall npm:pi-link      # Remove Pi extension\r\nnpm uninstall -g pi-link      # Remove CLI launcher (if you installed it)\r\n```\r\n\r\n### Usage\r\n\r\nLink is **off by default**. Three ways to start:\r\n\r\n```bash\r\npi --link                    # try it now, random name like t-a3f9\r\npi --link-name builder       # with a name of your choice\r\npi-link builder              # same, plus resume that session by name later (needs the shell launcher)\r\n```\r\n\r\nAlready in a session? Use `/link-connect`. Use `/link` any time to check status, or let the LLM tools handle cross-terminal coordination. See [Session Resume](#session-resume) for `pi-link <name>` details.\r\n\r\n---\r\n\r\n## Walkthrough\r\n\r\nHere's a concrete example of two terminals collaborating. Open two terminals, each with its name already set:\r\n\r\n```bash\r\npi --link-name builder       # Terminal 1\r\npi --link-name researcher    # Terminal 2\r\n```\r\n\r\nThe first one to start becomes the hub; the second joins it. **In Terminal 1**, both names are now visible:\r\n\r\n```\r\n> /link\r\n⚡ Link: builder (hub) · 2 online\r\n  builder: idle (5s) · 45K/272K (17%)\r\n    cwd: ~/my-project\r\n  researcher: idle (12s) · 80K/272K (29%)\r\n    cwd: ~/my-project\r\n```\r\n\r\nAlready inside a session? Name it from the prompt instead — `/link-connect` if it is not on the link yet, then:\r\n\r\n```\r\n> /link-name researcher\r\n✓ Reconnecting, requesting \"researcher\" (hub may assign a different name if taken)...\r\n```\r\n\r\nA client reconnects under its new name, so give it a moment before checking `/link`. Either way the name is saved with the session and comes back on resume.\r\n\r\n**Now ask Terminal 1's LLM to delegate work:**\r\n\r\nIn Terminal 1, type a normal prompt:\r\n\r\n```\r\n> Use link_send to ask \"researcher\" to summarize README.md, then report DONE with the summary back to builder\r\n```\r\n\r\nTerminal 1 calls `link_send` and returns immediately. The message enters Terminal 2's reasoning — steered into its current run if it is working, or starting a turn if it is idle. Terminal 2 completes the assignment, then sends a conventional `DONE` callback. That callback enters Terminal 1 the same way, where the result can be presented or used for follow-up work.\r\n\r\n### Once it works: groups\r\n\r\nWith two projects open, you may not want their terminals to see each other. Put a group after `@` in the name — `pi --link-name builder@frontend`, or `/link-name builder@frontend` from inside — and that terminal lists, messages and compacts only names ending in the same `@frontend`; a plain `builder` elsewhere no longer appears. The hub still serves everyone, and `pi-link --status` shows all groups. Full rule under [Configuration](#configuration).\r\n\r\n---\r\n\r\n## LLM Tools\r\n\r\nThree tools: `link_send` to talk to another terminal, `link_list` to see who is there, and `link_compact` to trim another terminal's context. pi-link also ships a **pi-link-tools** skill describing how they behave.\r\n\r\n### Which tool should I use?\r\n\r\n| Tool           | Behavior                                                | Returns                                                 |\r\n| -------------- | ------------------------------------------------------- | ------------------------------------------------------- |\r\n| `link_send`    | Send a message to one other terminal                    | Reports whether sending started, not whether it arrived |\r\n| `link_list`    | List currently connected terminals                      | Terminal list with roles, status, cwd, and context      |\r\n| `link_compact` | Ask another terminal to compact and wait for its result | Compacted, declined, or failed                          |\r\n\r\n### `link_send`\r\n\r\nSend a message to one other terminal. The sender returns immediately.\r\n\r\n| Parameter | Type     | Description          |\r\n| --------- | -------- | -------------------- |\r\n| `to`      | `string` | Target terminal name |\r\n| `message` | `string` | Message content      |\r\n\r\nWhen a message reaches the target terminal, it enters the receiver's inbox. Messages arriving close together are usually delivered as one batch, in arrival order, each batch one `[Link: N message(s) received]` block, with every message under a `From \"name\":` header. The batching window, the size caps that can split a batch and what can hold one back are in [Inbox](#inbox).\r\n\r\nThe receiver's state is read when that batch is delivered, not when it is sent. If the receiver is still running then, the batch is steered into that run at Pi's next safe boundary — current tool calls finish first, before the next LLM call. Otherwise it starts a turn. There is no way to send without entering the receiver's reasoning.\r\n\r\nEach send has exactly one recipient; there is no fan-out.\r\n\r\nTargets are pre-validated against the terminals in your group, so a definite typo, an offline name and a name outside your group all fail the same way. Sending to yourself is rejected. For a client, a successful send means the message was handed to its connection to the hub. It does not confirm that the hub routed it or that the receiver saw it — see [Message Routing](#message-routing--error-handling). A reply, when one comes, is an ordinary later `link_send` from the other side; nothing correlates it to your message or produces it automatically.\r\n\r\n### `link_list`\r\n\r\nLists the connected terminals in your [group](#configuration), with role info, status, working directory, context usage, and self-identification. Takes no parameters. Your own status and context are read when you run `link_list`; for other terminals, you see their most recently reported values, which may be slightly behind.\r\n\r\nEach terminal reports its current working directory on connect. `link_list` shows the full absolute path.\r\n\r\nEach terminal also reports its LLM context usage, rendered as `45K/272K (17%)` — tokens used over the context window, with percent. Briefly after compaction it shows as `?/272K` until the next measured context value is available.\r\n\r\nEach terminal's status is derived automatically from Pi lifecycle events - agents can't set it manually. Four states:\r\n\r\n| Status            | Meaning                                                                            |\r\n| ----------------- | ---------------------------------------------------------------------------------- |\r\n| `idle (2m)`       | Waiting for user input                                                             |\r\n| `thinking (3s)`   | Work Pi has not settled yet — the LLM call, plus any automatic retry or compaction |\r\n| `tool:bash (12s)` | The first still-active call, not a list                                            |\r\n| `compacting (8s)` | Manual compaction gate raised; messages held                                       |\r\n\r\nPi can run tools in parallel, so `tool:<name>` names the first still-active call Pi reported — one call that really is still running, not a list of every concurrent one — and it advances only when that call ends, staying `tool:<name>` until the last one does.\r\n\r\nOnly a manual compaction shows `compacting`; while it does, messages to that terminal are held — see [Inbox](#inbox).\r\n\r\nDurations are computed at render time from a `since` timestamp - no timer traffic over the wire. Terminals that just joined with no status data yet render as blank, not fake idle.\r\n\r\nWorking directories use full absolute paths in tool output. In the TUI (`/link`), paths are shortened to `~/...` when possible to keep the display compact.\r\n\r\n**Example output:**\r\n\r\n```\r\nConnected terminals:\r\n  • builder (you)  idle (12s)  · 45K/272K (17%)\r\n    cwd: /home/me/my-project\r\n  • researcher  thinking (3s)  · ?/272K\r\n    cwd: /home/me/my-project\r\n  • reviewer  idle (1m)  · 90K/272K (33%)\r\n    cwd: /home/me/my-project\r\n```\r\n\r\n### `link_compact`\r\n\r\nAsk another terminal to compact its context window and wait up to 300 seconds for a result. The target may compact successfully, decline, or return an error. After a successful result, the next call can dispatch work to the freshly trimmed worker.\r\n\r\n| Parameter      | Type     | Description                                            |\r\n| -------------- | -------- | ------------------------------------------------------ |\r\n| `to`           | `string` | Target terminal name                                   |\r\n| `instructions` | `string` | Optional custom compaction instructions for the target |\r\n\r\n- **Success** result: `Compacted \"<name>\"`.\r\n- **Busy decline** — the target accepts only when Pi reports its session idle **and** no manual compaction holds its delivery gate, so an active run, an automatic retry, an automatic compaction and a queued continuation all decline immediately with `reason: \"busy\"` without interrupting the work in progress.\r\n- **The other declines** — a runtime offering no compaction capability declines with `reason: \"unsupported\"` and is never asked to compact; a session under the runtime's threshold declines with `Compact on \"<target>\" not done: Nothing to compact (session too small)`; a target that has done nothing since its last compaction declines with `Compact on \"<target>\" not done: Already compacted`.\r\n- **Self-target rejection** — calling `link_compact` on yourself returns a `self_target` error (`Cannot compact yourself.`).\r\n- **Flat 300-second timeout** — the timeout bounds the caller's wait only; nothing aborts the target, so a timed-out call may mean the compaction is still running.\r\n- Any terminal can request compaction on another **in its group**.\r\n\r\nThe mechanics behind these outcomes — what an accepted request runs, the delivery gate it raises, what releases that gate after a cancellation, and what aborting the caller does — are in [Remote compaction](#remote-compaction).\r\n\r\n---\r\n\r\n## Slash Commands\r\n\r\n| Command             | Purpose                                                                                                                  |\r\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------ |\r\n| `/link`             | Show link status (name, role, online count, agent status, context usage, and cwd per terminal)                           |\r\n| `/link-name [name]` | Rename and save as this session's preferred link name. With no argument, adopts the Pi session name. Restored on resume. A name with `@` also sets your group. |\r\n| `/link-connect`     | Connect to Pi Link (works anytime, with or without `--link`)                                                             |\r\n| `/link-disconnect`  | Disconnect from Pi Link and suppress auto-reconnect (overrides `--link`)                                                 |\r\n\r\n### Examples\r\n\r\n```\r\n> /link\r\n⚡ Link: builder (hub) · 3 online\r\n  builder: idle (12s) · 45K/272K (17%)\r\n    cwd: ~/my-project\r\n  worker-1: thinking (3s) · ?/272K\r\n    cwd: ~/my-project\r\n  worker-2: tool:bash (5s) · 180K/272K (66%)\r\n    cwd: ~/other-project\r\n\r\n> /link-name orchestrator\r\n✓ Renamed to \"orchestrator\"\r\n\r\n> /link-name\r\n✓ Renamed to \"my-session\"\r\n\r\n> /link-disconnect\r\n✓ Disconnected from link\r\n\r\n> /link-connect\r\n✓ Joined link as \"orchestrator\" (3 online)\r\n```\r\n\r\nWith no argument, `/link-name` adopts the Pi session name. `/link-connect` joins an existing hub if one is running; otherwise it starts the hub.\r\n\r\n**Name persistence:** `/link-name` saves your preferred name to the session. Resume later and it's restored automatically. If the name is taken, the hub assigns a variant (e.g., `\"builder-2\"`), but your preferred name stays saved for the next reconnect. See [Name Uniqueness & Persistence](#name-uniqueness--persistence) for details.\r\n\r\nSee [Configuration](#configuration) for details on `--link`, `/link-connect`, and `/link-disconnect` behavior.\r\n\r\n---\r\n\r\n## CLI: `pi-link`\r\n\r\n`pi-link` is the optional shell launcher installed with `npm i -g pi-link`. It resolves sessions by name, lists them, and asks the running hub who is connected.\r\n\r\n### Session Resume\r\n\r\nPi's `--session` flag requires a file path, not a display name. `pi-link` bridges this — it resolves a session by name and launches Pi directly:\r\n\r\n```bash\r\npi-link worker-1                # resume or create session \"worker-1\"\r\npi-link worker-1 --model sonnet # with extra Pi flags\r\n```\r\n\r\nHow it works: `pi-link worker-1` scans Pi's session directory, finds the session named \"worker-1\", and spawns `pi --session <path> --link`. Session-dir resolution matches Pi's lookup order: `PI_CODING_AGENT_SESSION_DIR` env > `<cwd>/.pi/settings.json` `sessionDir` > `<agentDir>/settings.json` `sessionDir` > default `<agentDir>/sessions/`. `<agentDir>` follows `PI_CODING_AGENT_DIR` and defaults to `~/.pi/agent/`.\r\n\r\nLookup is **scoped to the current cwd by default**; pass `--global` (`-g`) to consider sessions in any cwd. A scoped lookup stops reading a session file as soon as its header names another cwd, so it never learns the names of sessions belonging to other directories, and its cost tracks this cwd's own history rather than the history of unrelated projects.\r\n\r\n- **One match in scope** → resumes that session\r\n- **No match in scope** → creates a new session in the current cwd, and advises `--global` without claiming a session exists elsewhere.\r\n- **Multiple matches in scope** → prints candidates to stderr, exits 1\r\n- **Conflicting flags** (`--session`, `--continue`, `--resume`, `--fork`, etc.) → rejected with an error\r\n\r\n### Discovering sessions\r\n\r\n`pi-link --list` shows pi-link sessions in the current cwd; `pi-link --list --global` (or `-g`) lists them across all directories. Sorted by last activity — starting a session with the same name it already has does not bump recency; only real activity (messages, tool calls, edits, name changes) does.\r\n\r\n```\r\n$ pi-link --list\r\nNAME             MODIFIED  MESSAGES  ID\r\nbuilder          2m ago    4632      6332faab\r\nresearcher       5m ago    1493      20d43841\r\n\r\nResume: pi-link <name>\r\n```\r\n\r\nWith `--global`:\r\n\r\n```\r\n$ pi-link --list --global\r\nNAME             CWD                   MODIFIED  MESSAGES  ID\r\nbuilder          ~/my-project          2m ago    4632      6332faab\r\nresearcher       ~/other-project       5m ago    1493      20d43841\r\n\r\nResume: pi-link <name>\r\n```\r\n\r\n`--global` adds a `CWD` column with `~` substituted for `$HOME`. Output is plain when piped (`NO_COLOR` honored).\r\n\r\n`pi-link <name>` and `pi-link --resolve <name>` follow the same scoping: local cwd by default, `--global` (or `-g`) widens. A local miss never silently jumps cwds: it says the name was not found here and suggests `--global`. Because a local scan skips sessions from other cwds without reading them, that suggestion is advice rather than a report that a match exists elsewhere - run it with `--global` to find out.\r\n\r\nFor scripting, `pi-link --resolve <name>` prints just the session path (machine-readable, no other output). Exit codes: `0` on single match, `1` if ambiguous (multiple matches printed to stderr), `2` if not found.\r\n\r\n### `--status`: who is connected right now\r\n\r\n`--list` and `--status` answer different questions. `--list` reads session files on disk and answers *which sessions exist in history on this machine* — a session that died days ago still appears. `--status` asks the running hub and answers *who is connected to the link at this instant* — it is held in the hub's memory, so it needs a live hub and reports nothing about sessions that are merely saved.\r\n\r\n```\r\n$ pi-link --status\r\nNAME        STATUS               CONTEXT          CWD\r\nbuilder     idle (7m)            92K/272K (34%)   ~/my-project\r\nresearcher  tool:link_send (3s)  ?/272K           ~/my-project\r\nreviewer    compacting (12s)     1.3M/2.0M (63%)  ~/other-project\r\nworker-1    ?                    ?                ?\r\n```\r\n\r\nThe hub is listed first, then clients sorted by name. A `?` means the hub could not report that field — for `worker-1` above, it has registered but has not yet sent its first status update. **`?` means unknown, not idle.** Reading it as idle is the mistake this command exists to prevent.\r\n\r\nThe word for what this proves is **connected**, not alive: it shows terminals registered with the hub that answered. A terminal whose process is wedged still holds its connection, so `--status` never claims a terminal is healthy — only that it is on the link.\r\n\r\nQuerying is read-only. It does not register a terminal, does not appear in anyone's `link_list`, and broadcasts nothing to the fleet.\r\n\r\n#### Scripting\r\n\r\n`--status --json` writes the hub's response body to stdout unchanged once it passes the same checks the table uses, and the endpoint is plain HTTP, so `curl` works too:\r\n\r\n```bash\r\npi-link --status --json\r\ncurl 127.0.0.1:9900/status\r\n```\r\n\r\nTwo of the terminals above, as the hub reports them:\r\n\r\n```json\r\n{\r\n  \"hub\": \"builder\",\r\n  \"port\": 9900,\r\n  \"terminals\": [\r\n    {\r\n      \"name\": \"builder\",\r\n      \"role\": \"hub\",\r\n      \"status\": \"idle\",\r\n      \"sinceSeconds\": 420,\r\n      \"cwd\": \"/home/me/my-project\",\r\n      \"context\": { \"tokens\": 92000, \"window\": 272000 }\r\n    },\r\n    {\r\n      \"name\": \"worker-1\",\r\n      \"role\": \"client\",\r\n      \"context\": null\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n| Field | Meaning |\r\n| --- | --- |\r\n| `hub` | Name of the hub that answered; always equal to `terminals[0].name` |\r\n| `port` | Port the hub is bound to |\r\n| `terminals` | Hub first, then clients sorted by name — never empty |\r\n| `terminals[].name` | Terminal name as the hub knows it |\r\n| `terminals[].role` | `hub` for the first entry, `client` for the rest |\r\n| `terminals[].status` | `idle`, `thinking`, `compacting`, or `tool:<name>` |\r\n| `terminals[].sinceSeconds` | Whole seconds in that status, rounded — relative, so no clock agreement is needed |\r\n| `terminals[].cwd` | Working directory; **omitted** when the hub does not know it |\r\n| `terminals[].context` | **Always present**: `null` when there is no snapshot, otherwise `{ tokens, window }` |\r\n\r\n**`status` and `sinceSeconds` are one optional pair — both present or both absent.** They are absent for a terminal the hub has registered but not yet heard from. Absence means unknown; do not substitute a default.\r\n\r\n**`context` uses `null`, not omission.** The field is always there; `null` means no snapshot exists, and `{ \"tokens\": null, \"window\": 272000 }` means a snapshot exists but the token count is mid-refresh (rendered `?/272K`).\r\n\r\nTwo things may grow, and consumers must tolerate both:\r\n\r\n- **Unknown fields.** Documented fields are frozen; new ones may be added, so ignore what you do not recognize rather than rejecting the payload.\r\n- **Unknown `status` values.** The vocabulary is not frozen — it has grown before, gaining `compacting` in 0.3.0. Treat any non-empty string as possible; the CLI renders an unrecognized value as-is instead of refusing the response.\r\n\r\nEverything above is what the hub sends. The CLI validates only the fields its table prints — `terminals` as an array of objects with a string `name`, an optional string `cwd`, `context` as `null` or `{ tokens, window }`, and `status`/`sinceSeconds` together or absent. A body failing that exits 1 with the unsupported message instead of a stack trace, **in both modes**. A zero exit certifies nothing beyond printability — not hub-first order, not `terminals[0].name === hub`, not a numeric `port`, not even that a hub answered; verify what you depend on yourself. Once the body passes, `--json` writes it as received, unreformatted.\r\n\r\n#### Exit codes\r\n\r\n| Code | Meaning | Message |\r\n| --- | --- | --- |\r\n| `0` | Something answered with a printable payload | — |\r\n| `2` | No hub answered | `No link hub running on :9900.` |\r\n| `1` | Usage error, or an answer the table cannot print | `Link hub does not support /status — update pi-link and restart terminals.` |\r\n\r\nThe two failure messages are deliberately distinct, so a script can tell *no usable answer* from *an answer this CLI cannot read* without parsing anything else. Exit `2` covers both nothing listening and a listener that accepts the request but does not answer within two seconds — every timeout is exit `2`. Exit `1` covers a listener that responds with something the table cannot read — for example a pi-link 0.3.0 hub, which answers plain HTTP with `426 Upgrade Required`, so an out-of-date fleet lands here deterministically rather than looking like an outage.\r\n\r\nExit `2` means no hub answered **at that instant**. When a hub exits, a surviving client promotes itself to replace it, which takes roughly 2–5 seconds — poll again before concluding the fleet is down.\r\n\r\n#### Notes\r\n\r\n`PI_LINK_PORT` changes only where the CLI looks; the extension always binds the hub to `9900`. The value is not validated — an unusable one simply fails the request and is reported back to you in the exit-`2` message.\r\n\r\nThe endpoint is bound to `127.0.0.1` with no authentication, the same trust boundary as the WebSocket surface it shares a port with: any process on this machine can already connect to the link.\r\n\r\nFinally, `--status` and `--json` belong to the wrapper only until a session name appears. After one, they are pi's: `pi-link mybot --status` forwards `--status` to pi untouched, exactly like any other passthrough flag.\r\n\r\n---\r\n\r\n## Configuration\r\n\r\nLink is **off by default**. Without `--link`, `--link-name`, or `pi-link`, a fresh session is completely silent — no status bar, no connections, no warnings.\r\n\r\n**Naming concepts**\r\n\r\n- **link name** — identity used on the network (visible in `link_list`, `/link`, and messages).\r\n- **group** — the text after the first `@` in a link name, compared exactly. `archon@pi-link` is in `pi-link`, `a@g@h` is in `g@h`, and a name without `@` (or ending in one, like `a@`) belongs to the single implicit group of plain names. Comparison is **case-sensitive**: `a@Team` and `a@team` are different groups. `link_list`, `link_send`, `link_compact`, `/link` and the footer see only your own group; the hub still routes for every group, and `pi-link --status` shows all of them. Renaming with `/link-name` moves you.\r\n- **Pi session name** — identity Pi gives the session itself; lives in the session JSONL's latest `session_info` entry.\r\n- **saved link name** — the link name persisted to the session, restored on resume. Set by `/link-name`, `pi-link <name>`, or `pi --link-name <name>`.\r\n- **`--link-name` flag vs `/link-name` command** — same concept (the link name) at different times (startup vs mid-session).\r\n\r\n| What you want                        | Use                     |\r\n| ------------------------------------ | ----------------------- |\r\n| Resume/create a named session        | `pi-link <name>`        |\r\n| Stable link identity, normal Pi flow | `pi --link-name <name>` |\r\n| Quick try, random name               | `pi --link`             |\r\n| Already in a session                 | `/link-connect`         |\r\n| Disconnect mid-session               | `/link-disconnect`      |\r\n\r\n`pi-link <name>` resumes/creates a session AND sets your link identity in one step. `pi --link-name <name>` sets only the link identity, leaving Pi's normal session selection (latest in cwd, or fresh) untouched.\r\n\r\n**Name normalization:** Link names are normalized — leading/trailing whitespace removed and internal whitespace runs collapsed to a single space. `/link-name \"build   lead\"` saves and shows as `build lead`.\r\n\r\n**Name precedence:** `pi --link-name` > `pi-link <name>` > saved `/link-name` > Pi session name > random `t-xxxx`. _(The `pi-link` wrapper itself does not accept `--link-name`; pick one or the other.)_\r\n\r\n`/link-connect` and `/link-disconnect` save their intent to the session — resume later and the connection state is restored without needing the flag. Explicit user intent takes precedence over `--link`.\r\n\r\n---\r\n\r\n## Troubleshooting\r\n\r\n### Port 9900 is already in use\r\n\r\nIf another process occupies port 9900, the terminal can't become the hub. It tries to connect as a client first, and that attempt fails within 5 seconds even against a listener that accepts the connection and never speaks WebSocket - so the terminal falls back to trying the hub role and then retries after 2-5 seconds, rather than sitting offline indefinitely. Free the port or modify `DEFAULT_PORT` in `index.ts` - see [Limitations](#limitations--design-decisions).\r\n\r\n### `link_compact` reports a busy target\r\n\r\nA `link_compact` request does not interrupt work the target is still doing, so it declines while that terminal is unsettled — see [`link_compact`](#link_compact) for the full list of what counts. Try again once `/link` or `link_list` reports the target idle, keeping in mind that idle is what was true a moment ago, not a reservation: the target can start working again before your request lands. `link_send` is different — it enters or steers the target's reasoning instead of declining just because it is busy.\r\n\r\n### I sent a message but got no reply\r\n\r\nA successful send is not a confirmation. On a client it means the message was handed to the hub connection, not that the target received it or acted on it — and replies are ordinary messages the other agent chooses to send, so nothing produces one automatically. Run `/link`, or ask your model to call `link_list`, and check the target: confirm the name is still exactly right, and note that a target in the `compacting` state holds messages that reach it until that state clears. Otherwise silence may mean the work is still running or that no reply was sent. If the target cancelled a `/compact`, pi-link cannot see that, so its messages may stay held until its next run or up to five minutes — see [Remote compaction](#remote-compaction).\r\n\r\n### `pi-link --status` reports no hub, or an unsupported one\r\n\r\nThe two messages mean different things. `No link hub running on :9900.` (exit `2`) means nothing answered — either the link is genuinely down, or you caught it during the 2–5 second window while a client promotes itself to hub, so poll again before believing it. `Link hub does not support /status — update pi-link and restart terminals.` (exit `1`) means something answered but the response did not pass the CLI's checks — for example, a pi-link 0.3.0 hub, which predates the endpoint. Updating is not enough on its own: the running terminals keep the old hub alive until they restart. See [`--status`: who is connected right now](#--status-who-is-connected-right-now).\r\n\r\n### Terminals don't see each other\r\n\r\n- Compare the part after `@`, exactly as spelled: `archon@pi-link` and `builder` are in different [groups](#configuration), and so are `a@Team` and `a@team`; they are invisible to each other by design. `pi-link --status` shows every group and settles it.\r\n- Verify both terminals are on the same machine (the link only works on `127.0.0.1`).\r\n- Run `/link` in each terminal to check status.\r\n- Ensure port 9900 isn't blocked or occupied by a non-link process.\r\n\r\n### Hub promotion loses state\r\n\r\nWhen the hub exits, a surviving client promotes itself in roughly 2–5 seconds; messages in flight during that gap can be lost, and a terminal that reconnects as a client after running as a hub-assigned variant like `builder-2` may come back under its preferred name, or vice versa. This is by design — see [Hub Promotion](#hub-promotion) for how promotion works and [Limitations](#limitations--design-decisions) for the decision behind it.\r\n\r\n---\r\n\r\n## Limitations & Design Decisions\r\n\r\n| #   | Decision                                  | Rationale / Impact                                                                                                                                               |\r\n| --- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |\r\n| 1   | **No authentication**                     | Any localhost process can connect to port 9900. Acceptable for local dev; don't expose the port externally.                                                      |\r\n| 2   | **Hardcoded port (9900)**                 | Not configurable without editing `DEFAULT_PORT` in `index.ts`. Could conflict with other services on the same port.                                              |\r\n| 3   | **Race-based hub promotion**              | Non-deterministic. Reconnect order can change which terminal holds a hub-assigned suffix; in-flight messages can be lost. Simple but imperfect.                  |\r\n| 4   | **No offline backlog**                    | A definitely absent target is rejected and nothing is stored, so a terminal that reconnects receives no messages it missed while offline.                        |\r\n| 5   | **Client rename triggers full reconnect** | Changing a client's name requires a new `register` message, so the client disconnects and reconnects. Hub renames are handled in-place.                          |\r\n| 6   | **Single-machine / localhost-only**       | Link only binds to `127.0.0.1`; terminals on different machines cannot join.                                                                                     |\r\n| 7   | **Callbacks are conventional**            | Async results arrive as uncorrelated messages, not protocol responses: no request identifier, and a callback exists only because the receiver chose to send one. |\r\n| 8   | **Groups isolate attention, not access**  | No auth: any process may pick any group, and `pi-link --status` shows all of them; isolation needs one version everywhere, so upgrade and restart together.      |\r\n\r\n---\r\n\r\n## Dependencies\r\n\r\nAt runtime pi-link needs one package, `ws` (^8.20.0), the WebSocket server and client; `pi install` brings it in. Development adds `@types/ws` (^8.18.1) for its type definitions. Everything else comes from Pi itself and needs no install: `@earendil-works/pi-coding-agent` for the SDK types (`ExtensionAPI`, `ExtensionContext`) and `VERSION`, `@earendil-works/pi-tui` for the Text widget used in custom message rendering, and `typebox` for the tool parameter schemas.\r\n\r\n> See [Prerequisites](#prerequisites) for supported Pi versions.\r\n\r\n---\r\n\r\n## Internals\r\n\r\n> This section covers implementation details for contributors and developers who want to understand or modify the extension's internals.\r\n\r\n### Hub-Spoke Topology\r\n\r\nThe network topology is **hub-spoke (star)**:\r\n\r\n```\r\n                       +-----------+\r\n                       |    Hub    |\r\n                       |   :9900   |\r\n                       +-----+-----+\r\n                             |\r\n              +--------------+--------------+\r\n              |              |              |\r\n          +---+---+      +---+---+      +---+---+\r\n          | pi-2  |      | pi-3  |      | pi-4  |\r\n          |client |      |client |      |client |\r\n          +-------+      +-------+      +-------+\r\n```\r\n\r\n- The **first terminal** to start becomes the **hub** - it runs a `WebSocketServer` on `127.0.0.1:9900`.\r\n- **Subsequent terminals** connect as **clients** via plain WebSocket.\r\n- All messages route **through the hub**; clients never talk directly to each other.\r\n\r\n### Auto-Discovery Protocol\r\n\r\nThe discovery sequence runs on startup (with `--link` or `pi-link`) or when `/link-connect` is used. See [Configuration](#configuration) for details.\r\n\r\nThe sequence is a simple fallback:\r\n\r\n1. Attempt to connect as a **client** to `127.0.0.1:9900`. The WebSocket opening handshake is bounded at **5 seconds**; a listener that accepts the connection but never completes the upgrade fails the attempt instead of holding it open.\r\n2. If connection fails → become the **hub** (start a WebSocket server on that port).\r\n3. If both fail (rare race condition) → retry after a randomized 2-5 second backoff.\r\n\r\nOnly **one attempt runs at a time**, across both steps. Startup, a retry and `/link-connect` arriving while an attempt is in flight all join that attempt rather than opening a second connection, so a terminal cannot register twice or leave a second socket behind.\r\n\r\n### Hub Promotion\r\n\r\nWhen the hub disconnects, clients detect the WebSocket close event, enter `\"disconnected\"` state, and call `scheduleReconnect()`. The **first terminal to retry** becomes the new hub via the same initialize-or-fallback flow.\r\n\r\nThere is **no explicit leader election** - promotion is race-based.\r\n\r\nThe old hub transfers no shared roster or routing state to its successor: every terminal drops the snapshots it held for the network that went away - names, statuses, working directories and context - while its own local state, the inbox included, survives. Clients wait the same randomized 2-5 second backoff before retrying, so promotion usually lands in that window rather than within a guaranteed bound, and messages in flight while no hub is listening can be lost. Survivors that rejoin the winner as clients re-register with their saved preferred name rather than their prior runtime name (see [Name Uniqueness & Persistence](#name-uniqueness--persistence)), so a terminal that was `builder-2` may come back as `builder`, or take the suffix this time. The winner does not re-register at all: it starts the hub keeping the identity it was already running under, unless a `/link-name` rename was in flight when the old hub vanished, which it then adopts.\r\n\r\n### Protocol\r\n\r\nThe wire protocol consists of the following message types, all serialized as JSON over WebSocket frames. Cwd and context fields are optional.\r\n\r\n| Type               | Direction       | Purpose                                                                                             |\r\n| ------------------ | --------------- | --------------------------------------------------------------------------------------------------- |\r\n| `register`         | Client → Hub    | First message after connecting; requests a name, optionally reports cwd and context                 |\r\n| `welcome`          | Hub → Client    | Confirms assigned name, terminal list + status/cwd/context snapshots                                |\r\n| `terminal_joined`  | Hub → All       | Broadcast when a terminal joins; may include cwd and context                                        |\r\n| `terminal_left`    | Hub → All       | Broadcast when a terminal disconnects                                                               |\r\n| `chat`             | Any → Any       | Message delivered to the receiver's model: steered into a running agent, or starting a turn if idle |\r\n| `compact_request`  | Any → Any       | Request a remote terminal to compact its context; awaits a response                                 |\r\n| `compact_response` | Any → Any       | Completion/failure response for a compact_request                                                   |\r\n| `status_update`    | Any → Hub → All | Terminal broadcasts agent status change; carries updated context                                    |\r\n| `error`            | Hub → Client    | Error notification                                                                                  |\r\n\r\n### Message Flow Examples\r\n\r\n**Joining the link:**\r\n\r\n```\r\nClient                         Hub\r\n  |                             |\r\n  | register {name:\"builder\",   |\r\n  |           cwd:\"C:\\\\Users\\\\...\"} |\r\n  |---------------------------->|\r\n  |                             |\r\n  | welcome {name, terminals,   |\r\n  | statuses, cwds}             |\r\n  |<----------------------------|\r\n  |                             |\r\n```\r\n\r\nHub then broadcasts `terminal_joined` to the other connected terminals. The `welcome` message includes status, cwd, and context snapshots for all connected terminals (fields omitted above for brevity). `terminal_joined` also includes the new terminal's optional cwd and context.\r\n\r\n**Sending a chat message:**\r\n\r\n```\r\nClient A            Hub              Client B\r\n  |                  |                  |\r\n  | chat {to:pi-2}   |                  |\r\n  |----------------->|                  |\r\n  |                  | chat {from:A}    |\r\n  |                  |----------------->|\r\n  |                  |                  |\r\n```\r\n\r\n### Name Uniqueness & Persistence\r\n\r\nThe hub enforces unique terminal names via a `uniqueName()` function. If `\"builder\"` is already taken, the next terminal requesting that name is assigned `\"builder-2\"`, then `\"builder-3\"`, and so on.\r\n\r\nDefault names are random 4-character hex IDs: `t-a1b2`, `t-c3d4`, etc.\r\n\r\n**Groups (`local@group`):** see [Configuration](#configuration) for the rule a user needs. Internally, the hub serves every group over one set of connections; for the targeted traffic it handles, `routeMessage()` treats a target in another group as not found. Collisions suffix the local part only, so a deduped name never changes group: `builder` becomes `builder-2`, `archon@pi-link` becomes `archon-2@pi-link`.\r\n\r\n**Persistence:** `/link-name` saves the preferred name to the session via `pi.appendEntry(\"link-name\", { name })`. On session resume, the saved name is restored and requested from the hub. Startup naming (`pi-link <name>`, `pi --link-name <name>`) persists the same way - hub-assigned variants like `\"builder-2\"` are not saved. On reconnect, the terminal always requests the preferred name, not the last runtime name.\r\n\r\n**Rename guards:**\r\n\r\n- If you're already using the requested name, `/link-name` returns early (`\"Already using...\"`).\r\n- On the hub, renaming checks if the name is taken by another connected client before accepting the change.\r\n- On a client, the rename triggers a reconnect; the hub enforces uniqueness during re-registration and may assign a different name if taken.\r\n\r\n**Unregistered client guard:** The hub ignores all non-`register` messages from clients that haven't completed registration, preventing protocol violations from malformed or out-of-order messages.\r\n\r\n### State Management\r\n\r\n| State Field               | Type                                  | Purpose                                                                                                                     |\r\n| ------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |\r\n| `role`                    | `\"hub\" \\| \"client\" \\| \"disconnected\"` | Current network role                                                                                                        |\r\n| `connectionAttempt`       | `record \\| null`                      | The one establishment attempt in flight: its shared promise plus any pending socket/server. Object identity marks the owner |\r\n| `agentRunning`            | `boolean`                             | Between `agent_start` and `agent_settled` — not `agent_end`; drives status only                                             |\r\n| `compactRunning`          | `boolean`                             | Whether a remote request holds this terminal's delivery gate                                                                |\r\n| `localCompacting`         | `boolean`                             | Whether a `manual`-reason compaction has raised the delivery gate                                                           |\r\n| `activeTools`             | `Map`                                 | `toolCallId` → `toolName` for calls still running; the first drives status                                                  |\r\n| `stateSince`              | `number`                              | Timestamp of last status change (used for duration display)                                                                 |\r\n| `currentCwd`              | `string`                              | Current working directory reported to peers on connect                                                                      |\r\n| `inbox`                   | `array`                               | Queued incoming messages awaiting delivery                                                                                  |\r\n| `flushTimer`              | `Timer \\| null`                       | Pending inbox flush; armed by the first queued message and not moved afterwards                                             |\r\n| `compactDeadline`         | `Timer \\| undefined`                  | Backstop releasing the inbox if a compaction reports no ending                                                              |\r\n| `pendingCompactResponses` | `Map`                                 | Outstanding compact requests awaiting bounded responses                                                                     |\r\n| `disposed`                | `boolean`                             | Set on shutdown; guards WebSocket callbacks against stale context                                                           |\r\n| `startupConnectTimer`     | `Timer \\| null`                       | Deferred startup connect so Pi's startup cycle completes first                                                              |\r\n| `manuallyDisconnected`    | `boolean`                             | Set by `/link-disconnect`; suppresses auto-reconnect                                                                        |\r\n\r\n### Message Routing & Error Handling\r\n\r\n`routeMessage()` returns a `boolean` indicating delivery status:\r\n\r\n- **Hub** - delivery is authoritative. If a chat target is not connected, the hub sends a protocol-level error back to a client sender; a local hub sender already receives the failed delivery result. A `compact_request` to an unknown target gets a synthesized `compact_response` (`ok: false`, `reason: \"not_found\"`), so the bounded compact call fails fast.\r\n- **Client** - delivery is optimistic (`true` means \"sent to hub\"). The hub handles routing and errors via the protocol.\r\n\r\n### Connection Lifecycle\r\n\r\nInternally, teardown is split into two functions:\r\n\r\n- **`disconnect()`** - closes sockets, clears connection state, resolves pending promises. Used by `/link-disconnect` and called internally by `cleanup()`.\r\n- **`cleanup()`** - calls `disconnect()`, sets `disposed = true`, clears `ctx`. Used on `session_shutdown`.\r\n\r\nThree helpers protect WebSocket callbacks from stale extension context:\r\n\r\n- **`getUi()`** - safely accesses `ctx.ui`, returns `null` if the context is invalidated.\r\n- **`notify()`** - wraps `getUi()?.notify()` for safe notification delivery.\r\n- **`isRuntimeLive()`** - returns `false` if `disposed` or context is stale; checked before processing any incoming WebSocket message.\r\n\r\nStartup connect is deferred via `scheduleStartupConnect()` (`setTimeout(0)`) so Pi's startup cycle completes and the extension context is fully valid before WebSocket work begins.\r\n\r\n`disconnect()` also cancels the connection attempt in flight: it invalidates the attempt first, then closes whatever it had pending - a dialing socket or a binding server - so nothing can finish establishing after the user asked to disconnect. The startup and retry timers are cancelled on the same path, so `/link-disconnect` before the deferred startup connect opens nothing at all. Because ownership is tracked per attempt, a callback from a superseded or cancelled transport is inert: only the established socket (`ws`) may deliver messages or tear down client state, and only the established server (`wss`) may accept clients.\r\n\r\nThe `manuallyDisconnected` flag distinguishes user-initiated disconnects (`/link-disconnect`) from connection loss. When set, `scheduleReconnect()` is suppressed - the terminal stays offline until `/link-connect` is explicitly called.\r\n\r\n### Agent Lifecycle Integration\r\n\r\nThe extension hooks into Pi's agent lifecycle events:\r\n\r\n- **`agent_start`** → Sets `agentRunning = true`, which drives status. Clears the local compaction gate (`localCompacting`, never a remote request's `compactRunning`) - safe only under the current deployment, not by Pi alone: Pi refuses to start an ordinary prompt during a manual compaction, and pi-link holds back the one delivery path that refusal does not cover, so nothing can start a run mid-compaction. Another extension starting a turn during one would void that. Status normally becomes `thinking`.\r\n- **`agent_end`** → Drops any tool calls still recorded, so an unmatched one falls back to `thinking` instead of staying pinned. It deliberately does **not** end the run: Pi may still retry, compact automatically, or drain a queued continuation, so the status stays `thinking`.\r\n- **`agent_settled`** → The authoritative end of a run: Pi emits it once no retry, compaction or queued continuation is left. Status becomes `idle` — unless the event's context reports Pi busy again, which means another extension started a new run during settlement, and that newer run keeps reporting `thinking`.\r\n- **`tool_execution_start`** → Records that call. It is displayed unless an earlier call is still running.\r\n- **`tool_execution_end`** → Drops that one call. The display stays `tool:<name>` while other calls remain, moves to the next call when the displayed one ends, and returns to `thinking` after the last one while the agent run continues.\r\n- **`session_before_compact`** → For a manual compaction, raises the gate that holds inbox delivery. Automatic (threshold/overflow) compaction is left alone: it runs inside the agent run, where Pi already queues and drains steered messages itself.\r\n- **`session_compact`** → Clears the local compaction gate and force-pushes a `status_update` so peers see the new (post-compaction) context usage immediately.\r\n- **`model_select`** → Force-pushes a `status_update`: the context window, and so the usage percentage, belongs to the model.\r\n- **`session_tree`** → Force-pushes a `status_update`: context usage belongs to the active branch.\r\n- **`session_shutdown`** → Full cleanup via `cleanup()`: closes all sockets, resolves pending promises, and disposes the extension.\r\n\r\nThe five agent and tool handlers — `agent_start`, `agent_end`, `agent_settled` (when Pi still reports the session idle), `tool_execution_start` and `tool_execution_end` — each recompute the status and hand it to `pushStatus()`, which publishes only when the display identity changed since the last publish: a second concurrent tool starting behind the one already shown is silent, and so is a mutation that leaves a raised compaction gate on display. The session handlers keep the separate paths described in their own bullets, and `session_shutdown` cleans up rather than pushing. `disconnect()` is the only thing that clears the stored baseline, and `pushStatus()` returns immediately while the terminal is disconnected, so handler events in that interval restore nothing; once connected again, either a client's forced `welcome` push or the first of those ordinary handler events restores it. `session_compact`, `model_select` and `session_tree` always push, whether the identity changed or not — including while a remote request's gate still holds the displayed identity.\r\n\r\nStatus updates are push-based: each terminal broadcasts changes to the hub, which fans them out. New joiners receive a status snapshot for all terminals in the `welcome` message. Context updates reuse the same status path, including forced updates after a compaction, a model change and tree navigation.\r\n\r\n### Inbox\r\n\r\nAn arriving `chat` message goes into a local inbox rather than calling `pi.sendMessage()` immediately, so that arrivals close together can be coalesced into a single delivery.\r\n\r\nThe flush pipeline:\r\n\r\n1. **Fixed window** - `scheduleFlush(FLUSH_DELAY_MS)` arms a timer only when none is pending, so the first queued message sets the deadline (about 200ms) and later arrivals join that window without moving it. The window is not sliding: sustained arrivals closer together than the delay cannot postpone delivery.\r\n2. **Compaction gate** - `flushInbox()` returns without rescheduling while the compaction gate is raised.\r\n3. **Batch** - up to 20 messages or ~16 000 chars per delivery (soft cap - the first item is always included even if oversized).\r\n4. **Deliver** - one `pi.sendMessage()` call with a `[Link: N message(s) received]` block. Pi reads the receiver's state at this point — not at send time — to decide whether the batch steers a running agent or starts a turn.\r\n5. **Drain** - if the inbox still has items, reschedule.\r\n\r\nDelivery is held while a `manual`-reason compaction holds this terminal's gate, because a message delivered mid-compaction would be reasoned about against context that is being rebuilt. Two flags gate it: `compactRunning` for a compaction serving a remote `link_compact`, and `localCompacting`, set from `session_before_compact`; a remote request raises `compactRunning` first and adds `localCompacting` only if its compaction reaches that hook, since Pi reports its own reason as `manual` either way. Automatic (threshold/overflow) compaction is deliberately **not** gated: it runs inside the agent run, where Pi already queues steered messages and drains them afterwards. Because a gated flush does not reschedule, the release path is load-bearing: `releaseInbox()` runs after either flag clears and schedules a flush only when neither gate remains, and `compactDeadline` is the backstop for a `localCompacting` left raised, since failure emits no lifecycle ending event that clears it.\r\n\r\n| Constant             | Value   | Purpose                                          |\r\n| -------------------- | ------- | ------------------------------------------------ |\r\n| `FLUSH_DELAY_MS`     | 200     | Batching window, from the first queued message   |\r\n| `BATCH_MAX_ITEMS`    | 20      | Max messages per batch                           |\r\n| `BATCH_MAX_CHARS`    | 16 000  | Soft cap on batch text size (~4K tokens)         |\r\n| `COMPACT_TIMEOUT_MS` | 300 000 | Remote-compact wait, reused as the gate backstop |\r\n\r\n### Remote compaction\r\n\r\nA `compact_request` that passes the capability and idle checks is served by calling `ctx.compact()` — the same operation as `/compact`. The caller is answered when the runtime reports a result through `onComplete`/`onError`. Each call targets one terminal, independent calls can run concurrently, and link participants are cooperating peers: nothing here is privileged.\r\n\r\nServing a request raises the inbox gates in order, and not always both. `compactRunning` is set synchronously before `ctx.compact()` is called; `localCompacting` rises only if execution reaches the manual `session_before_compact` hook, so a runtime that refuses early — nothing to compact, already compacted, or another preparation failure — returns through `onError` without the local gate ever going up. When the compaction does succeed, `session_compact` clears the local gate first, and the later `finish()` clears `compactRunning` and calls `releaseInbox()`, which is what drains the inbox.\r\n\r\nWhat a failure hides is the ending, not the failure itself. A remote compaction reports its error through `onError`, which runs `finish()` and answers the caller, so `compactRunning` always comes back down. But `session_compact` is a success-only ending and pi-link does not listen to Pi's `session_compact_failed`, so a `localCompacting` already raised by the hook stays up until the target's next agent run (`agent_start`), a later successful compaction, or the `compactDeadline` backstop 300 seconds after that gate rose. A human `/compact` has no result callback at all, so those three are its only release paths.\r\n\r\nThose 300 seconds are two independent timers, not one synchronized deadline: the caller's bounded wait uses `COMPACT_TIMEOUT_MS` to stop waiting, and the local gate backstop reuses the same constant only to avoid inventing a second one. Aborting the caller before the request goes out leaves the target untouched; aborting after it goes out stops the caller from waiting and nothing else — the target's compaction, once started, runs to its own end.\r\n\r\n### Rendering\r\n\r\nDelivered link batches render with a styled `⚡ [link]` prefix using the theme's accent color; sender attribution lives in the `From \"name\":` blocks inside the message body, not in the prefix. The link status text in Pi's footer uses `theme.fg(\"dim\", ...)` to match Pi's standard footer styling.\r\n\r\nDeliveries sit in the same background panel Pi draws around extension messages by default, indented by your configured output padding, so link messages line up with the rest of the transcript instead of standing outside it.\r\n\r\nA long delivery is previewed rather than printed whole. Collapsed, it shows the first six rows as they wrap at the current window width, followed by `... (N more lines, ctrl+o to expand)`; a message that already fits in six rows shows in full with no hint. Expanding is Pi's own global toggle — `Ctrl+O` unless you have rebound `app.tools.expand`, and the hint always names your actual binding — so it expands every custom message at once, including messages restored from an earlier session. If you have unbound `app.tools.expand` entirely there is no key to name, so both hints read `bind app.tools.expand to expand` instead. Only the display changes: the full text is what reaches the model, what `link_send` delivered, and what the session file keeps.\r\n\r\nThe same toggle also expands outgoing calls: `link_send` and `link_compact` collapse the message or instructions into a one-line preview — runs of whitespace become single spaces — and keep its first 60 characters, which a narrow terminal may still wrap onto more than one row. Expanding reveals the uncollapsed text with its line breaks and spacing as rendered by Pi. Expanding a call says nothing about delivery — the tool's own result line reports that.\r\n","readmeFilename":"README.md"}