Skip to content

Commit 17429fa

Browse files
committed
Polish usage and project help
1 parent 84d3773 commit 17429fa

6 files changed

Lines changed: 42 additions & 14 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codebase-cli",
3-
"version": "2.0.0-pre.76",
3+
"version": "2.0.0-pre.77",
44
"description": "Codebase CLI — a TypeScript coding agent on the pi-mono runtime. OAuth-aware, any LLM provider, single install.",
55
"keywords": [
66
"ai",

src/commands/builtins/usage.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe("formatUsageBalance", () => {
2929
plan: "pro",
3030
}),
3131
).toMatchObject({
32-
creditLine: "Credits: 250 / 1,000 used · 750 left",
32+
creditLine: "Metered credits: 250 / 1,000 used · 750 left",
3333
pct: 25,
3434
planName: "pro",
3535
});
@@ -42,23 +42,23 @@ describe("formatUsageBalance", () => {
4242
plan: { name: "team", monthlyCredits: 40 },
4343
}),
4444
).toMatchObject({
45-
creditLine: "Credits: 30 / 40 used · 10 left",
45+
creditLine: "Metered credits: 30 / 40 used · 10 left",
4646
pct: 75,
4747
planName: "team",
4848
});
4949
});
5050

5151
it("uses the known free allowance when the live endpoint omits it", () => {
5252
expect(formatUsageBalance({ creditsRemaining: 25, planId: "free" })).toMatchObject({
53-
creditLine: "Credits: 25 / 50 used · 25 left",
53+
creditLine: "Metered credits: 25 / 50 used · 25 left",
5454
pct: 50,
5555
planName: "free",
5656
});
5757
});
5858

5959
it("accepts snake-case allowance fields", () => {
6060
expect(formatUsageBalance({ creditsRemaining: 30, monthly_credits: 50, planId: "free" })).toMatchObject({
61-
creditLine: "Credits: 20 / 50 used · 30 left",
61+
creditLine: "Metered credits: 20 / 50 used · 30 left",
6262
pct: 40,
6363
});
6464
});
@@ -72,7 +72,7 @@ describe("formatUsageBalance", () => {
7272
planName: "pro",
7373
}),
7474
).toMatchObject({
75-
creditLine: "Credits: 100 / 1,000 used · 900 left",
75+
creditLine: "Metered credits: 100 / 1,000 used · 900 left",
7676
pct: 10,
7777
planName: "pro",
7878
});

src/commands/builtins/usage.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ interface Balance {
2424
*/
2525
export const usage: Command = {
2626
name: "usage",
27-
description: "Show your Codebase plan usage — credits used, remaining, and reset date.",
27+
description: "Show metered credits and included Codebase turn allowances.",
2828
handler: async (_args, ctx) => {
2929
ctx.emit(await fetchUsageReport());
3030
return { handled: true };
@@ -68,10 +68,10 @@ export async function fetchUsageReport(): Promise<string> {
6868
lines.push("Monthly allowance was not returned yet; showing remaining credits only.");
6969
}
7070
if (typeof b.anyBuildsRemaining === "number" && b.anyBuildsRemaining >= 0) {
71-
lines.push(`Build turns remaining: ${b.anyBuildsRemaining.toLocaleString()}`);
71+
lines.push(`Included web-build turns remaining: ${b.anyBuildsRemaining.toLocaleString()}`);
7272
}
7373
if (typeof b.cheapBuildsRemaining === "number" && b.cheapBuildsRemaining >= 0) {
74-
lines.push(`Fast turns remaining: ${b.cheapBuildsRemaining.toLocaleString()}`);
74+
lines.push(`Included fast coding turns remaining: ${b.cheapBuildsRemaining.toLocaleString()}`);
7575
}
7676
return lines.join("\n");
7777
} catch (err) {
@@ -99,7 +99,7 @@ export function formatUsageBalance(b: Balance): {
9999
b.planName ?? plan?.name ?? (typeof b.plan === "string" ? b.plan : undefined) ?? b.planId ?? "Codebase";
100100
if (!allowance || allowance <= 0) {
101101
return {
102-
creditLine: `Credits left: ${remaining.toLocaleString()}`,
102+
creditLine: `Metered credits left: ${remaining.toLocaleString()}`,
103103
days,
104104
pct: null,
105105
planName,
@@ -108,7 +108,7 @@ export function formatUsageBalance(b: Balance): {
108108
const used = Math.max(0, allowance - remaining);
109109
const pct = Math.max(0, Math.min(100, Math.round((used / allowance) * 100)));
110110
return {
111-
creditLine: `Credits: ${used.toLocaleString()} / ${allowance.toLocaleString()} used · ${remaining.toLocaleString()} left`,
111+
creditLine: `Metered credits: ${used.toLocaleString()} / ${allowance.toLocaleString()} used · ${remaining.toLocaleString()} left`,
112112
days,
113113
pct,
114114
planName,

src/projects/cli.test.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { mkdtempSync, rmSync } from "node:fs";
22
import { tmpdir } from "node:os";
33
import { join } from "node:path";
4-
import { describe, expect, it } from "vitest";
4+
import { describe, expect, it, vi } from "vitest";
55
import { runProjectSubcommand } from "./cli.js";
66
import { type ProjectClient, ProjectClientError } from "./client.js";
77
import { BuildHandoffStore } from "./handoff.js";
@@ -102,6 +102,19 @@ describe("runProjectSubcommand", () => {
102102
expect(result.stdout.join("\n")).toContain("alias: codebase web-build");
103103
});
104104

105+
it("prints pull help without treating the flag as a project id", async () => {
106+
const client = fakeClient();
107+
client.pull = vi.fn(() => {
108+
throw new Error("pull should not run for help");
109+
});
110+
111+
const result = await runProject(["project", "pull", "--help"], client);
112+
113+
expect(result.code).toBe(0);
114+
expect(result.stdout.join("\n")).toContain("usage: codebase project pull <id> [dest]");
115+
expect(client.pull).not.toHaveBeenCalled();
116+
});
117+
105118
it("defaults project list to 25 entries and puts titled indexed projects first", async () => {
106119
const projects: PlatformProject[] = [
107120
{ id: "storage-a", source: "storage-only" },

src/projects/cli.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ export async function runProjectSubcommand(argv: string[], options: ProjectCliOp
4444
printProjectHelp(out);
4545
return 0;
4646
}
47+
if (subcommand === "pull" && isHelpArg(argv[2])) {
48+
printPullHelp(out);
49+
return 0;
50+
}
4751
if (subcommand === "pull") return await pullCmd(client, argv[2], argv[3], out, err);
4852
if (subcommand === "build") return await buildCmd(client, argv.slice(2), out, err, sleep, handoffStore);
4953
if (subcommand === "status") return await statusCmd(client, argv[2], out, err, handoffStore);
@@ -127,6 +131,10 @@ function isListFlag(arg: string): boolean {
127131
return arg === "--all" || arg === "--limit" || arg.startsWith("--limit=");
128132
}
129133

134+
function isHelpArg(arg: string | undefined): boolean {
135+
return arg === "--help" || arg === "-h" || arg === "help";
136+
}
137+
130138
async function listCmd(client: ProjectClient, opts: ListOptions, out: (msg: string) => void): Promise<number> {
131139
const projects = sortProjects([...(await client.list())]);
132140
if (projects.length === 0) {
@@ -628,6 +636,13 @@ function printProjectHelp(out: (msg: string) => void): void {
628636
out(" cancel <id> cancel a running web build (`latest` is accepted)");
629637
}
630638

639+
function printPullHelp(out: (msg: string) => void): void {
640+
out("usage: codebase project pull <id> [dest]");
641+
out("");
642+
out("Download a codebase.design project as a ZIP archive.");
643+
out("When dest is omitted, the ZIP is saved under ~/.codebase/pulls/.");
644+
}
645+
631646
function printBuildHelp(out: (msg: string) => void): void {
632647
out("usage: codebase project build [--wait] [--model MODEL] [--scaffold ID] [--project ID] <prompt>");
633648
out("alias: codebase web-build [--wait] [--model MODEL] [--scaffold ID] [--project ID] <prompt>");

0 commit comments

Comments
 (0)