@h-rig/cli 0.0.6-alpha.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/README.md +30 -0
  2. package/dist/bin/build-rig-binaries.js +107 -0
  3. package/dist/bin/rig.js +9330 -0
  4. package/dist/src/commands/_authority-runs.js +110 -0
  5. package/dist/src/commands/_connection-state.js +123 -0
  6. package/dist/src/commands/_doctor-checks.js +501 -0
  7. package/dist/src/commands/_operator-view.js +322 -0
  8. package/dist/src/commands/_parsers.js +107 -0
  9. package/dist/src/commands/_paths.js +50 -0
  10. package/dist/src/commands/_pi-install.js +184 -0
  11. package/dist/src/commands/_policy.js +79 -0
  12. package/dist/src/commands/_preflight.js +460 -0
  13. package/dist/src/commands/_probes.js +13 -0
  14. package/dist/src/commands/_run-driver-helpers.js +289 -0
  15. package/dist/src/commands/_server-client.js +364 -0
  16. package/dist/src/commands/_snapshot-upload.js +313 -0
  17. package/dist/src/commands/_task-picker.js +48 -0
  18. package/dist/src/commands/agent.js +497 -0
  19. package/dist/src/commands/browser.js +890 -0
  20. package/dist/src/commands/connect.js +180 -0
  21. package/dist/src/commands/dist.js +402 -0
  22. package/dist/src/commands/doctor.js +511 -0
  23. package/dist/src/commands/github.js +276 -0
  24. package/dist/src/commands/inbox.js +160 -0
  25. package/dist/src/commands/init.js +1254 -0
  26. package/dist/src/commands/inspect.js +174 -0
  27. package/dist/src/commands/inspector.js +256 -0
  28. package/dist/src/commands/plugin.js +167 -0
  29. package/dist/src/commands/profile-and-review.js +178 -0
  30. package/dist/src/commands/queue.js +197 -0
  31. package/dist/src/commands/remote.js +507 -0
  32. package/dist/src/commands/repo-git-harness.js +221 -0
  33. package/dist/src/commands/run.js +753 -0
  34. package/dist/src/commands/server.js +368 -0
  35. package/dist/src/commands/setup.js +681 -0
  36. package/dist/src/commands/task-report-bug.js +1083 -0
  37. package/dist/src/commands/task-run-driver.js +1933 -0
  38. package/dist/src/commands/task.js +1325 -0
  39. package/dist/src/commands/test.js +39 -0
  40. package/dist/src/commands/workspace.js +123 -0
  41. package/dist/src/commands.js +9012 -0
  42. package/dist/src/index.js +9348 -0
  43. package/dist/src/launcher.js +131 -0
  44. package/dist/src/report-bug.js +260 -0
  45. package/dist/src/runner.js +272 -0
  46. package/dist/src/withMutedConsole.js +42 -0
  47. package/package.json +31 -0
@@ -0,0 +1,1083 @@
1
+ // @bun
2
+ // packages/cli/src/commands/task-report-bug.ts
3
+ import { existsSync as existsSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
4
+ import { resolve as resolve3 } from "path";
5
+ import * as clack from "@clack/prompts";
6
+ import pc from "picocolors";
7
+
8
+ // packages/cli/src/runner.ts
9
+ import { EventBus } from "@rig/runtime/control-plane/runtime/events";
10
+ import { CliError } from "@rig/runtime/control-plane/errors";
11
+ import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
12
+ import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
13
+ import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
14
+ import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
15
+ import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
16
+ function formatCommand(parts) {
17
+ return parts.map((part) => /[^a-zA-Z0-9_./:-]/.test(part) ? JSON.stringify(part) : part).join(" ");
18
+ }
19
+ function takeFlag(args, flag) {
20
+ const rest = [];
21
+ let value = false;
22
+ for (const arg of args) {
23
+ if (arg === flag) {
24
+ value = true;
25
+ continue;
26
+ }
27
+ rest.push(arg);
28
+ }
29
+ return { value, rest };
30
+ }
31
+ function takeOption(args, option) {
32
+ const rest = [];
33
+ let value;
34
+ for (let index = 0;index < args.length; index += 1) {
35
+ const current = args[index];
36
+ if (current === option) {
37
+ const next = args[index + 1];
38
+ if (!next || next.startsWith("-")) {
39
+ throw new CliError(`Missing value for ${option}`);
40
+ }
41
+ value = next;
42
+ index += 1;
43
+ continue;
44
+ }
45
+ if (current !== undefined) {
46
+ rest.push(current);
47
+ }
48
+ }
49
+ return { value, rest };
50
+ }
51
+ function requireNoExtraArgs(args, usage) {
52
+ if (args.length > 0) {
53
+ throw new CliError(`Unexpected arguments: ${args.join(" ")}
54
+ Usage: ${usage}`);
55
+ }
56
+ }
57
+
58
+ // packages/cli/src/commands/task-report-bug.ts
59
+ import {
60
+ appendJsonlRecord,
61
+ readJsonFile,
62
+ readJsonlFile,
63
+ writeJsonFile
64
+ } from "@rig/runtime/control-plane/authority-files";
65
+ import { runCapture, unique } from "@rig/runtime/control-plane/native/utils";
66
+
67
+ // packages/cli/src/commands/_paths.ts
68
+ import { resolve } from "path";
69
+ import { resolveMonorepoRoot } from "@rig/runtime/control-plane/native/utils";
70
+ function resolveControlPlaneMonorepoRoot(projectRoot) {
71
+ return resolveMonorepoRoot(projectRoot);
72
+ }
73
+ function resolveControlPlaneTaskConfigPath(projectRoot) {
74
+ return resolve(resolveControlPlaneMonorepoRoot(projectRoot), ".rig", "task-config.json");
75
+ }
76
+
77
+ // packages/cli/src/report-bug.ts
78
+ import { copyFileSync, existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
79
+ import { basename, extname, join, resolve as resolve2 } from "path";
80
+ function slugifyBugTitle(value) {
81
+ const slug = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80).replace(/-+$/g, "");
82
+ return slug || "bug-report";
83
+ }
84
+ function defaultBrowserBugProfile(title) {
85
+ return `hp-next-${slugifyBugTitle(title)}`;
86
+ }
87
+ function defaultBrowserBugOptions(environment) {
88
+ if (environment === "shared-dev") {
89
+ return {
90
+ preset: "hp-next-shared",
91
+ attachUrl: "http://127.0.0.1:9341",
92
+ stateDir: ".tmp/rig-browser/hp-next-shared"
93
+ };
94
+ }
95
+ return {
96
+ preset: "hp-next-local",
97
+ attachUrl: "http://127.0.0.1:9333",
98
+ stateDir: ".tmp/rig-browser/hp-next"
99
+ };
100
+ }
101
+ function createBugReportFiles(input) {
102
+ const slug = slugifyBugTitle(input.slug || input.title);
103
+ const outputDir = resolve2(input.projectRoot, input.outputRoot);
104
+ const reportDir = resolve2(outputDir, slug);
105
+ const assetDir = join(reportDir, "assets");
106
+ const screenshotDir = join(reportDir, "screenshots");
107
+ const taskPath = join(reportDir, "task.md");
108
+ const browserRequired = input.browserRequired ?? true;
109
+ const browserPath = browserRequired ? join(reportDir, "browser.json") : null;
110
+ const manifestPath = join(reportDir, "manifest.json");
111
+ if (existsSync(reportDir)) {
112
+ if (!input.overwrite) {
113
+ throw new Error(`Bug report directory already exists: ${reportDir}`);
114
+ }
115
+ rmSync(reportDir, { recursive: true, force: true });
116
+ }
117
+ mkdirSync(assetDir, { recursive: true });
118
+ mkdirSync(screenshotDir, { recursive: true });
119
+ const copiedScreenshots = copyEvidenceFiles(input.projectRoot, screenshotDir, input.screenshots ?? [], "screenshot");
120
+ const copiedAssets = copyEvidenceFiles(input.projectRoot, assetDir, input.assets ?? [], "asset");
121
+ const manifestAssets = [
122
+ ...copiedScreenshots.map((name) => ({
123
+ path: `screenshots/${name}`,
124
+ type: mediaTypeForFileName(name)
125
+ })),
126
+ ...copiedAssets.map((name) => ({
127
+ path: `assets/${name}`,
128
+ type: mediaTypeForFileName(name)
129
+ }))
130
+ ];
131
+ const browser = browserRequired ? buildBrowserBlock(input) : null;
132
+ const manifest = {
133
+ schema_version: 1,
134
+ slug,
135
+ issue_id: input.issueId ?? null,
136
+ title: input.title,
137
+ environment: input.environment,
138
+ url: input.url,
139
+ viewport: input.viewport,
140
+ task_file: "task.md",
141
+ browser_file: browserRequired ? "browser.json" : null,
142
+ screenshots: copiedScreenshots.map((name) => `screenshots/${name}`),
143
+ assets: manifestAssets,
144
+ created_at: new Date().toISOString(),
145
+ platform: process.platform,
146
+ arch: process.arch
147
+ };
148
+ writeFileSync(join(assetDir, "README.md"), buildAssetReadme(input.title), "utf8");
149
+ writeFileSync(join(screenshotDir, "README.md"), buildScreenshotReadme(input.title), "utf8");
150
+ if (browserPath && browser) {
151
+ writeFileSync(browserPath, `${JSON.stringify(browser, null, 2)}
152
+ `, "utf8");
153
+ }
154
+ writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
155
+ `, "utf8");
156
+ writeFileSync(taskPath, buildBugReportMarkdown(input, browser, copiedScreenshots, copiedAssets), "utf8");
157
+ return {
158
+ slug,
159
+ reportDir,
160
+ taskPath,
161
+ browserPath,
162
+ manifestPath,
163
+ assetDir,
164
+ screenshotDir,
165
+ copiedAssets,
166
+ copiedScreenshots
167
+ };
168
+ }
169
+ function buildBrowserBlock(input) {
170
+ return {
171
+ browser: {
172
+ required: true,
173
+ preset: input.preset,
174
+ profile: input.profile,
175
+ attach_url: input.attachUrl,
176
+ state_dir: input.stateDir,
177
+ dev_command: "bun run app:dev:browser:hp-next",
178
+ launch_command: "bun run app:start:browser:hp-next",
179
+ check_command: "bun run app:check:browser:hp-next",
180
+ e2e_command: "bun run app:e2e:browser:hp-next",
181
+ mode: input.mode
182
+ }
183
+ };
184
+ }
185
+ function buildBugReportMarkdown(input, browser, screenshots, assets) {
186
+ const browserRequired = input.browserRequired ?? true;
187
+ const assetEntries = [
188
+ ...screenshots.map((name) => ({ name, path: `screenshots/${name}` })),
189
+ ...assets.map((name) => ({ name, path: `assets/${name}` }))
190
+ ];
191
+ const assetLines = assetEntries.length > 0 ? assetEntries.map(({ name, path }) => formatAssetMarkdown(name, path)) : ["- No assets attached yet. Drag screenshots or videos into `assets/` and link them here."];
192
+ const evidenceLines = input.evidence.length > 0 ? input.evidence.map((item) => `- ${item}`) : ["- Console errors: not captured yet.", "- Failed network requests: not captured yet."];
193
+ return [
194
+ `# ${input.title}`,
195
+ "",
196
+ ...input.issueId ? ["## Task", "", `Task ID: \`${input.issueId}\``, ""] : [],
197
+ "## Summary",
198
+ "",
199
+ input.summary || "TODO: describe the user-visible bug.",
200
+ "",
201
+ ...browser ? [
202
+ "## Browser Block",
203
+ "",
204
+ "```json",
205
+ JSON.stringify(browser, null, 2),
206
+ "```",
207
+ ""
208
+ ] : [],
209
+ "## Environment",
210
+ "",
211
+ `- URL: \`${input.url}\``,
212
+ `- Environment: \`${input.environment}\``,
213
+ ...browserRequired ? [
214
+ `- Preset: \`${input.preset}\``,
215
+ `- Profile: \`${input.profile}\``,
216
+ `- Viewport: \`${input.viewport}\``
217
+ ] : ["- Browser: not required for this report."],
218
+ "",
219
+ "## Reproduction",
220
+ "",
221
+ ...numberedLines(input.steps.length > 0 ? input.steps : ["TODO: add reproduction step."]),
222
+ "",
223
+ "## Expected",
224
+ "",
225
+ input.expected || "TODO: expected behavior.",
226
+ "",
227
+ "## Actual",
228
+ "",
229
+ input.actual || "TODO: actual behavior.",
230
+ "",
231
+ "## Assets",
232
+ "",
233
+ ...assetLines,
234
+ "",
235
+ "## Evidence",
236
+ "",
237
+ ...evidenceLines,
238
+ "",
239
+ "## Agent Handoff",
240
+ "",
241
+ ...input.issueId ? [
242
+ `- Canonical task assets live under \`artifacts/${input.issueId}/bug-report/\`.`,
243
+ `- Start with \`artifacts/${input.issueId}/bug-report/task.md\` and the files in \`artifacts/${input.issueId}/bug-report/assets/\`.`,
244
+ browserRequired ? `- Run \`bun run rig task info --task ${input.issueId}\` to confirm browser wiring before debugging.` : `- Run \`bun run rig task info --task ${input.issueId}\` to confirm scope and artifact links before debugging.`
245
+ ] : [
246
+ "- Draft-only report: convert this into a beads task before assigning it to an agent run."
247
+ ],
248
+ "",
249
+ "## Validation",
250
+ "",
251
+ "```bash",
252
+ ...input.issueId ? [`bun run rig task validate --task ${input.issueId}`] : [],
253
+ ...browserRequired ? [
254
+ "bun run app:check:browser:hp-next",
255
+ "bun run app:e2e:browser:hp-next"
256
+ ] : [],
257
+ "```",
258
+ ""
259
+ ].join(`
260
+ `);
261
+ }
262
+ function numberedLines(items) {
263
+ return items.map((item, index) => `${index + 1}. ${item}`);
264
+ }
265
+ function buildAssetReadme(title) {
266
+ return [
267
+ `# Evidence Assets For ${title}`,
268
+ "",
269
+ "Drag and drop screenshots or videos for this bug report into this directory.",
270
+ "Use stable, descriptive file names such as `login-loading.png`, `chunk-404-network.png`, or `otp-flow.webm`.",
271
+ "Reference images from `../task.md` with Markdown image links and videos with normal file links.",
272
+ ""
273
+ ].join(`
274
+ `);
275
+ }
276
+ function buildScreenshotReadme(title) {
277
+ return [
278
+ `# Screenshots For ${title}`,
279
+ "",
280
+ "Drop legacy screenshot files for this bug report into this directory.",
281
+ "Use `assets/` for new screenshot, video, and mixed evidence attachments.",
282
+ "Reference screenshots from `../task.md` with Markdown image links.",
283
+ ""
284
+ ].join(`
285
+ `);
286
+ }
287
+ function copyEvidenceFiles(projectRoot, targetDir, paths, label) {
288
+ const copied = [];
289
+ const used = new Set;
290
+ for (const rawPath of paths.map((path) => path.trim()).filter(Boolean)) {
291
+ const source = resolve2(projectRoot, rawPath);
292
+ if (!existsSync(source)) {
293
+ throw new Error(`${label} does not exist: ${source}`);
294
+ }
295
+ const targetName = uniqueEvidenceName(rawPath, used, label);
296
+ copyFileSync(source, join(targetDir, targetName));
297
+ copied.push(targetName);
298
+ }
299
+ return copied;
300
+ }
301
+ function uniqueEvidenceName(path, used, fallback) {
302
+ const original = sanitizeFileName(basename(path)) || fallback;
303
+ const extension = extname(original);
304
+ const stem = extension ? original.slice(0, -extension.length) : original;
305
+ let candidate = original;
306
+ let index = 2;
307
+ while (used.has(candidate)) {
308
+ candidate = `${stem}-${index}${extension}`;
309
+ index += 1;
310
+ }
311
+ used.add(candidate);
312
+ return candidate;
313
+ }
314
+ function sanitizeFileName(value) {
315
+ return value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
316
+ }
317
+ function mediaTypeForFileName(fileName) {
318
+ const extension = extname(fileName).toLowerCase();
319
+ if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(extension)) {
320
+ return "image";
321
+ }
322
+ if ([".mp4", ".mov", ".m4v", ".webm", ".avi", ".mkv"].includes(extension)) {
323
+ return "video";
324
+ }
325
+ return "file";
326
+ }
327
+ function formatAssetMarkdown(name, path) {
328
+ return mediaTypeForFileName(name) === "image" ? `- ![${name}](${path})` : `- [${name}](${path})`;
329
+ }
330
+
331
+ // packages/cli/src/commands/task-report-bug.ts
332
+ var BROWSER_BUG_REPORT_VALIDATION = "integration:browser-bug-report-task";
333
+ function shouldColorizeCliOutput() {
334
+ return Boolean(process.stdout.isTTY) && process.env.NO_COLOR === undefined && process.env.TERM !== "dumb";
335
+ }
336
+ async function executeTaskReportBug(context, args) {
337
+ let pending = args;
338
+ const noPromptFlag = takeFlag(pending, "--no-prompt");
339
+ pending = noPromptFlag.rest;
340
+ const noBeadsFlag = takeFlag(pending, "--no-beads");
341
+ pending = noBeadsFlag.rest;
342
+ const browserFlag = takeFlag(pending, "--browser");
343
+ pending = browserFlag.rest;
344
+ const noBrowserFlag = takeFlag(pending, "--no-browser");
345
+ pending = noBrowserFlag.rest;
346
+ if (browserFlag.value && noBrowserFlag.value) {
347
+ throw new CliError2("Pass only one of --browser or --no-browser.");
348
+ }
349
+ const overwriteFlag = takeFlag(pending, "--overwrite");
350
+ pending = overwriteFlag.rest;
351
+ const titleResult = takeOption(pending, "--title");
352
+ pending = titleResult.rest;
353
+ const urlResult = takeOption(pending, "--url");
354
+ pending = urlResult.rest;
355
+ const environmentResult = takeOption(pending, "--environment");
356
+ pending = environmentResult.rest;
357
+ const presetResult = takeOption(pending, "--preset");
358
+ pending = presetResult.rest;
359
+ const profileResult = takeOption(pending, "--profile");
360
+ pending = profileResult.rest;
361
+ const attachUrlResult = takeOption(pending, "--attach-url");
362
+ pending = attachUrlResult.rest;
363
+ const stateDirResult = takeOption(pending, "--state-dir");
364
+ pending = stateDirResult.rest;
365
+ const modeResult = takeOption(pending, "--mode");
366
+ pending = modeResult.rest;
367
+ const viewportResult = takeOption(pending, "--viewport");
368
+ pending = viewportResult.rest;
369
+ const summaryResult = takeOption(pending, "--summary");
370
+ pending = summaryResult.rest;
371
+ const stepsResult = takeOption(pending, "--steps");
372
+ pending = stepsResult.rest;
373
+ const expectedResult = takeOption(pending, "--expected");
374
+ pending = expectedResult.rest;
375
+ const actualResult = takeOption(pending, "--actual");
376
+ pending = actualResult.rest;
377
+ const evidenceResult = takeOption(pending, "--evidence");
378
+ pending = evidenceResult.rest;
379
+ const priorityResult = takeOption(pending, "--priority");
380
+ pending = priorityResult.rest;
381
+ const labelsResult = takeOption(pending, "--labels");
382
+ pending = labelsResult.rest;
383
+ const parentResult = takeOption(pending, "--parent");
384
+ pending = parentResult.rest;
385
+ const assigneeResult = takeOption(pending, "--assignee");
386
+ pending = assigneeResult.rest;
387
+ const ownerResult = takeOption(pending, "--owner");
388
+ pending = ownerResult.rest;
389
+ const screenshotResult = takeRepeatedOption(pending, "--screenshot");
390
+ pending = screenshotResult.rest;
391
+ const assetResult = takeRepeatedOption(pending, "--asset");
392
+ pending = assetResult.rest;
393
+ const videoResult = takeRepeatedOption(pending, "--video");
394
+ pending = videoResult.rest;
395
+ const outputRootResult = takeOption(pending, "--output-dir");
396
+ pending = outputRootResult.rest;
397
+ const slugResult = takeOption(pending, "--slug");
398
+ pending = slugResult.rest;
399
+ requireNoExtraArgs(pending, "bun run rig report-bug [--no-prompt] [--no-beads] [--browser|--no-browser] --title <text> --url <url> [--asset <dragged-file>]");
400
+ let draft = {
401
+ outputRoot: outputRootResult.value || "",
402
+ title: titleResult.value,
403
+ url: urlResult.value,
404
+ browserRequired: !noBrowserFlag.value,
405
+ environment: environmentResult.value || "local",
406
+ preset: presetResult.value,
407
+ profile: profileResult.value,
408
+ attachUrl: attachUrlResult.value,
409
+ stateDir: stateDirResult.value,
410
+ mode: modeResult.value || "persistent",
411
+ viewport: viewportResult.value || "1440x900",
412
+ summary: summaryResult.value,
413
+ steps: splitPromptList(stepsResult.value),
414
+ expected: expectedResult.value,
415
+ actual: actualResult.value,
416
+ evidence: splitPromptList(evidenceResult.value),
417
+ priority: priorityResult.value || "P2",
418
+ labels: splitLabelList(labelsResult.value),
419
+ parent: parentResult.value,
420
+ assignee: assigneeResult.value,
421
+ owner: ownerResult.value,
422
+ screenshots: screenshotResult.values.flatMap(splitCliAssetOption),
423
+ assets: [...assetResult.values, ...videoResult.values].flatMap(splitCliAssetOption),
424
+ slug: slugResult.value,
425
+ overwrite: overwriteFlag.value,
426
+ createBeadsTask: !noBeadsFlag.value
427
+ };
428
+ const shouldPrompt = !noPromptFlag.value;
429
+ if (shouldPrompt) {
430
+ if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
431
+ throw new CliError2("Interactive bug reporting requires a TTY in text mode. Pass --no-prompt with flags for automation.");
432
+ }
433
+ draft = await promptForBugReportDetails(draft);
434
+ }
435
+ const title = requireReportBugValue(draft.title, "--title");
436
+ const url = requireReportBugValue(draft.url, "--url");
437
+ const defaults = defaultBrowserBugOptions(draft.environment);
438
+ const profile = draft.profile || defaultBrowserBugProfile(title);
439
+ const baseInput = {
440
+ projectRoot: context.projectRoot,
441
+ outputRoot: draft.outputRoot || "docs/bug-reports",
442
+ title,
443
+ url,
444
+ browserRequired: draft.browserRequired,
445
+ environment: draft.environment,
446
+ preset: draft.preset || defaults.preset,
447
+ profile,
448
+ attachUrl: draft.attachUrl || defaults.attachUrl,
449
+ stateDir: draft.stateDir || defaults.stateDir,
450
+ mode: draft.mode,
451
+ viewport: draft.viewport,
452
+ summary: draft.summary || "TODO: describe the user-visible bug.",
453
+ steps: draft.steps,
454
+ expected: draft.expected || "TODO: expected behavior.",
455
+ actual: draft.actual || "TODO: actual behavior.",
456
+ evidence: draft.evidence,
457
+ screenshots: resolveBugReportDroppedFiles(context.projectRoot, draft.screenshots),
458
+ assets: resolveBugReportDroppedFiles(context.projectRoot, draft.assets),
459
+ slug: draft.slug,
460
+ overwrite: draft.overwrite
461
+ };
462
+ validateBugReportInputFiles(baseInput);
463
+ if (context.dryRun) {
464
+ const slug = draft.createBeadsTask ? "<new-task-id>/bug-report" : slugifyBugTitle(baseInput.slug || baseInput.title);
465
+ const reportDir = draft.createBeadsTask ? resolve3(resolveControlPlaneMonorepoRoot(context.projectRoot), "artifacts", slug) : resolve3(context.projectRoot, baseInput.outputRoot, slug);
466
+ if (context.outputMode === "text") {
467
+ console.log(draft.createBeadsTask ? `Would create beads task and assets: ${reportDir}` : `Would create bug report: ${reportDir}`);
468
+ }
469
+ return {
470
+ ok: true,
471
+ group: "task",
472
+ command: "report-bug",
473
+ details: { dryRun: true, slug, reportDir }
474
+ };
475
+ }
476
+ const monorepoRoot = draft.createBeadsTask ? resolveControlPlaneMonorepoRoot(context.projectRoot) : null;
477
+ const issueCreation = monorepoRoot ? createBugReportBeadsTask(monorepoRoot, baseInput, draft) : null;
478
+ const issueId = issueCreation?.issueId ?? null;
479
+ const input = {
480
+ ...baseInput,
481
+ projectRoot: monorepoRoot ?? context.projectRoot,
482
+ outputRoot: issueId ? `artifacts/${issueId}` : baseInput.outputRoot,
483
+ slug: issueId ? "bug-report" : baseInput.slug,
484
+ issueId: issueId ?? undefined
485
+ };
486
+ const result = createBugReportFiles(input);
487
+ const reportRelativePath = issueId ? `artifacts/${issueId}/bug-report/task.md` : null;
488
+ const taskConfigPath = issueId ? upsertBugReportTaskConfig(context.projectRoot, issueId, input, reportRelativePath) : null;
489
+ if (issueCreation && monorepoRoot && reportRelativePath) {
490
+ updateBugReportBeadsTask(monorepoRoot, issueCreation, readFileSync(result.taskPath, "utf8"), reportRelativePath, input);
491
+ }
492
+ if (context.outputMode === "text") {
493
+ console.log(issueId ? `Bug task created: ${issueId}` : "Draft bug report created");
494
+ console.log(`Bug report assets: ${result.reportDir}`);
495
+ console.log(`Task: ${result.taskPath}`);
496
+ if (result.browserPath) {
497
+ console.log(`Browser block: ${result.browserPath}`);
498
+ } else {
499
+ console.log("Browser block: not requested");
500
+ }
501
+ console.log(`Evidence assets: ${result.assetDir}`);
502
+ if (taskConfigPath) {
503
+ console.log(`Task config: ${taskConfigPath}`);
504
+ console.log(`Run: bun run rig task info --task ${issueId}`);
505
+ }
506
+ }
507
+ return {
508
+ ok: true,
509
+ group: "task",
510
+ command: "report-bug",
511
+ details: {
512
+ slug: result.slug,
513
+ issueId,
514
+ reportDir: result.reportDir,
515
+ taskPath: result.taskPath,
516
+ browserPath: result.browserPath,
517
+ manifestPath: result.manifestPath,
518
+ assetDir: result.assetDir,
519
+ assets: result.copiedAssets,
520
+ screenshotDir: result.screenshotDir,
521
+ screenshots: result.copiedScreenshots,
522
+ taskConfigPath,
523
+ taskRecordBackend: issueCreation?.backend ?? null
524
+ }
525
+ };
526
+ }
527
+ function validateBugReportInputFiles(input) {
528
+ const files = [
529
+ ...(input.screenshots ?? []).map((path) => ({ path, label: "Screenshot" })),
530
+ ...(input.assets ?? []).map((path) => ({ path, label: "Asset" }))
531
+ ];
532
+ for (const { path, label } of files.map((item) => ({ ...item, path: item.path.trim() })).filter((item) => item.path)) {
533
+ const source = resolve3(input.projectRoot, path);
534
+ if (!existsSync2(source)) {
535
+ throw new CliError2(`${label} does not exist: ${source}`);
536
+ }
537
+ }
538
+ }
539
+ function resolveBugReportDroppedFiles(projectRoot, paths) {
540
+ return paths.map((path) => {
541
+ const trimmed = path.trim();
542
+ return trimmed ? resolve3(projectRoot, trimmed) : trimmed;
543
+ });
544
+ }
545
+ function createBugReportBeadsTask(monorepoRoot, input, draft) {
546
+ const labels = buildBugReportLabels(input, draft);
547
+ const priority = normalizeBugReportPriority(draft.priority);
548
+ const command = [
549
+ "br",
550
+ "create",
551
+ "--title",
552
+ input.title,
553
+ "--type",
554
+ "task",
555
+ "--priority",
556
+ priority,
557
+ "--description",
558
+ buildInitialBugReportDescription(input),
559
+ "--labels",
560
+ labels.join(","),
561
+ "--json"
562
+ ];
563
+ if (draft.parent?.trim()) {
564
+ command.push("--parent", draft.parent.trim());
565
+ }
566
+ if (draft.assignee?.trim()) {
567
+ command.push("--assignee", draft.assignee.trim());
568
+ }
569
+ if (draft.owner?.trim()) {
570
+ command.push("--owner", draft.owner.trim());
571
+ }
572
+ const issue = tryRunBrJson(monorepoRoot, command);
573
+ if (!issue.ok) {
574
+ if (!isRecoverableBeadsJsonlFailure(issue.error)) {
575
+ throw new CliError2(issue.error);
576
+ }
577
+ return {
578
+ issueId: generateBugReportIssueId(monorepoRoot, input.title),
579
+ backend: "jsonl",
580
+ labels,
581
+ priority
582
+ };
583
+ }
584
+ const issueId = issue.value.id?.trim();
585
+ if (!issueId) {
586
+ throw new CliError2("br create did not return an issue id.");
587
+ }
588
+ return {
589
+ issueId,
590
+ backend: "br",
591
+ labels,
592
+ priority
593
+ };
594
+ }
595
+ function updateBugReportBeadsTask(monorepoRoot, issueCreation, taskMarkdown, externalRef, input) {
596
+ if (issueCreation.backend === "jsonl") {
597
+ appendBugReportJsonlTask(monorepoRoot, issueCreation, taskMarkdown, externalRef, input);
598
+ return;
599
+ }
600
+ runBrJson(monorepoRoot, [
601
+ "br",
602
+ "update",
603
+ issueCreation.issueId,
604
+ "--description",
605
+ taskMarkdown,
606
+ "--acceptance-criteria",
607
+ buildBugReportAcceptanceCriteria(input),
608
+ "--external-ref",
609
+ externalRef,
610
+ "--json"
611
+ ]);
612
+ }
613
+ function appendBugReportJsonlTask(monorepoRoot, issueCreation, taskMarkdown, externalRef, input) {
614
+ const now = new Date().toISOString();
615
+ appendJsonlRecord(resolve3(monorepoRoot, ".beads", "issues.jsonl"), {
616
+ id: issueCreation.issueId,
617
+ title: input.title,
618
+ description: taskMarkdown,
619
+ acceptance_criteria: buildBugReportAcceptanceCriteria(input),
620
+ status: "open",
621
+ priority: priorityToNumber(issueCreation.priority),
622
+ issue_type: "task",
623
+ created_at: now,
624
+ created_by: process.env.USER || process.env.USERNAME || "rig",
625
+ updated_at: now,
626
+ external_ref: externalRef,
627
+ source_repo: ".",
628
+ compaction_level: 0,
629
+ original_size: 0,
630
+ labels: issueCreation.labels
631
+ });
632
+ }
633
+ function upsertBugReportTaskConfig(projectRoot, issueId, input, reportRelativePath) {
634
+ const taskConfigPath = resolveControlPlaneTaskConfigPath(projectRoot);
635
+ const browserRequired = input.browserRequired ?? true;
636
+ const entry = {
637
+ description: [
638
+ "Browser bug report generated by `rig report-bug`.",
639
+ "",
640
+ `Canonical report: \`${reportRelativePath}\``
641
+ ].join(`
642
+ `),
643
+ acceptance_criteria: buildBugReportAcceptanceCriteria(input),
644
+ scope: [
645
+ `artifacts/${issueId}/bug-report/**`,
646
+ "hp-next/**",
647
+ "microservices/hp-next-frontend/**",
648
+ "humoongate/humanity/hp-next/**"
649
+ ],
650
+ role: "verifier",
651
+ criticality: "normal"
652
+ };
653
+ if (browserRequired) {
654
+ entry.browser = {
655
+ required: true,
656
+ preset: input.preset,
657
+ profile: input.profile,
658
+ attach_url: input.attachUrl,
659
+ state_dir: input.stateDir,
660
+ dev_command: "bun run app:dev:browser:hp-next",
661
+ launch_command: "bun run app:start:browser:hp-next",
662
+ check_command: "bun run app:check:browser:hp-next",
663
+ e2e_command: "bun run app:e2e:browser:hp-next",
664
+ mode: input.mode
665
+ };
666
+ entry.validation = [BROWSER_BUG_REPORT_VALIDATION];
667
+ }
668
+ appendTaskConfigEntryPreservingText(taskConfigPath, issueId, entry);
669
+ return taskConfigPath;
670
+ }
671
+ function appendTaskConfigEntryPreservingText(taskConfigPath, issueId, entry) {
672
+ const raw = existsSync2(taskConfigPath) ? readFileSync(taskConfigPath, "utf8") : `{}
673
+ `;
674
+ if (raw.includes(`"${issueId}"`)) {
675
+ const parsed = readJsonFile(taskConfigPath, {});
676
+ parsed[issueId] = entry;
677
+ writeJsonFile(taskConfigPath, parsed);
678
+ return;
679
+ }
680
+ const trimmed = raw.trim();
681
+ const serializedEntry = `${JSON.stringify(issueId)}: ${JSON.stringify(entry, null, 2)}`.split(`
682
+ `).map((line) => ` ${line}`).join(`
683
+ `);
684
+ if (!trimmed || trimmed === "{}") {
685
+ writeFileSync2(taskConfigPath, `{
686
+ ${serializedEntry}
687
+ }
688
+ `, "utf8");
689
+ return;
690
+ }
691
+ const insertAt = raw.lastIndexOf("}");
692
+ if (insertAt < 0) {
693
+ throw new CliError2(`Invalid task config JSON object: ${taskConfigPath}`);
694
+ }
695
+ const before = raw.slice(0, insertAt).replace(/\s*$/, "");
696
+ const after = raw.slice(insertAt + 1).trim();
697
+ if (after) {
698
+ throw new CliError2(`Invalid trailing content in task config: ${taskConfigPath}`);
699
+ }
700
+ const comma = before.trim() === "{" ? "" : ",";
701
+ writeFileSync2(taskConfigPath, `${before}${comma}
702
+ ${serializedEntry}
703
+ }
704
+ `, "utf8");
705
+ }
706
+ function buildBugReportLabels(input, draft) {
707
+ return unique([
708
+ "bug",
709
+ "bug-report",
710
+ ...input.browserRequired === false ? [] : ["browser-required"],
711
+ "hp-next",
712
+ `env:${input.environment}`,
713
+ "role:verifier",
714
+ ...draft.labels
715
+ ].map(sanitizeBugReportLabel).filter(Boolean));
716
+ }
717
+ function buildInitialBugReportDescription(input) {
718
+ return [
719
+ input.summary || "Browser bug report generated by `rig report-bug`.",
720
+ "",
721
+ "The detailed task assets are written after task creation under `artifacts/<task-id>/bug-report/`."
722
+ ].join(`
723
+ `);
724
+ }
725
+ function buildBugReportAcceptanceCriteria(input) {
726
+ if (input.browserRequired === false) {
727
+ return [
728
+ "1. Reproduce the bug using the provided steps and task assets.",
729
+ "2. Fix the underlying product or server issue without removing the reported user flow.",
730
+ "3. Preserve all provided task assets and evidence in the task artifact directory.",
731
+ `4. Verify the target URL or endpoint behaves correctly: ${input.url}`,
732
+ "5. Run the generated task validation when available, or document the manual verification performed."
733
+ ].join(`
734
+ `);
735
+ }
736
+ return [
737
+ "1. Reproduce the bug with Rig Browser using the browser block in the task assets.",
738
+ "2. Fix the underlying product or server issue without removing the reported user flow.",
739
+ "3. Preserve all provided screenshots and evidence in the task artifact directory.",
740
+ `4. Verify the target URL renders correctly: ${input.url}`,
741
+ "5. Run the generated task validation plus `bun run app:check:browser:hp-next` and `bun run app:e2e:browser:hp-next`, or document why either browser command cannot run."
742
+ ].join(`
743
+ `);
744
+ }
745
+ function normalizeBugReportPriority(priority) {
746
+ const normalized = priority.trim().toUpperCase();
747
+ return /^P?[0-4]$/.test(normalized) ? normalized : "P2";
748
+ }
749
+ function priorityToNumber(priority) {
750
+ const normalized = normalizeBugReportPriority(priority).replace(/^P/, "");
751
+ return Number.parseInt(normalized, 10);
752
+ }
753
+ function sanitizeBugReportLabel(label) {
754
+ return label.trim().toLowerCase().replace(/\s+/g, "-");
755
+ }
756
+ function generateBugReportIssueId(monorepoRoot, title) {
757
+ const existingIds = new Set(readJsonlFile(resolve3(monorepoRoot, ".beads", "issues.jsonl")).filter((entry) => !!entry && typeof entry === "object").map((entry) => entry.id).filter((id) => typeof id === "string" && id.trim().length > 0));
758
+ const base = slugifyBugTitle(title).replace(/-/g, "").slice(0, 8) || "bug";
759
+ for (let attempt = 0;attempt < 10; attempt += 1) {
760
+ const suffix = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
761
+ const id = `bd-${base}-${suffix}`.slice(0, 48);
762
+ if (!existingIds.has(id)) {
763
+ return id;
764
+ }
765
+ }
766
+ throw new CliError2("Could not generate a unique bug task id.");
767
+ }
768
+ function isRecoverableBeadsJsonlFailure(message) {
769
+ return /Invalid JSON|missing field|JSONL is newer than DB|stale database/i.test(message);
770
+ }
771
+ function runBrJson(cwd, command) {
772
+ const result = tryRunBrJson(cwd, command);
773
+ if (!result.ok) {
774
+ throw new CliError2(result.error);
775
+ }
776
+ return result.value;
777
+ }
778
+ function tryRunBrJson(cwd, command) {
779
+ const result = runCapture(command, cwd);
780
+ if (result.exitCode !== 0) {
781
+ const detail = [result.stderr.trim(), result.stdout.trim()].filter(Boolean).join(`
782
+ `);
783
+ return {
784
+ ok: false,
785
+ error: `beads command failed: ${formatCommand(command)}${detail ? `
786
+ ${detail}` : ""}`
787
+ };
788
+ }
789
+ try {
790
+ return { ok: true, value: JSON.parse(result.stdout) };
791
+ } catch (error) {
792
+ return {
793
+ ok: false,
794
+ error: `beads command did not return JSON: ${formatCommand(command)}
795
+ ${result.stdout.trim() || String(error)}`
796
+ };
797
+ }
798
+ }
799
+ async function promptForBugReportDetails(initial) {
800
+ clack.intro("rig report-bug");
801
+ clack.note([
802
+ "Creates an agent-ready issue with copied evidence files.",
803
+ "When prompted for assets, drag screenshots/videos into the terminal and press Enter."
804
+ ].join(`
805
+ `), "Bug report wizard");
806
+ clack.log.step("1/5 Incident");
807
+ clack.note(bugPromptExampleText("incident"), "Input examples");
808
+ const title = await promptBugText("Bug title", initial.title, {
809
+ required: true,
810
+ placeholder: "Login page never leaves loading"
811
+ });
812
+ const url = await promptBugText("URL to reproduce", initial.url, {
813
+ required: true,
814
+ placeholder: "https://dev.rig.hptestingsite.com/login?next=%2F"
815
+ });
816
+ const environmentChoice = await promptBugSelect("Environment", [
817
+ { value: "local", label: "local", hint: "local hp-next / service fabric" },
818
+ { value: "shared-dev", label: "shared-dev", hint: "shared deployed dev surface" },
819
+ { value: "staging", label: "staging" },
820
+ { value: "production", label: "production" },
821
+ { value: "custom", label: "custom", hint: "enter a custom environment name" }
822
+ ], normalizeBugEnvironmentChoice(initial.environment));
823
+ const environment = environmentChoice === "custom" ? await promptBugText("Custom environment", ["local", "shared-dev", "staging", "production", "custom"].includes(initial.environment) ? undefined : initial.environment, { required: true, placeholder: "qa-dev" }) : environmentChoice;
824
+ const defaults = defaultBrowserBugOptions(environment);
825
+ const summary = await promptBugText("Short summary", initial.summary, {
826
+ placeholder: "Email field never appears after navigating from credentials."
827
+ });
828
+ clack.log.step("2/5 Reproduction");
829
+ clack.note(bugPromptExampleText("reproduction"), "Input examples");
830
+ const steps = splitPromptList(await promptBugText("Repro steps (semicolon-separated)", initial.steps.join("; "), {
831
+ placeholder: "Open /login; Enter qa@example.com; Press Continue"
832
+ }));
833
+ const expected = await promptBugText("Expected behavior", initial.expected, {
834
+ placeholder: "OTP form is visible and accepts a code."
835
+ });
836
+ const actual = await promptBugText("Actual behavior", initial.actual, {
837
+ placeholder: "Loading shell stays forever."
838
+ });
839
+ clack.log.step("3/5 Evidence");
840
+ clack.note(bugPromptExampleText("evidence"), "Input examples");
841
+ const evidence = splitPromptList(await promptBugText("Evidence notes (console/network/API; semicolon-separated)", initial.evidence.join("; "), {
842
+ placeholder: "Console: ChunkLoadError for credentials route; Network: /assets/app.js 404"
843
+ }));
844
+ const assets = splitDroppedAssetList(await promptBugText("Drag screenshots/videos here (Enter to skip)", initial.assets.map(formatDroppedAssetDefault).join(" "), { placeholder: "/Users/me/Desktop/login-loading.png /Users/me/Desktop/otp-flow.webm" }));
845
+ clack.log.step("4/5 Routing");
846
+ clack.note(bugPromptExampleText("routing"), "Input examples");
847
+ const priority = await promptBugSelect("Priority", [
848
+ { value: "P0", label: "P0", hint: "urgent / blocking" },
849
+ { value: "P1", label: "P1", hint: "high" },
850
+ { value: "P2", label: "P2", hint: "normal default" },
851
+ { value: "P3", label: "P3", hint: "low" },
852
+ { value: "P4", label: "P4", hint: "backlog" }
853
+ ], normalizeBugReportPriority(initial.priority || "P2"));
854
+ const labels = splitLabelList(await promptBugText("Extra labels, comma- or semicolon-separated", initial.labels.join(", "), {
855
+ placeholder: "auth, otp, hp-next"
856
+ }));
857
+ const parent = await promptBugText("Parent task or epic id (optional)", initial.parent, {
858
+ placeholder: "bd-auth-epic-123"
859
+ });
860
+ clack.log.step("5/5 Browser");
861
+ clack.note(bugPromptExampleText("browser"), "Input examples");
862
+ const browserRequired = await promptBugConfirm("Does this task need Rig Browser wiring?", initial.browserRequired);
863
+ let viewport = initial.viewport || "1440x900";
864
+ let profile = initial.profile;
865
+ if (browserRequired) {
866
+ clack.note([
867
+ "A profile is the browser state bucket: cookies, localStorage, auth session, and cache.",
868
+ "Use a bug-specific profile for clean login/OTP runs; use a shared profile only when saved auth matters."
869
+ ].join(`
870
+ `), "Rig Browser profile");
871
+ viewport = await promptBugText("Viewport", viewport, { placeholder: "1440x900" });
872
+ profile = await promptBugText("Rig Browser profile", initial.profile || (title ? defaultBrowserBugProfile(title) : undefined), { placeholder: "hp-next-login-loading-clean" });
873
+ } else {
874
+ clack.log.info("Browser wiring skipped. The task will keep assets and steps, but no browser block or browser validation.");
875
+ }
876
+ const outputRoot = initial.createBeadsTask ? initial.outputRoot : await promptBugText("Output directory", initial.outputRoot || "docs/bug-reports");
877
+ const overwrite = await promptBugConfirm("Overwrite if report exists?", initial.overwrite);
878
+ clack.outro("Bug report input captured.");
879
+ return {
880
+ ...initial,
881
+ outputRoot,
882
+ title,
883
+ url,
884
+ browserRequired,
885
+ environment,
886
+ preset: initial.preset || defaults.preset,
887
+ profile,
888
+ attachUrl: initial.attachUrl || defaults.attachUrl,
889
+ stateDir: initial.stateDir || defaults.stateDir,
890
+ viewport,
891
+ summary,
892
+ steps,
893
+ expected,
894
+ actual,
895
+ evidence,
896
+ screenshots: initial.screenshots,
897
+ assets,
898
+ priority,
899
+ labels,
900
+ parent: parent || undefined,
901
+ overwrite
902
+ };
903
+ }
904
+ async function promptBugText(message, defaultValue, options = {}) {
905
+ const result = await clack.text({
906
+ message,
907
+ defaultValue: defaultValue?.trim() ? defaultValue : undefined,
908
+ placeholder: options.placeholder,
909
+ validate: options.required ? (value) => validateRequiredBugPromptValue(value, message) : undefined
910
+ });
911
+ return unwrapClackPrompt(result).trim();
912
+ }
913
+ function validateRequiredBugPromptValue(value, label) {
914
+ return value?.trim() ? undefined : `${label} is required.`;
915
+ }
916
+ function bugPromptExampleText(section, options = {}) {
917
+ const c = pc.createColors(options.color ?? shouldColorizeCliOutput());
918
+ const label = (value) => c.bold(c.cyan(value));
919
+ const example = (value) => c.green(value);
920
+ const note2 = (value) => c.dim(value);
921
+ const rows = {
922
+ incident: [
923
+ ["Bug title", "Login page never leaves loading", "short user-visible failure"],
924
+ ["URL", "https://dev.rig.hptestingsite.com/login?next=%2F", "exact route or endpoint"],
925
+ ["Summary", "Email field never appears after navigating from credentials", "one sentence"]
926
+ ],
927
+ reproduction: [
928
+ ["Steps", "Open /login; Enter qa@example.com; Press Continue", "semicolon-separated"],
929
+ ["Expected", "OTP form is visible and accepts a code"],
930
+ ["Actual", "Loading shell stays forever"]
931
+ ],
932
+ evidence: [
933
+ ["Evidence", "Console: ChunkLoadError for credentials route", "console/network/API notes"],
934
+ ["Assets", "/Users/me/Desktop/login-loading.png /Users/me/Desktop/otp-flow.webm", "drag files into terminal"]
935
+ ],
936
+ routing: [
937
+ ["Priority", "P1 when login is blocked for all testers"],
938
+ ["Labels", "auth, otp, hp-next", "comma- or semicolon-separated"],
939
+ ["Parent", "bd-auth-epic-123", "optional"]
940
+ ],
941
+ browser: [
942
+ ["Browser needed", "Yes for console/network/auth/visual bugs"],
943
+ ["Browser not needed", "No for API-only/server/config bugs"],
944
+ ["Profile", "hp-next-login-loading-clean", "clean, bug-specific auth state"],
945
+ ["Viewport", "1440x900", "match the failing screen size"]
946
+ ]
947
+ };
948
+ return rows[section].map(([name, value, detail]) => {
949
+ const suffix = detail ? ` ${note2(`(${detail})`)}` : "";
950
+ return `${label(name)}: ${example(value)}${suffix}`;
951
+ }).join(`
952
+ `);
953
+ }
954
+ async function promptBugConfirm(message, initialValue) {
955
+ const result = await clack.confirm({
956
+ message,
957
+ initialValue
958
+ });
959
+ return unwrapClackPrompt(result);
960
+ }
961
+ async function promptBugSelect(message, options, initialValue) {
962
+ const result = await clack.select({
963
+ message,
964
+ options,
965
+ initialValue
966
+ });
967
+ return unwrapClackPrompt(result);
968
+ }
969
+ function unwrapClackPrompt(result) {
970
+ if (clack.isCancel(result)) {
971
+ clack.cancel("Bug report cancelled.");
972
+ throw new CliError2("Bug report cancelled by user.");
973
+ }
974
+ return result;
975
+ }
976
+ function normalizeBugEnvironmentChoice(environment) {
977
+ return environment === "local" || environment === "shared-dev" || environment === "staging" || environment === "production" ? environment : "custom";
978
+ }
979
+ function requireReportBugValue(value, option) {
980
+ const normalized = value?.trim();
981
+ if (!normalized) {
982
+ throw new CliError2(`Missing ${option}. Run interactively or pass ${option} <value>.`);
983
+ }
984
+ return normalized;
985
+ }
986
+ function splitPromptList(value) {
987
+ if (!value) {
988
+ return [];
989
+ }
990
+ return value.split(/[;\n]/).map((item) => item.trim()).filter(Boolean);
991
+ }
992
+ function splitCliAssetOption(value) {
993
+ return splitDroppedAssetList(value, { splitWhitespace: false });
994
+ }
995
+ function formatDroppedAssetDefault(value) {
996
+ return value.replace(/([\\\s;"'])/g, "\\$1");
997
+ }
998
+ function splitDroppedAssetList(value, options = {}) {
999
+ if (!value) {
1000
+ return [];
1001
+ }
1002
+ const splitWhitespace = options.splitWhitespace ?? true;
1003
+ const values = [];
1004
+ let current = "";
1005
+ let quote = null;
1006
+ let escaped = false;
1007
+ const push = () => {
1008
+ const normalized = current.trim();
1009
+ if (normalized) {
1010
+ values.push(normalized);
1011
+ }
1012
+ current = "";
1013
+ };
1014
+ for (const char of value) {
1015
+ if (escaped) {
1016
+ current += char;
1017
+ escaped = false;
1018
+ continue;
1019
+ }
1020
+ if (char === "\\") {
1021
+ escaped = true;
1022
+ continue;
1023
+ }
1024
+ if (quote) {
1025
+ if (char === quote) {
1026
+ quote = null;
1027
+ } else {
1028
+ current += char;
1029
+ }
1030
+ continue;
1031
+ }
1032
+ if (char === "'" || char === '"') {
1033
+ quote = char;
1034
+ continue;
1035
+ }
1036
+ if (char === ";" || char === `
1037
+ ` || splitWhitespace && /\s/.test(char)) {
1038
+ push();
1039
+ continue;
1040
+ }
1041
+ current += char;
1042
+ }
1043
+ if (escaped) {
1044
+ current += "\\";
1045
+ }
1046
+ push();
1047
+ return values;
1048
+ }
1049
+ function splitLabelList(value) {
1050
+ if (!value) {
1051
+ return [];
1052
+ }
1053
+ return value.split(/[;,\n]/).map(sanitizeBugReportLabel).filter(Boolean);
1054
+ }
1055
+ function takeRepeatedOption(args, option) {
1056
+ const rest = [];
1057
+ const values = [];
1058
+ for (let index = 0;index < args.length; index += 1) {
1059
+ const current = args[index];
1060
+ if (current === option) {
1061
+ const next = args[index + 1];
1062
+ if (!next || next.startsWith("-")) {
1063
+ throw new CliError2(`Missing value for ${option}`);
1064
+ }
1065
+ values.push(next);
1066
+ index += 1;
1067
+ continue;
1068
+ }
1069
+ if (current !== undefined) {
1070
+ rest.push(current);
1071
+ }
1072
+ }
1073
+ return { values, rest };
1074
+ }
1075
+ export {
1076
+ validateRequiredBugPromptValue,
1077
+ splitDroppedAssetList,
1078
+ promptBugText,
1079
+ promptBugConfirm,
1080
+ formatDroppedAssetDefault,
1081
+ executeTaskReportBug,
1082
+ bugPromptExampleText
1083
+ };