{"_id":"@badliveware/pi-footer-framework","_rev":"7-9b11bda5bac250a5672b25ea6c2eee1c","name":"@badliveware/pi-footer-framework","dist-tags":{"latest":"0.3.2"},"versions":{"0.1.0":{"name":"@badliveware/pi-footer-framework","version":"0.1.0","keywords":["pi-package","pi-extension","footer","ui"],"author":{"name":"BadLiveware"},"license":"MIT","_id":"@badliveware/pi-footer-framework@0.1.0","maintainers":[{"name":"doz3r","email":"flarsson93@gmail.com"}],"homepage":"https://github.com/BadLiveware/pi/tree/main/agent/extensions/public/footer-framework#readme","bugs":{"url":"https://github.com/BadLiveware/pi/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"dist":{"shasum":"942f216fab174e02b3257065c7abda37c8a4c476","tarball":"https://registry.npmjs.org/@badliveware/pi-footer-framework/-/pi-footer-framework-0.1.0.tgz","fileCount":5,"integrity":"sha512-YAFoZjmbWL6ZYKw+dfipCVFD7UGBykI95gIBKFaOrqvzPdFAr7noMPcm8KW4ahxXDOQJdZ8OHaYHFoBhaUmSag==","signatures":[{"sig":"MEQCIBAiVhm1DALB/+VblBRApkB+QloROwRGiyCnqrPfC8y9AiBvGG7y6r8MtbG1kmypU+GBUrZylU7aS7KJKbUc3rDqUQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":41483},"type":"module","engines":{"node":">=20"},"gitHead":"cb0878d4f4de79753ccea89954bfc98db598f88a","_npmUser":{"name":"doz3r","email":"flarsson93@gmail.com"},"repository":{"url":"git+https://github.com/BadLiveware/pi.git","type":"git","directory":"agent/extensions/public/footer-framework"},"_npmVersion":"10.9.7","description":"Configurable footer framework extension for Pi.","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"@mariozechner/pi-ai":"*","@mariozechner/pi-tui":"*","@mariozechner/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-footer-framework_0.1.0_1777196654464_0.1591190946638894","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@badliveware/pi-footer-framework","version":"0.1.1","keywords":["pi-package","pi-extension","footer","ui"],"author":{"name":"BadLiveware"},"license":"MIT","_id":"@badliveware/pi-footer-framework@0.1.1","maintainers":[{"name":"doz3r","email":"flarsson93@gmail.com"}],"homepage":"https://github.com/BadLiveware/pi/tree/main/agent/extensions/public/footer-framework#readme","bugs":{"url":"https://github.com/BadLiveware/pi/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"dist":{"shasum":"6e522102a78c9cd25fdc9c7c147b0ff2a89d7785","tarball":"https://registry.npmjs.org/@badliveware/pi-footer-framework/-/pi-footer-framework-0.1.1.tgz","fileCount":5,"integrity":"sha512-qVeBNvP3lYc+ZXjveacHP3Lavq6KdKkvHQsHKKGUSS8xaNw8Vun4xT4sygmiHOEpIoML6Nm06vxshMEiL4U+XA==","signatures":[{"sig":"MEQCIHGBGq1ZdytT/TpeztTNtNK8NM/rWoCxaFwkaaO9K8/dAiADH+N/xE9x6t5KAtQggwwJhTneoMS2F4k8bWPgLC5ccQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@badliveware%2fpi-footer-framework@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":40455},"type":"module","engines":{"node":">=20"},"gitHead":"c36b71f3d327837c2101ffb79a1c53498c952a89","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a013130a-b69c-4979-baa3-bd9fbb0b7897"}},"repository":{"url":"git+https://github.com/BadLiveware/pi.git","type":"git","directory":"agent/extensions/public/footer-framework"},"_npmVersion":"11.13.0","description":"Configurable footer framework extension for Pi.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"@mariozechner/pi-ai":"*","@mariozechner/pi-tui":"*","@mariozechner/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-footer-framework_0.1.1_1777197978981_0.5057900500199703","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@badliveware/pi-footer-framework","version":"0.2.0","keywords":["pi-package","pi-extension","footer","ui"],"author":{"name":"BadLiveware"},"license":"MIT","_id":"@badliveware/pi-footer-framework@0.2.0","maintainers":[{"name":"doz3r","email":"flarsson93@gmail.com"}],"homepage":"https://github.com/BadLiveware/pi/tree/main/agent/extensions/public/footer-framework#readme","bugs":{"url":"https://github.com/BadLiveware/pi/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"dist":{"shasum":"575b38e301038efd5f70dce29edcf71c339abe30","tarball":"https://registry.npmjs.org/@badliveware/pi-footer-framework/-/pi-footer-framework-0.2.0.tgz","fileCount":7,"integrity":"sha512-UebVMgR3USwIEUuuwj7AixkTNO8reEp8hxTeCiDjYwcDPZ/ChJW5EmQjD0itnnDkw0ExTqMJYcCTXQOCkfeCpg==","signatures":[{"sig":"MEUCIQCaVS1d2R2pVJ238cbwtwp2/0jFTW0PFyxULe0AP3HHIwIgHSdP3hEA0CmALelSgqQX0dZeoumbV8olDcCX5jTdAV4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@badliveware%2fpi-footer-framework@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":135820},"type":"module","types":"./index.ts","engines":{"node":">=20"},"gitHead":"6852c4f048eea81def66ec803f377234b34a9377","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a013130a-b69c-4979-baa3-bd9fbb0b7897"}},"repository":{"url":"git+https://github.com/BadLiveware/pi.git","type":"git","directory":"agent/extensions/public/footer-framework"},"_npmVersion":"11.13.0","description":"Configurable footer framework extension for Pi.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"@mariozechner/pi-ai":"*","@mariozechner/pi-tui":"*","@mariozechner/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-footer-framework_0.2.0_1777206744885_0.29288419843252544","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@badliveware/pi-footer-framework","version":"0.2.1","keywords":["pi-package","pi-extension","footer","ui"],"author":{"name":"BadLiveware"},"license":"MIT","_id":"@badliveware/pi-footer-framework@0.2.1","maintainers":[{"name":"doz3r","email":"flarsson93@gmail.com"}],"homepage":"https://github.com/BadLiveware/pi/tree/main/agent/extensions/public/footer-framework#readme","bugs":{"url":"https://github.com/BadLiveware/pi/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"dist":{"shasum":"43c10f96751ea70ac2506683b329cb9881b37378","tarball":"https://registry.npmjs.org/@badliveware/pi-footer-framework/-/pi-footer-framework-0.2.1.tgz","fileCount":8,"integrity":"sha512-T/pEo4ZuaaQ2am0i59508OOQ4abGEcG3ZTK5nh4KuEyKxJht7AA32X/E6hMmNiLa3TUtCrGIRHzQBDdJkkgFbg==","signatures":[{"sig":"MEYCIQCSH1/mmdFvps53qs7JMQTkgToDJTiWDEr3lQr6jikNsAIhAMsnf7njiSSt97rrtV/lAH4Y1Y8f6b9biMmFNEUbTDdT","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@badliveware%2fpi-footer-framework@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":144696},"type":"module","types":"./index.ts","engines":{"node":">=20"},"gitHead":"7156fc4810b83d8614951b95dadefaba03961c03","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a013130a-b69c-4979-baa3-bd9fbb0b7897"}},"repository":{"url":"git+https://github.com/BadLiveware/pi.git","type":"git","directory":"agent/extensions/public/footer-framework"},"_npmVersion":"11.13.0","description":"Configurable footer framework extension for Pi.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"@mariozechner/pi-ai":"*","@mariozechner/pi-tui":"*","@mariozechner/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-footer-framework_0.2.1_1777677060620_0.7837304273144243","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@badliveware/pi-footer-framework","version":"0.2.2","keywords":["pi-package","pi-extension","footer","ui"],"author":{"name":"BadLiveware"},"license":"MIT","_id":"@badliveware/pi-footer-framework@0.2.2","maintainers":[{"name":"doz3r","email":"flarsson93@gmail.com"}],"homepage":"https://github.com/BadLiveware/pi/tree/main/agent/extensions/public/footer-framework#readme","bugs":{"url":"https://github.com/BadLiveware/pi/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"dist":{"shasum":"5dd70c69db970b98fd9106fb35080327f95c4919","tarball":"https://registry.npmjs.org/@badliveware/pi-footer-framework/-/pi-footer-framework-0.2.2.tgz","fileCount":8,"integrity":"sha512-u3NPK+0LbdmsZ5J3TKQT5ZzCl8I/pr7mDxUaU+K277p/0olK0lQ2hXX0ha1ZSL0lqglp0X6wA4ocRnPJZy4B9A==","signatures":[{"sig":"MEUCIFmfwl6a7+v1Rg7Omlc0PI0DrTjVPHEposR9BpVWsGAjAiEAg6pvhkrNiNbTXsbpq0cTzB3MGkM+0j9uLt2O0u25dDo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@badliveware%2fpi-footer-framework@0.2.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":144857},"type":"module","types":"./index.ts","engines":{"node":">=20"},"gitHead":"d0901767319343b690155851ac26379133b81d2d","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a013130a-b69c-4979-baa3-bd9fbb0b7897"}},"repository":{"url":"git+https://github.com/BadLiveware/pi.git","type":"git","directory":"agent/extensions/public/footer-framework"},"_npmVersion":"11.13.0","description":"Configurable footer framework extension for Pi.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"@mariozechner/pi-ai":"*","@mariozechner/pi-tui":"*","@mariozechner/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-footer-framework_0.2.2_1778079393880_0.44201704404680475","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@badliveware/pi-footer-framework","version":"0.3.0","keywords":["pi-package","pi-extension","footer","ui"],"author":{"name":"BadLiveware"},"license":"MIT","_id":"@badliveware/pi-footer-framework@0.3.0","maintainers":[{"name":"doz3r","email":"flarsson93@gmail.com"}],"homepage":"https://github.com/BadLiveware/pi/tree/main/agent/extensions/public/footer-framework#readme","bugs":{"url":"https://github.com/BadLiveware/pi/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"dist":{"shasum":"142a2e4a429aa669d8b43bc88e9d7bca57b3de2c","tarball":"https://registry.npmjs.org/@badliveware/pi-footer-framework/-/pi-footer-framework-0.3.0.tgz","fileCount":8,"integrity":"sha512-jmt1ynmUlG3ofzrP0G4pVX4kuSWviBpBqlRY+q70mc0YF2LXQAT4Y9wj8W/K65X2pvzsgBAoPX9q4oSFn3t4Xw==","signatures":[{"sig":"MEUCIErgxbFa56jsJM6PzhE+2uq6S7NMzSgAxaE7qmlGPCBGAiEAoaeC1aSiOWoYames6Vg21j8a404St4hZ2g1LIbVNGHs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@badliveware%2fpi-footer-framework@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":144897},"type":"module","types":"./index.ts","engines":{"node":">=20"},"gitHead":"a22d66ffb8348de42a355f01915fe401d9e1585a","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a013130a-b69c-4979-baa3-bd9fbb0b7897"}},"repository":{"url":"git+https://github.com/BadLiveware/pi.git","type":"git","directory":"agent/extensions/public/footer-framework"},"_npmVersion":"11.14.1","description":"Configurable footer framework extension for Pi.","directories":{},"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-footer-framework_0.3.0_1778966551734_0.12003545671417126","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@badliveware/pi-footer-framework","version":"0.3.2","description":"Configurable footer framework extension for Pi.","type":"module","types":"./index.ts","keywords":["pi-package","pi-extension","footer","ui"],"license":"MIT","author":{"name":"BadLiveware"},"repository":{"type":"git","url":"git+https://github.com/BadLiveware/pi.git","directory":"agent/extensions/public/footer-framework"},"bugs":{"url":"https://github.com/BadLiveware/pi/issues"},"homepage":"https://github.com/BadLiveware/pi/tree/main/agent/extensions/public/footer-framework#readme","publishConfig":{"access":"public"},"pi":{"extensions":["./index.ts"],"skills":["./skills"]},"peerDependencies":{"@earendil-works/pi-ai":"*","@earendil-works/pi-coding-agent":"*","@earendil-works/pi-tui":"*"},"engines":{"node":">=20"},"gitHead":"f2e68dbc49a0c40d6ee2ba7136314300cecca2bc","_id":"@badliveware/pi-footer-framework@0.3.2","_nodeVersion":"24.18.0","_npmVersion":"11.18.0","dist":{"integrity":"sha512-Ai8ts0GyCCiKxjha9oMgh2eCqe1kOUy33h0enSUUah/M2BD0FDciyIr5MUQIfuvYxLPZMvXnhuHtvLd1TlKnCg==","shasum":"ce0c4bbc7ddf35844d2de0de8716bc0afe261356","tarball":"https://registry.npmjs.org/@badliveware/pi-footer-framework/-/pi-footer-framework-0.3.2.tgz","fileCount":11,"unpackedSize":150441,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@badliveware%2fpi-footer-framework@0.3.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH5Ut/zCDtnOw3Rf6UgiFU65zshJpfHdcPj/Z2NXrLcRAiBcIrFoQnZ7NpoQZCu+l7c0unFsPVJ1m3VBACUwyrKpBA=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a013130a-b69c-4979-baa3-bd9fbb0b7897"}},"directories":{},"maintainers":[{"name":"doz3r","email":"flarsson93@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-footer-framework_0.3.2_1783247542962_0.9616812620079849"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-26T09:44:14.371Z","modified":"2026-07-05T10:32:23.377Z","0.1.0":"2026-04-26T09:44:14.599Z","0.1.1":"2026-04-26T10:06:19.103Z","0.2.0":"2026-04-26T12:32:25.037Z","0.2.1":"2026-05-01T23:11:00.784Z","0.2.2":"2026-05-06T14:56:34.025Z","0.3.0":"2026-05-16T21:22:31.875Z","0.3.2":"2026-07-05T10:32:23.096Z"},"bugs":{"url":"https://github.com/BadLiveware/pi/issues"},"author":{"name":"BadLiveware"},"license":"MIT","homepage":"https://github.com/BadLiveware/pi/tree/main/agent/extensions/public/footer-framework#readme","keywords":["pi-package","pi-extension","footer","ui"],"repository":{"type":"git","url":"git+https://github.com/BadLiveware/pi.git","directory":"agent/extensions/public/footer-framework"},"description":"Configurable footer framework extension for Pi.","maintainers":[{"name":"doz3r","email":"flarsson93@gmail.com"}],"readme":"# pi-footer-framework\n\nConfigurable footer replacement for Pi. It owns footer layout, formatting, color, truncation, and placement while other extensions publish structured status/data for it to render.\n\nUse it when you want to customize Pi's footer through a configurable framework that agents can inspect and reconfigure from natural-language prompts.\n\n## Install\n\n```bash\npi install npm:@badliveware/pi-footer-framework\n```\n\nNo external services or credentials are required.\n\n## Quick use\n\n```text\n/footerfx on\n/footerfx item context line 1\n/footerfx item context after model\n/footerfx item pr line 3\n/footerfx anchor all right\n/footerfx save user\n```\n\nThe default layout uses two footer lines. If you place an item on another positive line number, the footer grows to include that line. Disable the replacement footer with:\n\n```text\n/footerfx off\n```\n\n## Showcase\n\nThis is a real Pi terminal using TS render closures, theme-aware styles, right-anchored layout, responsive column placement, PR state, and an adapted extension status item:\n\n![Footer framework showcase with cwd, PR, model, token stats, context, and watchdog status](assets/footer-framework-showcase.png)\n\nThe screenshot demonstrates:\n\n- combined render output: cwd plus branch/PR label in one item\n- token-level styling: labels, model id, thinking level, token stats, context, and cost use different Pi theme colors/attributes\n- built-in Pi data: `cwd`, `branch`, `model`, `stats`, `context`, and `pr`\n- extension status adaptation: `compaction-continue` status becomes `watchdog:on`\n- flexible placement: line 1 and line 2 are right-anchored, while line 3 uses responsive `column` placement for middle and center-right items\n\n<details>\n<summary>Config used for the screenshot</summary>\n\nSave this as `~/.pi/agent/footer-framework.config.ts`. The same file ships as `examples/footer-framework.config.ts` in the npm package.\n\n```ts\nimport type { FooterFrameworkConfig } from \"@badliveware/pi-footer-framework\";\n\nfunction shortPath(value: string, maxWidth = 48, tailSegments = 2): string {\n  const normalized = value.replace(/^\\/home\\/[^/]+/, \"~\");\n  const prefix = normalized.startsWith(\"~/\") ? \"~/\" : normalized.startsWith(\"/\") ? \"/\" : \"\";\n  const parts = normalized.slice(prefix.length).split(\"/\").filter(Boolean);\n  const compact = parts.length > tailSegments ? `${prefix}…/${parts.slice(-tailSegments).join(\"/\")}` : normalized;\n  return compact.length > maxWidth ? `…${compact.slice(-(maxWidth - 1))}` : compact;\n}\n\nconst config = {\n  enabled: true,\n  lineAnchors: { 1: \"right\", 2: \"right\", 3: \"left\" },\n  minGap: 2,\n  maxGap: 24,\n  items: {\n    branch: { visible: false },\n    ext: { visible: false },\n    cwd: {\n      visible: true,\n      line: 1,\n      zone: \"left\",\n      order: 10,\n      render: ({ pi, span, fn }) => [\n        span(\"cwd\", \"muted\"),\n        \" \",\n        span(shortPath(pi.cwd.trim(), 48, 2), \"dim\"),\n        span(\" · \", \"muted\"),\n        span(fn.truncate(pi.branch?.label ?? \"\", 22), \"accent\"),\n      ],\n    },\n    model: {\n      visible: true,\n      line: 1,\n      zone: \"right\",\n      order: 10,\n      render: ({ pi, span }) => [\n        span(\"model:\", \"muted\"),\n        span(pi.model.id ?? \"no-model\", \"accent\"),\n        span(\"/\", \"muted\"),\n        span(pi.model.thinking ?? \"\", \"thinkingXhigh,bold\"),\n      ],\n    },\n    stats: {\n      visible: true,\n      line: 2,\n      zone: \"left\",\n      order: 10,\n      render: ({ pi, span }) => [\n        span(\"↑\", \"dim\"), span(pi.stats.inputText ?? \"0\", \"dim\"), \" \",\n        span(\"↓\", \"dim\"), span(pi.stats.outputText ?? \"0\", \"dim\"), \" \",\n        span(\"$\", \"accent\"), span(pi.stats.costText ?? \"0.000\", \"success\"),\n      ],\n    },\n    context: {\n      visible: true,\n      line: 3,\n      zone: \"left\",\n      order: 10,\n      column: \"50%\",\n      render: ({ pi, span }) => {\n        if (!pi.context) return undefined;\n        const tone = pi.context.tone ?? \"muted\";\n        return [span(\"ctx\", tone), \" \", span(pi.context.percentText ?? \"?%\", tone), \" \", span(pi.context.tokenText ?? \"?/?\", tone)];\n      },\n    },\n    pr: {\n      visible: true,\n      line: 3,\n      zone: \"left\",\n      order: 20,\n      column: \"66%\",\n      render: ({ pi, span }) => {\n        if (!pi.pr) return undefined;\n        return [span(\"PR \", \"muted\"), span(pi.pr.checkGlyph ?? \"•\", pi.pr.checkTone ?? \"muted\"), span(pi.pr.commentsText ?? \"\", \"muted\")];\n      },\n    },\n  },\n  adapters: {\n    watchdog: {\n      source: \"extensionStatus\",\n      key: \"compaction-continue\",\n      itemId: \"watchdog\",\n      match: \"(on|off)\",\n      group: 1,\n      urlPath: \"url\",\n      placement: { visible: true, line: 2, zone: \"right\", order: 20 },\n      render: ({ value, span }) => [span(\"watchdog:\", \"muted\"), span(value ?? \"\", \"accent,bold\")],\n    },\n  },\n} satisfies FooterFrameworkConfig;\n\nexport default config;\n```\n\nString `column` positions such as `\"50%\"`, `\"66%\"`, and `\"center\"` are resolved from the current terminal width, so they keep their relative position after resize. Numeric columns remain fixed absolute terminal columns.\n\n</details>\n\n## How it works\n\nFooter-framework renders normalized footer items from adapter mappings, direct TS/JS item renderers, and extension-published items. Adapter mappings can read three source types:\n\n| Source | Use it for |\n| --- | --- |\n| `pi` | Built-in Pi/session/footer data such as `cwd`, `model`, `stats`, `context`, `branch`, `pr`, and `extensionStatuses`. |\n| `extensionStatus` | Existing `ctx.ui.setStatus()` footer/status entries from other extensions. |\n| `sessionEntry` | The latest custom session entry written by an extension with `pi.appendEntry()`. |\n\nThe built-in footer items (`cwd`, `model`, `branch`, `stats`, `context`, `pr`) are regular default adapters unless you replace them with `items.<id>.render` in TS/JS config. User/project config overrides built-in defaults and producer hints.\n\nAgents can inspect concise footer-relevant data with `footer_framework_sources`, then add adapter rules with `footer_framework_adapter_config` or adjust layout with `footer_framework_config`. Runtime metadata such as tools, commands, skills, descriptions, and `sourceInfo` is opt-in via `includeTools`, `includeCommands`, `includeSkills`, and `includeDetails`.\n\n## Templates and styles\n\nAdapters can render with a restricted Liquid-style interpolation subset:\n\n```liquid\n{{ pi.stats.costText }}\n{{ \"EUR\" }}\n{{ \"EUR\" | style: \"accent\" }}{{ pi.stats.costText | style: \"success,bold\" }}\n```\n\nQuoted strings are literals. Unquoted terms are variables, so missing variables are reported as template diagnostics instead of being guessed as text. Diagnostics appear in `/footerfx-debug`, `footer_framework_state`, and `footer_framework_sources`.\n\nUseful template context:\n\n| Path | Meaning |\n| --- | --- |\n| `value`, `label`, `status`, `data`, `url` | The current adapter source. |\n| `pi.cwd` | Current working directory. |\n| `pi.model.id`, `pi.model.provider`, `pi.model.thinking` | Current model information. |\n| `pi.stats.inputText`, `pi.stats.outputText`, `pi.stats.costText` | Formatted session token/cost stats. Raw numbers are `input`, `output`, and `cost`. |\n| `pi.context.percentText`, `pi.context.tokenText`, `pi.context.tone` | Context usage and recommended tone. |\n| `pi.branch.name`, `pi.branch.label` | Git branch values. Use `truncate` in templates when you want a shorter display. |\n| `pi.pr.number`, `pi.pr.url`, `pi.pr.checkGlyph`, `pi.pr.checkTone`, `pi.pr.commentsText` | Pull request state when available. |\n\nSupported filters:\n\n| Filter | Example |\n| --- | --- |\n| `style` / `color` | `{{ value | style: \"accent,bold\" }}` |\n| `bg` / `background` | `{{ value | bg: \"toolSuccessBg\" }}` |\n| `bold`, `italic`, `underline`, `inverse`, `strikethrough` | `{{ value | underline }}` |\n| `link` | `{{ pi.pr.number | link: pi.pr.url }}` |\n| `truncate` | `{{ pi.branch.label | truncate: 22 }}` limits any value to 22 cells with an ellipsis. |\n| `compactPath` | `{{ pi.cwd | compactPath: 48, 2 }}` keeps the last 2 path segments when the path is wider than 48 cells. |\n| `default` | `{{ data.state | default: \"unknown\" }}` |\n\nStyle strings use Pi theme tokens and text attributes. Foreground examples: `accent`, `muted`, `dim`, `success`, `warning`, `error`, `text`, `mdLink`, `toolDiffAdded`, and the other Pi theme foreground tokens. Backgrounds use `bg:<token>`, such as `bg:toolSuccessBg`. Attributes are `bold`, `italic`, `underline`, `inverse`, and `strikethrough`.\n\n## TypeScript render config\n\nFor personal formatting logic, use a normal TS/JS config file with render closures. Footer-framework still owns data collection, layout, clipping, diagnostics, and final rendering; the closure only returns text/spans for one item.\n\n```ts\nimport type { FooterFrameworkConfig } from \"@badliveware/pi-footer-framework\";\n\nfunction shortPath(value: string, maxWidth = 48, tailSegments = 2) {\n  const normalized = value.replace(/^\\/home\\/[^/]+/, \"~\");\n  const prefix = normalized.startsWith(\"~/\") ? \"~/\" : normalized.startsWith(\"/\") ? \"/\" : \"\";\n  const parts = normalized.slice(prefix.length).split(\"/\").filter(Boolean);\n  const compact = parts.length > tailSegments ? `${prefix}…/${parts.slice(-tailSegments).join(\"/\")}` : normalized;\n  return compact.length > maxWidth ? `…${compact.slice(-(maxWidth - 1))}` : compact;\n}\n\nexport default {\n  items: {\n    branch: { visible: false },\n    ext: { visible: false },\n    cwd: {\n      line: 1,\n      zone: \"left\",\n      order: 10,\n      render: ({ pi, span, fn }) => [\n        span(\"cwd\", \"muted\"),\n        \" \",\n        span(shortPath(pi.cwd.trim(), 48, 2), \"dim\"),\n        span(\" · \", \"muted\"),\n        span(fn.truncate(pi.branch?.label ?? \"\", 22), \"accent\"),\n      ],\n    },\n  },\n} satisfies FooterFrameworkConfig;\n```\n\nRender functions are synchronous and may return strings, spans, arrays, `null`, or `undefined`. Use `span(text, style, { url })` for token-level style/link metadata and `fn.text`, `fn.width`, `fn.truncate`, or `fn.compactPath` for footer-safe helpers. Adapter render functions also receive `value`, `label`, `status`, `data`, `url`, and `source`.\n\n`/footerfx-debug`, `footer_framework_state`, and `footer_framework_sources` show which TS/JS renderers loaded plus each rendered item's final tokens, width, placement, and diagnostics. If a render closure throws or returns a promise, the footer item is skipped and a diagnostic is recorded.\n\n## Configuration files\n\nUser settings persist to:\n\n```text\n~/.pi/agent/footer-framework.json\n```\n\nOptional user TS/JS config files live next to it:\n\n```text\n~/.pi/agent/footer-framework.config.ts\n~/.pi/agent/footer-framework.config.js\n```\n\nProject settings can override them:\n\n```text\n<project>/.pi/footer-framework.json\n<project>/.pi/footer-framework.config.ts\n```\n\nLoad order is defaults, user TS/JS, user JSON, project TS/JS, then project JSON. `/footerfx` commands write JSON overrides; they do not rewrite TS/JS source. Use `/footerfx save project` only when you intentionally want a project-specific footer layout.\n\n## Commands\n\n| Command | What it does |\n| --- | --- |\n| `/footerfx` | Show current config and source. |\n| `/footerfx on` / `/footerfx off` | Enable or disable the replacement footer. |\n| `/footerfx config` | Show loaded config source and config paths. |\n| `/footerfx load` | Reload user/project config files. |\n| `/footerfx save user` | Save current settings as the user default. |\n| `/footerfx save project` | Save current settings for the current project. |\n| `/footerfx reset` | Restore defaults and persist them to user config. |\n| `/footerfx section <cwd|stats|context|model|branch|pr|ext> <on|off>` | Convenience alias for item visibility. |\n| `/footerfx item <id> <show|hide|reset>` | Control item visibility. |\n| `/footerfx item <id> line <n>` / `row <n>` | Move an item to any positive footer line. |\n| `/footerfx item <id> zone <left|right>` | Move an item between left/right zones. |\n| `/footerfx item <id> before <other-id>` / `after <other-id>` | Place an item relative to another item. |\n| `/footerfx item <id> column <n|center|middle|percent|off>` | Pin, center, percentage-place, or unpin an item. Percent examples: `50%`, `66%`. |\n| `/footerfx anchor <line|all> <gap|left|center|right|spread>` | Control line alignment. `line3` and `3` both work. |\n| `/footerfx adapter` | List configured adapters. |\n| `/footerfx adapter <id> pi <source-key> [label]` | Adapt a built-in Pi source. |\n| `/footerfx adapter <id> status <status-key> [label]` | Adapt an existing extension status key. |\n| `/footerfx adapter <id> custom <custom-type> <path> [label]` | Adapt the latest matching custom session entry. |\n| `/footerfx adapter <id> template <template>` | Set the adapter's render template. |\n| `/footerfx adapter <id> empty-template <template>` | Set the template used for an empty adapter value. |\n| `/footerfx adapter <id> style <style>` | Apply a default style to the rendered adapter text. |\n| `/footerfx adapter <id> remove` | Remove an adapter. For built-ins, hide the item with `/footerfx item <id> hide`. |\n| `/footerfx gap <min> <max>` | Set spacing bounds. |\n| `/footerfx-debug` | Show render snapshot, settings, template/render diagnostics, loaded TS/JS renderers, and layout telemetry. |\n\n## Agent tools\n\nThe extension exposes tools so agents can inspect and adjust the footer without asking you to run commands:\n\n- `footer_framework_state`\n- `footer_framework_sources`\n- `footer_framework_config`\n- `footer_framework_adapter_config`\n\n`footer_framework_sources` is concise by default. Pass `includeTools`, `includeCommands`, `includeSkills`, and `includeDetails` only when runtime metadata is directly useful.\n\n## Extension data API\n\nCompatible extensions should publish data, not pre-rendered layout. The framework decides final text, color, position, and truncation. Producers may include hints, but user config wins.\n\n```ts\npi.events.emit(\"footer-framework:item\", {\n  id: \"cache:status\",\n  label: \"cache\",\n  value: \"warm\",\n  tone: \"success\",\n  data: { entries: 42 },\n  hint: {\n    icon: \"◇\",\n    format: \"label-value\",\n    placement: { line: 2, zone: \"right\", order: 50 }\n  }\n});\n```\n\nLegacy `text` and top-level `placement` fields still work for existing extensions, but new integrations should prefer `label`, `value`, `status`, `data`, and `hint`.\n\nRemove an item with:\n\n```ts\npi.events.emit(\"footer-framework:item\", { id: \"cache:status\", remove: true });\n```\n\n## Troubleshooting\n\n### Blank space below the footer\n\nIf blank rows sometimes appear below the footer and disappear after you send a prompt, check `/footerfx-debug` or `footer_framework_state`. When `lastFooterSnapshot.lines` contains only the expected footer lines, the framework is not rendering extra rows. This is usually Pi's TUI viewport/differential rendering leaving unused terminal space below the last rendered component.\n\nA Pi-side workaround is `terminal.clearOnShrink: true` in `~/.pi/agent/settings.json`, but that can add redraw flicker. Footer-framework does not change this setting.\n","readmeFilename":"README.md"}