@neilurk12/pi-clean-footer 0.2.5 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -1
- package/dist/index.js +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,14 +79,35 @@ Example:
|
|
|
79
79
|
|
|
80
80
|
```json
|
|
81
81
|
{
|
|
82
|
+
"preset": "compact",
|
|
82
83
|
"enabled": true,
|
|
83
84
|
"showGit": true,
|
|
84
85
|
"showTokens": true,
|
|
85
|
-
"showCache":
|
|
86
|
+
"showCache": true,
|
|
87
|
+
"showCacheRead": true,
|
|
88
|
+
"showCacheWrites": true,
|
|
86
89
|
"showContext": true,
|
|
87
90
|
"showDirectory": true,
|
|
88
91
|
"showEffort": true,
|
|
89
92
|
"gitRefreshDebounceMs": 500,
|
|
93
|
+
"separator": " • ",
|
|
94
|
+
"layouts": [
|
|
95
|
+
{
|
|
96
|
+
"minWidth": 100,
|
|
97
|
+
"left": ["model", "directory", "git"],
|
|
98
|
+
"right": ["context", "tokensFull"]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"minWidth": 60,
|
|
102
|
+
"left": ["model", "git"],
|
|
103
|
+
"right": ["context", "tokensTotal"]
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"minWidth": 0,
|
|
107
|
+
"left": ["model"],
|
|
108
|
+
"right": ["context"]
|
|
109
|
+
}
|
|
110
|
+
],
|
|
90
111
|
"contextWarningPercent": 70,
|
|
91
112
|
"contextDangerPercent": 85,
|
|
92
113
|
"modelAliases": {
|
|
@@ -107,6 +128,40 @@ Example:
|
|
|
107
128
|
}
|
|
108
129
|
```
|
|
109
130
|
|
|
131
|
+
Preset example:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"preset": "minimal",
|
|
136
|
+
"showGit": true
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Supported presets:
|
|
141
|
+
|
|
142
|
+
| Preset | Description |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `default` | Built-in footer behavior. |
|
|
145
|
+
| `minimal` | Quiet model + context layout with git, directory, and tokens hidden. |
|
|
146
|
+
| `compact` | Model, git, context, and total tokens with cache noise hidden. |
|
|
147
|
+
| `dense` | Full information layout with cache read and write counts enabled. |
|
|
148
|
+
| `focus` | Model + context only for low-distraction sessions. |
|
|
149
|
+
| `muted` | Softer semantic colors while keeping default-style behavior. |
|
|
150
|
+
|
|
151
|
+
Preset values are applied before user config, so explicit settings override the preset. Unknown preset names are ignored with a `/footer config` warning and default behavior is used.
|
|
152
|
+
|
|
153
|
+
Supported layout segment IDs:
|
|
154
|
+
|
|
155
|
+
- `model` - model name plus thinking effort when `showEffort` is enabled
|
|
156
|
+
- `directory` - current directory basename
|
|
157
|
+
- `git` - git branch and dirty count
|
|
158
|
+
- `context` - context usage
|
|
159
|
+
- `tokensFull` - input, output, total, cache read, and cache write tokens
|
|
160
|
+
- `tokensNoCache` - input, output, and total tokens
|
|
161
|
+
- `tokensTotal` - total tokens only
|
|
162
|
+
|
|
163
|
+
`layouts` are selected by the highest `minWidth` less than or equal to the terminal width. `showGit`, `showTokens`, `showContext`, `showDirectory`, and `showEffort` still act as global visibility controls. `showCache` is a deprecated global cache-token gate; use `showCacheRead` and `showCacheWrites` to hide cache read (`↯`) and write (`↥`) counts independently. Unknown or duplicate layout segments are omitted and reported by `/footer config`.
|
|
164
|
+
|
|
110
165
|
Malformed JSON keeps defaults/last loaded behavior and reports an error through `/footer config` or at startup.
|
|
111
166
|
|
|
112
167
|
## Package manifest
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
`).filter(Boolean).length;t={inRepo:!0,branch:a,dirtyCount:
|
|
3
|
-
`):"none",
|
|
4
|
-
${a}`,
|
|
1
|
+
import D from"path";import H from"os";import W from"path";import{existsSync as $,readFileSync as _}from"fs";var I=500,c={enabled:!0,showGit:!0,showTokens:!0,showCache:!0,showContext:!0,showDirectory:!0,showEffort:!0,gitRefreshDebounceMs:I,contextWarningPercent:70,contextDangerPercent:85,modelAliases:{},colors:{model:"accent",directory:"dim",git:"success",gitDirty:"warning",contextNormal:"success",contextWarning:"warning",contextDanger:"error",tokens:"muted",separator:"dim"}};function x(e,t){return L([e,t])}function L(e){let t=[],n={},r;for(let o of e)if($(o))try{let i=JSON.parse(_(o,"utf8"));n=j(n,i),t.push(o)}catch(i){r=`${o}: ${i instanceof Error?i.message:String(i)}`}return{config:A(n),loadedPaths:t,error:r}}function j(e,t){return{...e,...t,modelAliases:{...e.modelAliases??{},...t.modelAliases??{}},colors:{...e.colors??{},...t.colors??{}}}}function A(e){return{...c,...e,gitRefreshDebounceMs:G(e.gitRefreshDebounceMs,c.gitRefreshDebounceMs),contextWarningPercent:w(e.contextWarningPercent,c.contextWarningPercent),contextDangerPercent:w(e.contextDangerPercent,c.contextDangerPercent),modelAliases:{...c.modelAliases,...e.modelAliases??{}},colors:{...c.colors,...e.colors??{}}}}function G(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:t}function w(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>=0&&e<=100?e:t}import{execFile as N}from"child_process";import{promisify as U}from"util";var R=U(N);function m(e){let t={inRepo:!1,dirtyCount:0},n,r=[];e.onChange&&r.push(e.onChange);async function o(){if(!e.enabled){t={inRepo:!1,dirtyCount:0};return}try{let[d,s]=await Promise.all([R("git",["branch","--show-current"],{cwd:e.cwd,timeout:2e3}),R("git",["status","--porcelain"],{cwd:e.cwd,timeout:2e3})]),a=d.stdout.trim()||"detached",g=s.stdout.split(`
|
|
2
|
+
`).filter(Boolean).length;t={inRepo:!0,branch:a,dirtyCount:g}}catch{t={inRepo:!1,dirtyCount:0}}for(let d of r)d()}function i(){l(),n=setTimeout(()=>{n=void 0,o()},e.debounceMs)}function l(){n&&(clearTimeout(n),n=void 0)}return{get state(){return t},schedule:i,clear:l,refresh:o,onChange(d){return r.push(d),()=>{let s=r.indexOf(d);s>=0&&r.splice(s,1)}}}}function P(e){var n,r,o,i,l;let t={input:0,output:0,cacheRead:0,cacheWrite:0};for(let d of e){if(d.type!=="message"||((n=d.message)==null?void 0:n.role)!=="assistant")continue;let s=d.message;t.input+=((r=s.usage)==null?void 0:r.input)??0,t.output+=((o=s.usage)==null?void 0:o.output)??0,t.cacheRead+=((i=s.usage)==null?void 0:i.cacheRead)??0,t.cacheWrite+=((l=s.usage)==null?void 0:l.cacheWrite)??0}return t}function p(e){if(typeof e!="string")return;let t=e.toLowerCase();if(t==="medium")return"med";if(t==="extra-high"||t==="extra_high"||t==="x-high")return"xhigh";if(["low","med","high","xhigh"].includes(t))return t}var C=class{#t;#r;#i;#e;#n;#s;#a;#d;#l;#o;constructor(t){this.#a=t.globalConfigPath,this.#d=t.getProjectConfigPath,this.#l=t.getThinkingLevel,this.#o=t.onRenderNeeded,this.#t=c,this.#r={config:this.#t,loadedPaths:[]},this.#i=void 0,this.#e=void 0,this.#n=!0,this.#s=""}async start(t){this.#s=t.cwd,this.#r=x(this.#a,this.#d(t.cwd)),this.#t=this.#r.config,this.#i=p(this.#l()),this.#n=this.#t.enabled,this.#n&&(this.#e=m({cwd:t.cwd,debounceMs:this.#t.gitRefreshDebounceMs,enabled:this.#t.showGit,onChange:()=>this.#o()}),await this.#e.refresh())}shutdown(){var t;(t=this.#e)==null||t.clear(),this.#e=void 0}onThinkingLevel(t){this.#i=p(t),this.#o()}onModelSelect(){this.#o()}onMessageEnd(t){t==="assistant"&&this.#o()}onToolEnd(t){var n;["bash","edit","write"].includes(t)&&((n=this.#e)==null||n.schedule()),this.#o()}onUserBash(){var t;(t=this.#e)==null||t.schedule()}async refresh(){var t;await((t=this.#e)==null?void 0:t.refresh())}async reload(t){var n;this.#r=x(this.#a,this.#d(t.cwd)),this.#t=this.#r.config,this.#n=this.#t.enabled,(n=this.#e)==null||n.clear(),this.#e=void 0,this.#n&&(this.#e=m({cwd:this.#s,debounceMs:this.#t.gitRefreshDebounceMs,enabled:this.#t.showGit,onChange:()=>this.#o()}),await this.#e.refresh()),this.#o()}async toggle(){var t;return this.#n=!this.#n,this.#n?(this.#i=p(this.#l()),this.#e=m({cwd:this.#s,debounceMs:this.#t.gitRefreshDebounceMs,enabled:this.#t.showGit,onChange:()=>this.#o()}),await this.#e.refresh()):((t=this.#e)==null||t.clear(),this.#e=void 0),this.#o(),this.#n}getFooterInput(t){var n,r,o,i,l,d;return{modelId:((n=t.model)==null?void 0:n.id)??"no-model",thinkingLevel:this.#i,directory:W.basename(t.cwd),gitBranch:(r=this.#e)==null?void 0:r.state.branch,gitDirtyCount:((o=this.#e)==null?void 0:o.state.dirtyCount)??0,contextUsed:((l=(i=t.getContextUsage)==null?void 0:i.call(t))==null?void 0:l.tokens)??0,contextMax:(d=t.model)==null?void 0:d.contextWindow,totals:P(t.sessionManager.getBranch()),config:this.#t}}get isEnabled(){return this.#n}get loadedError(){return this.#r.error}get loadedPaths(){return this.#r.loadedPaths}get config(){return this.#t}};import{truncateToWidth as u,visibleWidth as T}from"@earendil-works/pi-tui";function f(e){return!Number.isFinite(e)||e<=0?"0":e<1e3?`${Math.round(e)}`:e<1e6?`${(e/1e3).toFixed(e<1e4?1:0)}k`:`${(e/1e6).toFixed(1)}m`}function B(e,t){if(t[e])return t[e];let n=e.toLowerCase(),r=n.includes("/")?n.split("/").pop():n;if(t[r])return t[r];if(r.includes("claude")&&r.includes("sonnet"))return r.includes("4-5")||r.includes("4.5")?"sonnet-4.5":r.includes("4")?"sonnet-4":"sonnet";if(r.includes("claude")&&r.includes("opus"))return"opus";if(r.includes("claude")&&r.includes("haiku"))return"haiku";let o=r.match(/gpt-5(?:[.-][a-z0-9]+)*/);if(o)return o[0];let i=r.match(/gpt-4(?:[.-][a-z0-9]+)*/);if(i)return i[0];let l=r.match(/gemini-[a-z0-9.-]+/);return l?l[0].replace(/-preview.*/,""):r.length>24?`${r.slice(0,21)}\u2026`:r}function F(e,t,n,r,o,i){let l=B(e,n),d=r&&t?` \u2022 ${t}`:"";return o(i,`${l}${d}`)}function k(e,t,n){if(e)return t(n,e)}function v(e,t,n,r,o){if(!e)return;let i=n(r,e);return t<=0?i:`${i} ${n(o,`\u25CF${t}`)}`}function b(e,t,n,r,o,i){if(!n)return;let l=r?t:t==="full"?"no-cache":t,d=e.input+e.output,s;if(l==="total-only")s=`\u03A3${f(d)}`;else{let a=`\u2191${f(e.input)} \u2193${f(e.output)} \u03A3${f(d)}`;s=l==="full"?`${a} \u21AF${f(e.cacheRead)} \u21A5${f(e.cacheWrite)}`:a}return o(i,s)}function E(e,t,n,r,o,i){let l=`ctx ${f(e)}/${t?f(t):"--"}`;if(!t||t<=0)return o(i.dim,l);let d=e/t*100;return d>=r?o(i.danger,l):d>=n?o(i.warning,l):o(i.normal,l)}function O(e,t){let n=e.config,r=F(e.modelId,e.thinkingLevel,n.modelAliases,n.showEffort,t,n.colors.model),o=n.showDirectory?k(e.directory,t,n.colors.directory):void 0,i=n.showGit?v(e.gitBranch,e.gitDirtyCount,t,n.colors.git,n.colors.gitDirty):void 0,l=n.showContext?E(e.contextUsed,e.contextMax,n.contextWarningPercent,n.contextDangerPercent,t,{normal:n.colors.contextNormal,warning:n.colors.contextWarning,danger:n.colors.contextDanger,dim:"dim"}):void 0,d={};return n.showTokens&&(d.full=b(e.totals,"full",!0,n.showCache,t,n.colors.tokens),d.noCache=b(e.totals,"no-cache",!0,n.showCache,t,n.colors.tokens),d.totalOnly=b(e.totals,"total-only",!0,n.showCache,t,n.colors.tokens)),{model:r,dir:o,git:i,context:l,tokens:d}}function z(e,t,n){let r=[e.model,e.dir,e.git].filter(Boolean).join(t),o=e.model;if(n>=100){let i=[e.context,e.tokens.full].filter(Boolean).join(t);return[h(r,i,n)]}if(n>=80){let i=[e.context,e.tokens.noCache].filter(Boolean).join(t);return[h(r,i,n)]}if(n>=60){let i=[e.context,e.tokens.totalOnly].filter(Boolean).join(t);return[h(r,i,n)]}return n>=40?[h(r,e.context??"",n)]:[h(o,e.context??"",n)]}function S(e,t,n){let r=(l,d)=>t.fg(l,d),o=O(e,r),i=r(e.config.colors.separator," | ");return z(o,i,n)}function h(e,t,n){if(!t)return u(e,n);if(!e)return u(t,n);let r=n-T(e)-T(t);if(r>=1)return u(e+" ".repeat(r)+t,n);let o=Math.max(1,Math.floor((n-1)/2));return u(e,o)+" "+u(t,n-o-1)}function ue(e){let t=D.join(H.homedir(),".pi","agent","clean-footer.json"),n=s=>D.join(s,".pi","clean-footer.json"),r=()=>{},o=new C({globalConfigPath:t,getProjectConfigPath:n,getThinkingLevel:()=>{var s;return(s=e.getThinkingLevel)==null?void 0:s.call(e)},onRenderNeeded:()=>r()});e.registerCommand("footer",{description:"Toggle, refresh, or configure the clean footer",handler:async(s,a)=>{let g=s.trim();if(g==="refresh"){await o.refresh(),a.hasUI&&a.ui.notify("Footer refreshed","info");return}if(g==="reload"){await o.reload(a),a.hasUI&&o.isEnabled&&i(a),a.hasUI&&!o.isEnabled&&a.ui.setFooter(void 0),r(),l(a);return}if(g==="config"){d(a);return}let y=await o.toggle();a.hasUI&&(y?(i(a),a.ui.notify("Clean footer enabled","info")):(a.ui.setFooter(void 0),a.ui.notify("Default footer restored","info")))}}),e.on("session_start",async(s,a)=>{await o.start(a),a.hasUI&&o.loadedError&&o.isEnabled&&a.ui.notify(`Config error: ${o.loadedError}`,"error"),a.hasUI&&o.isEnabled&&i(a)}),e.on("session_shutdown",(s,a)=>{o.shutdown(),r=()=>{},a.hasUI&&a.ui.setFooter(void 0)}),e.on("thinking_level_select",s=>{o.onThinkingLevel(s.level)}),e.on("model_select",()=>{o.onModelSelect()}),e.on("message_end",s=>{o.onMessageEnd(s.message.role)}),e.on("tool_execution_end",s=>{o.onToolEnd(s.toolName)}),e.on("user_bash",()=>{o.onUserBash()});function i(s){s.hasUI&&s.ui.setFooter((a,g)=>(r=()=>a.requestRender(),{invalidate(){},render(y){let M=o.getFooterInput(s);return S(M,g,y)}}))}function l(s){s.hasUI&&(o.loadedError?s.ui.notify(`Clean footer config error: ${o.loadedError}`,"error"):s.ui.notify("Clean footer config loaded","info"))}function d(s){if(!s.hasUI)return;let a=o.loadedPaths.length?o.loadedPaths.join(`
|
|
3
|
+
`):"none",g=n(s.cwd);s.ui.notify(["Clean footer config",`global: ${t}`,`project: ${g}`,`loaded:
|
|
4
|
+
${a}`,o.loadedError?`error: ${o.loadedError}`:"error: none",`resolved: ${JSON.stringify(o.config)}`].join(`
|
|
5
5
|
`),"info")}}export{ue as default};
|