@staff0rd/assist 0.19.0 → 0.20.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 (2) hide show
  1. package/dist/index.js +663 -239
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -91,12 +91,427 @@ function commit(message) {
91
91
  }
92
92
  }
93
93
 
94
+ // src/commands/complexity/cyclomatic.ts
95
+ import chalk2 from "chalk";
96
+
97
+ // src/commands/complexity/shared.ts
98
+ import fs from "fs";
99
+ import path from "path";
100
+ import chalk from "chalk";
101
+ import { minimatch } from "minimatch";
102
+ import ts from "typescript";
103
+ function getNodeName(node) {
104
+ if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
105
+ return node.name?.text ?? "<anonymous>";
106
+ }
107
+ if (ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) {
108
+ if (ts.isIdentifier(node.name)) {
109
+ return node.name.text;
110
+ }
111
+ if (ts.isStringLiteral(node.name)) {
112
+ return node.name.text;
113
+ }
114
+ return "<computed>";
115
+ }
116
+ if (ts.isArrowFunction(node)) {
117
+ const parent = node.parent;
118
+ if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
119
+ return parent.name.text;
120
+ }
121
+ if (ts.isPropertyAssignment(parent) && ts.isIdentifier(parent.name)) {
122
+ return parent.name.text;
123
+ }
124
+ return "<arrow>";
125
+ }
126
+ if (ts.isGetAccessor(node) || ts.isSetAccessor(node)) {
127
+ const prefix = ts.isGetAccessor(node) ? "get " : "set ";
128
+ if (ts.isIdentifier(node.name)) {
129
+ return `${prefix}${node.name.text}`;
130
+ }
131
+ return `${prefix}<computed>`;
132
+ }
133
+ if (ts.isConstructorDeclaration(node)) {
134
+ return "constructor";
135
+ }
136
+ return "<unknown>";
137
+ }
138
+ function createSourceFromFile(filePath) {
139
+ const content = fs.readFileSync(filePath, "utf-8");
140
+ return ts.createSourceFile(
141
+ path.basename(filePath),
142
+ content,
143
+ ts.ScriptTarget.Latest,
144
+ true,
145
+ filePath.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
146
+ );
147
+ }
148
+ function withSourceFiles(pattern2, callback) {
149
+ const files = findSourceFilesWithPattern(pattern2);
150
+ if (files.length === 0) {
151
+ console.log(chalk.yellow("No files found matching pattern"));
152
+ return void 0;
153
+ }
154
+ return callback(files);
155
+ }
156
+ function forEachFunction(files, callback) {
157
+ for (const file of files) {
158
+ const sourceFile = createSourceFromFile(file);
159
+ const visit = (node) => {
160
+ if (hasFunctionBody(node)) {
161
+ callback(file, getNodeName(node), node);
162
+ }
163
+ ts.forEachChild(node, visit);
164
+ };
165
+ visit(sourceFile);
166
+ }
167
+ }
168
+ function findSourceFilesWithPattern(pattern2, baseDir = ".") {
169
+ const results = [];
170
+ const extensions = [".ts", ".tsx"];
171
+ function walk(dir) {
172
+ if (!fs.existsSync(dir)) {
173
+ return;
174
+ }
175
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
176
+ for (const entry of entries) {
177
+ const fullPath = path.join(dir, entry.name);
178
+ if (entry.isDirectory()) {
179
+ if (entry.name !== "node_modules" && entry.name !== ".git") {
180
+ walk(fullPath);
181
+ }
182
+ } else if (entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext))) {
183
+ results.push(fullPath);
184
+ }
185
+ }
186
+ }
187
+ if (pattern2.includes("*")) {
188
+ walk(baseDir);
189
+ return results.filter((f) => minimatch(f, pattern2));
190
+ }
191
+ if (fs.existsSync(pattern2) && fs.statSync(pattern2).isFile()) {
192
+ return [pattern2];
193
+ }
194
+ if (fs.existsSync(pattern2) && fs.statSync(pattern2).isDirectory()) {
195
+ walk(pattern2);
196
+ return results;
197
+ }
198
+ walk(baseDir);
199
+ return results.filter((f) => minimatch(f, pattern2));
200
+ }
201
+ function hasFunctionBody(node) {
202
+ if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node) || ts.isConstructorDeclaration(node)) {
203
+ return node.body !== void 0;
204
+ }
205
+ return false;
206
+ }
207
+ function countSloc(content) {
208
+ let inMultiLineComment = false;
209
+ let count = 0;
210
+ const lines = content.split("\n");
211
+ for (const line of lines) {
212
+ const trimmed = line.trim();
213
+ if (inMultiLineComment) {
214
+ if (trimmed.includes("*/")) {
215
+ inMultiLineComment = false;
216
+ const afterComment = trimmed.substring(trimmed.indexOf("*/") + 2);
217
+ if (afterComment.trim().length > 0) {
218
+ count++;
219
+ }
220
+ }
221
+ continue;
222
+ }
223
+ if (trimmed.startsWith("//")) {
224
+ continue;
225
+ }
226
+ if (trimmed.startsWith("/*")) {
227
+ if (trimmed.includes("*/")) {
228
+ const afterComment = trimmed.substring(trimmed.indexOf("*/") + 2);
229
+ if (afterComment.trim().length > 0) {
230
+ count++;
231
+ }
232
+ } else {
233
+ inMultiLineComment = true;
234
+ }
235
+ continue;
236
+ }
237
+ if (trimmed.length > 0) {
238
+ count++;
239
+ }
240
+ }
241
+ return count;
242
+ }
243
+ function calculateCyclomaticComplexity(node) {
244
+ let complexity = 1;
245
+ function visit(n) {
246
+ switch (n.kind) {
247
+ case ts.SyntaxKind.IfStatement:
248
+ case ts.SyntaxKind.ForStatement:
249
+ case ts.SyntaxKind.ForInStatement:
250
+ case ts.SyntaxKind.ForOfStatement:
251
+ case ts.SyntaxKind.WhileStatement:
252
+ case ts.SyntaxKind.DoStatement:
253
+ case ts.SyntaxKind.CaseClause:
254
+ case ts.SyntaxKind.CatchClause:
255
+ case ts.SyntaxKind.ConditionalExpression:
256
+ complexity++;
257
+ break;
258
+ case ts.SyntaxKind.BinaryExpression: {
259
+ const binary = n;
260
+ if (binary.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || binary.operatorToken.kind === ts.SyntaxKind.BarBarToken || binary.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
261
+ complexity++;
262
+ }
263
+ break;
264
+ }
265
+ }
266
+ ts.forEachChild(n, visit);
267
+ }
268
+ ts.forEachChild(node, visit);
269
+ return complexity;
270
+ }
271
+ function calculateHalstead(node) {
272
+ const operators = /* @__PURE__ */ new Map();
273
+ const operands = /* @__PURE__ */ new Map();
274
+ function addOperator(op) {
275
+ operators.set(op, (operators.get(op) ?? 0) + 1);
276
+ }
277
+ function addOperand(op) {
278
+ operands.set(op, (operands.get(op) ?? 0) + 1);
279
+ }
280
+ function visit(n) {
281
+ if (ts.isIdentifier(n)) {
282
+ addOperand(n.text);
283
+ } else if (ts.isNumericLiteral(n)) {
284
+ addOperand(n.text);
285
+ } else if (ts.isStringLiteral(n)) {
286
+ addOperand(n.text);
287
+ } else if (ts.isBinaryExpression(n)) {
288
+ addOperator(n.operatorToken.getText());
289
+ } else if (ts.isPrefixUnaryExpression(n) || ts.isPostfixUnaryExpression(n)) {
290
+ addOperator(ts.tokenToString(n.operator) ?? "");
291
+ } else if (ts.isCallExpression(n)) {
292
+ addOperator("()");
293
+ } else if (ts.isPropertyAccessExpression(n)) {
294
+ addOperator(".");
295
+ } else if (ts.isElementAccessExpression(n)) {
296
+ addOperator("[]");
297
+ } else if (ts.isConditionalExpression(n)) {
298
+ addOperator("?:");
299
+ } else if (ts.isReturnStatement(n)) {
300
+ addOperator("return");
301
+ } else if (ts.isIfStatement(n)) {
302
+ addOperator("if");
303
+ } else if (ts.isForStatement(n) || ts.isForInStatement(n) || ts.isForOfStatement(n)) {
304
+ addOperator("for");
305
+ } else if (ts.isWhileStatement(n)) {
306
+ addOperator("while");
307
+ } else if (ts.isDoStatement(n)) {
308
+ addOperator("do");
309
+ } else if (ts.isSwitchStatement(n)) {
310
+ addOperator("switch");
311
+ } else if (ts.isCaseClause(n)) {
312
+ addOperator("case");
313
+ } else if (ts.isDefaultClause(n)) {
314
+ addOperator("default");
315
+ } else if (ts.isBreakStatement(n)) {
316
+ addOperator("break");
317
+ } else if (ts.isContinueStatement(n)) {
318
+ addOperator("continue");
319
+ } else if (ts.isThrowStatement(n)) {
320
+ addOperator("throw");
321
+ } else if (ts.isTryStatement(n)) {
322
+ addOperator("try");
323
+ } else if (ts.isCatchClause(n)) {
324
+ addOperator("catch");
325
+ } else if (ts.isNewExpression(n)) {
326
+ addOperator("new");
327
+ } else if (ts.isTypeOfExpression(n)) {
328
+ addOperator("typeof");
329
+ } else if (ts.isAwaitExpression(n)) {
330
+ addOperator("await");
331
+ }
332
+ ts.forEachChild(n, visit);
333
+ }
334
+ ts.forEachChild(node, visit);
335
+ const n1 = operators.size;
336
+ const n2 = operands.size;
337
+ const N1 = Array.from(operators.values()).reduce((a, b) => a + b, 0);
338
+ const N2 = Array.from(operands.values()).reduce((a, b) => a + b, 0);
339
+ const vocabulary = n1 + n2;
340
+ const length = N1 + N2;
341
+ const volume = length > 0 && vocabulary > 0 ? length * Math.log2(vocabulary) : 0;
342
+ const difficulty = n2 > 0 ? n1 / 2 * (N2 / n2) : 0;
343
+ const effort = volume * difficulty;
344
+ const time = effort / 18;
345
+ const bugsDelivered = volume / 3e3;
346
+ return {
347
+ length,
348
+ vocabulary,
349
+ volume,
350
+ difficulty,
351
+ effort,
352
+ time,
353
+ bugsDelivered
354
+ };
355
+ }
356
+
357
+ // src/commands/complexity/cyclomatic.ts
358
+ async function cyclomatic(pattern2 = "**/*.ts", options = {}) {
359
+ withSourceFiles(pattern2, (files) => {
360
+ const results = [];
361
+ let hasViolation = false;
362
+ forEachFunction(files, (file, name, node) => {
363
+ const complexity = calculateCyclomaticComplexity(node);
364
+ results.push({ file, name, complexity });
365
+ if (options.threshold !== void 0 && complexity > options.threshold) {
366
+ hasViolation = true;
367
+ }
368
+ });
369
+ results.sort((a, b) => b.complexity - a.complexity);
370
+ for (const { file, name, complexity } of results) {
371
+ const exceedsThreshold = options.threshold !== void 0 && complexity > options.threshold;
372
+ const color = exceedsThreshold ? chalk2.red : chalk2.white;
373
+ console.log(`${color(`${file}:${name}`)} \u2192 ${chalk2.cyan(complexity)}`);
374
+ }
375
+ console.log(
376
+ chalk2.dim(
377
+ `
378
+ Analyzed ${results.length} functions across ${files.length} files`
379
+ )
380
+ );
381
+ if (hasViolation) {
382
+ process.exit(1);
383
+ }
384
+ });
385
+ }
386
+
387
+ // src/commands/complexity/halstead.ts
388
+ import chalk3 from "chalk";
389
+ async function halstead(pattern2 = "**/*.ts", options = {}) {
390
+ withSourceFiles(pattern2, (files) => {
391
+ const results = [];
392
+ let hasViolation = false;
393
+ forEachFunction(files, (file, name, node) => {
394
+ const metrics = calculateHalstead(node);
395
+ results.push({ file, name, metrics });
396
+ if (options.threshold !== void 0 && metrics.volume > options.threshold) {
397
+ hasViolation = true;
398
+ }
399
+ });
400
+ results.sort((a, b) => b.metrics.effort - a.metrics.effort);
401
+ for (const { file, name, metrics } of results) {
402
+ const exceedsThreshold = options.threshold !== void 0 && metrics.volume > options.threshold;
403
+ const color = exceedsThreshold ? chalk3.red : chalk3.white;
404
+ console.log(
405
+ `${color(`${file}:${name}`)} \u2192 volume: ${chalk3.cyan(metrics.volume.toFixed(1))}, difficulty: ${chalk3.yellow(metrics.difficulty.toFixed(1))}, effort: ${chalk3.magenta(metrics.effort.toFixed(1))}`
406
+ );
407
+ }
408
+ console.log(
409
+ chalk3.dim(
410
+ `
411
+ Analyzed ${results.length} functions across ${files.length} files`
412
+ )
413
+ );
414
+ if (hasViolation) {
415
+ process.exit(1);
416
+ }
417
+ });
418
+ }
419
+
420
+ // src/commands/complexity/maintainability.ts
421
+ import fs2 from "fs";
422
+ import chalk4 from "chalk";
423
+ function calculateMaintainabilityIndex(halsteadVolume, cyclomaticComplexity, sloc2) {
424
+ if (halsteadVolume === 0 || sloc2 === 0) {
425
+ return 100;
426
+ }
427
+ const mi = 171 - 5.2 * Math.log(halsteadVolume) - 0.23 * cyclomaticComplexity - 16.2 * Math.log(sloc2);
428
+ return Math.max(0, Math.min(100, mi));
429
+ }
430
+ async function maintainability(pattern2 = "**/*.ts", options = {}) {
431
+ withSourceFiles(pattern2, (files) => {
432
+ const fileMetrics = /* @__PURE__ */ new Map();
433
+ for (const file of files) {
434
+ const content = fs2.readFileSync(file, "utf-8");
435
+ fileMetrics.set(file, { sloc: countSloc(content), functions: [] });
436
+ }
437
+ forEachFunction(files, (file, _name, node) => {
438
+ const metrics = fileMetrics.get(file);
439
+ if (metrics) {
440
+ const complexity = calculateCyclomaticComplexity(node);
441
+ const halstead2 = calculateHalstead(node);
442
+ const mi = calculateMaintainabilityIndex(
443
+ halstead2.volume,
444
+ complexity,
445
+ metrics.sloc
446
+ );
447
+ metrics.functions.push(mi);
448
+ }
449
+ });
450
+ const results = [];
451
+ let hasViolation = false;
452
+ for (const [file, metrics] of fileMetrics) {
453
+ if (metrics.functions.length === 0) continue;
454
+ const avgMaintainability = metrics.functions.reduce((a, b) => a + b, 0) / metrics.functions.length;
455
+ const minMaintainability = Math.min(...metrics.functions);
456
+ results.push({ file, avgMaintainability, minMaintainability });
457
+ if (options.threshold !== void 0 && minMaintainability < options.threshold) {
458
+ hasViolation = true;
459
+ }
460
+ }
461
+ results.sort((a, b) => a.minMaintainability - b.minMaintainability);
462
+ for (const { file, avgMaintainability, minMaintainability } of results) {
463
+ const exceedsThreshold = options.threshold !== void 0 && minMaintainability < options.threshold;
464
+ const color = exceedsThreshold ? chalk4.red : chalk4.white;
465
+ console.log(
466
+ `${color(file)} \u2192 avg: ${chalk4.cyan(avgMaintainability.toFixed(1))}, min: ${chalk4.yellow(minMaintainability.toFixed(1))}`
467
+ );
468
+ }
469
+ console.log(chalk4.dim(`
470
+ Analyzed ${results.length} files`));
471
+ if (hasViolation) {
472
+ process.exit(1);
473
+ }
474
+ });
475
+ }
476
+
477
+ // src/commands/complexity/sloc.ts
478
+ import fs3 from "fs";
479
+ import chalk5 from "chalk";
480
+ async function sloc(pattern2 = "**/*.ts", options = {}) {
481
+ withSourceFiles(pattern2, (files) => {
482
+ const results = [];
483
+ let hasViolation = false;
484
+ for (const file of files) {
485
+ const content = fs3.readFileSync(file, "utf-8");
486
+ const lines = countSloc(content);
487
+ results.push({ file, lines });
488
+ if (options.threshold !== void 0 && lines > options.threshold) {
489
+ hasViolation = true;
490
+ }
491
+ }
492
+ results.sort((a, b) => b.lines - a.lines);
493
+ for (const { file, lines } of results) {
494
+ const exceedsThreshold = options.threshold !== void 0 && lines > options.threshold;
495
+ const color = exceedsThreshold ? chalk5.red : chalk5.white;
496
+ console.log(`${color(file)} \u2192 ${chalk5.cyan(lines)} lines`);
497
+ }
498
+ const total = results.reduce((sum, r) => sum + r.lines, 0);
499
+ console.log(
500
+ chalk5.dim(`
501
+ Total: ${total} lines across ${files.length} files`)
502
+ );
503
+ if (hasViolation) {
504
+ process.exit(1);
505
+ }
506
+ });
507
+ }
508
+
94
509
  // src/commands/deploy/init.ts
95
510
  import { execSync as execSync2 } from "child_process";
96
511
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
97
512
  import { dirname, join as join2 } from "path";
98
513
  import { fileURLToPath } from "url";
99
- import chalk2 from "chalk";
514
+ import chalk7 from "chalk";
100
515
  import enquirer2 from "enquirer";
101
516
 
102
517
  // src/shared/promptConfirm.ts
@@ -117,7 +532,7 @@ async function promptConfirm(message, initial = true) {
117
532
  }
118
533
 
119
534
  // src/utils/printDiff.ts
120
- import chalk from "chalk";
535
+ import chalk6 from "chalk";
121
536
  import * as diff from "diff";
122
537
  function normalizeJson(content) {
123
538
  try {
@@ -135,11 +550,11 @@ function printDiff(oldContent, newContent) {
135
550
  const lines = change.value.replace(/\n$/, "").split("\n");
136
551
  for (const line of lines) {
137
552
  if (change.added) {
138
- console.log(chalk.green(`+ ${line}`));
553
+ console.log(chalk6.green(`+ ${line}`));
139
554
  } else if (change.removed) {
140
- console.log(chalk.red(`- ${line}`));
555
+ console.log(chalk6.red(`- ${line}`));
141
556
  } else {
142
- console.log(chalk.dim(` ${line}`));
557
+ console.log(chalk6.dim(` ${line}`));
143
558
  }
144
559
  }
145
560
  }
@@ -170,27 +585,27 @@ async function updateWorkflow(siteId) {
170
585
  if (existsSync2(WORKFLOW_PATH)) {
171
586
  const oldContent = readFileSync2(WORKFLOW_PATH, "utf-8");
172
587
  if (oldContent === newContent) {
173
- console.log(chalk2.green("build.yml is already up to date"));
588
+ console.log(chalk7.green("build.yml is already up to date"));
174
589
  return;
175
590
  }
176
- console.log(chalk2.yellow("\nbuild.yml will be updated:"));
591
+ console.log(chalk7.yellow("\nbuild.yml will be updated:"));
177
592
  console.log();
178
593
  printDiff(oldContent, newContent);
179
- const confirm = await promptConfirm(chalk2.red("Update build.yml?"));
594
+ const confirm = await promptConfirm(chalk7.red("Update build.yml?"));
180
595
  if (!confirm) {
181
596
  console.log("Skipped build.yml update");
182
597
  return;
183
598
  }
184
599
  }
185
600
  writeFileSync2(WORKFLOW_PATH, newContent);
186
- console.log(chalk2.green(`
601
+ console.log(chalk7.green(`
187
602
  Created ${WORKFLOW_PATH}`));
188
603
  }
189
604
  async function init() {
190
- console.log(chalk2.bold("Initializing Netlify deployment...\n"));
605
+ console.log(chalk7.bold("Initializing Netlify deployment...\n"));
191
606
  const existingSiteId = getExistingSiteId();
192
607
  if (existingSiteId) {
193
- console.log(chalk2.dim(`Using existing site ID: ${existingSiteId}
608
+ console.log(chalk7.dim(`Using existing site ID: ${existingSiteId}
194
609
  `));
195
610
  await updateWorkflow(existingSiteId);
196
611
  return;
@@ -202,10 +617,10 @@ async function init() {
202
617
  });
203
618
  } catch (error) {
204
619
  if (error instanceof Error && error.message.includes("command not found")) {
205
- console.error(chalk2.red("\nNetlify CLI is not installed.\n"));
620
+ console.error(chalk7.red("\nNetlify CLI is not installed.\n"));
206
621
  const install = await promptConfirm("Would you like to install it now?");
207
622
  if (install) {
208
- console.log(chalk2.dim("\nInstalling netlify-cli...\n"));
623
+ console.log(chalk7.dim("\nInstalling netlify-cli...\n"));
209
624
  execSync2("npm install -g netlify-cli", { stdio: "inherit" });
210
625
  console.log();
211
626
  execSync2("netlify sites:create --disable-linking", {
@@ -213,7 +628,7 @@ async function init() {
213
628
  });
214
629
  } else {
215
630
  console.log(
216
- chalk2.yellow(
631
+ chalk7.yellow(
217
632
  "\nInstall it manually with: npm install -g netlify-cli\n"
218
633
  )
219
634
  );
@@ -230,17 +645,17 @@ async function init() {
230
645
  validate: (value) => /^[a-f0-9-]+$/i.test(value) || "Invalid site ID format"
231
646
  });
232
647
  await updateWorkflow(siteId);
233
- console.log(chalk2.bold("\nDeployment initialized successfully!"));
648
+ console.log(chalk7.bold("\nDeployment initialized successfully!"));
234
649
  console.log(
235
- chalk2.yellow("\nTo complete setup, create a personal access token at:")
650
+ chalk7.yellow("\nTo complete setup, create a personal access token at:")
236
651
  );
237
652
  console.log(
238
- chalk2.cyan(
653
+ chalk7.cyan(
239
654
  "https://app.netlify.com/user/applications#personal-access-tokens"
240
655
  )
241
656
  );
242
657
  console.log(
243
- chalk2.yellow(
658
+ chalk7.yellow(
244
659
  "\nThen add it as NETLIFY_AUTH_TOKEN in your GitHub repository secrets."
245
660
  )
246
661
  );
@@ -248,7 +663,7 @@ async function init() {
248
663
 
249
664
  // src/commands/deploy/redirect.ts
250
665
  import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
251
- import chalk3 from "chalk";
666
+ import chalk8 from "chalk";
252
667
  var TRAILING_SLASH_SCRIPT = ` <script>
253
668
  if (!window.location.pathname.endsWith('/')) {
254
669
  window.location.href = \`\${window.location.pathname}/\${window.location.search}\${window.location.hash}\`;
@@ -257,32 +672,32 @@ var TRAILING_SLASH_SCRIPT = ` <script>
257
672
  function redirect() {
258
673
  const indexPath = "index.html";
259
674
  if (!existsSync3(indexPath)) {
260
- console.log(chalk3.yellow("No index.html found"));
675
+ console.log(chalk8.yellow("No index.html found"));
261
676
  return;
262
677
  }
263
678
  const content = readFileSync3(indexPath, "utf-8");
264
679
  if (content.includes("window.location.pathname.endsWith('/')")) {
265
- console.log(chalk3.dim("Trailing slash script already present"));
680
+ console.log(chalk8.dim("Trailing slash script already present"));
266
681
  return;
267
682
  }
268
683
  const headCloseIndex = content.indexOf("</head>");
269
684
  if (headCloseIndex === -1) {
270
- console.log(chalk3.red("Could not find </head> tag in index.html"));
685
+ console.log(chalk8.red("Could not find </head> tag in index.html"));
271
686
  return;
272
687
  }
273
688
  const newContent = content.slice(0, headCloseIndex) + TRAILING_SLASH_SCRIPT + "\n " + content.slice(headCloseIndex);
274
689
  writeFileSync3(indexPath, newContent);
275
- console.log(chalk3.green("Added trailing slash redirect to index.html"));
690
+ console.log(chalk8.green("Added trailing slash redirect to index.html"));
276
691
  }
277
692
 
278
693
  // src/commands/devlog/list.ts
279
694
  import { execSync as execSync4 } from "child_process";
280
695
  import { basename as basename2 } from "path";
281
- import chalk5 from "chalk";
696
+ import chalk10 from "chalk";
282
697
 
283
698
  // src/commands/devlog/shared.ts
284
699
  import { execSync as execSync3 } from "child_process";
285
- import chalk4 from "chalk";
700
+ import chalk9 from "chalk";
286
701
 
287
702
  // src/commands/devlog/loadDevlogEntries.ts
288
703
  import { readdirSync, readFileSync as readFileSync4 } from "fs";
@@ -343,13 +758,13 @@ function shouldIgnoreCommit(files, ignorePaths) {
343
758
  }
344
759
  function printCommitsWithFiles(commits, ignore2, verbose) {
345
760
  for (const commit2 of commits) {
346
- console.log(` ${chalk4.yellow(commit2.hash)} ${commit2.message}`);
761
+ console.log(` ${chalk9.yellow(commit2.hash)} ${commit2.message}`);
347
762
  if (verbose) {
348
763
  const visibleFiles = commit2.files.filter(
349
764
  (file) => !ignore2.some((p) => file.startsWith(p))
350
765
  );
351
766
  for (const file of visibleFiles) {
352
- console.log(` ${chalk4.dim(file)}`);
767
+ console.log(` ${chalk9.dim(file)}`);
353
768
  }
354
769
  }
355
770
  }
@@ -402,12 +817,12 @@ function list(options) {
402
817
  isFirst = false;
403
818
  const entries = devlogEntries.get(date);
404
819
  if (skipDays.has(date)) {
405
- console.log(`${chalk5.bold.blue(date)} ${chalk5.dim("skipped")}`);
820
+ console.log(`${chalk10.bold.blue(date)} ${chalk10.dim("skipped")}`);
406
821
  } else if (entries && entries.length > 0) {
407
- const entryInfo = entries.map((e) => `${chalk5.green(e.version)} ${e.title}`).join(" | ");
408
- console.log(`${chalk5.bold.blue(date)} ${entryInfo}`);
822
+ const entryInfo = entries.map((e) => `${chalk10.green(e.version)} ${e.title}`).join(" | ");
823
+ console.log(`${chalk10.bold.blue(date)} ${entryInfo}`);
409
824
  } else {
410
- console.log(`${chalk5.bold.blue(date)} ${chalk5.red("\u26A0 devlog missing")}`);
825
+ console.log(`${chalk10.bold.blue(date)} ${chalk10.red("\u26A0 devlog missing")}`);
411
826
  }
412
827
  printCommitsWithFiles(dateCommits, ignore2, options.verbose ?? false);
413
828
  }
@@ -415,7 +830,7 @@ function list(options) {
415
830
 
416
831
  // src/commands/devlog/next.ts
417
832
  import { execSync as execSync6 } from "child_process";
418
- import chalk6 from "chalk";
833
+ import chalk11 from "chalk";
419
834
 
420
835
  // src/commands/devlog/getLastVersionInfo.ts
421
836
  import { execSync as execSync5 } from "child_process";
@@ -524,43 +939,43 @@ function next(options) {
524
939
  const targetDate = dates[0];
525
940
  if (!targetDate) {
526
941
  if (lastInfo) {
527
- console.log(chalk6.dim("No commits after last versioned entry"));
942
+ console.log(chalk11.dim("No commits after last versioned entry"));
528
943
  } else {
529
- console.log(chalk6.dim("No commits found"));
944
+ console.log(chalk11.dim("No commits found"));
530
945
  }
531
946
  return;
532
947
  }
533
948
  const commits = commitsByDate.get(targetDate) ?? [];
534
- console.log(`${chalk6.bold("name:")} ${repoName}`);
949
+ console.log(`${chalk11.bold("name:")} ${repoName}`);
535
950
  if (config.commit?.conventional && commits.length > 0) {
536
951
  const version2 = getVersionAtCommit(commits[0].hash);
537
952
  if (version2) {
538
- console.log(`${chalk6.bold("version:")} ${stripToMinor(version2)}`);
953
+ console.log(`${chalk11.bold("version:")} ${stripToMinor(version2)}`);
539
954
  } else {
540
- console.log(`${chalk6.bold("version:")} ${chalk6.red("unknown")}`);
955
+ console.log(`${chalk11.bold("version:")} ${chalk11.red("unknown")}`);
541
956
  }
542
957
  } else if (patchVersion && minorVersion) {
543
958
  console.log(
544
- `${chalk6.bold("version:")} ${patchVersion} (patch) or ${minorVersion} (minor)`
959
+ `${chalk11.bold("version:")} ${patchVersion} (patch) or ${minorVersion} (minor)`
545
960
  );
546
961
  } else {
547
- console.log(`${chalk6.bold("version:")} v0.1 (initial)`);
962
+ console.log(`${chalk11.bold("version:")} v0.1 (initial)`);
548
963
  }
549
- console.log(`${chalk6.bold.blue(targetDate)}`);
964
+ console.log(`${chalk11.bold.blue(targetDate)}`);
550
965
  printCommitsWithFiles(commits, ignore2, options.verbose ?? false);
551
966
  }
552
967
 
553
968
  // src/commands/devlog/skip.ts
554
- import chalk7 from "chalk";
969
+ import chalk12 from "chalk";
555
970
  function skip(date) {
556
971
  if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
557
- console.log(chalk7.red("Invalid date format. Use YYYY-MM-DD"));
972
+ console.log(chalk12.red("Invalid date format. Use YYYY-MM-DD"));
558
973
  process.exit(1);
559
974
  }
560
975
  const config = loadConfig();
561
976
  const skipDays = config.devlog?.skip?.days ?? [];
562
977
  if (skipDays.includes(date)) {
563
- console.log(chalk7.yellow(`${date} is already in skip list`));
978
+ console.log(chalk12.yellow(`${date} is already in skip list`));
564
979
  return;
565
980
  }
566
981
  skipDays.push(date);
@@ -573,28 +988,28 @@ function skip(date) {
573
988
  }
574
989
  };
575
990
  saveConfig(config);
576
- console.log(chalk7.green(`Added ${date} to skip list`));
991
+ console.log(chalk12.green(`Added ${date} to skip list`));
577
992
  }
578
993
 
579
994
  // src/commands/devlog/version.ts
580
- import chalk8 from "chalk";
995
+ import chalk13 from "chalk";
581
996
  function version() {
582
997
  const config = loadConfig();
583
998
  const name = getRepoName();
584
999
  const lastInfo = getLastVersionInfo(name, config);
585
1000
  const lastVersion = lastInfo?.version ?? null;
586
1001
  const nextVersion = lastVersion ? bumpVersion(lastVersion, "patch") : null;
587
- console.log(`${chalk8.bold("name:")} ${name}`);
588
- console.log(`${chalk8.bold("last:")} ${lastVersion ?? chalk8.dim("none")}`);
589
- console.log(`${chalk8.bold("next:")} ${nextVersion ?? chalk8.dim("none")}`);
1002
+ console.log(`${chalk13.bold("name:")} ${name}`);
1003
+ console.log(`${chalk13.bold("last:")} ${lastVersion ?? chalk13.dim("none")}`);
1004
+ console.log(`${chalk13.bold("next:")} ${nextVersion ?? chalk13.dim("none")}`);
590
1005
  }
591
1006
 
592
1007
  // src/commands/enable-ralph/index.ts
593
- import * as fs from "fs";
594
- import * as path from "path";
1008
+ import * as fs4 from "fs";
1009
+ import * as path2 from "path";
595
1010
  import { fileURLToPath as fileURLToPath2 } from "url";
596
- import chalk9 from "chalk";
597
- var __dirname3 = path.dirname(fileURLToPath2(import.meta.url));
1011
+ import chalk14 from "chalk";
1012
+ var __dirname3 = path2.dirname(fileURLToPath2(import.meta.url));
598
1013
  function deepMerge(target, source) {
599
1014
  const result = { ...target };
600
1015
  for (const key of Object.keys(source)) {
@@ -612,30 +1027,30 @@ function deepMerge(target, source) {
612
1027
  return result;
613
1028
  }
614
1029
  async function enableRalph() {
615
- const sourcePath = path.join(
1030
+ const sourcePath = path2.join(
616
1031
  __dirname3,
617
1032
  "commands/enable-ralph/settings.local.json"
618
1033
  );
619
- const targetPath = path.join(process.cwd(), ".claude/settings.local.json");
620
- const sourceData = JSON.parse(fs.readFileSync(sourcePath, "utf-8"));
621
- const targetDir = path.dirname(targetPath);
622
- if (!fs.existsSync(targetDir)) {
623
- fs.mkdirSync(targetDir, { recursive: true });
1034
+ const targetPath = path2.join(process.cwd(), ".claude/settings.local.json");
1035
+ const sourceData = JSON.parse(fs4.readFileSync(sourcePath, "utf-8"));
1036
+ const targetDir = path2.dirname(targetPath);
1037
+ if (!fs4.existsSync(targetDir)) {
1038
+ fs4.mkdirSync(targetDir, { recursive: true });
624
1039
  }
625
1040
  let targetData = {};
626
1041
  let targetContent = "{}";
627
- if (fs.existsSync(targetPath)) {
628
- targetContent = fs.readFileSync(targetPath, "utf-8");
1042
+ if (fs4.existsSync(targetPath)) {
1043
+ targetContent = fs4.readFileSync(targetPath, "utf-8");
629
1044
  targetData = JSON.parse(targetContent);
630
1045
  }
631
1046
  const merged = deepMerge(targetData, sourceData);
632
1047
  const mergedContent = `${JSON.stringify(merged, null, " ")}
633
1048
  `;
634
1049
  if (mergedContent === targetContent) {
635
- console.log(chalk9.green("settings.local.json already has ralph enabled"));
1050
+ console.log(chalk14.green("settings.local.json already has ralph enabled"));
636
1051
  return;
637
1052
  }
638
- console.log(chalk9.yellow("\nChanges to settings.local.json:"));
1053
+ console.log(chalk14.yellow("\nChanges to settings.local.json:"));
639
1054
  console.log();
640
1055
  printDiff(targetContent, mergedContent);
641
1056
  const confirm = await promptConfirm("Apply these changes?");
@@ -643,15 +1058,15 @@ async function enableRalph() {
643
1058
  console.log("Skipped");
644
1059
  return;
645
1060
  }
646
- fs.writeFileSync(targetPath, mergedContent);
1061
+ fs4.writeFileSync(targetPath, mergedContent);
647
1062
  console.log(`Updated ${targetPath}`);
648
1063
  }
649
1064
 
650
1065
  // src/commands/verify/init.ts
651
- import chalk20 from "chalk";
1066
+ import chalk25 from "chalk";
652
1067
 
653
1068
  // src/shared/promptMultiselect.ts
654
- import chalk10 from "chalk";
1069
+ import chalk15 from "chalk";
655
1070
  import enquirer3 from "enquirer";
656
1071
  async function promptMultiselect(message, options) {
657
1072
  const { selected } = await enquirer3.prompt({
@@ -660,7 +1075,7 @@ async function promptMultiselect(message, options) {
660
1075
  message,
661
1076
  choices: options.map((opt) => ({
662
1077
  name: opt.value,
663
- message: `${opt.name} - ${chalk10.dim(opt.description)}`
1078
+ message: `${opt.name} - ${chalk15.dim(opt.description)}`
664
1079
  })),
665
1080
  // @ts-expect-error - enquirer types don't include symbols but it's supported
666
1081
  symbols: {
@@ -674,23 +1089,23 @@ async function promptMultiselect(message, options) {
674
1089
  }
675
1090
 
676
1091
  // src/shared/readPackageJson.ts
677
- import * as fs2 from "fs";
678
- import * as path2 from "path";
679
- import chalk11 from "chalk";
1092
+ import * as fs5 from "fs";
1093
+ import * as path3 from "path";
1094
+ import chalk16 from "chalk";
680
1095
  function findPackageJson() {
681
- const packageJsonPath = path2.join(process.cwd(), "package.json");
682
- if (fs2.existsSync(packageJsonPath)) {
1096
+ const packageJsonPath = path3.join(process.cwd(), "package.json");
1097
+ if (fs5.existsSync(packageJsonPath)) {
683
1098
  return packageJsonPath;
684
1099
  }
685
1100
  return null;
686
1101
  }
687
1102
  function readPackageJson(filePath) {
688
- return JSON.parse(fs2.readFileSync(filePath, "utf-8"));
1103
+ return JSON.parse(fs5.readFileSync(filePath, "utf-8"));
689
1104
  }
690
1105
  function requirePackageJson() {
691
1106
  const packageJsonPath = findPackageJson();
692
1107
  if (!packageJsonPath) {
693
- console.error(chalk11.red("No package.json found in current directory"));
1108
+ console.error(chalk16.red("No package.json found in current directory"));
694
1109
  process.exit(1);
695
1110
  }
696
1111
  const pkg = readPackageJson(packageJsonPath);
@@ -699,9 +1114,9 @@ function requirePackageJson() {
699
1114
  function findPackageJsonWithVerifyScripts(startDir) {
700
1115
  let currentDir = startDir;
701
1116
  while (true) {
702
- const packageJsonPath = path2.join(currentDir, "package.json");
703
- if (fs2.existsSync(packageJsonPath)) {
704
- const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
1117
+ const packageJsonPath = path3.join(currentDir, "package.json");
1118
+ if (fs5.existsSync(packageJsonPath)) {
1119
+ const packageJson = JSON.parse(fs5.readFileSync(packageJsonPath, "utf-8"));
705
1120
  const scripts = packageJson.scripts || {};
706
1121
  const verifyScripts = Object.keys(scripts).filter(
707
1122
  (name) => name.startsWith("verify:")
@@ -710,7 +1125,7 @@ function findPackageJsonWithVerifyScripts(startDir) {
710
1125
  return { packageJsonPath, verifyScripts };
711
1126
  }
712
1127
  }
713
- const parentDir = path2.dirname(currentDir);
1128
+ const parentDir = path3.dirname(currentDir);
714
1129
  if (parentDir === currentDir) {
715
1130
  return null;
716
1131
  }
@@ -728,15 +1143,15 @@ var expectedScripts = {
728
1143
  };
729
1144
 
730
1145
  // src/commands/verify/setup/setupBuild.ts
731
- import chalk13 from "chalk";
1146
+ import chalk18 from "chalk";
732
1147
 
733
1148
  // src/commands/verify/installPackage.ts
734
1149
  import { execSync as execSync7 } from "child_process";
735
- import * as fs3 from "fs";
736
- import * as path3 from "path";
737
- import chalk12 from "chalk";
1150
+ import * as fs6 from "fs";
1151
+ import * as path4 from "path";
1152
+ import chalk17 from "chalk";
738
1153
  function writePackageJson(filePath, pkg) {
739
- fs3.writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}
1154
+ fs6.writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}
740
1155
  `);
741
1156
  }
742
1157
  function addScript(pkg, name, command) {
@@ -749,36 +1164,36 @@ function addScript(pkg, name, command) {
749
1164
  };
750
1165
  }
751
1166
  function installPackage(name, cwd) {
752
- console.log(chalk12.dim(`Installing ${name}...`));
1167
+ console.log(chalk17.dim(`Installing ${name}...`));
753
1168
  try {
754
1169
  execSync7(`npm install -D ${name}`, { stdio: "inherit", cwd });
755
1170
  return true;
756
1171
  } catch {
757
- console.error(chalk12.red(`Failed to install ${name}`));
1172
+ console.error(chalk17.red(`Failed to install ${name}`));
758
1173
  return false;
759
1174
  }
760
1175
  }
761
1176
  function addToKnipIgnoreBinaries(cwd, binary) {
762
- const knipJsonPath = path3.join(cwd, "knip.json");
1177
+ const knipJsonPath = path4.join(cwd, "knip.json");
763
1178
  try {
764
1179
  let knipConfig;
765
- if (fs3.existsSync(knipJsonPath)) {
766
- knipConfig = JSON.parse(fs3.readFileSync(knipJsonPath, "utf-8"));
1180
+ if (fs6.existsSync(knipJsonPath)) {
1181
+ knipConfig = JSON.parse(fs6.readFileSync(knipJsonPath, "utf-8"));
767
1182
  } else {
768
1183
  knipConfig = { $schema: "https://unpkg.com/knip@5/schema.json" };
769
1184
  }
770
1185
  const ignoreBinaries = knipConfig.ignoreBinaries ?? [];
771
1186
  if (!ignoreBinaries.includes(binary)) {
772
1187
  knipConfig.ignoreBinaries = [...ignoreBinaries, binary];
773
- fs3.writeFileSync(
1188
+ fs6.writeFileSync(
774
1189
  knipJsonPath,
775
1190
  `${JSON.stringify(knipConfig, null, " ")}
776
1191
  `
777
1192
  );
778
- console.log(chalk12.dim(`Added '${binary}' to knip.json ignoreBinaries`));
1193
+ console.log(chalk17.dim(`Added '${binary}' to knip.json ignoreBinaries`));
779
1194
  }
780
1195
  } catch {
781
- console.log(chalk12.yellow("Warning: Could not update knip.json"));
1196
+ console.log(chalk17.yellow("Warning: Could not update knip.json"));
782
1197
  }
783
1198
  }
784
1199
  function setupVerifyScript(packageJsonPath, scriptName, command) {
@@ -790,7 +1205,7 @@ function setupVerifyScript(packageJsonPath, scriptName, command) {
790
1205
 
791
1206
  // src/commands/verify/setup/setupBuild.ts
792
1207
  async function setupBuild(packageJsonPath, hasVite, hasTypescript) {
793
- console.log(chalk13.blue("\nSetting up build verification..."));
1208
+ console.log(chalk18.blue("\nSetting up build verification..."));
794
1209
  let command;
795
1210
  if (hasVite && hasTypescript) {
796
1211
  command = "tsc -b && vite build --logLevel error";
@@ -799,17 +1214,17 @@ async function setupBuild(packageJsonPath, hasVite, hasTypescript) {
799
1214
  } else {
800
1215
  command = "tsc --noEmit";
801
1216
  }
802
- console.log(chalk13.dim(`Using: ${command}`));
1217
+ console.log(chalk18.dim(`Using: ${command}`));
803
1218
  const pkg = readPackageJson(packageJsonPath);
804
1219
  writePackageJson(packageJsonPath, addScript(pkg, "verify:build", command));
805
1220
  }
806
1221
 
807
1222
  // src/commands/verify/setup/setupDuplicateCode.ts
808
- import * as path4 from "path";
809
- import chalk14 from "chalk";
1223
+ import * as path5 from "path";
1224
+ import chalk19 from "chalk";
810
1225
  async function setupDuplicateCode(packageJsonPath) {
811
- console.log(chalk14.blue("\nSetting up jscpd..."));
812
- const cwd = path4.dirname(packageJsonPath);
1226
+ console.log(chalk19.blue("\nSetting up jscpd..."));
1227
+ const cwd = path5.dirname(packageJsonPath);
813
1228
  const pkg = readPackageJson(packageJsonPath);
814
1229
  const hasJscpd = !!pkg.dependencies?.jscpd || !!pkg.devDependencies?.jscpd;
815
1230
  if (!hasJscpd && !installPackage("jscpd", cwd)) {
@@ -823,11 +1238,11 @@ async function setupDuplicateCode(packageJsonPath) {
823
1238
  }
824
1239
 
825
1240
  // src/commands/verify/setup/setupHardcodedColors.ts
826
- import * as path5 from "path";
827
- import chalk15 from "chalk";
1241
+ import * as path6 from "path";
1242
+ import chalk20 from "chalk";
828
1243
  async function setupHardcodedColors(packageJsonPath, hasOpenColor) {
829
- console.log(chalk15.blue("\nSetting up hardcoded colors check..."));
830
- const cwd = path5.dirname(packageJsonPath);
1244
+ console.log(chalk20.blue("\nSetting up hardcoded colors check..."));
1245
+ const cwd = path6.dirname(packageJsonPath);
831
1246
  if (!hasOpenColor) {
832
1247
  installPackage("open-color", cwd);
833
1248
  }
@@ -840,11 +1255,11 @@ async function setupHardcodedColors(packageJsonPath, hasOpenColor) {
840
1255
  }
841
1256
 
842
1257
  // src/commands/verify/setup/setupKnip.ts
843
- import * as path6 from "path";
844
- import chalk16 from "chalk";
1258
+ import * as path7 from "path";
1259
+ import chalk21 from "chalk";
845
1260
  async function setupKnip(packageJsonPath) {
846
- console.log(chalk16.blue("\nSetting up knip..."));
847
- const cwd = path6.dirname(packageJsonPath);
1261
+ console.log(chalk21.blue("\nSetting up knip..."));
1262
+ const cwd = path7.dirname(packageJsonPath);
848
1263
  const pkg = readPackageJson(packageJsonPath);
849
1264
  if (!pkg.devDependencies?.knip && !installPackage("knip", cwd)) {
850
1265
  return;
@@ -857,15 +1272,15 @@ async function setupKnip(packageJsonPath) {
857
1272
  }
858
1273
 
859
1274
  // src/commands/verify/setup/setupLint.ts
860
- import * as path7 from "path";
861
- import chalk18 from "chalk";
1275
+ import * as path8 from "path";
1276
+ import chalk23 from "chalk";
862
1277
 
863
1278
  // src/commands/lint/init.ts
864
1279
  import { execSync as execSync9 } from "child_process";
865
1280
  import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
866
1281
  import { dirname as dirname7, join as join7 } from "path";
867
1282
  import { fileURLToPath as fileURLToPath3 } from "url";
868
- import chalk17 from "chalk";
1283
+ import chalk22 from "chalk";
869
1284
 
870
1285
  // src/shared/removeEslint.ts
871
1286
  import { execSync as execSync8 } from "child_process";
@@ -971,10 +1386,10 @@ async function init2() {
971
1386
  console.log("biome.json already has the correct linter config");
972
1387
  return;
973
1388
  }
974
- console.log(chalk17.yellow("\n\u26A0\uFE0F biome.json will be updated:"));
1389
+ console.log(chalk22.yellow("\n\u26A0\uFE0F biome.json will be updated:"));
975
1390
  console.log();
976
1391
  printDiff(oldContent, newContent);
977
- const confirm = await promptConfirm(chalk17.red("Update biome.json?"));
1392
+ const confirm = await promptConfirm(chalk22.red("Update biome.json?"));
978
1393
  if (!confirm) {
979
1394
  console.log("Skipped biome.json update");
980
1395
  return;
@@ -985,8 +1400,8 @@ async function init2() {
985
1400
 
986
1401
  // src/commands/verify/setup/setupLint.ts
987
1402
  async function setupLint(packageJsonPath) {
988
- console.log(chalk18.blue("\nSetting up biome..."));
989
- const cwd = path7.dirname(packageJsonPath);
1403
+ console.log(chalk23.blue("\nSetting up biome..."));
1404
+ const cwd = path8.dirname(packageJsonPath);
990
1405
  const pkg = readPackageJson(packageJsonPath);
991
1406
  if (!pkg.devDependencies?.["@biomejs/biome"]) {
992
1407
  if (!installPackage("@biomejs/biome", cwd)) {
@@ -1002,11 +1417,11 @@ async function setupLint(packageJsonPath) {
1002
1417
  }
1003
1418
 
1004
1419
  // src/commands/verify/setup/setupTest.ts
1005
- import * as path8 from "path";
1006
- import chalk19 from "chalk";
1420
+ import * as path9 from "path";
1421
+ import chalk24 from "chalk";
1007
1422
  async function setupTest(packageJsonPath) {
1008
- console.log(chalk19.blue("\nSetting up vitest..."));
1009
- const cwd = path8.dirname(packageJsonPath);
1423
+ console.log(chalk24.blue("\nSetting up vitest..."));
1424
+ const cwd = path9.dirname(packageJsonPath);
1010
1425
  const pkg = readPackageJson(packageJsonPath);
1011
1426
  if (!pkg.devDependencies?.vitest && !installPackage("vitest", cwd)) {
1012
1427
  return;
@@ -1148,16 +1563,16 @@ async function init3() {
1148
1563
  const setup = detectExistingSetup(pkg);
1149
1564
  const availableOptions = getAvailableOptions(setup);
1150
1565
  if (availableOptions.length === 0) {
1151
- console.log(chalk20.green("All verify scripts are already configured!"));
1566
+ console.log(chalk25.green("All verify scripts are already configured!"));
1152
1567
  return;
1153
1568
  }
1154
- console.log(chalk20.bold("Available verify scripts to add:\n"));
1569
+ console.log(chalk25.bold("Available verify scripts to add:\n"));
1155
1570
  const selected = await promptMultiselect(
1156
1571
  "Select verify scripts to add:",
1157
1572
  availableOptions
1158
1573
  );
1159
1574
  if (selected.length === 0) {
1160
- console.log(chalk20.yellow("No scripts selected"));
1575
+ console.log(chalk25.yellow("No scripts selected"));
1161
1576
  return;
1162
1577
  }
1163
1578
  for (const choice of selected) {
@@ -1182,43 +1597,43 @@ async function init3() {
1182
1597
  break;
1183
1598
  }
1184
1599
  }
1185
- console.log(chalk20.green(`
1600
+ console.log(chalk25.green(`
1186
1601
  Added ${selected.length} verify script(s):`));
1187
1602
  for (const choice of selected) {
1188
- console.log(chalk20.green(` - verify:${choice}`));
1603
+ console.log(chalk25.green(` - verify:${choice}`));
1189
1604
  }
1190
- console.log(chalk20.dim("\nRun 'assist verify' to run all verify scripts"));
1605
+ console.log(chalk25.dim("\nRun 'assist verify' to run all verify scripts"));
1191
1606
  }
1192
1607
 
1193
1608
  // src/commands/vscode/init.ts
1194
- import * as fs5 from "fs";
1195
- import * as path10 from "path";
1196
- import chalk22 from "chalk";
1609
+ import * as fs8 from "fs";
1610
+ import * as path11 from "path";
1611
+ import chalk27 from "chalk";
1197
1612
 
1198
1613
  // src/commands/vscode/createLaunchJson.ts
1199
- import * as fs4 from "fs";
1200
- import * as path9 from "path";
1201
- import chalk21 from "chalk";
1614
+ import * as fs7 from "fs";
1615
+ import * as path10 from "path";
1616
+ import chalk26 from "chalk";
1202
1617
  function ensureVscodeFolder() {
1203
- const vscodeDir = path9.join(process.cwd(), ".vscode");
1204
- if (!fs4.existsSync(vscodeDir)) {
1205
- fs4.mkdirSync(vscodeDir);
1206
- console.log(chalk21.dim("Created .vscode folder"));
1618
+ const vscodeDir = path10.join(process.cwd(), ".vscode");
1619
+ if (!fs7.existsSync(vscodeDir)) {
1620
+ fs7.mkdirSync(vscodeDir);
1621
+ console.log(chalk26.dim("Created .vscode folder"));
1207
1622
  }
1208
1623
  }
1209
1624
  function removeVscodeFromGitignore() {
1210
- const gitignorePath = path9.join(process.cwd(), ".gitignore");
1211
- if (!fs4.existsSync(gitignorePath)) {
1625
+ const gitignorePath = path10.join(process.cwd(), ".gitignore");
1626
+ if (!fs7.existsSync(gitignorePath)) {
1212
1627
  return;
1213
1628
  }
1214
- const content = fs4.readFileSync(gitignorePath, "utf-8");
1629
+ const content = fs7.readFileSync(gitignorePath, "utf-8");
1215
1630
  const lines = content.split("\n");
1216
1631
  const filteredLines = lines.filter(
1217
1632
  (line) => !line.trim().toLowerCase().includes(".vscode")
1218
1633
  );
1219
1634
  if (filteredLines.length !== lines.length) {
1220
- fs4.writeFileSync(gitignorePath, filteredLines.join("\n"));
1221
- console.log(chalk21.dim("Removed .vscode references from .gitignore"));
1635
+ fs7.writeFileSync(gitignorePath, filteredLines.join("\n"));
1636
+ console.log(chalk26.dim("Removed .vscode references from .gitignore"));
1222
1637
  }
1223
1638
  }
1224
1639
  function createLaunchJson() {
@@ -1233,10 +1648,10 @@ function createLaunchJson() {
1233
1648
  }
1234
1649
  ]
1235
1650
  };
1236
- const launchPath = path9.join(process.cwd(), ".vscode", "launch.json");
1237
- fs4.writeFileSync(launchPath, `${JSON.stringify(launchConfig, null, " ")}
1651
+ const launchPath = path10.join(process.cwd(), ".vscode", "launch.json");
1652
+ fs7.writeFileSync(launchPath, `${JSON.stringify(launchConfig, null, " ")}
1238
1653
  `);
1239
- console.log(chalk21.green("Created .vscode/launch.json"));
1654
+ console.log(chalk26.green("Created .vscode/launch.json"));
1240
1655
  }
1241
1656
  function createSettingsJson() {
1242
1657
  const settings = {
@@ -1246,31 +1661,31 @@ function createSettingsJson() {
1246
1661
  "source.organizeImports.biome": "explicit"
1247
1662
  }
1248
1663
  };
1249
- const settingsPath = path9.join(process.cwd(), ".vscode", "settings.json");
1250
- fs4.writeFileSync(settingsPath, `${JSON.stringify(settings, null, " ")}
1664
+ const settingsPath = path10.join(process.cwd(), ".vscode", "settings.json");
1665
+ fs7.writeFileSync(settingsPath, `${JSON.stringify(settings, null, " ")}
1251
1666
  `);
1252
- console.log(chalk21.green("Created .vscode/settings.json"));
1667
+ console.log(chalk26.green("Created .vscode/settings.json"));
1253
1668
  }
1254
1669
  function createExtensionsJson() {
1255
1670
  const extensions = {
1256
1671
  recommendations: ["biomejs.biome"]
1257
1672
  };
1258
- const extensionsPath = path9.join(process.cwd(), ".vscode", "extensions.json");
1259
- fs4.writeFileSync(
1673
+ const extensionsPath = path10.join(process.cwd(), ".vscode", "extensions.json");
1674
+ fs7.writeFileSync(
1260
1675
  extensionsPath,
1261
1676
  `${JSON.stringify(extensions, null, " ")}
1262
1677
  `
1263
1678
  );
1264
- console.log(chalk21.green("Created .vscode/extensions.json"));
1679
+ console.log(chalk26.green("Created .vscode/extensions.json"));
1265
1680
  }
1266
1681
 
1267
1682
  // src/commands/vscode/init.ts
1268
1683
  function detectExistingSetup2(pkg) {
1269
- const vscodeDir = path10.join(process.cwd(), ".vscode");
1684
+ const vscodeDir = path11.join(process.cwd(), ".vscode");
1270
1685
  return {
1271
- hasVscodeFolder: fs5.existsSync(vscodeDir),
1272
- hasLaunchJson: fs5.existsSync(path10.join(vscodeDir, "launch.json")),
1273
- hasSettingsJson: fs5.existsSync(path10.join(vscodeDir, "settings.json")),
1686
+ hasVscodeFolder: fs8.existsSync(vscodeDir),
1687
+ hasLaunchJson: fs8.existsSync(path11.join(vscodeDir, "launch.json")),
1688
+ hasSettingsJson: fs8.existsSync(path11.join(vscodeDir, "settings.json")),
1274
1689
  hasVite: !!pkg.devDependencies?.vite || !!pkg.dependencies?.vite
1275
1690
  };
1276
1691
  }
@@ -1293,16 +1708,16 @@ async function init4() {
1293
1708
  });
1294
1709
  }
1295
1710
  if (availableOptions.length === 0) {
1296
- console.log(chalk22.green("VS Code configuration already exists!"));
1711
+ console.log(chalk27.green("VS Code configuration already exists!"));
1297
1712
  return;
1298
1713
  }
1299
- console.log(chalk22.bold("Available VS Code configurations to add:\n"));
1714
+ console.log(chalk27.bold("Available VS Code configurations to add:\n"));
1300
1715
  const selected = await promptMultiselect(
1301
1716
  "Select configurations to add:",
1302
1717
  availableOptions
1303
1718
  );
1304
1719
  if (selected.length === 0) {
1305
- console.log(chalk22.yellow("No configurations selected"));
1720
+ console.log(chalk27.yellow("No configurations selected"));
1306
1721
  return;
1307
1722
  }
1308
1723
  removeVscodeFromGitignore();
@@ -1319,7 +1734,7 @@ async function init4() {
1319
1734
  }
1320
1735
  }
1321
1736
  console.log(
1322
- chalk22.green(`
1737
+ chalk27.green(`
1323
1738
  Added ${selected.length} VS Code configuration(s)`)
1324
1739
  );
1325
1740
  }
@@ -1331,23 +1746,23 @@ async function init5() {
1331
1746
  }
1332
1747
 
1333
1748
  // src/commands/lint/runFileNameCheck.ts
1334
- import fs7 from "fs";
1335
- import path12 from "path";
1336
- import chalk23 from "chalk";
1749
+ import fs10 from "fs";
1750
+ import path13 from "path";
1751
+ import chalk28 from "chalk";
1337
1752
 
1338
1753
  // src/shared/findSourceFiles.ts
1339
- import fs6 from "fs";
1340
- import path11 from "path";
1754
+ import fs9 from "fs";
1755
+ import path12 from "path";
1341
1756
  var EXTENSIONS = [".ts", ".tsx"];
1342
1757
  function findSourceFiles(dir, options = {}) {
1343
1758
  const { includeTests = true } = options;
1344
1759
  const results = [];
1345
- if (!fs6.existsSync(dir)) {
1760
+ if (!fs9.existsSync(dir)) {
1346
1761
  return results;
1347
1762
  }
1348
- const entries = fs6.readdirSync(dir, { withFileTypes: true });
1763
+ const entries = fs9.readdirSync(dir, { withFileTypes: true });
1349
1764
  for (const entry of entries) {
1350
- const fullPath = path11.join(dir, entry.name);
1765
+ const fullPath = path12.join(dir, entry.name);
1351
1766
  if (entry.isDirectory() && entry.name !== "node_modules") {
1352
1767
  results.push(...findSourceFiles(fullPath, options));
1353
1768
  } else if (entry.isFile() && EXTENSIONS.some((ext) => entry.name.endsWith(ext))) {
@@ -1371,10 +1786,10 @@ function checkFileNames() {
1371
1786
  const sourceFiles = findSourceFiles("src");
1372
1787
  const violations = [];
1373
1788
  for (const filePath of sourceFiles) {
1374
- const fileName = path12.basename(filePath);
1789
+ const fileName = path13.basename(filePath);
1375
1790
  const nameWithoutExt = fileName.replace(/\.(ts|tsx)$/, "");
1376
1791
  if (/^[A-Z]/.test(nameWithoutExt)) {
1377
- const content = fs7.readFileSync(filePath, "utf-8");
1792
+ const content = fs10.readFileSync(filePath, "utf-8");
1378
1793
  if (!hasClassOrComponent(content)) {
1379
1794
  violations.push({ filePath, fileName });
1380
1795
  }
@@ -1385,16 +1800,16 @@ function checkFileNames() {
1385
1800
  function runFileNameCheck() {
1386
1801
  const violations = checkFileNames();
1387
1802
  if (violations.length > 0) {
1388
- console.error(chalk23.red("\nFile name check failed:\n"));
1803
+ console.error(chalk28.red("\nFile name check failed:\n"));
1389
1804
  console.error(
1390
- chalk23.red(
1805
+ chalk28.red(
1391
1806
  " Files without classes or React components should not start with a capital letter.\n"
1392
1807
  )
1393
1808
  );
1394
1809
  for (const violation of violations) {
1395
- console.error(chalk23.red(` ${violation.filePath}`));
1810
+ console.error(chalk28.red(` ${violation.filePath}`));
1396
1811
  console.error(
1397
- chalk23.gray(
1812
+ chalk28.gray(
1398
1813
  ` Rename to: ${violation.fileName.charAt(0).toLowerCase()}${violation.fileName.slice(1)}
1399
1814
  `
1400
1815
  )
@@ -1411,20 +1826,20 @@ function runFileNameCheck() {
1411
1826
  }
1412
1827
 
1413
1828
  // src/commands/lint/runImportExtensionCheck.ts
1414
- import fs8 from "fs";
1829
+ import fs11 from "fs";
1415
1830
 
1416
1831
  // src/commands/lint/shared.ts
1417
- import chalk24 from "chalk";
1832
+ import chalk29 from "chalk";
1418
1833
  function reportViolations(violations, checkName, errorMessage, successMessage) {
1419
1834
  if (violations.length > 0) {
1420
- console.error(chalk24.red(`
1835
+ console.error(chalk29.red(`
1421
1836
  ${checkName} failed:
1422
1837
  `));
1423
- console.error(chalk24.red(` ${errorMessage}
1838
+ console.error(chalk29.red(` ${errorMessage}
1424
1839
  `));
1425
1840
  for (const violation of violations) {
1426
- console.error(chalk24.red(` ${violation.filePath}:${violation.line}`));
1427
- console.error(chalk24.gray(` ${violation.content}
1841
+ console.error(chalk29.red(` ${violation.filePath}:${violation.line}`));
1842
+ console.error(chalk29.gray(` ${violation.content}
1428
1843
  `));
1429
1844
  }
1430
1845
  return false;
@@ -1437,7 +1852,7 @@ ${checkName} failed:
1437
1852
 
1438
1853
  // src/commands/lint/runImportExtensionCheck.ts
1439
1854
  function checkForImportExtensions(filePath) {
1440
- const content = fs8.readFileSync(filePath, "utf-8");
1855
+ const content = fs11.readFileSync(filePath, "utf-8");
1441
1856
  const lines = content.split("\n");
1442
1857
  const violations = [];
1443
1858
  const importExtensionPattern = /from\s+["']\..*\.(js|ts)["']/;
@@ -1471,9 +1886,9 @@ function runImportExtensionCheck() {
1471
1886
  }
1472
1887
 
1473
1888
  // src/commands/lint/runStaticImportCheck.ts
1474
- import fs9 from "fs";
1889
+ import fs12 from "fs";
1475
1890
  function checkForDynamicImports(filePath) {
1476
- const content = fs9.readFileSync(filePath, "utf-8");
1891
+ const content = fs12.readFileSync(filePath, "utf-8");
1477
1892
  const lines = content.split("\n");
1478
1893
  const violations = [];
1479
1894
  const requirePattern = /\brequire\s*\(/;
@@ -1587,19 +2002,19 @@ function detectPlatform() {
1587
2002
 
1588
2003
  // src/commands/notify/showWindowsNotificationFromWsl.ts
1589
2004
  import { spawn } from "child_process";
1590
- import fs10 from "fs";
2005
+ import fs13 from "fs";
1591
2006
  import { createRequire } from "module";
1592
- import path13 from "path";
2007
+ import path14 from "path";
1593
2008
  var require2 = createRequire(import.meta.url);
1594
2009
  function getSnoreToastPath() {
1595
- const notifierPath = path13.dirname(require2.resolve("node-notifier"));
1596
- return path13.join(notifierPath, "vendor", "snoreToast", "snoretoast-x64.exe");
2010
+ const notifierPath = path14.dirname(require2.resolve("node-notifier"));
2011
+ return path14.join(notifierPath, "vendor", "snoreToast", "snoretoast-x64.exe");
1597
2012
  }
1598
2013
  function showWindowsNotificationFromWsl(options) {
1599
2014
  const { title, message, sound } = options;
1600
2015
  const snoreToastPath = getSnoreToastPath();
1601
2016
  try {
1602
- fs10.chmodSync(snoreToastPath, 493);
2017
+ fs13.chmodSync(snoreToastPath, 493);
1603
2018
  } catch {
1604
2019
  }
1605
2020
  const args = ["-t", title, "-m", message];
@@ -1671,7 +2086,7 @@ async function notify() {
1671
2086
 
1672
2087
  // src/commands/prs.ts
1673
2088
  import { execSync as execSync11 } from "child_process";
1674
- import chalk25 from "chalk";
2089
+ import chalk30 from "chalk";
1675
2090
  import enquirer4 from "enquirer";
1676
2091
  var PAGE_SIZE = 10;
1677
2092
  async function prs(options) {
@@ -1706,12 +2121,12 @@ async function displayPaginated(pullRequests) {
1706
2121
  let currentPage = 0;
1707
2122
  const getStatus = (pr) => {
1708
2123
  if (pr.state === "MERGED" && pr.mergedAt) {
1709
- return { label: chalk25.magenta("merged"), date: pr.mergedAt };
2124
+ return { label: chalk30.magenta("merged"), date: pr.mergedAt };
1710
2125
  }
1711
2126
  if (pr.state === "CLOSED" && pr.closedAt) {
1712
- return { label: chalk25.red("closed"), date: pr.closedAt };
2127
+ return { label: chalk30.red("closed"), date: pr.closedAt };
1713
2128
  }
1714
- return { label: chalk25.green("opened"), date: pr.createdAt };
2129
+ return { label: chalk30.green("opened"), date: pr.createdAt };
1715
2130
  };
1716
2131
  const displayPage = (page) => {
1717
2132
  const start = page * PAGE_SIZE;
@@ -1727,9 +2142,9 @@ Page ${page + 1} of ${totalPages} (${pullRequests.length} total)
1727
2142
  const formattedDate = new Date(status.date).toISOString().split("T")[0];
1728
2143
  const fileCount = pr.changedFiles.toLocaleString();
1729
2144
  console.log(
1730
- `${chalk25.cyan(`#${pr.number}`)} ${pr.title} ${chalk25.dim(`(${pr.author.login},`)} ${status.label} ${chalk25.dim(`${formattedDate})`)}`
2145
+ `${chalk30.cyan(`#${pr.number}`)} ${pr.title} ${chalk30.dim(`(${pr.author.login},`)} ${status.label} ${chalk30.dim(`${formattedDate})`)}`
1731
2146
  );
1732
- console.log(chalk25.dim(` ${fileCount} files | ${pr.url}`));
2147
+ console.log(chalk30.dim(` ${fileCount} files | ${pr.url}`));
1733
2148
  console.log();
1734
2149
  }
1735
2150
  };
@@ -1764,21 +2179,21 @@ Page ${page + 1} of ${totalPages} (${pullRequests.length} total)
1764
2179
 
1765
2180
  // src/commands/refactor/check.ts
1766
2181
  import { spawn as spawn2 } from "child_process";
1767
- import * as path14 from "path";
2182
+ import * as path15 from "path";
1768
2183
 
1769
2184
  // src/commands/refactor/getViolations.ts
1770
2185
  import { execSync as execSync12 } from "child_process";
1771
- import fs12 from "fs";
1772
- import { minimatch } from "minimatch";
2186
+ import fs15 from "fs";
2187
+ import { minimatch as minimatch2 } from "minimatch";
1773
2188
 
1774
2189
  // src/commands/refactor/getIgnoredFiles.ts
1775
- import fs11 from "fs";
2190
+ import fs14 from "fs";
1776
2191
  var REFACTOR_YML_PATH = "refactor.yml";
1777
2192
  function parseRefactorYml() {
1778
- if (!fs11.existsSync(REFACTOR_YML_PATH)) {
2193
+ if (!fs14.existsSync(REFACTOR_YML_PATH)) {
1779
2194
  return [];
1780
2195
  }
1781
- const content = fs11.readFileSync(REFACTOR_YML_PATH, "utf-8");
2196
+ const content = fs14.readFileSync(REFACTOR_YML_PATH, "utf-8");
1782
2197
  const entries = [];
1783
2198
  const lines = content.split("\n");
1784
2199
  let currentEntry = {};
@@ -1807,7 +2222,7 @@ function getIgnoredFiles() {
1807
2222
  }
1808
2223
 
1809
2224
  // src/commands/refactor/logViolations.ts
1810
- import chalk26 from "chalk";
2225
+ import chalk31 from "chalk";
1811
2226
  var DEFAULT_MAX_LINES = 100;
1812
2227
  function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
1813
2228
  if (violations.length === 0) {
@@ -1816,43 +2231,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
1816
2231
  }
1817
2232
  return;
1818
2233
  }
1819
- console.error(chalk26.red(`
2234
+ console.error(chalk31.red(`
1820
2235
  Refactor check failed:
1821
2236
  `));
1822
- console.error(chalk26.red(` The following files exceed ${maxLines} lines:
2237
+ console.error(chalk31.red(` The following files exceed ${maxLines} lines:
1823
2238
  `));
1824
2239
  for (const violation of violations) {
1825
- console.error(chalk26.red(` ${violation.file} (${violation.lines} lines)`));
2240
+ console.error(chalk31.red(` ${violation.file} (${violation.lines} lines)`));
1826
2241
  }
1827
2242
  console.error(
1828
- chalk26.yellow(
2243
+ chalk31.yellow(
1829
2244
  `
1830
2245
  Each file needs to be sensibly refactored, or if there is no sensible
1831
2246
  way to refactor it, ignore it with:
1832
2247
  `
1833
2248
  )
1834
2249
  );
1835
- console.error(chalk26.gray(` assist refactor ignore <file>
2250
+ console.error(chalk31.gray(` assist refactor ignore <file>
1836
2251
  `));
1837
2252
  if (process.env.CLAUDECODE) {
1838
- console.error(chalk26.cyan(`
2253
+ console.error(chalk31.cyan(`
1839
2254
  ## Extracting Code to New Files
1840
2255
  `));
1841
2256
  console.error(
1842
- chalk26.cyan(
2257
+ chalk31.cyan(
1843
2258
  ` When extracting logic from one file to another, consider where the extracted code belongs:
1844
2259
  `
1845
2260
  )
1846
2261
  );
1847
2262
  console.error(
1848
- chalk26.cyan(
2263
+ chalk31.cyan(
1849
2264
  ` 1. Keep related logic together: If the extracted code is tightly coupled to the
1850
2265
  original file's domain, create a new folder containing both the original and extracted files.
1851
2266
  `
1852
2267
  )
1853
2268
  );
1854
2269
  console.error(
1855
- chalk26.cyan(
2270
+ chalk31.cyan(
1856
2271
  ` 2. Share common utilities: If the extracted code can be reused across multiple
1857
2272
  domains, move it to a common/shared folder.
1858
2273
  `
@@ -1863,7 +2278,7 @@ Refactor check failed:
1863
2278
 
1864
2279
  // src/commands/refactor/getViolations.ts
1865
2280
  function countLines(filePath) {
1866
- const content = fs12.readFileSync(filePath, "utf-8");
2281
+ const content = fs15.readFileSync(filePath, "utf-8");
1867
2282
  return content.split("\n").length;
1868
2283
  }
1869
2284
  function getGitFiles(options) {
@@ -1892,7 +2307,7 @@ function getViolations(pattern2, options = {}, maxLines = DEFAULT_MAX_LINES) {
1892
2307
  const ignoredFiles = getIgnoredFiles();
1893
2308
  const gitFiles = getGitFiles(options);
1894
2309
  if (pattern2) {
1895
- sourceFiles = sourceFiles.filter((f) => minimatch(f, pattern2));
2310
+ sourceFiles = sourceFiles.filter((f) => minimatch2(f, pattern2));
1896
2311
  }
1897
2312
  if (gitFiles) {
1898
2313
  sourceFiles = sourceFiles.filter((f) => gitFiles.has(f));
@@ -1915,7 +2330,7 @@ async function runVerifyQuietly() {
1915
2330
  return true;
1916
2331
  }
1917
2332
  const { packageJsonPath, verifyScripts } = result;
1918
- const packageDir = path14.dirname(packageJsonPath);
2333
+ const packageDir = path15.dirname(packageJsonPath);
1919
2334
  const results = await Promise.all(
1920
2335
  verifyScripts.map(
1921
2336
  (script) => new Promise(
@@ -1968,28 +2383,28 @@ async function check(pattern2, options) {
1968
2383
  }
1969
2384
 
1970
2385
  // src/commands/refactor/ignore.ts
1971
- import fs13 from "fs";
1972
- import chalk27 from "chalk";
2386
+ import fs16 from "fs";
2387
+ import chalk32 from "chalk";
1973
2388
  var REFACTOR_YML_PATH2 = "refactor.yml";
1974
2389
  function ignore(file) {
1975
- if (!fs13.existsSync(file)) {
1976
- console.error(chalk27.red(`Error: File does not exist: ${file}`));
2390
+ if (!fs16.existsSync(file)) {
2391
+ console.error(chalk32.red(`Error: File does not exist: ${file}`));
1977
2392
  process.exit(1);
1978
2393
  }
1979
- const content = fs13.readFileSync(file, "utf-8");
2394
+ const content = fs16.readFileSync(file, "utf-8");
1980
2395
  const lineCount = content.split("\n").length;
1981
2396
  const maxLines = lineCount + 10;
1982
2397
  const entry = `- file: ${file}
1983
2398
  maxLines: ${maxLines}
1984
2399
  `;
1985
- if (fs13.existsSync(REFACTOR_YML_PATH2)) {
1986
- const existing = fs13.readFileSync(REFACTOR_YML_PATH2, "utf-8");
1987
- fs13.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
2400
+ if (fs16.existsSync(REFACTOR_YML_PATH2)) {
2401
+ const existing = fs16.readFileSync(REFACTOR_YML_PATH2, "utf-8");
2402
+ fs16.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
1988
2403
  } else {
1989
- fs13.writeFileSync(REFACTOR_YML_PATH2, entry);
2404
+ fs16.writeFileSync(REFACTOR_YML_PATH2, entry);
1990
2405
  }
1991
2406
  console.log(
1992
- chalk27.green(
2407
+ chalk32.green(
1993
2408
  `Added ${file} to refactor ignore list (max ${maxLines} lines)`
1994
2409
  )
1995
2410
  );
@@ -2080,31 +2495,31 @@ async function statusLine() {
2080
2495
  }
2081
2496
 
2082
2497
  // src/commands/sync.ts
2083
- import * as fs15 from "fs";
2498
+ import * as fs18 from "fs";
2084
2499
  import * as os from "os";
2085
- import * as path16 from "path";
2500
+ import * as path17 from "path";
2086
2501
  import { fileURLToPath as fileURLToPath4 } from "url";
2087
2502
 
2088
2503
  // src/commands/sync/syncSettings.ts
2089
- import * as fs14 from "fs";
2090
- import * as path15 from "path";
2091
- import chalk28 from "chalk";
2504
+ import * as fs17 from "fs";
2505
+ import * as path16 from "path";
2506
+ import chalk33 from "chalk";
2092
2507
  async function syncSettings(claudeDir, targetBase) {
2093
- const source = path15.join(claudeDir, "settings.json");
2094
- const target = path15.join(targetBase, "settings.json");
2095
- const sourceContent = fs14.readFileSync(source, "utf-8");
2508
+ const source = path16.join(claudeDir, "settings.json");
2509
+ const target = path16.join(targetBase, "settings.json");
2510
+ const sourceContent = fs17.readFileSync(source, "utf-8");
2096
2511
  const normalizedSource = JSON.stringify(JSON.parse(sourceContent), null, 2);
2097
- if (fs14.existsSync(target)) {
2098
- const targetContent = fs14.readFileSync(target, "utf-8");
2512
+ if (fs17.existsSync(target)) {
2513
+ const targetContent = fs17.readFileSync(target, "utf-8");
2099
2514
  const normalizedTarget = JSON.stringify(JSON.parse(targetContent), null, 2);
2100
2515
  if (normalizedSource !== normalizedTarget) {
2101
2516
  console.log(
2102
- chalk28.yellow("\n\u26A0\uFE0F Warning: settings.json differs from existing file")
2517
+ chalk33.yellow("\n\u26A0\uFE0F Warning: settings.json differs from existing file")
2103
2518
  );
2104
2519
  console.log();
2105
2520
  printDiff(targetContent, sourceContent);
2106
2521
  const confirm = await promptConfirm(
2107
- chalk28.red("Overwrite existing settings.json?"),
2522
+ chalk33.red("Overwrite existing settings.json?"),
2108
2523
  false
2109
2524
  );
2110
2525
  if (!confirm) {
@@ -2113,26 +2528,26 @@ async function syncSettings(claudeDir, targetBase) {
2113
2528
  }
2114
2529
  }
2115
2530
  }
2116
- fs14.copyFileSync(source, target);
2531
+ fs17.copyFileSync(source, target);
2117
2532
  console.log("Copied settings.json to ~/.claude/settings.json");
2118
2533
  }
2119
2534
 
2120
2535
  // src/commands/sync.ts
2121
2536
  var __filename2 = fileURLToPath4(import.meta.url);
2122
- var __dirname5 = path16.dirname(__filename2);
2537
+ var __dirname5 = path17.dirname(__filename2);
2123
2538
  async function sync() {
2124
- const claudeDir = path16.join(__dirname5, "..", "claude");
2125
- const targetBase = path16.join(os.homedir(), ".claude");
2539
+ const claudeDir = path17.join(__dirname5, "..", "claude");
2540
+ const targetBase = path17.join(os.homedir(), ".claude");
2126
2541
  syncCommands(claudeDir, targetBase);
2127
2542
  await syncSettings(claudeDir, targetBase);
2128
2543
  }
2129
2544
  function syncCommands(claudeDir, targetBase) {
2130
- const sourceDir = path16.join(claudeDir, "commands");
2131
- const targetDir = path16.join(targetBase, "commands");
2132
- fs15.mkdirSync(targetDir, { recursive: true });
2133
- const files = fs15.readdirSync(sourceDir);
2545
+ const sourceDir = path17.join(claudeDir, "commands");
2546
+ const targetDir = path17.join(targetBase, "commands");
2547
+ fs18.mkdirSync(targetDir, { recursive: true });
2548
+ const files = fs18.readdirSync(sourceDir);
2134
2549
  for (const file of files) {
2135
- fs15.copyFileSync(path16.join(sourceDir, file), path16.join(targetDir, file));
2550
+ fs18.copyFileSync(path17.join(sourceDir, file), path17.join(targetDir, file));
2136
2551
  console.log(`Copied ${file} to ${targetDir}`);
2137
2552
  }
2138
2553
  console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
@@ -2173,7 +2588,7 @@ Total: ${lines.length} hardcoded color(s)`);
2173
2588
 
2174
2589
  // src/commands/verify/run.ts
2175
2590
  import { spawn as spawn4 } from "child_process";
2176
- import * as path17 from "path";
2591
+ import * as path18 from "path";
2177
2592
  function formatDuration(ms) {
2178
2593
  if (ms < 1e3) {
2179
2594
  return `${ms}ms`;
@@ -2203,7 +2618,7 @@ async function run2(options = {}) {
2203
2618
  return;
2204
2619
  }
2205
2620
  const { packageJsonPath, verifyScripts } = result;
2206
- const packageDir = path17.dirname(packageJsonPath);
2621
+ const packageDir = path18.dirname(packageJsonPath);
2207
2622
  console.log(`Running ${verifyScripts.length} verify script(s) in parallel:`);
2208
2623
  for (const script of verifyScripts) {
2209
2624
  console.log(` - ${script}`);
@@ -2292,4 +2707,13 @@ program.command("status-line").description("Format Claude Code status line from
2292
2707
  program.command("notify").description(
2293
2708
  "Show notification from Claude Code hook (reads JSON from stdin)"
2294
2709
  ).action(notify);
2710
+ var complexityCommand = program.command("complexity").description("Analyze TypeScript code complexity metrics");
2711
+ complexityCommand.command("cyclomatic [pattern]").description("Calculate cyclomatic complexity per function").option("--threshold <number>", "Max complexity threshold", Number.parseInt).action(cyclomatic);
2712
+ complexityCommand.command("halstead [pattern]").description("Calculate Halstead metrics per function").option("--threshold <number>", "Max volume threshold", Number.parseInt).action(halstead);
2713
+ complexityCommand.command("maintainability [pattern]").description("Calculate maintainability index per file").option(
2714
+ "--threshold <number>",
2715
+ "Min maintainability threshold",
2716
+ Number.parseInt
2717
+ ).action(maintainability);
2718
+ complexityCommand.command("sloc [pattern]").description("Count source lines of code per file").option("--threshold <number>", "Max lines threshold", Number.parseInt).action(sloc);
2295
2719
  program.parse();