markupr 2.6.3 → 2.6.5

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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <img src="src/renderer/assets/logo.svg" alt="markupR" width="80" height="80">
3
3
  </p>
4
4
 
5
- <h1 align="center">markupr</h1>
5
+ <h1 align="center">markupR</h1>
6
6
 
7
7
  <p align="center">
8
8
  <strong>Record your screen. Say what's wrong. Your AI agent fixes it.</strong>
@@ -18,7 +18,7 @@
18
18
 
19
19
  <p align="center">
20
20
  <a href="#quick-start">Quick Start</a> &middot;
21
- <a href="#why-markupr">Why markupr</a> &middot;
21
+ <a href="#why-markupR">Why markupR</a> &middot;
22
22
  <a href="#mcp-server">MCP Server</a> &middot;
23
23
  <a href="#cli">CLI</a> &middot;
24
24
  <a href="#integrations">Integrations</a> &middot;
@@ -27,7 +27,7 @@
27
27
 
28
28
  ---
29
29
 
30
- <!-- hero-screenshot: Replace with an actual screenshot or GIF of markupr in action -->
30
+ <!-- hero-screenshot: Replace with an actual screenshot or GIF of markupR in action -->
31
31
 
32
32
  ## The Problem
33
33
 
@@ -35,7 +35,7 @@ AI coding agents can't see your screen. When you find a bug, you context-switch
35
35
 
36
36
  ## The Solution
37
37
 
38
- markupr records your screen while you narrate what's wrong. When you stop, it runs an intelligent pipeline that correlates your transcript timestamps with the video to extract the right frames at the right moments -- then stitches everything into structured Markdown your AI agent can act on immediately.
38
+ markupR records your screen while you narrate what's wrong. When you stop, it runs an intelligent pipeline that correlates your transcript timestamps with the video to extract the right frames at the right moments -- then stitches everything into structured Markdown your AI agent can act on immediately.
39
39
 
40
40
  - **Record** -- press a hotkey, talk through what you see
41
41
  - **Process** -- Whisper transcribes, ffmpeg extracts frames at the exact moments you described
@@ -65,7 +65,7 @@ Download from [markupr.com](https://markupr.com) or [GitHub Releases](https://gi
65
65
 
66
66
  No API keys required. Local Whisper transcription works out of the box.
67
67
 
68
- ## Why markupr?
68
+ ## Why markupR?
69
69
 
70
70
  **Local-first.** Whisper runs on your device. Your recordings, transcripts, and screenshots never leave your machine. No cloud dependency, no account required.
71
71
 
@@ -99,7 +99,7 @@ Each screenshot is extracted from the exact video frame matching your narration
99
99
 
100
100
  ## MCP Server
101
101
 
102
- Give your AI coding agent eyes and ears. Add markupr as an MCP server and it can capture screenshots, record your screen with voice, and receive structured reports -- all mid-conversation.
102
+ Give your AI coding agent eyes and ears. Add markupR as an MCP server and it can capture screenshots, record your screen with voice, and receive structured reports -- all mid-conversation.
103
103
 
104
104
  ### Setup
105
105
 
@@ -108,7 +108,7 @@ Give your AI coding agent eyes and ears. Add markupr as an MCP server and it can
108
108
  ```json
109
109
  {
110
110
  "mcpServers": {
111
- "markupr": {
111
+ "markupR": {
112
112
  "command": "npx",
113
113
  "args": ["-y", "markupr-mcp"]
114
114
  }
@@ -159,32 +159,32 @@ npm install -g markupr
159
159
 
160
160
  ### Commands
161
161
 
162
- **`markupr analyze <video>`** -- Process a screen recording into structured Markdown.
162
+ **`markupR analyze <video>`** -- Process a screen recording into structured Markdown.
163
163
 
164
164
  ```bash
165
- markupr analyze ./bug-demo.mov
166
- markupr analyze ./recording.mov --output ./reports
167
- markupr analyze ./recording.mov --template github-issue
168
- markupr analyze ./recording.mov --no-frames # transcript only
165
+ markupR analyze ./bug-demo.mov
166
+ markupR analyze ./recording.mov --output ./reports
167
+ markupR analyze ./recording.mov --template github-issue
168
+ markupR analyze ./recording.mov --no-frames # transcript only
169
169
  ```
170
170
 
171
- **`markupr watch [directory]`** -- Watch for new recordings and auto-process them.
171
+ **`markupR watch [directory]`** -- Watch for new recordings and auto-process them.
172
172
 
173
173
  ```bash
174
- markupr watch ~/Desktop --output ./reports
174
+ markupR watch ~/Desktop --output ./reports
175
175
  ```
176
176
 
177
- **`markupr push github <report>`** -- Create GitHub issues from a feedback report.
177
+ **`markupR push github <report>`** -- Create GitHub issues from a feedback report.
178
178
 
179
179
  ```bash
180
- markupr push github ./report.md --repo myorg/myapp
181
- markupr push github ./report.md --repo myorg/myapp --dry-run
180
+ markupR push github ./report.md --repo myorg/myapp
181
+ markupR push github ./report.md --repo myorg/myapp --dry-run
182
182
  ```
183
183
 
184
- **`markupr push linear <report>`** -- Create Linear issues from a feedback report.
184
+ **`markupR push linear <report>`** -- Create Linear issues from a feedback report.
185
185
 
186
186
  ```bash
187
- markupr push linear ./report.md --team ENG
187
+ markupR push linear ./report.md --team ENG
188
188
  ```
189
189
 
190
190
  ### Output Templates
@@ -200,7 +200,7 @@ markupr push linear ./report.md --team ENG
200
200
 
201
201
  ### GitHub Action
202
202
 
203
- Run markupr in CI to get visual feedback on pull requests:
203
+ Run markupR in CI to get visual feedback on pull requests:
204
204
 
205
205
  ```yaml
206
206
  - uses: eddiesanjuan/markupr-action@v1
@@ -244,7 +244,7 @@ For architecture details, see [CLAUDE.md](CLAUDE.md).
244
244
 
245
245
  ```bash
246
246
  git clone https://github.com/eddiesanjuan/markupr.git
247
- cd markupr
247
+ cd markupR
248
248
  npm install
249
249
  npm run dev
250
250
  ```
@@ -111,7 +111,7 @@ var init_LinearIssueCreator = __esm({
111
111
  this.token = token;
112
112
  }
113
113
  /**
114
- * Push a markupr report to Linear, creating one issue per feedback item.
114
+ * Push a markupR report to Linear, creating one issue per feedback item.
115
115
  */
116
116
  async pushReport(reportPath, options) {
117
117
  const markdown = await readFile3(reportPath, "utf-8");
@@ -275,7 +275,7 @@ var init_LinearIssueCreator = __esm({
275
275
  * Build markdown description for a Linear issue from a feedback item.
276
276
  */
277
277
  buildIssueDescription(item) {
278
- let desc = `## markupr Feedback: ${item.id}
278
+ let desc = `## markupR Feedback: ${item.id}
279
279
 
280
280
  `;
281
281
  desc += `**Severity:** ${item.severity}
@@ -310,7 +310,7 @@ ${item.suggestedAction}
310
310
  }
311
311
  desc += `
312
312
  ---
313
- *Created by [markupr](https://markupr.com)*`;
313
+ *Created by [markupR](https://markupr.com)*`;
314
314
  return desc;
315
315
  }
316
316
  /**
@@ -363,9 +363,9 @@ var init_types2 = __esm({
363
363
  Low: { name: "priority: low", color: "0e8a16", description: "Low priority" }
364
364
  };
365
365
  MARKUPR_LABEL = {
366
- name: "markupr",
366
+ name: "markupR",
367
367
  color: "6f42c1",
368
- description: "Created from markupr feedback session"
368
+ description: "Created from markupR feedback session"
369
369
  };
370
370
  }
371
371
  });
@@ -504,7 +504,7 @@ function formatIssueBody(item, reportPath) {
504
504
  body += `### Screenshots
505
505
 
506
506
  `;
507
- body += `_${item.screenshotPaths.length} screenshot(s) captured \u2014 see the markupr report for images._
507
+ body += `_${item.screenshotPaths.length} screenshot(s) captured \u2014 see the markupR report for images._
508
508
 
509
509
  `;
510
510
  }
@@ -522,7 +522,7 @@ function formatIssueBody(item, reportPath) {
522
522
  body += `_Source: \`${reportPath}\`_
523
523
  `;
524
524
  }
525
- body += `_Created by [markupr](https://markupr.com)_
525
+ body += `_Created by [markupR](https://markupr.com)_
526
526
  `;
527
527
  return body;
528
528
  }
@@ -562,7 +562,7 @@ async function pushToGitHub(options) {
562
562
  const markdown = await readFile4(reportPath, "utf-8");
563
563
  let items = parseMarkuprReport(markdown);
564
564
  if (items.length === 0) {
565
- throw new Error("No feedback items found in the report. Is this a valid markupr report?");
565
+ throw new Error("No feedback items found in the report. Is this a valid markupR report?");
566
566
  }
567
567
  if (filterIds && filterIds.length > 0) {
568
568
  const filterSet = new Set(filterIds.map((id) => id.toUpperCase()));
@@ -1120,13 +1120,13 @@ var MarkdownGeneratorImpl = class {
1120
1120
  const filename = this.generateFilename(projectName, session.startTime);
1121
1121
  if (items.length === 0) {
1122
1122
  const content2 = `# ${projectName} Feedback Report
1123
- > Generated by markupr on ${timestamp}
1123
+ > Generated by markupR on ${timestamp}
1124
1124
  > Duration: ${duration}
1125
1125
 
1126
1126
  _No feedback items were captured during this session._
1127
1127
 
1128
1128
  ---
1129
- *Generated by [markupr](https://markupr.com)*
1129
+ *Generated by [markupR](https://markupr.com)*
1130
1130
  ${REPORT_SUPPORT_LINE}
1131
1131
  `;
1132
1132
  return {
@@ -1147,7 +1147,7 @@ ${REPORT_SUPPORT_LINE}
1147
1147
  const highImpactCount = (severityCounts.Critical || 0) + (severityCounts.High || 0);
1148
1148
  const platform2 = session.metadata?.os || process?.platform || "Unknown";
1149
1149
  let content = `# ${projectName} Feedback Report
1150
- > Generated by markupr on ${timestamp}
1150
+ > Generated by markupR on ${timestamp}
1151
1151
  > Duration: ${duration} | Items: ${items.length} | Screenshots: ${screenshotCount}
1152
1152
 
1153
1153
  ## Session Overview
@@ -1244,7 +1244,7 @@ _No screenshot captured for this item._
1244
1244
  `;
1245
1245
  content += `
1246
1246
  ---
1247
- *Generated by [markupr](https://markupr.com)*
1247
+ *Generated by [markupR](https://markupr.com)*
1248
1248
  ${REPORT_SUPPORT_LINE}
1249
1249
  `;
1250
1250
  return {
@@ -1277,7 +1277,7 @@ ${REPORT_SUPPORT_LINE}
1277
1277
  const sessionDuration = transcriptSegments.length > 0 ? this.formatDuration(
1278
1278
  (transcriptSegments[transcriptSegments.length - 1].endTime - transcriptSegments[0].startTime) * 1e3
1279
1279
  ) : "0:00";
1280
- let md = `# markupr Session \u2014 ${sessionTimestamp}
1280
+ let md = `# markupR Session \u2014 ${sessionTimestamp}
1281
1281
  `;
1282
1282
  md += `> Segments: ${transcriptSegments.length} | Frames: ${extractedFrames.length} | Duration: ${sessionDuration}
1283
1283
 
@@ -1308,11 +1308,17 @@ ${REPORT_SUPPORT_LINE}
1308
1308
  md += `![Frame at ${frameTimestamp}](${relativePath})
1309
1309
 
1310
1310
  `;
1311
+ const contextLine = this.formatCaptureContextLine(frame.captureContext);
1312
+ if (contextLine) {
1313
+ md += `> ${contextLine}
1314
+
1315
+ `;
1316
+ }
1311
1317
  }
1312
1318
  }
1313
1319
  }
1314
1320
  md += `---
1315
- *Generated by [markupr](https://markupr.com)*
1321
+ *Generated by [markupR](https://markupr.com)*
1316
1322
  ${REPORT_SUPPORT_LINE}
1317
1323
  `;
1318
1324
  return md;
@@ -1359,6 +1365,16 @@ ${REPORT_SUPPORT_LINE}
1359
1365
  }
1360
1366
  return path.relative(sessionDir, framePath);
1361
1367
  }
1368
+ formatCaptureContextLine(context) {
1369
+ if (!context) {
1370
+ return void 0;
1371
+ }
1372
+ const cursor = context.cursor ? `Cursor: ${Math.round(context.cursor.x)}, ${Math.round(context.cursor.y)}` : void 0;
1373
+ const app = context.activeWindow?.appName || context.activeWindow?.sourceName;
1374
+ const focus = context.focusedElement?.textPreview || context.focusedElement?.label || context.focusedElement?.role;
1375
+ const parts = [cursor, app ? `App: ${app}` : void 0, focus ? `Focus: ${focus}` : void 0].filter((part) => Boolean(part));
1376
+ return parts.length ? parts.join(" | ") : void 0;
1377
+ }
1362
1378
  /**
1363
1379
  * Format a timestamp in seconds to M:SS format for post-process output.
1364
1380
  * Examples: 0 -> "0:00", 15.3 -> "0:15", 125 -> "2:05"
@@ -2307,7 +2323,7 @@ var markdownTemplate = {
2307
2323
  const { transcriptSegments, extractedFrames } = result;
2308
2324
  const sessionTimestamp = formatDate(new Date(timestamp ?? Date.now()));
2309
2325
  const sessionDuration = computeSessionDuration(transcriptSegments);
2310
- let md = `# markupr Session \u2014 ${sessionTimestamp}
2326
+ let md = `# markupR Session \u2014 ${sessionTimestamp}
2311
2327
  `;
2312
2328
  md += `> Segments: ${transcriptSegments.length} | Frames: ${extractedFrames.length} | Duration: ${sessionDuration}
2313
2329
 
@@ -2338,11 +2354,20 @@ var markdownTemplate = {
2338
2354
  md += `![Frame at ${frameTimestamp}](${relativePath})
2339
2355
 
2340
2356
  `;
2357
+ const cursor = frame.captureContext?.cursor ? `Cursor: ${Math.round(frame.captureContext.cursor.x)}, ${Math.round(frame.captureContext.cursor.y)}` : void 0;
2358
+ const app = frame.captureContext?.activeWindow?.appName || frame.captureContext?.activeWindow?.sourceName;
2359
+ const focus = frame.captureContext?.focusedElement?.textPreview || frame.captureContext?.focusedElement?.label || frame.captureContext?.focusedElement?.role;
2360
+ const contextLine = [cursor, app ? `App: ${app}` : void 0, focus ? `Focus: ${focus}` : void 0].filter(Boolean).join(" | ");
2361
+ if (contextLine) {
2362
+ md += `> ${contextLine}
2363
+
2364
+ `;
2365
+ }
2341
2366
  }
2342
2367
  }
2343
2368
  }
2344
2369
  md += `---
2345
- *Generated by [markupr](https://markupr.com)*
2370
+ *Generated by [markupR](https://markupr.com)*
2346
2371
  ${REPORT_SUPPORT_LINE2}
2347
2372
  `;
2348
2373
  return { content: md, fileExtension: ".md" };
@@ -2360,7 +2385,7 @@ var jsonTemplate = {
2360
2385
  const segmentFrameMap = mapFramesToSegments(transcriptSegments, extractedFrames);
2361
2386
  const output = {
2362
2387
  version: "1.0",
2363
- generator: "markupr",
2388
+ generator: "markupR",
2364
2389
  timestamp: new Date(timestamp ?? Date.now()).toISOString(),
2365
2390
  summary: {
2366
2391
  segments: transcriptSegments.length,
@@ -2377,7 +2402,8 @@ var jsonTemplate = {
2377
2402
  frames: frames.map((f) => ({
2378
2403
  path: computeRelativeFramePath(f.path, sessionDir),
2379
2404
  timestamp: f.timestamp,
2380
- reason: f.reason
2405
+ reason: f.reason,
2406
+ captureContext: f.captureContext
2381
2407
  }))
2382
2408
  };
2383
2409
  })
@@ -2402,7 +2428,7 @@ var githubIssueTemplate = {
2402
2428
  let md = `## Feedback Report
2403
2429
 
2404
2430
  `;
2405
- md += `> Captured by [markupr](https://markupr.com) on ${sessionTimestamp}
2431
+ md += `> Captured by [markupR](https://markupr.com) on ${sessionTimestamp}
2406
2432
  `;
2407
2433
  md += `> ${transcriptSegments.length} segments | ${extractedFrames.length} frames | Duration: ${duration}
2408
2434
 
@@ -2452,7 +2478,7 @@ var githubIssueTemplate = {
2452
2478
  `;
2453
2479
  }
2454
2480
  md += `---
2455
- _Generated by [markupr](https://markupr.com)_
2481
+ _Generated by [markupR](https://markupr.com)_
2456
2482
  `;
2457
2483
  return { content: md, fileExtension: ".md" };
2458
2484
  }
@@ -2512,7 +2538,7 @@ var linearTemplate = {
2512
2538
  }
2513
2539
  }
2514
2540
  md += `---
2515
- _Captured by [markupr](https://markupr.com)_
2541
+ _Captured by [markupR](https://markupr.com)_
2516
2542
  `;
2517
2543
  return { content: md, fileExtension: ".md" };
2518
2544
  }
@@ -2586,7 +2612,7 @@ ${segment.text}
2586
2612
  }
2587
2613
  }
2588
2614
  content += `----
2589
- _Generated by [markupr|https://markupr.com]_
2615
+ _Generated by [markupR|https://markupr.com]_
2590
2616
  `;
2591
2617
  return { content, fileExtension: ".jira" };
2592
2618
  }
@@ -2668,8 +2694,15 @@ var CLIPipeline = class _CLIPipeline {
2668
2694
  const result = {
2669
2695
  transcriptSegments: segments,
2670
2696
  extractedFrames,
2671
- reportPath: this.options.outputDir
2697
+ reportPath: this.options.outputDir,
2698
+ captureContexts: this.normalizeCaptureContexts(this.options.captureContexts || [])
2672
2699
  };
2700
+ if (result.captureContexts && result.captureContexts.length > 0 && result.extractedFrames.length > 0) {
2701
+ result.extractedFrames = this.attachCaptureContextsToFrames(
2702
+ result.extractedFrames,
2703
+ result.captureContexts
2704
+ );
2705
+ }
2673
2706
  let reportContent;
2674
2707
  let reportExtension = ".md";
2675
2708
  const templateName = this.options.template;
@@ -2755,6 +2788,38 @@ var CLIPipeline = class _CLIPipeline {
2755
2788
  this.activeProcesses.add(child);
2756
2789
  });
2757
2790
  }
2791
+ normalizeCaptureContexts(contexts) {
2792
+ return contexts.filter((context) => Number.isFinite(context.recordedAt)).slice().sort((a, b) => a.recordedAt - b.recordedAt);
2793
+ }
2794
+ attachCaptureContextsToFrames(frames, contexts) {
2795
+ const earliestContext = contexts[0]?.recordedAt;
2796
+ if (!Number.isFinite(earliestContext)) {
2797
+ return frames;
2798
+ }
2799
+ const maxDistanceMs = 5e3;
2800
+ return frames.map((frame) => {
2801
+ const frameAtMs = Number(earliestContext) + Math.round(frame.timestamp * 1e3);
2802
+ let bestMatch;
2803
+ let bestDistance = Number.POSITIVE_INFINITY;
2804
+ for (const context of contexts) {
2805
+ const distance = Math.abs(frameAtMs - context.recordedAt);
2806
+ if (distance < bestDistance) {
2807
+ bestDistance = distance;
2808
+ bestMatch = context;
2809
+ }
2810
+ if (context.recordedAt > frameAtMs && distance > bestDistance) {
2811
+ break;
2812
+ }
2813
+ }
2814
+ if (!bestMatch || bestDistance > maxDistanceMs) {
2815
+ return frame;
2816
+ }
2817
+ return {
2818
+ ...frame,
2819
+ captureContext: bestMatch
2820
+ };
2821
+ });
2822
+ }
2758
2823
  /**
2759
2824
  * Validate the video file is a real, non-empty file with a video stream.
2760
2825
  */
@@ -3304,7 +3369,7 @@ async function checkNodeVersion() {
3304
3369
  name: "Node.js",
3305
3370
  status: "warn",
3306
3371
  message: `Unknown version: ${version}`,
3307
- hint: "markupr requires Node.js >= 18.0.0"
3372
+ hint: "markupR requires Node.js >= 18.0.0"
3308
3373
  };
3309
3374
  }
3310
3375
  const [major] = parsed;
@@ -3319,7 +3384,7 @@ async function checkNodeVersion() {
3319
3384
  name: "Node.js",
3320
3385
  status: "fail",
3321
3386
  message: `${version} is too old`,
3322
- hint: "markupr requires Node.js >= 18.0.0. Upgrade at https://nodejs.org"
3387
+ hint: "markupR requires Node.js >= 18.0.0. Upgrade at https://nodejs.org"
3323
3388
  };
3324
3389
  }
3325
3390
  async function checkFfmpeg() {
@@ -3368,7 +3433,7 @@ async function checkWhisperModel() {
3368
3433
  status: "warn",
3369
3434
  message: "No models directory found",
3370
3435
  hint: `Models directory: ${modelsDir}
3371
- Download a model via the markupr desktop app, or manually place a ggml-*.bin file there`
3436
+ Download a model via the markupR desktop app, or manually place a ggml-*.bin file there`
3372
3437
  };
3373
3438
  }
3374
3439
  const model = findWhisperModel(modelsDir);
@@ -3378,7 +3443,7 @@ Download a model via the markupr desktop app, or manually place a ggml-*.bin fil
3378
3443
  status: "warn",
3379
3444
  message: "No model files found",
3380
3445
  hint: `Models directory: ${modelsDir}
3381
- Download a model via the markupr desktop app, or manually place a ggml-*.bin file there`
3446
+ Download a model via the markupR desktop app, or manually place a ggml-*.bin file there`
3382
3447
  };
3383
3448
  }
3384
3449
  return {
@@ -3444,7 +3509,7 @@ async function checkDiskSpace() {
3444
3509
  name: "Disk space",
3445
3510
  status: "warn",
3446
3511
  message: `${availableGB.toFixed(1)} GB available (low)`,
3447
- hint: "markupr recordings and output need disk space. Free up some space if you plan to record long sessions"
3512
+ hint: "markupR recordings and output need disk space. Free up some space if you plan to record long sessions"
3448
3513
  };
3449
3514
  }
3450
3515
  return {
@@ -3558,7 +3623,7 @@ async function runInit(options) {
3558
3623
  }
3559
3624
 
3560
3625
  // src/cli/index.ts
3561
- var VERSION = true ? "2.6.3" : "0.0.0-dev";
3626
+ var VERSION = true ? "2.6.5" : "0.0.0-dev";
3562
3627
  var SYMBOLS = {
3563
3628
  check: "\u2714",
3564
3629
  // checkmark
@@ -3577,7 +3642,7 @@ var SYMBOLS = {
3577
3642
  };
3578
3643
  function banner() {
3579
3644
  console.log();
3580
- console.log(` markupr v${VERSION} ${SYMBOLS.bullet} CLI Mode`);
3645
+ console.log(` markupR v${VERSION} ${SYMBOLS.bullet} CLI Mode`);
3581
3646
  console.log(` ${SYMBOLS.line.repeat(40)}`);
3582
3647
  console.log();
3583
3648
  }
@@ -3762,7 +3827,7 @@ program.command("watch").description("Watch a directory for new recordings and a
3762
3827
  process.exit(EXIT_SYSTEM_ERROR);
3763
3828
  }
3764
3829
  });
3765
- program.command("doctor").description("Check your environment for markupr dependencies and configuration").action(async () => {
3830
+ program.command("doctor").description("Check your environment for markupR dependencies and configuration").action(async () => {
3766
3831
  banner();
3767
3832
  step("Checking environment...");
3768
3833
  console.log();
@@ -3786,16 +3851,16 @@ program.command("doctor").description("Check your environment for markupr depend
3786
3851
  console.log(` ${result.passed} passed, ${result.warned} warnings, ${result.failed} failed`);
3787
3852
  console.log();
3788
3853
  if (result.failed > 0) {
3789
- fail("Some required checks failed. Fix them to use markupr.");
3854
+ fail("Some required checks failed. Fix them to use markupR.");
3790
3855
  process.exit(EXIT_USER_ERROR);
3791
3856
  } else if (result.warned > 0) {
3792
- success("markupr is ready (some optional features are not configured).");
3857
+ success("markupR is ready (some optional features are not configured).");
3793
3858
  } else {
3794
- success("markupr is fully configured and ready to go!");
3859
+ success("markupR is fully configured and ready to go!");
3795
3860
  }
3796
3861
  console.log();
3797
3862
  });
3798
- program.command("init").description("Create a .markupr.json config file in the current project").option("--output <dir>", "Output directory for feedback sessions", "./markupr-output").option("--no-gitignore", "Skip updating .gitignore").option("--force", "Overwrite existing config file", false).action(async (options) => {
3863
+ program.command("init").description("Create a markupR project config file (.markupr.json) in the current project").option("--output <dir>", "Output directory for feedback sessions", "./markupr-output").option("--no-gitignore", "Skip updating .gitignore").option("--force", "Overwrite existing config file", false).action(async (options) => {
3799
3864
  banner();
3800
3865
  const result = await runInit({
3801
3866
  directory: process.cwd(),
@@ -3811,17 +3876,17 @@ program.command("init").description("Create a .markupr.json config file in the c
3811
3876
  }
3812
3877
  success(`Created ${result.configPath}`);
3813
3878
  if (result.gitignoreUpdated) {
3814
- success("Updated .gitignore with markupr output directory");
3879
+ success("Updated .gitignore with markupR output directory");
3815
3880
  }
3816
3881
  console.log();
3817
3882
  step("Next steps:");
3818
3883
  console.log(" 1. Run `markupr doctor` to verify your environment");
3819
- console.log(" 2. Record a session with the markupr desktop app or screen recorder");
3884
+ console.log(" 2. Record a session with the markupR desktop app or screen recorder");
3820
3885
  console.log(" 3. Run `markupr analyze <video-file>` to generate a feedback report");
3821
3886
  console.log();
3822
3887
  });
3823
3888
  var pushCmd = program.command("push").description("Push feedback to external services");
3824
- pushCmd.command("linear").description("Create Linear issues from a markupr feedback report").argument("<report>", "Path to the markupr markdown report").requiredOption("--team <key>", "Linear team key (e.g., ENG, DES)").option("--token <token>", "Linear API key (prefer LINEAR_API_KEY env var)").option("--project <name>", "Linear project name to assign issues to").option("--dry-run", "Show what would be created without creating anything", false).action(async (report, options) => {
3889
+ pushCmd.command("linear").description("Create Linear issues from a markupR feedback report").argument("<report>", "Path to the markupR markdown report").requiredOption("--team <key>", "Linear team key (e.g., ENG, DES)").option("--token <token>", "Linear API key (prefer LINEAR_API_KEY env var)").option("--project <name>", "Linear project name to assign issues to").option("--dry-run", "Show what would be created without creating anything", false).action(async (report, options) => {
3825
3890
  banner();
3826
3891
  const reportPath = resolve3(report);
3827
3892
  if (!existsSync7(reportPath)) {
@@ -3892,7 +3957,7 @@ pushCmd.command("linear").description("Create Linear issues from a markupr feedb
3892
3957
  process.exit(EXIT_USER_ERROR);
3893
3958
  }
3894
3959
  });
3895
- pushCmd.command("github").description("Create GitHub issues from a markupr feedback report").argument("<report>", "Path to the markupr markdown report").requiredOption("--repo <owner/repo>", "Target GitHub repository (e.g., myorg/myapp)").option("--token <token>", "GitHub token (prefer GITHUB_TOKEN env var or gh auth login)").option("--items <ids...>", "Specific FB-XXX item IDs to push (default: all)").option("--dry-run", "Show what would be created without creating anything", false).action(async (report, options) => {
3960
+ pushCmd.command("github").description("Create GitHub issues from a markupR feedback report").argument("<report>", "Path to the markupR markdown report").requiredOption("--repo <owner/repo>", "Target GitHub repository (e.g., myorg/myapp)").option("--token <token>", "GitHub token (prefer GITHUB_TOKEN env var or gh auth login)").option("--items <ids...>", "Specific FB-XXX item IDs to push (default: all)").option("--dry-run", "Show what would be created without creating anything", false).action(async (report, options) => {
3896
3961
  banner();
3897
3962
  const reportPath = resolve3(report);
3898
3963
  if (!existsSync7(reportPath)) {