@staff0rd/assist 0.42.1 → 0.42.2

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.
@@ -44,6 +44,6 @@
44
44
  "Skill(transcript-summarise)",
45
45
  "WebFetch(domain:staffordwilliams.com)"
46
46
  ],
47
- "deny": ["Bash(git commit:*)", "Bash(npm run:*)"]
47
+ "deny": ["Bash(git commit:*)", "Bash(npm run:*)", "Bash(npx assist:*)"]
48
48
  }
49
49
  }
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { Command } from "commander";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@staff0rd/assist",
10
- version: "0.42.1",
10
+ version: "0.42.2",
11
11
  type: "module",
12
12
  main: "dist/index.js",
13
13
  bin: {
@@ -28,7 +28,7 @@ var package_default = {
28
28
  "verify:types": "tsc --noEmit",
29
29
  "verify:knip": "knip --no-progress --treat-config-hints-as-errors",
30
30
  "verify:duplicate-code": "jscpd --format 'typescript,tsx' --exitCode 1 --ignore '**/*.test.*' -r consoleFull src",
31
- "verify:maintainability": "assist complexity maintainability ./src --threshold 50",
31
+ "verify:maintainability": "assist complexity maintainability ./src --threshold 55",
32
32
  "verify:custom-lint": "assist lint"
33
33
  },
34
34
  keywords: [],
@@ -212,72 +212,159 @@ function commit(message) {
212
212
  import chalk3 from "chalk";
213
213
 
214
214
  // src/commands/complexity/shared.ts
215
+ import fs2 from "fs";
216
+ import path2 from "path";
217
+ import chalk2 from "chalk";
218
+ import ts4 from "typescript";
219
+
220
+ // src/commands/complexity/findSourceFiles.ts
215
221
  import fs from "fs";
216
222
  import path from "path";
217
- import chalk2 from "chalk";
218
223
  import { minimatch } from "minimatch";
219
- import ts3 from "typescript";
224
+ function applyIgnoreGlobs(files) {
225
+ const { complexity } = loadConfig();
226
+ return files.filter(
227
+ (f) => !complexity.ignore.some((glob) => minimatch(f, glob))
228
+ );
229
+ }
230
+ function walk(dir, results) {
231
+ if (!fs.existsSync(dir)) {
232
+ return;
233
+ }
234
+ const extensions = [".ts", ".tsx"];
235
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
236
+ for (const entry of entries) {
237
+ const fullPath = path.join(dir, entry.name);
238
+ if (entry.isDirectory()) {
239
+ if (entry.name !== "node_modules" && entry.name !== ".git") {
240
+ walk(fullPath, results);
241
+ }
242
+ } else if (entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext))) {
243
+ results.push(fullPath);
244
+ }
245
+ }
246
+ }
247
+ function findSourceFiles(pattern2, baseDir = ".") {
248
+ const results = [];
249
+ if (pattern2.includes("*")) {
250
+ walk(baseDir, results);
251
+ return applyIgnoreGlobs(results.filter((f) => minimatch(f, pattern2)));
252
+ }
253
+ if (fs.existsSync(pattern2) && fs.statSync(pattern2).isFile()) {
254
+ return [pattern2];
255
+ }
256
+ if (fs.existsSync(pattern2) && fs.statSync(pattern2).isDirectory()) {
257
+ walk(pattern2, results);
258
+ return applyIgnoreGlobs(results);
259
+ }
260
+ walk(baseDir, results);
261
+ return applyIgnoreGlobs(results.filter((f) => minimatch(f, pattern2)));
262
+ }
220
263
 
221
- // src/commands/complexity/calculateCyclomaticComplexity.ts
264
+ // src/commands/complexity/getNodeName.ts
222
265
  import ts from "typescript";
266
+ function getNodeName(node) {
267
+ if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
268
+ return node.name?.text ?? "<anonymous>";
269
+ }
270
+ if (ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) {
271
+ if (ts.isIdentifier(node.name)) {
272
+ return node.name.text;
273
+ }
274
+ if (ts.isStringLiteral(node.name)) {
275
+ return node.name.text;
276
+ }
277
+ return "<computed>";
278
+ }
279
+ if (ts.isArrowFunction(node)) {
280
+ const parent = node.parent;
281
+ if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
282
+ return parent.name.text;
283
+ }
284
+ if (ts.isPropertyAssignment(parent) && ts.isIdentifier(parent.name)) {
285
+ return parent.name.text;
286
+ }
287
+ return "<arrow>";
288
+ }
289
+ if (ts.isGetAccessor(node) || ts.isSetAccessor(node)) {
290
+ const prefix = ts.isGetAccessor(node) ? "get " : "set ";
291
+ if (ts.isIdentifier(node.name)) {
292
+ return `${prefix}${node.name.text}`;
293
+ }
294
+ return `${prefix}<computed>`;
295
+ }
296
+ if (ts.isConstructorDeclaration(node)) {
297
+ return "constructor";
298
+ }
299
+ return "<unknown>";
300
+ }
301
+ function hasFunctionBody(node) {
302
+ if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node) || ts.isConstructorDeclaration(node)) {
303
+ return node.body !== void 0;
304
+ }
305
+ return false;
306
+ }
307
+
308
+ // src/commands/complexity/calculateCyclomaticComplexity.ts
309
+ import ts2 from "typescript";
223
310
  var complexityKinds = /* @__PURE__ */ new Set([
224
- ts.SyntaxKind.IfStatement,
225
- ts.SyntaxKind.ForStatement,
226
- ts.SyntaxKind.ForInStatement,
227
- ts.SyntaxKind.ForOfStatement,
228
- ts.SyntaxKind.WhileStatement,
229
- ts.SyntaxKind.DoStatement,
230
- ts.SyntaxKind.CaseClause,
231
- ts.SyntaxKind.CatchClause,
232
- ts.SyntaxKind.ConditionalExpression
311
+ ts2.SyntaxKind.IfStatement,
312
+ ts2.SyntaxKind.ForStatement,
313
+ ts2.SyntaxKind.ForInStatement,
314
+ ts2.SyntaxKind.ForOfStatement,
315
+ ts2.SyntaxKind.WhileStatement,
316
+ ts2.SyntaxKind.DoStatement,
317
+ ts2.SyntaxKind.CaseClause,
318
+ ts2.SyntaxKind.CatchClause,
319
+ ts2.SyntaxKind.ConditionalExpression
233
320
  ]);
234
321
  var logicalOperators = /* @__PURE__ */ new Set([
235
- ts.SyntaxKind.AmpersandAmpersandToken,
236
- ts.SyntaxKind.BarBarToken,
237
- ts.SyntaxKind.QuestionQuestionToken
322
+ ts2.SyntaxKind.AmpersandAmpersandToken,
323
+ ts2.SyntaxKind.BarBarToken,
324
+ ts2.SyntaxKind.QuestionQuestionToken
238
325
  ]);
239
326
  function calculateCyclomaticComplexity(node) {
240
327
  let complexity = 1;
241
328
  function visit(n) {
242
329
  if (complexityKinds.has(n.kind)) {
243
330
  complexity++;
244
- } else if (ts.isBinaryExpression(n) && logicalOperators.has(n.operatorToken.kind)) {
331
+ } else if (ts2.isBinaryExpression(n) && logicalOperators.has(n.operatorToken.kind)) {
245
332
  complexity++;
246
333
  }
247
- ts.forEachChild(n, visit);
334
+ ts2.forEachChild(n, visit);
248
335
  }
249
- ts.forEachChild(node, visit);
336
+ ts2.forEachChild(node, visit);
250
337
  return complexity;
251
338
  }
252
339
 
253
340
  // src/commands/complexity/calculateHalstead.ts
254
- import ts2 from "typescript";
341
+ import ts3 from "typescript";
255
342
  var operatorChecks = [
256
- (n) => ts2.isBinaryExpression(n) ? n.operatorToken.getText() : void 0,
257
- (n) => ts2.isPrefixUnaryExpression(n) || ts2.isPostfixUnaryExpression(n) ? ts2.tokenToString(n.operator) ?? "" : void 0,
258
- (n) => ts2.isCallExpression(n) ? "()" : void 0,
259
- (n) => ts2.isPropertyAccessExpression(n) ? "." : void 0,
260
- (n) => ts2.isElementAccessExpression(n) ? "[]" : void 0,
261
- (n) => ts2.isConditionalExpression(n) ? "?:" : void 0,
262
- (n) => ts2.isReturnStatement(n) ? "return" : void 0,
263
- (n) => ts2.isIfStatement(n) ? "if" : void 0,
264
- (n) => ts2.isForStatement(n) || ts2.isForInStatement(n) || ts2.isForOfStatement(n) ? "for" : void 0,
265
- (n) => ts2.isWhileStatement(n) ? "while" : void 0,
266
- (n) => ts2.isDoStatement(n) ? "do" : void 0,
267
- (n) => ts2.isSwitchStatement(n) ? "switch" : void 0,
268
- (n) => ts2.isCaseClause(n) ? "case" : void 0,
269
- (n) => ts2.isDefaultClause(n) ? "default" : void 0,
270
- (n) => ts2.isBreakStatement(n) ? "break" : void 0,
271
- (n) => ts2.isContinueStatement(n) ? "continue" : void 0,
272
- (n) => ts2.isThrowStatement(n) ? "throw" : void 0,
273
- (n) => ts2.isTryStatement(n) ? "try" : void 0,
274
- (n) => ts2.isCatchClause(n) ? "catch" : void 0,
275
- (n) => ts2.isNewExpression(n) ? "new" : void 0,
276
- (n) => ts2.isTypeOfExpression(n) ? "typeof" : void 0,
277
- (n) => ts2.isAwaitExpression(n) ? "await" : void 0
343
+ (n) => ts3.isBinaryExpression(n) ? n.operatorToken.getText() : void 0,
344
+ (n) => ts3.isPrefixUnaryExpression(n) || ts3.isPostfixUnaryExpression(n) ? ts3.tokenToString(n.operator) ?? "" : void 0,
345
+ (n) => ts3.isCallExpression(n) ? "()" : void 0,
346
+ (n) => ts3.isPropertyAccessExpression(n) ? "." : void 0,
347
+ (n) => ts3.isElementAccessExpression(n) ? "[]" : void 0,
348
+ (n) => ts3.isConditionalExpression(n) ? "?:" : void 0,
349
+ (n) => ts3.isReturnStatement(n) ? "return" : void 0,
350
+ (n) => ts3.isIfStatement(n) ? "if" : void 0,
351
+ (n) => ts3.isForStatement(n) || ts3.isForInStatement(n) || ts3.isForOfStatement(n) ? "for" : void 0,
352
+ (n) => ts3.isWhileStatement(n) ? "while" : void 0,
353
+ (n) => ts3.isDoStatement(n) ? "do" : void 0,
354
+ (n) => ts3.isSwitchStatement(n) ? "switch" : void 0,
355
+ (n) => ts3.isCaseClause(n) ? "case" : void 0,
356
+ (n) => ts3.isDefaultClause(n) ? "default" : void 0,
357
+ (n) => ts3.isBreakStatement(n) ? "break" : void 0,
358
+ (n) => ts3.isContinueStatement(n) ? "continue" : void 0,
359
+ (n) => ts3.isThrowStatement(n) ? "throw" : void 0,
360
+ (n) => ts3.isTryStatement(n) ? "try" : void 0,
361
+ (n) => ts3.isCatchClause(n) ? "catch" : void 0,
362
+ (n) => ts3.isNewExpression(n) ? "new" : void 0,
363
+ (n) => ts3.isTypeOfExpression(n) ? "typeof" : void 0,
364
+ (n) => ts3.isAwaitExpression(n) ? "await" : void 0
278
365
  ];
279
366
  function classifyNode(n, operators, operands) {
280
- if (ts2.isIdentifier(n) || ts2.isNumericLiteral(n) || ts2.isStringLiteral(n)) {
367
+ if (ts3.isIdentifier(n) || ts3.isNumericLiteral(n) || ts3.isStringLiteral(n)) {
281
368
  operands.set(n.text, (operands.get(n.text) ?? 0) + 1);
282
369
  return;
283
370
  }
@@ -314,9 +401,9 @@ function calculateHalstead(node) {
314
401
  const operands = /* @__PURE__ */ new Map();
315
402
  function visit(n) {
316
403
  classifyNode(n, operators, operands);
317
- ts2.forEachChild(n, visit);
404
+ ts3.forEachChild(n, visit);
318
405
  }
319
- ts2.forEachChild(node, visit);
406
+ ts3.forEachChild(node, visit);
320
407
  return computeHalsteadMetrics(operators, operands);
321
408
  }
322
409
 
@@ -358,53 +445,18 @@ function countSloc(content) {
358
445
  }
359
446
 
360
447
  // src/commands/complexity/shared.ts
361
- function getNodeName(node) {
362
- if (ts3.isFunctionDeclaration(node) || ts3.isFunctionExpression(node)) {
363
- return node.name?.text ?? "<anonymous>";
364
- }
365
- if (ts3.isMethodDeclaration(node) || ts3.isMethodSignature(node)) {
366
- if (ts3.isIdentifier(node.name)) {
367
- return node.name.text;
368
- }
369
- if (ts3.isStringLiteral(node.name)) {
370
- return node.name.text;
371
- }
372
- return "<computed>";
373
- }
374
- if (ts3.isArrowFunction(node)) {
375
- const parent = node.parent;
376
- if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) {
377
- return parent.name.text;
378
- }
379
- if (ts3.isPropertyAssignment(parent) && ts3.isIdentifier(parent.name)) {
380
- return parent.name.text;
381
- }
382
- return "<arrow>";
383
- }
384
- if (ts3.isGetAccessor(node) || ts3.isSetAccessor(node)) {
385
- const prefix = ts3.isGetAccessor(node) ? "get " : "set ";
386
- if (ts3.isIdentifier(node.name)) {
387
- return `${prefix}${node.name.text}`;
388
- }
389
- return `${prefix}<computed>`;
390
- }
391
- if (ts3.isConstructorDeclaration(node)) {
392
- return "constructor";
393
- }
394
- return "<unknown>";
395
- }
396
448
  function createSourceFromFile(filePath) {
397
- const content = fs.readFileSync(filePath, "utf-8");
398
- return ts3.createSourceFile(
399
- path.basename(filePath),
449
+ const content = fs2.readFileSync(filePath, "utf-8");
450
+ return ts4.createSourceFile(
451
+ path2.basename(filePath),
400
452
  content,
401
- ts3.ScriptTarget.Latest,
453
+ ts4.ScriptTarget.Latest,
402
454
  true,
403
- filePath.endsWith(".tsx") ? ts3.ScriptKind.TSX : ts3.ScriptKind.TS
455
+ filePath.endsWith(".tsx") ? ts4.ScriptKind.TSX : ts4.ScriptKind.TS
404
456
  );
405
457
  }
406
458
  function withSourceFiles(pattern2, callback) {
407
- const files = findSourceFilesWithPattern(pattern2);
459
+ const files = findSourceFiles(pattern2);
408
460
  if (files.length === 0) {
409
461
  console.log(chalk2.yellow("No files found matching pattern"));
410
462
  return void 0;
@@ -418,56 +470,11 @@ function forEachFunction(files, callback) {
418
470
  if (hasFunctionBody(node)) {
419
471
  callback(file, getNodeName(node), node);
420
472
  }
421
- ts3.forEachChild(node, visit);
473
+ ts4.forEachChild(node, visit);
422
474
  };
423
475
  visit(sourceFile);
424
476
  }
425
477
  }
426
- function applyIgnoreGlobs(files) {
427
- const { complexity } = loadConfig();
428
- return files.filter(
429
- (f) => !complexity.ignore.some((glob) => minimatch(f, glob))
430
- );
431
- }
432
- function findSourceFilesWithPattern(pattern2, baseDir = ".") {
433
- const results = [];
434
- const extensions = [".ts", ".tsx"];
435
- function walk(dir) {
436
- if (!fs.existsSync(dir)) {
437
- return;
438
- }
439
- const entries = fs.readdirSync(dir, { withFileTypes: true });
440
- for (const entry of entries) {
441
- const fullPath = path.join(dir, entry.name);
442
- if (entry.isDirectory()) {
443
- if (entry.name !== "node_modules" && entry.name !== ".git") {
444
- walk(fullPath);
445
- }
446
- } else if (entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext))) {
447
- results.push(fullPath);
448
- }
449
- }
450
- }
451
- if (pattern2.includes("*")) {
452
- walk(baseDir);
453
- return applyIgnoreGlobs(results.filter((f) => minimatch(f, pattern2)));
454
- }
455
- if (fs.existsSync(pattern2) && fs.statSync(pattern2).isFile()) {
456
- return [pattern2];
457
- }
458
- if (fs.existsSync(pattern2) && fs.statSync(pattern2).isDirectory()) {
459
- walk(pattern2);
460
- return applyIgnoreGlobs(results);
461
- }
462
- walk(baseDir);
463
- return applyIgnoreGlobs(results.filter((f) => minimatch(f, pattern2)));
464
- }
465
- function hasFunctionBody(node) {
466
- if (ts3.isFunctionDeclaration(node) || ts3.isFunctionExpression(node) || ts3.isArrowFunction(node) || ts3.isMethodDeclaration(node) || ts3.isGetAccessor(node) || ts3.isSetAccessor(node) || ts3.isConstructorDeclaration(node)) {
467
- return node.body !== void 0;
468
- }
469
- return false;
470
- }
471
478
 
472
479
  // src/commands/complexity/cyclomatic.ts
473
480
  async function cyclomatic(pattern2 = "**/*.ts", options = {}) {
@@ -533,7 +540,7 @@ Analyzed ${results.length} functions across ${files.length} files`
533
540
  }
534
541
 
535
542
  // src/commands/complexity/maintainability.ts
536
- import fs2 from "fs";
543
+ import fs3 from "fs";
537
544
  import chalk5 from "chalk";
538
545
  function calculateMaintainabilityIndex(halsteadVolume, cyclomaticComplexity, sloc2) {
539
546
  if (halsteadVolume === 0 || sloc2 === 0) {
@@ -546,7 +553,7 @@ async function maintainability(pattern2 = "**/*.ts", options = {}) {
546
553
  withSourceFiles(pattern2, (files) => {
547
554
  const fileMetrics = /* @__PURE__ */ new Map();
548
555
  for (const file of files) {
549
- const content = fs2.readFileSync(file, "utf-8");
556
+ const content = fs3.readFileSync(file, "utf-8");
550
557
  fileMetrics.set(file, { sloc: countSloc(content), functions: [] });
551
558
  }
552
559
  forEachFunction(files, (file, _name, node) => {
@@ -585,10 +592,10 @@ async function maintainability(pattern2 = "**/*.ts", options = {}) {
585
592
  console.log(chalk5.dim(`
586
593
  Analyzed ${results.length} files`));
587
594
  if (filtered.length > 0 && threshold !== void 0) {
588
- console.log(
595
+ console.error(
589
596
  chalk5.red(
590
597
  `
591
- Fail: ${filtered.length} file(s) below threshold ${threshold}. Maintainability index (0\u2013100) is derived from Halstead volume, cyclomatic complexity, and lines of code.`
598
+ Fail: ${filtered.length} file(s) below threshold ${threshold}. Maintainability index (0\u2013100) is derived from Halstead volume, cyclomatic complexity, and lines of code. Try 'complexity cyclomatic', 'complexity halstead', or 'complexity sloc' to help identify which metric is contributing most. For larger files, start by extracting responsibilities into smaller files.`
592
599
  )
593
600
  );
594
601
  process.exit(1);
@@ -597,14 +604,14 @@ Fail: ${filtered.length} file(s) below threshold ${threshold}. Maintainability i
597
604
  }
598
605
 
599
606
  // src/commands/complexity/sloc.ts
600
- import fs3 from "fs";
607
+ import fs4 from "fs";
601
608
  import chalk6 from "chalk";
602
609
  async function sloc(pattern2 = "**/*.ts", options = {}) {
603
610
  withSourceFiles(pattern2, (files) => {
604
611
  const results = [];
605
612
  let hasViolation = false;
606
613
  for (const file of files) {
607
- const content = fs3.readFileSync(file, "utf-8");
614
+ const content = fs4.readFileSync(file, "utf-8");
608
615
  const lines = countSloc(content);
609
616
  results.push({ file, lines });
610
617
  if (options.threshold !== void 0 && lines > options.threshold) {
@@ -631,8 +638,8 @@ Total: ${total} lines across ${files.length} files`)
631
638
  // src/commands/config/index.ts
632
639
  import chalk7 from "chalk";
633
640
  import { stringify as stringifyYaml2 } from "yaml";
634
- function getNestedValue(obj, path19) {
635
- const keys = path19.split(".");
641
+ function getNestedValue(obj, path20) {
642
+ const keys = path20.split(".");
636
643
  let current = obj;
637
644
  for (const key of keys) {
638
645
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -642,8 +649,8 @@ function getNestedValue(obj, path19) {
642
649
  }
643
650
  return current;
644
651
  }
645
- function setNestedValue(obj, path19, value) {
646
- const keys = path19.split(".");
652
+ function setNestedValue(obj, path20, value) {
653
+ const keys = path20.split(".");
647
654
  const result = { ...obj };
648
655
  let current = result;
649
656
  for (let i = 0; i < keys.length - 1; i++) {
@@ -670,8 +677,8 @@ function configSet(key, value) {
670
677
  const result = assistConfigSchema.safeParse(updated);
671
678
  if (!result.success) {
672
679
  for (const issue of result.error.issues) {
673
- const path19 = issue.path.length > 0 ? issue.path.join(".") : key;
674
- console.error(chalk7.red(`${path19}: ${issue.message}`));
680
+ const path20 = issue.path.length > 0 ? issue.path.join(".") : key;
681
+ console.error(chalk7.red(`${path20}: ${issue.message}`));
675
682
  }
676
683
  process.exit(1);
677
684
  }
@@ -1221,18 +1228,18 @@ async function promptMultiselect(message, options) {
1221
1228
  }
1222
1229
 
1223
1230
  // src/shared/readPackageJson.ts
1224
- import * as fs4 from "fs";
1225
- import * as path2 from "path";
1231
+ import * as fs5 from "fs";
1232
+ import * as path3 from "path";
1226
1233
  import chalk17 from "chalk";
1227
1234
  function findPackageJson() {
1228
- const packageJsonPath = path2.join(process.cwd(), "package.json");
1229
- if (fs4.existsSync(packageJsonPath)) {
1235
+ const packageJsonPath = path3.join(process.cwd(), "package.json");
1236
+ if (fs5.existsSync(packageJsonPath)) {
1230
1237
  return packageJsonPath;
1231
1238
  }
1232
1239
  return null;
1233
1240
  }
1234
1241
  function readPackageJson(filePath) {
1235
- return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
1242
+ return JSON.parse(fs5.readFileSync(filePath, "utf-8"));
1236
1243
  }
1237
1244
  function requirePackageJson() {
1238
1245
  const packageJsonPath = findPackageJson();
@@ -1246,9 +1253,9 @@ function requirePackageJson() {
1246
1253
  function findPackageJsonWithVerifyScripts(startDir) {
1247
1254
  let currentDir = startDir;
1248
1255
  while (true) {
1249
- const packageJsonPath = path2.join(currentDir, "package.json");
1250
- if (fs4.existsSync(packageJsonPath)) {
1251
- const packageJson = JSON.parse(fs4.readFileSync(packageJsonPath, "utf-8"));
1256
+ const packageJsonPath = path3.join(currentDir, "package.json");
1257
+ if (fs5.existsSync(packageJsonPath)) {
1258
+ const packageJson = JSON.parse(fs5.readFileSync(packageJsonPath, "utf-8"));
1252
1259
  const scripts = packageJson.scripts || {};
1253
1260
  const verifyScripts = Object.keys(scripts).filter(
1254
1261
  (name) => name.startsWith("verify:")
@@ -1257,7 +1264,7 @@ function findPackageJsonWithVerifyScripts(startDir) {
1257
1264
  return { packageJsonPath, verifyScripts };
1258
1265
  }
1259
1266
  }
1260
- const parentDir = path2.dirname(currentDir);
1267
+ const parentDir = path3.dirname(currentDir);
1261
1268
  if (parentDir === currentDir) {
1262
1269
  return null;
1263
1270
  }
@@ -1279,11 +1286,11 @@ import chalk19 from "chalk";
1279
1286
 
1280
1287
  // src/commands/verify/installPackage.ts
1281
1288
  import { execSync as execSync7 } from "child_process";
1282
- import * as fs5 from "fs";
1283
- import * as path3 from "path";
1289
+ import * as fs6 from "fs";
1290
+ import * as path4 from "path";
1284
1291
  import chalk18 from "chalk";
1285
1292
  function writePackageJson(filePath, pkg) {
1286
- fs5.writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}
1293
+ fs6.writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}
1287
1294
  `);
1288
1295
  }
1289
1296
  function addScript(pkg, name, command) {
@@ -1306,18 +1313,18 @@ function installPackage(name, cwd) {
1306
1313
  }
1307
1314
  }
1308
1315
  function addToKnipIgnoreBinaries(cwd, binary) {
1309
- const knipJsonPath = path3.join(cwd, "knip.json");
1316
+ const knipJsonPath = path4.join(cwd, "knip.json");
1310
1317
  try {
1311
1318
  let knipConfig;
1312
- if (fs5.existsSync(knipJsonPath)) {
1313
- knipConfig = JSON.parse(fs5.readFileSync(knipJsonPath, "utf-8"));
1319
+ if (fs6.existsSync(knipJsonPath)) {
1320
+ knipConfig = JSON.parse(fs6.readFileSync(knipJsonPath, "utf-8"));
1314
1321
  } else {
1315
1322
  knipConfig = { $schema: "https://unpkg.com/knip@5/schema.json" };
1316
1323
  }
1317
1324
  const ignoreBinaries = knipConfig.ignoreBinaries ?? [];
1318
1325
  if (!ignoreBinaries.includes(binary)) {
1319
1326
  knipConfig.ignoreBinaries = [...ignoreBinaries, binary];
1320
- fs5.writeFileSync(
1327
+ fs6.writeFileSync(
1321
1328
  knipJsonPath,
1322
1329
  `${JSON.stringify(knipConfig, null, " ")}
1323
1330
  `
@@ -1352,11 +1359,11 @@ async function setupBuild(packageJsonPath, hasVite, hasTypescript) {
1352
1359
  }
1353
1360
 
1354
1361
  // src/commands/verify/setup/setupDuplicateCode.ts
1355
- import * as path4 from "path";
1362
+ import * as path5 from "path";
1356
1363
  import chalk20 from "chalk";
1357
1364
  async function setupDuplicateCode(packageJsonPath) {
1358
1365
  console.log(chalk20.blue("\nSetting up jscpd..."));
1359
- const cwd = path4.dirname(packageJsonPath);
1366
+ const cwd = path5.dirname(packageJsonPath);
1360
1367
  const pkg = readPackageJson(packageJsonPath);
1361
1368
  const hasJscpd = !!pkg.dependencies?.jscpd || !!pkg.devDependencies?.jscpd;
1362
1369
  if (!hasJscpd && !installPackage("jscpd", cwd)) {
@@ -1370,11 +1377,11 @@ async function setupDuplicateCode(packageJsonPath) {
1370
1377
  }
1371
1378
 
1372
1379
  // src/commands/verify/setup/setupHardcodedColors.ts
1373
- import * as path5 from "path";
1380
+ import * as path6 from "path";
1374
1381
  import chalk21 from "chalk";
1375
1382
  async function setupHardcodedColors(packageJsonPath, hasOpenColor) {
1376
1383
  console.log(chalk21.blue("\nSetting up hardcoded colors check..."));
1377
- const cwd = path5.dirname(packageJsonPath);
1384
+ const cwd = path6.dirname(packageJsonPath);
1378
1385
  if (!hasOpenColor) {
1379
1386
  installPackage("open-color", cwd);
1380
1387
  }
@@ -1387,11 +1394,11 @@ async function setupHardcodedColors(packageJsonPath, hasOpenColor) {
1387
1394
  }
1388
1395
 
1389
1396
  // src/commands/verify/setup/setupKnip.ts
1390
- import * as path6 from "path";
1397
+ import * as path7 from "path";
1391
1398
  import chalk22 from "chalk";
1392
1399
  async function setupKnip(packageJsonPath) {
1393
1400
  console.log(chalk22.blue("\nSetting up knip..."));
1394
- const cwd = path6.dirname(packageJsonPath);
1401
+ const cwd = path7.dirname(packageJsonPath);
1395
1402
  const pkg = readPackageJson(packageJsonPath);
1396
1403
  if (!pkg.devDependencies?.knip && !installPackage("knip", cwd)) {
1397
1404
  return;
@@ -1404,7 +1411,7 @@ async function setupKnip(packageJsonPath) {
1404
1411
  }
1405
1412
 
1406
1413
  // src/commands/verify/setup/setupLint.ts
1407
- import * as path7 from "path";
1414
+ import * as path8 from "path";
1408
1415
  import chalk24 from "chalk";
1409
1416
 
1410
1417
  // src/commands/lint/init.ts
@@ -1533,7 +1540,7 @@ async function init2() {
1533
1540
  // src/commands/verify/setup/setupLint.ts
1534
1541
  async function setupLint(packageJsonPath) {
1535
1542
  console.log(chalk24.blue("\nSetting up biome..."));
1536
- const cwd = path7.dirname(packageJsonPath);
1543
+ const cwd = path8.dirname(packageJsonPath);
1537
1544
  const pkg = readPackageJson(packageJsonPath);
1538
1545
  if (!pkg.devDependencies?.["@biomejs/biome"]) {
1539
1546
  if (!installPackage("@biomejs/biome", cwd)) {
@@ -1549,11 +1556,11 @@ async function setupLint(packageJsonPath) {
1549
1556
  }
1550
1557
 
1551
1558
  // src/commands/verify/setup/setupTest.ts
1552
- import * as path8 from "path";
1559
+ import * as path9 from "path";
1553
1560
  import chalk25 from "chalk";
1554
1561
  async function setupTest(packageJsonPath) {
1555
1562
  console.log(chalk25.blue("\nSetting up vitest..."));
1556
- const cwd = path8.dirname(packageJsonPath);
1563
+ const cwd = path9.dirname(packageJsonPath);
1557
1564
  const pkg = readPackageJson(packageJsonPath);
1558
1565
  if (!pkg.devDependencies?.vitest && !installPackage("vitest", cwd)) {
1559
1566
  return;
@@ -1738,33 +1745,33 @@ Added ${selected.length} verify script(s):`));
1738
1745
  }
1739
1746
 
1740
1747
  // src/commands/vscode/init.ts
1741
- import * as fs7 from "fs";
1742
- import * as path10 from "path";
1748
+ import * as fs8 from "fs";
1749
+ import * as path11 from "path";
1743
1750
  import chalk28 from "chalk";
1744
1751
 
1745
1752
  // src/commands/vscode/createLaunchJson.ts
1746
- import * as fs6 from "fs";
1747
- import * as path9 from "path";
1753
+ import * as fs7 from "fs";
1754
+ import * as path10 from "path";
1748
1755
  import chalk27 from "chalk";
1749
1756
  function ensureVscodeFolder() {
1750
- const vscodeDir = path9.join(process.cwd(), ".vscode");
1751
- if (!fs6.existsSync(vscodeDir)) {
1752
- fs6.mkdirSync(vscodeDir);
1757
+ const vscodeDir = path10.join(process.cwd(), ".vscode");
1758
+ if (!fs7.existsSync(vscodeDir)) {
1759
+ fs7.mkdirSync(vscodeDir);
1753
1760
  console.log(chalk27.dim("Created .vscode folder"));
1754
1761
  }
1755
1762
  }
1756
1763
  function removeVscodeFromGitignore() {
1757
- const gitignorePath = path9.join(process.cwd(), ".gitignore");
1758
- if (!fs6.existsSync(gitignorePath)) {
1764
+ const gitignorePath = path10.join(process.cwd(), ".gitignore");
1765
+ if (!fs7.existsSync(gitignorePath)) {
1759
1766
  return;
1760
1767
  }
1761
- const content = fs6.readFileSync(gitignorePath, "utf-8");
1768
+ const content = fs7.readFileSync(gitignorePath, "utf-8");
1762
1769
  const lines = content.split("\n");
1763
1770
  const filteredLines = lines.filter(
1764
1771
  (line) => !line.trim().toLowerCase().includes(".vscode")
1765
1772
  );
1766
1773
  if (filteredLines.length !== lines.length) {
1767
- fs6.writeFileSync(gitignorePath, filteredLines.join("\n"));
1774
+ fs7.writeFileSync(gitignorePath, filteredLines.join("\n"));
1768
1775
  console.log(chalk27.dim("Removed .vscode references from .gitignore"));
1769
1776
  }
1770
1777
  }
@@ -1780,8 +1787,8 @@ function createLaunchJson() {
1780
1787
  }
1781
1788
  ]
1782
1789
  };
1783
- const launchPath = path9.join(process.cwd(), ".vscode", "launch.json");
1784
- fs6.writeFileSync(launchPath, `${JSON.stringify(launchConfig, null, " ")}
1790
+ const launchPath = path10.join(process.cwd(), ".vscode", "launch.json");
1791
+ fs7.writeFileSync(launchPath, `${JSON.stringify(launchConfig, null, " ")}
1785
1792
  `);
1786
1793
  console.log(chalk27.green("Created .vscode/launch.json"));
1787
1794
  }
@@ -1793,8 +1800,8 @@ function createSettingsJson() {
1793
1800
  "source.organizeImports.biome": "explicit"
1794
1801
  }
1795
1802
  };
1796
- const settingsPath = path9.join(process.cwd(), ".vscode", "settings.json");
1797
- fs6.writeFileSync(settingsPath, `${JSON.stringify(settings, null, " ")}
1803
+ const settingsPath = path10.join(process.cwd(), ".vscode", "settings.json");
1804
+ fs7.writeFileSync(settingsPath, `${JSON.stringify(settings, null, " ")}
1798
1805
  `);
1799
1806
  console.log(chalk27.green("Created .vscode/settings.json"));
1800
1807
  }
@@ -1802,8 +1809,8 @@ function createExtensionsJson() {
1802
1809
  const extensions = {
1803
1810
  recommendations: ["biomejs.biome"]
1804
1811
  };
1805
- const extensionsPath = path9.join(process.cwd(), ".vscode", "extensions.json");
1806
- fs6.writeFileSync(
1812
+ const extensionsPath = path10.join(process.cwd(), ".vscode", "extensions.json");
1813
+ fs7.writeFileSync(
1807
1814
  extensionsPath,
1808
1815
  `${JSON.stringify(extensions, null, " ")}
1809
1816
  `
@@ -1813,11 +1820,11 @@ function createExtensionsJson() {
1813
1820
 
1814
1821
  // src/commands/vscode/init.ts
1815
1822
  function detectExistingSetup2(pkg) {
1816
- const vscodeDir = path10.join(process.cwd(), ".vscode");
1823
+ const vscodeDir = path11.join(process.cwd(), ".vscode");
1817
1824
  return {
1818
- hasVscodeFolder: fs7.existsSync(vscodeDir),
1819
- hasLaunchJson: fs7.existsSync(path10.join(vscodeDir, "launch.json")),
1820
- hasSettingsJson: fs7.existsSync(path10.join(vscodeDir, "settings.json")),
1825
+ hasVscodeFolder: fs8.existsSync(vscodeDir),
1826
+ hasLaunchJson: fs8.existsSync(path11.join(vscodeDir, "launch.json")),
1827
+ hasSettingsJson: fs8.existsSync(path11.join(vscodeDir, "settings.json")),
1821
1828
  hasVite: !!pkg.devDependencies?.vite || !!pkg.dependencies?.vite
1822
1829
  };
1823
1830
  }
@@ -1878,25 +1885,25 @@ async function init5() {
1878
1885
  }
1879
1886
 
1880
1887
  // src/commands/lint/runFileNameCheck.ts
1881
- import fs9 from "fs";
1882
- import path12 from "path";
1888
+ import fs10 from "fs";
1889
+ import path13 from "path";
1883
1890
  import chalk29 from "chalk";
1884
1891
 
1885
1892
  // src/shared/findSourceFiles.ts
1886
- import fs8 from "fs";
1887
- import path11 from "path";
1893
+ import fs9 from "fs";
1894
+ import path12 from "path";
1888
1895
  var EXTENSIONS = [".ts", ".tsx"];
1889
- function findSourceFiles(dir, options = {}) {
1896
+ function findSourceFiles2(dir, options = {}) {
1890
1897
  const { includeTests = true } = options;
1891
1898
  const results = [];
1892
- if (!fs8.existsSync(dir)) {
1899
+ if (!fs9.existsSync(dir)) {
1893
1900
  return results;
1894
1901
  }
1895
- const entries = fs8.readdirSync(dir, { withFileTypes: true });
1902
+ const entries = fs9.readdirSync(dir, { withFileTypes: true });
1896
1903
  for (const entry of entries) {
1897
- const fullPath = path11.join(dir, entry.name);
1904
+ const fullPath = path12.join(dir, entry.name);
1898
1905
  if (entry.isDirectory() && entry.name !== "node_modules") {
1899
- results.push(...findSourceFiles(fullPath, options));
1906
+ results.push(...findSourceFiles2(fullPath, options));
1900
1907
  } else if (entry.isFile() && EXTENSIONS.some((ext) => entry.name.endsWith(ext))) {
1901
1908
  if (!includeTests && entry.name.includes(".test.")) {
1902
1909
  continue;
@@ -1915,13 +1922,13 @@ function hasClassOrComponent(content) {
1915
1922
  return classPattern.test(content) || functionComponentPattern.test(content) || arrowComponentPattern.test(content);
1916
1923
  }
1917
1924
  function checkFileNames() {
1918
- const sourceFiles = findSourceFiles("src");
1925
+ const sourceFiles = findSourceFiles2("src");
1919
1926
  const violations = [];
1920
1927
  for (const filePath of sourceFiles) {
1921
- const fileName = path12.basename(filePath);
1928
+ const fileName = path13.basename(filePath);
1922
1929
  const nameWithoutExt = fileName.replace(/\.(ts|tsx)$/, "");
1923
1930
  if (/^[A-Z]/.test(nameWithoutExt)) {
1924
- const content = fs9.readFileSync(filePath, "utf-8");
1931
+ const content = fs10.readFileSync(filePath, "utf-8");
1925
1932
  if (!hasClassOrComponent(content)) {
1926
1933
  violations.push({ filePath, fileName });
1927
1934
  }
@@ -1958,7 +1965,7 @@ function runFileNameCheck() {
1958
1965
  }
1959
1966
 
1960
1967
  // src/commands/lint/runImportExtensionCheck.ts
1961
- import fs10 from "fs";
1968
+ import fs11 from "fs";
1962
1969
 
1963
1970
  // src/commands/lint/shared.ts
1964
1971
  import chalk30 from "chalk";
@@ -1984,7 +1991,7 @@ ${checkName} failed:
1984
1991
 
1985
1992
  // src/commands/lint/runImportExtensionCheck.ts
1986
1993
  function checkForImportExtensions(filePath) {
1987
- const content = fs10.readFileSync(filePath, "utf-8");
1994
+ const content = fs11.readFileSync(filePath, "utf-8");
1988
1995
  const lines = content.split("\n");
1989
1996
  const violations = [];
1990
1997
  const importExtensionPattern = /from\s+["']\..*\.(js|ts)["']/;
@@ -2001,7 +2008,7 @@ function checkForImportExtensions(filePath) {
2001
2008
  return violations;
2002
2009
  }
2003
2010
  function checkImportExtensions() {
2004
- const sourceFiles = findSourceFiles("src");
2011
+ const sourceFiles = findSourceFiles2("src");
2005
2012
  const violations = [];
2006
2013
  for (const filePath of sourceFiles) {
2007
2014
  violations.push(...checkForImportExtensions(filePath));
@@ -2018,9 +2025,9 @@ function runImportExtensionCheck() {
2018
2025
  }
2019
2026
 
2020
2027
  // src/commands/lint/runStaticImportCheck.ts
2021
- import fs11 from "fs";
2028
+ import fs12 from "fs";
2022
2029
  function checkForDynamicImports(filePath) {
2023
- const content = fs11.readFileSync(filePath, "utf-8");
2030
+ const content = fs12.readFileSync(filePath, "utf-8");
2024
2031
  const lines = content.split("\n");
2025
2032
  const violations = [];
2026
2033
  const requirePattern = /\brequire\s*\(/;
@@ -2038,7 +2045,7 @@ function checkForDynamicImports(filePath) {
2038
2045
  return violations;
2039
2046
  }
2040
2047
  function checkStaticImports() {
2041
- const sourceFiles = findSourceFiles("src");
2048
+ const sourceFiles = findSourceFiles2("src");
2042
2049
  const violations = [];
2043
2050
  for (const filePath of sourceFiles) {
2044
2051
  violations.push(...checkForDynamicImports(filePath));
@@ -2134,19 +2141,19 @@ function detectPlatform() {
2134
2141
 
2135
2142
  // src/commands/notify/showWindowsNotificationFromWsl.ts
2136
2143
  import { spawn } from "child_process";
2137
- import fs12 from "fs";
2144
+ import fs13 from "fs";
2138
2145
  import { createRequire } from "module";
2139
- import path13 from "path";
2146
+ import path14 from "path";
2140
2147
  var require2 = createRequire(import.meta.url);
2141
2148
  function getSnoreToastPath() {
2142
- const notifierPath = path13.dirname(require2.resolve("node-notifier"));
2143
- return path13.join(notifierPath, "vendor", "snoreToast", "snoretoast-x64.exe");
2149
+ const notifierPath = path14.dirname(require2.resolve("node-notifier"));
2150
+ return path14.join(notifierPath, "vendor", "snoreToast", "snoretoast-x64.exe");
2144
2151
  }
2145
2152
  function showWindowsNotificationFromWsl(options) {
2146
2153
  const { title, message, sound } = options;
2147
2154
  const snoreToastPath = getSnoreToastPath();
2148
2155
  try {
2149
- fs12.chmodSync(snoreToastPath, 493);
2156
+ fs13.chmodSync(snoreToastPath, 493);
2150
2157
  } catch {
2151
2158
  }
2152
2159
  const args = ["-t", title, "-m", message];
@@ -2347,7 +2354,6 @@ function fixed(commentId, sha) {
2347
2354
  }
2348
2355
 
2349
2356
  // src/commands/prs/listComments.ts
2350
- import { execSync as execSync13 } from "child_process";
2351
2357
  import { existsSync as existsSync12, mkdirSync as mkdirSync3, writeFileSync as writeFileSync11 } from "fs";
2352
2358
  import { join as join11 } from "path";
2353
2359
  import chalk31 from "chalk";
@@ -2358,8 +2364,53 @@ function isClaudeCode() {
2358
2364
  return process.env.CLAUDECODE !== void 0;
2359
2365
  }
2360
2366
 
2361
- // src/commands/prs/fetchThreadIds.ts
2367
+ // src/commands/prs/fetchReviewComments.ts
2362
2368
  import { execSync as execSync12 } from "child_process";
2369
+ function fetchReviewComments(org, repo, prNumber) {
2370
+ const result = execSync12(
2371
+ `gh api repos/${org}/${repo}/pulls/${prNumber}/reviews`,
2372
+ { encoding: "utf-8" }
2373
+ );
2374
+ if (!result.trim()) return [];
2375
+ const reviews = JSON.parse(result);
2376
+ return reviews.filter((r) => r.body).map(
2377
+ (r) => ({
2378
+ type: "review",
2379
+ id: r.id,
2380
+ user: r.user.login,
2381
+ state: r.state,
2382
+ body: r.body
2383
+ })
2384
+ );
2385
+ }
2386
+ function fetchLineComments(org, repo, prNumber, threadInfo) {
2387
+ const result = execSync12(
2388
+ `gh api repos/${org}/${repo}/pulls/${prNumber}/comments`,
2389
+ { encoding: "utf-8" }
2390
+ );
2391
+ if (!result.trim()) return [];
2392
+ const comments = JSON.parse(result);
2393
+ return comments.map(
2394
+ (c) => {
2395
+ const threadId = threadInfo.threadMap.get(c.id) ?? "";
2396
+ return {
2397
+ type: "line",
2398
+ id: c.id,
2399
+ threadId,
2400
+ user: c.user.login,
2401
+ path: c.path,
2402
+ line: c.line,
2403
+ body: c.body,
2404
+ diff_hunk: c.diff_hunk,
2405
+ html_url: c.html_url,
2406
+ resolved: threadInfo.resolvedThreadIds.has(threadId)
2407
+ };
2408
+ }
2409
+ );
2410
+ }
2411
+
2412
+ // src/commands/prs/fetchThreadIds.ts
2413
+ import { execSync as execSync13 } from "child_process";
2363
2414
  import { unlinkSync as unlinkSync3, writeFileSync as writeFileSync10 } from "fs";
2364
2415
  import { tmpdir as tmpdir2 } from "os";
2365
2416
  import { join as join10 } from "path";
@@ -2368,7 +2419,7 @@ function fetchThreadIds(org, repo, prNumber) {
2368
2419
  const queryFile = join10(tmpdir2(), `gh-query-${Date.now()}.graphql`);
2369
2420
  writeFileSync10(queryFile, THREAD_QUERY);
2370
2421
  try {
2371
- const result = execSync12(
2422
+ const result = execSync13(
2372
2423
  `gh api graphql -F query=@${queryFile} -F owner="${org}" -F repo="${repo}" -F prNumber=${prNumber}`,
2373
2424
  { encoding: "utf-8" }
2374
2425
  );
@@ -2439,48 +2490,6 @@ function writeCommentsCache(prNumber, comments) {
2439
2490
  const cachePath = join11(assistDir, `pr-${prNumber}-comments.yaml`);
2440
2491
  writeFileSync11(cachePath, stringify(cacheData));
2441
2492
  }
2442
- function fetchReviewComments(org, repo, prNumber) {
2443
- const result = execSync13(
2444
- `gh api repos/${org}/${repo}/pulls/${prNumber}/reviews`,
2445
- { encoding: "utf-8" }
2446
- );
2447
- if (!result.trim()) return [];
2448
- const reviews = JSON.parse(result);
2449
- return reviews.filter((r) => r.body).map(
2450
- (r) => ({
2451
- type: "review",
2452
- id: r.id,
2453
- user: r.user.login,
2454
- state: r.state,
2455
- body: r.body
2456
- })
2457
- );
2458
- }
2459
- function fetchLineComments(org, repo, prNumber, threadInfo) {
2460
- const result = execSync13(
2461
- `gh api repos/${org}/${repo}/pulls/${prNumber}/comments`,
2462
- { encoding: "utf-8" }
2463
- );
2464
- if (!result.trim()) return [];
2465
- const comments = JSON.parse(result);
2466
- return comments.map(
2467
- (c) => {
2468
- const threadId = threadInfo.threadMap.get(c.id) ?? "";
2469
- return {
2470
- type: "line",
2471
- id: c.id,
2472
- threadId,
2473
- user: c.user.login,
2474
- path: c.path,
2475
- line: c.line,
2476
- body: c.body,
2477
- diff_hunk: c.diff_hunk,
2478
- html_url: c.html_url,
2479
- resolved: threadInfo.resolvedThreadIds.has(threadId)
2480
- };
2481
- }
2482
- );
2483
- }
2484
2493
  async function listComments() {
2485
2494
  try {
2486
2495
  const prNumber = getCurrentPrNumber();
@@ -2647,21 +2656,21 @@ function wontfix(commentId, reason) {
2647
2656
 
2648
2657
  // src/commands/refactor/check.ts
2649
2658
  import { spawn as spawn2 } from "child_process";
2650
- import * as path14 from "path";
2659
+ import * as path15 from "path";
2651
2660
 
2652
2661
  // src/commands/refactor/getViolations.ts
2653
2662
  import { execSync as execSync16 } from "child_process";
2654
- import fs14 from "fs";
2663
+ import fs15 from "fs";
2655
2664
  import { minimatch as minimatch2 } from "minimatch";
2656
2665
 
2657
2666
  // src/commands/refactor/getIgnoredFiles.ts
2658
- import fs13 from "fs";
2667
+ import fs14 from "fs";
2659
2668
  var REFACTOR_YML_PATH = "refactor.yml";
2660
2669
  function parseRefactorYml() {
2661
- if (!fs13.existsSync(REFACTOR_YML_PATH)) {
2670
+ if (!fs14.existsSync(REFACTOR_YML_PATH)) {
2662
2671
  return [];
2663
2672
  }
2664
- const content = fs13.readFileSync(REFACTOR_YML_PATH, "utf-8");
2673
+ const content = fs14.readFileSync(REFACTOR_YML_PATH, "utf-8");
2665
2674
  const entries = [];
2666
2675
  const lines = content.split("\n");
2667
2676
  let currentEntry = {};
@@ -2746,7 +2755,7 @@ Refactor check failed:
2746
2755
 
2747
2756
  // src/commands/refactor/getViolations.ts
2748
2757
  function countLines(filePath) {
2749
- const content = fs14.readFileSync(filePath, "utf-8");
2758
+ const content = fs15.readFileSync(filePath, "utf-8");
2750
2759
  return content.split("\n").length;
2751
2760
  }
2752
2761
  function getGitFiles(options) {
@@ -2771,7 +2780,7 @@ function getGitFiles(options) {
2771
2780
  return files;
2772
2781
  }
2773
2782
  function getViolations(pattern2, options = {}, maxLines = DEFAULT_MAX_LINES) {
2774
- let sourceFiles = findSourceFiles("src", { includeTests: false });
2783
+ let sourceFiles = findSourceFiles2("src", { includeTests: false });
2775
2784
  const ignoredFiles = getIgnoredFiles();
2776
2785
  const gitFiles = getGitFiles(options);
2777
2786
  if (pattern2) {
@@ -2798,7 +2807,7 @@ async function runVerifyQuietly() {
2798
2807
  return true;
2799
2808
  }
2800
2809
  const { packageJsonPath, verifyScripts } = result;
2801
- const packageDir = path14.dirname(packageJsonPath);
2810
+ const packageDir = path15.dirname(packageJsonPath);
2802
2811
  const results = await Promise.all(
2803
2812
  verifyScripts.map(
2804
2813
  (script) => new Promise(
@@ -2851,25 +2860,25 @@ async function check(pattern2, options) {
2851
2860
  }
2852
2861
 
2853
2862
  // src/commands/refactor/ignore.ts
2854
- import fs15 from "fs";
2863
+ import fs16 from "fs";
2855
2864
  import chalk34 from "chalk";
2856
2865
  var REFACTOR_YML_PATH2 = "refactor.yml";
2857
2866
  function ignore(file) {
2858
- if (!fs15.existsSync(file)) {
2867
+ if (!fs16.existsSync(file)) {
2859
2868
  console.error(chalk34.red(`Error: File does not exist: ${file}`));
2860
2869
  process.exit(1);
2861
2870
  }
2862
- const content = fs15.readFileSync(file, "utf-8");
2871
+ const content = fs16.readFileSync(file, "utf-8");
2863
2872
  const lineCount = content.split("\n").length;
2864
2873
  const maxLines = lineCount + 10;
2865
2874
  const entry = `- file: ${file}
2866
2875
  maxLines: ${maxLines}
2867
2876
  `;
2868
- if (fs15.existsSync(REFACTOR_YML_PATH2)) {
2869
- const existing = fs15.readFileSync(REFACTOR_YML_PATH2, "utf-8");
2870
- fs15.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
2877
+ if (fs16.existsSync(REFACTOR_YML_PATH2)) {
2878
+ const existing = fs16.readFileSync(REFACTOR_YML_PATH2, "utf-8");
2879
+ fs16.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
2871
2880
  } else {
2872
- fs15.writeFileSync(REFACTOR_YML_PATH2, entry);
2881
+ fs16.writeFileSync(REFACTOR_YML_PATH2, entry);
2873
2882
  }
2874
2883
  console.log(
2875
2884
  chalk34.green(
@@ -2963,21 +2972,21 @@ async function statusLine() {
2963
2972
  }
2964
2973
 
2965
2974
  // src/commands/sync.ts
2966
- import * as fs18 from "fs";
2975
+ import * as fs19 from "fs";
2967
2976
  import * as os from "os";
2968
- import * as path17 from "path";
2977
+ import * as path18 from "path";
2969
2978
  import { fileURLToPath as fileURLToPath3 } from "url";
2970
2979
 
2971
2980
  // src/commands/sync/syncClaudeMd.ts
2972
- import * as fs16 from "fs";
2973
- import * as path15 from "path";
2981
+ import * as fs17 from "fs";
2982
+ import * as path16 from "path";
2974
2983
  import chalk35 from "chalk";
2975
2984
  async function syncClaudeMd(claudeDir, targetBase) {
2976
- const source = path15.join(claudeDir, "CLAUDE.md");
2977
- const target = path15.join(targetBase, "CLAUDE.md");
2978
- const sourceContent = fs16.readFileSync(source, "utf-8");
2979
- if (fs16.existsSync(target)) {
2980
- const targetContent = fs16.readFileSync(target, "utf-8");
2985
+ const source = path16.join(claudeDir, "CLAUDE.md");
2986
+ const target = path16.join(targetBase, "CLAUDE.md");
2987
+ const sourceContent = fs17.readFileSync(source, "utf-8");
2988
+ if (fs17.existsSync(target)) {
2989
+ const targetContent = fs17.readFileSync(target, "utf-8");
2981
2990
  if (sourceContent !== targetContent) {
2982
2991
  console.log(
2983
2992
  chalk35.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
@@ -2994,21 +3003,21 @@ async function syncClaudeMd(claudeDir, targetBase) {
2994
3003
  }
2995
3004
  }
2996
3005
  }
2997
- fs16.copyFileSync(source, target);
3006
+ fs17.copyFileSync(source, target);
2998
3007
  console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
2999
3008
  }
3000
3009
 
3001
3010
  // src/commands/sync/syncSettings.ts
3002
- import * as fs17 from "fs";
3003
- import * as path16 from "path";
3011
+ import * as fs18 from "fs";
3012
+ import * as path17 from "path";
3004
3013
  import chalk36 from "chalk";
3005
3014
  async function syncSettings(claudeDir, targetBase) {
3006
- const source = path16.join(claudeDir, "settings.json");
3007
- const target = path16.join(targetBase, "settings.json");
3008
- const sourceContent = fs17.readFileSync(source, "utf-8");
3015
+ const source = path17.join(claudeDir, "settings.json");
3016
+ const target = path17.join(targetBase, "settings.json");
3017
+ const sourceContent = fs18.readFileSync(source, "utf-8");
3009
3018
  const normalizedSource = JSON.stringify(JSON.parse(sourceContent), null, 2);
3010
- if (fs17.existsSync(target)) {
3011
- const targetContent = fs17.readFileSync(target, "utf-8");
3019
+ if (fs18.existsSync(target)) {
3020
+ const targetContent = fs18.readFileSync(target, "utf-8");
3012
3021
  const normalizedTarget = JSON.stringify(JSON.parse(targetContent), null, 2);
3013
3022
  if (normalizedSource !== normalizedTarget) {
3014
3023
  console.log(
@@ -3026,27 +3035,27 @@ async function syncSettings(claudeDir, targetBase) {
3026
3035
  }
3027
3036
  }
3028
3037
  }
3029
- fs17.copyFileSync(source, target);
3038
+ fs18.copyFileSync(source, target);
3030
3039
  console.log("Copied settings.json to ~/.claude/settings.json");
3031
3040
  }
3032
3041
 
3033
3042
  // src/commands/sync.ts
3034
3043
  var __filename2 = fileURLToPath3(import.meta.url);
3035
- var __dirname4 = path17.dirname(__filename2);
3044
+ var __dirname4 = path18.dirname(__filename2);
3036
3045
  async function sync() {
3037
- const claudeDir = path17.join(__dirname4, "..", "claude");
3038
- const targetBase = path17.join(os.homedir(), ".claude");
3046
+ const claudeDir = path18.join(__dirname4, "..", "claude");
3047
+ const targetBase = path18.join(os.homedir(), ".claude");
3039
3048
  syncCommands(claudeDir, targetBase);
3040
3049
  await syncSettings(claudeDir, targetBase);
3041
3050
  await syncClaudeMd(claudeDir, targetBase);
3042
3051
  }
3043
3052
  function syncCommands(claudeDir, targetBase) {
3044
- const sourceDir = path17.join(claudeDir, "commands");
3045
- const targetDir = path17.join(targetBase, "commands");
3046
- fs18.mkdirSync(targetDir, { recursive: true });
3047
- const files = fs18.readdirSync(sourceDir);
3053
+ const sourceDir = path18.join(claudeDir, "commands");
3054
+ const targetDir = path18.join(targetBase, "commands");
3055
+ fs19.mkdirSync(targetDir, { recursive: true });
3056
+ const files = fs19.readdirSync(sourceDir);
3048
3057
  for (const file of files) {
3049
- fs18.copyFileSync(path17.join(sourceDir, file), path17.join(targetDir, file));
3058
+ fs19.copyFileSync(path18.join(sourceDir, file), path18.join(targetDir, file));
3050
3059
  console.log(`Copied ${file} to ${targetDir}`);
3051
3060
  }
3052
3061
  console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
@@ -3168,58 +3177,7 @@ async function configure() {
3168
3177
  import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
3169
3178
  import { basename as basename4, dirname as dirname11, join as join17 } from "path";
3170
3179
 
3171
- // src/commands/transcript/parseVtt.ts
3172
- function parseTimestamp(ts4) {
3173
- const parts = ts4.split(":");
3174
- let hours = 0;
3175
- let minutes = 0;
3176
- let seconds = 0;
3177
- if (parts.length === 3) {
3178
- hours = Number.parseInt(parts[0], 10);
3179
- minutes = Number.parseInt(parts[1], 10);
3180
- seconds = Number.parseFloat(parts[2]);
3181
- } else if (parts.length === 2) {
3182
- minutes = Number.parseInt(parts[0], 10);
3183
- seconds = Number.parseFloat(parts[1]);
3184
- }
3185
- return Math.round((hours * 3600 + minutes * 60 + seconds) * 1e3);
3186
- }
3187
- function parseVtt(content) {
3188
- const cues = [];
3189
- const lines = content.split(/\r?\n/);
3190
- let i = 0;
3191
- while (i < lines.length && !lines[i].includes("-->")) {
3192
- i++;
3193
- }
3194
- while (i < lines.length) {
3195
- const line = lines[i].trim();
3196
- if (line.includes("-->")) {
3197
- const [startStr, endStr] = line.split("-->").map((s) => s.trim());
3198
- const startMs = parseTimestamp(startStr);
3199
- const endMs = parseTimestamp(endStr);
3200
- const textLines = [];
3201
- i++;
3202
- while (i < lines.length && lines[i].trim() && !lines[i].includes("-->")) {
3203
- textLines.push(lines[i].trim());
3204
- i++;
3205
- }
3206
- const fullText = textLines.join(" ");
3207
- const speakerMatch = fullText.match(/^<v\s+([^>]+)>/);
3208
- let speaker = null;
3209
- let text = fullText;
3210
- if (speakerMatch) {
3211
- speaker = speakerMatch[1];
3212
- text = fullText.replace(/<v\s+[^>]+>/, "").trim();
3213
- }
3214
- if (text) {
3215
- cues.push({ startMs, endMs, speaker, text });
3216
- }
3217
- } else {
3218
- i++;
3219
- }
3220
- }
3221
- return cues;
3222
- }
3180
+ // src/commands/transcript/deduplicateCues.ts
3223
3181
  function removeSubstringDuplicates(cues) {
3224
3182
  const toRemove = /* @__PURE__ */ new Set();
3225
3183
  for (let i = 0; i < cues.length; i++) {
@@ -3305,6 +3263,59 @@ function deduplicateCues(cues) {
3305
3263
  }));
3306
3264
  }
3307
3265
 
3266
+ // src/commands/transcript/parseVtt.ts
3267
+ function parseTimestamp(ts5) {
3268
+ const parts = ts5.split(":");
3269
+ let hours = 0;
3270
+ let minutes = 0;
3271
+ let seconds = 0;
3272
+ if (parts.length === 3) {
3273
+ hours = Number.parseInt(parts[0], 10);
3274
+ minutes = Number.parseInt(parts[1], 10);
3275
+ seconds = Number.parseFloat(parts[2]);
3276
+ } else if (parts.length === 2) {
3277
+ minutes = Number.parseInt(parts[0], 10);
3278
+ seconds = Number.parseFloat(parts[1]);
3279
+ }
3280
+ return Math.round((hours * 3600 + minutes * 60 + seconds) * 1e3);
3281
+ }
3282
+ function parseVtt(content) {
3283
+ const cues = [];
3284
+ const lines = content.split(/\r?\n/);
3285
+ let i = 0;
3286
+ while (i < lines.length && !lines[i].includes("-->")) {
3287
+ i++;
3288
+ }
3289
+ while (i < lines.length) {
3290
+ const line = lines[i].trim();
3291
+ if (line.includes("-->")) {
3292
+ const [startStr, endStr] = line.split("-->").map((s) => s.trim());
3293
+ const startMs = parseTimestamp(startStr);
3294
+ const endMs = parseTimestamp(endStr);
3295
+ const textLines = [];
3296
+ i++;
3297
+ while (i < lines.length && lines[i].trim() && !lines[i].includes("-->")) {
3298
+ textLines.push(lines[i].trim());
3299
+ i++;
3300
+ }
3301
+ const fullText = textLines.join(" ");
3302
+ const speakerMatch = fullText.match(/^<v\s+([^>]+)>/);
3303
+ let speaker = null;
3304
+ let text = fullText;
3305
+ if (speakerMatch) {
3306
+ speaker = speakerMatch[1];
3307
+ text = fullText.replace(/<v\s+[^>]+>/, "").trim();
3308
+ }
3309
+ if (text) {
3310
+ cues.push({ startMs, endMs, speaker, text });
3311
+ }
3312
+ } else {
3313
+ i++;
3314
+ }
3315
+ }
3316
+ return cues;
3317
+ }
3318
+
3308
3319
  // src/commands/transcript/formatChatLog.ts
3309
3320
  function cuesToChatMessages(cues) {
3310
3321
  const messages = [];
@@ -3618,7 +3629,7 @@ Total: ${lines.length} hardcoded color(s)`);
3618
3629
 
3619
3630
  // src/commands/verify/run.ts
3620
3631
  import { spawn as spawn4 } from "child_process";
3621
- import * as path18 from "path";
3632
+ import * as path19 from "path";
3622
3633
  function formatDuration(ms) {
3623
3634
  if (ms < 1e3) {
3624
3635
  return `${ms}ms`;
@@ -3648,7 +3659,7 @@ async function run2(options = {}) {
3648
3659
  return;
3649
3660
  }
3650
3661
  const { packageJsonPath, verifyScripts } = result;
3651
- const packageDir = path18.dirname(packageJsonPath);
3662
+ const packageDir = path19.dirname(packageJsonPath);
3652
3663
  console.log(`Running ${verifyScripts.length} verify script(s) in parallel:`);
3653
3664
  for (const script of verifyScripts) {
3654
3665
  console.log(` - ${script}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.42.1",
3
+ "version": "0.42.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -21,7 +21,7 @@
21
21
  "verify:types": "tsc --noEmit",
22
22
  "verify:knip": "knip --no-progress --treat-config-hints-as-errors",
23
23
  "verify:duplicate-code": "jscpd --format 'typescript,tsx' --exitCode 1 --ignore '**/*.test.*' -r consoleFull src",
24
- "verify:maintainability": "assist complexity maintainability ./src --threshold 50",
24
+ "verify:maintainability": "assist complexity maintainability ./src --threshold 55",
25
25
  "verify:custom-lint": "assist lint"
26
26
  },
27
27
  "keywords": [],