diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30024de..0cc78e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,32 @@
# Changelog
+## 0.4.4 - 2026-04-15
+
+### Added
+- Auto-refresh flag. `codeburn report --refresh 60` reloads data at a set
+ interval. Works on `report`, `today`, and `month` commands. Default off.
+- Readable project names. Strips home directory prefix from encoded paths,
+ shows 3 path segments for more context. Home dir sessions display as "home".
+- Responsive dashboard reflows on terminal resize via Ink's useWindowSize
+ hook. Width cap raised from 104 to 160 columns. Contributed by @AleBles.
+- Total downloads and install size badges in README.
+
+### Fixed
+- Agent/subagent session files were excluded, dropping ~46% of API calls.
+ Subagent sessions live in separate subagents/ directories with unique
+ message IDs and are now included. Closes #17.
+- Codex cache hit always showed 100%. OpenAI includes cached tokens inside
+ input_tokens (unlike Anthropic). Normalized to prevent double-counting
+ in cost calculation and cache hit display. Closes #21.
+- CSV formula injection. Cells starting with =, +, -, @ are prefixed with
+ an apostrophe before CSV escaping. Contributed by @serabi.
+- Menubar "Open Full Report" and "Export CSV" actions broken for npm-installed
+ users. Invokes resolved binary directly instead of assuming ~/codeburn
+ checkout. Currency picker used nonexistent `config currency` subcommand.
+ Contributed by @MukundaKatta. Closes #32, #27.
+- Activity panel moved from full-width to half-width row for better space
+ usage on wide terminals.
+
## 0.4.1 - 2026-04-14
### Added
diff --git a/README.md b/README.md
index 449adea..f18b330 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,9 @@
-
+
+
+
@@ -45,6 +47,7 @@ codeburn # interactive dashboard (default: 7 days)
codeburn today # today's usage
codeburn month # this month's usage
codeburn report -p 30days # rolling 30-day window
+codeburn report --refresh 60 # auto-refresh every 60 seconds
codeburn status # compact one-liner (today + month)
codeburn status --format json
codeburn export # CSV with today, 7 days, 30 days
diff --git a/assets/dashboard.jpg b/assets/dashboard.jpg
index b27c7ee..7006631 100644
Binary files a/assets/dashboard.jpg and b/assets/dashboard.jpg differ
diff --git a/src/cli.ts b/src/cli.ts
index d3c5c45..f422239 100644
--- a/src/cli.ts
+++ b/src/cli.ts
@@ -67,8 +67,9 @@ program
.description('Interactive usage dashboard')
.option('-p, --period ', 'Starting period: today, week, month, 30days', 'week')
.option('--provider ', 'Filter by provider: all, claude, codex', 'all')
+ .option('--refresh ', 'Auto-refresh interval in seconds', parseInt)
.action(async (opts) => {
- await renderDashboard(toPeriod(opts.period), opts.provider)
+ await renderDashboard(toPeriod(opts.period), opts.provider, opts.refresh)
})
function buildPeriodData(label: string, projects: ProjectSummary[]): PeriodData {
@@ -153,16 +154,18 @@ program
.command('today')
.description('Today\'s usage dashboard')
.option('--provider ', 'Filter by provider: all, claude, codex', 'all')
+ .option('--refresh ', 'Auto-refresh interval in seconds', parseInt)
.action(async (opts) => {
- await renderDashboard('today', opts.provider)
+ await renderDashboard('today', opts.provider, opts.refresh)
})
program
.command('month')
.description('This month\'s usage dashboard')
.option('--provider ', 'Filter by provider: all, claude, codex', 'all')
+ .option('--refresh ', 'Auto-refresh interval in seconds', parseInt)
.action(async (opts) => {
- await renderDashboard('month', opts.provider)
+ await renderDashboard('month', opts.provider, opts.refresh)
})
program
diff --git a/src/dashboard.tsx b/src/dashboard.tsx
index 2130c0f..04ed622 100644
--- a/src/dashboard.tsx
+++ b/src/dashboard.tsx
@@ -194,18 +194,16 @@ const _homeEncoded = homedir().replace(/\//g, '-')
function shortProject(encoded: string): string {
let path = encoded.replace(/^-/, '')
- // Strip home dir prefix (e.g. "Users-torukmakto-" → "")
if (path.startsWith(_homeEncoded.replace(/^-/, ''))) {
path = path.slice(_homeEncoded.replace(/^-/, '').length).replace(/^-/, '')
}
- // Strip common system prefixes
path = path
.replace(/^private-tmp-[^-]+-[^-]+-/, '') // /private/tmp///
.replace(/^private-tmp-/, '')
.replace(/^tmp-/, '')
- if (!path) return '~'
+ if (!path) return 'home'
const parts = path.split('-').filter(Boolean)
if (parts.length <= 3) return parts.join('/')
@@ -485,10 +483,11 @@ function DashboardContent({ projects, period, columns }: { projects: ProjectSumm
)
}
-function InteractiveDashboard({ initialProjects, initialPeriod, initialProvider }: {
+function InteractiveDashboard({ initialProjects, initialPeriod, initialProvider, refreshSeconds }: {
initialProjects: ProjectSummary[]
initialPeriod: Period
initialProvider: string
+ refreshSeconds?: number
}) {
const { exit } = useApp()
const [period, setPeriod] = useState(initialPeriod)
@@ -528,6 +527,12 @@ function InteractiveDashboard({ initialProjects, initialPeriod, initialProvider
setLoading(false)
}, [])
+ useEffect(() => {
+ if (!refreshSeconds || refreshSeconds <= 0) return
+ const id = setInterval(() => { reloadData(period, activeProvider) }, refreshSeconds * 1000)
+ return () => clearInterval(id)
+ }, [refreshSeconds, period, activeProvider, reloadData])
+
const switchPeriod = useCallback(async (newPeriod: Period) => {
if (newPeriod === period) return
setPeriod(newPeriod)
@@ -592,7 +597,7 @@ function StaticDashboard({ projects, period }: { projects: ProjectSummary[]; per
)
}
-export async function renderDashboard(period: Period = 'week', provider: string = 'all'): Promise {
+export async function renderDashboard(period: Period = 'week', provider: string = 'all', refreshSeconds?: number): Promise {
await loadPricing()
const range = getDateRange(period)
const projects = await parseAllSessions(range, provider)
@@ -601,7 +606,7 @@ export async function renderDashboard(period: Period = 'week', provider: string
if (isTTY) {
const { waitUntilExit } = render(
-
+
)
await waitUntilExit()
} else {