@staff0rd/assist 0.109.0 → 0.110.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/claude/CLAUDE.md +5 -0
- package/dist/commands/lint/lint/applyMoves.ts +50 -0
- package/dist/commands/lint/lint/checkFileNames.ts +81 -0
- package/dist/commands/lint/lint/createLintProject.ts +16 -0
- package/dist/commands/lint/lint/fixFileNameViolations.ts +16 -0
- package/dist/commands/lint/lint/index.ts +6 -2
- package/dist/commands/lint/lint/renameExports.ts +37 -0
- package/dist/commands/lint/lint/runFileNameCheck.ts +31 -55
- package/dist/index.js +651 -407
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.110.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -46,6 +46,7 @@ var package_default = {
|
|
|
46
46
|
"node-notifier": "^10.0.1",
|
|
47
47
|
semver: "^7.7.3",
|
|
48
48
|
"shell-quote": "^1.8.3",
|
|
49
|
+
"ts-morph": "^27.0.2",
|
|
49
50
|
typescript: "^5.9.3",
|
|
50
51
|
yaml: "^2.8.2",
|
|
51
52
|
zod: "^4.3.6"
|
|
@@ -88,10 +89,10 @@ import { stringify as stringifyYaml } from "yaml";
|
|
|
88
89
|
// src/shared/loadRawYaml.ts
|
|
89
90
|
import { existsSync, readFileSync } from "fs";
|
|
90
91
|
import { parse as parseYaml } from "yaml";
|
|
91
|
-
function loadRawYaml(
|
|
92
|
-
if (!existsSync(
|
|
92
|
+
function loadRawYaml(path43) {
|
|
93
|
+
if (!existsSync(path43)) return {};
|
|
93
94
|
try {
|
|
94
|
-
const content = readFileSync(
|
|
95
|
+
const content = readFileSync(path43, "utf-8");
|
|
95
96
|
return parseYaml(content) || {};
|
|
96
97
|
} catch {
|
|
97
98
|
return {};
|
|
@@ -329,9 +330,9 @@ function isTraversable(value) {
|
|
|
329
330
|
function stepInto(current, key) {
|
|
330
331
|
return isTraversable(current) ? current[key] : void 0;
|
|
331
332
|
}
|
|
332
|
-
function getNestedValue(obj,
|
|
333
|
+
function getNestedValue(obj, path43) {
|
|
333
334
|
let current = obj;
|
|
334
|
-
for (const key of
|
|
335
|
+
for (const key of path43.split(".")) current = stepInto(current, key);
|
|
335
336
|
return current;
|
|
336
337
|
}
|
|
337
338
|
|
|
@@ -372,8 +373,8 @@ function stepIntoNested(container, key, nextKey) {
|
|
|
372
373
|
}
|
|
373
374
|
return ensureObject(container, resolved);
|
|
374
375
|
}
|
|
375
|
-
function setNestedValue(obj,
|
|
376
|
-
const keys =
|
|
376
|
+
function setNestedValue(obj, path43, value) {
|
|
377
|
+
const keys = path43.split(".");
|
|
377
378
|
const result = { ...obj };
|
|
378
379
|
let current = result;
|
|
379
380
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -515,7 +516,7 @@ function findPackageJsonWithVerifyScripts(startDir) {
|
|
|
515
516
|
// src/commands/verify/setup/expectedScripts.ts
|
|
516
517
|
var expectedScripts = {
|
|
517
518
|
"verify:knip": "knip --no-progress --treat-config-hints-as-errors",
|
|
518
|
-
"verify:lint": "biome check --write .",
|
|
519
|
+
"verify:lint": "biome check --write --error-on-warnings .",
|
|
519
520
|
"verify:duplicate-code": "jscpd --format 'typescript,tsx' --exitCode 1 --ignore '**/*.test.*' -r consoleFull src",
|
|
520
521
|
"verify:test": "vitest run --reporter=dot --silent",
|
|
521
522
|
"verify:hardcoded-colors": "assist verify hardcoded-colors",
|
|
@@ -1245,9 +1246,12 @@ async function init4() {
|
|
|
1245
1246
|
}
|
|
1246
1247
|
|
|
1247
1248
|
// src/commands/lint/lint/runFileNameCheck.ts
|
|
1249
|
+
import path17 from "path";
|
|
1250
|
+
import chalk20 from "chalk";
|
|
1251
|
+
|
|
1252
|
+
// src/commands/lint/lint/checkFileNames.ts
|
|
1248
1253
|
import fs6 from "fs";
|
|
1249
1254
|
import path13 from "path";
|
|
1250
|
-
import chalk19 from "chalk";
|
|
1251
1255
|
|
|
1252
1256
|
// src/shared/findSourceFiles.ts
|
|
1253
1257
|
import fs5 from "fs";
|
|
@@ -1274,71 +1278,198 @@ function findSourceFiles(dir, options2 = {}) {
|
|
|
1274
1278
|
return results;
|
|
1275
1279
|
}
|
|
1276
1280
|
|
|
1277
|
-
// src/commands/lint/lint/
|
|
1281
|
+
// src/commands/lint/lint/checkFileNames.ts
|
|
1278
1282
|
function hasClassOrComponent(content) {
|
|
1279
1283
|
const classPattern = /^(export\s+)?(abstract\s+)?class\s+\w+/m;
|
|
1280
1284
|
const functionComponentPattern = /^(export\s+)?(default\s+)?function\s+[A-Z]\w*\s*\(/m;
|
|
1281
1285
|
const arrowComponentPattern = /^(export\s+)?(const|let)\s+[A-Z]\w*\s*=.*=>/m;
|
|
1282
1286
|
return classPattern.test(content) || functionComponentPattern.test(content) || arrowComponentPattern.test(content);
|
|
1283
1287
|
}
|
|
1288
|
+
function hasMatchingTypeExport(content, nameWithoutExt) {
|
|
1289
|
+
const typePattern = new RegExp(
|
|
1290
|
+
`^export\\s+type\\s+${nameWithoutExt}\\b`,
|
|
1291
|
+
"m"
|
|
1292
|
+
);
|
|
1293
|
+
const interfacePattern = new RegExp(
|
|
1294
|
+
`^export\\s+interface\\s+${nameWithoutExt}\\b`,
|
|
1295
|
+
"m"
|
|
1296
|
+
);
|
|
1297
|
+
return typePattern.test(content) || interfacePattern.test(content);
|
|
1298
|
+
}
|
|
1299
|
+
function suggestName(fileName) {
|
|
1300
|
+
const nameWithoutExt = fileName.replace(/\.(ts|tsx)$/, "");
|
|
1301
|
+
const ext = fileName.slice(nameWithoutExt.length);
|
|
1302
|
+
if (/^[A-Z][A-Z0-9]*(_[A-Z0-9]+)+$/.test(nameWithoutExt)) {
|
|
1303
|
+
const camel = nameWithoutExt.toLowerCase().replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
1304
|
+
return `${camel}${ext}`;
|
|
1305
|
+
}
|
|
1306
|
+
return `${nameWithoutExt.charAt(0).toLowerCase()}${nameWithoutExt.slice(1)}${ext}`;
|
|
1307
|
+
}
|
|
1284
1308
|
function checkFileNames() {
|
|
1285
1309
|
const sourceFiles = findSourceFiles("src");
|
|
1286
1310
|
const violations = [];
|
|
1287
1311
|
for (const filePath of sourceFiles) {
|
|
1288
1312
|
const fileName = path13.basename(filePath);
|
|
1289
1313
|
const nameWithoutExt = fileName.replace(/\.(ts|tsx)$/, "");
|
|
1314
|
+
if (/\.(stories|test)\.(ts|tsx)$/.test(fileName)) continue;
|
|
1290
1315
|
if (/^[A-Z]/.test(nameWithoutExt)) {
|
|
1291
1316
|
const content = fs6.readFileSync(filePath, "utf-8");
|
|
1292
|
-
if (!hasClassOrComponent(content)) {
|
|
1293
|
-
violations.push({
|
|
1317
|
+
if (!hasClassOrComponent(content) && !hasMatchingTypeExport(content, nameWithoutExt)) {
|
|
1318
|
+
violations.push({
|
|
1319
|
+
filePath,
|
|
1320
|
+
fileName,
|
|
1321
|
+
suggestedName: suggestName(fileName)
|
|
1322
|
+
});
|
|
1294
1323
|
}
|
|
1295
1324
|
}
|
|
1296
1325
|
}
|
|
1297
1326
|
return violations;
|
|
1298
1327
|
}
|
|
1299
|
-
|
|
1328
|
+
|
|
1329
|
+
// src/commands/lint/lint/fixFileNameViolations.ts
|
|
1330
|
+
import chalk19 from "chalk";
|
|
1331
|
+
|
|
1332
|
+
// src/commands/lint/lint/applyMoves.ts
|
|
1333
|
+
import fs7 from "fs";
|
|
1334
|
+
import path15 from "path";
|
|
1335
|
+
|
|
1336
|
+
// src/commands/lint/lint/renameExports.ts
|
|
1337
|
+
import path14 from "path";
|
|
1338
|
+
import { SyntaxKind } from "ts-morph";
|
|
1339
|
+
function nameWithoutExtension(filePath) {
|
|
1340
|
+
return path14.basename(filePath).replace(/\.(ts|tsx)$/, "");
|
|
1341
|
+
}
|
|
1342
|
+
function renameExports(project, absSource, absDest) {
|
|
1343
|
+
const oldName = nameWithoutExtension(absSource);
|
|
1344
|
+
const newName = nameWithoutExtension(absDest);
|
|
1345
|
+
const sourceFile = project.getSourceFile(absSource);
|
|
1346
|
+
if (!sourceFile) return [];
|
|
1347
|
+
const renamed = [];
|
|
1348
|
+
for (const [, declarations] of sourceFile.getExportedDeclarations()) {
|
|
1349
|
+
for (const decl of declarations) {
|
|
1350
|
+
const kind = decl.getKind();
|
|
1351
|
+
if (kind === SyntaxKind.TypeAliasDeclaration || kind === SyntaxKind.InterfaceDeclaration) {
|
|
1352
|
+
continue;
|
|
1353
|
+
}
|
|
1354
|
+
const nameNode = decl.getFirstChildByKind(SyntaxKind.Identifier);
|
|
1355
|
+
if (!nameNode || nameNode.getText() !== oldName) continue;
|
|
1356
|
+
nameNode.rename(newName);
|
|
1357
|
+
renamed.push(`${oldName} \u2192 ${newName}`);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return renamed;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// src/commands/lint/lint/applyMoves.ts
|
|
1364
|
+
function isCaseOnly(a, b) {
|
|
1365
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
1366
|
+
}
|
|
1367
|
+
function moveCaseInsensitive(absSource, absDest) {
|
|
1368
|
+
const tmp = `${absSource}.tmp`;
|
|
1369
|
+
fs7.renameSync(absSource, tmp);
|
|
1370
|
+
fs7.renameSync(tmp, absDest);
|
|
1371
|
+
}
|
|
1372
|
+
function applyMoves(project, moves, cwd, emit) {
|
|
1373
|
+
for (const { sourcePath, destPath } of moves) {
|
|
1374
|
+
const start3 = performance.now();
|
|
1375
|
+
const absSource = path15.resolve(sourcePath);
|
|
1376
|
+
const absDest = path15.resolve(destPath);
|
|
1377
|
+
for (const r of renameExports(project, absSource, absDest)) {
|
|
1378
|
+
emit(` Renamed export ${r} in ${sourcePath}`);
|
|
1379
|
+
}
|
|
1380
|
+
const sourceFile = project.getSourceFile(absSource);
|
|
1381
|
+
if (sourceFile) sourceFile.move(absDest);
|
|
1382
|
+
const ms = (performance.now() - start3).toFixed(0);
|
|
1383
|
+
const rel = `${path15.relative(cwd, absSource)} \u2192 ${path15.relative(cwd, absDest)}`;
|
|
1384
|
+
emit(` Renamed ${rel} (${ms}ms)`);
|
|
1385
|
+
}
|
|
1386
|
+
project.saveSync();
|
|
1387
|
+
for (const { sourcePath, destPath } of moves) {
|
|
1388
|
+
const absSource = path15.resolve(sourcePath);
|
|
1389
|
+
const absDest = path15.resolve(destPath);
|
|
1390
|
+
if (isCaseOnly(absSource, absDest) && fs7.existsSync(absSource)) {
|
|
1391
|
+
moveCaseInsensitive(absSource, absDest);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// src/commands/lint/lint/createLintProject.ts
|
|
1397
|
+
import fs8 from "fs";
|
|
1398
|
+
import path16 from "path";
|
|
1399
|
+
import { Project } from "ts-morph";
|
|
1400
|
+
function createLintProject() {
|
|
1401
|
+
const tsConfigPath = path16.resolve("tsconfig.json");
|
|
1402
|
+
const project = fs8.existsSync(tsConfigPath) ? new Project({
|
|
1403
|
+
tsConfigFilePath: tsConfigPath,
|
|
1404
|
+
skipAddingFilesFromTsConfig: true
|
|
1405
|
+
}) : new Project();
|
|
1406
|
+
project.addSourceFilesAtPaths("src/**/*.{ts,tsx}");
|
|
1407
|
+
return project;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
// src/commands/lint/lint/fixFileNameViolations.ts
|
|
1411
|
+
function fixFileNameViolations(moves) {
|
|
1412
|
+
const start3 = performance.now();
|
|
1413
|
+
const project = createLintProject();
|
|
1414
|
+
const cwd = process.cwd();
|
|
1415
|
+
applyMoves(project, moves, cwd, (line) => console.log(chalk19.green(line)));
|
|
1416
|
+
const ms = (performance.now() - start3).toFixed(0);
|
|
1417
|
+
console.log(chalk19.dim(` Done in ${ms}ms`));
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
// src/commands/lint/lint/runFileNameCheck.ts
|
|
1421
|
+
function reportViolations(violations) {
|
|
1422
|
+
console.error(chalk20.red("\nFile name check failed:\n"));
|
|
1423
|
+
console.error(
|
|
1424
|
+
chalk20.red(
|
|
1425
|
+
" Files without classes or React components should not start with a capital letter.\n"
|
|
1426
|
+
)
|
|
1427
|
+
);
|
|
1428
|
+
for (const violation of violations) {
|
|
1429
|
+
console.error(chalk20.red(` ${violation.filePath}`));
|
|
1430
|
+
console.error(chalk20.gray(` Rename to: ${violation.suggestedName}
|
|
1431
|
+
`));
|
|
1432
|
+
}
|
|
1433
|
+
console.error(chalk20.dim(" Run with -f to auto-fix.\n"));
|
|
1434
|
+
}
|
|
1435
|
+
function runFileNameCheck(fix = false) {
|
|
1300
1436
|
const violations = checkFileNames();
|
|
1301
|
-
if (violations.length
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
" Files without classes or React components should not start with a capital letter.\n"
|
|
1306
|
-
)
|
|
1307
|
-
);
|
|
1308
|
-
for (const violation of violations) {
|
|
1309
|
-
console.error(chalk19.red(` ${violation.filePath}`));
|
|
1310
|
-
console.error(
|
|
1311
|
-
chalk19.gray(
|
|
1312
|
-
` Rename to: ${violation.fileName.charAt(0).toLowerCase()}${violation.fileName.slice(1)}
|
|
1313
|
-
`
|
|
1314
|
-
)
|
|
1437
|
+
if (violations.length === 0) {
|
|
1438
|
+
if (!process.env.CLAUDECODE) {
|
|
1439
|
+
console.log(
|
|
1440
|
+
"File name check passed. All PascalCase files contain classes or components."
|
|
1315
1441
|
);
|
|
1316
1442
|
}
|
|
1317
|
-
return
|
|
1443
|
+
return true;
|
|
1318
1444
|
}
|
|
1319
|
-
if (!
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
);
|
|
1445
|
+
if (!fix) {
|
|
1446
|
+
reportViolations(violations);
|
|
1447
|
+
return false;
|
|
1323
1448
|
}
|
|
1449
|
+
fixFileNameViolations(
|
|
1450
|
+
violations.map((v) => ({
|
|
1451
|
+
sourcePath: v.filePath,
|
|
1452
|
+
destPath: path17.join(path17.dirname(v.filePath), v.suggestedName)
|
|
1453
|
+
}))
|
|
1454
|
+
);
|
|
1324
1455
|
return true;
|
|
1325
1456
|
}
|
|
1326
1457
|
|
|
1327
1458
|
// src/commands/lint/lint/runImportExtensionCheck.ts
|
|
1328
|
-
import
|
|
1459
|
+
import fs9 from "fs";
|
|
1329
1460
|
|
|
1330
1461
|
// src/commands/lint/shared.ts
|
|
1331
|
-
import
|
|
1332
|
-
function
|
|
1462
|
+
import chalk21 from "chalk";
|
|
1463
|
+
function reportViolations2(violations, checkName, errorMessage, successMessage) {
|
|
1333
1464
|
if (violations.length > 0) {
|
|
1334
|
-
console.error(
|
|
1465
|
+
console.error(chalk21.red(`
|
|
1335
1466
|
${checkName} failed:
|
|
1336
1467
|
`));
|
|
1337
|
-
console.error(
|
|
1468
|
+
console.error(chalk21.red(` ${errorMessage}
|
|
1338
1469
|
`));
|
|
1339
1470
|
for (const violation of violations) {
|
|
1340
|
-
console.error(
|
|
1341
|
-
console.error(
|
|
1471
|
+
console.error(chalk21.red(` ${violation.filePath}:${violation.line}`));
|
|
1472
|
+
console.error(chalk21.gray(` ${violation.content}
|
|
1342
1473
|
`));
|
|
1343
1474
|
}
|
|
1344
1475
|
return false;
|
|
@@ -1351,7 +1482,7 @@ ${checkName} failed:
|
|
|
1351
1482
|
|
|
1352
1483
|
// src/commands/lint/lint/runImportExtensionCheck.ts
|
|
1353
1484
|
function checkForImportExtensions(filePath) {
|
|
1354
|
-
const content =
|
|
1485
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
1355
1486
|
const lines = content.split("\n");
|
|
1356
1487
|
const violations = [];
|
|
1357
1488
|
const importExtensionPattern = /from\s+["']\..*\.(js|ts)["']/;
|
|
@@ -1376,7 +1507,7 @@ function checkImportExtensions() {
|
|
|
1376
1507
|
return violations;
|
|
1377
1508
|
}
|
|
1378
1509
|
function runImportExtensionCheck() {
|
|
1379
|
-
return
|
|
1510
|
+
return reportViolations2(
|
|
1380
1511
|
checkImportExtensions(),
|
|
1381
1512
|
"Import extension check",
|
|
1382
1513
|
"File extensions in imports are not allowed. Use extensionless imports instead.",
|
|
@@ -1385,9 +1516,9 @@ function runImportExtensionCheck() {
|
|
|
1385
1516
|
}
|
|
1386
1517
|
|
|
1387
1518
|
// src/commands/lint/lint/runStaticImportCheck.ts
|
|
1388
|
-
import
|
|
1519
|
+
import fs10 from "fs";
|
|
1389
1520
|
function checkForDynamicImports(filePath) {
|
|
1390
|
-
const content =
|
|
1521
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
1391
1522
|
const lines = content.split("\n");
|
|
1392
1523
|
const violations = [];
|
|
1393
1524
|
const requirePattern = /\brequire\s*\(/;
|
|
@@ -1413,7 +1544,7 @@ function checkStaticImports() {
|
|
|
1413
1544
|
return violations;
|
|
1414
1545
|
}
|
|
1415
1546
|
function runStaticImportCheck() {
|
|
1416
|
-
return
|
|
1547
|
+
return reportViolations2(
|
|
1417
1548
|
checkStaticImports(),
|
|
1418
1549
|
"Static import check",
|
|
1419
1550
|
"Dynamic imports are not allowed. Use static imports instead.",
|
|
@@ -1422,8 +1553,8 @@ function runStaticImportCheck() {
|
|
|
1422
1553
|
}
|
|
1423
1554
|
|
|
1424
1555
|
// src/commands/lint/lint/index.ts
|
|
1425
|
-
function lint() {
|
|
1426
|
-
const fileNamePassed = runFileNameCheck();
|
|
1556
|
+
function lint(options2 = {}) {
|
|
1557
|
+
const fileNamePassed = runFileNameCheck(options2.fix);
|
|
1427
1558
|
const staticImportPassed = runStaticImportCheck();
|
|
1428
1559
|
const importExtensionPassed = runImportExtensionCheck();
|
|
1429
1560
|
if (!fileNamePassed || !staticImportPassed || !importExtensionPassed) {
|
|
@@ -1480,7 +1611,7 @@ Total: ${lines.length} hardcoded color(s)`);
|
|
|
1480
1611
|
}
|
|
1481
1612
|
|
|
1482
1613
|
// src/commands/verify/run/resolveEntries.ts
|
|
1483
|
-
import * as
|
|
1614
|
+
import * as path18 from "path";
|
|
1484
1615
|
function buildFullCommand(command, args) {
|
|
1485
1616
|
return [shellQuote(command), ...(args ?? []).map(shellQuote)].join(" ");
|
|
1486
1617
|
}
|
|
@@ -1497,7 +1628,7 @@ function getRunEntries() {
|
|
|
1497
1628
|
function getPackageJsonEntries() {
|
|
1498
1629
|
const result = findPackageJsonWithVerifyScripts(process.cwd());
|
|
1499
1630
|
if (!result) return [];
|
|
1500
|
-
const cwd =
|
|
1631
|
+
const cwd = path18.dirname(result.packageJsonPath);
|
|
1501
1632
|
return result.verifyScripts.map((script) => ({
|
|
1502
1633
|
name: script,
|
|
1503
1634
|
fullCommand: `npm run ${script}`,
|
|
@@ -1818,14 +1949,14 @@ import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSyn
|
|
|
1818
1949
|
|
|
1819
1950
|
// src/commands/deploy/init/index.ts
|
|
1820
1951
|
import { execSync as execSync11 } from "child_process";
|
|
1821
|
-
import
|
|
1952
|
+
import chalk23 from "chalk";
|
|
1822
1953
|
import enquirer3 from "enquirer";
|
|
1823
1954
|
|
|
1824
1955
|
// src/commands/deploy/init/updateWorkflow.ts
|
|
1825
1956
|
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
1826
1957
|
import { dirname as dirname11, join as join7 } from "path";
|
|
1827
1958
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1828
|
-
import
|
|
1959
|
+
import chalk22 from "chalk";
|
|
1829
1960
|
var WORKFLOW_PATH = ".github/workflows/build.yml";
|
|
1830
1961
|
var __dirname3 = dirname11(fileURLToPath2(import.meta.url));
|
|
1831
1962
|
function getExistingSiteId() {
|
|
@@ -1850,20 +1981,20 @@ async function updateWorkflow(siteId) {
|
|
|
1850
1981
|
if (existsSync10(WORKFLOW_PATH)) {
|
|
1851
1982
|
const oldContent = readFileSync8(WORKFLOW_PATH, "utf-8");
|
|
1852
1983
|
if (oldContent === newContent) {
|
|
1853
|
-
console.log(
|
|
1984
|
+
console.log(chalk22.green("build.yml is already up to date"));
|
|
1854
1985
|
return;
|
|
1855
1986
|
}
|
|
1856
|
-
console.log(
|
|
1987
|
+
console.log(chalk22.yellow("\nbuild.yml will be updated:"));
|
|
1857
1988
|
console.log();
|
|
1858
1989
|
printDiff(oldContent, newContent);
|
|
1859
|
-
const confirm = await promptConfirm(
|
|
1990
|
+
const confirm = await promptConfirm(chalk22.red("Update build.yml?"));
|
|
1860
1991
|
if (!confirm) {
|
|
1861
1992
|
console.log("Skipped build.yml update");
|
|
1862
1993
|
return;
|
|
1863
1994
|
}
|
|
1864
1995
|
}
|
|
1865
1996
|
writeFileSync8(WORKFLOW_PATH, newContent);
|
|
1866
|
-
console.log(
|
|
1997
|
+
console.log(chalk22.green(`
|
|
1867
1998
|
Created ${WORKFLOW_PATH}`));
|
|
1868
1999
|
}
|
|
1869
2000
|
|
|
@@ -1874,43 +2005,43 @@ async function ensureNetlifyCli() {
|
|
|
1874
2005
|
} catch (error) {
|
|
1875
2006
|
if (!(error instanceof Error) || !error.message.includes("command not found"))
|
|
1876
2007
|
throw error;
|
|
1877
|
-
console.error(
|
|
2008
|
+
console.error(chalk23.red("\nNetlify CLI is not installed.\n"));
|
|
1878
2009
|
const install = await promptConfirm("Would you like to install it now?");
|
|
1879
2010
|
if (!install) {
|
|
1880
2011
|
console.log(
|
|
1881
|
-
|
|
2012
|
+
chalk23.yellow(
|
|
1882
2013
|
"\nInstall it manually with: npm install -g netlify-cli\n"
|
|
1883
2014
|
)
|
|
1884
2015
|
);
|
|
1885
2016
|
process.exit(1);
|
|
1886
2017
|
}
|
|
1887
|
-
console.log(
|
|
2018
|
+
console.log(chalk23.dim("\nInstalling netlify-cli...\n"));
|
|
1888
2019
|
execSync11("npm install -g netlify-cli", { stdio: "inherit" });
|
|
1889
2020
|
console.log();
|
|
1890
2021
|
execSync11("netlify sites:create --disable-linking", { stdio: "inherit" });
|
|
1891
2022
|
}
|
|
1892
2023
|
}
|
|
1893
2024
|
function printSetupInstructions() {
|
|
1894
|
-
console.log(
|
|
2025
|
+
console.log(chalk23.bold("\nDeployment initialized successfully!"));
|
|
1895
2026
|
console.log(
|
|
1896
|
-
|
|
2027
|
+
chalk23.yellow("\nTo complete setup, create a personal access token at:")
|
|
1897
2028
|
);
|
|
1898
2029
|
console.log(
|
|
1899
|
-
|
|
2030
|
+
chalk23.cyan(
|
|
1900
2031
|
"https://app.netlify.com/user/applications#personal-access-tokens"
|
|
1901
2032
|
)
|
|
1902
2033
|
);
|
|
1903
2034
|
console.log(
|
|
1904
|
-
|
|
2035
|
+
chalk23.yellow(
|
|
1905
2036
|
"\nThen add it as NETLIFY_AUTH_TOKEN in your GitHub repository secrets."
|
|
1906
2037
|
)
|
|
1907
2038
|
);
|
|
1908
2039
|
}
|
|
1909
2040
|
async function init5() {
|
|
1910
|
-
console.log(
|
|
2041
|
+
console.log(chalk23.bold("Initializing Netlify deployment...\n"));
|
|
1911
2042
|
const existingSiteId = getExistingSiteId();
|
|
1912
2043
|
if (existingSiteId) {
|
|
1913
|
-
console.log(
|
|
2044
|
+
console.log(chalk23.dim(`Using existing site ID: ${existingSiteId}
|
|
1914
2045
|
`));
|
|
1915
2046
|
await updateWorkflow(existingSiteId);
|
|
1916
2047
|
return;
|
|
@@ -2004,19 +2135,19 @@ function detectPlatform() {
|
|
|
2004
2135
|
|
|
2005
2136
|
// src/commands/notify/showNotification/showWindowsNotificationFromWsl.ts
|
|
2006
2137
|
import { spawn as spawn2 } from "child_process";
|
|
2007
|
-
import
|
|
2138
|
+
import fs11 from "fs";
|
|
2008
2139
|
import { createRequire } from "module";
|
|
2009
|
-
import
|
|
2140
|
+
import path19 from "path";
|
|
2010
2141
|
var require2 = createRequire(import.meta.url);
|
|
2011
2142
|
function getSnoreToastPath() {
|
|
2012
|
-
const notifierPath =
|
|
2013
|
-
return
|
|
2143
|
+
const notifierPath = path19.dirname(require2.resolve("node-notifier"));
|
|
2144
|
+
return path19.join(notifierPath, "vendor", "snoreToast", "snoretoast-x64.exe");
|
|
2014
2145
|
}
|
|
2015
2146
|
function showWindowsNotificationFromWsl(options2) {
|
|
2016
2147
|
const { title, message, sound } = options2;
|
|
2017
2148
|
const snoreToastPath = getSnoreToastPath();
|
|
2018
2149
|
try {
|
|
2019
|
-
|
|
2150
|
+
fs11.chmodSync(snoreToastPath, 493);
|
|
2020
2151
|
} catch {
|
|
2021
2152
|
}
|
|
2022
2153
|
const args = ["-t", title, "-m", message];
|
|
@@ -2090,12 +2221,12 @@ async function notify() {
|
|
|
2090
2221
|
|
|
2091
2222
|
// src/commands/backlog/add/index.ts
|
|
2092
2223
|
import { existsSync as existsSync13 } from "fs";
|
|
2093
|
-
import
|
|
2224
|
+
import chalk25 from "chalk";
|
|
2094
2225
|
|
|
2095
2226
|
// src/commands/backlog/shared.ts
|
|
2096
2227
|
import { existsSync as existsSync12, readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
2097
2228
|
import { join as join8 } from "path";
|
|
2098
|
-
import
|
|
2229
|
+
import chalk24 from "chalk";
|
|
2099
2230
|
import { parse as parseYaml2, stringify as stringifyYaml3 } from "yaml";
|
|
2100
2231
|
|
|
2101
2232
|
// src/commands/backlog/types.ts
|
|
@@ -2139,7 +2270,7 @@ function findItem(items, id) {
|
|
|
2139
2270
|
function loadAndFindItem(id) {
|
|
2140
2271
|
if (!existsSync12(getBacklogPath())) {
|
|
2141
2272
|
console.log(
|
|
2142
|
-
|
|
2273
|
+
chalk24.yellow(
|
|
2143
2274
|
"No backlog found. Run 'assist backlog init' to create one."
|
|
2144
2275
|
)
|
|
2145
2276
|
);
|
|
@@ -2148,7 +2279,7 @@ function loadAndFindItem(id) {
|
|
|
2148
2279
|
const items = loadBacklog();
|
|
2149
2280
|
const item = findItem(items, Number.parseInt(id, 10));
|
|
2150
2281
|
if (!item) {
|
|
2151
|
-
console.log(
|
|
2282
|
+
console.log(chalk24.red(`Item #${id} not found.`));
|
|
2152
2283
|
return void 0;
|
|
2153
2284
|
}
|
|
2154
2285
|
return { items, item };
|
|
@@ -2246,7 +2377,7 @@ async function add() {
|
|
|
2246
2377
|
const backlogPath = getBacklogPath();
|
|
2247
2378
|
if (!existsSync13(backlogPath)) {
|
|
2248
2379
|
console.log(
|
|
2249
|
-
|
|
2380
|
+
chalk25.yellow(
|
|
2250
2381
|
"No backlog found. Run 'assist backlog init' to create one."
|
|
2251
2382
|
)
|
|
2252
2383
|
);
|
|
@@ -2267,67 +2398,67 @@ async function add() {
|
|
|
2267
2398
|
status: "todo"
|
|
2268
2399
|
});
|
|
2269
2400
|
saveBacklog(items);
|
|
2270
|
-
console.log(
|
|
2401
|
+
console.log(chalk25.green(`Added item #${id}: ${name}`));
|
|
2271
2402
|
}
|
|
2272
2403
|
|
|
2273
2404
|
// src/commands/backlog/delete/index.ts
|
|
2274
|
-
import
|
|
2405
|
+
import chalk26 from "chalk";
|
|
2275
2406
|
async function del(id) {
|
|
2276
2407
|
const name = removeItem(id);
|
|
2277
2408
|
if (name) {
|
|
2278
|
-
console.log(
|
|
2409
|
+
console.log(chalk26.green(`Deleted item #${id}: ${name}`));
|
|
2279
2410
|
}
|
|
2280
2411
|
}
|
|
2281
2412
|
|
|
2282
2413
|
// src/commands/backlog/done/index.ts
|
|
2283
|
-
import
|
|
2414
|
+
import chalk27 from "chalk";
|
|
2284
2415
|
async function done(id) {
|
|
2285
2416
|
const name = setStatus(id, "done");
|
|
2286
2417
|
if (name) {
|
|
2287
|
-
console.log(
|
|
2418
|
+
console.log(chalk27.green(`Completed item #${id}: ${name}`));
|
|
2288
2419
|
}
|
|
2289
2420
|
}
|
|
2290
2421
|
|
|
2291
2422
|
// src/commands/backlog/init/index.ts
|
|
2292
2423
|
import { existsSync as existsSync14 } from "fs";
|
|
2293
|
-
import
|
|
2424
|
+
import chalk28 from "chalk";
|
|
2294
2425
|
async function init6() {
|
|
2295
2426
|
const backlogPath = getBacklogPath();
|
|
2296
2427
|
if (existsSync14(backlogPath)) {
|
|
2297
|
-
console.log(
|
|
2428
|
+
console.log(chalk28.yellow("assist.backlog.yml already exists."));
|
|
2298
2429
|
return;
|
|
2299
2430
|
}
|
|
2300
2431
|
saveBacklog([]);
|
|
2301
|
-
console.log(
|
|
2432
|
+
console.log(chalk28.green("Created assist.backlog.yml"));
|
|
2302
2433
|
}
|
|
2303
2434
|
|
|
2304
2435
|
// src/commands/backlog/list/index.ts
|
|
2305
2436
|
import { existsSync as existsSync15 } from "fs";
|
|
2306
|
-
import
|
|
2437
|
+
import chalk29 from "chalk";
|
|
2307
2438
|
function statusIcon(status2) {
|
|
2308
2439
|
switch (status2) {
|
|
2309
2440
|
case "todo":
|
|
2310
|
-
return
|
|
2441
|
+
return chalk29.dim("[ ]");
|
|
2311
2442
|
case "in-progress":
|
|
2312
|
-
return
|
|
2443
|
+
return chalk29.yellow("[~]");
|
|
2313
2444
|
case "done":
|
|
2314
|
-
return
|
|
2445
|
+
return chalk29.green("[x]");
|
|
2315
2446
|
}
|
|
2316
2447
|
}
|
|
2317
2448
|
function typeLabel(type) {
|
|
2318
2449
|
switch (type) {
|
|
2319
2450
|
case "bug":
|
|
2320
|
-
return
|
|
2451
|
+
return chalk29.magenta("Bug");
|
|
2321
2452
|
case "story":
|
|
2322
|
-
return
|
|
2453
|
+
return chalk29.cyan("Story");
|
|
2323
2454
|
}
|
|
2324
2455
|
}
|
|
2325
2456
|
function printVerboseDetails(item) {
|
|
2326
2457
|
if (item.description) {
|
|
2327
|
-
console.log(` ${
|
|
2458
|
+
console.log(` ${chalk29.dim("Description:")} ${item.description}`);
|
|
2328
2459
|
}
|
|
2329
2460
|
if (item.acceptanceCriteria.length > 0) {
|
|
2330
|
-
console.log(` ${
|
|
2461
|
+
console.log(` ${chalk29.dim("Acceptance criteria:")}`);
|
|
2331
2462
|
for (const criterion of item.acceptanceCriteria) {
|
|
2332
2463
|
console.log(` - ${criterion}`);
|
|
2333
2464
|
}
|
|
@@ -2343,7 +2474,7 @@ async function list2(options2) {
|
|
|
2343
2474
|
const backlogPath = getBacklogPath();
|
|
2344
2475
|
if (!existsSync15(backlogPath)) {
|
|
2345
2476
|
console.log(
|
|
2346
|
-
|
|
2477
|
+
chalk29.yellow(
|
|
2347
2478
|
"No backlog found. Run 'assist backlog init' to create one."
|
|
2348
2479
|
)
|
|
2349
2480
|
);
|
|
@@ -2351,12 +2482,12 @@ async function list2(options2) {
|
|
|
2351
2482
|
}
|
|
2352
2483
|
const items = filterItems(loadBacklog(), options2);
|
|
2353
2484
|
if (items.length === 0) {
|
|
2354
|
-
console.log(
|
|
2485
|
+
console.log(chalk29.dim("Backlog is empty."));
|
|
2355
2486
|
return;
|
|
2356
2487
|
}
|
|
2357
2488
|
for (const item of items) {
|
|
2358
2489
|
console.log(
|
|
2359
|
-
`${statusIcon(item.status)} ${typeLabel(item.type)} ${
|
|
2490
|
+
`${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk29.dim(`#${item.id}`)} ${item.name}`
|
|
2360
2491
|
);
|
|
2361
2492
|
if (options2.verbose) {
|
|
2362
2493
|
printVerboseDetails(item);
|
|
@@ -2365,11 +2496,11 @@ async function list2(options2) {
|
|
|
2365
2496
|
}
|
|
2366
2497
|
|
|
2367
2498
|
// src/commands/backlog/start/index.ts
|
|
2368
|
-
import
|
|
2499
|
+
import chalk30 from "chalk";
|
|
2369
2500
|
async function start(id) {
|
|
2370
2501
|
const name = setStatus(id, "in-progress");
|
|
2371
2502
|
if (name) {
|
|
2372
|
-
console.log(
|
|
2503
|
+
console.log(chalk30.green(`Started item #${id}: ${name}`));
|
|
2373
2504
|
}
|
|
2374
2505
|
}
|
|
2375
2506
|
|
|
@@ -2381,7 +2512,7 @@ import {
|
|
|
2381
2512
|
} from "http";
|
|
2382
2513
|
import { dirname as dirname12, join as join10 } from "path";
|
|
2383
2514
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2384
|
-
import
|
|
2515
|
+
import chalk31 from "chalk";
|
|
2385
2516
|
function respondJson(res, status2, data) {
|
|
2386
2517
|
res.writeHead(status2, { "Content-Type": "application/json" });
|
|
2387
2518
|
res.end(JSON.stringify(data));
|
|
@@ -2425,8 +2556,8 @@ function startWebServer(label2, port, handler) {
|
|
|
2425
2556
|
handler(req, res, port);
|
|
2426
2557
|
});
|
|
2427
2558
|
server.listen(port, () => {
|
|
2428
|
-
console.log(
|
|
2429
|
-
console.log(
|
|
2559
|
+
console.log(chalk31.green(`${label2}: ${url}`));
|
|
2560
|
+
console.log(chalk31.dim("Press Ctrl+C to stop"));
|
|
2430
2561
|
exec(`open ${url}`);
|
|
2431
2562
|
});
|
|
2432
2563
|
}
|
|
@@ -2666,12 +2797,12 @@ function getCliReadsPath() {
|
|
|
2666
2797
|
var cachedLines;
|
|
2667
2798
|
function getCliReadsLines() {
|
|
2668
2799
|
if (cachedLines) return cachedLines;
|
|
2669
|
-
const
|
|
2670
|
-
if (!existsSync16(
|
|
2800
|
+
const path43 = getCliReadsPath();
|
|
2801
|
+
if (!existsSync16(path43)) {
|
|
2671
2802
|
cachedLines = [];
|
|
2672
2803
|
return cachedLines;
|
|
2673
2804
|
}
|
|
2674
|
-
cachedLines = readFileSync13(
|
|
2805
|
+
cachedLines = readFileSync13(path43, "utf-8").split("\n").filter((line) => line.trim() !== "");
|
|
2675
2806
|
return cachedLines;
|
|
2676
2807
|
}
|
|
2677
2808
|
function loadCliReads() {
|
|
@@ -2917,11 +3048,11 @@ function assertCliExists(cli) {
|
|
|
2917
3048
|
}
|
|
2918
3049
|
|
|
2919
3050
|
// src/commands/permitCliReads/colorize.ts
|
|
2920
|
-
import
|
|
3051
|
+
import chalk32 from "chalk";
|
|
2921
3052
|
function colorize(plainOutput) {
|
|
2922
3053
|
return plainOutput.split("\n").map((line) => {
|
|
2923
|
-
if (line.startsWith(" R ")) return
|
|
2924
|
-
if (line.startsWith(" W ")) return
|
|
3054
|
+
if (line.startsWith(" R ")) return chalk32.green(line);
|
|
3055
|
+
if (line.startsWith(" W ")) return chalk32.red(line);
|
|
2925
3056
|
return line;
|
|
2926
3057
|
}).join("\n");
|
|
2927
3058
|
}
|
|
@@ -3023,14 +3154,14 @@ function showProgress(p, label2) {
|
|
|
3023
3154
|
const pct = Math.round(p.done / p.total * 100);
|
|
3024
3155
|
process.stderr.write(`\r\x1B[K[${pct}%] Scanning ${label2}...`);
|
|
3025
3156
|
}
|
|
3026
|
-
async function resolveCommand(cli,
|
|
3027
|
-
showProgress(p,
|
|
3028
|
-
const subHelp = await runHelp([cli, ...
|
|
3157
|
+
async function resolveCommand(cli, path43, description, depth, p) {
|
|
3158
|
+
showProgress(p, path43.join(" "));
|
|
3159
|
+
const subHelp = await runHelp([cli, ...path43]);
|
|
3029
3160
|
if (!subHelp || !hasSubcommands(subHelp)) {
|
|
3030
|
-
return [{ path:
|
|
3161
|
+
return [{ path: path43, description }];
|
|
3031
3162
|
}
|
|
3032
|
-
const children = await discoverAt(cli,
|
|
3033
|
-
return children.length > 0 ? children : [{ path:
|
|
3163
|
+
const children = await discoverAt(cli, path43, depth + 1, p);
|
|
3164
|
+
return children.length > 0 ? children : [{ path: path43, description }];
|
|
3034
3165
|
}
|
|
3035
3166
|
async function discoverAt(cli, parentPath, depth, p) {
|
|
3036
3167
|
if (depth > SAFETY_DEPTH) return [];
|
|
@@ -3178,9 +3309,9 @@ function logPath(cli) {
|
|
|
3178
3309
|
return join12(homedir4(), ".assist", `cli-discover-${safeName}.log`);
|
|
3179
3310
|
}
|
|
3180
3311
|
function readCache(cli) {
|
|
3181
|
-
const
|
|
3182
|
-
if (!existsSync18(
|
|
3183
|
-
return readFileSync15(
|
|
3312
|
+
const path43 = logPath(cli);
|
|
3313
|
+
if (!existsSync18(path43)) return void 0;
|
|
3314
|
+
return readFileSync15(path43, "utf-8");
|
|
3184
3315
|
}
|
|
3185
3316
|
function writeCache(cli, output) {
|
|
3186
3317
|
const dir = join12(homedir4(), ".assist");
|
|
@@ -3232,20 +3363,20 @@ function registerCliHook(program2) {
|
|
|
3232
3363
|
}
|
|
3233
3364
|
|
|
3234
3365
|
// src/commands/complexity/analyze.ts
|
|
3235
|
-
import
|
|
3366
|
+
import chalk38 from "chalk";
|
|
3236
3367
|
|
|
3237
3368
|
// src/commands/complexity/cyclomatic.ts
|
|
3238
|
-
import
|
|
3369
|
+
import chalk34 from "chalk";
|
|
3239
3370
|
|
|
3240
3371
|
// src/commands/complexity/shared/index.ts
|
|
3241
|
-
import
|
|
3242
|
-
import
|
|
3243
|
-
import
|
|
3372
|
+
import fs13 from "fs";
|
|
3373
|
+
import path21 from "path";
|
|
3374
|
+
import chalk33 from "chalk";
|
|
3244
3375
|
import ts5 from "typescript";
|
|
3245
3376
|
|
|
3246
3377
|
// src/commands/complexity/findSourceFiles.ts
|
|
3247
|
-
import
|
|
3248
|
-
import
|
|
3378
|
+
import fs12 from "fs";
|
|
3379
|
+
import path20 from "path";
|
|
3249
3380
|
import { minimatch as minimatch3 } from "minimatch";
|
|
3250
3381
|
function applyIgnoreGlobs(files) {
|
|
3251
3382
|
const { complexity } = loadConfig();
|
|
@@ -3254,13 +3385,13 @@ function applyIgnoreGlobs(files) {
|
|
|
3254
3385
|
);
|
|
3255
3386
|
}
|
|
3256
3387
|
function walk(dir, results) {
|
|
3257
|
-
if (!
|
|
3388
|
+
if (!fs12.existsSync(dir)) {
|
|
3258
3389
|
return;
|
|
3259
3390
|
}
|
|
3260
3391
|
const extensions = [".ts", ".tsx"];
|
|
3261
|
-
const entries =
|
|
3392
|
+
const entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
3262
3393
|
for (const entry of entries) {
|
|
3263
|
-
const fullPath =
|
|
3394
|
+
const fullPath = path20.join(dir, entry.name);
|
|
3264
3395
|
if (entry.isDirectory()) {
|
|
3265
3396
|
if (entry.name !== "node_modules" && entry.name !== ".git") {
|
|
3266
3397
|
walk(fullPath, results);
|
|
@@ -3276,10 +3407,10 @@ function findSourceFiles2(pattern2, baseDir = ".") {
|
|
|
3276
3407
|
walk(baseDir, results);
|
|
3277
3408
|
return applyIgnoreGlobs(results.filter((f) => minimatch3(f, pattern2)));
|
|
3278
3409
|
}
|
|
3279
|
-
if (
|
|
3410
|
+
if (fs12.existsSync(pattern2) && fs12.statSync(pattern2).isFile()) {
|
|
3280
3411
|
return [pattern2];
|
|
3281
3412
|
}
|
|
3282
|
-
if (
|
|
3413
|
+
if (fs12.existsSync(pattern2) && fs12.statSync(pattern2).isDirectory()) {
|
|
3283
3414
|
walk(pattern2, results);
|
|
3284
3415
|
return applyIgnoreGlobs(results);
|
|
3285
3416
|
}
|
|
@@ -3474,9 +3605,9 @@ function countSloc(content) {
|
|
|
3474
3605
|
|
|
3475
3606
|
// src/commands/complexity/shared/index.ts
|
|
3476
3607
|
function createSourceFromFile(filePath) {
|
|
3477
|
-
const content =
|
|
3608
|
+
const content = fs13.readFileSync(filePath, "utf-8");
|
|
3478
3609
|
return ts5.createSourceFile(
|
|
3479
|
-
|
|
3610
|
+
path21.basename(filePath),
|
|
3480
3611
|
content,
|
|
3481
3612
|
ts5.ScriptTarget.Latest,
|
|
3482
3613
|
true,
|
|
@@ -3486,7 +3617,7 @@ function createSourceFromFile(filePath) {
|
|
|
3486
3617
|
function withSourceFiles(pattern2, callback) {
|
|
3487
3618
|
const files = findSourceFiles2(pattern2);
|
|
3488
3619
|
if (files.length === 0) {
|
|
3489
|
-
console.log(
|
|
3620
|
+
console.log(chalk33.yellow("No files found matching pattern"));
|
|
3490
3621
|
return void 0;
|
|
3491
3622
|
}
|
|
3492
3623
|
return callback(files);
|
|
@@ -3519,11 +3650,11 @@ async function cyclomatic(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
3519
3650
|
results.sort((a, b) => b.complexity - a.complexity);
|
|
3520
3651
|
for (const { file, name, complexity } of results) {
|
|
3521
3652
|
const exceedsThreshold = options2.threshold !== void 0 && complexity > options2.threshold;
|
|
3522
|
-
const color = exceedsThreshold ?
|
|
3523
|
-
console.log(`${color(`${file}:${name}`)} \u2192 ${
|
|
3653
|
+
const color = exceedsThreshold ? chalk34.red : chalk34.white;
|
|
3654
|
+
console.log(`${color(`${file}:${name}`)} \u2192 ${chalk34.cyan(complexity)}`);
|
|
3524
3655
|
}
|
|
3525
3656
|
console.log(
|
|
3526
|
-
|
|
3657
|
+
chalk34.dim(
|
|
3527
3658
|
`
|
|
3528
3659
|
Analyzed ${results.length} functions across ${files.length} files`
|
|
3529
3660
|
)
|
|
@@ -3535,7 +3666,7 @@ Analyzed ${results.length} functions across ${files.length} files`
|
|
|
3535
3666
|
}
|
|
3536
3667
|
|
|
3537
3668
|
// src/commands/complexity/halstead.ts
|
|
3538
|
-
import
|
|
3669
|
+
import chalk35 from "chalk";
|
|
3539
3670
|
async function halstead(pattern2 = "**/*.ts", options2 = {}) {
|
|
3540
3671
|
withSourceFiles(pattern2, (files) => {
|
|
3541
3672
|
const results = [];
|
|
@@ -3550,13 +3681,13 @@ async function halstead(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
3550
3681
|
results.sort((a, b) => b.metrics.effort - a.metrics.effort);
|
|
3551
3682
|
for (const { file, name, metrics } of results) {
|
|
3552
3683
|
const exceedsThreshold = options2.threshold !== void 0 && metrics.volume > options2.threshold;
|
|
3553
|
-
const color = exceedsThreshold ?
|
|
3684
|
+
const color = exceedsThreshold ? chalk35.red : chalk35.white;
|
|
3554
3685
|
console.log(
|
|
3555
|
-
`${color(`${file}:${name}`)} \u2192 volume: ${
|
|
3686
|
+
`${color(`${file}:${name}`)} \u2192 volume: ${chalk35.cyan(metrics.volume.toFixed(1))}, difficulty: ${chalk35.yellow(metrics.difficulty.toFixed(1))}, effort: ${chalk35.magenta(metrics.effort.toFixed(1))}`
|
|
3556
3687
|
);
|
|
3557
3688
|
}
|
|
3558
3689
|
console.log(
|
|
3559
|
-
|
|
3690
|
+
chalk35.dim(
|
|
3560
3691
|
`
|
|
3561
3692
|
Analyzed ${results.length} functions across ${files.length} files`
|
|
3562
3693
|
)
|
|
@@ -3568,31 +3699,31 @@ Analyzed ${results.length} functions across ${files.length} files`
|
|
|
3568
3699
|
}
|
|
3569
3700
|
|
|
3570
3701
|
// src/commands/complexity/maintainability/index.ts
|
|
3571
|
-
import
|
|
3702
|
+
import fs14 from "fs";
|
|
3572
3703
|
|
|
3573
3704
|
// src/commands/complexity/maintainability/displayMaintainabilityResults.ts
|
|
3574
|
-
import
|
|
3705
|
+
import chalk36 from "chalk";
|
|
3575
3706
|
function displayMaintainabilityResults(results, threshold) {
|
|
3576
3707
|
const filtered = threshold !== void 0 ? results.filter((r) => r.minMaintainability < threshold) : results;
|
|
3577
3708
|
if (threshold !== void 0 && filtered.length === 0) {
|
|
3578
|
-
console.log(
|
|
3709
|
+
console.log(chalk36.green("All files pass maintainability threshold"));
|
|
3579
3710
|
} else {
|
|
3580
3711
|
for (const { file, avgMaintainability, minMaintainability } of filtered) {
|
|
3581
|
-
const color = threshold !== void 0 ?
|
|
3712
|
+
const color = threshold !== void 0 ? chalk36.red : chalk36.white;
|
|
3582
3713
|
console.log(
|
|
3583
|
-
`${color(file)} \u2192 avg: ${
|
|
3714
|
+
`${color(file)} \u2192 avg: ${chalk36.cyan(avgMaintainability.toFixed(1))}, min: ${chalk36.yellow(minMaintainability.toFixed(1))}`
|
|
3584
3715
|
);
|
|
3585
3716
|
}
|
|
3586
3717
|
}
|
|
3587
|
-
console.log(
|
|
3718
|
+
console.log(chalk36.dim(`
|
|
3588
3719
|
Analyzed ${results.length} files`));
|
|
3589
3720
|
if (filtered.length > 0 && threshold !== void 0) {
|
|
3590
3721
|
console.error(
|
|
3591
|
-
|
|
3722
|
+
chalk36.red(
|
|
3592
3723
|
`
|
|
3593
3724
|
Fail: ${filtered.length} file(s) below threshold ${threshold}. Maintainability index (0\u2013100) is derived from Halstead volume, cyclomatic complexity, and lines of code.
|
|
3594
3725
|
|
|
3595
|
-
\u26A0\uFE0F ${
|
|
3726
|
+
\u26A0\uFE0F ${chalk36.bold("Diagnose and fix one file at a time")} \u2014 do not investigate or fix multiple files in parallel. Run 'assist complexity <file>' to see all metrics. For larger files, start by extracting responsibilities into smaller files.`
|
|
3596
3727
|
)
|
|
3597
3728
|
);
|
|
3598
3729
|
process.exit(1);
|
|
@@ -3610,7 +3741,7 @@ function calculateMaintainabilityIndex(halsteadVolume, cyclomaticComplexity, slo
|
|
|
3610
3741
|
function collectFileMetrics(files) {
|
|
3611
3742
|
const fileMetrics = /* @__PURE__ */ new Map();
|
|
3612
3743
|
for (const file of files) {
|
|
3613
|
-
const content =
|
|
3744
|
+
const content = fs14.readFileSync(file, "utf-8");
|
|
3614
3745
|
fileMetrics.set(file, { sloc: countSloc(content), functions: [] });
|
|
3615
3746
|
}
|
|
3616
3747
|
forEachFunction(files, (file, _name, node) => {
|
|
@@ -3648,14 +3779,14 @@ async function maintainability(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
3648
3779
|
}
|
|
3649
3780
|
|
|
3650
3781
|
// src/commands/complexity/sloc.ts
|
|
3651
|
-
import
|
|
3652
|
-
import
|
|
3782
|
+
import fs15 from "fs";
|
|
3783
|
+
import chalk37 from "chalk";
|
|
3653
3784
|
async function sloc(pattern2 = "**/*.ts", options2 = {}) {
|
|
3654
3785
|
withSourceFiles(pattern2, (files) => {
|
|
3655
3786
|
const results = [];
|
|
3656
3787
|
let hasViolation = false;
|
|
3657
3788
|
for (const file of files) {
|
|
3658
|
-
const content =
|
|
3789
|
+
const content = fs15.readFileSync(file, "utf-8");
|
|
3659
3790
|
const lines = countSloc(content);
|
|
3660
3791
|
results.push({ file, lines });
|
|
3661
3792
|
if (options2.threshold !== void 0 && lines > options2.threshold) {
|
|
@@ -3665,12 +3796,12 @@ async function sloc(pattern2 = "**/*.ts", options2 = {}) {
|
|
|
3665
3796
|
results.sort((a, b) => b.lines - a.lines);
|
|
3666
3797
|
for (const { file, lines } of results) {
|
|
3667
3798
|
const exceedsThreshold = options2.threshold !== void 0 && lines > options2.threshold;
|
|
3668
|
-
const color = exceedsThreshold ?
|
|
3669
|
-
console.log(`${color(file)} \u2192 ${
|
|
3799
|
+
const color = exceedsThreshold ? chalk37.red : chalk37.white;
|
|
3800
|
+
console.log(`${color(file)} \u2192 ${chalk37.cyan(lines)} lines`);
|
|
3670
3801
|
}
|
|
3671
3802
|
const total = results.reduce((sum, r) => sum + r.lines, 0);
|
|
3672
3803
|
console.log(
|
|
3673
|
-
|
|
3804
|
+
chalk37.dim(`
|
|
3674
3805
|
Total: ${total} lines across ${files.length} files`)
|
|
3675
3806
|
);
|
|
3676
3807
|
if (hasViolation) {
|
|
@@ -3684,21 +3815,21 @@ async function analyze(pattern2) {
|
|
|
3684
3815
|
const searchPattern = pattern2.includes("*") || pattern2.includes("/") ? pattern2 : `**/${pattern2}`;
|
|
3685
3816
|
const files = findSourceFiles2(searchPattern);
|
|
3686
3817
|
if (files.length === 0) {
|
|
3687
|
-
console.log(
|
|
3818
|
+
console.log(chalk38.yellow("No files found matching pattern"));
|
|
3688
3819
|
return;
|
|
3689
3820
|
}
|
|
3690
3821
|
if (files.length === 1) {
|
|
3691
3822
|
const file = files[0];
|
|
3692
|
-
console.log(
|
|
3823
|
+
console.log(chalk38.bold.underline("SLOC"));
|
|
3693
3824
|
await sloc(file);
|
|
3694
3825
|
console.log();
|
|
3695
|
-
console.log(
|
|
3826
|
+
console.log(chalk38.bold.underline("Cyclomatic Complexity"));
|
|
3696
3827
|
await cyclomatic(file);
|
|
3697
3828
|
console.log();
|
|
3698
|
-
console.log(
|
|
3829
|
+
console.log(chalk38.bold.underline("Halstead Metrics"));
|
|
3699
3830
|
await halstead(file);
|
|
3700
3831
|
console.log();
|
|
3701
|
-
console.log(
|
|
3832
|
+
console.log(chalk38.bold.underline("Maintainability Index"));
|
|
3702
3833
|
await maintainability(file);
|
|
3703
3834
|
return;
|
|
3704
3835
|
}
|
|
@@ -3726,7 +3857,7 @@ function registerComplexity(program2) {
|
|
|
3726
3857
|
|
|
3727
3858
|
// src/commands/deploy/redirect.ts
|
|
3728
3859
|
import { existsSync as existsSync19, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
3729
|
-
import
|
|
3860
|
+
import chalk39 from "chalk";
|
|
3730
3861
|
var TRAILING_SLASH_SCRIPT = ` <script>
|
|
3731
3862
|
if (!window.location.pathname.endsWith('/')) {
|
|
3732
3863
|
window.location.href = \`\${window.location.pathname}/\${window.location.search}\${window.location.hash}\`;
|
|
@@ -3735,22 +3866,22 @@ var TRAILING_SLASH_SCRIPT = ` <script>
|
|
|
3735
3866
|
function redirect() {
|
|
3736
3867
|
const indexPath = "index.html";
|
|
3737
3868
|
if (!existsSync19(indexPath)) {
|
|
3738
|
-
console.log(
|
|
3869
|
+
console.log(chalk39.yellow("No index.html found"));
|
|
3739
3870
|
return;
|
|
3740
3871
|
}
|
|
3741
3872
|
const content = readFileSync16(indexPath, "utf-8");
|
|
3742
3873
|
if (content.includes("window.location.pathname.endsWith('/')")) {
|
|
3743
|
-
console.log(
|
|
3874
|
+
console.log(chalk39.dim("Trailing slash script already present"));
|
|
3744
3875
|
return;
|
|
3745
3876
|
}
|
|
3746
3877
|
const headCloseIndex = content.indexOf("</head>");
|
|
3747
3878
|
if (headCloseIndex === -1) {
|
|
3748
|
-
console.log(
|
|
3879
|
+
console.log(chalk39.red("Could not find </head> tag in index.html"));
|
|
3749
3880
|
return;
|
|
3750
3881
|
}
|
|
3751
3882
|
const newContent = content.slice(0, headCloseIndex) + TRAILING_SLASH_SCRIPT + "\n " + content.slice(headCloseIndex);
|
|
3752
3883
|
writeFileSync14(indexPath, newContent);
|
|
3753
|
-
console.log(
|
|
3884
|
+
console.log(chalk39.green("Added trailing slash redirect to index.html"));
|
|
3754
3885
|
}
|
|
3755
3886
|
|
|
3756
3887
|
// src/commands/registerDeploy.ts
|
|
@@ -3777,7 +3908,7 @@ function loadBlogSkipDays(repoName) {
|
|
|
3777
3908
|
|
|
3778
3909
|
// src/commands/devlog/shared.ts
|
|
3779
3910
|
import { execSync as execSync15 } from "child_process";
|
|
3780
|
-
import
|
|
3911
|
+
import chalk40 from "chalk";
|
|
3781
3912
|
|
|
3782
3913
|
// src/commands/devlog/loadDevlogEntries.ts
|
|
3783
3914
|
import { readdirSync, readFileSync as readFileSync17 } from "fs";
|
|
@@ -3864,13 +3995,13 @@ function shouldIgnoreCommit(files, ignorePaths) {
|
|
|
3864
3995
|
}
|
|
3865
3996
|
function printCommitsWithFiles(commits, ignore2, verbose) {
|
|
3866
3997
|
for (const commit2 of commits) {
|
|
3867
|
-
console.log(` ${
|
|
3998
|
+
console.log(` ${chalk40.yellow(commit2.hash)} ${commit2.message}`);
|
|
3868
3999
|
if (verbose) {
|
|
3869
4000
|
const visibleFiles = commit2.files.filter(
|
|
3870
4001
|
(file) => !ignore2.some((p) => file.startsWith(p))
|
|
3871
4002
|
);
|
|
3872
4003
|
for (const file of visibleFiles) {
|
|
3873
|
-
console.log(` ${
|
|
4004
|
+
console.log(` ${chalk40.dim(file)}`);
|
|
3874
4005
|
}
|
|
3875
4006
|
}
|
|
3876
4007
|
}
|
|
@@ -3895,15 +4026,15 @@ function parseGitLogCommits(output, ignore2, afterDate) {
|
|
|
3895
4026
|
}
|
|
3896
4027
|
|
|
3897
4028
|
// src/commands/devlog/list/printDateHeader.ts
|
|
3898
|
-
import
|
|
4029
|
+
import chalk41 from "chalk";
|
|
3899
4030
|
function printDateHeader(date, isSkipped, entries) {
|
|
3900
4031
|
if (isSkipped) {
|
|
3901
|
-
console.log(`${
|
|
4032
|
+
console.log(`${chalk41.bold.blue(date)} ${chalk41.dim("skipped")}`);
|
|
3902
4033
|
} else if (entries && entries.length > 0) {
|
|
3903
|
-
const entryInfo = entries.map((e) => `${
|
|
3904
|
-
console.log(`${
|
|
4034
|
+
const entryInfo = entries.map((e) => `${chalk41.green(e.version)} ${e.title}`).join(" | ");
|
|
4035
|
+
console.log(`${chalk41.bold.blue(date)} ${entryInfo}`);
|
|
3905
4036
|
} else {
|
|
3906
|
-
console.log(`${
|
|
4037
|
+
console.log(`${chalk41.bold.blue(date)} ${chalk41.red("\u26A0 devlog missing")}`);
|
|
3907
4038
|
}
|
|
3908
4039
|
}
|
|
3909
4040
|
|
|
@@ -4006,24 +4137,24 @@ function bumpVersion(version2, type) {
|
|
|
4006
4137
|
|
|
4007
4138
|
// src/commands/devlog/next/displayNextEntry/index.ts
|
|
4008
4139
|
import { execSync as execSync18 } from "child_process";
|
|
4009
|
-
import
|
|
4140
|
+
import chalk43 from "chalk";
|
|
4010
4141
|
|
|
4011
4142
|
// src/commands/devlog/next/displayNextEntry/displayVersion.ts
|
|
4012
|
-
import
|
|
4143
|
+
import chalk42 from "chalk";
|
|
4013
4144
|
function displayVersion(conventional, firstHash, patchVersion, minorVersion) {
|
|
4014
4145
|
if (conventional && firstHash) {
|
|
4015
4146
|
const version2 = getVersionAtCommit(firstHash);
|
|
4016
4147
|
if (version2) {
|
|
4017
|
-
console.log(`${
|
|
4148
|
+
console.log(`${chalk42.bold("version:")} ${stripToMinor(version2)}`);
|
|
4018
4149
|
} else {
|
|
4019
|
-
console.log(`${
|
|
4150
|
+
console.log(`${chalk42.bold("version:")} ${chalk42.red("unknown")}`);
|
|
4020
4151
|
}
|
|
4021
4152
|
} else if (patchVersion && minorVersion) {
|
|
4022
4153
|
console.log(
|
|
4023
|
-
`${
|
|
4154
|
+
`${chalk42.bold("version:")} ${patchVersion} (patch) or ${minorVersion} (minor)`
|
|
4024
4155
|
);
|
|
4025
4156
|
} else {
|
|
4026
|
-
console.log(`${
|
|
4157
|
+
console.log(`${chalk42.bold("version:")} v0.1 (initial)`);
|
|
4027
4158
|
}
|
|
4028
4159
|
}
|
|
4029
4160
|
|
|
@@ -4070,16 +4201,16 @@ function noCommitsMessage(hasLastInfo) {
|
|
|
4070
4201
|
return hasLastInfo ? "No commits after last versioned entry" : "No commits found";
|
|
4071
4202
|
}
|
|
4072
4203
|
function logName(repoName) {
|
|
4073
|
-
console.log(`${
|
|
4204
|
+
console.log(`${chalk43.bold("name:")} ${repoName}`);
|
|
4074
4205
|
}
|
|
4075
4206
|
function displayNextEntry(ctx, targetDate, commits) {
|
|
4076
4207
|
logName(ctx.repoName);
|
|
4077
4208
|
printVersionInfo(ctx.config, ctx.lastInfo, commits[0]?.hash);
|
|
4078
|
-
console.log(
|
|
4209
|
+
console.log(chalk43.bold.blue(targetDate));
|
|
4079
4210
|
printCommitsWithFiles(commits, ctx.ignore, ctx.verbose);
|
|
4080
4211
|
}
|
|
4081
4212
|
function logNoCommits(lastInfo) {
|
|
4082
|
-
console.log(
|
|
4213
|
+
console.log(chalk43.dim(noCommitsMessage(!!lastInfo)));
|
|
4083
4214
|
}
|
|
4084
4215
|
|
|
4085
4216
|
// src/commands/devlog/next/index.ts
|
|
@@ -4120,11 +4251,11 @@ function next(options2) {
|
|
|
4120
4251
|
import { execSync as execSync19 } from "child_process";
|
|
4121
4252
|
|
|
4122
4253
|
// src/commands/devlog/repos/printReposTable.ts
|
|
4123
|
-
import
|
|
4254
|
+
import chalk44 from "chalk";
|
|
4124
4255
|
function colorStatus(status2) {
|
|
4125
|
-
if (status2 === "missing") return
|
|
4126
|
-
if (status2 === "outdated") return
|
|
4127
|
-
return
|
|
4256
|
+
if (status2 === "missing") return chalk44.red(status2);
|
|
4257
|
+
if (status2 === "outdated") return chalk44.yellow(status2);
|
|
4258
|
+
return chalk44.green(status2);
|
|
4128
4259
|
}
|
|
4129
4260
|
function formatRow(row, nameWidth) {
|
|
4130
4261
|
const devlog = (row.lastDevlog ?? "-").padEnd(11);
|
|
@@ -4138,8 +4269,8 @@ function printReposTable(rows) {
|
|
|
4138
4269
|
"Last Devlog".padEnd(11),
|
|
4139
4270
|
"Status"
|
|
4140
4271
|
].join(" ");
|
|
4141
|
-
console.log(
|
|
4142
|
-
console.log(
|
|
4272
|
+
console.log(chalk44.dim(header));
|
|
4273
|
+
console.log(chalk44.dim("-".repeat(header.length)));
|
|
4143
4274
|
for (const row of rows) {
|
|
4144
4275
|
console.log(formatRow(row, nameWidth));
|
|
4145
4276
|
}
|
|
@@ -4197,14 +4328,14 @@ function repos(options2) {
|
|
|
4197
4328
|
// src/commands/devlog/skip.ts
|
|
4198
4329
|
import { writeFileSync as writeFileSync15 } from "fs";
|
|
4199
4330
|
import { join as join15 } from "path";
|
|
4200
|
-
import
|
|
4331
|
+
import chalk45 from "chalk";
|
|
4201
4332
|
import { stringify as stringifyYaml4 } from "yaml";
|
|
4202
4333
|
function getBlogConfigPath() {
|
|
4203
4334
|
return join15(BLOG_REPO_ROOT, "assist.yml");
|
|
4204
4335
|
}
|
|
4205
4336
|
function skip(date) {
|
|
4206
4337
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
4207
|
-
console.log(
|
|
4338
|
+
console.log(chalk45.red("Invalid date format. Use YYYY-MM-DD"));
|
|
4208
4339
|
process.exit(1);
|
|
4209
4340
|
}
|
|
4210
4341
|
const repoName = getRepoName();
|
|
@@ -4215,7 +4346,7 @@ function skip(date) {
|
|
|
4215
4346
|
const skipDays = skip2[repoName] ?? [];
|
|
4216
4347
|
if (skipDays.includes(date)) {
|
|
4217
4348
|
console.log(
|
|
4218
|
-
|
|
4349
|
+
chalk45.yellow(`${date} is already in skip list for ${repoName}`)
|
|
4219
4350
|
);
|
|
4220
4351
|
return;
|
|
4221
4352
|
}
|
|
@@ -4225,20 +4356,20 @@ function skip(date) {
|
|
|
4225
4356
|
devlog.skip = skip2;
|
|
4226
4357
|
config.devlog = devlog;
|
|
4227
4358
|
writeFileSync15(configPath, stringifyYaml4(config, { lineWidth: 0 }));
|
|
4228
|
-
console.log(
|
|
4359
|
+
console.log(chalk45.green(`Added ${date} to skip list for ${repoName}`));
|
|
4229
4360
|
}
|
|
4230
4361
|
|
|
4231
4362
|
// src/commands/devlog/version.ts
|
|
4232
|
-
import
|
|
4363
|
+
import chalk46 from "chalk";
|
|
4233
4364
|
function version() {
|
|
4234
4365
|
const config = loadConfig();
|
|
4235
4366
|
const name = getRepoName();
|
|
4236
4367
|
const lastInfo = getLastVersionInfo(name, config);
|
|
4237
4368
|
const lastVersion = lastInfo?.version ?? null;
|
|
4238
4369
|
const nextVersion = lastVersion ? bumpVersion(lastVersion, "patch") : null;
|
|
4239
|
-
console.log(`${
|
|
4240
|
-
console.log(`${
|
|
4241
|
-
console.log(`${
|
|
4370
|
+
console.log(`${chalk46.bold("name:")} ${name}`);
|
|
4371
|
+
console.log(`${chalk46.bold("last:")} ${lastVersion ?? chalk46.dim("none")}`);
|
|
4372
|
+
console.log(`${chalk46.bold("next:")} ${nextVersion ?? chalk46.dim("none")}`);
|
|
4242
4373
|
}
|
|
4243
4374
|
|
|
4244
4375
|
// src/commands/registerDevlog.ts
|
|
@@ -4261,7 +4392,7 @@ function registerDevlog(program2) {
|
|
|
4261
4392
|
|
|
4262
4393
|
// src/commands/jira/acceptanceCriteria.ts
|
|
4263
4394
|
import { execSync as execSync20 } from "child_process";
|
|
4264
|
-
import
|
|
4395
|
+
import chalk47 from "chalk";
|
|
4265
4396
|
|
|
4266
4397
|
// src/commands/jira/adfToText.ts
|
|
4267
4398
|
function renderInline(node) {
|
|
@@ -4336,21 +4467,21 @@ function acceptanceCriteria(issueKey) {
|
|
|
4336
4467
|
const stderr = error.stderr;
|
|
4337
4468
|
if (stderr.includes("unauthorized")) {
|
|
4338
4469
|
console.error(
|
|
4339
|
-
|
|
4470
|
+
chalk47.red("Jira authentication expired."),
|
|
4340
4471
|
"Run",
|
|
4341
|
-
|
|
4472
|
+
chalk47.cyan("assist jira auth"),
|
|
4342
4473
|
"to re-authenticate."
|
|
4343
4474
|
);
|
|
4344
4475
|
process.exit(1);
|
|
4345
4476
|
}
|
|
4346
4477
|
}
|
|
4347
|
-
console.error(
|
|
4478
|
+
console.error(chalk47.red(`Failed to fetch ${issueKey}.`));
|
|
4348
4479
|
process.exit(1);
|
|
4349
4480
|
}
|
|
4350
4481
|
const parsed = JSON.parse(result);
|
|
4351
4482
|
const acValue = parsed?.fields?.[field];
|
|
4352
4483
|
if (!acValue) {
|
|
4353
|
-
console.log(
|
|
4484
|
+
console.log(chalk47.yellow(`No acceptance criteria found on ${issueKey}.`));
|
|
4354
4485
|
return;
|
|
4355
4486
|
}
|
|
4356
4487
|
if (typeof acValue === "string") {
|
|
@@ -4379,10 +4510,10 @@ function getStorePath(filename) {
|
|
|
4379
4510
|
return join16(getStoreDir(), filename);
|
|
4380
4511
|
}
|
|
4381
4512
|
function loadJson(filename) {
|
|
4382
|
-
const
|
|
4383
|
-
if (existsSync20(
|
|
4513
|
+
const path43 = getStorePath(filename);
|
|
4514
|
+
if (existsSync20(path43)) {
|
|
4384
4515
|
try {
|
|
4385
|
-
return JSON.parse(readFileSync18(
|
|
4516
|
+
return JSON.parse(readFileSync18(path43, "utf-8"));
|
|
4386
4517
|
} catch {
|
|
4387
4518
|
return {};
|
|
4388
4519
|
}
|
|
@@ -4449,7 +4580,7 @@ function registerJira(program2) {
|
|
|
4449
4580
|
|
|
4450
4581
|
// src/commands/netframework/buildTree.ts
|
|
4451
4582
|
import { readFileSync as readFileSync19 } from "fs";
|
|
4452
|
-
import
|
|
4583
|
+
import path22 from "path";
|
|
4453
4584
|
var PROJECT_REF_RE = /<ProjectReference\s+Include="([^"]+)"/g;
|
|
4454
4585
|
function getProjectRefs(csprojPath) {
|
|
4455
4586
|
const content = readFileSync19(csprojPath, "utf-8");
|
|
@@ -4460,14 +4591,14 @@ function getProjectRefs(csprojPath) {
|
|
|
4460
4591
|
return refs;
|
|
4461
4592
|
}
|
|
4462
4593
|
function buildTree(csprojPath, repoRoot, visited = /* @__PURE__ */ new Set()) {
|
|
4463
|
-
const abs =
|
|
4464
|
-
const rel =
|
|
4594
|
+
const abs = path22.resolve(csprojPath);
|
|
4595
|
+
const rel = path22.relative(repoRoot, abs);
|
|
4465
4596
|
const node = { path: abs, relativePath: rel, children: [] };
|
|
4466
4597
|
if (visited.has(abs)) return node;
|
|
4467
4598
|
visited.add(abs);
|
|
4468
|
-
const dir =
|
|
4599
|
+
const dir = path22.dirname(abs);
|
|
4469
4600
|
for (const ref of getProjectRefs(abs)) {
|
|
4470
|
-
const childAbs =
|
|
4601
|
+
const childAbs = path22.resolve(dir, ref);
|
|
4471
4602
|
try {
|
|
4472
4603
|
readFileSync19(childAbs);
|
|
4473
4604
|
node.children.push(buildTree(childAbs, repoRoot, visited));
|
|
@@ -4495,7 +4626,7 @@ function collectAllDeps(node) {
|
|
|
4495
4626
|
|
|
4496
4627
|
// src/commands/netframework/findContainingSolutions.ts
|
|
4497
4628
|
import { readdirSync as readdirSync2, readFileSync as readFileSync20, statSync } from "fs";
|
|
4498
|
-
import
|
|
4629
|
+
import path23 from "path";
|
|
4499
4630
|
function findSlnFiles(dir, maxDepth, depth = 0) {
|
|
4500
4631
|
if (depth > maxDepth) return [];
|
|
4501
4632
|
const results = [];
|
|
@@ -4508,7 +4639,7 @@ function findSlnFiles(dir, maxDepth, depth = 0) {
|
|
|
4508
4639
|
for (const entry of entries) {
|
|
4509
4640
|
if (entry.startsWith(".") || entry === "node_modules" || entry === "packages")
|
|
4510
4641
|
continue;
|
|
4511
|
-
const full =
|
|
4642
|
+
const full = path23.join(dir, entry);
|
|
4512
4643
|
try {
|
|
4513
4644
|
const stat = statSync(full);
|
|
4514
4645
|
if (stat.isFile() && entry.endsWith(".sln")) {
|
|
@@ -4522,8 +4653,8 @@ function findSlnFiles(dir, maxDepth, depth = 0) {
|
|
|
4522
4653
|
return results;
|
|
4523
4654
|
}
|
|
4524
4655
|
function findContainingSolutions(csprojPath, repoRoot) {
|
|
4525
|
-
const csprojAbs =
|
|
4526
|
-
const csprojBasename =
|
|
4656
|
+
const csprojAbs = path23.resolve(csprojPath);
|
|
4657
|
+
const csprojBasename = path23.basename(csprojAbs);
|
|
4527
4658
|
const slnFiles = findSlnFiles(repoRoot, 3);
|
|
4528
4659
|
const matches = [];
|
|
4529
4660
|
const pattern2 = new RegExp(`[\\\\"/]${escapeRegex(csprojBasename)}"`);
|
|
@@ -4531,7 +4662,7 @@ function findContainingSolutions(csprojPath, repoRoot) {
|
|
|
4531
4662
|
try {
|
|
4532
4663
|
const content = readFileSync20(sln, "utf-8");
|
|
4533
4664
|
if (pattern2.test(content)) {
|
|
4534
|
-
matches.push(
|
|
4665
|
+
matches.push(path23.relative(repoRoot, sln));
|
|
4535
4666
|
}
|
|
4536
4667
|
} catch {
|
|
4537
4668
|
}
|
|
@@ -4543,30 +4674,30 @@ function escapeRegex(s) {
|
|
|
4543
4674
|
}
|
|
4544
4675
|
|
|
4545
4676
|
// src/commands/netframework/printTree.ts
|
|
4546
|
-
import
|
|
4677
|
+
import chalk48 from "chalk";
|
|
4547
4678
|
function printNodes(nodes, prefix2) {
|
|
4548
4679
|
for (let i = 0; i < nodes.length; i++) {
|
|
4549
4680
|
const isLast = i === nodes.length - 1;
|
|
4550
4681
|
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
4551
4682
|
const childPrefix = isLast ? " " : "\u2502 ";
|
|
4552
4683
|
const isMissing = nodes[i].relativePath.startsWith("[MISSING]");
|
|
4553
|
-
const label2 = isMissing ?
|
|
4684
|
+
const label2 = isMissing ? chalk48.red(nodes[i].relativePath) : nodes[i].relativePath;
|
|
4554
4685
|
console.log(`${prefix2}${connector}${label2}`);
|
|
4555
4686
|
printNodes(nodes[i].children, prefix2 + childPrefix);
|
|
4556
4687
|
}
|
|
4557
4688
|
}
|
|
4558
4689
|
function printTree(tree, totalCount, solutions) {
|
|
4559
|
-
console.log(
|
|
4560
|
-
console.log(
|
|
4690
|
+
console.log(chalk48.bold("\nProject Dependency Tree"));
|
|
4691
|
+
console.log(chalk48.cyan(tree.relativePath));
|
|
4561
4692
|
printNodes(tree.children, "");
|
|
4562
|
-
console.log(
|
|
4693
|
+
console.log(chalk48.dim(`
|
|
4563
4694
|
${totalCount} projects total (including root)`));
|
|
4564
|
-
console.log(
|
|
4695
|
+
console.log(chalk48.bold("\nSolution Membership"));
|
|
4565
4696
|
if (solutions.length === 0) {
|
|
4566
|
-
console.log(
|
|
4697
|
+
console.log(chalk48.yellow(" Not found in any .sln"));
|
|
4567
4698
|
} else {
|
|
4568
4699
|
for (const sln of solutions) {
|
|
4569
|
-
console.log(` ${
|
|
4700
|
+
console.log(` ${chalk48.green(sln)}`);
|
|
4570
4701
|
}
|
|
4571
4702
|
}
|
|
4572
4703
|
console.log();
|
|
@@ -4594,33 +4725,33 @@ function printJson(tree, totalCount, solutions) {
|
|
|
4594
4725
|
|
|
4595
4726
|
// src/commands/netframework/resolveCsproj.ts
|
|
4596
4727
|
import { existsSync as existsSync22 } from "fs";
|
|
4597
|
-
import
|
|
4598
|
-
import
|
|
4728
|
+
import path25 from "path";
|
|
4729
|
+
import chalk49 from "chalk";
|
|
4599
4730
|
|
|
4600
4731
|
// src/commands/netframework/findRepoRoot.ts
|
|
4601
4732
|
import { existsSync as existsSync21 } from "fs";
|
|
4602
|
-
import
|
|
4733
|
+
import path24 from "path";
|
|
4603
4734
|
function findRepoRoot(dir) {
|
|
4604
4735
|
let current = dir;
|
|
4605
|
-
while (current !==
|
|
4606
|
-
if (existsSync21(
|
|
4736
|
+
while (current !== path24.dirname(current)) {
|
|
4737
|
+
if (existsSync21(path24.join(current, ".git"))) {
|
|
4607
4738
|
return current;
|
|
4608
4739
|
}
|
|
4609
|
-
current =
|
|
4740
|
+
current = path24.dirname(current);
|
|
4610
4741
|
}
|
|
4611
4742
|
return null;
|
|
4612
4743
|
}
|
|
4613
4744
|
|
|
4614
4745
|
// src/commands/netframework/resolveCsproj.ts
|
|
4615
4746
|
function resolveCsproj(csprojPath) {
|
|
4616
|
-
const resolved =
|
|
4747
|
+
const resolved = path25.resolve(csprojPath);
|
|
4617
4748
|
if (!existsSync22(resolved)) {
|
|
4618
|
-
console.error(
|
|
4749
|
+
console.error(chalk49.red(`File not found: ${resolved}`));
|
|
4619
4750
|
process.exit(1);
|
|
4620
4751
|
}
|
|
4621
|
-
const repoRoot = findRepoRoot(
|
|
4752
|
+
const repoRoot = findRepoRoot(path25.dirname(resolved));
|
|
4622
4753
|
if (!repoRoot) {
|
|
4623
|
-
console.error(
|
|
4754
|
+
console.error(chalk49.red("Could not find git repository root"));
|
|
4624
4755
|
process.exit(1);
|
|
4625
4756
|
}
|
|
4626
4757
|
return { resolved, repoRoot };
|
|
@@ -4640,12 +4771,12 @@ async function deps(csprojPath, options2) {
|
|
|
4640
4771
|
}
|
|
4641
4772
|
|
|
4642
4773
|
// src/commands/netframework/inSln.ts
|
|
4643
|
-
import
|
|
4774
|
+
import chalk50 from "chalk";
|
|
4644
4775
|
async function inSln(csprojPath) {
|
|
4645
4776
|
const { resolved, repoRoot } = resolveCsproj(csprojPath);
|
|
4646
4777
|
const solutions = findContainingSolutions(resolved, repoRoot);
|
|
4647
4778
|
if (solutions.length === 0) {
|
|
4648
|
-
console.log(
|
|
4779
|
+
console.log(chalk50.yellow("Not found in any .sln file"));
|
|
4649
4780
|
process.exit(1);
|
|
4650
4781
|
}
|
|
4651
4782
|
for (const sln of solutions) {
|
|
@@ -4661,7 +4792,7 @@ function registerNetframework(program2) {
|
|
|
4661
4792
|
}
|
|
4662
4793
|
|
|
4663
4794
|
// src/commands/news/add/index.ts
|
|
4664
|
-
import
|
|
4795
|
+
import chalk51 from "chalk";
|
|
4665
4796
|
import enquirer5 from "enquirer";
|
|
4666
4797
|
async function add2(url) {
|
|
4667
4798
|
if (!url) {
|
|
@@ -4684,17 +4815,17 @@ async function add2(url) {
|
|
|
4684
4815
|
const news = config.news ?? {};
|
|
4685
4816
|
const feeds = news.feeds ?? [];
|
|
4686
4817
|
if (feeds.includes(url)) {
|
|
4687
|
-
console.log(
|
|
4818
|
+
console.log(chalk51.yellow("Feed already exists in config"));
|
|
4688
4819
|
return;
|
|
4689
4820
|
}
|
|
4690
4821
|
feeds.push(url);
|
|
4691
4822
|
config.news = { ...news, feeds };
|
|
4692
4823
|
saveGlobalConfig(config);
|
|
4693
|
-
console.log(
|
|
4824
|
+
console.log(chalk51.green(`Added feed: ${url}`));
|
|
4694
4825
|
}
|
|
4695
4826
|
|
|
4696
4827
|
// src/commands/news/web/handleRequest.ts
|
|
4697
|
-
import
|
|
4828
|
+
import chalk52 from "chalk";
|
|
4698
4829
|
|
|
4699
4830
|
// src/commands/news/web/shared.ts
|
|
4700
4831
|
import { decodeHTML } from "entities";
|
|
@@ -4830,17 +4961,17 @@ function prefetch() {
|
|
|
4830
4961
|
const config = loadConfig();
|
|
4831
4962
|
const total = config.news.feeds.length;
|
|
4832
4963
|
if (total === 0) return;
|
|
4833
|
-
process.stdout.write(
|
|
4964
|
+
process.stdout.write(chalk52.dim(`Fetching ${total} feed(s)\u2026 `));
|
|
4834
4965
|
prefetchPromise = fetchFeeds(config.news.feeds, (done2, t) => {
|
|
4835
4966
|
const width = 20;
|
|
4836
4967
|
const filled = Math.round(done2 / t * width);
|
|
4837
4968
|
const bar = `${"\u2588".repeat(filled)}${"\u2591".repeat(width - filled)}`;
|
|
4838
4969
|
process.stdout.write(
|
|
4839
|
-
`\r${
|
|
4970
|
+
`\r${chalk52.dim(`Fetching feeds ${bar} ${done2}/${t}`)}`
|
|
4840
4971
|
);
|
|
4841
4972
|
}).then((items) => {
|
|
4842
4973
|
process.stdout.write(
|
|
4843
|
-
`\r${
|
|
4974
|
+
`\r${chalk52.green(`Fetched ${items.length} items from ${total} feed(s)`)}
|
|
4844
4975
|
`
|
|
4845
4976
|
);
|
|
4846
4977
|
cachedItems = items;
|
|
@@ -4955,7 +5086,7 @@ function validateLine(line) {
|
|
|
4955
5086
|
process.exit(1);
|
|
4956
5087
|
}
|
|
4957
5088
|
}
|
|
4958
|
-
function comment(
|
|
5089
|
+
function comment(path43, line, body) {
|
|
4959
5090
|
validateBody(body);
|
|
4960
5091
|
validateLine(line);
|
|
4961
5092
|
try {
|
|
@@ -4975,7 +5106,7 @@ function comment(path36, line, body) {
|
|
|
4975
5106
|
"-f",
|
|
4976
5107
|
`body=${body}`,
|
|
4977
5108
|
"-f",
|
|
4978
|
-
`path=${
|
|
5109
|
+
`path=${path43}`,
|
|
4979
5110
|
"-F",
|
|
4980
5111
|
`line=${line}`
|
|
4981
5112
|
],
|
|
@@ -4984,7 +5115,7 @@ function comment(path36, line, body) {
|
|
|
4984
5115
|
if (result.status !== 0) {
|
|
4985
5116
|
throw new Error(result.stderr || result.stdout);
|
|
4986
5117
|
}
|
|
4987
|
-
console.log(`Added review comment on ${
|
|
5118
|
+
console.log(`Added review comment on ${path43}:${line}`);
|
|
4988
5119
|
} finally {
|
|
4989
5120
|
unlinkSync3(queryFile);
|
|
4990
5121
|
}
|
|
@@ -5201,20 +5332,20 @@ function fetchLineComments(org, repo, prNumber, threadInfo) {
|
|
|
5201
5332
|
}
|
|
5202
5333
|
|
|
5203
5334
|
// src/commands/prs/listComments/printComments.ts
|
|
5204
|
-
import
|
|
5335
|
+
import chalk53 from "chalk";
|
|
5205
5336
|
function formatForHuman(comment2) {
|
|
5206
5337
|
if (comment2.type === "review") {
|
|
5207
|
-
const stateColor = comment2.state === "APPROVED" ?
|
|
5338
|
+
const stateColor = comment2.state === "APPROVED" ? chalk53.green : comment2.state === "CHANGES_REQUESTED" ? chalk53.red : chalk53.yellow;
|
|
5208
5339
|
return [
|
|
5209
|
-
`${
|
|
5340
|
+
`${chalk53.cyan("Review")} by ${chalk53.bold(comment2.user)} ${stateColor(`[${comment2.state}]`)}`,
|
|
5210
5341
|
comment2.body,
|
|
5211
5342
|
""
|
|
5212
5343
|
].join("\n");
|
|
5213
5344
|
}
|
|
5214
5345
|
const location = comment2.line ? `:${comment2.line}` : "";
|
|
5215
5346
|
return [
|
|
5216
|
-
`${
|
|
5217
|
-
|
|
5347
|
+
`${chalk53.cyan("Line comment")} by ${chalk53.bold(comment2.user)} on ${chalk53.dim(`${comment2.path}${location}`)}`,
|
|
5348
|
+
chalk53.dim(comment2.diff_hunk.split("\n").slice(-3).join("\n")),
|
|
5218
5349
|
comment2.body,
|
|
5219
5350
|
""
|
|
5220
5351
|
].join("\n");
|
|
@@ -5304,13 +5435,13 @@ import { execSync as execSync27 } from "child_process";
|
|
|
5304
5435
|
import enquirer6 from "enquirer";
|
|
5305
5436
|
|
|
5306
5437
|
// src/commands/prs/prs/displayPaginated/printPr.ts
|
|
5307
|
-
import
|
|
5438
|
+
import chalk54 from "chalk";
|
|
5308
5439
|
var STATUS_MAP = {
|
|
5309
|
-
MERGED: (pr) => pr.mergedAt ? { label:
|
|
5310
|
-
CLOSED: (pr) => pr.closedAt ? { label:
|
|
5440
|
+
MERGED: (pr) => pr.mergedAt ? { label: chalk54.magenta("merged"), date: pr.mergedAt } : null,
|
|
5441
|
+
CLOSED: (pr) => pr.closedAt ? { label: chalk54.red("closed"), date: pr.closedAt } : null
|
|
5311
5442
|
};
|
|
5312
5443
|
function defaultStatus(pr) {
|
|
5313
|
-
return { label:
|
|
5444
|
+
return { label: chalk54.green("opened"), date: pr.createdAt };
|
|
5314
5445
|
}
|
|
5315
5446
|
function getStatus2(pr) {
|
|
5316
5447
|
return STATUS_MAP[pr.state]?.(pr) ?? defaultStatus(pr);
|
|
@@ -5319,11 +5450,11 @@ function formatDate(dateStr) {
|
|
|
5319
5450
|
return new Date(dateStr).toISOString().split("T")[0];
|
|
5320
5451
|
}
|
|
5321
5452
|
function formatPrHeader(pr, status2) {
|
|
5322
|
-
return `${
|
|
5453
|
+
return `${chalk54.cyan(`#${pr.number}`)} ${pr.title} ${chalk54.dim(`(${pr.author.login},`)} ${status2.label} ${chalk54.dim(`${formatDate(status2.date)})`)}`;
|
|
5323
5454
|
}
|
|
5324
5455
|
function logPrDetails(pr) {
|
|
5325
5456
|
console.log(
|
|
5326
|
-
|
|
5457
|
+
chalk54.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
|
|
5327
5458
|
);
|
|
5328
5459
|
console.log();
|
|
5329
5460
|
}
|
|
@@ -5483,17 +5614,17 @@ function registerPrs(program2) {
|
|
|
5483
5614
|
prsCommand.command("wontfix <comment-id> <reason>").description("Reply with reason and resolve thread").action((commentId, reason) => {
|
|
5484
5615
|
wontfix(Number.parseInt(commentId, 10), reason);
|
|
5485
5616
|
});
|
|
5486
|
-
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((
|
|
5487
|
-
comment(
|
|
5617
|
+
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((path43, line, body) => {
|
|
5618
|
+
comment(path43, Number.parseInt(line, 10), body);
|
|
5488
5619
|
});
|
|
5489
5620
|
}
|
|
5490
5621
|
|
|
5491
5622
|
// src/commands/refactor/check/index.ts
|
|
5492
5623
|
import { spawn as spawn3 } from "child_process";
|
|
5493
|
-
import * as
|
|
5624
|
+
import * as path26 from "path";
|
|
5494
5625
|
|
|
5495
5626
|
// src/commands/refactor/logViolations.ts
|
|
5496
|
-
import
|
|
5627
|
+
import chalk55 from "chalk";
|
|
5497
5628
|
var DEFAULT_MAX_LINES = 100;
|
|
5498
5629
|
function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
5499
5630
|
if (violations.length === 0) {
|
|
@@ -5502,43 +5633,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
|
5502
5633
|
}
|
|
5503
5634
|
return;
|
|
5504
5635
|
}
|
|
5505
|
-
console.error(
|
|
5636
|
+
console.error(chalk55.red(`
|
|
5506
5637
|
Refactor check failed:
|
|
5507
5638
|
`));
|
|
5508
|
-
console.error(
|
|
5639
|
+
console.error(chalk55.red(` The following files exceed ${maxLines} lines:
|
|
5509
5640
|
`));
|
|
5510
5641
|
for (const violation of violations) {
|
|
5511
|
-
console.error(
|
|
5642
|
+
console.error(chalk55.red(` ${violation.file} (${violation.lines} lines)`));
|
|
5512
5643
|
}
|
|
5513
5644
|
console.error(
|
|
5514
|
-
|
|
5645
|
+
chalk55.yellow(
|
|
5515
5646
|
`
|
|
5516
5647
|
Each file needs to be sensibly refactored, or if there is no sensible
|
|
5517
5648
|
way to refactor it, ignore it with:
|
|
5518
5649
|
`
|
|
5519
5650
|
)
|
|
5520
5651
|
);
|
|
5521
|
-
console.error(
|
|
5652
|
+
console.error(chalk55.gray(` assist refactor ignore <file>
|
|
5522
5653
|
`));
|
|
5523
5654
|
if (process.env.CLAUDECODE) {
|
|
5524
|
-
console.error(
|
|
5655
|
+
console.error(chalk55.cyan(`
|
|
5525
5656
|
## Extracting Code to New Files
|
|
5526
5657
|
`));
|
|
5527
5658
|
console.error(
|
|
5528
|
-
|
|
5659
|
+
chalk55.cyan(
|
|
5529
5660
|
` When extracting logic from one file to another, consider where the extracted code belongs:
|
|
5530
5661
|
`
|
|
5531
5662
|
)
|
|
5532
5663
|
);
|
|
5533
5664
|
console.error(
|
|
5534
|
-
|
|
5665
|
+
chalk55.cyan(
|
|
5535
5666
|
` 1. Keep related logic together: If the extracted code is tightly coupled to the
|
|
5536
5667
|
original file's domain, create a new folder containing both the original and extracted files.
|
|
5537
5668
|
`
|
|
5538
5669
|
)
|
|
5539
5670
|
);
|
|
5540
5671
|
console.error(
|
|
5541
|
-
|
|
5672
|
+
chalk55.cyan(
|
|
5542
5673
|
` 2. Share common utilities: If the extracted code can be reused across multiple
|
|
5543
5674
|
domains, move it to a common/shared folder.
|
|
5544
5675
|
`
|
|
@@ -5549,17 +5680,17 @@ Refactor check failed:
|
|
|
5549
5680
|
|
|
5550
5681
|
// src/commands/refactor/check/getViolations/index.ts
|
|
5551
5682
|
import { execSync as execSync29 } from "child_process";
|
|
5552
|
-
import
|
|
5683
|
+
import fs17 from "fs";
|
|
5553
5684
|
import { minimatch as minimatch4 } from "minimatch";
|
|
5554
5685
|
|
|
5555
5686
|
// src/commands/refactor/check/getViolations/getIgnoredFiles.ts
|
|
5556
|
-
import
|
|
5687
|
+
import fs16 from "fs";
|
|
5557
5688
|
var REFACTOR_YML_PATH = "refactor.yml";
|
|
5558
5689
|
function parseRefactorYml() {
|
|
5559
|
-
if (!
|
|
5690
|
+
if (!fs16.existsSync(REFACTOR_YML_PATH)) {
|
|
5560
5691
|
return [];
|
|
5561
5692
|
}
|
|
5562
|
-
const content =
|
|
5693
|
+
const content = fs16.readFileSync(REFACTOR_YML_PATH, "utf-8");
|
|
5563
5694
|
const entries = [];
|
|
5564
5695
|
const lines = content.split("\n");
|
|
5565
5696
|
let currentEntry = {};
|
|
@@ -5589,7 +5720,7 @@ function getIgnoredFiles() {
|
|
|
5589
5720
|
|
|
5590
5721
|
// src/commands/refactor/check/getViolations/index.ts
|
|
5591
5722
|
function countLines(filePath) {
|
|
5592
|
-
const content =
|
|
5723
|
+
const content = fs17.readFileSync(filePath, "utf-8");
|
|
5593
5724
|
return content.split("\n").length;
|
|
5594
5725
|
}
|
|
5595
5726
|
function getGitFiles(options2) {
|
|
@@ -5667,7 +5798,7 @@ ${failed.length} verify script(s) failed:`);
|
|
|
5667
5798
|
async function runVerifyQuietly() {
|
|
5668
5799
|
const result = findPackageJsonWithVerifyScripts(process.cwd());
|
|
5669
5800
|
if (!result) return true;
|
|
5670
|
-
const packageDir =
|
|
5801
|
+
const packageDir = path26.dirname(result.packageJsonPath);
|
|
5671
5802
|
const results = await Promise.all(
|
|
5672
5803
|
result.verifyScripts.map((script) => runScript(script, packageDir))
|
|
5673
5804
|
);
|
|
@@ -5693,39 +5824,147 @@ async function check(pattern2, options2) {
|
|
|
5693
5824
|
}
|
|
5694
5825
|
|
|
5695
5826
|
// src/commands/refactor/ignore.ts
|
|
5696
|
-
import
|
|
5697
|
-
import
|
|
5827
|
+
import fs18 from "fs";
|
|
5828
|
+
import chalk56 from "chalk";
|
|
5698
5829
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
5699
5830
|
function ignore(file) {
|
|
5700
|
-
if (!
|
|
5701
|
-
console.error(
|
|
5831
|
+
if (!fs18.existsSync(file)) {
|
|
5832
|
+
console.error(chalk56.red(`Error: File does not exist: ${file}`));
|
|
5702
5833
|
process.exit(1);
|
|
5703
5834
|
}
|
|
5704
|
-
const content =
|
|
5835
|
+
const content = fs18.readFileSync(file, "utf-8");
|
|
5705
5836
|
const lineCount = content.split("\n").length;
|
|
5706
5837
|
const maxLines = lineCount + 10;
|
|
5707
5838
|
const entry = `- file: ${file}
|
|
5708
5839
|
maxLines: ${maxLines}
|
|
5709
5840
|
`;
|
|
5710
|
-
if (
|
|
5711
|
-
const existing =
|
|
5712
|
-
|
|
5841
|
+
if (fs18.existsSync(REFACTOR_YML_PATH2)) {
|
|
5842
|
+
const existing = fs18.readFileSync(REFACTOR_YML_PATH2, "utf-8");
|
|
5843
|
+
fs18.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
|
|
5713
5844
|
} else {
|
|
5714
|
-
|
|
5845
|
+
fs18.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
5715
5846
|
}
|
|
5716
5847
|
console.log(
|
|
5717
|
-
|
|
5848
|
+
chalk56.green(
|
|
5718
5849
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
5719
5850
|
)
|
|
5720
5851
|
);
|
|
5721
5852
|
}
|
|
5722
5853
|
|
|
5723
|
-
// src/commands/refactor/
|
|
5724
|
-
import
|
|
5854
|
+
// src/commands/refactor/rename/index.ts
|
|
5855
|
+
import path27 from "path";
|
|
5856
|
+
import chalk57 from "chalk";
|
|
5857
|
+
import { Project as Project2 } from "ts-morph";
|
|
5858
|
+
async function rename(source, destination, options2 = {}) {
|
|
5859
|
+
const sourcePath = path27.resolve(source);
|
|
5860
|
+
const destPath = path27.resolve(destination);
|
|
5861
|
+
const cwd = process.cwd();
|
|
5862
|
+
const relSource = path27.relative(cwd, sourcePath);
|
|
5863
|
+
const relDest = path27.relative(cwd, destPath);
|
|
5864
|
+
const project = new Project2({
|
|
5865
|
+
tsConfigFilePath: path27.resolve("tsconfig.json")
|
|
5866
|
+
});
|
|
5867
|
+
const sourceFile = project.getSourceFile(sourcePath);
|
|
5868
|
+
if (!sourceFile) {
|
|
5869
|
+
console.log(chalk57.red(`File not found in project: ${source}`));
|
|
5870
|
+
process.exit(1);
|
|
5871
|
+
}
|
|
5872
|
+
console.log(chalk57.bold(`Rename: ${relSource} \u2192 ${relDest}`));
|
|
5873
|
+
if (options2.apply) {
|
|
5874
|
+
sourceFile.move(destPath);
|
|
5875
|
+
await project.save();
|
|
5876
|
+
console.log(chalk57.green("Done"));
|
|
5877
|
+
} else {
|
|
5878
|
+
console.log(chalk57.dim("Dry run. Use --apply to execute."));
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
|
|
5882
|
+
// src/commands/refactor/renameSymbol/index.ts
|
|
5883
|
+
import path29 from "path";
|
|
5725
5884
|
import chalk58 from "chalk";
|
|
5885
|
+
import { Project as Project3 } from "ts-morph";
|
|
5886
|
+
|
|
5887
|
+
// src/commands/refactor/renameSymbol/findSymbol.ts
|
|
5888
|
+
import { SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
5889
|
+
var declarationKinds = [
|
|
5890
|
+
SyntaxKind2.VariableDeclaration,
|
|
5891
|
+
SyntaxKind2.FunctionDeclaration,
|
|
5892
|
+
SyntaxKind2.ClassDeclaration,
|
|
5893
|
+
SyntaxKind2.InterfaceDeclaration,
|
|
5894
|
+
SyntaxKind2.TypeAliasDeclaration,
|
|
5895
|
+
SyntaxKind2.EnumDeclaration,
|
|
5896
|
+
SyntaxKind2.PropertyDeclaration,
|
|
5897
|
+
SyntaxKind2.MethodDeclaration,
|
|
5898
|
+
SyntaxKind2.Parameter
|
|
5899
|
+
];
|
|
5900
|
+
function isDeclaration(identifier) {
|
|
5901
|
+
const parent = identifier.getParent();
|
|
5902
|
+
return parent !== void 0 && declarationKinds.includes(parent.getKind());
|
|
5903
|
+
}
|
|
5904
|
+
function findSymbol(sourceFile, symbolName) {
|
|
5905
|
+
for (const id of sourceFile.getDescendantsOfKind(SyntaxKind2.Identifier)) {
|
|
5906
|
+
if (id.getText() === symbolName && isDeclaration(id)) return id;
|
|
5907
|
+
}
|
|
5908
|
+
return void 0;
|
|
5909
|
+
}
|
|
5910
|
+
|
|
5911
|
+
// src/commands/refactor/renameSymbol/groupReferences.ts
|
|
5912
|
+
import path28 from "path";
|
|
5913
|
+
function groupReferences(symbol, cwd) {
|
|
5914
|
+
const refs = symbol.findReferencesAsNodes();
|
|
5915
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
5916
|
+
for (const ref of refs) {
|
|
5917
|
+
const refFile = path28.relative(cwd, ref.getSourceFile().getFilePath());
|
|
5918
|
+
const lines = grouped.get(refFile) ?? [];
|
|
5919
|
+
if (!grouped.has(refFile)) grouped.set(refFile, lines);
|
|
5920
|
+
lines.push(ref.getStartLineNumber());
|
|
5921
|
+
}
|
|
5922
|
+
return grouped;
|
|
5923
|
+
}
|
|
5924
|
+
|
|
5925
|
+
// src/commands/refactor/renameSymbol/index.ts
|
|
5926
|
+
async function renameSymbol(file, oldName, newName, options2 = {}) {
|
|
5927
|
+
const filePath = path29.resolve(file);
|
|
5928
|
+
const tsConfigPath = path29.resolve("tsconfig.json");
|
|
5929
|
+
const cwd = process.cwd();
|
|
5930
|
+
const project = new Project3({ tsConfigFilePath: tsConfigPath });
|
|
5931
|
+
const sourceFile = project.getSourceFile(filePath);
|
|
5932
|
+
if (!sourceFile) {
|
|
5933
|
+
console.log(chalk58.red(`File not found in project: ${file}`));
|
|
5934
|
+
process.exit(1);
|
|
5935
|
+
}
|
|
5936
|
+
const symbol = findSymbol(sourceFile, oldName);
|
|
5937
|
+
if (!symbol) {
|
|
5938
|
+
console.log(chalk58.red(`Symbol "${oldName}" not found in ${file}`));
|
|
5939
|
+
process.exit(1);
|
|
5940
|
+
}
|
|
5941
|
+
const grouped = groupReferences(symbol, cwd);
|
|
5942
|
+
const totalRefs = [...grouped.values()].reduce((s, l) => s + l.length, 0);
|
|
5943
|
+
console.log(
|
|
5944
|
+
chalk58.bold(`Rename: ${oldName} \u2192 ${newName} (${totalRefs} references)
|
|
5945
|
+
`)
|
|
5946
|
+
);
|
|
5947
|
+
for (const [refFile, lines] of grouped) {
|
|
5948
|
+
console.log(
|
|
5949
|
+
` ${chalk58.dim(refFile)}: lines ${chalk58.cyan(lines.join(", "))}`
|
|
5950
|
+
);
|
|
5951
|
+
}
|
|
5952
|
+
if (options2.apply) {
|
|
5953
|
+
symbol.rename(newName);
|
|
5954
|
+
await project.save();
|
|
5955
|
+
console.log(chalk58.green(`
|
|
5956
|
+
Renamed ${oldName} \u2192 ${newName}`));
|
|
5957
|
+
} else {
|
|
5958
|
+
console.log(chalk58.dim("\nDry run. Use --apply to execute."));
|
|
5959
|
+
}
|
|
5960
|
+
}
|
|
5961
|
+
|
|
5962
|
+
// src/commands/refactor/restructure/index.ts
|
|
5963
|
+
import path38 from "path";
|
|
5964
|
+
import chalk61 from "chalk";
|
|
5726
5965
|
|
|
5727
5966
|
// src/commands/refactor/restructure/buildImportGraph/index.ts
|
|
5728
|
-
import
|
|
5967
|
+
import path30 from "path";
|
|
5729
5968
|
import ts7 from "typescript";
|
|
5730
5969
|
|
|
5731
5970
|
// src/commands/refactor/restructure/buildImportGraph/getImportSpecifiers.ts
|
|
@@ -5752,7 +5991,7 @@ function loadParsedConfig(tsConfigPath) {
|
|
|
5752
5991
|
return ts7.parseJsonConfigFileContent(
|
|
5753
5992
|
configFile.config,
|
|
5754
5993
|
ts7.sys,
|
|
5755
|
-
|
|
5994
|
+
path30.dirname(tsConfigPath)
|
|
5756
5995
|
);
|
|
5757
5996
|
}
|
|
5758
5997
|
function addToSetMap(map, key, value) {
|
|
@@ -5768,7 +6007,7 @@ function resolveImport(specifier, filePath, options2) {
|
|
|
5768
6007
|
const resolved = ts7.resolveModuleName(specifier, filePath, options2, ts7.sys);
|
|
5769
6008
|
const resolvedPath = resolved.resolvedModule?.resolvedFileName;
|
|
5770
6009
|
if (!resolvedPath || resolvedPath.includes("node_modules")) return null;
|
|
5771
|
-
return
|
|
6010
|
+
return path30.resolve(resolvedPath);
|
|
5772
6011
|
}
|
|
5773
6012
|
function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
5774
6013
|
const parsed = loadParsedConfig(tsConfigPath);
|
|
@@ -5777,7 +6016,7 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
5777
6016
|
const importedBy = /* @__PURE__ */ new Map();
|
|
5778
6017
|
const imports = /* @__PURE__ */ new Map();
|
|
5779
6018
|
for (const sourceFile of program2.getSourceFiles()) {
|
|
5780
|
-
const filePath =
|
|
6019
|
+
const filePath = path30.resolve(sourceFile.fileName);
|
|
5781
6020
|
if (filePath.includes("node_modules")) continue;
|
|
5782
6021
|
for (const specifier of getImportSpecifiers(sourceFile)) {
|
|
5783
6022
|
const absTarget = resolveImport(specifier, filePath, parsed.options);
|
|
@@ -5791,12 +6030,12 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
5791
6030
|
}
|
|
5792
6031
|
|
|
5793
6032
|
// src/commands/refactor/restructure/clusterDirectories.ts
|
|
5794
|
-
import
|
|
6033
|
+
import path31 from "path";
|
|
5795
6034
|
function clusterDirectories(graph) {
|
|
5796
6035
|
const dirImportedBy = /* @__PURE__ */ new Map();
|
|
5797
6036
|
for (const edge of graph.edges) {
|
|
5798
|
-
const sourceDir =
|
|
5799
|
-
const targetDir =
|
|
6037
|
+
const sourceDir = path31.dirname(edge.source);
|
|
6038
|
+
const targetDir = path31.dirname(edge.target);
|
|
5800
6039
|
if (sourceDir === targetDir) continue;
|
|
5801
6040
|
if (!graph.files.has(edge.target)) continue;
|
|
5802
6041
|
const existing = dirImportedBy.get(targetDir) ?? /* @__PURE__ */ new Set();
|
|
@@ -5824,20 +6063,20 @@ function clusterDirectories(graph) {
|
|
|
5824
6063
|
return clusters;
|
|
5825
6064
|
}
|
|
5826
6065
|
function isAncestor(ancestor, descendant) {
|
|
5827
|
-
const rel =
|
|
6066
|
+
const rel = path31.relative(ancestor, descendant);
|
|
5828
6067
|
return !rel.startsWith("..") && rel !== "";
|
|
5829
6068
|
}
|
|
5830
6069
|
|
|
5831
6070
|
// src/commands/refactor/restructure/clusterFiles.ts
|
|
5832
|
-
import
|
|
6071
|
+
import path32 from "path";
|
|
5833
6072
|
function findRootParent(file, importedBy, visited) {
|
|
5834
6073
|
const importers = importedBy.get(file);
|
|
5835
6074
|
if (!importers || importers.size !== 1) return file;
|
|
5836
6075
|
const parent = [...importers][0];
|
|
5837
|
-
const parentDir =
|
|
5838
|
-
const fileDir =
|
|
6076
|
+
const parentDir = path32.dirname(parent);
|
|
6077
|
+
const fileDir = path32.dirname(file);
|
|
5839
6078
|
if (parentDir !== fileDir) return file;
|
|
5840
|
-
if (
|
|
6079
|
+
if (path32.basename(parent, path32.extname(parent)) === "index") return file;
|
|
5841
6080
|
if (visited.has(parent)) return file;
|
|
5842
6081
|
visited.add(parent);
|
|
5843
6082
|
return findRootParent(parent, importedBy, visited);
|
|
@@ -5845,16 +6084,16 @@ function findRootParent(file, importedBy, visited) {
|
|
|
5845
6084
|
function clusterFiles(graph) {
|
|
5846
6085
|
const clusters = /* @__PURE__ */ new Map();
|
|
5847
6086
|
for (const file of graph.files) {
|
|
5848
|
-
const basename7 =
|
|
6087
|
+
const basename7 = path32.basename(file, path32.extname(file));
|
|
5849
6088
|
if (basename7 === "index") continue;
|
|
5850
6089
|
const importers = graph.importedBy.get(file);
|
|
5851
6090
|
if (!importers || importers.size !== 1) continue;
|
|
5852
6091
|
const parent = [...importers][0];
|
|
5853
6092
|
if (!graph.files.has(parent)) continue;
|
|
5854
|
-
const parentDir =
|
|
5855
|
-
const fileDir =
|
|
6093
|
+
const parentDir = path32.dirname(parent);
|
|
6094
|
+
const fileDir = path32.dirname(file);
|
|
5856
6095
|
if (parentDir !== fileDir) continue;
|
|
5857
|
-
const parentBasename =
|
|
6096
|
+
const parentBasename = path32.basename(parent, path32.extname(parent));
|
|
5858
6097
|
if (parentBasename === "index") continue;
|
|
5859
6098
|
const root = findRootParent(parent, graph.importedBy, /* @__PURE__ */ new Set([file]));
|
|
5860
6099
|
if (!root || root === file) continue;
|
|
@@ -5866,10 +6105,10 @@ function clusterFiles(graph) {
|
|
|
5866
6105
|
}
|
|
5867
6106
|
|
|
5868
6107
|
// src/commands/refactor/restructure/computeRewrites/index.ts
|
|
5869
|
-
import
|
|
6108
|
+
import path33 from "path";
|
|
5870
6109
|
|
|
5871
6110
|
// src/commands/refactor/restructure/computeRewrites/applyRewrites.ts
|
|
5872
|
-
import
|
|
6111
|
+
import fs19 from "fs";
|
|
5873
6112
|
function getOrCreateList(map, key) {
|
|
5874
6113
|
const list4 = map.get(key) ?? [];
|
|
5875
6114
|
if (!map.has(key)) map.set(key, list4);
|
|
@@ -5888,7 +6127,7 @@ function rewriteSpecifier(content, oldSpecifier, newSpecifier) {
|
|
|
5888
6127
|
return content.replace(pattern2, `$1${newSpecifier}$2`);
|
|
5889
6128
|
}
|
|
5890
6129
|
function applyFileRewrites(file, fileRewrites) {
|
|
5891
|
-
let content =
|
|
6130
|
+
let content = fs19.readFileSync(file, "utf-8");
|
|
5892
6131
|
for (const { oldSpecifier, newSpecifier } of fileRewrites) {
|
|
5893
6132
|
content = rewriteSpecifier(content, oldSpecifier, newSpecifier);
|
|
5894
6133
|
}
|
|
@@ -5920,7 +6159,7 @@ function normalizeSpecifier(rel) {
|
|
|
5920
6159
|
);
|
|
5921
6160
|
}
|
|
5922
6161
|
function computeSpecifier(fromFile, toFile) {
|
|
5923
|
-
return normalizeSpecifier(
|
|
6162
|
+
return normalizeSpecifier(path33.relative(path33.dirname(fromFile), toFile));
|
|
5924
6163
|
}
|
|
5925
6164
|
function isAffected(edge, moveMap) {
|
|
5926
6165
|
return moveMap.has(edge.target) || moveMap.has(edge.source);
|
|
@@ -5964,51 +6203,51 @@ function computeRewrites(moves, edges, allProjectFiles) {
|
|
|
5964
6203
|
}
|
|
5965
6204
|
|
|
5966
6205
|
// src/commands/refactor/restructure/displayPlan.ts
|
|
5967
|
-
import
|
|
5968
|
-
import
|
|
6206
|
+
import path34 from "path";
|
|
6207
|
+
import chalk59 from "chalk";
|
|
5969
6208
|
function relPath(filePath) {
|
|
5970
|
-
return
|
|
6209
|
+
return path34.relative(process.cwd(), filePath);
|
|
5971
6210
|
}
|
|
5972
6211
|
function displayMoves(plan) {
|
|
5973
6212
|
if (plan.moves.length === 0) return;
|
|
5974
|
-
console.log(
|
|
6213
|
+
console.log(chalk59.bold("\nFile moves:"));
|
|
5975
6214
|
for (const move of plan.moves) {
|
|
5976
6215
|
console.log(
|
|
5977
|
-
` ${
|
|
6216
|
+
` ${chalk59.red(relPath(move.from))} \u2192 ${chalk59.green(relPath(move.to))}`
|
|
5978
6217
|
);
|
|
5979
|
-
console.log(
|
|
6218
|
+
console.log(chalk59.dim(` ${move.reason}`));
|
|
5980
6219
|
}
|
|
5981
6220
|
}
|
|
5982
6221
|
function displayRewrites(rewrites) {
|
|
5983
6222
|
if (rewrites.length === 0) return;
|
|
5984
6223
|
const affectedFiles = new Set(rewrites.map((r) => r.file));
|
|
5985
|
-
console.log(
|
|
6224
|
+
console.log(chalk59.bold(`
|
|
5986
6225
|
Import rewrites (${affectedFiles.size} files):`));
|
|
5987
6226
|
for (const file of affectedFiles) {
|
|
5988
|
-
console.log(` ${
|
|
6227
|
+
console.log(` ${chalk59.cyan(relPath(file))}:`);
|
|
5989
6228
|
for (const { oldSpecifier, newSpecifier } of rewrites.filter(
|
|
5990
6229
|
(r) => r.file === file
|
|
5991
6230
|
)) {
|
|
5992
6231
|
console.log(
|
|
5993
|
-
` ${
|
|
6232
|
+
` ${chalk59.red(`"${oldSpecifier}"`)} \u2192 ${chalk59.green(`"${newSpecifier}"`)}`
|
|
5994
6233
|
);
|
|
5995
6234
|
}
|
|
5996
6235
|
}
|
|
5997
6236
|
}
|
|
5998
6237
|
function displayPlan(plan) {
|
|
5999
6238
|
if (plan.warnings.length > 0) {
|
|
6000
|
-
console.log(
|
|
6001
|
-
for (const w of plan.warnings) console.log(
|
|
6239
|
+
console.log(chalk59.yellow("\nWarnings:"));
|
|
6240
|
+
for (const w of plan.warnings) console.log(chalk59.yellow(` ${w}`));
|
|
6002
6241
|
}
|
|
6003
6242
|
if (plan.newDirectories.length > 0) {
|
|
6004
|
-
console.log(
|
|
6243
|
+
console.log(chalk59.bold("\nNew directories:"));
|
|
6005
6244
|
for (const dir of plan.newDirectories)
|
|
6006
|
-
console.log(
|
|
6245
|
+
console.log(chalk59.green(` ${dir}/`));
|
|
6007
6246
|
}
|
|
6008
6247
|
displayMoves(plan);
|
|
6009
6248
|
displayRewrites(plan.rewrites);
|
|
6010
6249
|
console.log(
|
|
6011
|
-
|
|
6250
|
+
chalk59.dim(
|
|
6012
6251
|
`
|
|
6013
6252
|
Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rewritten`
|
|
6014
6253
|
)
|
|
@@ -6016,45 +6255,45 @@ Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rew
|
|
|
6016
6255
|
}
|
|
6017
6256
|
|
|
6018
6257
|
// src/commands/refactor/restructure/executePlan.ts
|
|
6019
|
-
import
|
|
6020
|
-
import
|
|
6021
|
-
import
|
|
6258
|
+
import fs20 from "fs";
|
|
6259
|
+
import path35 from "path";
|
|
6260
|
+
import chalk60 from "chalk";
|
|
6022
6261
|
function executePlan(plan) {
|
|
6023
6262
|
const updatedContents = applyRewrites(plan.rewrites);
|
|
6024
6263
|
for (const [file, content] of updatedContents) {
|
|
6025
|
-
|
|
6264
|
+
fs20.writeFileSync(file, content, "utf-8");
|
|
6026
6265
|
console.log(
|
|
6027
|
-
|
|
6266
|
+
chalk60.cyan(` Rewrote imports in ${path35.relative(process.cwd(), file)}`)
|
|
6028
6267
|
);
|
|
6029
6268
|
}
|
|
6030
6269
|
for (const dir of plan.newDirectories) {
|
|
6031
|
-
|
|
6032
|
-
console.log(
|
|
6270
|
+
fs20.mkdirSync(dir, { recursive: true });
|
|
6271
|
+
console.log(chalk60.green(` Created ${path35.relative(process.cwd(), dir)}/`));
|
|
6033
6272
|
}
|
|
6034
6273
|
for (const move of plan.moves) {
|
|
6035
|
-
const targetDir =
|
|
6036
|
-
if (!
|
|
6037
|
-
|
|
6274
|
+
const targetDir = path35.dirname(move.to);
|
|
6275
|
+
if (!fs20.existsSync(targetDir)) {
|
|
6276
|
+
fs20.mkdirSync(targetDir, { recursive: true });
|
|
6038
6277
|
}
|
|
6039
|
-
|
|
6278
|
+
fs20.renameSync(move.from, move.to);
|
|
6040
6279
|
console.log(
|
|
6041
|
-
|
|
6042
|
-
` Moved ${
|
|
6280
|
+
chalk60.white(
|
|
6281
|
+
` Moved ${path35.relative(process.cwd(), move.from)} \u2192 ${path35.relative(process.cwd(), move.to)}`
|
|
6043
6282
|
)
|
|
6044
6283
|
);
|
|
6045
6284
|
}
|
|
6046
|
-
removeEmptyDirectories(plan.moves.map((m) =>
|
|
6285
|
+
removeEmptyDirectories(plan.moves.map((m) => path35.dirname(m.from)));
|
|
6047
6286
|
}
|
|
6048
6287
|
function removeEmptyDirectories(dirs) {
|
|
6049
6288
|
const unique = [...new Set(dirs)];
|
|
6050
6289
|
for (const dir of unique) {
|
|
6051
|
-
if (!
|
|
6052
|
-
const entries =
|
|
6290
|
+
if (!fs20.existsSync(dir)) continue;
|
|
6291
|
+
const entries = fs20.readdirSync(dir);
|
|
6053
6292
|
if (entries.length === 0) {
|
|
6054
|
-
|
|
6293
|
+
fs20.rmdirSync(dir);
|
|
6055
6294
|
console.log(
|
|
6056
|
-
|
|
6057
|
-
` Removed empty directory ${
|
|
6295
|
+
chalk60.dim(
|
|
6296
|
+
` Removed empty directory ${path35.relative(process.cwd(), dir)}`
|
|
6058
6297
|
)
|
|
6059
6298
|
);
|
|
6060
6299
|
}
|
|
@@ -6062,36 +6301,36 @@ function removeEmptyDirectories(dirs) {
|
|
|
6062
6301
|
}
|
|
6063
6302
|
|
|
6064
6303
|
// src/commands/refactor/restructure/planFileMoves/index.ts
|
|
6065
|
-
import
|
|
6066
|
-
import
|
|
6304
|
+
import fs22 from "fs";
|
|
6305
|
+
import path37 from "path";
|
|
6067
6306
|
|
|
6068
6307
|
// src/commands/refactor/restructure/planFileMoves/planDirectoryMoves.ts
|
|
6069
|
-
import
|
|
6070
|
-
import
|
|
6308
|
+
import fs21 from "fs";
|
|
6309
|
+
import path36 from "path";
|
|
6071
6310
|
function collectEntry(results, dir, entry) {
|
|
6072
|
-
const full =
|
|
6311
|
+
const full = path36.join(dir, entry.name);
|
|
6073
6312
|
const items = entry.isDirectory() ? listFilesRecursive(full) : [full];
|
|
6074
6313
|
results.push(...items);
|
|
6075
6314
|
}
|
|
6076
6315
|
function listFilesRecursive(dir) {
|
|
6077
|
-
if (!
|
|
6316
|
+
if (!fs21.existsSync(dir)) return [];
|
|
6078
6317
|
const results = [];
|
|
6079
|
-
for (const entry of
|
|
6318
|
+
for (const entry of fs21.readdirSync(dir, { withFileTypes: true })) {
|
|
6080
6319
|
collectEntry(results, dir, entry);
|
|
6081
6320
|
}
|
|
6082
6321
|
return results;
|
|
6083
6322
|
}
|
|
6084
6323
|
function addDirectoryFileMoves(moves, childDir, newLocation, reason) {
|
|
6085
6324
|
for (const file of listFilesRecursive(childDir)) {
|
|
6086
|
-
const rel =
|
|
6087
|
-
moves.push({ from: file, to:
|
|
6325
|
+
const rel = path36.relative(childDir, file);
|
|
6326
|
+
moves.push({ from: file, to: path36.join(newLocation, rel), reason });
|
|
6088
6327
|
}
|
|
6089
6328
|
}
|
|
6090
6329
|
function resolveChildDest(parentDir, childDir) {
|
|
6091
|
-
return
|
|
6330
|
+
return path36.join(parentDir, path36.basename(childDir));
|
|
6092
6331
|
}
|
|
6093
6332
|
function childMoveReason(parentDir) {
|
|
6094
|
-
return `Directory only imported from ${
|
|
6333
|
+
return `Directory only imported from ${path36.basename(parentDir)}/`;
|
|
6095
6334
|
}
|
|
6096
6335
|
function registerDirectoryMove(result, childDir, dest, parentDir) {
|
|
6097
6336
|
result.directories.push(dest);
|
|
@@ -6119,7 +6358,7 @@ function emptyResult() {
|
|
|
6119
6358
|
return { moves: [], directories: [], warnings: [] };
|
|
6120
6359
|
}
|
|
6121
6360
|
function childMoveData(child, newDir, parentBase) {
|
|
6122
|
-
const to =
|
|
6361
|
+
const to = path37.join(newDir, path37.basename(child));
|
|
6123
6362
|
return { from: child, to, reason: `Only imported by ${parentBase}` };
|
|
6124
6363
|
}
|
|
6125
6364
|
function addChildMoves(moves, children, newDir, parentBase) {
|
|
@@ -6127,20 +6366,20 @@ function addChildMoves(moves, children, newDir, parentBase) {
|
|
|
6127
6366
|
moves.push(childMoveData(child, newDir, parentBase));
|
|
6128
6367
|
}
|
|
6129
6368
|
function checkDirConflict(result, label2, dir) {
|
|
6130
|
-
if (!
|
|
6369
|
+
if (!fs22.existsSync(dir)) return false;
|
|
6131
6370
|
result.warnings.push(`Skipping ${label2}: directory ${dir} already exists`);
|
|
6132
6371
|
return true;
|
|
6133
6372
|
}
|
|
6134
6373
|
function getBaseName(filePath) {
|
|
6135
|
-
return
|
|
6374
|
+
return path37.basename(filePath, path37.extname(filePath));
|
|
6136
6375
|
}
|
|
6137
6376
|
function resolveClusterDir(parent) {
|
|
6138
|
-
return
|
|
6377
|
+
return path37.join(path37.dirname(parent), getBaseName(parent));
|
|
6139
6378
|
}
|
|
6140
6379
|
function createParentMove(parent, newDir) {
|
|
6141
6380
|
return {
|
|
6142
6381
|
from: parent,
|
|
6143
|
-
to:
|
|
6382
|
+
to: path37.join(newDir, `index${path37.extname(parent)}`),
|
|
6144
6383
|
reason: `Main module of new ${getBaseName(parent)}/ directory`
|
|
6145
6384
|
};
|
|
6146
6385
|
}
|
|
@@ -6164,7 +6403,7 @@ function planFileMoves(clusters) {
|
|
|
6164
6403
|
|
|
6165
6404
|
// src/commands/refactor/restructure/index.ts
|
|
6166
6405
|
function buildPlan(candidateFiles, tsConfigPath) {
|
|
6167
|
-
const candidates = new Set(candidateFiles.map((f) =>
|
|
6406
|
+
const candidates = new Set(candidateFiles.map((f) => path38.resolve(f)));
|
|
6168
6407
|
const graph = buildImportGraph(candidates, tsConfigPath);
|
|
6169
6408
|
const allProjectFiles = /* @__PURE__ */ new Set([
|
|
6170
6409
|
...graph.importedBy.keys(),
|
|
@@ -6184,22 +6423,22 @@ async function restructure(pattern2, options2 = {}) {
|
|
|
6184
6423
|
const targetPattern = pattern2 ?? "src";
|
|
6185
6424
|
const files = findSourceFiles2(targetPattern);
|
|
6186
6425
|
if (files.length === 0) {
|
|
6187
|
-
console.log(
|
|
6426
|
+
console.log(chalk61.yellow("No files found matching pattern"));
|
|
6188
6427
|
return;
|
|
6189
6428
|
}
|
|
6190
|
-
const tsConfigPath =
|
|
6429
|
+
const tsConfigPath = path38.resolve("tsconfig.json");
|
|
6191
6430
|
const plan = buildPlan(files, tsConfigPath);
|
|
6192
6431
|
if (plan.moves.length === 0) {
|
|
6193
|
-
console.log(
|
|
6432
|
+
console.log(chalk61.green("No restructuring needed"));
|
|
6194
6433
|
return;
|
|
6195
6434
|
}
|
|
6196
6435
|
displayPlan(plan);
|
|
6197
6436
|
if (options2.apply) {
|
|
6198
|
-
console.log(
|
|
6437
|
+
console.log(chalk61.bold("\nApplying changes..."));
|
|
6199
6438
|
executePlan(plan);
|
|
6200
|
-
console.log(
|
|
6439
|
+
console.log(chalk61.green("\nRestructuring complete"));
|
|
6201
6440
|
} else {
|
|
6202
|
-
console.log(
|
|
6441
|
+
console.log(chalk61.dim("\nDry run. Use --apply to execute."));
|
|
6203
6442
|
}
|
|
6204
6443
|
}
|
|
6205
6444
|
|
|
@@ -6212,6 +6451,11 @@ function registerRefactor(program2) {
|
|
|
6212
6451
|
Number.parseInt
|
|
6213
6452
|
).action(check);
|
|
6214
6453
|
refactorCommand.command("ignore <file>").description("Add a file to the refactor ignore list").action(ignore);
|
|
6454
|
+
const renameCommand = refactorCommand.command("rename").description("Rename files or symbols with automatic import updates");
|
|
6455
|
+
renameCommand.command("file <source> <destination>").description("Rename/move a TypeScript file and update all imports").option("--apply", "Execute the rename (default: dry-run)").action(rename);
|
|
6456
|
+
renameCommand.command("symbol <file> <oldName> <newName>").description(
|
|
6457
|
+
"Rename a variable, function, class, or type across the project"
|
|
6458
|
+
).option("--apply", "Execute the rename (default: dry-run)").action(renameSymbol);
|
|
6215
6459
|
refactorCommand.command("restructure [pattern]").description(
|
|
6216
6460
|
"Analyze import graph and restructure tightly-coupled files into nested directories"
|
|
6217
6461
|
).option("--apply", "Execute the restructuring (default: dry-run)").option(
|
|
@@ -6742,14 +6986,14 @@ import {
|
|
|
6742
6986
|
import { dirname as dirname18, join as join26 } from "path";
|
|
6743
6987
|
|
|
6744
6988
|
// src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
|
|
6745
|
-
import
|
|
6989
|
+
import chalk62 from "chalk";
|
|
6746
6990
|
var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
|
|
6747
6991
|
function validateStagedContent(filename, content) {
|
|
6748
6992
|
const firstLine = content.split("\n")[0];
|
|
6749
6993
|
const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
|
|
6750
6994
|
if (!match) {
|
|
6751
6995
|
console.error(
|
|
6752
|
-
|
|
6996
|
+
chalk62.red(
|
|
6753
6997
|
`Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
|
|
6754
6998
|
)
|
|
6755
6999
|
);
|
|
@@ -6758,7 +7002,7 @@ function validateStagedContent(filename, content) {
|
|
|
6758
7002
|
const contentAfterLink = content.slice(firstLine.length).trim();
|
|
6759
7003
|
if (!contentAfterLink) {
|
|
6760
7004
|
console.error(
|
|
6761
|
-
|
|
7005
|
+
chalk62.red(
|
|
6762
7006
|
`Staged file ${filename} has no summary content after the transcript link.`
|
|
6763
7007
|
)
|
|
6764
7008
|
);
|
|
@@ -7151,7 +7395,7 @@ function registerVoice(program2) {
|
|
|
7151
7395
|
|
|
7152
7396
|
// src/commands/roam/auth.ts
|
|
7153
7397
|
import { randomBytes } from "crypto";
|
|
7154
|
-
import
|
|
7398
|
+
import chalk63 from "chalk";
|
|
7155
7399
|
|
|
7156
7400
|
// src/lib/openBrowser.ts
|
|
7157
7401
|
import { execSync as execSync31 } from "child_process";
|
|
@@ -7326,13 +7570,13 @@ async function auth() {
|
|
|
7326
7570
|
saveGlobalConfig(config);
|
|
7327
7571
|
const state = randomBytes(16).toString("hex");
|
|
7328
7572
|
console.log(
|
|
7329
|
-
|
|
7573
|
+
chalk63.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
7330
7574
|
);
|
|
7331
|
-
console.log(
|
|
7332
|
-
console.log(
|
|
7333
|
-
console.log(
|
|
7575
|
+
console.log(chalk63.white("http://localhost:14523/callback\n"));
|
|
7576
|
+
console.log(chalk63.blue("Opening browser for authorization..."));
|
|
7577
|
+
console.log(chalk63.dim("Waiting for authorization callback..."));
|
|
7334
7578
|
const { code, redirectUri } = await authorizeInBrowser(clientId, state);
|
|
7335
|
-
console.log(
|
|
7579
|
+
console.log(chalk63.dim("Exchanging code for tokens..."));
|
|
7336
7580
|
const tokens = await exchangeToken({
|
|
7337
7581
|
code,
|
|
7338
7582
|
clientId,
|
|
@@ -7348,7 +7592,7 @@ async function auth() {
|
|
|
7348
7592
|
};
|
|
7349
7593
|
saveGlobalConfig(config);
|
|
7350
7594
|
console.log(
|
|
7351
|
-
|
|
7595
|
+
chalk63.green("Roam credentials and tokens saved to ~/.assist.yml")
|
|
7352
7596
|
);
|
|
7353
7597
|
}
|
|
7354
7598
|
|
|
@@ -7536,14 +7780,14 @@ function run2(name, args) {
|
|
|
7536
7780
|
}
|
|
7537
7781
|
|
|
7538
7782
|
// src/commands/statusLine.ts
|
|
7539
|
-
import
|
|
7783
|
+
import chalk64 from "chalk";
|
|
7540
7784
|
function formatNumber(num) {
|
|
7541
7785
|
return num.toLocaleString("en-US");
|
|
7542
7786
|
}
|
|
7543
7787
|
function colorizePercent(pct) {
|
|
7544
7788
|
const label2 = `${pct}%`;
|
|
7545
|
-
if (pct > 80) return
|
|
7546
|
-
if (pct > 40) return
|
|
7789
|
+
if (pct > 80) return chalk64.red(label2);
|
|
7790
|
+
if (pct > 40) return chalk64.yellow(label2);
|
|
7547
7791
|
return label2;
|
|
7548
7792
|
}
|
|
7549
7793
|
async function statusLine() {
|
|
@@ -7561,29 +7805,29 @@ async function statusLine() {
|
|
|
7561
7805
|
}
|
|
7562
7806
|
|
|
7563
7807
|
// src/commands/sync.ts
|
|
7564
|
-
import * as
|
|
7808
|
+
import * as fs25 from "fs";
|
|
7565
7809
|
import * as os from "os";
|
|
7566
|
-
import * as
|
|
7810
|
+
import * as path41 from "path";
|
|
7567
7811
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
7568
7812
|
|
|
7569
7813
|
// src/commands/sync/syncClaudeMd.ts
|
|
7570
|
-
import * as
|
|
7571
|
-
import * as
|
|
7572
|
-
import
|
|
7814
|
+
import * as fs23 from "fs";
|
|
7815
|
+
import * as path39 from "path";
|
|
7816
|
+
import chalk65 from "chalk";
|
|
7573
7817
|
async function syncClaudeMd(claudeDir, targetBase) {
|
|
7574
|
-
const source =
|
|
7575
|
-
const target =
|
|
7576
|
-
const sourceContent =
|
|
7577
|
-
if (
|
|
7578
|
-
const targetContent =
|
|
7818
|
+
const source = path39.join(claudeDir, "CLAUDE.md");
|
|
7819
|
+
const target = path39.join(targetBase, "CLAUDE.md");
|
|
7820
|
+
const sourceContent = fs23.readFileSync(source, "utf-8");
|
|
7821
|
+
if (fs23.existsSync(target)) {
|
|
7822
|
+
const targetContent = fs23.readFileSync(target, "utf-8");
|
|
7579
7823
|
if (sourceContent !== targetContent) {
|
|
7580
7824
|
console.log(
|
|
7581
|
-
|
|
7825
|
+
chalk65.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
7582
7826
|
);
|
|
7583
7827
|
console.log();
|
|
7584
7828
|
printDiff(targetContent, sourceContent);
|
|
7585
7829
|
const confirm = await promptConfirm(
|
|
7586
|
-
|
|
7830
|
+
chalk65.red("Overwrite existing CLAUDE.md?"),
|
|
7587
7831
|
false
|
|
7588
7832
|
);
|
|
7589
7833
|
if (!confirm) {
|
|
@@ -7592,21 +7836,21 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
7592
7836
|
}
|
|
7593
7837
|
}
|
|
7594
7838
|
}
|
|
7595
|
-
|
|
7839
|
+
fs23.copyFileSync(source, target);
|
|
7596
7840
|
console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
|
|
7597
7841
|
}
|
|
7598
7842
|
|
|
7599
7843
|
// src/commands/sync/syncSettings.ts
|
|
7600
|
-
import * as
|
|
7601
|
-
import * as
|
|
7602
|
-
import
|
|
7844
|
+
import * as fs24 from "fs";
|
|
7845
|
+
import * as path40 from "path";
|
|
7846
|
+
import chalk66 from "chalk";
|
|
7603
7847
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
7604
|
-
const source =
|
|
7605
|
-
const target =
|
|
7606
|
-
const sourceContent =
|
|
7848
|
+
const source = path40.join(claudeDir, "settings.json");
|
|
7849
|
+
const target = path40.join(targetBase, "settings.json");
|
|
7850
|
+
const sourceContent = fs24.readFileSync(source, "utf-8");
|
|
7607
7851
|
const mergedContent = JSON.stringify(JSON.parse(sourceContent), null, " ");
|
|
7608
|
-
if (
|
|
7609
|
-
const targetContent =
|
|
7852
|
+
if (fs24.existsSync(target)) {
|
|
7853
|
+
const targetContent = fs24.readFileSync(target, "utf-8");
|
|
7610
7854
|
const normalizedTarget = JSON.stringify(
|
|
7611
7855
|
JSON.parse(targetContent),
|
|
7612
7856
|
null,
|
|
@@ -7615,14 +7859,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
7615
7859
|
if (mergedContent !== normalizedTarget) {
|
|
7616
7860
|
if (!options2?.yes) {
|
|
7617
7861
|
console.log(
|
|
7618
|
-
|
|
7862
|
+
chalk66.yellow(
|
|
7619
7863
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
7620
7864
|
)
|
|
7621
7865
|
);
|
|
7622
7866
|
console.log();
|
|
7623
7867
|
printDiff(targetContent, mergedContent);
|
|
7624
7868
|
const confirm = await promptConfirm(
|
|
7625
|
-
|
|
7869
|
+
chalk66.red("Overwrite existing settings.json?"),
|
|
7626
7870
|
false
|
|
7627
7871
|
);
|
|
7628
7872
|
if (!confirm) {
|
|
@@ -7632,27 +7876,27 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
7632
7876
|
}
|
|
7633
7877
|
}
|
|
7634
7878
|
}
|
|
7635
|
-
|
|
7879
|
+
fs24.writeFileSync(target, mergedContent);
|
|
7636
7880
|
console.log("Copied settings.json to ~/.claude/settings.json");
|
|
7637
7881
|
}
|
|
7638
7882
|
|
|
7639
7883
|
// src/commands/sync.ts
|
|
7640
7884
|
var __filename4 = fileURLToPath7(import.meta.url);
|
|
7641
|
-
var __dirname7 =
|
|
7885
|
+
var __dirname7 = path41.dirname(__filename4);
|
|
7642
7886
|
async function sync(options2) {
|
|
7643
|
-
const claudeDir =
|
|
7644
|
-
const targetBase =
|
|
7887
|
+
const claudeDir = path41.join(__dirname7, "..", "claude");
|
|
7888
|
+
const targetBase = path41.join(os.homedir(), ".claude");
|
|
7645
7889
|
syncCommands(claudeDir, targetBase);
|
|
7646
7890
|
await syncSettings(claudeDir, targetBase, { yes: options2?.yes });
|
|
7647
7891
|
await syncClaudeMd(claudeDir, targetBase);
|
|
7648
7892
|
}
|
|
7649
7893
|
function syncCommands(claudeDir, targetBase) {
|
|
7650
|
-
const sourceDir =
|
|
7651
|
-
const targetDir =
|
|
7652
|
-
|
|
7653
|
-
const files =
|
|
7894
|
+
const sourceDir = path41.join(claudeDir, "commands");
|
|
7895
|
+
const targetDir = path41.join(targetBase, "commands");
|
|
7896
|
+
fs25.mkdirSync(targetDir, { recursive: true });
|
|
7897
|
+
const files = fs25.readdirSync(sourceDir);
|
|
7654
7898
|
for (const file of files) {
|
|
7655
|
-
|
|
7899
|
+
fs25.copyFileSync(path41.join(sourceDir, file), path41.join(targetDir, file));
|
|
7656
7900
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
7657
7901
|
}
|
|
7658
7902
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
@@ -7660,15 +7904,15 @@ function syncCommands(claudeDir, targetBase) {
|
|
|
7660
7904
|
|
|
7661
7905
|
// src/commands/update.ts
|
|
7662
7906
|
import { execSync as execSync32 } from "child_process";
|
|
7663
|
-
import * as
|
|
7907
|
+
import * as path42 from "path";
|
|
7664
7908
|
function isGlobalNpmInstall(dir) {
|
|
7665
7909
|
try {
|
|
7666
|
-
const resolved =
|
|
7667
|
-
if (resolved.split(
|
|
7910
|
+
const resolved = path42.resolve(dir);
|
|
7911
|
+
if (resolved.split(path42.sep).includes("node_modules")) {
|
|
7668
7912
|
return true;
|
|
7669
7913
|
}
|
|
7670
7914
|
const globalPrefix = execSync32("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
7671
|
-
return resolved.toLowerCase().startsWith(
|
|
7915
|
+
return resolved.toLowerCase().startsWith(path42.resolve(globalPrefix).toLowerCase());
|
|
7672
7916
|
} catch {
|
|
7673
7917
|
return false;
|
|
7674
7918
|
}
|
|
@@ -7717,7 +7961,7 @@ runCommand.command("add").description("Add a new run configuration to assist.yml
|
|
|
7717
7961
|
'\nPositional params can be added to the config manually:\n params:\n - name: env # assist run deploy prod \u2192 appends "prod"\n required: true\n - name: tag\n default: latest'
|
|
7718
7962
|
).allowUnknownOption().allowExcessArguments().action(() => add3());
|
|
7719
7963
|
registerNew(program);
|
|
7720
|
-
var lintCommand = program.command("lint").description("Run lint checks for conventions not enforced by biomejs").action(lint);
|
|
7964
|
+
var lintCommand = program.command("lint").description("Run lint checks for conventions not enforced by biomejs").option("-f, --fix", "Auto-fix violations where possible").action(lint);
|
|
7721
7965
|
lintCommand.command("init").description("Initialize Biome with standard linter config").action(init);
|
|
7722
7966
|
var vscodeCommand = program.command("vscode").description("VS Code configuration utilities");
|
|
7723
7967
|
vscodeCommand.command("init").description("Add VS Code configuration files").action(init3);
|