@bdsqqq/lnr-cli 1.5.0 → 2.0.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.
Files changed (47) hide show
  1. package/package.json +2 -3
  2. package/src/bench-lnr-overhead.ts +160 -0
  3. package/src/e2e-mutations.test.ts +378 -0
  4. package/src/e2e-readonly.test.ts +103 -0
  5. package/src/generated/doc.ts +270 -0
  6. package/src/generated/issue.ts +807 -0
  7. package/src/generated/label.ts +273 -0
  8. package/src/generated/project.ts +596 -0
  9. package/src/generated/template.ts +157 -0
  10. package/src/hand-crafted/issue.ts +27 -0
  11. package/src/lib/adapters/doc.ts +14 -0
  12. package/src/lib/adapters/index.ts +4 -0
  13. package/src/lib/adapters/issue.ts +32 -0
  14. package/src/lib/adapters/label.ts +20 -0
  15. package/src/lib/adapters/project.ts +23 -0
  16. package/src/lib/arktype-config.ts +18 -0
  17. package/src/lib/command-introspection.ts +97 -0
  18. package/src/lib/dispatch-effects.test.ts +297 -0
  19. package/src/lib/error.ts +37 -1
  20. package/src/lib/operation-spec.test.ts +317 -0
  21. package/src/lib/operation-spec.ts +11 -0
  22. package/src/lib/operation-specs.ts +21 -0
  23. package/src/lib/output.test.ts +3 -1
  24. package/src/lib/output.ts +1 -296
  25. package/src/lib/renderers/comments.ts +300 -0
  26. package/src/lib/renderers/detail.ts +61 -0
  27. package/src/lib/renderers/index.ts +2 -0
  28. package/src/router/agent-sessions.ts +253 -0
  29. package/src/router/auth.ts +6 -5
  30. package/src/router/config.ts +7 -6
  31. package/src/router/contract.test.ts +364 -0
  32. package/src/router/cycles.ts +372 -95
  33. package/src/router/git-automation-states.ts +355 -0
  34. package/src/router/git-automation-target-branches.ts +309 -0
  35. package/src/router/index.ts +26 -8
  36. package/src/router/initiatives.ts +260 -0
  37. package/src/router/me.ts +8 -7
  38. package/src/router/notifications.ts +176 -0
  39. package/src/router/roadmaps.ts +172 -0
  40. package/src/router/search.ts +7 -6
  41. package/src/router/teams.ts +82 -24
  42. package/src/router/users.ts +126 -0
  43. package/src/router/views.ts +399 -0
  44. package/src/router/docs.ts +0 -153
  45. package/src/router/issues.ts +0 -600
  46. package/src/router/labels.ts +0 -192
  47. package/src/router/projects.ts +0 -220
@@ -0,0 +1,273 @@
1
+ /**
2
+ * GENERATED FILE - DO NOT EDIT
3
+ * Regenerate with: bun run packages/codegen/generate-commands.ts
4
+ */
5
+
6
+ import "../lib/arktype-config";
7
+ import { type } from "arktype";
8
+ import {
9
+ getClient,
10
+ listLabels,
11
+ getLabel,
12
+ createLabel,
13
+ updateLabel,
14
+ deleteLabel,
15
+ resolveTeamByKey,
16
+ createSubscription,
17
+ deleteSubscription,
18
+ findUserSubscription,
19
+ type Label,
20
+ } from "@bdsqqq/lnr-core";
21
+ import { router, procedure } from "../router/trpc";
22
+ import { handleApiError, exitWithError, EXIT_CODES } from "../lib/error";
23
+ import type { OperationSpec } from "../lib/operation-spec";
24
+ import {
25
+ outputJson,
26
+ outputQuiet,
27
+ outputTable,
28
+ getOutputFormat,
29
+ truncate,
30
+ type OutputOptions,
31
+ type TableColumn,
32
+ } from "../lib/output";
33
+ import { outputDetail } from "../lib/renderers/detail";
34
+ import { labelToDetail } from "../lib/adapters";
35
+
36
+
37
+ export const listLabelsInput = type({
38
+ "team?": type("string").describe("filter by team key"),
39
+ "json?": type("boolean").describe("output as json"),
40
+ "quiet?": type("boolean").describe("output ids only"),
41
+ "verbose?": type("boolean").describe("show all columns"),
42
+ });
43
+
44
+ export const labelInput = type({
45
+ id: type("string").configure({ positional: true }).describe("label id or 'new'"),
46
+ "json?": type("boolean").describe("output as json"),
47
+ "delete?": type("boolean").describe("delete the label"),
48
+ "team?": type("string").describe("team key (required for new)"),
49
+ "name?": type("string").describe("label name (required for new)"),
50
+ "description?": type("string").describe("label description"),
51
+ "color?": type("string").describe("hex color code"),
52
+ });
53
+
54
+ type LabelInput = typeof labelInput.infer;
55
+
56
+
57
+
58
+ const labelColumns: TableColumn<Label>[] = [
59
+ { header: "ID", value: (l) => l.id.slice(0, 8), width: 10 },
60
+ { header: "NAME", value: (l) => truncate(l.name, 30), width: 30 },
61
+ { header: "COLOR", value: (l) => l.color ?? "-", width: 10 },
62
+ { header: "DESCRIPTION", value: (l) => truncate(l.description ?? "-", 40), width: 40 },
63
+ ];
64
+
65
+ export const labelOperations = ["create", "read", "update", "delete"] as const;
66
+ type Operation = (typeof labelOperations)[number];
67
+
68
+ export const labelMutationFlags: readonly (keyof LabelInput)[] = [
69
+ "name", "color", "description"
70
+ ] as const;
71
+
72
+ export function inferOperation(input: LabelInput): Operation {
73
+ if (input.id === "new") return "create";
74
+ if (input.delete) return "delete";
75
+
76
+ for (const flag of labelMutationFlags) {
77
+ if (input[flag] !== undefined) return "update";
78
+ }
79
+
80
+ return "read";
81
+ }
82
+
83
+ export const labelOperationSpec: OperationSpec<LabelInput, Operation> = {
84
+ command: "label",
85
+ operations: labelOperations,
86
+ mutationFlags: labelMutationFlags,
87
+ inferOperation,
88
+ };
89
+
90
+ async function handleListLabels(
91
+ input: typeof listLabelsInput.infer
92
+ ): Promise<void> {
93
+ try {
94
+ const client = getClient();
95
+
96
+ const outputOpts: OutputOptions = {
97
+ format: input.json ? "json" : input.quiet ? "quiet" : undefined,
98
+ verbose: input.verbose,
99
+ };
100
+ const format = getOutputFormat(outputOpts);
101
+
102
+ let teamId: string | undefined;
103
+ if (input.team) {
104
+ teamId = await resolveTeamByKey(client, input.team);
105
+ }
106
+
107
+ const labels = await listLabels(client, teamId);
108
+
109
+ if (format === "json") {
110
+ outputJson(labels);
111
+ return;
112
+ }
113
+
114
+ if (format === "quiet") {
115
+ outputQuiet(labels.map((l) => l.id));
116
+ return;
117
+ }
118
+
119
+ outputTable(labels, labelColumns, outputOpts);
120
+ } catch (error) {
121
+ handleApiError(error);
122
+ }
123
+ }
124
+
125
+ async function handleShowLabel(
126
+ id: string,
127
+ input: LabelInput
128
+ ): Promise<void> {
129
+ try {
130
+ const client = getClient();
131
+
132
+ const outputOpts: OutputOptions = {
133
+ format: input.json ? "json" : undefined,
134
+ };
135
+ const format = getOutputFormat(outputOpts);
136
+
137
+ const label = await getLabel(client, id);
138
+
139
+ if (!label) {
140
+ exitWithError(`label "${id}" not found`, undefined, EXIT_CODES.NOT_FOUND);
141
+ }
142
+
143
+ if (format === "json") {
144
+ outputJson(label);
145
+ return;
146
+ }
147
+
148
+ outputDetail(labelToDetail(label));
149
+ } catch (error) {
150
+ handleApiError(error);
151
+ }
152
+ }
153
+
154
+ async function handleUpdateLabel(
155
+ id: string,
156
+ input: LabelInput
157
+ ): Promise<void> {
158
+ try {
159
+ const client = getClient();
160
+
161
+ const updatePayload: {
162
+ name?: string;
163
+ color?: string;
164
+ description?: string;
165
+ } = {};
166
+
167
+ if (input.name !== undefined) updatePayload.name = input.name;
168
+ if (input.color !== undefined) updatePayload.color = input.color;
169
+ if (input.description !== undefined) updatePayload.description = input.description;
170
+
171
+ if (Object.keys(updatePayload).length > 0) {
172
+ const success = await updateLabel(client, id, updatePayload);
173
+ if (!success) {
174
+ exitWithError(`label "${id}" not found`, undefined, EXIT_CODES.NOT_FOUND);
175
+ }
176
+ console.log(`updated label: ${id}`);
177
+ }
178
+ } catch (error) {
179
+ handleApiError(error);
180
+ }
181
+ }
182
+
183
+ async function handleCreateLabel(input: LabelInput): Promise<void> {
184
+ if (!input.name) {
185
+ exitWithError("--name is required", 'usage: lnr label new --name "..." --team <key>');
186
+ }
187
+
188
+ if (!input.team) {
189
+ exitWithError("--team is required", 'usage: lnr label new --name "..." --team <key>');
190
+ }
191
+
192
+ try {
193
+ const client = getClient();
194
+
195
+ const teamId = await resolveTeamByKey(client, input.team);
196
+
197
+ const label = await createLabel(client, {
198
+ name: input.name,
199
+ teamId,
200
+ color: input.color,
201
+ description: input.description,
202
+ });
203
+
204
+ if (label) {
205
+ console.log(`created label: ${label.name}`);
206
+ } else {
207
+ exitWithError("failed to create label");
208
+ }
209
+ } catch (error) {
210
+ handleApiError(error);
211
+ }
212
+ }
213
+
214
+ async function handleDeleteLabel(
215
+ id: string,
216
+ _input: LabelInput
217
+ ): Promise<void> {
218
+ try {
219
+ const client = getClient();
220
+ const success = await deleteLabel(client, id);
221
+
222
+ if (!success) {
223
+ exitWithError(`label "${id}" not found`, undefined, EXIT_CODES.NOT_FOUND);
224
+ }
225
+
226
+ console.log(`deleted label: ${id}`);
227
+ } catch (error) {
228
+ handleApiError(error);
229
+ }
230
+ }
231
+
232
+
233
+
234
+
235
+
236
+ export const generatedLabelsRouter = router({
237
+ labels: procedure
238
+ .meta({
239
+ description: "list labels",
240
+
241
+ })
242
+ .input(listLabelsInput)
243
+ .query(async ({ input }) => {
244
+ await handleListLabels(input);
245
+ }),
246
+
247
+ label: procedure
248
+ .meta({
249
+ description: "show or update a label, or create with 'new'",
250
+ })
251
+ .input(labelInput)
252
+ .mutation(async ({ input }) => {
253
+ const operation = inferOperation(input);
254
+
255
+ switch (operation) {
256
+ case "create":
257
+ await handleCreateLabel(input);
258
+ break;
259
+ case "delete":
260
+ await handleDeleteLabel(input.id, input);
261
+ break;
262
+
263
+ case "update":
264
+ await handleUpdateLabel(input.id, input);
265
+ break;
266
+ case "read":
267
+ default:
268
+ await handleShowLabel(input.id, input);
269
+ break;
270
+ }
271
+ }),
272
+
273
+ });