@staff0rd/assist 0.21.2 → 0.22.1
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 +6 -0
- package/dist/index.js +285 -302
- package/package.json +1 -1
- package/dist/commands/enable-ralph/index.ts +0 -78
- package/dist/commands/enable-ralph/settings.local.json +0 -8
package/README.md
CHANGED
|
@@ -35,6 +35,7 @@ After installation, the `assist` command will be available globally.
|
|
|
35
35
|
- `assist sync` - Copy command files to `~/.claude/commands`
|
|
36
36
|
- `assist commit <message>` - Create a git commit with validation
|
|
37
37
|
- `assist prs` - List pull requests for the current repository
|
|
38
|
+
- `assist prs comments <pr-number>` - List all comments on a pull request
|
|
38
39
|
- `assist run <name>` - Run a configured command from assist.yml
|
|
39
40
|
- `assist run add` - Add a new run configuration to assist.yml
|
|
40
41
|
- `assist update` - Update claude-code to the latest version
|
|
@@ -51,6 +52,11 @@ After installation, the `assist` command will be available globally.
|
|
|
51
52
|
- `assist devlog version` - Show current repo name and version info
|
|
52
53
|
- `assist vscode init` - Add VS Code configuration files
|
|
53
54
|
- `assist deploy init` - Initialize Netlify project and configure deployment
|
|
55
|
+
- `assist deploy redirect` - Add trailing slash redirect script to index.html
|
|
54
56
|
- `assist notify` - Show desktop notification from JSON stdin (supports macOS, Windows, WSL)
|
|
55
57
|
- `assist status-line` - Format Claude Code status line from JSON stdin
|
|
58
|
+
- `assist complexity cyclomatic [pattern]` - Calculate cyclomatic complexity per function
|
|
59
|
+
- `assist complexity halstead [pattern]` - Calculate Halstead metrics per function
|
|
60
|
+
- `assist complexity maintainability [pattern]` - Calculate maintainability index per file
|
|
61
|
+
- `assist complexity sloc [pattern]` - Count source lines of code per file
|
|
56
62
|
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { execSync as
|
|
4
|
+
import { execSync as execSync16 } from "child_process";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/commands/commit.ts
|
|
@@ -1004,69 +1004,11 @@ function version() {
|
|
|
1004
1004
|
console.log(`${chalk13.bold("next:")} ${nextVersion ?? chalk13.dim("none")}`);
|
|
1005
1005
|
}
|
|
1006
1006
|
|
|
1007
|
-
// src/commands/enable-ralph/index.ts
|
|
1008
|
-
import * as fs4 from "fs";
|
|
1009
|
-
import * as path2 from "path";
|
|
1010
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1011
|
-
import chalk14 from "chalk";
|
|
1012
|
-
var __dirname3 = path2.dirname(fileURLToPath2(import.meta.url));
|
|
1013
|
-
function deepMerge(target, source) {
|
|
1014
|
-
const result = { ...target };
|
|
1015
|
-
for (const key of Object.keys(source)) {
|
|
1016
|
-
const sourceVal = source[key];
|
|
1017
|
-
const targetVal = result[key];
|
|
1018
|
-
if (sourceVal && typeof sourceVal === "object" && !Array.isArray(sourceVal) && targetVal && typeof targetVal === "object" && !Array.isArray(targetVal)) {
|
|
1019
|
-
result[key] = deepMerge(
|
|
1020
|
-
targetVal,
|
|
1021
|
-
sourceVal
|
|
1022
|
-
);
|
|
1023
|
-
} else {
|
|
1024
|
-
result[key] = sourceVal;
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
return result;
|
|
1028
|
-
}
|
|
1029
|
-
async function enableRalph() {
|
|
1030
|
-
const sourcePath = path2.join(
|
|
1031
|
-
__dirname3,
|
|
1032
|
-
"commands/enable-ralph/settings.local.json"
|
|
1033
|
-
);
|
|
1034
|
-
const targetPath = path2.join(process.cwd(), ".claude/settings.local.json");
|
|
1035
|
-
const sourceData = JSON.parse(fs4.readFileSync(sourcePath, "utf-8"));
|
|
1036
|
-
const targetDir = path2.dirname(targetPath);
|
|
1037
|
-
if (!fs4.existsSync(targetDir)) {
|
|
1038
|
-
fs4.mkdirSync(targetDir, { recursive: true });
|
|
1039
|
-
}
|
|
1040
|
-
let targetData = {};
|
|
1041
|
-
let targetContent = "{}";
|
|
1042
|
-
if (fs4.existsSync(targetPath)) {
|
|
1043
|
-
targetContent = fs4.readFileSync(targetPath, "utf-8");
|
|
1044
|
-
targetData = JSON.parse(targetContent);
|
|
1045
|
-
}
|
|
1046
|
-
const merged = deepMerge(targetData, sourceData);
|
|
1047
|
-
const mergedContent = `${JSON.stringify(merged, null, " ")}
|
|
1048
|
-
`;
|
|
1049
|
-
if (mergedContent === targetContent) {
|
|
1050
|
-
console.log(chalk14.green("settings.local.json already has ralph enabled"));
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
console.log(chalk14.yellow("\nChanges to settings.local.json:"));
|
|
1054
|
-
console.log();
|
|
1055
|
-
printDiff(targetContent, mergedContent);
|
|
1056
|
-
const confirm = await promptConfirm("Apply these changes?");
|
|
1057
|
-
if (!confirm) {
|
|
1058
|
-
console.log("Skipped");
|
|
1059
|
-
return;
|
|
1060
|
-
}
|
|
1061
|
-
fs4.writeFileSync(targetPath, mergedContent);
|
|
1062
|
-
console.log(`Updated ${targetPath}`);
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
1007
|
// src/commands/verify/init.ts
|
|
1066
|
-
import
|
|
1008
|
+
import chalk24 from "chalk";
|
|
1067
1009
|
|
|
1068
1010
|
// src/shared/promptMultiselect.ts
|
|
1069
|
-
import
|
|
1011
|
+
import chalk14 from "chalk";
|
|
1070
1012
|
import enquirer3 from "enquirer";
|
|
1071
1013
|
async function promptMultiselect(message, options) {
|
|
1072
1014
|
const { selected } = await enquirer3.prompt({
|
|
@@ -1075,7 +1017,7 @@ async function promptMultiselect(message, options) {
|
|
|
1075
1017
|
message,
|
|
1076
1018
|
choices: options.map((opt) => ({
|
|
1077
1019
|
name: opt.value,
|
|
1078
|
-
message: `${opt.name} - ${
|
|
1020
|
+
message: `${opt.name} - ${chalk14.dim(opt.description)}`
|
|
1079
1021
|
})),
|
|
1080
1022
|
// @ts-expect-error - enquirer types don't include symbols but it's supported
|
|
1081
1023
|
symbols: {
|
|
@@ -1089,23 +1031,23 @@ async function promptMultiselect(message, options) {
|
|
|
1089
1031
|
}
|
|
1090
1032
|
|
|
1091
1033
|
// src/shared/readPackageJson.ts
|
|
1092
|
-
import * as
|
|
1093
|
-
import * as
|
|
1094
|
-
import
|
|
1034
|
+
import * as fs4 from "fs";
|
|
1035
|
+
import * as path2 from "path";
|
|
1036
|
+
import chalk15 from "chalk";
|
|
1095
1037
|
function findPackageJson() {
|
|
1096
|
-
const packageJsonPath =
|
|
1097
|
-
if (
|
|
1038
|
+
const packageJsonPath = path2.join(process.cwd(), "package.json");
|
|
1039
|
+
if (fs4.existsSync(packageJsonPath)) {
|
|
1098
1040
|
return packageJsonPath;
|
|
1099
1041
|
}
|
|
1100
1042
|
return null;
|
|
1101
1043
|
}
|
|
1102
1044
|
function readPackageJson(filePath) {
|
|
1103
|
-
return JSON.parse(
|
|
1045
|
+
return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
|
|
1104
1046
|
}
|
|
1105
1047
|
function requirePackageJson() {
|
|
1106
1048
|
const packageJsonPath = findPackageJson();
|
|
1107
1049
|
if (!packageJsonPath) {
|
|
1108
|
-
console.error(
|
|
1050
|
+
console.error(chalk15.red("No package.json found in current directory"));
|
|
1109
1051
|
process.exit(1);
|
|
1110
1052
|
}
|
|
1111
1053
|
const pkg = readPackageJson(packageJsonPath);
|
|
@@ -1114,9 +1056,9 @@ function requirePackageJson() {
|
|
|
1114
1056
|
function findPackageJsonWithVerifyScripts(startDir) {
|
|
1115
1057
|
let currentDir = startDir;
|
|
1116
1058
|
while (true) {
|
|
1117
|
-
const packageJsonPath =
|
|
1118
|
-
if (
|
|
1119
|
-
const packageJson = JSON.parse(
|
|
1059
|
+
const packageJsonPath = path2.join(currentDir, "package.json");
|
|
1060
|
+
if (fs4.existsSync(packageJsonPath)) {
|
|
1061
|
+
const packageJson = JSON.parse(fs4.readFileSync(packageJsonPath, "utf-8"));
|
|
1120
1062
|
const scripts = packageJson.scripts || {};
|
|
1121
1063
|
const verifyScripts = Object.keys(scripts).filter(
|
|
1122
1064
|
(name) => name.startsWith("verify:")
|
|
@@ -1125,7 +1067,7 @@ function findPackageJsonWithVerifyScripts(startDir) {
|
|
|
1125
1067
|
return { packageJsonPath, verifyScripts };
|
|
1126
1068
|
}
|
|
1127
1069
|
}
|
|
1128
|
-
const parentDir =
|
|
1070
|
+
const parentDir = path2.dirname(currentDir);
|
|
1129
1071
|
if (parentDir === currentDir) {
|
|
1130
1072
|
return null;
|
|
1131
1073
|
}
|
|
@@ -1143,15 +1085,15 @@ var expectedScripts = {
|
|
|
1143
1085
|
};
|
|
1144
1086
|
|
|
1145
1087
|
// src/commands/verify/setup/setupBuild.ts
|
|
1146
|
-
import
|
|
1088
|
+
import chalk17 from "chalk";
|
|
1147
1089
|
|
|
1148
1090
|
// src/commands/verify/installPackage.ts
|
|
1149
1091
|
import { execSync as execSync7 } from "child_process";
|
|
1150
|
-
import * as
|
|
1151
|
-
import * as
|
|
1152
|
-
import
|
|
1092
|
+
import * as fs5 from "fs";
|
|
1093
|
+
import * as path3 from "path";
|
|
1094
|
+
import chalk16 from "chalk";
|
|
1153
1095
|
function writePackageJson(filePath, pkg) {
|
|
1154
|
-
|
|
1096
|
+
fs5.writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}
|
|
1155
1097
|
`);
|
|
1156
1098
|
}
|
|
1157
1099
|
function addScript(pkg, name, command) {
|
|
@@ -1164,36 +1106,36 @@ function addScript(pkg, name, command) {
|
|
|
1164
1106
|
};
|
|
1165
1107
|
}
|
|
1166
1108
|
function installPackage(name, cwd) {
|
|
1167
|
-
console.log(
|
|
1109
|
+
console.log(chalk16.dim(`Installing ${name}...`));
|
|
1168
1110
|
try {
|
|
1169
1111
|
execSync7(`npm install -D ${name}`, { stdio: "inherit", cwd });
|
|
1170
1112
|
return true;
|
|
1171
1113
|
} catch {
|
|
1172
|
-
console.error(
|
|
1114
|
+
console.error(chalk16.red(`Failed to install ${name}`));
|
|
1173
1115
|
return false;
|
|
1174
1116
|
}
|
|
1175
1117
|
}
|
|
1176
1118
|
function addToKnipIgnoreBinaries(cwd, binary) {
|
|
1177
|
-
const knipJsonPath =
|
|
1119
|
+
const knipJsonPath = path3.join(cwd, "knip.json");
|
|
1178
1120
|
try {
|
|
1179
1121
|
let knipConfig;
|
|
1180
|
-
if (
|
|
1181
|
-
knipConfig = JSON.parse(
|
|
1122
|
+
if (fs5.existsSync(knipJsonPath)) {
|
|
1123
|
+
knipConfig = JSON.parse(fs5.readFileSync(knipJsonPath, "utf-8"));
|
|
1182
1124
|
} else {
|
|
1183
1125
|
knipConfig = { $schema: "https://unpkg.com/knip@5/schema.json" };
|
|
1184
1126
|
}
|
|
1185
1127
|
const ignoreBinaries = knipConfig.ignoreBinaries ?? [];
|
|
1186
1128
|
if (!ignoreBinaries.includes(binary)) {
|
|
1187
1129
|
knipConfig.ignoreBinaries = [...ignoreBinaries, binary];
|
|
1188
|
-
|
|
1130
|
+
fs5.writeFileSync(
|
|
1189
1131
|
knipJsonPath,
|
|
1190
1132
|
`${JSON.stringify(knipConfig, null, " ")}
|
|
1191
1133
|
`
|
|
1192
1134
|
);
|
|
1193
|
-
console.log(
|
|
1135
|
+
console.log(chalk16.dim(`Added '${binary}' to knip.json ignoreBinaries`));
|
|
1194
1136
|
}
|
|
1195
1137
|
} catch {
|
|
1196
|
-
console.log(
|
|
1138
|
+
console.log(chalk16.yellow("Warning: Could not update knip.json"));
|
|
1197
1139
|
}
|
|
1198
1140
|
}
|
|
1199
1141
|
function setupVerifyScript(packageJsonPath, scriptName, command) {
|
|
@@ -1205,7 +1147,7 @@ function setupVerifyScript(packageJsonPath, scriptName, command) {
|
|
|
1205
1147
|
|
|
1206
1148
|
// src/commands/verify/setup/setupBuild.ts
|
|
1207
1149
|
async function setupBuild(packageJsonPath, hasVite, hasTypescript) {
|
|
1208
|
-
console.log(
|
|
1150
|
+
console.log(chalk17.blue("\nSetting up build verification..."));
|
|
1209
1151
|
let command;
|
|
1210
1152
|
if (hasVite && hasTypescript) {
|
|
1211
1153
|
command = "tsc -b && vite build --logLevel error";
|
|
@@ -1214,17 +1156,17 @@ async function setupBuild(packageJsonPath, hasVite, hasTypescript) {
|
|
|
1214
1156
|
} else {
|
|
1215
1157
|
command = "tsc --noEmit";
|
|
1216
1158
|
}
|
|
1217
|
-
console.log(
|
|
1159
|
+
console.log(chalk17.dim(`Using: ${command}`));
|
|
1218
1160
|
const pkg = readPackageJson(packageJsonPath);
|
|
1219
1161
|
writePackageJson(packageJsonPath, addScript(pkg, "verify:build", command));
|
|
1220
1162
|
}
|
|
1221
1163
|
|
|
1222
1164
|
// src/commands/verify/setup/setupDuplicateCode.ts
|
|
1223
|
-
import * as
|
|
1224
|
-
import
|
|
1165
|
+
import * as path4 from "path";
|
|
1166
|
+
import chalk18 from "chalk";
|
|
1225
1167
|
async function setupDuplicateCode(packageJsonPath) {
|
|
1226
|
-
console.log(
|
|
1227
|
-
const cwd =
|
|
1168
|
+
console.log(chalk18.blue("\nSetting up jscpd..."));
|
|
1169
|
+
const cwd = path4.dirname(packageJsonPath);
|
|
1228
1170
|
const pkg = readPackageJson(packageJsonPath);
|
|
1229
1171
|
const hasJscpd = !!pkg.dependencies?.jscpd || !!pkg.devDependencies?.jscpd;
|
|
1230
1172
|
if (!hasJscpd && !installPackage("jscpd", cwd)) {
|
|
@@ -1238,11 +1180,11 @@ async function setupDuplicateCode(packageJsonPath) {
|
|
|
1238
1180
|
}
|
|
1239
1181
|
|
|
1240
1182
|
// src/commands/verify/setup/setupHardcodedColors.ts
|
|
1241
|
-
import * as
|
|
1242
|
-
import
|
|
1183
|
+
import * as path5 from "path";
|
|
1184
|
+
import chalk19 from "chalk";
|
|
1243
1185
|
async function setupHardcodedColors(packageJsonPath, hasOpenColor) {
|
|
1244
|
-
console.log(
|
|
1245
|
-
const cwd =
|
|
1186
|
+
console.log(chalk19.blue("\nSetting up hardcoded colors check..."));
|
|
1187
|
+
const cwd = path5.dirname(packageJsonPath);
|
|
1246
1188
|
if (!hasOpenColor) {
|
|
1247
1189
|
installPackage("open-color", cwd);
|
|
1248
1190
|
}
|
|
@@ -1255,11 +1197,11 @@ async function setupHardcodedColors(packageJsonPath, hasOpenColor) {
|
|
|
1255
1197
|
}
|
|
1256
1198
|
|
|
1257
1199
|
// src/commands/verify/setup/setupKnip.ts
|
|
1258
|
-
import * as
|
|
1259
|
-
import
|
|
1200
|
+
import * as path6 from "path";
|
|
1201
|
+
import chalk20 from "chalk";
|
|
1260
1202
|
async function setupKnip(packageJsonPath) {
|
|
1261
|
-
console.log(
|
|
1262
|
-
const cwd =
|
|
1203
|
+
console.log(chalk20.blue("\nSetting up knip..."));
|
|
1204
|
+
const cwd = path6.dirname(packageJsonPath);
|
|
1263
1205
|
const pkg = readPackageJson(packageJsonPath);
|
|
1264
1206
|
if (!pkg.devDependencies?.knip && !installPackage("knip", cwd)) {
|
|
1265
1207
|
return;
|
|
@@ -1272,19 +1214,19 @@ async function setupKnip(packageJsonPath) {
|
|
|
1272
1214
|
}
|
|
1273
1215
|
|
|
1274
1216
|
// src/commands/verify/setup/setupLint.ts
|
|
1275
|
-
import * as
|
|
1276
|
-
import
|
|
1217
|
+
import * as path7 from "path";
|
|
1218
|
+
import chalk22 from "chalk";
|
|
1277
1219
|
|
|
1278
1220
|
// src/commands/lint/init.ts
|
|
1279
1221
|
import { execSync as execSync9 } from "child_process";
|
|
1280
|
-
import { existsSync as
|
|
1281
|
-
import { dirname as
|
|
1282
|
-
import { fileURLToPath as
|
|
1283
|
-
import
|
|
1222
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
1223
|
+
import { dirname as dirname6, join as join6 } from "path";
|
|
1224
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1225
|
+
import chalk21 from "chalk";
|
|
1284
1226
|
|
|
1285
1227
|
// src/shared/removeEslint.ts
|
|
1286
1228
|
import { execSync as execSync8 } from "child_process";
|
|
1287
|
-
import { existsSync as
|
|
1229
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
|
|
1288
1230
|
function removeEslint(options = {}) {
|
|
1289
1231
|
const removedFromPackageJson = removeEslintFromPackageJson(options);
|
|
1290
1232
|
const removedConfigFiles = removeEslintConfigFiles();
|
|
@@ -1297,10 +1239,10 @@ function removeEslint(options = {}) {
|
|
|
1297
1239
|
}
|
|
1298
1240
|
function removeEslintFromPackageJson(options) {
|
|
1299
1241
|
const packageJsonPath = "package.json";
|
|
1300
|
-
if (!
|
|
1242
|
+
if (!existsSync6(packageJsonPath)) {
|
|
1301
1243
|
return false;
|
|
1302
1244
|
}
|
|
1303
|
-
const packageJson = JSON.parse(
|
|
1245
|
+
const packageJson = JSON.parse(readFileSync7(packageJsonPath, "utf-8"));
|
|
1304
1246
|
let modified = false;
|
|
1305
1247
|
if (packageJson.dependencies) {
|
|
1306
1248
|
for (const key of Object.keys(packageJson.dependencies)) {
|
|
@@ -1328,7 +1270,7 @@ function removeEslintFromPackageJson(options) {
|
|
|
1328
1270
|
}
|
|
1329
1271
|
}
|
|
1330
1272
|
if (modified) {
|
|
1331
|
-
|
|
1273
|
+
writeFileSync5(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}
|
|
1332
1274
|
`);
|
|
1333
1275
|
console.log("Removed eslint references from package.json");
|
|
1334
1276
|
}
|
|
@@ -1349,7 +1291,7 @@ function removeEslintConfigFiles() {
|
|
|
1349
1291
|
];
|
|
1350
1292
|
let removed = false;
|
|
1351
1293
|
for (const configFile of eslintConfigFiles) {
|
|
1352
|
-
if (
|
|
1294
|
+
if (existsSync6(configFile)) {
|
|
1353
1295
|
unlinkSync(configFile);
|
|
1354
1296
|
console.log(`Removed ${configFile}`);
|
|
1355
1297
|
removed = true;
|
|
@@ -1359,21 +1301,21 @@ function removeEslintConfigFiles() {
|
|
|
1359
1301
|
}
|
|
1360
1302
|
|
|
1361
1303
|
// src/commands/lint/init.ts
|
|
1362
|
-
var
|
|
1304
|
+
var __dirname3 = dirname6(fileURLToPath2(import.meta.url));
|
|
1363
1305
|
async function init2() {
|
|
1364
1306
|
removeEslint();
|
|
1365
1307
|
const biomeConfigPath = "biome.json";
|
|
1366
|
-
if (!
|
|
1308
|
+
if (!existsSync7(biomeConfigPath)) {
|
|
1367
1309
|
console.log("Initializing Biome...");
|
|
1368
1310
|
execSync9("npx @biomejs/biome init", { stdio: "inherit" });
|
|
1369
1311
|
}
|
|
1370
|
-
if (!
|
|
1312
|
+
if (!existsSync7(biomeConfigPath)) {
|
|
1371
1313
|
console.log("No biome.json found, skipping linter config");
|
|
1372
1314
|
return;
|
|
1373
1315
|
}
|
|
1374
|
-
const linterConfigPath =
|
|
1375
|
-
const linterConfig = JSON.parse(
|
|
1376
|
-
const biomeConfig = JSON.parse(
|
|
1316
|
+
const linterConfigPath = join6(__dirname3, "commands/lint/biome.linter.json");
|
|
1317
|
+
const linterConfig = JSON.parse(readFileSync8(linterConfigPath, "utf-8"));
|
|
1318
|
+
const biomeConfig = JSON.parse(readFileSync8(biomeConfigPath, "utf-8"));
|
|
1377
1319
|
const oldContent = `${JSON.stringify(biomeConfig, null, 2)}
|
|
1378
1320
|
`;
|
|
1379
1321
|
biomeConfig.linter = linterConfig.linter;
|
|
@@ -1386,22 +1328,22 @@ async function init2() {
|
|
|
1386
1328
|
console.log("biome.json already has the correct linter config");
|
|
1387
1329
|
return;
|
|
1388
1330
|
}
|
|
1389
|
-
console.log(
|
|
1331
|
+
console.log(chalk21.yellow("\n\u26A0\uFE0F biome.json will be updated:"));
|
|
1390
1332
|
console.log();
|
|
1391
1333
|
printDiff(oldContent, newContent);
|
|
1392
|
-
const confirm = await promptConfirm(
|
|
1334
|
+
const confirm = await promptConfirm(chalk21.red("Update biome.json?"));
|
|
1393
1335
|
if (!confirm) {
|
|
1394
1336
|
console.log("Skipped biome.json update");
|
|
1395
1337
|
return;
|
|
1396
1338
|
}
|
|
1397
|
-
|
|
1339
|
+
writeFileSync6(biomeConfigPath, newContent);
|
|
1398
1340
|
console.log("Updated biome.json with linter config");
|
|
1399
1341
|
}
|
|
1400
1342
|
|
|
1401
1343
|
// src/commands/verify/setup/setupLint.ts
|
|
1402
1344
|
async function setupLint(packageJsonPath) {
|
|
1403
|
-
console.log(
|
|
1404
|
-
const cwd =
|
|
1345
|
+
console.log(chalk22.blue("\nSetting up biome..."));
|
|
1346
|
+
const cwd = path7.dirname(packageJsonPath);
|
|
1405
1347
|
const pkg = readPackageJson(packageJsonPath);
|
|
1406
1348
|
if (!pkg.devDependencies?.["@biomejs/biome"]) {
|
|
1407
1349
|
if (!installPackage("@biomejs/biome", cwd)) {
|
|
@@ -1417,11 +1359,11 @@ async function setupLint(packageJsonPath) {
|
|
|
1417
1359
|
}
|
|
1418
1360
|
|
|
1419
1361
|
// src/commands/verify/setup/setupTest.ts
|
|
1420
|
-
import * as
|
|
1421
|
-
import
|
|
1362
|
+
import * as path8 from "path";
|
|
1363
|
+
import chalk23 from "chalk";
|
|
1422
1364
|
async function setupTest(packageJsonPath) {
|
|
1423
|
-
console.log(
|
|
1424
|
-
const cwd =
|
|
1365
|
+
console.log(chalk23.blue("\nSetting up vitest..."));
|
|
1366
|
+
const cwd = path8.dirname(packageJsonPath);
|
|
1425
1367
|
const pkg = readPackageJson(packageJsonPath);
|
|
1426
1368
|
if (!pkg.devDependencies?.vitest && !installPackage("vitest", cwd)) {
|
|
1427
1369
|
return;
|
|
@@ -1563,16 +1505,16 @@ async function init3() {
|
|
|
1563
1505
|
const setup = detectExistingSetup(pkg);
|
|
1564
1506
|
const availableOptions = getAvailableOptions(setup);
|
|
1565
1507
|
if (availableOptions.length === 0) {
|
|
1566
|
-
console.log(
|
|
1508
|
+
console.log(chalk24.green("All verify scripts are already configured!"));
|
|
1567
1509
|
return;
|
|
1568
1510
|
}
|
|
1569
|
-
console.log(
|
|
1511
|
+
console.log(chalk24.bold("Available verify scripts to add:\n"));
|
|
1570
1512
|
const selected = await promptMultiselect(
|
|
1571
1513
|
"Select verify scripts to add:",
|
|
1572
1514
|
availableOptions
|
|
1573
1515
|
);
|
|
1574
1516
|
if (selected.length === 0) {
|
|
1575
|
-
console.log(
|
|
1517
|
+
console.log(chalk24.yellow("No scripts selected"));
|
|
1576
1518
|
return;
|
|
1577
1519
|
}
|
|
1578
1520
|
for (const choice of selected) {
|
|
@@ -1597,43 +1539,43 @@ async function init3() {
|
|
|
1597
1539
|
break;
|
|
1598
1540
|
}
|
|
1599
1541
|
}
|
|
1600
|
-
console.log(
|
|
1542
|
+
console.log(chalk24.green(`
|
|
1601
1543
|
Added ${selected.length} verify script(s):`));
|
|
1602
1544
|
for (const choice of selected) {
|
|
1603
|
-
console.log(
|
|
1545
|
+
console.log(chalk24.green(` - verify:${choice}`));
|
|
1604
1546
|
}
|
|
1605
|
-
console.log(
|
|
1547
|
+
console.log(chalk24.dim("\nRun 'assist verify' to run all verify scripts"));
|
|
1606
1548
|
}
|
|
1607
1549
|
|
|
1608
1550
|
// src/commands/vscode/init.ts
|
|
1609
|
-
import * as fs8 from "fs";
|
|
1610
|
-
import * as path11 from "path";
|
|
1611
|
-
import chalk27 from "chalk";
|
|
1612
|
-
|
|
1613
|
-
// src/commands/vscode/createLaunchJson.ts
|
|
1614
1551
|
import * as fs7 from "fs";
|
|
1615
1552
|
import * as path10 from "path";
|
|
1616
1553
|
import chalk26 from "chalk";
|
|
1554
|
+
|
|
1555
|
+
// src/commands/vscode/createLaunchJson.ts
|
|
1556
|
+
import * as fs6 from "fs";
|
|
1557
|
+
import * as path9 from "path";
|
|
1558
|
+
import chalk25 from "chalk";
|
|
1617
1559
|
function ensureVscodeFolder() {
|
|
1618
|
-
const vscodeDir =
|
|
1619
|
-
if (!
|
|
1620
|
-
|
|
1621
|
-
console.log(
|
|
1560
|
+
const vscodeDir = path9.join(process.cwd(), ".vscode");
|
|
1561
|
+
if (!fs6.existsSync(vscodeDir)) {
|
|
1562
|
+
fs6.mkdirSync(vscodeDir);
|
|
1563
|
+
console.log(chalk25.dim("Created .vscode folder"));
|
|
1622
1564
|
}
|
|
1623
1565
|
}
|
|
1624
1566
|
function removeVscodeFromGitignore() {
|
|
1625
|
-
const gitignorePath =
|
|
1626
|
-
if (!
|
|
1567
|
+
const gitignorePath = path9.join(process.cwd(), ".gitignore");
|
|
1568
|
+
if (!fs6.existsSync(gitignorePath)) {
|
|
1627
1569
|
return;
|
|
1628
1570
|
}
|
|
1629
|
-
const content =
|
|
1571
|
+
const content = fs6.readFileSync(gitignorePath, "utf-8");
|
|
1630
1572
|
const lines = content.split("\n");
|
|
1631
1573
|
const filteredLines = lines.filter(
|
|
1632
1574
|
(line) => !line.trim().toLowerCase().includes(".vscode")
|
|
1633
1575
|
);
|
|
1634
1576
|
if (filteredLines.length !== lines.length) {
|
|
1635
|
-
|
|
1636
|
-
console.log(
|
|
1577
|
+
fs6.writeFileSync(gitignorePath, filteredLines.join("\n"));
|
|
1578
|
+
console.log(chalk25.dim("Removed .vscode references from .gitignore"));
|
|
1637
1579
|
}
|
|
1638
1580
|
}
|
|
1639
1581
|
function createLaunchJson() {
|
|
@@ -1648,10 +1590,10 @@ function createLaunchJson() {
|
|
|
1648
1590
|
}
|
|
1649
1591
|
]
|
|
1650
1592
|
};
|
|
1651
|
-
const launchPath =
|
|
1652
|
-
|
|
1593
|
+
const launchPath = path9.join(process.cwd(), ".vscode", "launch.json");
|
|
1594
|
+
fs6.writeFileSync(launchPath, `${JSON.stringify(launchConfig, null, " ")}
|
|
1653
1595
|
`);
|
|
1654
|
-
console.log(
|
|
1596
|
+
console.log(chalk25.green("Created .vscode/launch.json"));
|
|
1655
1597
|
}
|
|
1656
1598
|
function createSettingsJson() {
|
|
1657
1599
|
const settings = {
|
|
@@ -1661,31 +1603,31 @@ function createSettingsJson() {
|
|
|
1661
1603
|
"source.organizeImports.biome": "explicit"
|
|
1662
1604
|
}
|
|
1663
1605
|
};
|
|
1664
|
-
const settingsPath =
|
|
1665
|
-
|
|
1606
|
+
const settingsPath = path9.join(process.cwd(), ".vscode", "settings.json");
|
|
1607
|
+
fs6.writeFileSync(settingsPath, `${JSON.stringify(settings, null, " ")}
|
|
1666
1608
|
`);
|
|
1667
|
-
console.log(
|
|
1609
|
+
console.log(chalk25.green("Created .vscode/settings.json"));
|
|
1668
1610
|
}
|
|
1669
1611
|
function createExtensionsJson() {
|
|
1670
1612
|
const extensions = {
|
|
1671
1613
|
recommendations: ["biomejs.biome"]
|
|
1672
1614
|
};
|
|
1673
|
-
const extensionsPath =
|
|
1674
|
-
|
|
1615
|
+
const extensionsPath = path9.join(process.cwd(), ".vscode", "extensions.json");
|
|
1616
|
+
fs6.writeFileSync(
|
|
1675
1617
|
extensionsPath,
|
|
1676
1618
|
`${JSON.stringify(extensions, null, " ")}
|
|
1677
1619
|
`
|
|
1678
1620
|
);
|
|
1679
|
-
console.log(
|
|
1621
|
+
console.log(chalk25.green("Created .vscode/extensions.json"));
|
|
1680
1622
|
}
|
|
1681
1623
|
|
|
1682
1624
|
// src/commands/vscode/init.ts
|
|
1683
1625
|
function detectExistingSetup2(pkg) {
|
|
1684
|
-
const vscodeDir =
|
|
1626
|
+
const vscodeDir = path10.join(process.cwd(), ".vscode");
|
|
1685
1627
|
return {
|
|
1686
|
-
hasVscodeFolder:
|
|
1687
|
-
hasLaunchJson:
|
|
1688
|
-
hasSettingsJson:
|
|
1628
|
+
hasVscodeFolder: fs7.existsSync(vscodeDir),
|
|
1629
|
+
hasLaunchJson: fs7.existsSync(path10.join(vscodeDir, "launch.json")),
|
|
1630
|
+
hasSettingsJson: fs7.existsSync(path10.join(vscodeDir, "settings.json")),
|
|
1689
1631
|
hasVite: !!pkg.devDependencies?.vite || !!pkg.dependencies?.vite
|
|
1690
1632
|
};
|
|
1691
1633
|
}
|
|
@@ -1708,16 +1650,16 @@ async function init4() {
|
|
|
1708
1650
|
});
|
|
1709
1651
|
}
|
|
1710
1652
|
if (availableOptions.length === 0) {
|
|
1711
|
-
console.log(
|
|
1653
|
+
console.log(chalk26.green("VS Code configuration already exists!"));
|
|
1712
1654
|
return;
|
|
1713
1655
|
}
|
|
1714
|
-
console.log(
|
|
1656
|
+
console.log(chalk26.bold("Available VS Code configurations to add:\n"));
|
|
1715
1657
|
const selected = await promptMultiselect(
|
|
1716
1658
|
"Select configurations to add:",
|
|
1717
1659
|
availableOptions
|
|
1718
1660
|
);
|
|
1719
1661
|
if (selected.length === 0) {
|
|
1720
|
-
console.log(
|
|
1662
|
+
console.log(chalk26.yellow("No configurations selected"));
|
|
1721
1663
|
return;
|
|
1722
1664
|
}
|
|
1723
1665
|
removeVscodeFromGitignore();
|
|
@@ -1734,7 +1676,7 @@ async function init4() {
|
|
|
1734
1676
|
}
|
|
1735
1677
|
}
|
|
1736
1678
|
console.log(
|
|
1737
|
-
|
|
1679
|
+
chalk26.green(`
|
|
1738
1680
|
Added ${selected.length} VS Code configuration(s)`)
|
|
1739
1681
|
);
|
|
1740
1682
|
}
|
|
@@ -1746,23 +1688,23 @@ async function init5() {
|
|
|
1746
1688
|
}
|
|
1747
1689
|
|
|
1748
1690
|
// src/commands/lint/runFileNameCheck.ts
|
|
1749
|
-
import fs10 from "fs";
|
|
1750
|
-
import path13 from "path";
|
|
1751
|
-
import chalk28 from "chalk";
|
|
1752
|
-
|
|
1753
|
-
// src/shared/findSourceFiles.ts
|
|
1754
1691
|
import fs9 from "fs";
|
|
1755
1692
|
import path12 from "path";
|
|
1693
|
+
import chalk27 from "chalk";
|
|
1694
|
+
|
|
1695
|
+
// src/shared/findSourceFiles.ts
|
|
1696
|
+
import fs8 from "fs";
|
|
1697
|
+
import path11 from "path";
|
|
1756
1698
|
var EXTENSIONS = [".ts", ".tsx"];
|
|
1757
1699
|
function findSourceFiles(dir, options = {}) {
|
|
1758
1700
|
const { includeTests = true } = options;
|
|
1759
1701
|
const results = [];
|
|
1760
|
-
if (!
|
|
1702
|
+
if (!fs8.existsSync(dir)) {
|
|
1761
1703
|
return results;
|
|
1762
1704
|
}
|
|
1763
|
-
const entries =
|
|
1705
|
+
const entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
1764
1706
|
for (const entry of entries) {
|
|
1765
|
-
const fullPath =
|
|
1707
|
+
const fullPath = path11.join(dir, entry.name);
|
|
1766
1708
|
if (entry.isDirectory() && entry.name !== "node_modules") {
|
|
1767
1709
|
results.push(...findSourceFiles(fullPath, options));
|
|
1768
1710
|
} else if (entry.isFile() && EXTENSIONS.some((ext) => entry.name.endsWith(ext))) {
|
|
@@ -1786,10 +1728,10 @@ function checkFileNames() {
|
|
|
1786
1728
|
const sourceFiles = findSourceFiles("src");
|
|
1787
1729
|
const violations = [];
|
|
1788
1730
|
for (const filePath of sourceFiles) {
|
|
1789
|
-
const fileName =
|
|
1731
|
+
const fileName = path12.basename(filePath);
|
|
1790
1732
|
const nameWithoutExt = fileName.replace(/\.(ts|tsx)$/, "");
|
|
1791
1733
|
if (/^[A-Z]/.test(nameWithoutExt)) {
|
|
1792
|
-
const content =
|
|
1734
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
1793
1735
|
if (!hasClassOrComponent(content)) {
|
|
1794
1736
|
violations.push({ filePath, fileName });
|
|
1795
1737
|
}
|
|
@@ -1800,16 +1742,16 @@ function checkFileNames() {
|
|
|
1800
1742
|
function runFileNameCheck() {
|
|
1801
1743
|
const violations = checkFileNames();
|
|
1802
1744
|
if (violations.length > 0) {
|
|
1803
|
-
console.error(
|
|
1745
|
+
console.error(chalk27.red("\nFile name check failed:\n"));
|
|
1804
1746
|
console.error(
|
|
1805
|
-
|
|
1747
|
+
chalk27.red(
|
|
1806
1748
|
" Files without classes or React components should not start with a capital letter.\n"
|
|
1807
1749
|
)
|
|
1808
1750
|
);
|
|
1809
1751
|
for (const violation of violations) {
|
|
1810
|
-
console.error(
|
|
1752
|
+
console.error(chalk27.red(` ${violation.filePath}`));
|
|
1811
1753
|
console.error(
|
|
1812
|
-
|
|
1754
|
+
chalk27.gray(
|
|
1813
1755
|
` Rename to: ${violation.fileName.charAt(0).toLowerCase()}${violation.fileName.slice(1)}
|
|
1814
1756
|
`
|
|
1815
1757
|
)
|
|
@@ -1826,20 +1768,20 @@ function runFileNameCheck() {
|
|
|
1826
1768
|
}
|
|
1827
1769
|
|
|
1828
1770
|
// src/commands/lint/runImportExtensionCheck.ts
|
|
1829
|
-
import
|
|
1771
|
+
import fs10 from "fs";
|
|
1830
1772
|
|
|
1831
1773
|
// src/commands/lint/shared.ts
|
|
1832
|
-
import
|
|
1774
|
+
import chalk28 from "chalk";
|
|
1833
1775
|
function reportViolations(violations, checkName, errorMessage, successMessage) {
|
|
1834
1776
|
if (violations.length > 0) {
|
|
1835
|
-
console.error(
|
|
1777
|
+
console.error(chalk28.red(`
|
|
1836
1778
|
${checkName} failed:
|
|
1837
1779
|
`));
|
|
1838
|
-
console.error(
|
|
1780
|
+
console.error(chalk28.red(` ${errorMessage}
|
|
1839
1781
|
`));
|
|
1840
1782
|
for (const violation of violations) {
|
|
1841
|
-
console.error(
|
|
1842
|
-
console.error(
|
|
1783
|
+
console.error(chalk28.red(` ${violation.filePath}:${violation.line}`));
|
|
1784
|
+
console.error(chalk28.gray(` ${violation.content}
|
|
1843
1785
|
`));
|
|
1844
1786
|
}
|
|
1845
1787
|
return false;
|
|
@@ -1852,7 +1794,7 @@ ${checkName} failed:
|
|
|
1852
1794
|
|
|
1853
1795
|
// src/commands/lint/runImportExtensionCheck.ts
|
|
1854
1796
|
function checkForImportExtensions(filePath) {
|
|
1855
|
-
const content =
|
|
1797
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
1856
1798
|
const lines = content.split("\n");
|
|
1857
1799
|
const violations = [];
|
|
1858
1800
|
const importExtensionPattern = /from\s+["']\..*\.(js|ts)["']/;
|
|
@@ -1886,9 +1828,9 @@ function runImportExtensionCheck() {
|
|
|
1886
1828
|
}
|
|
1887
1829
|
|
|
1888
1830
|
// src/commands/lint/runStaticImportCheck.ts
|
|
1889
|
-
import
|
|
1831
|
+
import fs11 from "fs";
|
|
1890
1832
|
function checkForDynamicImports(filePath) {
|
|
1891
|
-
const content =
|
|
1833
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
1892
1834
|
const lines = content.split("\n");
|
|
1893
1835
|
const violations = [];
|
|
1894
1836
|
const requirePattern = /\brequire\s*\(/;
|
|
@@ -1934,7 +1876,7 @@ function lint() {
|
|
|
1934
1876
|
|
|
1935
1877
|
// src/commands/new/newProject.ts
|
|
1936
1878
|
import { execSync as execSync10 } from "child_process";
|
|
1937
|
-
import { existsSync as
|
|
1879
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
1938
1880
|
async function newProject() {
|
|
1939
1881
|
console.log("Initializing Vite with react-ts template...");
|
|
1940
1882
|
execSync10("npm create vite@latest . -- --template react-ts", {
|
|
@@ -1947,11 +1889,11 @@ async function newProject() {
|
|
|
1947
1889
|
}
|
|
1948
1890
|
function addViteBaseConfig() {
|
|
1949
1891
|
const viteConfigPath = "vite.config.ts";
|
|
1950
|
-
if (!
|
|
1892
|
+
if (!existsSync10(viteConfigPath)) {
|
|
1951
1893
|
console.log("No vite.config.ts found, skipping base config");
|
|
1952
1894
|
return;
|
|
1953
1895
|
}
|
|
1954
|
-
const content =
|
|
1896
|
+
const content = readFileSync10(viteConfigPath, "utf-8");
|
|
1955
1897
|
if (content.includes("base:")) {
|
|
1956
1898
|
console.log("vite.config.ts already has base config");
|
|
1957
1899
|
return;
|
|
@@ -1961,7 +1903,7 @@ function addViteBaseConfig() {
|
|
|
1961
1903
|
'defineConfig({\n base: "./",'
|
|
1962
1904
|
);
|
|
1963
1905
|
if (updated !== content) {
|
|
1964
|
-
|
|
1906
|
+
writeFileSync8(viteConfigPath, updated);
|
|
1965
1907
|
console.log('Added base: "./" to vite.config.ts');
|
|
1966
1908
|
}
|
|
1967
1909
|
}
|
|
@@ -2002,19 +1944,19 @@ function detectPlatform() {
|
|
|
2002
1944
|
|
|
2003
1945
|
// src/commands/notify/showWindowsNotificationFromWsl.ts
|
|
2004
1946
|
import { spawn } from "child_process";
|
|
2005
|
-
import
|
|
1947
|
+
import fs12 from "fs";
|
|
2006
1948
|
import { createRequire } from "module";
|
|
2007
|
-
import
|
|
1949
|
+
import path13 from "path";
|
|
2008
1950
|
var require2 = createRequire(import.meta.url);
|
|
2009
1951
|
function getSnoreToastPath() {
|
|
2010
|
-
const notifierPath =
|
|
2011
|
-
return
|
|
1952
|
+
const notifierPath = path13.dirname(require2.resolve("node-notifier"));
|
|
1953
|
+
return path13.join(notifierPath, "vendor", "snoreToast", "snoretoast-x64.exe");
|
|
2012
1954
|
}
|
|
2013
1955
|
function showWindowsNotificationFromWsl(options) {
|
|
2014
1956
|
const { title, message, sound } = options;
|
|
2015
1957
|
const snoreToastPath = getSnoreToastPath();
|
|
2016
1958
|
try {
|
|
2017
|
-
|
|
1959
|
+
fs12.chmodSync(snoreToastPath, 493);
|
|
2018
1960
|
} catch {
|
|
2019
1961
|
}
|
|
2020
1962
|
const args = ["-t", title, "-m", message];
|
|
@@ -2084,11 +2026,17 @@ async function notify() {
|
|
|
2084
2026
|
console.log(`Notification sent: ${notification_type} for ${projectName}`);
|
|
2085
2027
|
}
|
|
2086
2028
|
|
|
2087
|
-
// src/commands/prs.ts
|
|
2029
|
+
// src/commands/prs/comments.ts
|
|
2030
|
+
import { execSync as execSync12 } from "child_process";
|
|
2031
|
+
import chalk29 from "chalk";
|
|
2032
|
+
|
|
2033
|
+
// src/lib/isClaudeCode.ts
|
|
2034
|
+
function isClaudeCode() {
|
|
2035
|
+
return process.env.CLAUDECODE !== void 0;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
// src/commands/prs/shared.ts
|
|
2088
2039
|
import { execSync as execSync11 } from "child_process";
|
|
2089
|
-
import chalk30 from "chalk";
|
|
2090
|
-
import enquirer4 from "enquirer";
|
|
2091
|
-
var PAGE_SIZE = 10;
|
|
2092
2040
|
function isGhNotInstalled(error) {
|
|
2093
2041
|
if (error instanceof Error) {
|
|
2094
2042
|
const msg = error.message.toLowerCase();
|
|
@@ -2108,10 +2056,104 @@ function getRepoInfo() {
|
|
|
2108
2056
|
);
|
|
2109
2057
|
return { org: repoInfo.owner.login, repo: repoInfo.name };
|
|
2110
2058
|
}
|
|
2059
|
+
|
|
2060
|
+
// src/commands/prs/comments.ts
|
|
2061
|
+
function formatForHuman(comment) {
|
|
2062
|
+
if (comment.type === "review") {
|
|
2063
|
+
const stateColor = comment.state === "APPROVED" ? chalk29.green : comment.state === "CHANGES_REQUESTED" ? chalk29.red : chalk29.yellow;
|
|
2064
|
+
return [
|
|
2065
|
+
`${chalk29.cyan("Review")} by ${chalk29.bold(comment.user)} ${stateColor(`[${comment.state}]`)}`,
|
|
2066
|
+
comment.body,
|
|
2067
|
+
""
|
|
2068
|
+
].join("\n");
|
|
2069
|
+
}
|
|
2070
|
+
const location = comment.line ? `:${comment.line}` : "";
|
|
2071
|
+
return [
|
|
2072
|
+
`${chalk29.cyan("Line comment")} by ${chalk29.bold(comment.user)} on ${chalk29.dim(`${comment.path}${location}`)}`,
|
|
2073
|
+
chalk29.dim(comment.diff_hunk.split("\n").slice(-3).join("\n")),
|
|
2074
|
+
comment.body,
|
|
2075
|
+
""
|
|
2076
|
+
].join("\n");
|
|
2077
|
+
}
|
|
2078
|
+
function printComments(comments2) {
|
|
2079
|
+
if (comments2.length === 0) {
|
|
2080
|
+
console.log("No comments found.");
|
|
2081
|
+
return;
|
|
2082
|
+
}
|
|
2083
|
+
if (isClaudeCode()) {
|
|
2084
|
+
for (const comment of comments2) {
|
|
2085
|
+
console.log(JSON.stringify(comment));
|
|
2086
|
+
}
|
|
2087
|
+
} else {
|
|
2088
|
+
for (const comment of comments2) {
|
|
2089
|
+
console.log(formatForHuman(comment));
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
async function comments(prNumber) {
|
|
2094
|
+
try {
|
|
2095
|
+
const { org, repo } = getRepoInfo();
|
|
2096
|
+
const allComments = [];
|
|
2097
|
+
const reviewResult = execSync12(
|
|
2098
|
+
`gh api repos/${org}/${repo}/pulls/${prNumber}/reviews`,
|
|
2099
|
+
{ encoding: "utf-8" }
|
|
2100
|
+
);
|
|
2101
|
+
if (reviewResult.trim()) {
|
|
2102
|
+
const reviews = JSON.parse(reviewResult);
|
|
2103
|
+
for (const review of reviews) {
|
|
2104
|
+
if (review.body) {
|
|
2105
|
+
allComments.push({
|
|
2106
|
+
type: "review",
|
|
2107
|
+
id: review.id,
|
|
2108
|
+
user: review.user.login,
|
|
2109
|
+
state: review.state,
|
|
2110
|
+
body: review.body
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
const lineResult = execSync12(
|
|
2116
|
+
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments`,
|
|
2117
|
+
{ encoding: "utf-8" }
|
|
2118
|
+
);
|
|
2119
|
+
if (lineResult.trim()) {
|
|
2120
|
+
const lineComments = JSON.parse(lineResult);
|
|
2121
|
+
for (const comment of lineComments) {
|
|
2122
|
+
allComments.push({
|
|
2123
|
+
type: "line",
|
|
2124
|
+
id: comment.id,
|
|
2125
|
+
user: comment.user.login,
|
|
2126
|
+
path: comment.path,
|
|
2127
|
+
line: comment.line,
|
|
2128
|
+
body: comment.body,
|
|
2129
|
+
diff_hunk: comment.diff_hunk
|
|
2130
|
+
});
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
return allComments;
|
|
2134
|
+
} catch (error) {
|
|
2135
|
+
if (isGhNotInstalled(error)) {
|
|
2136
|
+
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
2137
|
+
console.error("Install it from https://cli.github.com/");
|
|
2138
|
+
return [];
|
|
2139
|
+
}
|
|
2140
|
+
if (isNotFound(error)) {
|
|
2141
|
+
console.error("Error: Pull request not found.");
|
|
2142
|
+
return [];
|
|
2143
|
+
}
|
|
2144
|
+
throw error;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
// src/commands/prs/prs.ts
|
|
2149
|
+
import { execSync as execSync13 } from "child_process";
|
|
2150
|
+
import chalk30 from "chalk";
|
|
2151
|
+
import enquirer4 from "enquirer";
|
|
2152
|
+
var PAGE_SIZE = 10;
|
|
2111
2153
|
async function prs(options) {
|
|
2112
2154
|
const state = options.open ? "open" : options.closed ? "closed" : "all";
|
|
2113
2155
|
try {
|
|
2114
|
-
const result =
|
|
2156
|
+
const result = execSync13(
|
|
2115
2157
|
`gh pr list --state ${state} --json number,title,url,author,createdAt,mergedAt,closedAt,state,changedFiles --limit 100`,
|
|
2116
2158
|
{ encoding: "utf-8" }
|
|
2117
2159
|
);
|
|
@@ -2192,78 +2234,24 @@ Page ${page + 1} of ${totalPages} (${pullRequests.length} total)
|
|
|
2192
2234
|
}
|
|
2193
2235
|
}
|
|
2194
2236
|
}
|
|
2195
|
-
async function comments(prNumber) {
|
|
2196
|
-
try {
|
|
2197
|
-
const { org, repo } = getRepoInfo();
|
|
2198
|
-
const allComments = [];
|
|
2199
|
-
const reviewResult = execSync11(
|
|
2200
|
-
`gh api repos/${org}/${repo}/pulls/${prNumber}/reviews`,
|
|
2201
|
-
{ encoding: "utf-8" }
|
|
2202
|
-
);
|
|
2203
|
-
if (reviewResult.trim()) {
|
|
2204
|
-
const reviews = JSON.parse(reviewResult);
|
|
2205
|
-
for (const review of reviews) {
|
|
2206
|
-
if (review.body) {
|
|
2207
|
-
allComments.push({
|
|
2208
|
-
type: "review",
|
|
2209
|
-
id: review.id,
|
|
2210
|
-
user: review.user.login,
|
|
2211
|
-
state: review.state,
|
|
2212
|
-
body: review.body
|
|
2213
|
-
});
|
|
2214
|
-
}
|
|
2215
|
-
}
|
|
2216
|
-
}
|
|
2217
|
-
const lineResult = execSync11(
|
|
2218
|
-
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments`,
|
|
2219
|
-
{ encoding: "utf-8" }
|
|
2220
|
-
);
|
|
2221
|
-
if (lineResult.trim()) {
|
|
2222
|
-
const lineComments = JSON.parse(lineResult);
|
|
2223
|
-
for (const comment of lineComments) {
|
|
2224
|
-
allComments.push({
|
|
2225
|
-
type: "line",
|
|
2226
|
-
id: comment.id,
|
|
2227
|
-
user: comment.user.login,
|
|
2228
|
-
path: comment.path,
|
|
2229
|
-
line: comment.line,
|
|
2230
|
-
body: comment.body,
|
|
2231
|
-
diff_hunk: comment.diff_hunk
|
|
2232
|
-
});
|
|
2233
|
-
}
|
|
2234
|
-
}
|
|
2235
|
-
return allComments;
|
|
2236
|
-
} catch (error) {
|
|
2237
|
-
if (isGhNotInstalled(error)) {
|
|
2238
|
-
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
2239
|
-
console.error("Install it from https://cli.github.com/");
|
|
2240
|
-
return [];
|
|
2241
|
-
}
|
|
2242
|
-
if (isNotFound(error)) {
|
|
2243
|
-
console.error("Error: Pull request not found.");
|
|
2244
|
-
return [];
|
|
2245
|
-
}
|
|
2246
|
-
throw error;
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2249
2237
|
|
|
2250
2238
|
// src/commands/refactor/check.ts
|
|
2251
2239
|
import { spawn as spawn2 } from "child_process";
|
|
2252
|
-
import * as
|
|
2240
|
+
import * as path14 from "path";
|
|
2253
2241
|
|
|
2254
2242
|
// src/commands/refactor/getViolations.ts
|
|
2255
|
-
import { execSync as
|
|
2256
|
-
import
|
|
2243
|
+
import { execSync as execSync14 } from "child_process";
|
|
2244
|
+
import fs14 from "fs";
|
|
2257
2245
|
import { minimatch as minimatch2 } from "minimatch";
|
|
2258
2246
|
|
|
2259
2247
|
// src/commands/refactor/getIgnoredFiles.ts
|
|
2260
|
-
import
|
|
2248
|
+
import fs13 from "fs";
|
|
2261
2249
|
var REFACTOR_YML_PATH = "refactor.yml";
|
|
2262
2250
|
function parseRefactorYml() {
|
|
2263
|
-
if (!
|
|
2251
|
+
if (!fs13.existsSync(REFACTOR_YML_PATH)) {
|
|
2264
2252
|
return [];
|
|
2265
2253
|
}
|
|
2266
|
-
const content =
|
|
2254
|
+
const content = fs13.readFileSync(REFACTOR_YML_PATH, "utf-8");
|
|
2267
2255
|
const entries = [];
|
|
2268
2256
|
const lines = content.split("\n");
|
|
2269
2257
|
let currentEntry = {};
|
|
@@ -2348,7 +2336,7 @@ Refactor check failed:
|
|
|
2348
2336
|
|
|
2349
2337
|
// src/commands/refactor/getViolations.ts
|
|
2350
2338
|
function countLines(filePath) {
|
|
2351
|
-
const content =
|
|
2339
|
+
const content = fs14.readFileSync(filePath, "utf-8");
|
|
2352
2340
|
return content.split("\n").length;
|
|
2353
2341
|
}
|
|
2354
2342
|
function getGitFiles(options) {
|
|
@@ -2357,7 +2345,7 @@ function getGitFiles(options) {
|
|
|
2357
2345
|
}
|
|
2358
2346
|
const files = /* @__PURE__ */ new Set();
|
|
2359
2347
|
if (options.staged || options.modified) {
|
|
2360
|
-
const staged =
|
|
2348
|
+
const staged = execSync14("git diff --cached --name-only", {
|
|
2361
2349
|
encoding: "utf-8"
|
|
2362
2350
|
});
|
|
2363
2351
|
for (const file of staged.trim().split("\n").filter(Boolean)) {
|
|
@@ -2365,7 +2353,7 @@ function getGitFiles(options) {
|
|
|
2365
2353
|
}
|
|
2366
2354
|
}
|
|
2367
2355
|
if (options.unstaged || options.modified) {
|
|
2368
|
-
const unstaged =
|
|
2356
|
+
const unstaged = execSync14("git diff --name-only", { encoding: "utf-8" });
|
|
2369
2357
|
for (const file of unstaged.trim().split("\n").filter(Boolean)) {
|
|
2370
2358
|
files.add(file);
|
|
2371
2359
|
}
|
|
@@ -2400,7 +2388,7 @@ async function runVerifyQuietly() {
|
|
|
2400
2388
|
return true;
|
|
2401
2389
|
}
|
|
2402
2390
|
const { packageJsonPath, verifyScripts } = result;
|
|
2403
|
-
const packageDir =
|
|
2391
|
+
const packageDir = path14.dirname(packageJsonPath);
|
|
2404
2392
|
const results = await Promise.all(
|
|
2405
2393
|
verifyScripts.map(
|
|
2406
2394
|
(script) => new Promise(
|
|
@@ -2453,25 +2441,25 @@ async function check(pattern2, options) {
|
|
|
2453
2441
|
}
|
|
2454
2442
|
|
|
2455
2443
|
// src/commands/refactor/ignore.ts
|
|
2456
|
-
import
|
|
2444
|
+
import fs15 from "fs";
|
|
2457
2445
|
import chalk32 from "chalk";
|
|
2458
2446
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
2459
2447
|
function ignore(file) {
|
|
2460
|
-
if (!
|
|
2448
|
+
if (!fs15.existsSync(file)) {
|
|
2461
2449
|
console.error(chalk32.red(`Error: File does not exist: ${file}`));
|
|
2462
2450
|
process.exit(1);
|
|
2463
2451
|
}
|
|
2464
|
-
const content =
|
|
2452
|
+
const content = fs15.readFileSync(file, "utf-8");
|
|
2465
2453
|
const lineCount = content.split("\n").length;
|
|
2466
2454
|
const maxLines = lineCount + 10;
|
|
2467
2455
|
const entry = `- file: ${file}
|
|
2468
2456
|
maxLines: ${maxLines}
|
|
2469
2457
|
`;
|
|
2470
|
-
if (
|
|
2471
|
-
const existing =
|
|
2472
|
-
|
|
2458
|
+
if (fs15.existsSync(REFACTOR_YML_PATH2)) {
|
|
2459
|
+
const existing = fs15.readFileSync(REFACTOR_YML_PATH2, "utf-8");
|
|
2460
|
+
fs15.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
|
|
2473
2461
|
} else {
|
|
2474
|
-
|
|
2462
|
+
fs15.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
2475
2463
|
}
|
|
2476
2464
|
console.log(
|
|
2477
2465
|
chalk32.green(
|
|
@@ -2565,22 +2553,22 @@ async function statusLine() {
|
|
|
2565
2553
|
}
|
|
2566
2554
|
|
|
2567
2555
|
// src/commands/sync.ts
|
|
2568
|
-
import * as
|
|
2556
|
+
import * as fs17 from "fs";
|
|
2569
2557
|
import * as os from "os";
|
|
2570
|
-
import * as
|
|
2571
|
-
import { fileURLToPath as
|
|
2558
|
+
import * as path16 from "path";
|
|
2559
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2572
2560
|
|
|
2573
2561
|
// src/commands/sync/syncSettings.ts
|
|
2574
|
-
import * as
|
|
2575
|
-
import * as
|
|
2562
|
+
import * as fs16 from "fs";
|
|
2563
|
+
import * as path15 from "path";
|
|
2576
2564
|
import chalk33 from "chalk";
|
|
2577
2565
|
async function syncSettings(claudeDir, targetBase) {
|
|
2578
|
-
const source =
|
|
2579
|
-
const target =
|
|
2580
|
-
const sourceContent =
|
|
2566
|
+
const source = path15.join(claudeDir, "settings.json");
|
|
2567
|
+
const target = path15.join(targetBase, "settings.json");
|
|
2568
|
+
const sourceContent = fs16.readFileSync(source, "utf-8");
|
|
2581
2569
|
const normalizedSource = JSON.stringify(JSON.parse(sourceContent), null, 2);
|
|
2582
|
-
if (
|
|
2583
|
-
const targetContent =
|
|
2570
|
+
if (fs16.existsSync(target)) {
|
|
2571
|
+
const targetContent = fs16.readFileSync(target, "utf-8");
|
|
2584
2572
|
const normalizedTarget = JSON.stringify(JSON.parse(targetContent), null, 2);
|
|
2585
2573
|
if (normalizedSource !== normalizedTarget) {
|
|
2586
2574
|
console.log(
|
|
@@ -2598,37 +2586,37 @@ async function syncSettings(claudeDir, targetBase) {
|
|
|
2598
2586
|
}
|
|
2599
2587
|
}
|
|
2600
2588
|
}
|
|
2601
|
-
|
|
2589
|
+
fs16.copyFileSync(source, target);
|
|
2602
2590
|
console.log("Copied settings.json to ~/.claude/settings.json");
|
|
2603
2591
|
}
|
|
2604
2592
|
|
|
2605
2593
|
// src/commands/sync.ts
|
|
2606
|
-
var __filename2 =
|
|
2607
|
-
var
|
|
2594
|
+
var __filename2 = fileURLToPath3(import.meta.url);
|
|
2595
|
+
var __dirname4 = path16.dirname(__filename2);
|
|
2608
2596
|
async function sync() {
|
|
2609
|
-
const claudeDir =
|
|
2610
|
-
const targetBase =
|
|
2597
|
+
const claudeDir = path16.join(__dirname4, "..", "claude");
|
|
2598
|
+
const targetBase = path16.join(os.homedir(), ".claude");
|
|
2611
2599
|
syncCommands(claudeDir, targetBase);
|
|
2612
2600
|
await syncSettings(claudeDir, targetBase);
|
|
2613
2601
|
}
|
|
2614
2602
|
function syncCommands(claudeDir, targetBase) {
|
|
2615
|
-
const sourceDir =
|
|
2616
|
-
const targetDir =
|
|
2617
|
-
|
|
2618
|
-
const files =
|
|
2603
|
+
const sourceDir = path16.join(claudeDir, "commands");
|
|
2604
|
+
const targetDir = path16.join(targetBase, "commands");
|
|
2605
|
+
fs17.mkdirSync(targetDir, { recursive: true });
|
|
2606
|
+
const files = fs17.readdirSync(sourceDir);
|
|
2619
2607
|
for (const file of files) {
|
|
2620
|
-
|
|
2608
|
+
fs17.copyFileSync(path16.join(sourceDir, file), path16.join(targetDir, file));
|
|
2621
2609
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
2622
2610
|
}
|
|
2623
2611
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
2624
2612
|
}
|
|
2625
2613
|
|
|
2626
2614
|
// src/commands/verify/hardcodedColors.ts
|
|
2627
|
-
import { execSync as
|
|
2615
|
+
import { execSync as execSync15 } from "child_process";
|
|
2628
2616
|
var pattern = "0x[0-9a-fA-F]{6}|#[0-9a-fA-F]{3,6}";
|
|
2629
2617
|
function hardcodedColors() {
|
|
2630
2618
|
try {
|
|
2631
|
-
const output =
|
|
2619
|
+
const output = execSync15(`grep -rEnH '${pattern}' src/`, {
|
|
2632
2620
|
encoding: "utf-8"
|
|
2633
2621
|
});
|
|
2634
2622
|
const lines = output.trim().split("\n");
|
|
@@ -2658,7 +2646,7 @@ Total: ${lines.length} hardcoded color(s)`);
|
|
|
2658
2646
|
|
|
2659
2647
|
// src/commands/verify/run.ts
|
|
2660
2648
|
import { spawn as spawn4 } from "child_process";
|
|
2661
|
-
import * as
|
|
2649
|
+
import * as path17 from "path";
|
|
2662
2650
|
function formatDuration(ms) {
|
|
2663
2651
|
if (ms < 1e3) {
|
|
2664
2652
|
return `${ms}ms`;
|
|
@@ -2688,7 +2676,7 @@ async function run2(options = {}) {
|
|
|
2688
2676
|
return;
|
|
2689
2677
|
}
|
|
2690
2678
|
const { packageJsonPath, verifyScripts } = result;
|
|
2691
|
-
const packageDir =
|
|
2679
|
+
const packageDir = path17.dirname(packageJsonPath);
|
|
2692
2680
|
console.log(`Running ${verifyScripts.length} verify script(s) in parallel:`);
|
|
2693
2681
|
for (const script of verifyScripts) {
|
|
2694
2682
|
console.log(` - ${script}`);
|
|
@@ -2738,15 +2726,11 @@ program.command("init").description("Initialize VS Code and verify configuration
|
|
|
2738
2726
|
program.command("commit <message>").description("Create a git commit with validation").action(commit);
|
|
2739
2727
|
program.command("update").description("Update claude-code to the latest version").action(() => {
|
|
2740
2728
|
console.log("Updating claude-code...");
|
|
2741
|
-
|
|
2729
|
+
execSync16("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
|
|
2742
2730
|
});
|
|
2743
2731
|
var prsCommand = program.command("prs").description("Pull request utilities").option("--open", "List only open pull requests").option("--closed", "List only closed pull requests").action(prs);
|
|
2744
2732
|
prsCommand.command("comments <pr-number>").description("List all comments on a pull request").action((prNumber) => {
|
|
2745
|
-
comments(Number.parseInt(prNumber, 10)).then(
|
|
2746
|
-
for (const comment of comments2) {
|
|
2747
|
-
console.log(JSON.stringify(comment));
|
|
2748
|
-
}
|
|
2749
|
-
});
|
|
2733
|
+
comments(Number.parseInt(prNumber, 10)).then(printComments);
|
|
2750
2734
|
});
|
|
2751
2735
|
var runCommand = program.command("run").description("Run a configured command from assist.yml").argument("<name>", "Name of the configured command").argument("[args...]", "Arguments to pass to the command").allowUnknownOption().action((name, args) => {
|
|
2752
2736
|
run(name, args);
|
|
@@ -2779,7 +2763,6 @@ vscodeCommand.command("init").description("Add VS Code configuration files").act
|
|
|
2779
2763
|
var deployCommand = program.command("deploy").description("Netlify deployment utilities");
|
|
2780
2764
|
deployCommand.command("init").description("Initialize Netlify project and configure deployment").action(init);
|
|
2781
2765
|
deployCommand.command("redirect").description("Add trailing slash redirect script to index.html").action(redirect);
|
|
2782
|
-
program.command("enable-ralph").description("Enable ralph-wiggum plugin for spacetraders").action(enableRalph);
|
|
2783
2766
|
program.command("status-line").description("Format Claude Code status line from JSON stdin").action(statusLine);
|
|
2784
2767
|
program.command("notify").description(
|
|
2785
2768
|
"Show notification from Claude Code hook (reads JSON from stdin)"
|
package/package.json
CHANGED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import chalk from "chalk";
|
|
5
|
-
import { promptConfirm } from "../../shared/promptConfirm";
|
|
6
|
-
import { printDiff } from "../../utils/printDiff";
|
|
7
|
-
|
|
8
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
|
|
10
|
-
function deepMerge(
|
|
11
|
-
target: Record<string, unknown>,
|
|
12
|
-
source: Record<string, unknown>,
|
|
13
|
-
): Record<string, unknown> {
|
|
14
|
-
const result = { ...target };
|
|
15
|
-
for (const key of Object.keys(source)) {
|
|
16
|
-
const sourceVal = source[key];
|
|
17
|
-
const targetVal = result[key];
|
|
18
|
-
if (
|
|
19
|
-
sourceVal &&
|
|
20
|
-
typeof sourceVal === "object" &&
|
|
21
|
-
!Array.isArray(sourceVal) &&
|
|
22
|
-
targetVal &&
|
|
23
|
-
typeof targetVal === "object" &&
|
|
24
|
-
!Array.isArray(targetVal)
|
|
25
|
-
) {
|
|
26
|
-
result[key] = deepMerge(
|
|
27
|
-
targetVal as Record<string, unknown>,
|
|
28
|
-
sourceVal as Record<string, unknown>,
|
|
29
|
-
);
|
|
30
|
-
} else {
|
|
31
|
-
result[key] = sourceVal;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return result;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export async function enableRalph(): Promise<void> {
|
|
38
|
-
const sourcePath = path.join(
|
|
39
|
-
__dirname,
|
|
40
|
-
"commands/enable-ralph/settings.local.json",
|
|
41
|
-
);
|
|
42
|
-
const targetPath = path.join(process.cwd(), ".claude/settings.local.json");
|
|
43
|
-
const sourceData = JSON.parse(fs.readFileSync(sourcePath, "utf-8"));
|
|
44
|
-
|
|
45
|
-
const targetDir = path.dirname(targetPath);
|
|
46
|
-
if (!fs.existsSync(targetDir)) {
|
|
47
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
let targetData: Record<string, unknown> = {};
|
|
51
|
-
let targetContent = "{}";
|
|
52
|
-
if (fs.existsSync(targetPath)) {
|
|
53
|
-
targetContent = fs.readFileSync(targetPath, "utf-8");
|
|
54
|
-
targetData = JSON.parse(targetContent);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const merged = deepMerge(targetData, sourceData);
|
|
58
|
-
const mergedContent = `${JSON.stringify(merged, null, "\t")}\n`;
|
|
59
|
-
|
|
60
|
-
if (mergedContent === targetContent) {
|
|
61
|
-
console.log(chalk.green("settings.local.json already has ralph enabled"));
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
console.log(chalk.yellow("\nChanges to settings.local.json:"));
|
|
66
|
-
console.log();
|
|
67
|
-
printDiff(targetContent, mergedContent);
|
|
68
|
-
|
|
69
|
-
const confirm = await promptConfirm("Apply these changes?");
|
|
70
|
-
|
|
71
|
-
if (!confirm) {
|
|
72
|
-
console.log("Skipped");
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
fs.writeFileSync(targetPath, mergedContent);
|
|
77
|
-
console.log(`Updated ${targetPath}`);
|
|
78
|
-
}
|