@staff0rd/assist 0.323.0 → 0.324.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +61 -47
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -166,7 +166,7 @@ The first backlog command in a repository that still has a local `.assist/backlo
166
166
  - `assist verify --measure` - After the run, print a summary table of each command's status and duration (slowest first) plus a wall-clock total
167
167
  - `assist verify init` - Add verify scripts to a project (writes to `assist.yml` by default; pass `--package-json` to write to `package.json` scripts instead)
168
168
  - `assist verify hardcoded-colors` - Check for hardcoded hex colors in src/ (supports `hardcodedColors.ignore` globs in config)
169
- - `assist verify comment-policy` - Flag comments added on changed lines (staged + unstaged) unless they carry a justification marker; supports `commentPolicy.markers` and `commentPolicy.ignore` globs in config
169
+ - `assist verify block-comments` - Fail on any comment on a changed line (staged + unstaged), whether newly added or edited, reporting each offending file:line; functional machine directives are exempt and `blockComments.ignore` globs in config scope which files are scanned
170
170
  - `assist verify forbidden-strings` - Check configured JSON files for disallowed values. Each `forbiddenStrings` rule names a `file`, a list of dot-`paths` to inspect (string or string[] values are scanned; other types skipped), and a `disallowed` wildcard matched via minimatch; any matching value fails the check. Zero rules is a no-op that passes
171
171
  - `assist lint [-f, --fix]` - Run lint checks for conventions not enforced by oxlint (use `-f` to auto-fix)
172
172
  - `assist lint init` - Initialize oxlint with baseline linter config
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.323.0",
9
+ version: "0.324.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -215,9 +215,8 @@ var assistConfigSchema = z2.strictObject({
215
215
  hardcodedColors: z2.strictObject({
216
216
  ignore: z2.array(z2.string()).default([])
217
217
  }).optional(),
218
- commentPolicy: z2.strictObject({
219
- ignore: z2.array(z2.string()).default([]),
220
- markers: z2.array(z2.string()).default(["HACK:", "why:"])
218
+ blockComments: z2.strictObject({
219
+ ignore: z2.array(z2.string()).default([])
221
220
  }).optional(),
222
221
  restructure: z2.strictObject({
223
222
  ignore: z2.array(z2.string()).default([])
@@ -1753,13 +1752,13 @@ function lint(options2 = {}) {
1753
1752
  import { execSync as execSync12 } from "child_process";
1754
1753
  import { basename, resolve as resolve5 } from "path";
1755
1754
 
1756
- // src/commands/verify/commentPolicy/findAddedComments.ts
1755
+ // src/commands/verify/blockComments/findComments.ts
1757
1756
  import { execSync as execSync6 } from "child_process";
1758
1757
  import fs13 from "fs";
1759
1758
  import { minimatch } from "minimatch";
1760
1759
  import { Project as Project2 } from "ts-morph";
1761
1760
 
1762
- // src/commands/verify/commentPolicy/collectComments.ts
1761
+ // src/commands/verify/blockComments/collectComments.ts
1763
1762
  function collectComments(sourceFile) {
1764
1763
  const seen = /* @__PURE__ */ new Set();
1765
1764
  const comments3 = [];
@@ -1796,7 +1795,7 @@ function parseMaintainabilityOverride(content) {
1796
1795
  return void 0;
1797
1796
  }
1798
1797
 
1799
- // src/commands/verify/commentPolicy/isCommentExempt.ts
1798
+ // src/commands/verify/blockComments/isCommentExempt.ts
1800
1799
  var MACHINE_DIRECTIVES = [
1801
1800
  "oxlint-disable",
1802
1801
  "oxlint-enable",
@@ -1812,13 +1811,12 @@ var MACHINE_DIRECTIVES = [
1812
1811
  "@vitest-environment",
1813
1812
  MAINTAINABILITY_OVERRIDE_MARKER
1814
1813
  ];
1815
- function isCommentExempt(text6, markers) {
1814
+ function isCommentExempt(text6) {
1816
1815
  const lower = text6.toLowerCase();
1817
- if (MACHINE_DIRECTIVES.some((d) => lower.includes(d))) return true;
1818
- return markers.some((m) => lower.includes(m.toLowerCase()));
1816
+ return MACHINE_DIRECTIVES.some((d) => lower.includes(d));
1819
1817
  }
1820
1818
 
1821
- // src/commands/verify/commentPolicy/parseDiffAddedLines.ts
1819
+ // src/commands/verify/blockComments/parseDiffAddedLines.ts
1822
1820
  var FILE_HEADER = /^\+\+\+ (?:b\/)?(.+)$/;
1823
1821
  var HUNK_HEADER = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
1824
1822
  function parseDiffAddedLines(diff2) {
@@ -1846,15 +1844,14 @@ function parseDiffAddedLines(diff2) {
1846
1844
  }
1847
1845
  set.add(newLine);
1848
1846
  newLine++;
1849
- } else if (line.startsWith("-")) {
1850
- } else {
1847
+ } else if (!line.startsWith("-")) {
1851
1848
  newLine++;
1852
1849
  }
1853
1850
  }
1854
1851
  return added;
1855
1852
  }
1856
1853
 
1857
- // src/commands/verify/commentPolicy/findAddedComments.ts
1854
+ // src/commands/verify/blockComments/findComments.ts
1858
1855
  var SOURCE_EXTENSIONS = [".ts", ".tsx", ".cts", ".mts", ".js", ".jsx"];
1859
1856
  function toSingleLine(text6) {
1860
1857
  return text6.replace(/\s+/g, " ").trim();
@@ -1864,7 +1861,7 @@ function shouldScan(file, ignoreGlobs) {
1864
1861
  if (ignoreGlobs.some((glob) => minimatch(file, glob))) return false;
1865
1862
  return fs13.existsSync(file);
1866
1863
  }
1867
- function findAddedComments(options2) {
1864
+ function findComments(options2) {
1868
1865
  const diff2 = execSync6("git diff HEAD", {
1869
1866
  encoding: "utf8",
1870
1867
  maxBuffer: 64 * 1024 * 1024
@@ -1881,7 +1878,7 @@ function findAddedComments(options2) {
1881
1878
  for (const { pos, text: text6 } of collectComments(sourceFile)) {
1882
1879
  const { line } = sourceFile.getLineAndColumnAtPos(pos);
1883
1880
  if (!lines.has(line)) continue;
1884
- if (isCommentExempt(text6, options2.markers)) continue;
1881
+ if (isCommentExempt(text6)) continue;
1885
1882
  findings.push({ file, line, text: toSingleLine(text6) });
1886
1883
  }
1887
1884
  }
@@ -1889,29 +1886,22 @@ function findAddedComments(options2) {
1889
1886
  return findings;
1890
1887
  }
1891
1888
 
1892
- // src/commands/verify/commentPolicy/index.ts
1893
- var DEFAULT_MARKERS = ["HACK:", "why:"];
1894
- function commentPolicy() {
1895
- const config = loadConfig().commentPolicy;
1896
- const markers = config?.markers ?? DEFAULT_MARKERS;
1897
- const ignoreGlobs = config?.ignore ?? [];
1898
- const findings = findAddedComments({ markers, ignoreGlobs });
1889
+ // src/commands/verify/blockComments/index.ts
1890
+ function blockComments() {
1891
+ const ignoreGlobs = loadConfig().blockComments?.ignore ?? [];
1892
+ const findings = findComments({ ignoreGlobs });
1899
1893
  if (findings.length === 0) {
1900
- console.log("No undocumented comments on changed lines.");
1894
+ console.log("No comments on changed lines.");
1901
1895
  process.exit(0);
1902
1896
  }
1903
- console.log("Comments added on changed lines:\n");
1897
+ console.log("Comments on changed lines:\n");
1904
1898
  for (const { file, line, text: text6 } of findings) {
1905
1899
  console.log(`${file}:${line} \u2192 ${text6}`);
1906
1900
  }
1907
1901
  console.log(`
1908
1902
  Total: ${findings.length} comment(s)`);
1909
1903
  console.log(
1910
- "\nDon't comment standard logic or syntax \u2014 only unintuitive complexity or a hack."
1911
- );
1912
- console.log("Prefer self-documenting code.");
1913
- console.log(
1914
- `Remove each comment, or justify it inline with a ${markers.map((m) => `'${m}'`).join(" or ")} marker.`
1904
+ "\nEvery comment on a changed line fails this gate, whether you added or edited it. Remove them and let the code document itself."
1915
1905
  );
1916
1906
  process.exit(1);
1917
1907
  }
@@ -18894,7 +18884,9 @@ function registerVerify(program2) {
18894
18884
  "Write scripts to package.json instead of assist.yml"
18895
18885
  ).action(init3);
18896
18886
  verifyCommand.command("hardcoded-colors").description("Check for hardcoded hex colors in src/").action(hardcodedColors);
18897
- verifyCommand.command("comment-policy").description("Check for undocumented comments added on changed lines").action(commentPolicy);
18887
+ verifyCommand.command("block-comments").description(
18888
+ "Fail on any comment on a changed line, whether added or edited"
18889
+ ).action(blockComments);
18898
18890
  verifyCommand.command("no-venv").description("Check that no venv folders exist in the repo").action(noVenv);
18899
18891
  verifyCommand.command("forbidden-strings").description(
18900
18892
  "Check configured JSON files for values matching forbiddenStrings rules"
@@ -21038,7 +21030,18 @@ async function forwardWindowsCreate(conn, state, client, data) {
21038
21030
  try {
21039
21031
  await conn.ensure();
21040
21032
  daemonLog("windows proxy: connection ready, forwarding create");
21041
- state.pendingCreators.push(client);
21033
+ const timer = setTimeout(() => {
21034
+ const index3 = state.pendingCreators.findIndex((p) => p.timer === timer);
21035
+ if (index3 === -1) return;
21036
+ state.pendingCreators.splice(index3, 1);
21037
+ daemonLog(`windows proxy: create timed out (cwd=${data.cwd})`);
21038
+ sendTo(client, {
21039
+ type: "error",
21040
+ message: "Windows session did not start: the Windows daemon did not respond."
21041
+ });
21042
+ }, state.createTimeoutMs);
21043
+ timer.unref?.();
21044
+ state.pendingCreators.push({ client, timer });
21042
21045
  conn.write(stripOutboundSessionId(data));
21043
21046
  } catch (error) {
21044
21047
  const message = error instanceof Error ? error.message : String(error);
@@ -21068,11 +21071,13 @@ function versionsMatch(a, b) {
21068
21071
 
21069
21072
  // src/commands/sessions/daemon/WindowsProxyState.ts
21070
21073
  var MAX_SCROLLBACK2 = 256 * 1024;
21071
- function createState(broadcast2, onSessionsChanged, onVersionMismatch) {
21074
+ var DEFAULT_CREATE_TIMEOUT_MS = 15e3;
21075
+ function createState(broadcast2, onSessionsChanged, onVersionMismatch, createTimeoutMs = DEFAULT_CREATE_TIMEOUT_MS) {
21072
21076
  return {
21073
21077
  windowsSessions: [],
21074
21078
  scrollback: /* @__PURE__ */ new Map(),
21075
21079
  pendingCreators: [],
21080
+ createTimeoutMs,
21076
21081
  broadcast: broadcast2,
21077
21082
  onSessionsChanged,
21078
21083
  onVersionMismatch
@@ -21081,6 +21086,20 @@ function createState(broadcast2, onSessionsChanged, onVersionMismatch) {
21081
21086
  function resetState(state) {
21082
21087
  state.windowsSessions = [];
21083
21088
  state.scrollback.clear();
21089
+ for (const { timer } of state.pendingCreators) clearTimeout(timer);
21090
+ state.pendingCreators = [];
21091
+ }
21092
+ function takePendingCreator(state) {
21093
+ const pending = state.pendingCreators.shift();
21094
+ if (!pending) return void 0;
21095
+ clearTimeout(pending.timer);
21096
+ return pending.client;
21097
+ }
21098
+ function failPendingCreators(state, message) {
21099
+ for (const { client, timer } of state.pendingCreators) {
21100
+ clearTimeout(timer);
21101
+ sendTo(client, { type: "error", message });
21102
+ }
21084
21103
  state.pendingCreators = [];
21085
21104
  }
21086
21105
  function replayScrollback2(state, client) {
@@ -21130,7 +21149,7 @@ function handleHello(state, msg) {
21130
21149
  }
21131
21150
  function handleCreated(state, msg) {
21132
21151
  daemonLog(`windows daemon: created session ${nsId(msg)}`);
21133
- const client = state.pendingCreators.shift();
21152
+ const client = takePendingCreator(state);
21134
21153
  if (client)
21135
21154
  sendTo(client, {
21136
21155
  type: "created",
@@ -21165,11 +21184,7 @@ function nsId(msg) {
21165
21184
  // src/commands/sessions/daemon/handleWindowsClose.ts
21166
21185
  function handleWindowsClose(state) {
21167
21186
  daemonLog("windows proxy: connection to windows daemon closed");
21168
- for (const client of state.pendingCreators)
21169
- sendTo(client, {
21170
- type: "error",
21171
- message: "Windows daemon connection closed"
21172
- });
21187
+ failPendingCreators(state, "Windows daemon connection closed");
21173
21188
  resetState(state);
21174
21189
  state.onSessionsChanged();
21175
21190
  }
@@ -21359,12 +21374,10 @@ async function autoHealWindowsDaemon(conn, state, heal, version2) {
21359
21374
  }
21360
21375
  }
21361
21376
  function notifyHealing(state) {
21362
- for (const client of state.pendingCreators)
21363
- sendTo(client, {
21364
- type: "error",
21365
- message: "Windows host is out of date; updating it now \u2014 reselect the repo once the update finishes."
21366
- });
21367
- state.pendingCreators = [];
21377
+ failPendingCreators(
21378
+ state,
21379
+ "Windows host is out of date; updating it now \u2014 reselect the repo once the update finishes."
21380
+ );
21368
21381
  }
21369
21382
 
21370
21383
  // src/commands/sessions/daemon/WindowsVersionHealer.ts
@@ -21412,11 +21425,12 @@ var WindowsProxy = class {
21412
21425
  healer;
21413
21426
  // why: on the Windows host the proxy would dial its own bridge, self-feeding a log loop and connect churn.
21414
21427
  enabled = process.platform !== "win32";
21415
- constructor(clients, onSessionsChanged, connect4 = defaultConnect, heal = healWindowsDaemon) {
21428
+ constructor(clients, onSessionsChanged, connect4 = defaultConnect, heal = healWindowsDaemon, createTimeoutMs) {
21416
21429
  this.state = createState(
21417
21430
  (msg) => broadcast(clients, msg),
21418
21431
  onSessionsChanged,
21419
- (version2) => void this.healer.onMismatch(version2)
21432
+ (version2) => void this.healer.onMismatch(version2),
21433
+ createTimeoutMs
21420
21434
  );
21421
21435
  this.conn = new WindowsConnection({
21422
21436
  connect: connect4,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.323.0",
3
+ "version": "0.324.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {