@staff0rd/assist 0.132.0 → 0.134.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.
package/README.md CHANGED
@@ -46,6 +46,7 @@ After installation, the `assist` command will be available globally. You can als
46
46
  - `/standup` - Summarise recent journal entries as a standup update
47
47
  - `/sync` - Sync commands and settings to ~/.claude
48
48
  - `/inspect` - Run .NET code inspections on changed files
49
+ - `/screenshot` - Capture a screenshot of a running application window
49
50
  - `/seq` - Query Seq logs from a URL or filter expression
50
51
  - `/verify` - Run all verification commands in parallel
51
52
  - `/transcript-format` - Format meeting transcripts from VTT files
@@ -117,6 +118,7 @@ After installation, the `assist` command will be available globally. You can als
117
118
  - `assist dotnet inspect [sln] --scope all` - Inspect the full solution
118
119
  - `assist dotnet inspect [sln] --scope base:<ref>` - Inspect all .cs files changed since diverging from a base ref (e.g. `--scope base:main` for a full PR)
119
120
  - `assist dotnet inspect [sln] --scope commit:<ref>` - Inspect .cs files changed in a specific commit
121
+ - `assist dotnet inspect [sln] --only <ids...>` - Show only the specified issue type IDs (e.g. `--only CommentedCode`)
120
122
  - `assist dotnet inspect [sln] --suppress <ids...>` - Suppress specific issue type IDs on the command line
121
123
  - `assist dotnet inspect [sln] --roslyn` - Use Roslyn analyzers via msbuild instead of JetBrains
122
124
  - `assist dotnet inspect [sln] --swea` - Enable solution-wide error analysis (slower but more thorough)
@@ -141,6 +143,7 @@ After installation, the `assist` command will be available globally. You can als
141
143
  - `assist seq query <filter> -c <connection>` - Query using a specific connection
142
144
  - `assist seq query <filter> --json` - Output raw JSON
143
145
  - `assist seq query <filter> -n <count>` - Fetch a specific number of events (default 50)
146
+ - `assist screenshot <process>` - Capture a screenshot of a running application window (e.g. `assist screenshot notepad`). Output directory is configurable via `screenshot.outputDir` (default `./screenshots`)
144
147
  - `assist complexity <pattern>` - Analyze a file (all metrics if single match, maintainability if multiple)
145
148
  - `assist complexity cyclomatic [pattern]` - Calculate cyclomatic complexity per function
146
149
  - `assist complexity halstead [pattern]` - Calculate Halstead metrics per function
@@ -0,0 +1,9 @@
1
+ ---
2
+ description: Capture a screenshot of a running application window
3
+ ---
4
+
5
+ Take a screenshot of a running application window. Ask the user which process to capture if not specified.
6
+
7
+ Run `assist screenshot <process>` where `<process>` is the name of the running process (e.g. notepad, code, chrome).
8
+
9
+ The screenshot is saved to the configured output directory (default `./screenshots`).
@@ -46,6 +46,7 @@
46
46
  "Bash(assist ravendb auth list:*)",
47
47
  "Bash(assist seq query:*)",
48
48
  "Bash(assist seq auth list:*)",
49
+ "Bash(assist screenshot:*)",
49
50
  "Bash(assist roam show-claude-code-icon:*)",
50
51
  "SlashCommand(/next-backlog-item)",
51
52
  "SlashCommand(/verify)",
@@ -83,6 +84,8 @@
83
84
  "SlashCommand(/seq)",
84
85
  "Skill(inspect)",
85
86
  "SlashCommand(/inspect)",
87
+ "Skill(screenshot)",
88
+ "SlashCommand(/screenshot)",
86
89
  "WebFetch(domain:staffordwilliams.com)"
87
90
  ],
88
91
  "deny": ["Bash(git commit:*)", "Bash(npm run:*)", "Bash(npx assist:*)"]
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.132.0",
9
+ version: "0.134.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -189,6 +189,9 @@ var assistConfigSchema = z.strictObject({
189
189
  ).default([]),
190
190
  defaultConnection: z.string().optional()
191
191
  }).optional(),
192
+ screenshot: z.strictObject({
193
+ outputDir: z.string().default("./screenshots")
194
+ }).default({ outputDir: "./screenshots" }),
192
195
  voice: z.strictObject({
193
196
  wakeWords: z.array(z.string()).default(DEFAULT_WAKE_WORDS),
194
197
  mic: z.string().optional(),
@@ -1841,14 +1844,14 @@ function flushIfFailed(exitCode, chunks) {
1841
1844
 
1842
1845
  // src/commands/verify/run/runAllEntries.ts
1843
1846
  function runEntry(entry, onComplete) {
1844
- return new Promise((resolve6) => {
1847
+ return new Promise((resolve7) => {
1845
1848
  const child = spawnCommand(entry.fullCommand, entry.cwd, entry.env);
1846
1849
  const chunks = collectOutput(child);
1847
1850
  child.on("close", (code) => {
1848
1851
  const exitCode = code ?? 1;
1849
1852
  flushIfFailed(exitCode, chunks);
1850
1853
  onComplete?.(exitCode);
1851
- resolve6({ script: entry.name, code: exitCode });
1854
+ resolve7({ script: entry.name, code: exitCode });
1852
1855
  });
1853
1856
  });
1854
1857
  }
@@ -2635,12 +2638,12 @@ function getHtml() {
2635
2638
 
2636
2639
  // src/commands/backlog/web/parseItemBody.ts
2637
2640
  function readBody(req) {
2638
- return new Promise((resolve6, reject) => {
2641
+ return new Promise((resolve7, reject) => {
2639
2642
  let body = "";
2640
2643
  req.on("data", (chunk) => {
2641
2644
  body += chunk.toString();
2642
2645
  });
2643
- req.on("end", () => resolve6(body));
2646
+ req.on("end", () => resolve7(body));
2644
2647
  req.on("error", reject);
2645
2648
  });
2646
2649
  }
@@ -3197,12 +3200,12 @@ function hasSubcommands(helpText) {
3197
3200
  // src/commands/permitCliReads/runHelp.ts
3198
3201
  import { exec as exec2 } from "child_process";
3199
3202
  function runHelp(args) {
3200
- return new Promise((resolve6) => {
3203
+ return new Promise((resolve7) => {
3201
3204
  exec2(
3202
3205
  `${args.join(" ")} --help`,
3203
3206
  { encoding: "utf-8", timeout: 3e4 },
3204
3207
  (_err, stdout, stderr) => {
3205
- resolve6(stdout || stderr || "");
3208
+ resolve7(stdout || stderr || "");
3206
3209
  }
3207
3210
  );
3208
3211
  });
@@ -4839,13 +4842,14 @@ var deadCodeRules = /* @__PURE__ */ new Set([
4839
4842
  ]);
4840
4843
 
4841
4844
  // src/commands/dotnet/filterIssues.ts
4842
- function filterIssues(issues, all, cliSuppress) {
4845
+ function filterIssues(issues, all, cliOnly, cliSuppress) {
4846
+ const only = cliOnly.length > 0 ? new Set(cliOnly) : null;
4843
4847
  const suppress = /* @__PURE__ */ new Set([
4844
4848
  ...loadConfig().dotnet?.inspect.suppress ?? [],
4845
4849
  ...cliSuppress
4846
4850
  ]);
4847
4851
  return issues.filter(
4848
- (i) => (all || deadCodeRules.has(i.typeId)) && !suppress.has(i.typeId)
4852
+ (i) => (only ? only.has(i.typeId) : all || deadCodeRules.has(i.typeId)) && !suppress.has(i.typeId)
4849
4853
  );
4850
4854
  }
4851
4855
 
@@ -5031,6 +5035,15 @@ function runEngine(resolved, changedFiles, options2) {
5031
5035
  }
5032
5036
 
5033
5037
  // src/commands/dotnet/inspect.ts
5038
+ function logScope(changedFiles) {
5039
+ if (changedFiles === null) {
5040
+ console.log(chalk57.dim("Inspecting full solution..."));
5041
+ } else {
5042
+ console.log(
5043
+ chalk57.dim(`Inspecting ${changedFiles.length} changed file(s)...`)
5044
+ );
5045
+ }
5046
+ }
5034
5047
  function reportResults(issues, elapsed) {
5035
5048
  if (issues.length > 0) displayIssues(issues);
5036
5049
  else console.log(chalk57.green("No issues found"));
@@ -5048,20 +5061,12 @@ async function inspect(sln, options2) {
5048
5061
  console.log(chalk57.green("No changed .cs files found"));
5049
5062
  return;
5050
5063
  }
5051
- if (changedFiles === null) {
5052
- console.log(chalk57.dim("Inspecting full solution..."));
5053
- } else {
5054
- console.log(
5055
- chalk57.dim(`Inspecting ${changedFiles.length} changed file(s)...`)
5056
- );
5057
- }
5064
+ logScope(changedFiles);
5058
5065
  const start3 = Date.now();
5059
5066
  const issues = runEngine(resolved, changedFiles, options2);
5060
5067
  const elapsed = Date.now() - start3;
5061
- reportResults(
5062
- filterIssues(issues, !!options2.all, options2.suppress ?? []),
5063
- elapsed
5064
- );
5068
+ const { all = false, only = [], suppress = [] } = options2;
5069
+ reportResults(filterIssues(issues, all, only, suppress), elapsed);
5065
5070
  }
5066
5071
 
5067
5072
  // src/commands/registerDotnet.ts
@@ -5072,7 +5077,7 @@ function registerDotnet(program2) {
5072
5077
  ).argument("[sln]", "Path to a .sln file (auto-detected if omitted)").option(
5073
5078
  "--scope <mode>",
5074
5079
  `File scope: ${scopeHelpText} (default: working copy diff)`
5075
- ).option("--all", "Show all issues, not just dead code").option("--suppress <ids...>", "Suppress specific issue type IDs").option("--swea", "Enable solution-wide error analysis").option("--roslyn", "Use Roslyn analyzers via msbuild instead of JetBrains").action(inspect);
5080
+ ).option("--all", "Show all issues, not just dead code").option("--only <ids...>", "Show only the specified issue type IDs").option("--suppress <ids...>", "Suppress specific issue type IDs").option("--swea", "Enable solution-wide error analysis").option("--roslyn", "Use Roslyn analyzers via msbuild instead of JetBrains").action(inspect);
5076
5081
  cmd.command("check-locks").description("Check if build output files are locked by a debugger").action(checkBuildLocksCommand);
5077
5082
  cmd.command("deps").description("Show .csproj project dependency tree and solution membership").argument("<csproj>", "Path to a .csproj file").option("--json", "Output as JSON").action(deps);
5078
5083
  cmd.command("in-sln").description("Check whether a .csproj is referenced by any .sln file").argument("<csproj>", "Path to a .csproj file").action(inSln);
@@ -6702,7 +6707,7 @@ function getViolations(pattern2, options2 = {}, maxLines = DEFAULT_MAX_LINES) {
6702
6707
 
6703
6708
  // src/commands/refactor/check/index.ts
6704
6709
  function runScript(script, cwd) {
6705
- return new Promise((resolve6) => {
6710
+ return new Promise((resolve7) => {
6706
6711
  const child = spawn3("npm", ["run", script], {
6707
6712
  stdio: "pipe",
6708
6713
  shell: true,
@@ -6716,7 +6721,7 @@ function runScript(script, cwd) {
6716
6721
  output += data.toString();
6717
6722
  });
6718
6723
  child.on("close", (code) => {
6719
- resolve6({ script, code: code ?? 1, output });
6724
+ resolve7({ script, code: code ?? 1, output });
6720
6725
  });
6721
6726
  });
6722
6727
  }
@@ -7660,9 +7665,9 @@ function createReadlineInterface() {
7660
7665
  });
7661
7666
  }
7662
7667
  function askQuestion(rl, question) {
7663
- return new Promise((resolve6) => {
7668
+ return new Promise((resolve7) => {
7664
7669
  rl.question(question, (answer) => {
7665
- resolve6(answer.trim());
7670
+ resolve7(answer.trim());
7666
7671
  });
7667
7672
  });
7668
7673
  }
@@ -8603,7 +8608,7 @@ function extractCode(url, expectedState) {
8603
8608
  return code;
8604
8609
  }
8605
8610
  function waitForCallback(port, expectedState) {
8606
- return new Promise((resolve6, reject) => {
8611
+ return new Promise((resolve7, reject) => {
8607
8612
  const timeout = setTimeout(() => {
8608
8613
  server.close();
8609
8614
  reject(new Error("Authorization timed out after 120 seconds"));
@@ -8620,7 +8625,7 @@ function waitForCallback(port, expectedState) {
8620
8625
  const code = extractCode(url, expectedState);
8621
8626
  respondHtml(res, 200, "Authorization successful!");
8622
8627
  server.close();
8623
- resolve6(code);
8628
+ resolve7(code);
8624
8629
  } catch (err) {
8625
8630
  respondHtml(res, 400, err.message);
8626
8631
  server.close();
@@ -8951,11 +8956,178 @@ function run2(name, args) {
8951
8956
  );
8952
8957
  }
8953
8958
 
8959
+ // src/commands/screenshot/index.ts
8960
+ import { execSync as execSync38 } from "child_process";
8961
+ import { existsSync as existsSync36, mkdirSync as mkdirSync13, unlinkSync as unlinkSync9, writeFileSync as writeFileSync26 } from "fs";
8962
+ import { tmpdir as tmpdir6 } from "os";
8963
+ import { join as join37, resolve as resolve5 } from "path";
8964
+ import chalk92 from "chalk";
8965
+
8966
+ // src/commands/screenshot/captureWindowPs1.ts
8967
+ var captureWindowPs1 = `
8968
+ param([string]$ProcessName, [string]$OutputPath)
8969
+
8970
+ Add-Type -AssemblyName System.Drawing
8971
+
8972
+ Add-Type @"
8973
+ using System;
8974
+ using System.Runtime.InteropServices;
8975
+ public class Win32Window {
8976
+ [DllImport("user32.dll")]
8977
+ [return: MarshalAs(UnmanagedType.Bool)]
8978
+ public static extern bool SetProcessDPIAware();
8979
+
8980
+ [DllImport("user32.dll")]
8981
+ [return: MarshalAs(UnmanagedType.Bool)]
8982
+ public static extern bool IsIconic(IntPtr hWnd);
8983
+
8984
+ [DllImport("user32.dll")]
8985
+ [return: MarshalAs(UnmanagedType.Bool)]
8986
+ public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
8987
+
8988
+ [DllImport("dwmapi.dll")]
8989
+ public static extern int DwmGetWindowAttribute(
8990
+ IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute);
8991
+
8992
+ [DllImport("user32.dll")]
8993
+ public static extern IntPtr GetDC(IntPtr hWnd);
8994
+
8995
+ [DllImport("user32.dll")]
8996
+ public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
8997
+
8998
+ [DllImport("gdi32.dll")]
8999
+ public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
9000
+
9001
+ [DllImport("gdi32.dll")]
9002
+ public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int w, int h);
9003
+
9004
+ [DllImport("gdi32.dll")]
9005
+ public static extern IntPtr SelectObject(IntPtr hdc, IntPtr h);
9006
+
9007
+ [DllImport("gdi32.dll")]
9008
+ [return: MarshalAs(UnmanagedType.Bool)]
9009
+ public static extern bool BitBlt(
9010
+ IntPtr hdc, int x, int y, int cx, int cy,
9011
+ IntPtr hdcSrc, int x1, int y1, uint rop);
9012
+
9013
+ [DllImport("gdi32.dll")]
9014
+ [return: MarshalAs(UnmanagedType.Bool)]
9015
+ public static extern bool DeleteObject(IntPtr ho);
9016
+
9017
+ [DllImport("gdi32.dll")]
9018
+ [return: MarshalAs(UnmanagedType.Bool)]
9019
+ public static extern bool DeleteDC(IntPtr hdc);
9020
+
9021
+ [StructLayout(LayoutKind.Sequential)]
9022
+ public struct RECT {
9023
+ public int Left;
9024
+ public int Top;
9025
+ public int Right;
9026
+ public int Bottom;
9027
+ }
9028
+ }
9029
+ "@
9030
+
9031
+ # DPI awareness ensures all coordinates are in physical pixels
9032
+ [Win32Window]::SetProcessDPIAware() | Out-Null
9033
+
9034
+ $procs = Get-Process -Name $ProcessName -ErrorAction SilentlyContinue |
9035
+ Where-Object { $_.MainWindowHandle -ne [IntPtr]::Zero }
9036
+
9037
+ if (-not $procs) {
9038
+ Write-Error "No visible window found for process '$ProcessName'"
9039
+ exit 1
9040
+ }
9041
+
9042
+ $proc = $procs | Select-Object -First 1
9043
+ $hwnd = $proc.MainWindowHandle
9044
+
9045
+ if ([Win32Window]::IsIconic($hwnd)) {
9046
+ [Win32Window]::ShowWindow($hwnd, 9) | Out-Null # SW_RESTORE
9047
+ Start-Sleep -Milliseconds 300
9048
+ }
9049
+
9050
+ # DWMWA_EXTENDED_FRAME_BOUNDS (9) = visible bounds excluding invisible DWM shadow
9051
+ $rect = New-Object Win32Window+RECT
9052
+ $hr = [Win32Window]::DwmGetWindowAttribute(
9053
+ $hwnd, 9, [ref]$rect,
9054
+ [System.Runtime.InteropServices.Marshal]::SizeOf($rect))
9055
+
9056
+ if ($hr -ne 0) {
9057
+ Write-Error "Failed to get window bounds (HRESULT: $hr)"
9058
+ exit 1
9059
+ }
9060
+
9061
+ $width = $rect.Right - $rect.Left
9062
+ $height = $rect.Bottom - $rect.Top
9063
+
9064
+ if ($width -le 0 -or $height -le 0) {
9065
+ Write-Error "Window has invalid dimensions ($width x $height)"
9066
+ exit 1
9067
+ }
9068
+
9069
+ # BitBlt from screen DC \u2014 both coords and capture use physical pixels
9070
+ $hdcScreen = [Win32Window]::GetDC([IntPtr]::Zero)
9071
+ $hdcMem = [Win32Window]::CreateCompatibleDC($hdcScreen)
9072
+ $hBitmap = [Win32Window]::CreateCompatibleBitmap($hdcScreen, $width, $height)
9073
+ $hOld = [Win32Window]::SelectObject($hdcMem, $hBitmap)
9074
+
9075
+ $SRCCOPY = 0x00CC0020
9076
+ [Win32Window]::BitBlt($hdcMem, 0, 0, $width, $height,
9077
+ $hdcScreen, $rect.Left, $rect.Top, $SRCCOPY) | Out-Null
9078
+
9079
+ $bitmap = [System.Drawing.Image]::FromHbitmap($hBitmap)
9080
+ $bitmap.Save($OutputPath, [System.Drawing.Imaging.ImageFormat]::Png)
9081
+ $bitmap.Dispose()
9082
+
9083
+ [Win32Window]::SelectObject($hdcMem, $hOld) | Out-Null
9084
+ [Win32Window]::DeleteObject($hBitmap) | Out-Null
9085
+ [Win32Window]::DeleteDC($hdcMem) | Out-Null
9086
+ [Win32Window]::ReleaseDC([IntPtr]::Zero, $hdcScreen) | Out-Null
9087
+
9088
+ Write-Output $OutputPath
9089
+ `;
9090
+
9091
+ // src/commands/screenshot/index.ts
9092
+ function buildOutputPath(outputDir, processName) {
9093
+ if (!existsSync36(outputDir)) {
9094
+ mkdirSync13(outputDir, { recursive: true });
9095
+ }
9096
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
9097
+ return resolve5(outputDir, `${processName}-${timestamp}.png`);
9098
+ }
9099
+ function runPowerShellScript(processName, outputPath) {
9100
+ const scriptPath = join37(tmpdir6(), `assist-screenshot-${Date.now()}.ps1`);
9101
+ writeFileSync26(scriptPath, captureWindowPs1, "utf-8");
9102
+ try {
9103
+ execSync38(
9104
+ `powershell -NoProfile -ExecutionPolicy Bypass -File "${scriptPath}" -ProcessName "${processName}" -OutputPath "${outputPath}"`,
9105
+ { stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }
9106
+ );
9107
+ } finally {
9108
+ unlinkSync9(scriptPath);
9109
+ }
9110
+ }
9111
+ function screenshot(processName) {
9112
+ const config = loadConfig();
9113
+ const outputDir = resolve5(config.screenshot.outputDir);
9114
+ const outputPath = buildOutputPath(outputDir, processName);
9115
+ console.log(chalk92.gray(`Capturing window for process "${processName}" ...`));
9116
+ try {
9117
+ runPowerShellScript(processName, outputPath);
9118
+ console.log(chalk92.green(`Screenshot saved: ${outputPath}`));
9119
+ } catch (error) {
9120
+ const msg = error instanceof Error ? error.message : String(error);
9121
+ console.error(chalk92.red(`Failed to capture screenshot: ${msg}`));
9122
+ process.exit(1);
9123
+ }
9124
+ }
9125
+
8954
9126
  // src/commands/statusLine.ts
8955
- import chalk93 from "chalk";
9127
+ import chalk94 from "chalk";
8956
9128
 
8957
9129
  // src/commands/buildLimitsSegment.ts
8958
- import chalk92 from "chalk";
9130
+ import chalk93 from "chalk";
8959
9131
  var FIVE_HOUR_SECONDS = 5 * 3600;
8960
9132
  var SEVEN_DAY_SECONDS = 7 * 86400;
8961
9133
  function formatTimeLeft(resetsAt) {
@@ -8978,10 +9150,10 @@ function projectUsage(pct, resetsAt, windowSeconds) {
8978
9150
  function colorizeRateLimit(pct, resetsAt, windowSeconds) {
8979
9151
  const label2 = `${Math.round(pct)}%`;
8980
9152
  const projected = projectUsage(pct, resetsAt, windowSeconds);
8981
- if (projected == null) return chalk92.green(label2);
8982
- if (projected > 100) return chalk92.red(label2);
8983
- if (projected > 75) return chalk92.yellow(label2);
8984
- return chalk92.green(label2);
9153
+ if (projected == null) return chalk93.green(label2);
9154
+ if (projected > 100) return chalk93.red(label2);
9155
+ if (projected > 75) return chalk93.yellow(label2);
9156
+ return chalk93.green(label2);
8985
9157
  }
8986
9158
  function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel) {
8987
9159
  const timeLabel = resetsAt ? formatTimeLeft(resetsAt) : fallbackLabel;
@@ -9007,14 +9179,14 @@ function buildLimitsSegment(rateLimits) {
9007
9179
  }
9008
9180
 
9009
9181
  // src/commands/statusLine.ts
9010
- chalk93.level = 3;
9182
+ chalk94.level = 3;
9011
9183
  function formatNumber(num) {
9012
9184
  return num.toLocaleString("en-US");
9013
9185
  }
9014
9186
  function colorizePercent(pct) {
9015
9187
  const label2 = `${Math.round(pct)}%`;
9016
- if (pct > 80) return chalk93.red(label2);
9017
- if (pct > 40) return chalk93.yellow(label2);
9188
+ if (pct > 80) return chalk94.red(label2);
9189
+ if (pct > 40) return chalk94.yellow(label2);
9018
9190
  return label2;
9019
9191
  }
9020
9192
  async function statusLine() {
@@ -9037,7 +9209,7 @@ import { fileURLToPath as fileURLToPath7 } from "url";
9037
9209
  // src/commands/sync/syncClaudeMd.ts
9038
9210
  import * as fs22 from "fs";
9039
9211
  import * as path40 from "path";
9040
- import chalk94 from "chalk";
9212
+ import chalk95 from "chalk";
9041
9213
  async function syncClaudeMd(claudeDir, targetBase) {
9042
9214
  const source = path40.join(claudeDir, "CLAUDE.md");
9043
9215
  const target = path40.join(targetBase, "CLAUDE.md");
@@ -9046,12 +9218,12 @@ async function syncClaudeMd(claudeDir, targetBase) {
9046
9218
  const targetContent = fs22.readFileSync(target, "utf-8");
9047
9219
  if (sourceContent !== targetContent) {
9048
9220
  console.log(
9049
- chalk94.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
9221
+ chalk95.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
9050
9222
  );
9051
9223
  console.log();
9052
9224
  printDiff(targetContent, sourceContent);
9053
9225
  const confirm = await promptConfirm(
9054
- chalk94.red("Overwrite existing CLAUDE.md?"),
9226
+ chalk95.red("Overwrite existing CLAUDE.md?"),
9055
9227
  false
9056
9228
  );
9057
9229
  if (!confirm) {
@@ -9067,7 +9239,7 @@ async function syncClaudeMd(claudeDir, targetBase) {
9067
9239
  // src/commands/sync/syncSettings.ts
9068
9240
  import * as fs23 from "fs";
9069
9241
  import * as path41 from "path";
9070
- import chalk95 from "chalk";
9242
+ import chalk96 from "chalk";
9071
9243
  async function syncSettings(claudeDir, targetBase, options2) {
9072
9244
  const source = path41.join(claudeDir, "settings.json");
9073
9245
  const target = path41.join(targetBase, "settings.json");
@@ -9083,14 +9255,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
9083
9255
  if (mergedContent !== normalizedTarget) {
9084
9256
  if (!options2?.yes) {
9085
9257
  console.log(
9086
- chalk95.yellow(
9258
+ chalk96.yellow(
9087
9259
  "\n\u26A0\uFE0F Warning: settings.json differs from existing file"
9088
9260
  )
9089
9261
  );
9090
9262
  console.log();
9091
9263
  printDiff(targetContent, mergedContent);
9092
9264
  const confirm = await promptConfirm(
9093
- chalk95.red("Overwrite existing settings.json?"),
9265
+ chalk96.red("Overwrite existing settings.json?"),
9094
9266
  false
9095
9267
  );
9096
9268
  if (!confirm) {
@@ -9127,7 +9299,7 @@ function syncCommands(claudeDir, targetBase) {
9127
9299
  }
9128
9300
 
9129
9301
  // src/commands/update.ts
9130
- import { execSync as execSync38 } from "child_process";
9302
+ import { execSync as execSync39 } from "child_process";
9131
9303
  import * as path43 from "path";
9132
9304
  function isGlobalNpmInstall(dir) {
9133
9305
  try {
@@ -9135,7 +9307,7 @@ function isGlobalNpmInstall(dir) {
9135
9307
  if (resolved.split(path43.sep).includes("node_modules")) {
9136
9308
  return true;
9137
9309
  }
9138
- const globalPrefix = execSync38("npm prefix -g", { stdio: "pipe" }).toString().trim();
9310
+ const globalPrefix = execSync39("npm prefix -g", { stdio: "pipe" }).toString().trim();
9139
9311
  return resolved.toLowerCase().startsWith(path43.resolve(globalPrefix).toLowerCase());
9140
9312
  } catch {
9141
9313
  return false;
@@ -9146,18 +9318,18 @@ async function update() {
9146
9318
  console.log(`Assist is installed at: ${installDir}`);
9147
9319
  if (isGitRepo(installDir)) {
9148
9320
  console.log("Detected git repo installation, pulling latest...");
9149
- execSync38("git pull", { cwd: installDir, stdio: "inherit" });
9321
+ execSync39("git pull", { cwd: installDir, stdio: "inherit" });
9150
9322
  console.log("Installing dependencies...");
9151
- execSync38("npm i", { cwd: installDir, stdio: "inherit" });
9323
+ execSync39("npm i", { cwd: installDir, stdio: "inherit" });
9152
9324
  console.log("Building...");
9153
- execSync38("npm run build", { cwd: installDir, stdio: "inherit" });
9325
+ execSync39("npm run build", { cwd: installDir, stdio: "inherit" });
9154
9326
  console.log("Syncing commands...");
9155
- execSync38("assist sync", { stdio: "inherit" });
9327
+ execSync39("assist sync", { stdio: "inherit" });
9156
9328
  } else if (isGlobalNpmInstall(installDir)) {
9157
9329
  console.log("Detected global npm installation, updating...");
9158
- execSync38("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
9330
+ execSync39("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
9159
9331
  console.log("Syncing commands...");
9160
- execSync38("assist sync", { stdio: "inherit" });
9332
+ execSync39("assist sync", { stdio: "inherit" });
9161
9333
  } else {
9162
9334
  console.error(
9163
9335
  "Could not determine installation method. Expected a git repo or global npm install."
@@ -9194,6 +9366,7 @@ program.command("notify").description(
9194
9366
  "Show notification from Claude Code hook (reads JSON from stdin)"
9195
9367
  ).action(notify);
9196
9368
  program.command("update").description("Update assist to the latest version and sync commands").action(update);
9369
+ program.command("screenshot").description("Capture a screenshot of a running application window").argument("<process>", "Name of the running process (e.g. notepad, code)").action(screenshot);
9197
9370
  registerCliHook(program);
9198
9371
  registerJira(program);
9199
9372
  registerPrs(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.132.0",
3
+ "version": "0.134.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {