@staff0rd/assist 0.133.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 +2 -0
- package/claude/commands/screenshot.md +9 -0
- package/claude/settings.json +3 -0
- package/dist/index.js +207 -36
- package/package.json +1 -1
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
|
|
@@ -142,6 +143,7 @@ After installation, the `assist` command will be available globally. You can als
|
|
|
142
143
|
- `assist seq query <filter> -c <connection>` - Query using a specific connection
|
|
143
144
|
- `assist seq query <filter> --json` - Output raw JSON
|
|
144
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`)
|
|
145
147
|
- `assist complexity <pattern>` - Analyze a file (all metrics if single match, maintainability if multiple)
|
|
146
148
|
- `assist complexity cyclomatic [pattern]` - Calculate cyclomatic complexity per function
|
|
147
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`).
|
package/claude/settings.json
CHANGED
|
@@ -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.
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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", () =>
|
|
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((
|
|
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
|
-
|
|
3208
|
+
resolve7(stdout || stderr || "");
|
|
3206
3209
|
}
|
|
3207
3210
|
);
|
|
3208
3211
|
});
|
|
@@ -6704,7 +6707,7 @@ function getViolations(pattern2, options2 = {}, maxLines = DEFAULT_MAX_LINES) {
|
|
|
6704
6707
|
|
|
6705
6708
|
// src/commands/refactor/check/index.ts
|
|
6706
6709
|
function runScript(script, cwd) {
|
|
6707
|
-
return new Promise((
|
|
6710
|
+
return new Promise((resolve7) => {
|
|
6708
6711
|
const child = spawn3("npm", ["run", script], {
|
|
6709
6712
|
stdio: "pipe",
|
|
6710
6713
|
shell: true,
|
|
@@ -6718,7 +6721,7 @@ function runScript(script, cwd) {
|
|
|
6718
6721
|
output += data.toString();
|
|
6719
6722
|
});
|
|
6720
6723
|
child.on("close", (code) => {
|
|
6721
|
-
|
|
6724
|
+
resolve7({ script, code: code ?? 1, output });
|
|
6722
6725
|
});
|
|
6723
6726
|
});
|
|
6724
6727
|
}
|
|
@@ -7662,9 +7665,9 @@ function createReadlineInterface() {
|
|
|
7662
7665
|
});
|
|
7663
7666
|
}
|
|
7664
7667
|
function askQuestion(rl, question) {
|
|
7665
|
-
return new Promise((
|
|
7668
|
+
return new Promise((resolve7) => {
|
|
7666
7669
|
rl.question(question, (answer) => {
|
|
7667
|
-
|
|
7670
|
+
resolve7(answer.trim());
|
|
7668
7671
|
});
|
|
7669
7672
|
});
|
|
7670
7673
|
}
|
|
@@ -8605,7 +8608,7 @@ function extractCode(url, expectedState) {
|
|
|
8605
8608
|
return code;
|
|
8606
8609
|
}
|
|
8607
8610
|
function waitForCallback(port, expectedState) {
|
|
8608
|
-
return new Promise((
|
|
8611
|
+
return new Promise((resolve7, reject) => {
|
|
8609
8612
|
const timeout = setTimeout(() => {
|
|
8610
8613
|
server.close();
|
|
8611
8614
|
reject(new Error("Authorization timed out after 120 seconds"));
|
|
@@ -8622,7 +8625,7 @@ function waitForCallback(port, expectedState) {
|
|
|
8622
8625
|
const code = extractCode(url, expectedState);
|
|
8623
8626
|
respondHtml(res, 200, "Authorization successful!");
|
|
8624
8627
|
server.close();
|
|
8625
|
-
|
|
8628
|
+
resolve7(code);
|
|
8626
8629
|
} catch (err) {
|
|
8627
8630
|
respondHtml(res, 400, err.message);
|
|
8628
8631
|
server.close();
|
|
@@ -8953,11 +8956,178 @@ function run2(name, args) {
|
|
|
8953
8956
|
);
|
|
8954
8957
|
}
|
|
8955
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
|
+
|
|
8956
9126
|
// src/commands/statusLine.ts
|
|
8957
|
-
import
|
|
9127
|
+
import chalk94 from "chalk";
|
|
8958
9128
|
|
|
8959
9129
|
// src/commands/buildLimitsSegment.ts
|
|
8960
|
-
import
|
|
9130
|
+
import chalk93 from "chalk";
|
|
8961
9131
|
var FIVE_HOUR_SECONDS = 5 * 3600;
|
|
8962
9132
|
var SEVEN_DAY_SECONDS = 7 * 86400;
|
|
8963
9133
|
function formatTimeLeft(resetsAt) {
|
|
@@ -8980,10 +9150,10 @@ function projectUsage(pct, resetsAt, windowSeconds) {
|
|
|
8980
9150
|
function colorizeRateLimit(pct, resetsAt, windowSeconds) {
|
|
8981
9151
|
const label2 = `${Math.round(pct)}%`;
|
|
8982
9152
|
const projected = projectUsage(pct, resetsAt, windowSeconds);
|
|
8983
|
-
if (projected == null) return
|
|
8984
|
-
if (projected > 100) return
|
|
8985
|
-
if (projected > 75) return
|
|
8986
|
-
return
|
|
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);
|
|
8987
9157
|
}
|
|
8988
9158
|
function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel) {
|
|
8989
9159
|
const timeLabel = resetsAt ? formatTimeLeft(resetsAt) : fallbackLabel;
|
|
@@ -9009,14 +9179,14 @@ function buildLimitsSegment(rateLimits) {
|
|
|
9009
9179
|
}
|
|
9010
9180
|
|
|
9011
9181
|
// src/commands/statusLine.ts
|
|
9012
|
-
|
|
9182
|
+
chalk94.level = 3;
|
|
9013
9183
|
function formatNumber(num) {
|
|
9014
9184
|
return num.toLocaleString("en-US");
|
|
9015
9185
|
}
|
|
9016
9186
|
function colorizePercent(pct) {
|
|
9017
9187
|
const label2 = `${Math.round(pct)}%`;
|
|
9018
|
-
if (pct > 80) return
|
|
9019
|
-
if (pct > 40) return
|
|
9188
|
+
if (pct > 80) return chalk94.red(label2);
|
|
9189
|
+
if (pct > 40) return chalk94.yellow(label2);
|
|
9020
9190
|
return label2;
|
|
9021
9191
|
}
|
|
9022
9192
|
async function statusLine() {
|
|
@@ -9039,7 +9209,7 @@ import { fileURLToPath as fileURLToPath7 } from "url";
|
|
|
9039
9209
|
// src/commands/sync/syncClaudeMd.ts
|
|
9040
9210
|
import * as fs22 from "fs";
|
|
9041
9211
|
import * as path40 from "path";
|
|
9042
|
-
import
|
|
9212
|
+
import chalk95 from "chalk";
|
|
9043
9213
|
async function syncClaudeMd(claudeDir, targetBase) {
|
|
9044
9214
|
const source = path40.join(claudeDir, "CLAUDE.md");
|
|
9045
9215
|
const target = path40.join(targetBase, "CLAUDE.md");
|
|
@@ -9048,12 +9218,12 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
9048
9218
|
const targetContent = fs22.readFileSync(target, "utf-8");
|
|
9049
9219
|
if (sourceContent !== targetContent) {
|
|
9050
9220
|
console.log(
|
|
9051
|
-
|
|
9221
|
+
chalk95.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
9052
9222
|
);
|
|
9053
9223
|
console.log();
|
|
9054
9224
|
printDiff(targetContent, sourceContent);
|
|
9055
9225
|
const confirm = await promptConfirm(
|
|
9056
|
-
|
|
9226
|
+
chalk95.red("Overwrite existing CLAUDE.md?"),
|
|
9057
9227
|
false
|
|
9058
9228
|
);
|
|
9059
9229
|
if (!confirm) {
|
|
@@ -9069,7 +9239,7 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
9069
9239
|
// src/commands/sync/syncSettings.ts
|
|
9070
9240
|
import * as fs23 from "fs";
|
|
9071
9241
|
import * as path41 from "path";
|
|
9072
|
-
import
|
|
9242
|
+
import chalk96 from "chalk";
|
|
9073
9243
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
9074
9244
|
const source = path41.join(claudeDir, "settings.json");
|
|
9075
9245
|
const target = path41.join(targetBase, "settings.json");
|
|
@@ -9085,14 +9255,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
9085
9255
|
if (mergedContent !== normalizedTarget) {
|
|
9086
9256
|
if (!options2?.yes) {
|
|
9087
9257
|
console.log(
|
|
9088
|
-
|
|
9258
|
+
chalk96.yellow(
|
|
9089
9259
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
9090
9260
|
)
|
|
9091
9261
|
);
|
|
9092
9262
|
console.log();
|
|
9093
9263
|
printDiff(targetContent, mergedContent);
|
|
9094
9264
|
const confirm = await promptConfirm(
|
|
9095
|
-
|
|
9265
|
+
chalk96.red("Overwrite existing settings.json?"),
|
|
9096
9266
|
false
|
|
9097
9267
|
);
|
|
9098
9268
|
if (!confirm) {
|
|
@@ -9129,7 +9299,7 @@ function syncCommands(claudeDir, targetBase) {
|
|
|
9129
9299
|
}
|
|
9130
9300
|
|
|
9131
9301
|
// src/commands/update.ts
|
|
9132
|
-
import { execSync as
|
|
9302
|
+
import { execSync as execSync39 } from "child_process";
|
|
9133
9303
|
import * as path43 from "path";
|
|
9134
9304
|
function isGlobalNpmInstall(dir) {
|
|
9135
9305
|
try {
|
|
@@ -9137,7 +9307,7 @@ function isGlobalNpmInstall(dir) {
|
|
|
9137
9307
|
if (resolved.split(path43.sep).includes("node_modules")) {
|
|
9138
9308
|
return true;
|
|
9139
9309
|
}
|
|
9140
|
-
const globalPrefix =
|
|
9310
|
+
const globalPrefix = execSync39("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
9141
9311
|
return resolved.toLowerCase().startsWith(path43.resolve(globalPrefix).toLowerCase());
|
|
9142
9312
|
} catch {
|
|
9143
9313
|
return false;
|
|
@@ -9148,18 +9318,18 @@ async function update() {
|
|
|
9148
9318
|
console.log(`Assist is installed at: ${installDir}`);
|
|
9149
9319
|
if (isGitRepo(installDir)) {
|
|
9150
9320
|
console.log("Detected git repo installation, pulling latest...");
|
|
9151
|
-
|
|
9321
|
+
execSync39("git pull", { cwd: installDir, stdio: "inherit" });
|
|
9152
9322
|
console.log("Installing dependencies...");
|
|
9153
|
-
|
|
9323
|
+
execSync39("npm i", { cwd: installDir, stdio: "inherit" });
|
|
9154
9324
|
console.log("Building...");
|
|
9155
|
-
|
|
9325
|
+
execSync39("npm run build", { cwd: installDir, stdio: "inherit" });
|
|
9156
9326
|
console.log("Syncing commands...");
|
|
9157
|
-
|
|
9327
|
+
execSync39("assist sync", { stdio: "inherit" });
|
|
9158
9328
|
} else if (isGlobalNpmInstall(installDir)) {
|
|
9159
9329
|
console.log("Detected global npm installation, updating...");
|
|
9160
|
-
|
|
9330
|
+
execSync39("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
|
|
9161
9331
|
console.log("Syncing commands...");
|
|
9162
|
-
|
|
9332
|
+
execSync39("assist sync", { stdio: "inherit" });
|
|
9163
9333
|
} else {
|
|
9164
9334
|
console.error(
|
|
9165
9335
|
"Could not determine installation method. Expected a git repo or global npm install."
|
|
@@ -9196,6 +9366,7 @@ program.command("notify").description(
|
|
|
9196
9366
|
"Show notification from Claude Code hook (reads JSON from stdin)"
|
|
9197
9367
|
).action(notify);
|
|
9198
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);
|
|
9199
9370
|
registerCliHook(program);
|
|
9200
9371
|
registerJira(program);
|
|
9201
9372
|
registerPrs(program);
|