claude-statusline 2.1.2 → 2.1.3

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  Simple statusline for Claude Code with project-branch, git indicators, and context usage. Optimized for speed with bun. Just the essentials, none of the bloat.
4
4
 
5
5
  ![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)
6
- ![Version](https://img.shields.io/badge/version-2.1.2-green.svg)
6
+ ![Version](https://img.shields.io/badge/version-2.1.3-green.svg)
7
7
  ![TypeScript](https://img.shields.io/badge/language-TypeScript-3178C6.svg)
8
8
  ![Node](https://img.shields.io/badge/node-%3E%3D22.6.0-brightgreen.svg)
9
9
  ![Bun](https://img.shields.io/badge/runtime-Bun-black.svg)
@@ -1,11 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as bt}from"fs";import{z as l}from"zod";import{readFileSync as X,existsSync as Z}from"fs";import{homedir as q}from"os";import{join as P,dirname as Q}from"path";import{parse as tt}from"yaml";var I=l.object({cacheTTL:l.number().default(300),cacheDir:l.string().default("/tmp/.claude-statusline-cache"),maxLength:l.number().default(1e3),noEmoji:l.boolean().default(!1),noGitStatus:l.boolean().default(!1),noContextWindow:l.boolean().default(!1),envContext:l.boolean().default(!1),truncate:l.boolean().default(!1),softWrap:l.boolean().default(!1),noSoftWrap:l.boolean().default(!1),forceWidth:l.number().optional(),debugWidth:l.boolean().default(!1),rightMargin:l.number().default(15),symbols:l.object({git:l.string().default("\uF418"),model:l.string().default("\u{F06A9}"),contextWindow:l.string().default("\u26A1\uFE0E"),staged:l.string().default("+"),conflict:l.string().default("\xD7"),stashed:l.string().default("\u2691"),ahead:l.string().default("\u21E1"),behind:l.string().default("\u21E3"),diverged:l.string().default("\u21D5"),renamed:l.string().default("\xBB"),deleted:l.string().default("\u2718")}).default({}),asciiSymbols:l.object({git:l.string().default("@"),model:l.string().default("*"),contextWindow:l.string().default("#"),staged:l.string().default("+"),conflict:l.string().default("C"),stashed:l.string().default("$"),ahead:l.string().default("A"),behind:l.string().default("B"),diverged:l.string().default("D"),renamed:l.string().default(">"),deleted:l.string().default("X")}).default({})}),et=I.parse({}),rt=["claude-statusline.json","claude-statusline.yaml"];function O(r=process.cwd()){let t={...et};return t={...t,...nt(r)},t={...t,...ot()},I.parse(t)}function nt(r){let t=[r,Q(r),P(q(),".claude")];for(let e of t)for(let n of rt){let o=P(e,n);if(Z(o))try{let a=X(o,"utf-8");if(n.endsWith(".json"))return JSON.parse(a);if(n.endsWith(".yaml"))return tt(a)}catch{}}return{}}function ot(){let r={};if(process.env.CLAUDE_CODE_STATUSLINE_NO_EMOJI==="1"&&(r.noEmoji=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_GITSTATUS==="1"&&(r.noGitStatus=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_CONTEXT_WINDOW==="1"&&(r.noContextWindow=!0),process.env.CLAUDE_CODE_STATUSLINE_ENV_CONTEXT==="1"&&(r.envContext=!0),process.env.CLAUDE_CODE_STATUSLINE_TRUNCATE==="1"&&(r.truncate=!0),process.env.CLAUDE_CODE_STATUSLINE_SOFT_WRAP==="1"&&(r.softWrap=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_SOFT_WRAP==="1"&&(r.noSoftWrap=!0),process.env.CLAUDE_CODE_STATUSLINE_FORCE_WIDTH){let t=parseInt(process.env.CLAUDE_CODE_STATUSLINE_FORCE_WIDTH,10);!isNaN(t)&&t>0&&(r.forceWidth=t)}return process.env.CLAUDE_CODE_STATUSLINE_DEBUG_WIDTH==="1"&&(r.debugWidth=!0),process.env.CLAUDE_CODE_STATUSLINE_CACHE_DIR&&(r.cacheDir=process.env.CLAUDE_CODE_STATUSLINE_CACHE_DIR),r}var st=4096,it=[/\.\./,/\.\.\\/,/\[/,/;/,/&/,/</,/>/,/`/];function R(r,t){if(!r||typeof r!="string")return!1;let e=r.trimEnd();if(e.length===0||e.length>t.maxLength||!e.includes("{")||!e.includes("}"))return!1;let n=(e.match(/"/g)||[]).length;if(n===0||n%2!==0)return!1;try{JSON.parse(e)}catch{return!1}return!0}function at(r){if(!r||typeof r!="string"||r.length>st)return!1;for(let t of it)if(t.test(r))return!1;if(r.includes("${")||r.includes("`")||r.includes("$("))return!1;try{let t=r.replace(/\/+/g,"/").replace(/\\+/g,"\\");if(t.includes("../")||t.includes("..\\")||t.startsWith("/")&&!t.startsWith("/home/")&&!t.startsWith("/Users/")&&!t.startsWith("/tmp/")&&!["/home","/Users","/tmp","/var","/opt"].some(o=>t.startsWith(o)))return!1;if(t.includes(":")&&/^[A-Za-z]:/.test(t)){let e=t.charAt(0).toUpperCase();if(e<"C"||e>"Z")return!1}}catch{return!1}return!0}async function W(r){if(!at(r))return!1;try{let{access:t}=await import("fs/promises"),{constants:e}=await import("fs");return await t(r,e.R_OK),!0}catch{return!1}}import{readFile as $,writeFile as x,mkdir as ct}from"fs/promises";import{existsSync as A}from"fs";import{join as C}from"path";var w=class{config;constructor(t){this.config=t}async ensureCacheDir(){try{await ct(this.config.cacheDir,{recursive:!0})}catch{}}getCachePath(t){return C(this.config.cacheDir,t)}getTimestampPath(t){return C(this.config.cacheDir,`${t}.time`)}async get(t,e=this.config.cacheTTL){let n=this.getCachePath(t),o=this.getTimestampPath(t);try{if(!A(n)||!A(o))return null;let a=await $(o,"utf-8"),s=parseInt(a.trim(),10);if(isNaN(s)||Math.floor(Date.now()/1e3)-s>=e)return null;let d=await $(n,"utf-8");try{return JSON.parse(d)}catch{return d}}catch{return null}}async set(t,e){let n=this.getCachePath(t),o=this.getTimestampPath(t);try{await this.ensureCacheDir();let a;typeof e=="string"?a=e:a=JSON.stringify(e);let s=Math.floor(Date.now()/1e3);return await Promise.all([x(n,a,"utf-8"),x(o,s.toString(),"utf-8")]),!0}catch{return!1}}async has(t,e=this.config.cacheTTL){return await this.get(t,e)!==null}async delete(t){let e=this.getCachePath(t),n=this.getTimestampPath(t);try{let{unlink:o}=await import("fs/promises");return await Promise.allSettled([o(e),o(n)]),!0}catch{return!1}}async clear(){try{let{readdir:t,unlink:e}=await import("fs/promises"),n=await t(this.config.cacheDir);return await Promise.allSettled(n.map(o=>e(C(this.config.cacheDir,o)))),!0}catch{return!1}}async getStats(){try{let{readdir:t,stat:e}=await import("fs/promises"),n=await t(this.config.cacheDir),o=0,a=0;for(let s of n)if(!s.endsWith(".time")){a++;let i=C(this.config.cacheDir,s);try{let c=await e(i);o+=c.size}catch{}}return{total:a,size:o}}catch{return{total:0,size:0}}}},b={NODE_VERSION:"node_version",PYTHON_VERSION:"python_version",PYTHON3_VERSION:"python3_version",DOCKER_VERSION:"docker_version",GIT_REMOTE_URL:r=>`git_remote_${Buffer.from(r).toString("base64")}`,GIT_BRANCH:r=>`git_branch_${Buffer.from(r).toString("base64")}`};async function E(r,t,e,n=[],o=300){let a=await r.get(t,o);if(a!==null)return a;try{let{exec:s}=await import("child_process"),{promisify:i}=await import("util"),c=i(s),{stdout:u}=await c(`${e} ${n.join(" ")}`,{timeout:5e3,encoding:"utf-8"}),d=u.trim();return d&&await r.set(t,d),d}catch{return null}}import{spawn as lt}from"child_process";async function g(r,t={}){return new Promise((e,n)=>{let o=lt("git",r,{cwd:t.cwd||process.cwd(),stdio:["ignore","pipe","pipe"],timeout:t.timeout||5e3}),a="",s="";o.stdout.on("data",i=>{a+=i.toString()}),o.stderr.on("data",i=>{s+=i.toString()}),o.on("close",i=>{i===0?e(a):n(new Error(`Git command failed with code ${i}: ${s||a}`))}),o.on("error",i=>{n(new Error(`Failed to execute git command: ${i.message}`))})})}async function F(r){try{return await g(["rev-parse","--git-dir"],r?{cwd:r}:{}),!0}catch{return!1}}async function k(r){let t=r?{cwd:r}:{};try{let n=(await g(["branch","--show-current"],t)).trim();if(n)return n}catch{}try{let n=(await g(["rev-parse","--abbrev-ref","HEAD"],t)).trim();if(n&&n!=="HEAD")return n}catch{}try{let n=(await g(["branch","--no-color"],t)).split(`
3
- `);for(let o of n)if(o.startsWith("* "))return o.substring(2).trim()}catch{}return null}async function M(r){return g(["status","--porcelain"],r?{cwd:r}:{})}async function U(r){try{return g(["stash","list"],r?{cwd:r}:{})}catch{return""}}async function G(r){try{return(await g(["rev-parse","--abbrev-ref","@{u}"],r?{cwd:r}:{})).trim()}catch{return""}}async function L(r){try{let n=(await g(["rev-list","--count","--left-right","@{u}...HEAD"],r?{cwd:r}:{})).trim().split(" ");if(n.length===2){let o=parseInt(n[0]||"0",10);return{ahead:parseInt(n[1]||"0",10),behind:o}}}catch{}return{ahead:0,behind:0}}var ut={stashed:0,staged:0,modified:0,untracked:0,renamed:0,deleted:0,conflicts:0,ahead:0,behind:0,diverged:!1},T=class{config;cache;constructor(t,e){this.config=t,this.cache=e}async getGitInfo(t){if(this.config.noGitStatus)return null;try{if(!await F(t))return null;let n=await this.getCurrentBranch(t);if(!n)return null;let o=await this.getGitIndicators(t);return{branch:n,indicators:o}}catch{return null}}async getCurrentBranch(t){let e=`${b.GIT_BRANCH(t)}_current`,n=await this.cache.get(e,60);if(n)return n;try{let o=await k(t);return o?(await this.cache.set(e,o),o):null}catch{return null}}async getGitIndicators(t){let e={...ut};try{let o=(await M(t)).split(`
4
- `).map(i=>i.trimEnd()).filter(i=>i.length>0);for(let i of o){if(i.length<2)continue;let c=i.charAt(0),u=i.charAt(1);if(c==="U"||u==="U"||c==="A"&&u==="A"||c==="D"&&u==="D")e.conflicts++;else if(c==="?"&&u==="?")e.untracked++;else{switch(c){case"M":e.staged++;break;case"A":e.staged++;break;case"D":e.deleted++;break;case"R":e.renamed++;break;case"C":e.staged++;break}switch(u){case"M":e.modified++;break;case"D":e.deleted++;break;case"R":e.renamed++;break}}}e.stashed=await this.getStashedCount(t);let{ahead:a,behind:s}=await this.getAheadBehind(t);e.ahead=a,e.behind=s,e.diverged=a>0&&s>0}catch{}return e}async getStashedCount(t){try{return(await U(t)).trim().split(`
5
- `).filter(n=>n.trim().length>0).length}catch{return 0}}async getAheadBehind(t){try{return await G(t)?await L(t):{ahead:0,behind:0}}catch{}return{ahead:0,behind:0}}formatIndicators(t,e){let n=[],o=["stashed","renamed","modified","staged","untracked","deleted","conflicts"];return t.stashed>0&&n.push(e.stashed),t.renamed>0&&n.push(e.renamed),t.modified>0&&n.push("!"),t.staged>0&&n.push(e.staged),t.untracked>0&&n.push("?"),t.deleted>0&&n.push(e.deleted),t.conflicts>0&&n.push(e.conflict),t.diverged?n.push(e.diverged):(t.ahead>0&&n.push(e.ahead),t.behind>0&&n.push(e.behind)),n.join("")}formatGitStatus(t,e){let n=this.formatIndicators(t.indicators,e),o=(this.config.noEmoji,e.git);return n?` ${o} ${t.branch} [${n}]`:` ${o} ${t.branch}`}};var B=new Map,dt=1,H={git:"@",model:"*",contextWindow:"#",staged:"+",conflict:"C",stashed:"$",ahead:"A",behind:"B",diverged:"D",renamed:">",deleted:"X"},ft={git:"\uF418",model:"\u{F06A9}",contextWindow:"\u26A1\uFE0E",staged:"+",conflict:"\xD7",stashed:"\u2691",ahead:"\u21E1",behind:"\u21E3",diverged:"\u21D5",renamed:"\xBB",deleted:"\u2718"};async function j(r){let t=process.env.NERD_FONT+"|"+process.env.TERM_PROGRAM+"|"+process.env.TERM,e=`${dt}:${r.noEmoji?"ascii":"nerd"}:${t}`,n=B.get(e);if(n&&Date.now()-n.timestamp<6e4)return n.symbols;let o;return r.noEmoji?o={...H,...r.symbols,...r.asciiSymbols}:(await mt()).hasNerdFont?o={...ft,...r.symbols}:o={...H,...r.asciiSymbols},B.set(e,{symbols:o,timestamp:Date.now()}),o}async function mt(){let r={hasNerdFont:!1,terminal:"",font:"",method:""};if(process.env.NERD_FONT==="1")return r.hasNerdFont=!0,r.method="NERD_FONT env var",r;let t=process.env.TERM_PROGRAM,e=process.env.TERM;if(t&&(r.terminal=t,r.method="TERM_PROGRAM detection",["vscode","ghostty","wezterm","iterm"].includes(t))||e&&(r.terminal=e,r.method="TERM detection",["alacritty","kitty","wezterm","ghostty","xterm-256color"].includes(e)))return r.hasNerdFont=!0,r;let n=await ht();if(n.hasNerdFont)return r.hasNerdFont=!0,r.font=n.font,r.method="font list detection",r;let o=await gt();return o.hasNerdFont?(r.hasNerdFont=!0,r.font=o.font,r.method="installation detection",r):pt().hasNerdFont?(r.hasNerdFont=!0,r.method="environment detection",r):((await yt()).hasNerdFont&&(r.hasNerdFont=!0,r.method="platform-specific detection"),r)}async function ht(){try{let{exec:r}=await import("child_process"),{promisify:t}=await import("util"),e=t(r),n="",o=process.platform;if(o==="linux"?n="fc-list":o==="darwin"&&(n="system_profiler SPFontsDataType 2>/dev/null || system_profiler SPFontsDataType"),n){let{stdout:a}=await e(n,{timeout:3e3,encoding:"utf-8"}),s=[/nerd font/i,/symbols only/i,/jetbrains mono.*nerd/i,/fira code.*nerd/i,/hack.*nerd/i,/source code pro.*nerd/i,/ubuntu mono.*nerd/i,/anonymous pro.*nerd/i];for(let i of s)if(i.test(a)){let c=a.match(/([^:\n]*)(?=\s*(nerd|symbols))/i);return{hasNerdFont:!0,font:(c?c[1]:"Nerd Font")?.trim()||"Nerd Font"}}}}catch{}return{hasNerdFont:!1,font:""}}async function gt(){try{let{access:r,readdir:t}=await import("fs/promises"),{homedir:e}=await import("os"),n=process.platform,o=[];n==="darwin"?o.push(`${e()}/Library/Fonts`,"/System/Library/Fonts","/Library/Fonts"):n==="linux"&&o.push(`${e()}/.local/share/fonts`,`${e()}/.fonts`,"/usr/share/fonts","/usr/local/share/fonts");let a=["jetbrains-mono-nerd-font","fira-code-nerd-font","hack-nerd-font","source-code-pro-nerd-font","ubuntu-mono-nerd-font","anonymous-pro-nerd-font"];for(let s of o)try{await r(s);let i=await t(s);for(let c of i){let u=c.toLowerCase();for(let d of a)if(u.includes(d))return{hasNerdFont:!0,font:c}}}catch{}}catch{}return{hasNerdFont:!1,font:""}}function pt(){let r=["POWERLINE_COMMAND","NERDFONTS","FONT_FAMILY"];for(let t of r){let e=process.env[t];if(e&&e.toLowerCase().includes("nerd"))return{hasNerdFont:!0}}return process.env.VSCODE_PID||process.env.TERM_PROGRAM==="vscode"||process.env.TERM_PROGRAM==="ghostty"||process.env.TERM_PROGRAM==="wezterm"?{hasNerdFont:!0}:{hasNerdFont:!1}}async function yt(){if(process.platform==="darwin")try{let{exec:t}=await import("child_process"),{promisify:e}=await import("util"),n=e(t),{stdout:o}=await n("brew list | grep -i font",{timeout:2e3,encoding:"utf-8"});if(o.includes("nerd"))return{hasNerdFont:!0}}catch{}return{hasNerdFont:!1}}function V(r){return{node:"\uE718",python:"\uE235",docker:"\uF308",git:r.git,model:r.model}}async function _(r){if(r.forceWidth&&r.forceWidth>0)return r.forceWidth;let t=process.env.COLUMNS;if(t){let i=parseInt(t,10);if(!isNaN(i)&&i>0)return i}if(process.stdout.columns&&process.stdout.columns>0)return process.stdout.columns;let e=await z("tput",["cols"]);if(e)return e;let n=await K();if(n)return n;let o=process.env.CLAUDE_CODE_TERMINAL_WIDTH;if(o){let i=parseInt(o,10);if(!isNaN(i)&&i>0)return i}let a=process.env.TERM_PROGRAM,s=process.env.TERM;return a==="vscode"&&process.env.VSCODE_PID||["ghostty","wezterm","iterm"].includes(a||"")||s&&["alacritty","kitty","wezterm","ghostty","xterm-256color"].includes(s)||process.env.WT_SESSION||process.env.WT_PROFILE_ID?120:80}async function z(r,t){try{let{exec:e}=await import("child_process"),{promisify:n}=await import("util"),o=n(e),{stdout:a}=await o(`${r} ${t.join(" ")}`,{timeout:1e3,encoding:"utf-8"}),s=parseInt(a.trim(),10);if(!isNaN(s)&&s>0)return s}catch{}return null}async function K(){try{let{exec:r}=await import("child_process"),{promisify:t}=await import("util"),e=t(r),{stdout:n}=await e("stty size",{timeout:1e3,encoding:"utf-8"}),o=n.trim().split(" ");if(o.length===2){let a=parseInt(o[1]||"0",10);if(!isNaN(a)&&a>0)return a}}catch{}return null}async function Y(r){if(!r.debugWidth)return;process.stdout.columns;let t=process.env.COLUMNS,e=await z("tput",["cols"]),n=await K(),o=await _(r)}function D(r,t){return r.length<=t?r:t<4?"..":`${r.substring(0,t-2)}..`}function J(r,t,e,n){if(r.length+t.length<=e)return"";let o=e-t.length-2;if(o>=5)return`${r.substring(0,o)}..${t}`;let a="",s=t.match(/\[([^\]]+)\]/);s&&(a=s[1]||"");let i=e-a.length-8;if(i>=8){let c=t.substring(0,Math.min(t.length,i));return`${r.substring(0,4)}..${c}..${a?` [${a}]`:""}`}return`${r.substring(0,e)}..`}var v=class{config;cache;constructor(t,e){this.config=t,this.cache=e}async getEnvironmentInfo(){if(!this.config.envContext)return null;let t={},[e,n,o]=await Promise.allSettled([this.getNodeVersion(),this.getPythonVersion(),this.getDockerVersion()]);return e.status==="fulfilled"&&e.value&&(t.node=e.value),n.status==="fulfilled"&&n.value&&(t.python=n.value),o.status==="fulfilled"&&o.value&&(t.docker=o.value),Object.keys(t).length===0?null:t}async getNodeVersion(){let t=b.NODE_VERSION,e=this.config.cacheTTL*96,n=await this.cache.get(t,e);if(n)return n;try{let o=await E(this.cache,t,"node",["--version"],e);return o?o.replace(/^v/,"").trim():null}catch{return null}}async getPythonVersion(){let t=b.PYTHON3_VERSION,e=b.PYTHON_VERSION,n=this.config.cacheTTL*96;try{let o=await E(this.cache,t,"python3",["--version"],n);if(o){let a=o.match(/(\d+\.\d+\.\d+)/);if(a)return a[1]||null}}catch{}try{let o=await E(this.cache,e,"python",["--version"],n);if(o){let a=o.match(/(\d+\.\d+\.\d+)/);if(a)return a[1]||null}}catch{}return null}async getDockerVersion(){let t="docker_version";try{let e=await E(this.cache,t,"docker",["--version"],this.config.cacheTTL*96);if(e){let n=e.match(/Docker version (\d+\.\d+\.\d+)/);if(n)return n[1]||null}return null}catch{return null}}formatEnvironmentInfo(t,e){let n=[];return t.node&&n.push(`${e.node}${t.node}`),t.python&&n.push(`${e.python}${t.python}`),t.docker&&n.push(`${e.docker}${t.docker}`),n.join(" ")}async getAdditionalTools(){return{}}async isToolAvailable(t){try{let{exec:e}=await import("child_process"),{promisify:n}=await import("util");return await n(e)(`command -v ${t}`,{timeout:2e3}),!0}catch{return!1}}getShellEnvironment(){let t=process.env.SHELL||"unknown",e;return t.includes("bash")?e=process.env.BASH_VERSION:t.includes("zsh")?e=process.env.ZSH_VERSION:t.includes("fish")&&(e=process.env.FISH_VERSION),e?{shell:t,shellVersion:e}:{shell:t}}getOSInfo(){let t=process.platform,e=process.arch,n=process.env.OSTYPE||process.env.OS;return n?{platform:t,arch:e,release:n}:{platform:t,arch:e}}},N=class{symbols;constructor(t){this.symbols=t}format(t,e="compact"){switch(e){case"compact":return this.formatCompact(t);case"verbose":return this.formatVerbose(t);case"minimal":return this.formatMinimal(t);default:return this.formatCompact(t)}}formatCompact(t){let e=[];return t.node&&e.push(`Node${t.node}`),t.python&&e.push(`Py${t.python}`),t.docker&&e.push(`Docker${t.docker}`),e.join(" ")}formatVerbose(t){let e=[];return t.node&&e.push(`Node.js v${t.node}`),t.python&&e.push(`Python ${t.python}`),t.docker&&e.push(`Docker ${t.docker}`),e.join(" \u2022 ")}formatMinimal(t){let e=[];if(t.node){let n=t.node.split(".")[0];e.push(`N${n}`)}if(t.python){let n=t.python.split(".")[0];e.push(`P${n}`)}if(t.docker){let n=t.docker.split(".")[0];e.push(`D${n}`)}return e.join(" ")}formatWithIcons(t){let e=[];return t.node&&e.push(`${this.symbols.node}${t.node}`),t.python&&e.push(`${this.symbols.python}${t.python}`),t.docker&&e.push(`${this.symbols.docker}${t.docker}`),e.join(" ")}};async function Et(){try{let r=O(),t=new w(r),e=new T(r,t),n=new v(r,t);await Y(r);let o=await St();R(JSON.stringify(o),r)||process.exit(1);let{fullDir:a,modelName:s,contextWindow:i}=Ct(o);(!a||!s)&&process.exit(1),await W(a)||process.exit(1);let u=[e.getGitInfo(a),n.getEnvironmentInfo(),j(r)],d;r.truncate&&u.push(_(r));let f=await Promise.all(u),[h,p,m]=f;r.truncate&&f.length>3&&(d=f[3]);let y=await wt({fullDir:a,modelName:s,contextWindow:i,gitInfo:h,envInfo:p,symbols:m,...d&&{terminalWidth:d},config:r,gitOps:e});process.stdout.write(y)}catch{process.exit(1)}}async function St(){try{let r=bt(0,"utf-8");return JSON.parse(r.trim())}catch(r){throw new Error(`Failed to read or parse input: ${r instanceof Error?r.message:String(r)}`)}}function Ct(r){let t=r.workspace?.current_dir||"",e=r.model?.display_name||"Unknown",n=r.context_window;return{fullDir:t,modelName:e,contextWindow:n}}async function wt(r){let{fullDir:t,modelName:e,contextWindow:n,gitInfo:o,envInfo:a,symbols:s,terminalWidth:i,config:c,gitOps:u}=r,d=t.split("/").pop()||t.split("\\").pop()||"project",f="";o&&(f=u.formatGitStatus(o,s));let h="";if(a){let S=V(s);h=` ${new N(S).formatWithIcons(a)}`}let p="";if(n&&!c.noContextWindow){let S=Tt(n);S!==null&&(p=` ${s.contextWindow}${S}%`)}let m=`${s.model}${e}${h}${p}`,y=`${d}${f} ${m}`;return c.truncate&&(i||process.exit(1),y=vt({statusline:y,projectName:d,gitStatus:f,modelString:m,terminalWidth:i,config:c,symbols:s})),y}function Tt(r){try{let{current_usage:t,context_window_size:e}=r;if(!e||e===0)return null;if(!t)return 0;let n=t.input_tokens+(t.cache_creation_input_tokens||0)+(t.cache_read_input_tokens||0),o=Math.round(n/e*100);return Math.max(0,Math.min(100,o))}catch{return 0}}function vt(r){let{statusline:t,projectName:e,gitStatus:n,modelString:o,terminalWidth:a,config:s}=r,i=Math.max(a-s.rightMargin,30),c=`${e}${n}`;if(t.length<=i)return t;if(c.length+1<=i)if(s.noSoftWrap){let d=i-c.length-1,f=D(o,d);return`${c} ${f}`}else{let d=i-c.length-1;if(/^[󰚩*]/.test(o)&&o.length>d)return`${c}
6
- ${o}`;let h=Nt(o,d);return`${c} ${h}`}let u=J(e,n,i,s);return u||D(t,i)}function Nt(r,t){if(r.length<=t)return r;if(/^[󰚩*]/.test(r))return _t(r,t);let n=!1,o=Array.from(r),a=0,s=o.length,i=-1;for(let m=0;m<o.length;m++)if(o[m]===" "&&(i=m),a++,a>t){i>=0?s=i:s=m,n=i>=0;break}if(s>=o.length)return r;let c=o.length>0?o[0]:"",u=c&&c!==" "&&Buffer.byteLength(c,"utf8")>1;if(!n&&t-a>-3&&!u)return r;if(u&&s<=2&&t>=3)for(let m=2;m<Math.min(o.length,t);m++){s=m,n=!0;break}let d=o.slice(0,s),f=o.slice(s);f.length>0&&f[0]===" "&&f.shift();let h=d.join(""),p=f.join("");return p?`${h}
7
- ${p}`:h}function _t(r,t){if(r.length<=t)return r;let e=Array.from(r),n=[];for(let s=0;s<e.length;s++)e[s]===" "&&n.push(s);let o=-1;for(let s=0;s<e.length;s++){let i=e[s];if(i==="\u26A1"||i==="#"){o=s;break}}if(o>0){let s=e.findIndex((i,c)=>c<o&&i===" ");if(s>0){let i=e.slice(s+1).join("");if(i.length<=t)return`${e.slice(0,s).join("")}
8
- ${i}`}}if(n.length>0){let s=n[0];if(s!==void 0&&s>1){let i=e.slice(0,s).join("");if(i.length<=t){let c=e.slice(s+1).join("");return c?`${i}
9
- ${c}`:i}}}let a=Math.min(5,t);if(a>=3&&e.length>a){let s=Math.min(t,e.length-1),i=e.slice(0,s).join(""),c=e.slice(s).join("");return c?`${i}
10
- ${c}`:i}if(t>=3){let s=e.slice(0,t).join(""),i=e.slice(t).join("");return i?`${s}
11
- ${i}`:s}return r}import.meta.url===`file://${process.argv[1]}`&&Et();export{Et as main};
2
+ import{readFileSync as St}from"fs";import{z as u}from"zod";import{readFileSync as q,existsSync as Q}from"fs";import{homedir as tt}from"os";import{join as P,dirname as et}from"path";import{parse as rt}from"yaml";var I=u.object({cacheTTL:u.number().default(300),cacheDir:u.string().default("/tmp/.claude-statusline-cache"),maxLength:u.number().default(1e3),noEmoji:u.boolean().default(!1),noGitStatus:u.boolean().default(!1),noContextWindow:u.boolean().default(!1),envContext:u.boolean().default(!1),truncate:u.boolean().default(!1),softWrap:u.boolean().default(!1),noSoftWrap:u.boolean().default(!1),forceWidth:u.number().optional(),debugWidth:u.boolean().default(!1),rightMargin:u.number().default(15),symbols:u.object({git:u.string().default("\uF418"),model:u.string().default("\u{F06A9}"),contextWindow:u.string().default("\u26A1\uFE0E"),staged:u.string().default("+"),conflict:u.string().default("\xD7"),stashed:u.string().default("\u2691"),ahead:u.string().default("\u21E1"),behind:u.string().default("\u21E3"),diverged:u.string().default("\u21D5"),renamed:u.string().default("\xBB"),deleted:u.string().default("\u2718")}).default({}),asciiSymbols:u.object({git:u.string().default("@"),model:u.string().default("*"),contextWindow:u.string().default("#"),staged:u.string().default("+"),conflict:u.string().default("C"),stashed:u.string().default("$"),ahead:u.string().default("A"),behind:u.string().default("B"),diverged:u.string().default("D"),renamed:u.string().default(">"),deleted:u.string().default("X")}).default({})}),nt=I.parse({}),ot=["claude-statusline.json","claude-statusline.yaml"];function W(n=process.cwd()){let t={...nt};return t={...t,...st(n)},t={...t,...it()},I.parse(t)}function st(n){let t=[n,et(n),P(tt(),".claude")];for(let r of t)for(let e of ot){let o=P(r,e);if(Q(o))try{let s=q(o,"utf-8");if(e.endsWith(".json"))return JSON.parse(s);if(e.endsWith(".yaml"))return rt(s)}catch{}}return{}}function it(){let n={};if(process.env.CLAUDE_CODE_STATUSLINE_NO_EMOJI==="1"&&(n.noEmoji=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_GITSTATUS==="1"&&(n.noGitStatus=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_CONTEXT_WINDOW==="1"&&(n.noContextWindow=!0),process.env.CLAUDE_CODE_STATUSLINE_ENV_CONTEXT==="1"&&(n.envContext=!0),process.env.CLAUDE_CODE_STATUSLINE_TRUNCATE==="1"&&(n.truncate=!0),process.env.CLAUDE_CODE_STATUSLINE_SOFT_WRAP==="1"&&(n.softWrap=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_SOFT_WRAP==="1"&&(n.noSoftWrap=!0),process.env.CLAUDE_CODE_STATUSLINE_FORCE_WIDTH){let t=parseInt(process.env.CLAUDE_CODE_STATUSLINE_FORCE_WIDTH,10);!isNaN(t)&&t>0&&(n.forceWidth=t)}return process.env.CLAUDE_CODE_STATUSLINE_DEBUG_WIDTH==="1"&&(n.debugWidth=!0),process.env.CLAUDE_CODE_STATUSLINE_CACHE_DIR&&(n.cacheDir=process.env.CLAUDE_CODE_STATUSLINE_CACHE_DIR),n}var at=4096,ct=[/\.\./,/\.\.\\/,/\[/,/;/,/&/,/</,/>/,/`/];function O(n,t){if(!n||typeof n!="string")return!1;let r=n.trimEnd();if(r.length===0||r.length>t.maxLength||!r.includes("{")||!r.includes("}"))return!1;let e=(r.match(/"/g)||[]).length;if(e===0||e%2!==0)return!1;try{JSON.parse(r)}catch{return!1}return!0}function lt(n){if(!n||typeof n!="string"||n.length>at)return!1;for(let t of ct)if(t.test(n))return!1;if(n.includes("${")||n.includes("`")||n.includes("$("))return!1;try{let t=n.replace(/\/+/g,"/").replace(/\\+/g,"\\");if(t.includes("../")||t.includes("..\\")||t.startsWith("/")&&!t.startsWith("/home/")&&!t.startsWith("/Users/")&&!t.startsWith("/tmp/")&&!["/home","/Users","/tmp","/var","/opt"].some(o=>t.startsWith(o)))return!1;if(t.includes(":")&&/^[A-Za-z]:/.test(t)){let r=t.charAt(0).toUpperCase();if(r<"C"||r>"Z")return!1}}catch{return!1}return!0}async function R(n){if(!lt(n))return!1;try{let{access:t}=await import("fs/promises"),{constants:r}=await import("fs");return await t(n,r.R_OK),!0}catch{return!1}}import{readFile as $,writeFile as A,mkdir as ut}from"fs/promises";import{existsSync as k}from"fs";import{join as w}from"path";var T=class{config;constructor(t){this.config=t}async ensureCacheDir(){try{await ut(this.config.cacheDir,{recursive:!0})}catch{}}getCachePath(t){return w(this.config.cacheDir,t)}getTimestampPath(t){return w(this.config.cacheDir,`${t}.time`)}async get(t,r=this.config.cacheTTL){let e=this.getCachePath(t),o=this.getTimestampPath(t);try{if(!k(e)||!k(o))return null;let s=await $(o,"utf-8"),c=parseInt(s.trim(),10);if(isNaN(c)||Math.floor(Date.now()/1e3)-c>=r)return null;let d=await $(e,"utf-8");try{return JSON.parse(d)}catch{return d}}catch{return null}}async set(t,r){let e=this.getCachePath(t),o=this.getTimestampPath(t);try{await this.ensureCacheDir();let s;typeof r=="string"?s=r:s=JSON.stringify(r);let c=Math.floor(Date.now()/1e3);return await Promise.all([A(e,s,"utf-8"),A(o,c.toString(),"utf-8")]),!0}catch{return!1}}async has(t,r=this.config.cacheTTL){return await this.get(t,r)!==null}async delete(t){let r=this.getCachePath(t),e=this.getTimestampPath(t);try{let{unlink:o}=await import("fs/promises");return await Promise.allSettled([o(r),o(e)]),!0}catch{return!1}}async clear(){try{let{readdir:t,unlink:r}=await import("fs/promises"),e=await t(this.config.cacheDir);return await Promise.allSettled(e.map(o=>r(w(this.config.cacheDir,o)))),!0}catch{return!1}}async getStats(){try{let{readdir:t,stat:r}=await import("fs/promises"),e=await t(this.config.cacheDir),o=0,s=0;for(let c of e)if(!c.endsWith(".time")){s++;let i=w(this.config.cacheDir,c);try{let a=await r(i);o+=a.size}catch{}}return{total:s,size:o}}catch{return{total:0,size:0}}}},S={NODE_VERSION:"node_version",PYTHON_VERSION:"python_version",PYTHON3_VERSION:"python3_version",DOCKER_VERSION:"docker_version",GIT_REMOTE_URL:n=>`git_remote_${Buffer.from(n).toString("base64")}`,GIT_BRANCH:n=>`git_branch_${Buffer.from(n).toString("base64")}`};async function D(n,t,r,e=[],o=300){let s=await n.get(t,o);if(s!==null)return s;try{let{exec:c}=await import("child_process"),{promisify:i}=await import("util"),a=i(c),{stdout:l}=await a(`${r} ${e.join(" ")}`,{timeout:5e3,encoding:"utf-8"}),d=l.trim();return d&&await n.set(t,d),d}catch{return null}}import{spawn as dt}from"child_process";async function E(n,t={}){return new Promise((r,e)=>{let o=dt("git",n,{cwd:t.cwd||process.cwd(),stdio:["ignore","pipe","pipe"],timeout:t.timeout||5e3}),s="",c="";o.stdout.on("data",i=>{s+=i.toString()}),o.stderr.on("data",i=>{c+=i.toString()}),o.on("close",i=>{i===0?r(s):e(new Error(`Git command failed with code ${i}: ${c||s}`))}),o.on("error",i=>{e(new Error(`Failed to execute git command: ${i.message}`))})})}async function U(n){try{return await E(["rev-parse","--git-dir"],n?{cwd:n}:{}),!0}catch{return!1}}async function G(n){let t=n?{cwd:n}:{};try{let e=(await E(["branch","--show-current"],t)).trim();if(e)return e}catch{}try{let e=(await E(["rev-parse","--abbrev-ref","HEAD"],t)).trim();if(e&&e!=="HEAD")return e}catch{}try{let e=(await E(["branch","--no-color"],t)).split(`
3
+ `);for(let o of e)if(o.startsWith("* "))return o.substring(2).trim()}catch{}return null}async function M(n){return E(["status","--porcelain"],n?{cwd:n}:{})}async function L(n){try{return E(["stash","list"],n?{cwd:n}:{})}catch{return""}}async function B(n){try{return(await E(["rev-parse","--abbrev-ref","@{u}"],n?{cwd:n}:{})).trim()}catch{return""}}async function H(n){try{let e=(await E(["rev-list","--count","--left-right","@{u}...HEAD"],n?{cwd:n}:{})).trim().split(" ");if(e.length===2){let o=parseInt(e[0]||"0",10);return{ahead:parseInt(e[1]||"0",10),behind:o}}}catch{}return{ahead:0,behind:0}}var ft={stashed:0,staged:0,modified:0,untracked:0,renamed:0,deleted:0,conflicts:0,ahead:0,behind:0,diverged:!1},v=class{config;cache;constructor(t,r){this.config=t,this.cache=r}async getGitInfo(t){if(this.config.noGitStatus)return null;try{if(!await U(t))return null;let e=await this.getCurrentBranch(t);if(!e)return null;let o=await this.getGitIndicators(t);return{branch:e,indicators:o}}catch{return null}}async getCurrentBranch(t){let r=`${S.GIT_BRANCH(t)}_current`,e=await this.cache.get(r,60);if(e)return e;try{let o=await G(t);return o?(await this.cache.set(r,o),o):null}catch{return null}}async getGitIndicators(t){let r={...ft};try{let o=(await M(t)).split(`
4
+ `).map(i=>i.trimEnd()).filter(i=>i.length>0);for(let i of o){if(i.length<2)continue;let a=i.charAt(0),l=i.charAt(1);if(a==="U"||l==="U"||a==="A"&&l==="A"||a==="D"&&l==="D")r.conflicts++;else if(a==="?"&&l==="?")r.untracked++;else{switch(a){case"M":r.staged++;break;case"A":r.staged++;break;case"D":r.deleted++;break;case"R":r.renamed++;break;case"C":r.staged++;break}switch(l){case"M":r.modified++;break;case"D":r.deleted++;break;case"R":r.renamed++;break}}}r.stashed=await this.getStashedCount(t);let{ahead:s,behind:c}=await this.getAheadBehind(t);r.ahead=s,r.behind=c,r.diverged=s>0&&c>0}catch{}return r}async getStashedCount(t){try{return(await L(t)).trim().split(`
5
+ `).filter(e=>e.trim().length>0).length}catch{return 0}}async getAheadBehind(t){try{return await B(t)?await H(t):{ahead:0,behind:0}}catch{}return{ahead:0,behind:0}}formatIndicators(t,r){let e=[],o=["stashed","renamed","modified","staged","untracked","deleted","conflicts"];return t.stashed>0&&e.push(r.stashed),t.renamed>0&&e.push(r.renamed),t.modified>0&&e.push("!"),t.staged>0&&e.push(r.staged),t.untracked>0&&e.push("?"),t.deleted>0&&e.push(r.deleted),t.conflicts>0&&e.push(r.conflict),t.diverged?e.push(r.diverged):(t.ahead>0&&e.push(r.ahead),t.behind>0&&e.push(r.behind)),e.join("")}formatGitStatus(t,r){let e=this.formatIndicators(t.indicators,r),o=(this.config.noEmoji,r.git);return e?` ${o} ${t.branch} [${e}]`:` ${o} ${t.branch}`}};var j=new Map,mt=1,V={git:"@",model:"*",contextWindow:"#",staged:"+",conflict:"C",stashed:"$",ahead:"A",behind:"B",diverged:"D",renamed:">",deleted:"X"},ht={git:"\uF418",model:"\u{F06A9}",contextWindow:"\u26A1\uFE0E",staged:"+",conflict:"\xD7",stashed:"\u2691",ahead:"\u21E1",behind:"\u21E3",diverged:"\u21D5",renamed:"\xBB",deleted:"\u2718"};async function z(n){let t=process.env.NERD_FONT+"|"+process.env.TERM_PROGRAM+"|"+process.env.TERM,r=`${mt}:${n.noEmoji?"ascii":"nerd"}:${t}`,e=j.get(r);if(e&&Date.now()-e.timestamp<6e4)return e.symbols;let o;return n.noEmoji?o={...V,...n.symbols,...n.asciiSymbols}:(await gt()).hasNerdFont?o={...ht,...n.symbols}:o={...V,...n.asciiSymbols},j.set(r,{symbols:o,timestamp:Date.now()}),o}async function gt(){let n={hasNerdFont:!1,terminal:"",font:"",method:""};if(process.env.NERD_FONT==="1")return n.hasNerdFont=!0,n.method="NERD_FONT env var",n;let t=process.env.TERM_PROGRAM,r=process.env.TERM;if(t&&(n.terminal=t,n.method="TERM_PROGRAM detection",["vscode","ghostty","wezterm","iterm"].includes(t))||r&&(n.terminal=r,n.method="TERM detection",["alacritty","kitty","wezterm","ghostty","xterm-256color"].includes(r)))return n.hasNerdFont=!0,n;let e=await pt();if(e.hasNerdFont)return n.hasNerdFont=!0,n.font=e.font,n.method="font list detection",n;let o=await yt();return o.hasNerdFont?(n.hasNerdFont=!0,n.font=o.font,n.method="installation detection",n):bt().hasNerdFont?(n.hasNerdFont=!0,n.method="environment detection",n):((await Et()).hasNerdFont&&(n.hasNerdFont=!0,n.method="platform-specific detection"),n)}async function pt(){try{let{exec:n}=await import("child_process"),{promisify:t}=await import("util"),r=t(n),e="",o=process.platform;if(o==="linux"?e="fc-list":o==="darwin"&&(e="system_profiler SPFontsDataType 2>/dev/null || system_profiler SPFontsDataType"),e){let{stdout:s}=await r(e,{timeout:3e3,encoding:"utf-8"}),c=[/nerd font/i,/symbols only/i,/jetbrains mono.*nerd/i,/fira code.*nerd/i,/hack.*nerd/i,/source code pro.*nerd/i,/ubuntu mono.*nerd/i,/anonymous pro.*nerd/i];for(let i of c)if(i.test(s)){let a=s.match(/([^:\n]*)(?=\s*(nerd|symbols))/i);return{hasNerdFont:!0,font:(a?a[1]:"Nerd Font")?.trim()||"Nerd Font"}}}}catch{}return{hasNerdFont:!1,font:""}}async function yt(){try{let{access:n,readdir:t}=await import("fs/promises"),{homedir:r}=await import("os"),e=process.platform,o=[];e==="darwin"?o.push(`${r()}/Library/Fonts`,"/System/Library/Fonts","/Library/Fonts"):e==="linux"&&o.push(`${r()}/.local/share/fonts`,`${r()}/.fonts`,"/usr/share/fonts","/usr/local/share/fonts");let s=["jetbrains-mono-nerd-font","fira-code-nerd-font","hack-nerd-font","source-code-pro-nerd-font","ubuntu-mono-nerd-font","anonymous-pro-nerd-font"];for(let c of o)try{await n(c);let i=await t(c);for(let a of i){let l=a.toLowerCase();for(let d of s)if(l.includes(d))return{hasNerdFont:!0,font:a}}}catch{}}catch{}return{hasNerdFont:!1,font:""}}function bt(){let n=["POWERLINE_COMMAND","NERDFONTS","FONT_FAMILY"];for(let t of n){let r=process.env[t];if(r&&r.toLowerCase().includes("nerd"))return{hasNerdFont:!0}}return process.env.VSCODE_PID||process.env.TERM_PROGRAM==="vscode"||process.env.TERM_PROGRAM==="ghostty"||process.env.TERM_PROGRAM==="wezterm"?{hasNerdFont:!0}:{hasNerdFont:!1}}async function Et(){if(process.platform==="darwin")try{let{exec:t}=await import("child_process"),{promisify:r}=await import("util"),e=r(t),{stdout:o}=await e("brew list | grep -i font",{timeout:2e3,encoding:"utf-8"});if(o.includes("nerd"))return{hasNerdFont:!0}}catch{}return{hasNerdFont:!1}}function K(n){return{node:"\uE718",python:"\uE235",docker:"\uF308",git:n.git,model:n.model}}async function x(n){if(n.forceWidth&&n.forceWidth>0)return n.forceWidth;let t=process.env.COLUMNS;if(t){let i=parseInt(t,10);if(!isNaN(i)&&i>0)return i}if(process.stdout.columns&&process.stdout.columns>0)return process.stdout.columns;let r=await Y("tput",["cols"]);if(r)return r;let e=await J();if(e)return e;let o=process.env.CLAUDE_CODE_TERMINAL_WIDTH;if(o){let i=parseInt(o,10);if(!isNaN(i)&&i>0)return i}let s=process.env.TERM_PROGRAM,c=process.env.TERM;return s==="vscode"&&process.env.VSCODE_PID||["ghostty","wezterm","iterm"].includes(s||"")||c&&["alacritty","kitty","wezterm","ghostty","xterm-256color"].includes(c)||process.env.WT_SESSION||process.env.WT_PROFILE_ID?120:80}async function Y(n,t){try{let{exec:r}=await import("child_process"),{promisify:e}=await import("util"),o=e(r),{stdout:s}=await o(`${n} ${t.join(" ")}`,{timeout:1e3,encoding:"utf-8"}),c=parseInt(s.trim(),10);if(!isNaN(c)&&c>0)return c}catch{}return null}async function J(){try{let{exec:n}=await import("child_process"),{promisify:t}=await import("util"),r=t(n),{stdout:e}=await r("stty size",{timeout:1e3,encoding:"utf-8"}),o=e.trim().split(" ");if(o.length===2){let s=parseInt(o[1]||"0",10);if(!isNaN(s)&&s>0)return s}}catch{}return null}async function X(n){if(!n.debugWidth)return;process.stdout.columns;let t=process.env.COLUMNS,r=await Y("tput",["cols"]),e=await J(),o=await x(n)}function F(n,t){return n.length<=t?n:t<4?"..":`${n.substring(0,t-2)}..`}function Z(n,t,r,e){if(n.length+t.length<=r)return"";let o=r-t.length-2;if(o>=5)return`${n.substring(0,o)}..${t}`;let s="",c=t.match(/\[([^\]]+)\]/);c&&(s=c[1]||"");let i=r-s.length-8;if(i>=8){let a=t.substring(0,Math.min(t.length,i));return`${n.substring(0,4)}..${a}..${s?` [${s}]`:""}`}return`${n.substring(0,r)}..`}function y(n){let t=0;for(let r of n){let e=r.codePointAt(0)??0;e>=4352&&(e>=4352&&e<=4447||e>=11904&&e<=42191||e>=44032&&e<=55203||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65135||e>=65280&&e<=65376||e>=65504&&e<=65510||e>=131072&&e<=196605||e>=196608&&e<=262141)||e>=127744&&e<=129535||e>=9728&&e<=10175||e>=65024&&e<=65039||e>=126976&&e<=127023||e>=57344&&e<=63743||e>=983040&&e<=1048573||e>=1048576&&e<=1114109?t+=2:e>=768&&e<=879||e>=7616&&e<=7679||e>=8400&&e<=8447||e>=65056&&e<=65071||(t+=1)}return t}var N=class{config;cache;constructor(t,r){this.config=t,this.cache=r}async getEnvironmentInfo(){if(!this.config.envContext)return null;let t={},[r,e,o]=await Promise.allSettled([this.getNodeVersion(),this.getPythonVersion(),this.getDockerVersion()]);return r.status==="fulfilled"&&r.value&&(t.node=r.value),e.status==="fulfilled"&&e.value&&(t.python=e.value),o.status==="fulfilled"&&o.value&&(t.docker=o.value),Object.keys(t).length===0?null:t}async getNodeVersion(){let t=S.NODE_VERSION,r=this.config.cacheTTL*96,e=await this.cache.get(t,r);if(e)return e;try{let o=await D(this.cache,t,"node",["--version"],r);return o?o.replace(/^v/,"").trim():null}catch{return null}}async getPythonVersion(){let t=S.PYTHON3_VERSION,r=S.PYTHON_VERSION,e=this.config.cacheTTL*96;try{let o=await D(this.cache,t,"python3",["--version"],e);if(o){let s=o.match(/(\d+\.\d+\.\d+)/);if(s)return s[1]||null}}catch{}try{let o=await D(this.cache,r,"python",["--version"],e);if(o){let s=o.match(/(\d+\.\d+\.\d+)/);if(s)return s[1]||null}}catch{}return null}async getDockerVersion(){let t="docker_version";try{let r=await D(this.cache,t,"docker",["--version"],this.config.cacheTTL*96);if(r){let e=r.match(/Docker version (\d+\.\d+\.\d+)/);if(e)return e[1]||null}return null}catch{return null}}formatEnvironmentInfo(t,r){let e=[];return t.node&&e.push(`${r.node}${t.node}`),t.python&&e.push(`${r.python}${t.python}`),t.docker&&e.push(`${r.docker}${t.docker}`),e.join(" ")}async getAdditionalTools(){return{}}async isToolAvailable(t){try{let{exec:r}=await import("child_process"),{promisify:e}=await import("util");return await e(r)(`command -v ${t}`,{timeout:2e3}),!0}catch{return!1}}getShellEnvironment(){let t=process.env.SHELL||"unknown",r;return t.includes("bash")?r=process.env.BASH_VERSION:t.includes("zsh")?r=process.env.ZSH_VERSION:t.includes("fish")&&(r=process.env.FISH_VERSION),r?{shell:t,shellVersion:r}:{shell:t}}getOSInfo(){let t=process.platform,r=process.arch,e=process.env.OSTYPE||process.env.OS;return e?{platform:t,arch:r,release:e}:{platform:t,arch:r}}},_=class{symbols;constructor(t){this.symbols=t}format(t,r="compact"){switch(r){case"compact":return this.formatCompact(t);case"verbose":return this.formatVerbose(t);case"minimal":return this.formatMinimal(t);default:return this.formatCompact(t)}}formatCompact(t){let r=[];return t.node&&r.push(`Node${t.node}`),t.python&&r.push(`Py${t.python}`),t.docker&&r.push(`Docker${t.docker}`),r.join(" ")}formatVerbose(t){let r=[];return t.node&&r.push(`Node.js v${t.node}`),t.python&&r.push(`Python ${t.python}`),t.docker&&r.push(`Docker ${t.docker}`),r.join(" \u2022 ")}formatMinimal(t){let r=[];if(t.node){let e=t.node.split(".")[0];r.push(`N${e}`)}if(t.python){let e=t.python.split(".")[0];r.push(`P${e}`)}if(t.docker){let e=t.docker.split(".")[0];r.push(`D${e}`)}return r.join(" ")}formatWithIcons(t){let r=[];return t.node&&r.push(`${this.symbols.node}${t.node}`),t.python&&r.push(`${this.symbols.python}${t.python}`),t.docker&&r.push(`${this.symbols.docker}${t.docker}`),r.join(" ")}};async function Ct(){try{let n=W(),t=new T(n),r=new v(n,t),e=new N(n,t);await X(n);let o=await Dt();O(JSON.stringify(o),n)||process.exit(1);let{fullDir:s,modelName:c,contextWindow:i}=wt(o);(!s||!c)&&process.exit(1),await R(s)||process.exit(1);let l=[r.getGitInfo(s),e.getEnvironmentInfo(),z(n)],d;n.truncate&&l.push(x(n));let m=await Promise.all(l),[h,p,b]=m;n.truncate&&m.length>3&&(d=m[3]);let f=await Tt({fullDir:s,modelName:c,contextWindow:i,gitInfo:h,envInfo:p,symbols:b,...d&&{terminalWidth:d},config:n,gitOps:r});process.stdout.write(f)}catch{process.exit(1)}}async function Dt(){try{let n=St(0,"utf-8");return JSON.parse(n.trim())}catch(n){throw new Error(`Failed to read or parse input: ${n instanceof Error?n.message:String(n)}`)}}function wt(n){let t=n.workspace?.current_dir||"",r=n.model?.display_name||"Unknown",e=n.context_window;return{fullDir:t,modelName:r,contextWindow:e}}async function Tt(n){let{fullDir:t,modelName:r,contextWindow:e,gitInfo:o,envInfo:s,symbols:c,terminalWidth:i,config:a,gitOps:l}=n,d=t.split("/").pop()||t.split("\\").pop()||"project",m="";o&&(m=l.formatGitStatus(o,c));let h="";if(s){let g=K(c);h=` ${new _(g).formatWithIcons(s)}`}let p="";if(e&&!a.noContextWindow){let g=vt(e);g!==null&&(p=` ${c.contextWindow}${g}%`)}let b=`${c.model}${r}${h}${p}`,f=`${d}${m} ${b}`;return a.truncate&&(i||process.exit(1),f=Nt({statusline:f,projectName:d,gitStatus:m,modelString:b,terminalWidth:i,config:a,symbols:c})),f}function vt(n){try{let{current_usage:t,context_window_size:r}=n;if(!r||r===0)return null;if(!t)return 0;let e=t.input_tokens+(t.cache_creation_input_tokens||0)+(t.cache_read_input_tokens||0),o=Math.round(e/r*100);return Math.max(0,Math.min(100,o))}catch{return 0}}function Nt(n){let{statusline:t,projectName:r,gitStatus:e,modelString:o,terminalWidth:s,config:c}=n,i=Math.max(s-c.rightMargin,30),a=`${r}${e}`;if(y(t)<=i)return t;let d=y(a);if(d+1<=i)if(c.noSoftWrap){let h=i-d-1,p=F(o,h);return`${a} ${p}`}else{let h=i-d-1,p=/^[󰚩*]/,b=y(o);if(p.test(o)&&b>h)return`${a}
6
+ ${o}`;let f=_t(o,h);return`${a} ${f}`}let m=Z(r,e,i,c);return m||F(t,i)}function _t(n,t){if(y(n)<=t)return n;if(/^[󰚩*]/.test(n))return xt(n,t);let o=!1,s=Array.from(n),c=0,i=s.length,a=-1;for(let f=0;f<s.length;f++){let g=s[f];if(!g)break;g===" "&&(a=f);let C=y(g);if(c+=C,c>t){a>=0?i=a:i=f,o=a>=0;break}}if(i>=s.length)return n;let l=s.length>0?s[0]:"",d=l&&l!==" "&&Buffer.byteLength(l,"utf8")>1;if(!o&&t-c>-3&&!d)return n;if(d&&i<=2&&t>=3){let f=0;for(let g=0;g<s.length;g++){let C=s[g];if(!C)break;if(f+=y(C),f>=3||f>=t){i=g,o=!0;break}}}let m=s.slice(0,i),h=s.slice(i);h.length>0&&h[0]===" "&&h.shift();let p=m.join(""),b=h.join("");return b?`${p}
7
+ ${b}`:p}function xt(n,t){if(y(n)<=t)return n;let e=Array.from(n),o=[];for(let a=0;a<e.length;a++)e[a]===" "&&o.push(a);let s=-1;for(let a=0;a<e.length;a++){let l=e[a];if(l==="\u26A1"||l==="#"){s=a;break}}if(s>0){let a=e.findIndex((l,d)=>d<s&&l===" ");if(a>0){let l=e.slice(a+1).join("");if(y(l)<=t)return`${e.slice(0,a).join("")}
8
+ ${l}`}}if(o.length>0){let a=o[0];if(a!==void 0&&a>1){let l=e.slice(0,a).join("");if(y(l)<=t){let m=e.slice(a+1).join("");return m?`${l}
9
+ ${m}`:l}}}let c=0,i=e.length;for(let a=0;a<e.length;a++){let l=e[a];if(!l)break;let d=y(l);if(c+d>t){i=a;break}c+=d}if(i<e.length&&i>0){let a=e.slice(0,i).join(""),l=e.slice(i).join("");return l?`${a}
10
+ ${l}`:a}return n}import.meta.url===`file://${process.argv[1]}`&&Ct();export{Ct as main};
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { validateInput, validateDirectory } from './core/security.js';
9
9
  import { Cache } from './core/cache.js';
10
10
  import { GitOperations } from './git/status.js';
11
11
  import { detectSymbols, getEnvironmentSymbols } from './ui/symbols.js';
12
- import { getTerminalWidth, truncateText, smartTruncate, debugWidthDetection } from './ui/width.js';
12
+ import { getTerminalWidth, truncateText, smartTruncate, debugWidthDetection, getStringDisplayWidth } from './ui/width.js';
13
13
  import { EnvironmentDetector, EnvironmentFormatter } from './env/context.js';
14
14
  /**
15
15
  * Main execution function
@@ -188,27 +188,30 @@ function applySmartTruncation(params) {
188
188
  // Use 15-char margin for Claude telemetry compatibility
189
189
  const maxLen = Math.max(terminalWidth - config.rightMargin, 30);
190
190
  const projectGit = `${projectName}${gitStatus}`;
191
- // Check if everything fits
192
- if (statusline.length <= maxLen) {
191
+ // Check if everything fits (using display width for accuracy)
192
+ const statuslineDisplayWidth = getStringDisplayWidth(statusline);
193
+ if (statuslineDisplayWidth <= maxLen) {
193
194
  return statusline;
194
195
  }
195
- // Check if project + space fits, truncate model part only
196
- if (projectGit.length + 1 <= maxLen) {
196
+ // Check if project + space fits, truncate model part only (using display width)
197
+ const projectGitDisplayWidth = getStringDisplayWidth(projectGit);
198
+ if (projectGitDisplayWidth + 1 <= maxLen) {
197
199
  // Smart truncation with soft-wrapping (default behavior)
198
200
  // Allow disabling soft-wrapping with config setting
199
201
  if (config.noSoftWrap) {
200
202
  // Legacy behavior: simple truncation only
201
- const modelMaxLen = maxLen - projectGit.length - 1;
203
+ const modelMaxLen = maxLen - projectGitDisplayWidth - 1;
202
204
  const truncatedModel = truncateText(modelString, modelMaxLen);
203
205
  return `${projectGit} ${truncatedModel}`;
204
206
  }
205
207
  else {
206
208
  // Default: soft-wrap model part
207
- const modelMaxLen = maxLen - projectGit.length - 1;
209
+ const modelMaxLen = maxLen - projectGitDisplayWidth - 1;
208
210
  // If model string needs wrapping and it starts with model icon,
209
211
  // prefer wrapping the entire model string to next line
210
212
  const modelIconPattern = /^[󰚩*]/;
211
- if (modelIconPattern.test(modelString) && modelString.length > modelMaxLen) {
213
+ const modelDisplayWidth = getStringDisplayWidth(modelString);
214
+ if (modelIconPattern.test(modelString) && modelDisplayWidth > modelMaxLen) {
212
215
  // Wrap entire model to next line
213
216
  return `${projectGit}\n${modelString}`;
214
217
  }
@@ -228,7 +231,9 @@ function applySmartTruncation(params) {
228
231
  * Apply soft wrapping to text
229
232
  */
230
233
  function applySoftWrap(text, maxLength) {
231
- if (text.length <= maxLength) {
234
+ // Use display width for accurate measurement
235
+ const displayWidth = getStringDisplayWidth(text);
236
+ if (displayWidth <= maxLength) {
232
237
  return text;
233
238
  }
234
239
  // Check if this is a model string (starts with model icon)
@@ -236,26 +241,27 @@ function applySoftWrap(text, maxLength) {
236
241
  if (modelIconPattern.test(text)) {
237
242
  return applySoftWrapToModelString(text, maxLength);
238
243
  }
239
- // Find a good break point
244
+ // Find a good break point using display width
240
245
  let foundBreak = false;
241
246
  // Work with actual Unicode characters to avoid splitting multi-byte sequences
242
247
  const chars = Array.from(text); // This splits by actual Unicode characters
243
- let charCount = 0;
248
+ let currentDisplayWidth = 0;
244
249
  let breakCharIndex = chars.length; // Default to no break
245
250
  let lastSpaceIndex = -1;
246
- // Find the best break point by character count
251
+ // Find the best break point by display width
247
252
  for (let i = 0; i < chars.length; i++) {
248
253
  const char = chars[i];
254
+ if (!char)
255
+ break;
249
256
  // Track spaces for potential break points
250
257
  if (char === ' ') {
251
258
  lastSpaceIndex = i;
252
259
  }
253
- // Estimate display width (this is approximate)
254
- // Most Unicode icons count as 1 display character
255
- // ASCII characters count as 1
256
- charCount++;
260
+ // Calculate display width for this character
261
+ const charWidth = getStringDisplayWidth(char);
262
+ currentDisplayWidth += charWidth;
257
263
  // Check if we've exceeded the max length
258
- if (charCount > maxLength) {
264
+ if (currentDisplayWidth > maxLength) {
259
265
  // If we found a space before this point, use it
260
266
  if (lastSpaceIndex >= 0) {
261
267
  breakCharIndex = lastSpaceIndex;
@@ -276,17 +282,24 @@ function applySoftWrap(text, maxLength) {
276
282
  // But only if we're not dealing with a model string that starts with an icon
277
283
  const firstChar = chars.length > 0 ? chars[0] : '';
278
284
  const startsWithIcon = firstChar && firstChar !== ' ' && Buffer.byteLength(firstChar, 'utf8') > 1;
279
- if (!foundBreak && maxLength - charCount > -3 && !startsWithIcon) {
285
+ if (!foundBreak && maxLength - currentDisplayWidth > -3 && !startsWithIcon) {
280
286
  return text;
281
287
  }
282
288
  // Special case: if we're starting with an icon and breaking very early,
283
289
  // try to keep at least the icon and 1-2 more characters
284
290
  if (startsWithIcon && breakCharIndex <= 2 && maxLength >= 3) {
285
- // Find a better break point after at least 3 characters total
286
- for (let i = 2; i < Math.min(chars.length, maxLength); i++) {
287
- breakCharIndex = i;
288
- foundBreak = true;
289
- break;
291
+ // Find a better break point after at least 3 characters total (by display width)
292
+ let testWidth = 0;
293
+ for (let i = 0; i < chars.length; i++) {
294
+ const char = chars[i];
295
+ if (!char)
296
+ break;
297
+ testWidth += getStringDisplayWidth(char);
298
+ if (testWidth >= 3 || testWidth >= maxLength) {
299
+ breakCharIndex = i;
300
+ foundBreak = true;
301
+ break;
302
+ }
290
303
  }
291
304
  }
292
305
  // Build the strings using character indices
@@ -312,7 +325,9 @@ function applySoftWrap(text, maxLength) {
312
325
  * with the model name and context usage as a unit
313
326
  */
314
327
  function applySoftWrapToModelString(text, maxLength) {
315
- if (text.length <= maxLength) {
328
+ // Use display width for accurate measurement
329
+ const displayWidth = getStringDisplayWidth(text);
330
+ if (displayWidth <= maxLength) {
316
331
  return text;
317
332
  }
318
333
  const chars = Array.from(text);
@@ -340,9 +355,10 @@ function applySoftWrapToModelString(text, maxLength) {
340
355
  // Find the space before context usage
341
356
  const spaceBeforeContext = chars.findIndex((c, i) => i < contextIconIndex && c === ' ');
342
357
  if (spaceBeforeContext > 0) {
343
- // Check if we can fit model + icon + percentage on second line
358
+ // Check if we can fit model + icon + percentage on second line (using display width)
344
359
  const contextPart = chars.slice(spaceBeforeContext + 1).join('');
345
- if (contextPart.length <= maxLength) {
360
+ const contextPartWidth = getStringDisplayWidth(contextPart);
361
+ if (contextPartWidth <= maxLength) {
346
362
  // Put model name on first line, context on second line
347
363
  const modelPart = chars.slice(0, spaceBeforeContext).join('');
348
364
  return `${modelPart}\n${contextPart}`;
@@ -354,9 +370,10 @@ function applySoftWrapToModelString(text, maxLength) {
354
370
  if (spacePositions.length > 0) {
355
371
  const firstSpaceAfterModel = spacePositions[0];
356
372
  if (firstSpaceAfterModel !== undefined && firstSpaceAfterModel > 1) {
357
- // Check if the model part fits
373
+ // Check if the model part fits (using display width)
358
374
  const modelPart = chars.slice(0, firstSpaceAfterModel).join('');
359
- if (modelPart.length <= maxLength) {
375
+ const modelPartWidth = getStringDisplayWidth(modelPart);
376
+ if (modelPartWidth <= maxLength) {
360
377
  const remainingPart = chars.slice(firstSpaceAfterModel + 1).join('');
361
378
  if (remainingPart) {
362
379
  return `${modelPart}\n${remainingPart}`;
@@ -365,23 +382,25 @@ function applySoftWrapToModelString(text, maxLength) {
365
382
  }
366
383
  }
367
384
  }
368
- // Strategy 3: Try to break at a reasonable point that doesn't split the model icon
369
- // We want to keep at least the icon + first few characters together
370
- const minKeepLength = Math.min(5, maxLength); // Keep at least 5 chars or maxLength
371
- if (minKeepLength >= 3 && chars.length > minKeepLength) {
372
- // Find a character break point after the minimum keep length
373
- const breakPoint = Math.min(maxLength, chars.length - 1);
374
- const firstPart = chars.slice(0, breakPoint).join('');
375
- const secondPart = chars.slice(breakPoint).join('');
376
- if (secondPart) {
377
- return `${firstPart}\n${secondPart}`;
385
+ // Strategy 3: Find break point by display width (not character count)
386
+ // This is the critical fix for the garbled output artifacts
387
+ let currentWidth = 0;
388
+ let breakCharIndex = chars.length;
389
+ for (let i = 0; i < chars.length; i++) {
390
+ const char = chars[i];
391
+ if (!char)
392
+ break;
393
+ const charWidth = getStringDisplayWidth(char);
394
+ if (currentWidth + charWidth > maxLength) {
395
+ breakCharIndex = i;
396
+ break;
378
397
  }
379
- return firstPart;
398
+ currentWidth += charWidth;
380
399
  }
381
- // Last resort: simple character-based truncation
382
- if (maxLength >= 3) {
383
- const firstPart = chars.slice(0, maxLength).join('');
384
- const secondPart = chars.slice(maxLength).join('');
400
+ // If we found a valid break point
401
+ if (breakCharIndex < chars.length && breakCharIndex > 0) {
402
+ const firstPart = chars.slice(0, breakCharIndex).join('');
403
+ const secondPart = chars.slice(breakCharIndex).join('');
385
404
  if (secondPart) {
386
405
  return `${firstPart}\n${secondPart}`;
387
406
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\n/**\n * Claude Statusline - TypeScript v2.0\n * Main entry point\n */\n\nimport { readFileSync } from 'fs';\nimport { loadConfig, Config } from './core/config.js';\nimport { validateInput, validateDirectory } from './core/security.js';\nimport { Cache } from './core/cache.js';\nimport { GitOperations } from './git/status.js';\nimport { detectSymbols, getEnvironmentSymbols, SymbolSet } from './ui/symbols.js';\nimport { getTerminalWidth, truncateText, smartTruncate, debugWidthDetection } from './ui/width.js';\nimport { EnvironmentDetector, EnvironmentFormatter } from './env/context.js';\n\n/**\n * Claude Code input interface\n */\ninterface ClaudeInput {\n workspace: {\n current_dir: string;\n };\n model: {\n display_name: string;\n };\n context_window?: {\n total_input_tokens: number;\n total_output_tokens: number;\n context_window_size: number;\n current_usage: {\n input_tokens: number;\n output_tokens: number;\n cache_creation_input_tokens: number;\n cache_read_input_tokens: number;\n };\n };\n}\n\n/**\n * Main execution function\n */\nexport async function main(): Promise<void> {\n try {\n // Load configuration\n const config = loadConfig();\n\n // Initialize components\n const cache = new Cache(config);\n const gitOps = new GitOperations(config, cache);\n const envDetector = new EnvironmentDetector(config, cache);\n\n // Debug width detection if enabled\n await debugWidthDetection(config);\n\n // Read and validate input from stdin\n const input = await readInput();\n if (!validateInput(JSON.stringify(input), config)) {\n console.error('[ERROR] Invalid input received');\n process.exit(1);\n }\n\n // Extract information from input\n const { fullDir, modelName, contextWindow } = extractInputInfo(input);\n if (!fullDir || !modelName) {\n console.error('[ERROR] Failed to extract required information from input');\n process.exit(1);\n }\n\n // Validate directory\n const isValidDir = await validateDirectory(fullDir);\n if (!isValidDir) {\n console.error('[ERROR] Invalid or inaccessible directory:', fullDir);\n process.exit(1);\n }\n\n // Get components (run in parallel for better performance)\n const operations: Promise<any>[] = [\n gitOps.getGitInfo(fullDir),\n envDetector.getEnvironmentInfo(),\n detectSymbols(config),\n ];\n\n // Only get terminal width if smart truncation is enabled\n let terminalWidth: number | undefined;\n if (config.truncate) {\n operations.push(getTerminalWidth(config));\n }\n\n const results = await Promise.all(operations);\n const [gitInfo, envInfo, symbols] = results;\n\n // Extract terminal width from results if it was requested\n if (config.truncate && results.length > 3) {\n terminalWidth = results[3];\n }\n\n // Build statusline\n const statusline = await buildStatusline({\n fullDir,\n modelName,\n contextWindow,\n gitInfo,\n envInfo,\n symbols,\n ...(terminalWidth && { terminalWidth }), // Only include if defined\n config,\n gitOps,\n });\n\n // Output result\n process.stdout.write(statusline);\n\n } catch (error) {\n console.error('[ERROR]', error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n}\n\n/**\n * Read JSON input from stdin\n */\nasync function readInput(): Promise<ClaudeInput> {\n try {\n const input = readFileSync(0, 'utf-8'); // Read from stdin (fd 0)\n const parsed = JSON.parse(input.trim());\n return parsed as ClaudeInput;\n } catch (error) {\n throw new Error(`Failed to read or parse input: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\n/**\n * Extract directory and model name from Claude input\n */\nfunction extractInputInfo(input: ClaudeInput): { fullDir: string; modelName: string; contextWindow?: ClaudeInput['context_window'] } {\n const fullDir = input.workspace?.current_dir || '';\n const modelName = input.model?.display_name || 'Unknown';\n const contextWindow = input.context_window;\n\n return { fullDir, modelName, contextWindow };\n}\n\n/**\n * Build the complete statusline string\n */\nasync function buildStatusline(params: {\n fullDir: string;\n modelName: string;\n contextWindow?: ClaudeInput['context_window'];\n gitInfo: any;\n envInfo: any;\n symbols: SymbolSet;\n terminalWidth?: number; // Optional - only needed for smart truncation\n config: Config;\n gitOps: GitOperations;\n}): Promise<string> {\n const { fullDir, modelName, contextWindow, gitInfo, envInfo, symbols, terminalWidth, config, gitOps } = params;\n\n // Get project name\n const projectName = fullDir.split('/').pop() || fullDir.split('\\\\').pop() || 'project';\n\n // Build git status string\n let gitStatus = '';\n if (gitInfo) {\n gitStatus = gitOps.formatGitStatus(gitInfo, symbols);\n }\n\n // Build environment context string\n let envContext = '';\n if (envInfo) {\n const envSymbols = getEnvironmentSymbols(symbols);\n const envFormatter = new EnvironmentFormatter(envSymbols);\n envContext = ` ${envFormatter.formatWithIcons(envInfo)}`;\n }\n\n // Build context window usage string\n let contextUsage = '';\n if (contextWindow && !config.noContextWindow) {\n const percentage = calculateContextWindowPercentage(contextWindow);\n if (percentage !== null) {\n contextUsage = ` ${symbols.contextWindow}${percentage}%`;\n }\n }\n\n // Build model string\n const modelString = `${symbols.model}${modelName}${envContext}${contextUsage}`;\n\n // Initial statusline\n let statusline = `${projectName}${gitStatus} ${modelString}`;\n\n // Apply smart truncation if enabled\n if (config.truncate) {\n if (!terminalWidth) {\n console.error('[ERROR] Smart truncation enabled but terminal width not available');\n process.exit(1);\n }\n statusline = applySmartTruncation({\n statusline,\n projectName,\n gitStatus,\n modelString,\n terminalWidth,\n config,\n symbols,\n });\n }\n // No basic truncation - let terminal handle overflow\n\n return statusline;\n}\n\n/**\n * Calculate context window usage percentage\n */\nfunction calculateContextWindowPercentage(contextWindow: NonNullable<ClaudeInput['context_window']>): number | null {\n try {\n const { current_usage, context_window_size } = contextWindow;\n\n if (!context_window_size || context_window_size === 0) {\n return null;\n }\n\n // If current_usage is null or undefined, we cannot calculate the percentage\n // This matches the official Claude Code documentation behavior\n if (!current_usage) {\n return 0;\n }\n\n // Calculate total tokens used (input + cache tokens from current_usage)\n // Note: Output tokens are NOT included in context window calculation\n // per Claude Code documentation\n const totalUsed = current_usage.input_tokens +\n (current_usage.cache_creation_input_tokens || 0) +\n (current_usage.cache_read_input_tokens || 0);\n\n // Calculate percentage\n const percentage = Math.round((totalUsed / context_window_size) * 100);\n\n // Cap at 100% and ensure non-negative\n return Math.max(0, Math.min(100, percentage));\n } catch {\n return 0;\n }\n}\n\n/**\n * Apply smart truncation with branch prioritization\n */\nfunction applySmartTruncation(params: {\n statusline: string;\n projectName: string;\n gitStatus: string;\n modelString: string;\n terminalWidth: number;\n config: Config;\n symbols: SymbolSet;\n}): string {\n const { statusline, projectName, gitStatus, modelString, terminalWidth, config } = params;\n\n // Use 15-char margin for Claude telemetry compatibility\n const maxLen = Math.max(terminalWidth - config.rightMargin, 30);\n const projectGit = `${projectName}${gitStatus}`;\n\n // Check if everything fits\n if (statusline.length <= maxLen) {\n return statusline;\n }\n\n // Check if project + space fits, truncate model part only\n if (projectGit.length + 1 <= maxLen) {\n // Smart truncation with soft-wrapping (default behavior)\n // Allow disabling soft-wrapping with config setting\n if (config.noSoftWrap) {\n // Legacy behavior: simple truncation only\n const modelMaxLen = maxLen - projectGit.length - 1;\n const truncatedModel = truncateText(modelString, modelMaxLen);\n return `${projectGit} ${truncatedModel}`;\n } else {\n // Default: soft-wrap model part\n const modelMaxLen = maxLen - projectGit.length - 1;\n\n // If model string needs wrapping and it starts with model icon,\n // prefer wrapping the entire model string to next line\n const modelIconPattern = /^[󰚩*]/;\n if (modelIconPattern.test(modelString) && modelString.length > modelMaxLen) {\n // Wrap entire model to next line\n return `${projectGit}\\n${modelString}`;\n }\n\n const wrappedModel = applySoftWrap(modelString, modelMaxLen);\n return `${projectGit} ${wrappedModel}`;\n }\n }\n\n // Smart truncation of project+git part\n const truncated = smartTruncate(projectName, gitStatus, maxLen, config);\n if (truncated) {\n return truncated;\n }\n\n // Basic fallback\n return truncateText(statusline, maxLen);\n}\n\n/**\n * Apply soft wrapping to text\n */\nfunction applySoftWrap(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n\n // Check if this is a model string (starts with model icon)\n const modelIconPattern = /^[󰚩*]/; // Nerd Font or ASCII model icon\n if (modelIconPattern.test(text)) {\n return applySoftWrapToModelString(text, maxLength);\n }\n\n // Find a good break point\n let foundBreak = false;\n\n // Work with actual Unicode characters to avoid splitting multi-byte sequences\n const chars = Array.from(text); // This splits by actual Unicode characters\n let charCount = 0;\n let breakCharIndex = chars.length; // Default to no break\n let lastSpaceIndex = -1;\n\n // Find the best break point by character count\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n\n // Track spaces for potential break points\n if (char === ' ') {\n lastSpaceIndex = i;\n }\n\n // Estimate display width (this is approximate)\n // Most Unicode icons count as 1 display character\n // ASCII characters count as 1\n charCount++;\n\n // Check if we've exceeded the max length\n if (charCount > maxLength) {\n // If we found a space before this point, use it\n if (lastSpaceIndex >= 0) {\n breakCharIndex = lastSpaceIndex;\n } else {\n // No space found, break before current character\n breakCharIndex = i;\n }\n foundBreak = lastSpaceIndex >= 0;\n break;\n }\n }\n\n // If everything fits, return as is\n if (breakCharIndex >= chars.length) {\n return text;\n }\n\n // If no safe break found and we're very close to max_length, just fit without wrapping\n // But only if we're not dealing with a model string that starts with an icon\n const firstChar = chars.length > 0 ? chars[0] : '';\n const startsWithIcon = firstChar && firstChar !== ' ' && Buffer.byteLength(firstChar, 'utf8') > 1;\n if (!foundBreak && maxLength - charCount > -3 && !startsWithIcon) {\n return text;\n }\n\n // Special case: if we're starting with an icon and breaking very early,\n // try to keep at least the icon and 1-2 more characters\n if (startsWithIcon && breakCharIndex <= 2 && maxLength >= 3) {\n // Find a better break point after at least 3 characters total\n for (let i = 2; i < Math.min(chars.length, maxLength); i++) {\n breakCharIndex = i;\n foundBreak = true;\n break;\n }\n }\n\n // Build the strings using character indices\n const firstChars = chars.slice(0, breakCharIndex);\n const secondChars = chars.slice(breakCharIndex);\n\n // Remove leading space from second line if we broke at space\n if (secondChars.length > 0 && secondChars[0] === ' ') {\n secondChars.shift();\n }\n\n // Join characters back into strings\n const firstLine = firstChars.join('');\n const secondLine = secondChars.join('');\n\n // Only wrap if second line has meaningful content\n if (secondLine) {\n return `${firstLine}\\n${secondLine}`;\n } else {\n return firstLine;\n }\n}\n\n/**\n * Apply soft wrapping specifically to model strings, keeping the model icon\n * with the model name and context usage as a unit\n */\nfunction applySoftWrapToModelString(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n\n const chars = Array.from(text);\n\n // Find all space positions to understand the structure\n const spacePositions: number[] = [];\n for (let i = 0; i < chars.length; i++) {\n if (chars[i] === ' ') {\n spacePositions.push(i);\n }\n }\n\n // If we have context usage (marked by context window icon like ⚡︎ or #)\n // The structure is: [icon][model] [env] [context icon][percentage]\n // We want to prefer keeping: [icon][model] [context icon][percentage] together if possible\n\n let contextIconIndex = -1;\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n if (char === '⚡' || char === '#') {\n contextIconIndex = i;\n break;\n }\n }\n\n // Strategy 1: If we have context usage, try to keep it with the model on the second line\n // This ensures the icon and percentage stay together\n if (contextIconIndex > 0) {\n // Find the space before context usage\n const spaceBeforeContext = chars.findIndex((c, i) => i < contextIconIndex && c === ' ');\n\n if (spaceBeforeContext > 0) {\n // Check if we can fit model + icon + percentage on second line\n const contextPart = chars.slice(spaceBeforeContext + 1).join('');\n if (contextPart.length <= maxLength) {\n // Put model name on first line, context on second line\n const modelPart = chars.slice(0, spaceBeforeContext).join('');\n return `${modelPart}\\n${contextPart}`;\n }\n }\n }\n\n // Strategy 2: Try to find a break point that keeps the model icon with model name\n // Look for first space after model name (if no context or if context doesn't fit)\n if (spacePositions.length > 0) {\n const firstSpaceAfterModel = spacePositions[0];\n if (firstSpaceAfterModel !== undefined && firstSpaceAfterModel > 1) {\n // Check if the model part fits\n const modelPart = chars.slice(0, firstSpaceAfterModel).join('');\n if (modelPart.length <= maxLength) {\n const remainingPart = chars.slice(firstSpaceAfterModel + 1).join('');\n if (remainingPart) {\n return `${modelPart}\\n${remainingPart}`;\n }\n return modelPart;\n }\n }\n }\n\n // Strategy 3: Try to break at a reasonable point that doesn't split the model icon\n // We want to keep at least the icon + first few characters together\n const minKeepLength = Math.min(5, maxLength); // Keep at least 5 chars or maxLength\n if (minKeepLength >= 3 && chars.length > minKeepLength) {\n // Find a character break point after the minimum keep length\n const breakPoint = Math.min(maxLength, chars.length - 1);\n const firstPart = chars.slice(0, breakPoint).join('');\n const secondPart = chars.slice(breakPoint).join('');\n if (secondPart) {\n return `${firstPart}\\n${secondPart}`;\n }\n return firstPart;\n }\n\n // Last resort: simple character-based truncation\n if (maxLength >= 3) {\n const firstPart = chars.slice(0, maxLength).join('');\n const secondPart = chars.slice(maxLength).join('');\n if (secondPart) {\n return `${firstPart}\\n${secondPart}`;\n }\n return firstPart;\n }\n\n return text;\n}\n\n// Run main function if this file is executed directly\nif (import.meta.url === `file://${process.argv[1]}`) {\n main();\n}"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\n/**\n * Claude Statusline - TypeScript v2.0\n * Main entry point\n */\n\nimport { readFileSync } from 'fs';\nimport { loadConfig, Config } from './core/config.js';\nimport { validateInput, validateDirectory } from './core/security.js';\nimport { Cache } from './core/cache.js';\nimport { GitOperations } from './git/status.js';\nimport { detectSymbols, getEnvironmentSymbols, SymbolSet } from './ui/symbols.js';\nimport { getTerminalWidth, truncateText, smartTruncate, debugWidthDetection, getStringDisplayWidth } from './ui/width.js';\nimport { EnvironmentDetector, EnvironmentFormatter } from './env/context.js';\n\n/**\n * Claude Code input interface\n */\ninterface ClaudeInput {\n workspace: {\n current_dir: string;\n };\n model: {\n display_name: string;\n };\n context_window?: {\n total_input_tokens: number;\n total_output_tokens: number;\n context_window_size: number;\n current_usage: {\n input_tokens: number;\n output_tokens: number;\n cache_creation_input_tokens: number;\n cache_read_input_tokens: number;\n };\n };\n}\n\n/**\n * Main execution function\n */\nexport async function main(): Promise<void> {\n try {\n // Load configuration\n const config = loadConfig();\n\n // Initialize components\n const cache = new Cache(config);\n const gitOps = new GitOperations(config, cache);\n const envDetector = new EnvironmentDetector(config, cache);\n\n // Debug width detection if enabled\n await debugWidthDetection(config);\n\n // Read and validate input from stdin\n const input = await readInput();\n if (!validateInput(JSON.stringify(input), config)) {\n console.error('[ERROR] Invalid input received');\n process.exit(1);\n }\n\n // Extract information from input\n const { fullDir, modelName, contextWindow } = extractInputInfo(input);\n if (!fullDir || !modelName) {\n console.error('[ERROR] Failed to extract required information from input');\n process.exit(1);\n }\n\n // Validate directory\n const isValidDir = await validateDirectory(fullDir);\n if (!isValidDir) {\n console.error('[ERROR] Invalid or inaccessible directory:', fullDir);\n process.exit(1);\n }\n\n // Get components (run in parallel for better performance)\n const operations: Promise<any>[] = [\n gitOps.getGitInfo(fullDir),\n envDetector.getEnvironmentInfo(),\n detectSymbols(config),\n ];\n\n // Only get terminal width if smart truncation is enabled\n let terminalWidth: number | undefined;\n if (config.truncate) {\n operations.push(getTerminalWidth(config));\n }\n\n const results = await Promise.all(operations);\n const [gitInfo, envInfo, symbols] = results;\n\n // Extract terminal width from results if it was requested\n if (config.truncate && results.length > 3) {\n terminalWidth = results[3];\n }\n\n // Build statusline\n const statusline = await buildStatusline({\n fullDir,\n modelName,\n contextWindow,\n gitInfo,\n envInfo,\n symbols,\n ...(terminalWidth && { terminalWidth }), // Only include if defined\n config,\n gitOps,\n });\n\n // Output result\n process.stdout.write(statusline);\n\n } catch (error) {\n console.error('[ERROR]', error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n}\n\n/**\n * Read JSON input from stdin\n */\nasync function readInput(): Promise<ClaudeInput> {\n try {\n const input = readFileSync(0, 'utf-8'); // Read from stdin (fd 0)\n const parsed = JSON.parse(input.trim());\n return parsed as ClaudeInput;\n } catch (error) {\n throw new Error(`Failed to read or parse input: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\n/**\n * Extract directory and model name from Claude input\n */\nfunction extractInputInfo(input: ClaudeInput): { fullDir: string; modelName: string; contextWindow?: ClaudeInput['context_window'] } {\n const fullDir = input.workspace?.current_dir || '';\n const modelName = input.model?.display_name || 'Unknown';\n const contextWindow = input.context_window;\n\n return { fullDir, modelName, contextWindow };\n}\n\n/**\n * Build the complete statusline string\n */\nasync function buildStatusline(params: {\n fullDir: string;\n modelName: string;\n contextWindow?: ClaudeInput['context_window'];\n gitInfo: any;\n envInfo: any;\n symbols: SymbolSet;\n terminalWidth?: number; // Optional - only needed for smart truncation\n config: Config;\n gitOps: GitOperations;\n}): Promise<string> {\n const { fullDir, modelName, contextWindow, gitInfo, envInfo, symbols, terminalWidth, config, gitOps } = params;\n\n // Get project name\n const projectName = fullDir.split('/').pop() || fullDir.split('\\\\').pop() || 'project';\n\n // Build git status string\n let gitStatus = '';\n if (gitInfo) {\n gitStatus = gitOps.formatGitStatus(gitInfo, symbols);\n }\n\n // Build environment context string\n let envContext = '';\n if (envInfo) {\n const envSymbols = getEnvironmentSymbols(symbols);\n const envFormatter = new EnvironmentFormatter(envSymbols);\n envContext = ` ${envFormatter.formatWithIcons(envInfo)}`;\n }\n\n // Build context window usage string\n let contextUsage = '';\n if (contextWindow && !config.noContextWindow) {\n const percentage = calculateContextWindowPercentage(contextWindow);\n if (percentage !== null) {\n contextUsage = ` ${symbols.contextWindow}${percentage}%`;\n }\n }\n\n // Build model string\n const modelString = `${symbols.model}${modelName}${envContext}${contextUsage}`;\n\n // Initial statusline\n let statusline = `${projectName}${gitStatus} ${modelString}`;\n\n // Apply smart truncation if enabled\n if (config.truncate) {\n if (!terminalWidth) {\n console.error('[ERROR] Smart truncation enabled but terminal width not available');\n process.exit(1);\n }\n statusline = applySmartTruncation({\n statusline,\n projectName,\n gitStatus,\n modelString,\n terminalWidth,\n config,\n symbols,\n });\n }\n // No basic truncation - let terminal handle overflow\n\n return statusline;\n}\n\n/**\n * Calculate context window usage percentage\n */\nfunction calculateContextWindowPercentage(contextWindow: NonNullable<ClaudeInput['context_window']>): number | null {\n try {\n const { current_usage, context_window_size } = contextWindow;\n\n if (!context_window_size || context_window_size === 0) {\n return null;\n }\n\n // If current_usage is null or undefined, we cannot calculate the percentage\n // This matches the official Claude Code documentation behavior\n if (!current_usage) {\n return 0;\n }\n\n // Calculate total tokens used (input + cache tokens from current_usage)\n // Note: Output tokens are NOT included in context window calculation\n // per Claude Code documentation\n const totalUsed = current_usage.input_tokens +\n (current_usage.cache_creation_input_tokens || 0) +\n (current_usage.cache_read_input_tokens || 0);\n\n // Calculate percentage\n const percentage = Math.round((totalUsed / context_window_size) * 100);\n\n // Cap at 100% and ensure non-negative\n return Math.max(0, Math.min(100, percentage));\n } catch {\n return 0;\n }\n}\n\n/**\n * Apply smart truncation with branch prioritization\n */\nfunction applySmartTruncation(params: {\n statusline: string;\n projectName: string;\n gitStatus: string;\n modelString: string;\n terminalWidth: number;\n config: Config;\n symbols: SymbolSet;\n}): string {\n const { statusline, projectName, gitStatus, modelString, terminalWidth, config } = params;\n\n // Use 15-char margin for Claude telemetry compatibility\n const maxLen = Math.max(terminalWidth - config.rightMargin, 30);\n const projectGit = `${projectName}${gitStatus}`;\n\n // Check if everything fits (using display width for accuracy)\n const statuslineDisplayWidth = getStringDisplayWidth(statusline);\n if (statuslineDisplayWidth <= maxLen) {\n return statusline;\n }\n\n // Check if project + space fits, truncate model part only (using display width)\n const projectGitDisplayWidth = getStringDisplayWidth(projectGit);\n if (projectGitDisplayWidth + 1 <= maxLen) {\n // Smart truncation with soft-wrapping (default behavior)\n // Allow disabling soft-wrapping with config setting\n if (config.noSoftWrap) {\n // Legacy behavior: simple truncation only\n const modelMaxLen = maxLen - projectGitDisplayWidth - 1;\n const truncatedModel = truncateText(modelString, modelMaxLen);\n return `${projectGit} ${truncatedModel}`;\n } else {\n // Default: soft-wrap model part\n const modelMaxLen = maxLen - projectGitDisplayWidth - 1;\n\n // If model string needs wrapping and it starts with model icon,\n // prefer wrapping the entire model string to next line\n const modelIconPattern = /^[󰚩*]/;\n const modelDisplayWidth = getStringDisplayWidth(modelString);\n if (modelIconPattern.test(modelString) && modelDisplayWidth > modelMaxLen) {\n // Wrap entire model to next line\n return `${projectGit}\\n${modelString}`;\n }\n\n const wrappedModel = applySoftWrap(modelString, modelMaxLen);\n return `${projectGit} ${wrappedModel}`;\n }\n }\n\n // Smart truncation of project+git part\n const truncated = smartTruncate(projectName, gitStatus, maxLen, config);\n if (truncated) {\n return truncated;\n }\n\n // Basic fallback\n return truncateText(statusline, maxLen);\n}\n\n/**\n * Apply soft wrapping to text\n */\nfunction applySoftWrap(text: string, maxLength: number): string {\n // Use display width for accurate measurement\n const displayWidth = getStringDisplayWidth(text);\n if (displayWidth <= maxLength) {\n return text;\n }\n\n // Check if this is a model string (starts with model icon)\n const modelIconPattern = /^[󰚩*]/; // Nerd Font or ASCII model icon\n if (modelIconPattern.test(text)) {\n return applySoftWrapToModelString(text, maxLength);\n }\n\n // Find a good break point using display width\n let foundBreak = false;\n\n // Work with actual Unicode characters to avoid splitting multi-byte sequences\n const chars = Array.from(text); // This splits by actual Unicode characters\n let currentDisplayWidth = 0;\n let breakCharIndex = chars.length; // Default to no break\n let lastSpaceIndex = -1;\n\n // Find the best break point by display width\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n if (!char) break;\n\n // Track spaces for potential break points\n if (char === ' ') {\n lastSpaceIndex = i;\n }\n\n // Calculate display width for this character\n const charWidth = getStringDisplayWidth(char);\n currentDisplayWidth += charWidth;\n\n // Check if we've exceeded the max length\n if (currentDisplayWidth > maxLength) {\n // If we found a space before this point, use it\n if (lastSpaceIndex >= 0) {\n breakCharIndex = lastSpaceIndex;\n } else {\n // No space found, break before current character\n breakCharIndex = i;\n }\n foundBreak = lastSpaceIndex >= 0;\n break;\n }\n }\n\n // If everything fits, return as is\n if (breakCharIndex >= chars.length) {\n return text;\n }\n\n // If no safe break found and we're very close to max_length, just fit without wrapping\n // But only if we're not dealing with a model string that starts with an icon\n const firstChar = chars.length > 0 ? chars[0] : '';\n const startsWithIcon = firstChar && firstChar !== ' ' && Buffer.byteLength(firstChar, 'utf8') > 1;\n if (!foundBreak && maxLength - currentDisplayWidth > -3 && !startsWithIcon) {\n return text;\n }\n\n // Special case: if we're starting with an icon and breaking very early,\n // try to keep at least the icon and 1-2 more characters\n if (startsWithIcon && breakCharIndex <= 2 && maxLength >= 3) {\n // Find a better break point after at least 3 characters total (by display width)\n let testWidth = 0;\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n if (!char) break;\n testWidth += getStringDisplayWidth(char);\n if (testWidth >= 3 || testWidth >= maxLength) {\n breakCharIndex = i;\n foundBreak = true;\n break;\n }\n }\n }\n\n // Build the strings using character indices\n const firstChars = chars.slice(0, breakCharIndex);\n const secondChars = chars.slice(breakCharIndex);\n\n // Remove leading space from second line if we broke at space\n if (secondChars.length > 0 && secondChars[0] === ' ') {\n secondChars.shift();\n }\n\n // Join characters back into strings\n const firstLine = firstChars.join('');\n const secondLine = secondChars.join('');\n\n // Only wrap if second line has meaningful content\n if (secondLine) {\n return `${firstLine}\\n${secondLine}`;\n } else {\n return firstLine;\n }\n}\n\n/**\n * Apply soft wrapping specifically to model strings, keeping the model icon\n * with the model name and context usage as a unit\n */\nfunction applySoftWrapToModelString(text: string, maxLength: number): string {\n // Use display width for accurate measurement\n const displayWidth = getStringDisplayWidth(text);\n if (displayWidth <= maxLength) {\n return text;\n }\n\n const chars = Array.from(text);\n\n // Find all space positions to understand the structure\n const spacePositions: number[] = [];\n for (let i = 0; i < chars.length; i++) {\n if (chars[i] === ' ') {\n spacePositions.push(i);\n }\n }\n\n // If we have context usage (marked by context window icon like ⚡︎ or #)\n // The structure is: [icon][model] [env] [context icon][percentage]\n // We want to prefer keeping: [icon][model] [context icon][percentage] together if possible\n\n let contextIconIndex = -1;\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n if (char === '⚡' || char === '#') {\n contextIconIndex = i;\n break;\n }\n }\n\n // Strategy 1: If we have context usage, try to keep it with the model on the second line\n // This ensures the icon and percentage stay together\n if (contextIconIndex > 0) {\n // Find the space before context usage\n const spaceBeforeContext = chars.findIndex((c, i) => i < contextIconIndex && c === ' ');\n\n if (spaceBeforeContext > 0) {\n // Check if we can fit model + icon + percentage on second line (using display width)\n const contextPart = chars.slice(spaceBeforeContext + 1).join('');\n const contextPartWidth = getStringDisplayWidth(contextPart);\n if (contextPartWidth <= maxLength) {\n // Put model name on first line, context on second line\n const modelPart = chars.slice(0, spaceBeforeContext).join('');\n return `${modelPart}\\n${contextPart}`;\n }\n }\n }\n\n // Strategy 2: Try to find a break point that keeps the model icon with model name\n // Look for first space after model name (if no context or if context doesn't fit)\n if (spacePositions.length > 0) {\n const firstSpaceAfterModel = spacePositions[0];\n if (firstSpaceAfterModel !== undefined && firstSpaceAfterModel > 1) {\n // Check if the model part fits (using display width)\n const modelPart = chars.slice(0, firstSpaceAfterModel).join('');\n const modelPartWidth = getStringDisplayWidth(modelPart);\n if (modelPartWidth <= maxLength) {\n const remainingPart = chars.slice(firstSpaceAfterModel + 1).join('');\n if (remainingPart) {\n return `${modelPart}\\n${remainingPart}`;\n }\n return modelPart;\n }\n }\n }\n\n // Strategy 3: Find break point by display width (not character count)\n // This is the critical fix for the garbled output artifacts\n let currentWidth = 0;\n let breakCharIndex = chars.length;\n\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n if (!char) break;\n const charWidth = getStringDisplayWidth(char);\n if (currentWidth + charWidth > maxLength) {\n breakCharIndex = i;\n break;\n }\n currentWidth += charWidth;\n }\n\n // If we found a valid break point\n if (breakCharIndex < chars.length && breakCharIndex > 0) {\n const firstPart = chars.slice(0, breakCharIndex).join('');\n const secondPart = chars.slice(breakCharIndex).join('');\n if (secondPart) {\n return `${firstPart}\\n${secondPart}`;\n }\n return firstPart;\n }\n\n return text;\n}\n\n// Run main function if this file is executed directly\nif (import.meta.url === `file://${process.argv[1]}`) {\n main();\n}"]}
@@ -177,7 +177,7 @@
177
177
  "format": "esm"
178
178
  },
179
179
  "src/ui/width.ts": {
180
- "bytes": 8824,
180
+ "bytes": 11061,
181
181
  "imports": [
182
182
  {
183
183
  "path": "../core/config.js",
@@ -239,7 +239,7 @@
239
239
  "format": "esm"
240
240
  },
241
241
  "src/index.ts": {
242
- "bytes": 15562,
242
+ "bytes": 16333,
243
243
  "imports": [
244
244
  {
245
245
  "path": "fs",
@@ -440,10 +440,10 @@
440
440
  "entryPoint": "src/index.ts",
441
441
  "inputs": {
442
442
  "src/index.ts": {
443
- "bytesInOutput": 3413
443
+ "bytesInOutput": 3444
444
444
  },
445
445
  "src/core/config.ts": {
446
- "bytesInOutput": 2737
446
+ "bytesInOutput": 2741
447
447
  },
448
448
  "src/core/security.ts": {
449
449
  "bytesInOutput": 1027
@@ -461,13 +461,13 @@
461
461
  "bytesInOutput": 3618
462
462
  },
463
463
  "src/ui/width.ts": {
464
- "bytesInOutput": 1755
464
+ "bytesInOutput": 2274
465
465
  },
466
466
  "src/env/context.ts": {
467
467
  "bytesInOutput": 3027
468
468
  }
469
469
  },
470
- "bytes": 21349
470
+ "bytes": 21903
471
471
  }
472
472
  }
473
473
  }
@@ -23,6 +23,11 @@ export declare function truncateText(text: string, maxLength: number): string;
23
23
  * Smart truncation with branch prioritization (matches bash implementation)
24
24
  */
25
25
  export declare function smartTruncate(project: string, gitInfo: string, maxLen: number, _config: Config): string;
26
+ /**
27
+ * Get the display width of a string, accounting for wide characters (CJK, emoji, Nerd Font icons)
28
+ * Uses wcwidth-style logic where wide characters = 2 columns, narrow = 1 column
29
+ */
30
+ export declare function getStringDisplayWidth(str: string): number;
26
31
  /**
27
32
  * Soft wrapping function (experimental)
28
33
  */
package/dist/ui/width.js CHANGED
@@ -202,6 +202,54 @@ export function smartTruncate(project, gitInfo, maxLen, _config) {
202
202
  // Step 4: Basic fallback
203
203
  return `${project.substring(0, maxLen)}..`;
204
204
  }
205
+ /**
206
+ * Get the display width of a string, accounting for wide characters (CJK, emoji, Nerd Font icons)
207
+ * Uses wcwidth-style logic where wide characters = 2 columns, narrow = 1 column
208
+ */
209
+ export function getStringDisplayWidth(str) {
210
+ let width = 0;
211
+ for (const char of str) {
212
+ const code = char.codePointAt(0) ?? 0;
213
+ // Wide character ranges (CJK, emoji, Nerd Font icons, etc.)
214
+ // CJK Unified Ideographs
215
+ if (code >= 0x1100 && ((code >= 0x1100 && code <= 0x115F) || // Hangul Jamo
216
+ (code >= 0x2E80 && code <= 0xA4CF) || // CJK和各种符号
217
+ (code >= 0xAC00 && code <= 0xD7A3) || // Hangul Syllables
218
+ (code >= 0xF900 && code <= 0xFAFF) || // CJK Compatibility Ideographs
219
+ (code >= 0xFE10 && code <= 0xFE19) || // Vertical forms
220
+ (code >= 0xFE30 && code <= 0xFE6F) || // CJK Compatibility Forms
221
+ (code >= 0xFF00 && code <= 0xFF60) || // Fullwidth Forms
222
+ (code >= 0xFFE0 && code <= 0xFFE6) ||
223
+ (code >= 0x20000 && code <= 0x2FFFD) ||
224
+ (code >= 0x30000 && code <= 0x3FFFD))) {
225
+ width += 2;
226
+ }
227
+ // Emoji and various symbols (including Nerd Font icons in Private Use Area)
228
+ else if ((code >= 0x1F300 && code <= 0x1F9FF) || // Emoji
229
+ (code >= 0x2600 && code <= 0x27BF) || // Miscellaneous symbols
230
+ (code >= 0xFE00 && code <= 0xFE0F) || // Variation Selectors
231
+ (code >= 0x1F000 && code <= 0x1F02F) || // Mahjong tiles
232
+ (code >= 0xE000 && code <= 0xF8FF) || // Private Use Area (Nerd Font icons)
233
+ (code >= 0xF0000 && code <= 0xFFFFD) || // Supplementary Private Use Area-A
234
+ (code >= 0x100000 && code <= 0x10FFFD) // Supplementary Private Use Area-B
235
+ ) {
236
+ width += 2;
237
+ }
238
+ // Combining characters (zero width)
239
+ else if ((code >= 0x0300 && code <= 0x036F) || // Combining diacritical marks
240
+ (code >= 0x1DC0 && code <= 0x1DFF) || // Combining diacritical marks extended
241
+ (code >= 0x20D0 && code <= 0x20FF) || // Combining marks for symbols
242
+ (code >= 0xFE20 && code <= 0xFE2F) // Combining half marks
243
+ ) {
244
+ // Zero width, don't increment
245
+ }
246
+ // Normal ASCII and narrow characters
247
+ else {
248
+ width += 1;
249
+ }
250
+ }
251
+ return width;
252
+ }
205
253
  /**
206
254
  * Soft wrapping function (experimental)
207
255
  */
@@ -1 +1 @@
1
- {"version":3,"file":"width.js","sourceRoot":"","sources":["../../src/ui/width.ts"],"names":[],"mappings":"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","sourcesContent":["import { Config } from '../core/config.js';\n\n/**\n * Terminal width detection utilities\n * Ported from bash implementation with cross-platform Node.js support\n */\n\n/**\n * Get terminal width using multiple detection methods\n * Ordered from most reliable to fallback methods\n */\nexport async function getTerminalWidth(config: Config): Promise<number> {\n // Method 0: Respect manual width override first (for testing)\n if (config.forceWidth && config.forceWidth > 0) {\n return config.forceWidth;\n }\n\n // Method 1: Try COLUMNS environment variable\n const columnsEnv = process.env.COLUMNS;\n if (columnsEnv) {\n const columns = parseInt(columnsEnv, 10);\n if (!isNaN(columns) && columns > 0) {\n return columns;\n }\n }\n\n // Method 2: Try Node.js process.stdout.columns\n if (process.stdout.columns && process.stdout.columns > 0) {\n return process.stdout.columns;\n }\n\n // Method 3: Try tput command (Unix/Linux/macOS)\n const tputWidth = await tryCommand('tput', ['cols']);\n if (tputWidth) {\n return tputWidth;\n }\n\n // Method 4: Try stty command (Unix/Linux/macOS)\n const sttyWidth = await tryStty();\n if (sttyWidth) {\n return sttyWidth;\n }\n\n // Method 5: Check Claude Code specific environment\n const claudeWidth = process.env.CLAUDE_CODE_TERMINAL_WIDTH;\n if (claudeWidth) {\n const width = parseInt(claudeWidth, 10);\n if (!isNaN(width) && width > 0) {\n return width;\n }\n }\n\n // Method 6: Terminal-specific defaults\n const termProgram = process.env.TERM_PROGRAM;\n const term = process.env.TERM;\n\n if (termProgram === 'vscode' && process.env.VSCODE_PID) {\n return 120; // VS Code default\n }\n\n if (['ghostty', 'wezterm', 'iterm'].includes(termProgram || '')) {\n return 120; // Modern terminals default to wider\n }\n\n if (term && ['alacritty', 'kitty', 'wezterm', 'ghostty', 'xterm-256color'].includes(term)) {\n return 120; // Modern terminals\n }\n\n // Method 7: Check for Windows Terminal\n if (process.env.WT_SESSION || process.env.WT_PROFILE_ID) {\n return 120; // Windows Terminal\n }\n\n // Final fallback: conservative 80-column default\n return 80;\n}\n\n/**\n * Execute a command and parse numeric output\n */\nasync function tryCommand(command: string, args: string[]): Promise<number | null> {\n try {\n const { exec } = await import('child_process');\n const { promisify } = await import('util');\n const execAsync = promisify(exec);\n\n const { stdout } = await execAsync(`${command} ${args.join(' ')}`, {\n timeout: 1000, // 1 second timeout\n encoding: 'utf-8',\n });\n\n const width = parseInt(stdout.trim(), 10);\n if (!isNaN(width) && width > 0) {\n return width;\n }\n } catch {\n // Command failed or not available\n }\n\n return null;\n}\n\n/**\n * Try stty size command\n */\nasync function tryStty(): Promise<number | null> {\n try {\n const { exec } = await import('child_process');\n const { promisify } = await import('util');\n const execAsync = promisify(exec);\n\n const { stdout } = await execAsync('stty size', {\n timeout: 1000,\n encoding: 'utf-8',\n });\n\n // stty size returns: \"rows cols\"\n const parts = stdout.trim().split(' ');\n if (parts.length === 2) {\n const width = parseInt(parts[1] || '0', 10);\n if (!isNaN(width) && width > 0) {\n return width;\n }\n }\n } catch {\n // stty failed or not available\n }\n\n return null;\n}\n\n/**\n * Debug width detection (matches bash implementation)\n */\nexport async function debugWidthDetection(config: Config): Promise<void> {\n if (!config.debugWidth) {\n return;\n }\n\n console.error('[WIDTH DEBUG] Debug mode enabled');\n\n console.error('[WIDTH DEBUG] Methods tried:');\n\n // Test process.stdout.columns\n if (process.stdout.columns) {\n console.error(`[WIDTH DEBUG] process.stdout.columns: ${process.stdout.columns}`);\n } else {\n console.error('[WIDTH DEBUG] process.stdout.columns: not available');\n }\n\n // Test COLUMNS variable\n const columnsEnv = process.env.COLUMNS;\n if (columnsEnv) {\n console.error(`[WIDTH DEBUG] COLUMNS variable: ${columnsEnv}`);\n } else {\n console.error('[WIDTH DEBUG] COLUMNS variable: not set');\n }\n\n // Test tput\n const tputWidth = await tryCommand('tput', ['cols']);\n if (tputWidth) {\n console.error(`[WIDTH DEBUG] tput cols: ${tputWidth}`);\n } else {\n console.error('[WIDTH DEBUG] tput: not available or failed');\n }\n\n // Test stty\n const sttyWidth = await tryStty();\n if (sttyWidth) {\n console.error(`[WIDTH DEBUG] stty size: ${sttyWidth}`);\n } else {\n console.error('[WIDTH DEBUG] stty: not available or failed');\n }\n\n // Test environment variables\n console.error(`[WIDTH DEBUG] CLAUDE_CODE_STATUSLINE_FORCE_WIDTH: ${config.forceWidth || 'not set'}`);\n console.error(`[WIDTH DEBUG] COLUMNS variable: ${columnsEnv || 'not set'}`);\n console.error(`[WIDTH DEBUG] CLAUDE_CODE_TERMINAL_WIDTH: ${process.env.CLAUDE_CODE_TERMINAL_WIDTH || 'not set'}`);\n console.error(`[WIDTH DEBUG] TERM_PROGRAM: ${process.env.TERM_PROGRAM || 'not set'}`);\n console.error(`[WIDTH DEBUG] TERM: ${process.env.TERM || 'not set'}`);\n\n // Show final result\n const finalWidth = await getTerminalWidth(config);\n console.error(`[WIDTH DEBUG] Final detected width: ${finalWidth}`);\n console.error(`[WIDTH DEBUG] Statusline will use: ${finalWidth - config.rightMargin} columns max`);\n}\n\n/**\n * Text truncation utilities\n */\n\n/**\n * Simple text truncation with ellipsis\n */\nexport function truncateText(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n\n // Edge case: if maxLength is too small, return minimal result\n if (maxLength < 4) {\n return '..';\n }\n\n return `${text.substring(0, maxLength - 2)}..`;\n}\n\n/**\n * Smart truncation with branch prioritization (matches bash implementation)\n */\nexport function smartTruncate(\n project: string,\n gitInfo: string,\n maxLen: number,\n _config: Config\n): string {\n // Step 1: Check if everything fits\n if (project.length + gitInfo.length <= maxLen) {\n return '';\n }\n\n // Step 2: Truncate project only (preserve branch)\n const projLen = maxLen - gitInfo.length - 2;\n if (projLen >= 5) {\n return `${project.substring(0, projLen)}..${gitInfo}`;\n }\n\n // Step 3: Truncate project + branch (preserve indicators)\n let indicators = '';\n const bracketMatch = gitInfo.match(/\\[([^\\]]+)\\]/);\n if (bracketMatch) {\n indicators = bracketMatch[1] || '';\n }\n\n const branchLen = maxLen - indicators.length - 8;\n if (branchLen >= 8) {\n const gitPrefix = gitInfo.substring(0, Math.min(gitInfo.length, branchLen));\n return `${project.substring(0, 4)}..${gitPrefix}..${indicators ? ` [${indicators}]` : ''}`;\n }\n\n // Step 4: Basic fallback\n return `${project.substring(0, maxLen)}..`;\n}\n\n/**\n * Soft wrapping function (experimental)\n */\nexport function softWrapText(\n text: string,\n maxLength: number,\n wrapChar: 'newline' | 'space' = 'newline',\n modelPrefix: string = '*'\n): string {\n if (text.length <= maxLength) {\n return text;\n }\n\n if (wrapChar === 'newline') {\n // Smart wrap: try to break at space or safe character\n let breakPos = maxLength;\n let foundBreak = false;\n\n // Look for safe break points (spaces)\n for (let i = Math.min(maxLength - 1, text.length - 1); i > Math.max(maxLength - 20, 0) && i >= 0; i--) {\n const char = text[i];\n if (char === ' ') {\n breakPos = i;\n foundBreak = true;\n break;\n }\n }\n\n // If no safe break found and we're very close to max_length, just fit without wrapping\n if (!foundBreak && maxLength - text.length > -3) {\n return text;\n }\n\n // Adjust break position to avoid splitting multi-byte UTF-8 characters\n // UTF-8 continuation bytes have their two most significant bits set to 10 (0x80 to 0xBF)\n while (breakPos > 0 && (text.charCodeAt(breakPos) & 0xC0) === 0x80) {\n breakPos--;\n }\n\n const firstLine = text.substring(0, breakPos);\n let secondLine = text.substring(breakPos);\n\n // Remove leading space from second line if we broke at space\n if (secondLine.startsWith(' ')) {\n secondLine = secondLine.substring(1);\n }\n\n // Only wrap if second line has meaningful content\n if (secondLine) {\n return `${firstLine}\\n${modelPrefix}${secondLine}`;\n } else {\n return firstLine;\n }\n } else {\n // Use space separator for wrapping\n let breakPos = maxLength;\n\n // Adjust break position to avoid splitting multi-byte UTF-8 characters\n // UTF-8 continuation bytes have their two most significant bits set to 10 (0x80 to 0xBF)\n while (breakPos > 0 && (text.charCodeAt(breakPos) & 0xC0) === 0x80) {\n breakPos--;\n }\n\n const firstLine = text.substring(0, breakPos);\n const secondLine = text.substring(breakPos);\n return `${firstLine} ${secondLine}`;\n }\n}"]}
1
+ {"version":3,"file":"width.js","sourceRoot":"","sources":["../../src/ui/width.ts"],"names":[],"mappings":"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","sourcesContent":["import { Config } from '../core/config.js';\n\n/**\n * Terminal width detection utilities\n * Ported from bash implementation with cross-platform Node.js support\n */\n\n/**\n * Get terminal width using multiple detection methods\n * Ordered from most reliable to fallback methods\n */\nexport async function getTerminalWidth(config: Config): Promise<number> {\n // Method 0: Respect manual width override first (for testing)\n if (config.forceWidth && config.forceWidth > 0) {\n return config.forceWidth;\n }\n\n // Method 1: Try COLUMNS environment variable\n const columnsEnv = process.env.COLUMNS;\n if (columnsEnv) {\n const columns = parseInt(columnsEnv, 10);\n if (!isNaN(columns) && columns > 0) {\n return columns;\n }\n }\n\n // Method 2: Try Node.js process.stdout.columns\n if (process.stdout.columns && process.stdout.columns > 0) {\n return process.stdout.columns;\n }\n\n // Method 3: Try tput command (Unix/Linux/macOS)\n const tputWidth = await tryCommand('tput', ['cols']);\n if (tputWidth) {\n return tputWidth;\n }\n\n // Method 4: Try stty command (Unix/Linux/macOS)\n const sttyWidth = await tryStty();\n if (sttyWidth) {\n return sttyWidth;\n }\n\n // Method 5: Check Claude Code specific environment\n const claudeWidth = process.env.CLAUDE_CODE_TERMINAL_WIDTH;\n if (claudeWidth) {\n const width = parseInt(claudeWidth, 10);\n if (!isNaN(width) && width > 0) {\n return width;\n }\n }\n\n // Method 6: Terminal-specific defaults\n const termProgram = process.env.TERM_PROGRAM;\n const term = process.env.TERM;\n\n if (termProgram === 'vscode' && process.env.VSCODE_PID) {\n return 120; // VS Code default\n }\n\n if (['ghostty', 'wezterm', 'iterm'].includes(termProgram || '')) {\n return 120; // Modern terminals default to wider\n }\n\n if (term && ['alacritty', 'kitty', 'wezterm', 'ghostty', 'xterm-256color'].includes(term)) {\n return 120; // Modern terminals\n }\n\n // Method 7: Check for Windows Terminal\n if (process.env.WT_SESSION || process.env.WT_PROFILE_ID) {\n return 120; // Windows Terminal\n }\n\n // Final fallback: conservative 80-column default\n return 80;\n}\n\n/**\n * Execute a command and parse numeric output\n */\nasync function tryCommand(command: string, args: string[]): Promise<number | null> {\n try {\n const { exec } = await import('child_process');\n const { promisify } = await import('util');\n const execAsync = promisify(exec);\n\n const { stdout } = await execAsync(`${command} ${args.join(' ')}`, {\n timeout: 1000, // 1 second timeout\n encoding: 'utf-8',\n });\n\n const width = parseInt(stdout.trim(), 10);\n if (!isNaN(width) && width > 0) {\n return width;\n }\n } catch {\n // Command failed or not available\n }\n\n return null;\n}\n\n/**\n * Try stty size command\n */\nasync function tryStty(): Promise<number | null> {\n try {\n const { exec } = await import('child_process');\n const { promisify } = await import('util');\n const execAsync = promisify(exec);\n\n const { stdout } = await execAsync('stty size', {\n timeout: 1000,\n encoding: 'utf-8',\n });\n\n // stty size returns: \"rows cols\"\n const parts = stdout.trim().split(' ');\n if (parts.length === 2) {\n const width = parseInt(parts[1] || '0', 10);\n if (!isNaN(width) && width > 0) {\n return width;\n }\n }\n } catch {\n // stty failed or not available\n }\n\n return null;\n}\n\n/**\n * Debug width detection (matches bash implementation)\n */\nexport async function debugWidthDetection(config: Config): Promise<void> {\n if (!config.debugWidth) {\n return;\n }\n\n console.error('[WIDTH DEBUG] Debug mode enabled');\n\n console.error('[WIDTH DEBUG] Methods tried:');\n\n // Test process.stdout.columns\n if (process.stdout.columns) {\n console.error(`[WIDTH DEBUG] process.stdout.columns: ${process.stdout.columns}`);\n } else {\n console.error('[WIDTH DEBUG] process.stdout.columns: not available');\n }\n\n // Test COLUMNS variable\n const columnsEnv = process.env.COLUMNS;\n if (columnsEnv) {\n console.error(`[WIDTH DEBUG] COLUMNS variable: ${columnsEnv}`);\n } else {\n console.error('[WIDTH DEBUG] COLUMNS variable: not set');\n }\n\n // Test tput\n const tputWidth = await tryCommand('tput', ['cols']);\n if (tputWidth) {\n console.error(`[WIDTH DEBUG] tput cols: ${tputWidth}`);\n } else {\n console.error('[WIDTH DEBUG] tput: not available or failed');\n }\n\n // Test stty\n const sttyWidth = await tryStty();\n if (sttyWidth) {\n console.error(`[WIDTH DEBUG] stty size: ${sttyWidth}`);\n } else {\n console.error('[WIDTH DEBUG] stty: not available or failed');\n }\n\n // Test environment variables\n console.error(`[WIDTH DEBUG] CLAUDE_CODE_STATUSLINE_FORCE_WIDTH: ${config.forceWidth || 'not set'}`);\n console.error(`[WIDTH DEBUG] COLUMNS variable: ${columnsEnv || 'not set'}`);\n console.error(`[WIDTH DEBUG] CLAUDE_CODE_TERMINAL_WIDTH: ${process.env.CLAUDE_CODE_TERMINAL_WIDTH || 'not set'}`);\n console.error(`[WIDTH DEBUG] TERM_PROGRAM: ${process.env.TERM_PROGRAM || 'not set'}`);\n console.error(`[WIDTH DEBUG] TERM: ${process.env.TERM || 'not set'}`);\n\n // Show final result\n const finalWidth = await getTerminalWidth(config);\n console.error(`[WIDTH DEBUG] Final detected width: ${finalWidth}`);\n console.error(`[WIDTH DEBUG] Statusline will use: ${finalWidth - config.rightMargin} columns max`);\n}\n\n/**\n * Text truncation utilities\n */\n\n/**\n * Simple text truncation with ellipsis\n */\nexport function truncateText(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n\n // Edge case: if maxLength is too small, return minimal result\n if (maxLength < 4) {\n return '..';\n }\n\n return `${text.substring(0, maxLength - 2)}..`;\n}\n\n/**\n * Smart truncation with branch prioritization (matches bash implementation)\n */\nexport function smartTruncate(\n project: string,\n gitInfo: string,\n maxLen: number,\n _config: Config\n): string {\n // Step 1: Check if everything fits\n if (project.length + gitInfo.length <= maxLen) {\n return '';\n }\n\n // Step 2: Truncate project only (preserve branch)\n const projLen = maxLen - gitInfo.length - 2;\n if (projLen >= 5) {\n return `${project.substring(0, projLen)}..${gitInfo}`;\n }\n\n // Step 3: Truncate project + branch (preserve indicators)\n let indicators = '';\n const bracketMatch = gitInfo.match(/\\[([^\\]]+)\\]/);\n if (bracketMatch) {\n indicators = bracketMatch[1] || '';\n }\n\n const branchLen = maxLen - indicators.length - 8;\n if (branchLen >= 8) {\n const gitPrefix = gitInfo.substring(0, Math.min(gitInfo.length, branchLen));\n return `${project.substring(0, 4)}..${gitPrefix}..${indicators ? ` [${indicators}]` : ''}`;\n }\n\n // Step 4: Basic fallback\n return `${project.substring(0, maxLen)}..`;\n}\n\n/**\n * Get the display width of a string, accounting for wide characters (CJK, emoji, Nerd Font icons)\n * Uses wcwidth-style logic where wide characters = 2 columns, narrow = 1 column\n */\nexport function getStringDisplayWidth(str: string): number {\n let width = 0;\n for (const char of str) {\n const code = char.codePointAt(0) ?? 0;\n\n // Wide character ranges (CJK, emoji, Nerd Font icons, etc.)\n // CJK Unified Ideographs\n if (code >= 0x1100 && (\n (code >= 0x1100 && code <= 0x115F) || // Hangul Jamo\n (code >= 0x2E80 && code <= 0xA4CF) || // CJK和各种符号\n (code >= 0xAC00 && code <= 0xD7A3) || // Hangul Syllables\n (code >= 0xF900 && code <= 0xFAFF) || // CJK Compatibility Ideographs\n (code >= 0xFE10 && code <= 0xFE19) || // Vertical forms\n (code >= 0xFE30 && code <= 0xFE6F) || // CJK Compatibility Forms\n (code >= 0xFF00 && code <= 0xFF60) || // Fullwidth Forms\n (code >= 0xFFE0 && code <= 0xFFE6) ||\n (code >= 0x20000 && code <= 0x2FFFD) ||\n (code >= 0x30000 && code <= 0x3FFFD)\n )) {\n width += 2;\n }\n // Emoji and various symbols (including Nerd Font icons in Private Use Area)\n else if (\n (code >= 0x1F300 && code <= 0x1F9FF) || // Emoji\n (code >= 0x2600 && code <= 0x27BF) || // Miscellaneous symbols\n (code >= 0xFE00 && code <= 0xFE0F) || // Variation Selectors\n (code >= 0x1F000 && code <= 0x1F02F) || // Mahjong tiles\n (code >= 0xE000 && code <= 0xF8FF) || // Private Use Area (Nerd Font icons)\n (code >= 0xF0000 && code <= 0xFFFFD) || // Supplementary Private Use Area-A\n (code >= 0x100000 && code <= 0x10FFFD) // Supplementary Private Use Area-B\n ) {\n width += 2;\n }\n // Combining characters (zero width)\n else if (\n (code >= 0x0300 && code <= 0x036F) || // Combining diacritical marks\n (code >= 0x1DC0 && code <= 0x1DFF) || // Combining diacritical marks extended\n (code >= 0x20D0 && code <= 0x20FF) || // Combining marks for symbols\n (code >= 0xFE20 && code <= 0xFE2F) // Combining half marks\n ) {\n // Zero width, don't increment\n }\n // Normal ASCII and narrow characters\n else {\n width += 1;\n }\n }\n return width;\n}\n\n/**\n * Soft wrapping function (experimental)\n */\nexport function softWrapText(\n text: string,\n maxLength: number,\n wrapChar: 'newline' | 'space' = 'newline',\n modelPrefix: string = '*'\n): string {\n if (text.length <= maxLength) {\n return text;\n }\n\n if (wrapChar === 'newline') {\n // Smart wrap: try to break at space or safe character\n let breakPos = maxLength;\n let foundBreak = false;\n\n // Look for safe break points (spaces)\n for (let i = Math.min(maxLength - 1, text.length - 1); i > Math.max(maxLength - 20, 0) && i >= 0; i--) {\n const char = text[i];\n if (char === ' ') {\n breakPos = i;\n foundBreak = true;\n break;\n }\n }\n\n // If no safe break found and we're very close to max_length, just fit without wrapping\n if (!foundBreak && maxLength - text.length > -3) {\n return text;\n }\n\n // Adjust break position to avoid splitting multi-byte UTF-8 characters\n // UTF-8 continuation bytes have their two most significant bits set to 10 (0x80 to 0xBF)\n while (breakPos > 0 && (text.charCodeAt(breakPos) & 0xC0) === 0x80) {\n breakPos--;\n }\n\n const firstLine = text.substring(0, breakPos);\n let secondLine = text.substring(breakPos);\n\n // Remove leading space from second line if we broke at space\n if (secondLine.startsWith(' ')) {\n secondLine = secondLine.substring(1);\n }\n\n // Only wrap if second line has meaningful content\n if (secondLine) {\n return `${firstLine}\\n${modelPrefix}${secondLine}`;\n } else {\n return firstLine;\n }\n } else {\n // Use space separator for wrapping\n let breakPos = maxLength;\n\n // Adjust break position to avoid splitting multi-byte UTF-8 characters\n // UTF-8 continuation bytes have their two most significant bits set to 10 (0x80 to 0xBF)\n while (breakPos > 0 && (text.charCodeAt(breakPos) & 0xC0) === 0x80) {\n breakPos--;\n }\n\n const firstLine = text.substring(0, breakPos);\n const secondLine = text.substring(breakPos);\n return `${firstLine} ${secondLine}`;\n }\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-statusline",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "description": "Simple statusline for Claude Code with git indicators. TypeScript rewrite for performance and npm distribution.",
5
5
  "main": "dist/index.bundle.js",
6
6
  "bin": {
@@ -44,7 +44,7 @@
44
44
  "license": "Apache-2.0",
45
45
  "repository": {
46
46
  "type": "git",
47
- "url": "https://github.com/shrwnsan/claude-statusline.git"
47
+ "url": "git+https://github.com/shrwnsan/claude-statusline.git"
48
48
  },
49
49
  "bugs": {
50
50
  "url": "https://github.com/shrwnsan/claude-statusline/issues"
@@ -86,4 +86,4 @@
86
86
  "types": "./dist/core/config.d.ts"
87
87
  }
88
88
  }
89
- }
89
+ }