@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 +3 -0
- package/claude/commands/screenshot.md +9 -0
- package/claude/settings.json +3 -0
- package/dist/index.js +223 -50
- 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
|
|
@@ -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`).
|
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
|
});
|
|
@@ -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
|
-
|
|
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
|
-
|
|
5062
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
7668
|
+
return new Promise((resolve7) => {
|
|
7664
7669
|
rl.question(question, (answer) => {
|
|
7665
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
9127
|
+
import chalk94 from "chalk";
|
|
8956
9128
|
|
|
8957
9129
|
// src/commands/buildLimitsSegment.ts
|
|
8958
|
-
import
|
|
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
|
|
8982
|
-
if (projected > 100) return
|
|
8983
|
-
if (projected > 75) return
|
|
8984
|
-
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);
|
|
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
|
-
|
|
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
|
|
9017
|
-
if (pct > 40) return
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
9321
|
+
execSync39("git pull", { cwd: installDir, stdio: "inherit" });
|
|
9150
9322
|
console.log("Installing dependencies...");
|
|
9151
|
-
|
|
9323
|
+
execSync39("npm i", { cwd: installDir, stdio: "inherit" });
|
|
9152
9324
|
console.log("Building...");
|
|
9153
|
-
|
|
9325
|
+
execSync39("npm run build", { cwd: installDir, stdio: "inherit" });
|
|
9154
9326
|
console.log("Syncing commands...");
|
|
9155
|
-
|
|
9327
|
+
execSync39("assist sync", { stdio: "inherit" });
|
|
9156
9328
|
} else if (isGlobalNpmInstall(installDir)) {
|
|
9157
9329
|
console.log("Detected global npm installation, updating...");
|
|
9158
|
-
|
|
9330
|
+
execSync39("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
|
|
9159
9331
|
console.log("Syncing commands...");
|
|
9160
|
-
|
|
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);
|