vibestats 1.2.0 → 1.3.0

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
@@ -31,11 +31,16 @@ vibestats --wrapped # Annual wrapped summary
31
31
  |------|-------------|
32
32
  | `--monthly` | Aggregate by month |
33
33
  | `--model` | Aggregate by model |
34
+ | `--sessions` | Aggregate by session |
34
35
  | `--total` | Show only totals |
35
36
  | `--since YYYY-MM-DD` | Filter from date |
36
37
  | `--until YYYY-MM-DD` | Filter to date |
38
+ | `--last N` | Last N days (shorthand: `--last7`, `--last30`, etc.) |
37
39
  | `--compact`, `-c` | Compact table (hide cache columns) |
38
40
  | `--json` | Output raw JSON |
41
+ | `--quiet`, `-q` | Quiet output (totals line) |
42
+ | `--share`, `-s` | Generate a shareable usage URL |
43
+ | `--project`, `-p` | Current project only (Claude Code) |
39
44
 
40
45
  ### Wrapped Mode
41
46
 
@@ -59,7 +64,7 @@ vibestats --wrapped # Annual wrapped summary
59
64
  |------|-------------|
60
65
  | `--init` | Create config file |
61
66
  | `--config` | Show current config |
62
- | `--url <url>` | Custom base URL |
67
+ | `--url <url>` | Custom base URL for shareable links/pages |
63
68
 
64
69
  ## Config File
65
70
 
package/dist/index.js CHANGED
@@ -1978,10 +1978,55 @@ function createSpinner(label = "Loading vibestats...") {
1978
1978
  };
1979
1979
  return { whilePromise, stop };
1980
1980
  }
1981
+ function formatLocalDateYYYYMMDD(date) {
1982
+ const year = date.getFullYear();
1983
+ const month = String(date.getMonth() + 1).padStart(2, "0");
1984
+ const day = String(date.getDate()).padStart(2, "0");
1985
+ return `${year}-${month}-${day}`;
1986
+ }
1987
+ function parseLocalDateYYYYMMDD(dateStr) {
1988
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dateStr.trim());
1989
+ if (!m) return null;
1990
+ const year = Number.parseInt(m[1], 10);
1991
+ const month = Number.parseInt(m[2], 10);
1992
+ const day = Number.parseInt(m[3], 10);
1993
+ if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day)) return null;
1994
+ if (month < 1 || month > 12) return null;
1995
+ if (day < 1 || day > 31) return null;
1996
+ const date = new Date(year, month - 1, day, 12, 0, 0, 0);
1997
+ if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) return null;
1998
+ return date;
1999
+ }
2000
+ function parseLastDaysFlag(args) {
2001
+ const parseDays = (value) => {
2002
+ if (typeof value === "number") {
2003
+ if (!Number.isInteger(value) || value <= 0) return null;
2004
+ return value;
2005
+ }
2006
+ if (typeof value === "string") {
2007
+ const m = /^(\d+)\s*d?$/.exec(value.trim());
2008
+ if (!m) return null;
2009
+ const days = Number.parseInt(m[1], 10);
2010
+ if (!Number.isFinite(days) || days <= 0) return null;
2011
+ return days;
2012
+ }
2013
+ return null;
2014
+ };
2015
+ const direct = parseDays(args.last);
2016
+ if (direct) return direct;
2017
+ const shorthandDays = Object.keys(args).map((k) => {
2018
+ const m = /^last(\d+)$/.exec(k);
2019
+ if (!m) return null;
2020
+ return Number.parseInt(m[1], 10);
2021
+ }).filter((n) => typeof n === "number" && Number.isFinite(n) && n > 0);
2022
+ if (shorthandDays.length === 0) return null;
2023
+ shorthandDays.sort((a, b) => a - b);
2024
+ return shorthandDays[0];
2025
+ }
1981
2026
  var main = defineCommand({
1982
2027
  meta: {
1983
2028
  name: "vibestats",
1984
- version: "1.0.0",
2029
+ version: "1.3.0",
1985
2030
  description: "AI coding stats - usage tracking and annual wrapped for Claude Code & Codex"
1986
2031
  },
1987
2032
  args: {
@@ -2012,7 +2057,7 @@ var main = defineCommand({
2012
2057
  quiet: {
2013
2058
  type: "boolean",
2014
2059
  alias: "q",
2015
- description: "Only output the shareable URL (wrapped mode)",
2060
+ description: "Quiet mode (usage: totals line; wrapped: URL only)",
2016
2061
  default: false
2017
2062
  },
2018
2063
  // Usage-specific options
@@ -2047,6 +2092,11 @@ var main = defineCommand({
2047
2092
  type: "string",
2048
2093
  description: "End date for filtering (YYYY-MM-DD)"
2049
2094
  },
2095
+ last: {
2096
+ type: "string",
2097
+ alias: "l",
2098
+ description: "Show only the last N days (e.g. --last 7). Shorthand also supported: --last7, --last30, etc."
2099
+ },
2050
2100
  compact: {
2051
2101
  type: "boolean",
2052
2102
  alias: "c",
@@ -2075,7 +2125,7 @@ var main = defineCommand({
2075
2125
  // Wrapped-specific options
2076
2126
  url: {
2077
2127
  type: "string",
2078
- description: "Custom base URL for the wrapped page"
2128
+ description: "Custom base URL for shareable links/pages"
2079
2129
  },
2080
2130
  "no-short": {
2081
2131
  type: "boolean",
@@ -2113,6 +2163,23 @@ var main = defineCommand({
2113
2163
  }
2114
2164
  });
2115
2165
  async function runUsage(args, config) {
2166
+ const requestedSince = args.since;
2167
+ const requestedUntil = args.until;
2168
+ const requestedLastDays = parseLastDaysFlag(args);
2169
+ let since = requestedSince;
2170
+ let until = requestedUntil;
2171
+ if (requestedLastDays && !since) {
2172
+ const anchorStr = until || formatLocalDateYYYYMMDD(/* @__PURE__ */ new Date());
2173
+ const anchor = parseLocalDateYYYYMMDD(anchorStr);
2174
+ if (!anchor) {
2175
+ console.error(`Error: Invalid --until date "${anchorStr}". Expected YYYY-MM-DD.`);
2176
+ process.exit(1);
2177
+ }
2178
+ const start = new Date(anchor);
2179
+ start.setDate(start.getDate() - (requestedLastDays - 1));
2180
+ since = formatLocalDateYYYYMMDD(start);
2181
+ if (!until) until = anchorStr;
2182
+ }
2116
2183
  let aggregation = "daily";
2117
2184
  if (args.sessions) aggregation = "session";
2118
2185
  else if (args.monthly) aggregation = "monthly";
@@ -2122,14 +2189,27 @@ async function runUsage(args, config) {
2122
2189
  const stats = await spinner.whilePromise(
2123
2190
  loadUsageStats({
2124
2191
  aggregation,
2125
- since: args.since,
2126
- until: args.until,
2192
+ since,
2193
+ until,
2127
2194
  codexOnly: args.codex,
2128
2195
  combined: args.combined,
2129
2196
  projectFilter: args.project ? process.cwd() : void 0
2130
2197
  })
2131
2198
  );
2132
2199
  if (!stats) {
2200
+ if (requestedSince || requestedUntil || requestedLastDays) {
2201
+ const rangeStr = `${since || "(start)"} to ${until || "(end)"}`;
2202
+ console.error(`Error: No usage data found for date range ${rangeStr}.`);
2203
+ if (args.codex) {
2204
+ console.error("Checked: ~/.codex/sessions and ~/.codex/archived_sessions");
2205
+ } else if (args.combined) {
2206
+ console.error("Checked: ~/.claude/projects and ~/.codex/sessions (plus archived sessions)");
2207
+ } else {
2208
+ console.error("Checked: ~/.claude/projects");
2209
+ }
2210
+ console.error("Try widening the range or removing the date filter.");
2211
+ process.exit(1);
2212
+ }
2133
2213
  if (args.codex) {
2134
2214
  console.error("Error: OpenAI Codex data not found at ~/.codex");
2135
2215
  console.error("Make sure you have used the Codex CLI at least once.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibestats",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "AI coding stats - usage tracking and annual wrapped for Claude Code & Codex",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",