Skip to content

feat: add standalone zcode-quota CLI with watch mode#13

Merged
william0wang merged 3 commits into
mainfrom
feta/quota-tool
Jul 9, 2026
Merged

feat: add standalone zcode-quota CLI with watch mode#13
william0wang merged 3 commits into
mainfrom
feta/quota-tool

Conversation

@william0wang

Copy link
Copy Markdown
Owner

Summary

  • Add a standalone zcode-quota CLI (new bin) that queries GLM Coding Plan usage from the terminal — no ACP server or editor needed. Reuses the existing queryQuota() / formatQuotaPlain() (only reads ~/.zcode/v2/config.json).
    • One-shot mode: zcode-quota
    • Watch mode: zcode-quota -w (clear-and-redraw like top, with a per-second refresh countdown in the footer; default 30s, min 10s to match the cache TTL)
    • zcode-quota -h for help; zero new dependencies (hand-rolled argv parser + ANSI escapes)
  • Add formatQuotaPlain() to strip the ```text fence for raw terminal output (the fenced card stays for the in-editor /quota slash command).
  • Add a Privacy section to both READMEs and a Data & Privacy section to docs/ARCHITECTURE.md, explaining what data the bridge touches, where it goes, and why each access is needed (notably: the tasks-index.sqlite write syncs sessions into the ZCode app history/search).
  • Fix the upstream repository URL (zcode-org/zcode-acp-serverwilliam0wang/zcode-acp) across README badges, package.json, and CONTRIBUTING.md — the old address pointed to a non-existent repo.

Test plan

  • pnpm typecheck clean
  • pnpm test — 152 tests pass (incl. 14 new CLI tests for parseArgs / resolveIntervalMs, 2 new formatQuotaPlain tests)
  • pnpm build succeeds; dist/bin/quota.js generated
  • node dist/bin/quota.js — one-shot prints the card against the live API
  • node dist/bin/quota.js -w -i 10 — watch mode stays alive (fixed: the sleep timer was unref()-ed, which let Node exit after the first frame), countdown ticks per second, Ctrl-C exits cleanly
  • node dist/bin/quota.js -w -i 3 — clamps below-floor interval to 10s with a stderr warning
  • Manually reviewed the three privacy sections for accuracy against the codebase

Notes

  • No new runtime dependencies (Local First).
  • The fence-stripping logic lives only in the CLI entry (formatQuotaPlain); the in-editor /quota card keeps its ```text fence unchanged.

@william0wang william0wang merged commit a371f51 into main Jul 9, 2026
1 check passed
@william0wang william0wang deleted the feta/quota-tool branch July 9, 2026 14:18

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a standalone CLI tool, zcode-quota, allowing users to query their GLM Coding Plan usage directly from the terminal. It supports both one-shot and live watch modes, strips markdown fences for clean terminal rendering, and includes comprehensive unit tests. The feedback highlights two issues in the CLI implementation: an off-by-one error in the watch mode countdown loop that results in a shorter sleep duration than requested, and a SIGINT handler that prematurely exits the process, bypassing the finally block cleanup in runWatch.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/bin/quota.ts
Comment on lines +203 to +207
for (let remaining = intervalSec - 1; remaining > 0; remaining--) {
await sleep(1000, controller.signal).catch(() => undefined);
if (controller.signal.aborted) break;
process.stdout.write(`\r${ANSI.clearLine}${renderFooter(updatedAt, remaining)}`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is an off-by-one error in the countdown loop. If intervalSec is 10, the loop runs for remaining values from 9 down to 1 (9 iterations), sleeping for 1 second each. This results in a total sleep of 9 seconds instead of the requested 10 seconds. To fix this, the loop should run for intervalSec iterations (from intervalSec - 1 down to 0 inclusive), while conditionally skipping the footer write when remaining is 0 to avoid showing a '0s' countdown right before the redraw.

      for (let remaining = intervalSec - 1; remaining >= 0; remaining--) {
        await sleep(1000, controller.signal).catch(() => undefined);
        if (controller.signal.aborted) break;
        if (remaining > 0) {
          process.stdout.write(`\r${ANSI.clearLine}${renderFooter(updatedAt, remaining)}`);
        }
      }

Comment thread src/bin/quota.ts
Comment on lines +186 to +190
const onInt = (): void => {
controller.abort();
restore();
process.exit(0);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Calling process.exit(0) inside the SIGINT handler terminates the process synchronously, which completely bypasses the finally block at the end of runWatch. This makes the finally block (and the process.off cleanup) dead code. By simply calling controller.abort(), the active sleep promise will reject, the loops will terminate naturally, and the finally block will execute to restore the cursor and clean up the event listener.

  const onInt = (): void => {
    controller.abort();
  };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant