@staff0rd/assist 0.306.0 → 0.308.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
@@ -93,6 +93,7 @@ After installation, the `assist` command will be available globally. You can als
93
93
  - `assist prs list-comments` - List all comments on the current branch's pull request
94
94
  - `assist prs fixed <comment-id> <sha>` - Reply with commit link and resolve thread
95
95
  - `assist prs wontfix <comment-id> <reason>` - Reply with reason and resolve thread
96
+ - `assist prs reply <comment-id> <body>` - Reply to a comment thread without resolving it
96
97
  - `assist prs comment <path> <line> <body>` - Add a line comment to the pending review
97
98
  - `assist review [number] [options]` - Run Claude and Codex in parallel to review the open PR for the current branch. The diff is fetched from GitHub (base SHA → head SHA via `gh pr diff`), so stale local base branches don't pollute the review; fails fast if no PR is open. By default, prompts before posting line-bound comments and then prompts again to submit the pending review (defaulting to no). Findings whose lines fall outside the diff are skipped with a warning rather than being silently dropped by GitHub. Cached `claude.md` / `codex.md` / `synthesis.md` are reused when present; if any reviewer is re-run, the synthesis is invalidated.
98
99
  - `[number]` - Run `gh pr checkout <number>` first, then review that PR's branch. If the checkout fails (dirty working tree, unknown PR number), the review aborts
@@ -224,7 +225,8 @@ The first backlog command in a repository that still has a local `.assist/backlo
224
225
  - `assist sql mutate "<sql>" [connection]` - Execute a mutating SQL statement and print rows affected (rejects non-mutating statements like pure SELECTs)
225
226
  - `assist sql tables [connection]` - List tables in the connected database (via INFORMATION_SCHEMA.TABLES)
226
227
  - `assist sql columns <table> [connection]` - List columns for a table (use `schema.table` for non-default schema; via INFORMATION_SCHEMA.COLUMNS)
227
- - `assist netcap [-p, --port <port>]` - Start a local receiver that captures browser network traffic to a JSONL file (default `~/.assist/netcap/capture.jsonl`), paired with the netcap browser extension. Sends permissive CORS, logs each ping/capture live, and runs until Ctrl-C (default port `8723`). See [netcap browser extension](#netcap-browser-extension)
228
+ - `assist netcap [-p, --port <port>] [-o, --out <dir>] [-f, --filter <pattern>]` - Start a local receiver that captures browser network traffic to a JSONL file (`capture.jsonl` under `--out`, default `~/.assist/netcap`), paired with the netcap browser extension. Sends permissive CORS, logs each ping/capture live, and runs until Ctrl-C, then prints a capture count (default port `8723`). `--filter` bakes a URL substring into the extension so only matching requests forward. See [netcap browser extension](#netcap-browser-extension)
229
+ - `assist netcap extract [file]` - Parse a netcap capture file into structured posts (`text`, `markdown` with mentions linked, `author`, `mentions`, `hashtags`, `links`, `relatedPosts`, and `postedAt` decoded from the activity id) and write them to `posts.json` beside the capture (defaults to `~/.assist/netcap/capture.jsonl`). Reads both the LinkedIn SDUI (rsc-action) responses rendered on first paint and the voyager GraphQL profile-updates responses loaded on scroll, deduped by activity urn keeping the richest copy of each post
228
230
  - `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`)
229
231
  - `assist handover save --summary <s>` - Save a session handover note to the backlog DB (content read from stdin), scoped by the repo's git origin. Appends a new row each call; `--summary` is the one-line description shown when recalling
230
232
  - `assist handover list` - List unrecalled handovers for this repo, most recent first, one per line as tab-separated `id`, ISO-8601 created timestamp, and one-line summary. Prints nothing when none are pending
@@ -277,11 +279,13 @@ When `commit.pull` is enabled in config, `assist draft`, `assist bug`, `assist r
277
279
 
278
280
  ## netcap browser extension
279
281
 
280
- `assist netcap` only runs the receiver; the browser side is a raw Manifest V3 extension (no build step) under `netcap-extension/`. A MAIN-world content script patches `fetch`/`XMLHttpRequest` to capture `{url, method, status, requestBody, responseBody, timestamp}`, relays each entry to the background service worker (`window.postMessage` → `chrome.runtime.sendMessage`), and the background worker POSTs it to the receiver. Forwarding happens in the background context, so the page's CSP (`connect-src`) never blocks it — the limitation that killed the earlier console-paste approach.
282
+ `assist netcap` only runs the receiver; the browser side is a raw Manifest V3 extension (no build step) under `netcap-extension/`. A MAIN-world content script patches `fetch`/`XMLHttpRequest` to capture `{url, method, status, requestBody, responseBody, timestamp}`, relays each entry to the background service worker (`window.postMessage` → `chrome.runtime.sendMessage`), and the background worker POSTs it to the receiver. The XHR patch reads the response across every `responseType` (`json`, `blob`, `arraybuffer`, `document`, not just `text`) — LinkedIn's voyager GraphQL calls use `responseType: "json"`, which exposes no `responseText`, so reading `responseText` alone captured those bodies empty. Forwarding happens in the background context, so the page's CSP (`connect-src`) never blocks it — the limitation that killed the earlier console-paste approach.
281
283
 
282
284
  1. Run `assist netcap` — it prints the receiver URL, the capture file path, and the absolute path to the extension directory to load. The receiver host/port is baked into the extension's `background.js` at this point. Under WSL (where the browser runs on the Windows host, cannot load from a WSL path, and cannot reach the receiver on `localhost` because WSL2 localhost forwarding is unreliable) it copies the extension to `C:\tools\netcap-extension`, targets the WSL VM's IP, and prints that Windows path instead. Re-run `assist netcap` after a reboot (the WSL IP can change) and reload the extension.
283
285
  2. Load the unpacked extension:
284
286
  - **Firefox**: open `about:debugging#/runtime/this-firefox` → **Load Temporary Add-on…** → pick `manifest.json` inside the printed extension directory. (Requires Firefox 128+ for MAIN-world content scripts.)
285
287
  - **Chrome**: open `chrome://extensions`, enable **Developer mode**, click **Load unpacked**, and select the extension directory.
286
288
  3. On load the background worker pings the receiver; `ping from extension` appears in the `assist netcap` log, confirming browser→server connectivity.
287
- 4. Browse a site (e.g. LinkedIn activity); matching requests append to the capture file live and survive page refreshes. The extension targets `http://127.0.0.1:8723`; if you change `--port`, edit `RECEIVER` in `netcap-extension/background.js`.
289
+ 4. Browse a site (e.g. LinkedIn activity); matching requests append to the capture file live and survive page refreshes (re-loading the extension appends to the same file). Press Ctrl-C to stop the receiver; it prints how many entries were captured.
290
+
291
+ `assist netcap` bakes the receiver host/port and the optional `--filter` substring into the extension's `background.js` at startup, so you don't normally edit it by hand. With `--filter <pattern>`, the background worker only forwards requests whose URL contains `<pattern>` (case-sensitive substring); the matching logic mirrors `matchesNetcapFilter` in `src/commands/netcap`. Use `--out <dir>` to write `capture.jsonl` into a different directory.
@@ -17,6 +17,8 @@ Ask short, targeted questions one at a time to fill in any gaps. You need three
17
17
  2. **Expected behavior** — what should happen?
18
18
  3. **Actual behavior** — what happens instead?
19
19
 
20
+ Before asking the user about existing functionality — how a feature currently works, what a command does, where something lives — investigate the codebase first and answer it yourself. Only ask the user about things the code can't tell you (their intent, what they observed, how to reproduce it).
21
+
20
22
  Skip questions the user has already answered. Stop asking as soon as you have enough to write a clear bug report — don't over-interrogate.
21
23
 
22
24
  ## Step 3: Propose the item
@@ -3,17 +3,25 @@
3
3
  // startup it pings the receiver (the connectivity spike) and thereafter
4
4
  // forwards every captured entry relayed from the content scripts.
5
5
  const RECEIVER = "http://127.0.0.1:8723/";
6
+ const FILTER = "";
6
7
 
7
8
  function ping() {
8
9
  fetch(`${RECEIVER}ping`, { method: "GET" }).catch(() => {});
9
10
  }
10
11
 
12
+ // why: duplicates matchesNetcapFilter (src/commands/netcap) — plain JS, no TS import
13
+ function matchesFilter(url) {
14
+ if (!FILTER) return true;
15
+ return typeof url === "string" && url.includes(FILTER);
16
+ }
17
+
11
18
  ping();
12
19
  chrome.runtime.onInstalled?.addListener(ping);
13
20
  chrome.runtime.onStartup?.addListener(ping);
14
21
 
15
22
  chrome.runtime.onMessage.addListener((message) => {
16
23
  if (!message || message.type !== "netcap-entry") return;
24
+ if (!matchesFilter(message.entry && message.entry.url)) return;
17
25
  fetch(RECEIVER, {
18
26
  method: "POST",
19
27
  headers: { "Content-Type": "application/json" },
@@ -57,6 +57,24 @@
57
57
  };
58
58
  }
59
59
 
60
+ // why: voyager GraphQL uses responseType "json"/"blob" (no responseText), so reading responseText alone captured them empty
61
+ function xhrResponseBody(xhr) {
62
+ try {
63
+ const type = xhr.responseType;
64
+ if (type === "" || type === "text") return xhr.responseText ?? "";
65
+ if (type === "json")
66
+ return xhr.response == null ? "" : JSON.stringify(xhr.response);
67
+ if (type === "arraybuffer")
68
+ return new TextDecoder().decode(xhr.response);
69
+ if (type === "document")
70
+ return xhr.response?.documentElement?.outerHTML ?? "";
71
+ if (type === "blob") return xhr.response;
72
+ } catch {
73
+ // ignore: response unavailable for this responseType
74
+ }
75
+ return "";
76
+ }
77
+
60
78
  const OriginalXHR = window.XMLHttpRequest;
61
79
  if (OriginalXHR) {
62
80
  const open = OriginalXHR.prototype.open;
@@ -70,22 +88,24 @@
70
88
  if (meta) {
71
89
  meta.requestBody = bodyToString(body);
72
90
  this.addEventListener("loadend", () => {
73
- let responseBody = "";
74
- try {
75
- if (this.responseType === "" || this.responseType === "text") {
76
- responseBody = this.responseText;
77
- }
78
- } catch {
79
- // ignore: responseText unavailable for this responseType
91
+ const emit = (responseBody) =>
92
+ post({
93
+ url: meta.url,
94
+ method: meta.method,
95
+ status: this.status,
96
+ requestBody: meta.requestBody ?? null,
97
+ responseBody,
98
+ timestamp: Date.now(),
99
+ });
100
+ const value = xhrResponseBody(this);
101
+ if (value instanceof Blob) {
102
+ value
103
+ .text()
104
+ .then(emit)
105
+ .catch(() => emit(""));
106
+ } else {
107
+ emit(value);
80
108
  }
81
- post({
82
- url: meta.url,
83
- method: meta.method,
84
- status: this.status,
85
- requestBody: meta.requestBody ?? null,
86
- responseBody,
87
- timestamp: Date.now(),
88
- });
89
109
  });
90
110
  }
91
111
  return send.call(this, body);