Skip to content

Commit efbe0b3

Browse files
lollipop-onlclaude
andauthored
refactor: replace project positional arguments with --project flag (#87)
* refactor: replace project positional arguments with --project flag Unify how project is specified across all commands by converting positional arguments (<project> and [project]) to the -p/--project flag, improving consistency and reducing errors from AI agents confusing the argument style. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: update recipe to use --project flag for status list command Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d66e2bf commit efbe0b3

29 files changed

Lines changed: 170 additions & 139 deletions

apps/cli/src/commands/category/list.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const sampleCategories = [
1515
describe("category list", () => {
1616
it("displays category list in tabular format", async () => {
1717
mockClient.getCategories.mockResolvedValue(sampleCategories);
18-
await parseCommand(() => import("./list"), ["TEST"]);
18+
await parseCommand(() => import("./list"), ["-p", "TEST"]);
1919

2020
expect(mockClient.getCategories).toHaveBeenCalledWith("TEST");
2121
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("ID"));
@@ -25,7 +25,7 @@ describe("category list", () => {
2525

2626
it("shows message when no categories found", async () => {
2727
mockClient.getCategories.mockResolvedValue([]);
28-
await parseCommand(() => import("./list"), ["TEST"]);
28+
await parseCommand(() => import("./list"), ["-p", "TEST"]);
2929

3030
expect(consola.info).toHaveBeenCalledWith("No categories found.");
3131
});
@@ -34,7 +34,7 @@ describe("category list", () => {
3434
"outputs JSON when --json flag is set",
3535
itOutputsJson(
3636
() => import("./list"),
37-
["TEST", "--json"],
37+
["-p", "TEST", "--json"],
3838
"Bug",
3939
() => {
4040
mockClient.getCategories.mockResolvedValue(sampleCategories);

apps/cli/src/commands/category/list.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,24 @@ import { type Row, outputResult, printTable } from "@repo/cli-utils";
33
import consola from "consola";
44
import { BeeCommand, ENV_AUTH, ENV_PROJECT } from "../../lib/bee-command";
55
import * as opt from "../../lib/common-options";
6+
import { resolveOptions } from "../../lib/required-option";
67

78
const list = new BeeCommand("list")
89
.summary("List categories")
910
.description(`Categories help organize issues by grouping them into logical areas.`)
10-
.argument("[project]", "Project ID or project key")
11+
.addOption(opt.project())
1112
.addOption(opt.json())
1213
.addOption(opt.space())
1314
.envVars([...ENV_AUTH, ENV_PROJECT])
1415
.examples([
15-
{ description: "List all categories in a project", command: "bee category list PROJECT" },
16-
{ description: "Output as JSON", command: "bee category list PROJECT --json" },
16+
{ description: "List all categories in a project", command: "bee category list -p PROJECT" },
17+
{ description: "Output as JSON", command: "bee category list -p PROJECT --json" },
1718
])
18-
.action(async (project, opts) => {
19+
.action(async (opts, cmd) => {
20+
await resolveOptions(cmd);
1921
const { client } = await getClient(opts.space);
2022

21-
const categories = await client.getCategories(project);
23+
const categories = await client.getCategories(opts.project);
2224

2325
outputResult(categories, opts, (data) => {
2426
if (data.length === 0) {

apps/cli/src/commands/document/tree.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describe("document tree", () => {
4343
mockClient.getDocumentTree.mockResolvedValue(sampleTree);
4444

4545
const { default: tree } = await import("./tree");
46-
await tree.parseAsync(["PROJECT"], { from: "user" });
46+
await tree.parseAsync(["-p", "PROJECT"], { from: "user" });
4747

4848
expect(mockClient.getDocumentTree).toHaveBeenCalledWith("PROJECT");
4949
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("Getting Started"));
@@ -55,7 +55,7 @@ describe("document tree", () => {
5555
mockClient.getDocumentTree.mockResolvedValue(sampleTree);
5656

5757
const { default: tree } = await import("./tree");
58-
await tree.parseAsync(["PROJECT"], { from: "user" });
58+
await tree.parseAsync(["-p", "PROJECT"], { from: "user" });
5959

6060
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("\ud83d\ude80"));
6161
});
@@ -67,7 +67,7 @@ describe("document tree", () => {
6767
});
6868

6969
const { default: tree } = await import("./tree");
70-
await tree.parseAsync(["PROJECT"], { from: "user" });
70+
await tree.parseAsync(["-p", "PROJECT"], { from: "user" });
7171

7272
expect(consola.info).toHaveBeenCalledWith("No documents found.");
7373
});
@@ -78,7 +78,7 @@ describe("document tree", () => {
7878
});
7979

8080
const { default: tree } = await import("./tree");
81-
await tree.parseAsync(["PROJECT"], { from: "user" });
81+
await tree.parseAsync(["-p", "PROJECT"], { from: "user" });
8282

8383
expect(consola.info).toHaveBeenCalledWith("No documents found.");
8484
});
@@ -99,7 +99,7 @@ describe("document tree", () => {
9999
});
100100

101101
const { default: tree } = await import("./tree");
102-
await tree.parseAsync(["PROJECT"], { from: "user" });
102+
await tree.parseAsync(["-p", "PROJECT"], { from: "user" });
103103

104104
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("doc-no-name"));
105105
});
@@ -109,15 +109,15 @@ describe("document tree", () => {
109109

110110
await expectStdoutContaining(async () => {
111111
const { default: tree } = await import("./tree");
112-
await tree.parseAsync(["PROJECT", "--json"], { from: "user" });
112+
await tree.parseAsync(["-p", "PROJECT", "--json"], { from: "user" });
113113
}, "doc-1");
114114
});
115115

116116
it("renders tree connectors correctly", async () => {
117117
mockClient.getDocumentTree.mockResolvedValue(sampleTree);
118118

119119
const { default: tree } = await import("./tree");
120-
await tree.parseAsync(["PROJECT"], { from: "user" });
120+
await tree.parseAsync(["-p", "PROJECT"], { from: "user" });
121121

122122
// First child uses ├── connector
123123
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("\u251c\u2500\u2500"));

apps/cli/src/commands/document/tree.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { type Entity } from "backlog-js";
44
import consola from "consola";
55
import { BeeCommand, ENV_AUTH, ENV_PROJECT } from "../../lib/bee-command";
66
import * as opt from "../../lib/common-options";
7+
import { resolveOptions } from "../../lib/required-option";
78

89
const renderNode = (
910
node: Entity.Document.DocumentTreeNode,
@@ -36,18 +37,19 @@ const renderTree = (children: Entity.Document.DocumentTreeNode[]): string[] => {
3637
const tree = new BeeCommand("tree")
3738
.summary("Display document tree")
3839
.description(`Shows the hierarchical structure of documents with tree-style indentation.`)
39-
.argument("[project]", "Project ID or project key", process.env.BACKLOG_PROJECT)
40+
.addOption(opt.project())
4041
.addOption(opt.json())
4142
.addOption(opt.space())
4243
.envVars([...ENV_AUTH, ENV_PROJECT])
4344
.examples([
44-
{ description: "Show document tree", command: "bee document tree PROJECT" },
45-
{ description: "Output as JSON", command: "bee document tree PROJECT --json" },
45+
{ description: "Show document tree", command: "bee document tree -p PROJECT" },
46+
{ description: "Output as JSON", command: "bee document tree -p PROJECT --json" },
4647
])
47-
.action(async (project, opts) => {
48+
.action(async (opts, cmd) => {
49+
await resolveOptions(cmd);
4850
const { client } = await getClient(opts.space);
4951

50-
const docTree = await client.getDocumentTree(project);
52+
const docTree = await client.getDocumentTree(opts.project);
5153

5254
outputResult(docTree, opts, (data) => {
5355
if (!data.activeTree || data.activeTree.children.length === 0) {

apps/cli/src/commands/issue-type/list.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe("issue-type list", () => {
1616
it("displays issue type list in tabular format", async () => {
1717
mockClient.getIssueTypes.mockResolvedValue(sampleIssueTypes);
1818

19-
await parseCommand(() => import("./list"), ["TEST"]);
19+
await parseCommand(() => import("./list"), ["-p", "TEST"]);
2020

2121
expect(mockClient.getIssueTypes).toHaveBeenCalledWith("TEST");
2222
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("ID"));
@@ -27,15 +27,15 @@ describe("issue-type list", () => {
2727
it("shows message when no issue types found", async () => {
2828
mockClient.getIssueTypes.mockResolvedValue([]);
2929

30-
await parseCommand(() => import("./list"), ["TEST"]);
30+
await parseCommand(() => import("./list"), ["-p", "TEST"]);
3131

3232
expect(consola.info).toHaveBeenCalledWith("No issue types found.");
3333
});
3434

3535
it("displays color column", async () => {
3636
mockClient.getIssueTypes.mockResolvedValue(sampleIssueTypes);
3737

38-
await parseCommand(() => import("./list"), ["TEST"]);
38+
await parseCommand(() => import("./list"), ["-p", "TEST"]);
3939

4040
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("#e30000"));
4141
});
@@ -44,7 +44,7 @@ describe("issue-type list", () => {
4444
"outputs JSON when --json flag is set",
4545
itOutputsJson(
4646
() => import("./list"),
47-
["TEST", "--json"],
47+
["-p", "TEST", "--json"],
4848
"Bug",
4949
() => mockClient.getIssueTypes.mockResolvedValue(sampleIssueTypes),
5050
),

apps/cli/src/commands/issue-type/list.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,24 @@ import { type Row, outputResult, printTable } from "@repo/cli-utils";
33
import consola from "consola";
44
import { BeeCommand, ENV_AUTH, ENV_PROJECT } from "../../lib/bee-command";
55
import * as opt from "../../lib/common-options";
6+
import { resolveOptions } from "../../lib/required-option";
67

78
const list = new BeeCommand("list")
89
.summary("List issue types")
910
.description(`Issue types categorize issues and are displayed with their associated color.`)
10-
.argument("[project]", "Project ID or project key")
11+
.addOption(opt.project())
1112
.addOption(opt.json())
1213
.addOption(opt.space())
1314
.envVars([...ENV_AUTH, ENV_PROJECT])
1415
.examples([
15-
{ description: "List all issue types", command: "bee issue-type list PROJECT" },
16-
{ description: "Output as JSON", command: "bee issue-type list PROJECT --json" },
16+
{ description: "List all issue types", command: "bee issue-type list -p PROJECT" },
17+
{ description: "Output as JSON", command: "bee issue-type list -p PROJECT --json" },
1718
])
18-
.action(async (project, opts) => {
19+
.action(async (opts, cmd) => {
20+
await resolveOptions(cmd);
1921
const { client } = await getClient(opts.space);
2022

21-
const issueTypes = await client.getIssueTypes(project);
23+
const issueTypes = await client.getIssueTypes(opts.project);
2224

2325
outputResult(issueTypes, opts, (data) => {
2426
if (data.length === 0) {

apps/cli/src/commands/milestone/list.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe("milestone list", () => {
3030
it("displays milestone list in tabular format", async () => {
3131
mockClient.getVersions.mockResolvedValue(sampleMilestones);
3232

33-
await parseCommand(() => import("./list"), ["TEST"]);
33+
await parseCommand(() => import("./list"), ["-p", "TEST"]);
3434

3535
expect(mockClient.getVersions).toHaveBeenCalledWith("TEST");
3636
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("ID"));
@@ -41,15 +41,15 @@ describe("milestone list", () => {
4141
it("shows message when no milestones found", async () => {
4242
mockClient.getVersions.mockResolvedValue([]);
4343

44-
await parseCommand(() => import("./list"), ["TEST"]);
44+
await parseCommand(() => import("./list"), ["-p", "TEST"]);
4545

4646
expect(consola.info).toHaveBeenCalledWith("No milestones found.");
4747
});
4848

4949
it("displays archived milestone as Yes and non-archived as No", async () => {
5050
mockClient.getVersions.mockResolvedValue(sampleMilestones);
5151

52-
await parseCommand(() => import("./list"), ["TEST"]);
52+
await parseCommand(() => import("./list"), ["-p", "TEST"]);
5353

5454
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("Yes"));
5555
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("No"));
@@ -67,7 +67,7 @@ describe("milestone list", () => {
6767
},
6868
]);
6969

70-
await parseCommand(() => import("./list"), ["TEST"]);
70+
await parseCommand(() => import("./list"), ["-p", "TEST"]);
7171

7272
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("v3.0.0"));
7373
});
@@ -76,7 +76,7 @@ describe("milestone list", () => {
7676
"outputs JSON when --json flag is set",
7777
itOutputsJson(
7878
() => import("./list"),
79-
["TEST", "--json"],
79+
["-p", "TEST", "--json"],
8080
"v1.0.0",
8181
() => mockClient.getVersions.mockResolvedValue(sampleMilestones),
8282
),

apps/cli/src/commands/milestone/list.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,26 @@ import { type Row, outputResult, printTable } from "@repo/cli-utils";
33
import consola from "consola";
44
import { BeeCommand, ENV_AUTH, ENV_PROJECT } from "../../lib/bee-command";
55
import * as opt from "../../lib/common-options";
6+
import { resolveOptions } from "../../lib/required-option";
67

78
const list = new BeeCommand("list")
89
.summary("List milestones")
910
.description(
1011
`Milestones (versions) track release schedules and group issues by development cycle.`,
1112
)
12-
.argument("[project]", "Project ID or project key")
13+
.addOption(opt.project())
1314
.addOption(opt.json())
1415
.addOption(opt.space())
1516
.envVars([...ENV_AUTH, ENV_PROJECT])
1617
.examples([
17-
{ description: "List all milestones", command: "bee milestone list PROJECT" },
18-
{ description: "Output as JSON", command: "bee milestone list PROJECT --json" },
18+
{ description: "List all milestones", command: "bee milestone list -p PROJECT" },
19+
{ description: "Output as JSON", command: "bee milestone list -p PROJECT --json" },
1920
])
20-
.action(async (project, opts) => {
21+
.action(async (opts, cmd) => {
22+
await resolveOptions(cmd);
2123
const { client } = await getClient(opts.space);
2224

23-
const milestones = await client.getVersions(project);
25+
const milestones = await client.getVersions(opts.project);
2426

2527
outputResult(milestones, opts, (data) => {
2628
if (data.length === 0) {

apps/cli/src/commands/project/activities.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe("project activities", () => {
3838
]);
3939

4040
const { default: activities } = await import("./activities");
41-
await activities.parseAsync(["PROJ1"], { from: "user" });
41+
await activities.parseAsync(["-p", "PROJ1"], { from: "user" });
4242

4343
expect(mockClient.getProjectActivities).toHaveBeenCalledWith("PROJ1", expect.any(Object));
4444
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("2024-01-15"));
@@ -51,7 +51,7 @@ describe("project activities", () => {
5151
mockClient.getProjectActivities.mockResolvedValue([]);
5252

5353
const { default: activities } = await import("./activities");
54-
await activities.parseAsync(["PROJ1"], { from: "user" });
54+
await activities.parseAsync(["-p", "PROJ1"], { from: "user" });
5555

5656
expect(consola.info).toHaveBeenCalledWith("No activities found.");
5757
});
@@ -61,7 +61,7 @@ describe("project activities", () => {
6161

6262
const { default: activities } = await import("./activities");
6363
await activities.parseAsync(
64-
["PROJ1", "--activity-type", "1", "--activity-type", "2", "--activity-type", "3"],
64+
["-p", "PROJ1", "--activity-type", "1", "--activity-type", "2", "--activity-type", "3"],
6565
{ from: "user" },
6666
);
6767

@@ -77,7 +77,7 @@ describe("project activities", () => {
7777
mockClient.getProjectActivities.mockResolvedValue([]);
7878

7979
const { default: activities } = await import("./activities");
80-
await activities.parseAsync(["PROJ1", "--count", "50"], { from: "user" });
80+
await activities.parseAsync(["-p", "PROJ1", "--count", "50"], { from: "user" });
8181

8282
expect(mockClient.getProjectActivities).toHaveBeenCalledWith(
8383
"PROJ1",
@@ -97,7 +97,7 @@ describe("project activities", () => {
9797
]);
9898

9999
const { default: activities } = await import("./activities");
100-
await activities.parseAsync(["PROJ1"], { from: "user" });
100+
await activities.parseAsync(["-p", "PROJ1"], { from: "user" });
101101

102102
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("Unknown"));
103103
});
@@ -114,7 +114,7 @@ describe("project activities", () => {
114114
]);
115115

116116
const { default: activities } = await import("./activities");
117-
await activities.parseAsync(["PROJ1"], { from: "user" });
117+
await activities.parseAsync(["-p", "PROJ1"], { from: "user" });
118118

119119
expect(consola.log).toHaveBeenCalledWith(expect.stringContaining("User"));
120120
});
@@ -132,7 +132,7 @@ describe("project activities", () => {
132132

133133
await expectStdoutContaining(async () => {
134134
const { default: activities } = await import("./activities");
135-
await activities.parseAsync(["PROJ1", "--json"], { from: "user" });
135+
await activities.parseAsync(["-p", "PROJ1", "--json"], { from: "user" });
136136
}, "Test");
137137
});
138138
});

0 commit comments

Comments
 (0)