quotacap 0.0.48 → 0.0.49

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.
@@ -7,7 +7,12 @@ export function parseClaudeUsage(result, now = new Date()) {
7
7
  result.match(/Current week[^\d%]*(\d+)%\s+used/i);
8
8
  const usedPct = weeklyMatch ? parseInt(weeklyMatch[1], 10) : 0;
9
9
  const sessionPct = sessionMatch ? parseInt(sessionMatch[1], 10) : undefined;
10
- let resetsAt = parseResetText(result, now);
10
+ // The paced window's reset lives on the "Current week" line. Parse that
11
+ // line alone: within ~5h of the weekly roll the session reset is sooner,
12
+ // and whole-text farthest-wins would pick it instead (muse precedent:
13
+ // scope to the weekly section, then degrade to +7d when it won't parse).
14
+ const weeklyLine = result.match(/^.*current week.*$/gim)?.[0];
15
+ let resetsAt = parseResetText(weeklyLine ?? result, now);
11
16
  const parsedReset = !!resetsAt;
12
17
  if (!resetsAt)
13
18
  resetsAt = new Date(now.getTime() + 7 * 86400000).toISOString();
@@ -2,7 +2,15 @@ import { parseResetText } from "./parse.js";
2
2
  export function parseManualUsage(provider, text, now = new Date()) {
3
3
  const m = text.match(/(\d+)% used/);
4
4
  const usedPct = m ? parseInt(m[1], 10) : 0;
5
- const parsedReset = parseResetText(text, now);
5
+ // Prefer the weekly line's reset: within ~5h of the weekly roll the 5h
6
+ // reset is sooner, and whole-text farthest-wins would pick it instead.
7
+ // Fall back to whole-text only when the weekly line states no reset at
8
+ // all (e.g. a paste with the reset on its own line).
9
+ const weeklyLine = text.match(/^.*(?:current week|weekly).*$/gim)?.[0];
10
+ let parsedReset = weeklyLine ? parseResetText(weeklyLine, now) : null;
11
+ if (!parsedReset && (!weeklyLine || !/resets/i.test(weeklyLine))) {
12
+ parsedReset = parseResetText(text, now);
13
+ }
6
14
  const resetsAt = parsedReset ?? new Date(now.getTime() + 3 * 86400000).toISOString();
7
15
  const periodStart = (parsedReset ? new Date(new Date(resetsAt).getTime() - 7 * 86400000) : new Date(now.getTime() - 7 * 86400000)).toISOString();
8
16
  let sessionPct;
@@ -60,9 +60,11 @@ function parseRelativeDuration(raw, now) {
60
60
  return new Date(now.getTime() + ms);
61
61
  }
62
62
  export function parseResetText(text, now) {
63
- // Farthest future reset wins. Claude output lists the session reset before
64
- // the weekly one; kimi lists the weekly window before its 5h rolling one —
65
- // the period reset is always the farthest, so one rule serves both.
63
+ // Farthest future reset wins across the given text. Callers whose output
64
+ // carries two windows must pre-scope to the paced window's line first —
65
+ // near the weekly roll the session reset is sooner, so whole-text
66
+ // farthest-wins would pick the wrong one (claude/manual scope; kimi/muse
67
+ // pass pre-scoped snippets).
66
68
  const matches = [...text.matchAll(/resets\s+([^\n(]+?)\s*(?:\(|$)/gm)];
67
69
  let best = null;
68
70
  for (const m of matches) {
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // generated by scripts/build-embed.mjs — do not edit
2
- export const VERSION = "0.0.48";
2
+ export const VERSION = "0.0.49";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quotacap",
3
- "version": "0.0.48",
3
+ "version": "0.0.49",
4
4
  "description": "Local quota tracker for AI coding plans with a dashboard, CLI, MCP server, and next-plan recommendations.",
5
5
  "license": "MIT",
6
6
  "keywords": [