@staff0rd/assist 0.48.0 → 0.50.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 +2 -1
- package/dist/index.js +718 -586
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { execSync as
|
|
4
|
+
import { execSync as execSync22 } from "child_process";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// package.json
|
|
8
8
|
var package_default = {
|
|
9
9
|
name: "@staff0rd/assist",
|
|
10
|
-
version: "0.
|
|
10
|
+
version: "0.50.0",
|
|
11
11
|
type: "module",
|
|
12
12
|
main: "dist/index.js",
|
|
13
13
|
bin: {
|
|
@@ -222,9 +222,9 @@ function isTraversable(value) {
|
|
|
222
222
|
function stepInto(current, key) {
|
|
223
223
|
return isTraversable(current) ? current[key] : void 0;
|
|
224
224
|
}
|
|
225
|
-
function getNestedValue(obj,
|
|
225
|
+
function getNestedValue(obj, path30) {
|
|
226
226
|
let current = obj;
|
|
227
|
-
for (const key of
|
|
227
|
+
for (const key of path30.split(".")) current = stepInto(current, key);
|
|
228
228
|
return current;
|
|
229
229
|
}
|
|
230
230
|
function isPlainObject(val) {
|
|
@@ -241,8 +241,8 @@ function buildNestedPath(root, keys) {
|
|
|
241
241
|
}
|
|
242
242
|
return current;
|
|
243
243
|
}
|
|
244
|
-
function setNestedValue(obj,
|
|
245
|
-
const keys =
|
|
244
|
+
function setNestedValue(obj, path30, value) {
|
|
245
|
+
const keys = path30.split(".");
|
|
246
246
|
const result = { ...obj };
|
|
247
247
|
buildNestedPath(result, keys)[keys[keys.length - 1]] = value;
|
|
248
248
|
return result;
|
|
@@ -311,17 +311,17 @@ function configList() {
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
// src/commands/verify/init/index.ts
|
|
314
|
-
import
|
|
314
|
+
import chalk15 from "chalk";
|
|
315
315
|
|
|
316
316
|
// src/shared/promptMultiselect.ts
|
|
317
317
|
import chalk3 from "chalk";
|
|
318
318
|
import enquirer from "enquirer";
|
|
319
|
-
async function promptMultiselect(message,
|
|
319
|
+
async function promptMultiselect(message, options2) {
|
|
320
320
|
const { selected } = await enquirer.prompt({
|
|
321
321
|
type: "multiselect",
|
|
322
322
|
name: "selected",
|
|
323
323
|
message,
|
|
324
|
-
choices:
|
|
324
|
+
choices: options2.map((opt) => ({
|
|
325
325
|
name: opt.value,
|
|
326
326
|
message: `${opt.name} - ${chalk3.dim(opt.description)}`
|
|
327
327
|
})),
|
|
@@ -387,7 +387,8 @@ var expectedScripts = {
|
|
|
387
387
|
"verify:lint": "biome check --write .",
|
|
388
388
|
"verify:duplicate-code": "jscpd --format 'typescript,tsx' --exitCode 1 --ignore '**/*.test.*' -r consoleFull src",
|
|
389
389
|
"verify:test": "vitest run --reporter=dot --silent",
|
|
390
|
-
"verify:hardcoded-colors": "assist verify hardcoded-colors"
|
|
390
|
+
"verify:hardcoded-colors": "assist verify hardcoded-colors",
|
|
391
|
+
"verify:maintainability": "assist complexity maintainability ./src --threshold 60"
|
|
391
392
|
};
|
|
392
393
|
|
|
393
394
|
// src/commands/verify/setup/setupBuild.ts
|
|
@@ -460,12 +461,22 @@ async function setupBuild(packageJsonPath, hasVite, hasTypescript) {
|
|
|
460
461
|
} else if (hasVite) {
|
|
461
462
|
command = "vite build --logLevel error";
|
|
462
463
|
} else {
|
|
463
|
-
command = "
|
|
464
|
+
command = "npm run build";
|
|
464
465
|
}
|
|
465
466
|
console.log(chalk6.dim(`Using: ${command}`));
|
|
466
467
|
const pkg = readPackageJson(packageJsonPath);
|
|
467
468
|
writePackageJson(packageJsonPath, addScript(pkg, "verify:build", command));
|
|
468
469
|
}
|
|
470
|
+
async function setupTypecheck(packageJsonPath) {
|
|
471
|
+
console.log(chalk6.blue("\nSetting up typecheck verification..."));
|
|
472
|
+
const command = "tsc --noEmit";
|
|
473
|
+
console.log(chalk6.dim(`Using: ${command}`));
|
|
474
|
+
const pkg = readPackageJson(packageJsonPath);
|
|
475
|
+
writePackageJson(
|
|
476
|
+
packageJsonPath,
|
|
477
|
+
addScript(pkg, "verify:typecheck", command)
|
|
478
|
+
);
|
|
479
|
+
}
|
|
469
480
|
|
|
470
481
|
// src/commands/verify/setup/setupDuplicateCode.ts
|
|
471
482
|
import * as path3 from "path";
|
|
@@ -578,8 +589,8 @@ function removeEslintConfigFiles() {
|
|
|
578
589
|
}
|
|
579
590
|
|
|
580
591
|
// src/shared/removeEslint/index.ts
|
|
581
|
-
function removeEslint(
|
|
582
|
-
const removedFromPackageJson = removeEslintFromPackageJson(
|
|
592
|
+
function removeEslint(options2 = {}) {
|
|
593
|
+
const removedFromPackageJson = removeEslintFromPackageJson(options2);
|
|
583
594
|
const removedConfigFiles = removeEslintConfigFiles();
|
|
584
595
|
if (removedFromPackageJson || removedConfigFiles) {
|
|
585
596
|
console.log("Running npm install...");
|
|
@@ -588,7 +599,7 @@ function removeEslint(options = {}) {
|
|
|
588
599
|
}
|
|
589
600
|
return false;
|
|
590
601
|
}
|
|
591
|
-
function removeEslintFromPackageJson(
|
|
602
|
+
function removeEslintFromPackageJson(options2) {
|
|
592
603
|
const packageJsonPath = "package.json";
|
|
593
604
|
if (!existsSync5(packageJsonPath)) {
|
|
594
605
|
return false;
|
|
@@ -597,7 +608,7 @@ function removeEslintFromPackageJson(options) {
|
|
|
597
608
|
let modified = false;
|
|
598
609
|
modified = removeEslintDeps(packageJson.dependencies) || modified;
|
|
599
610
|
modified = removeEslintDeps(packageJson.devDependencies) || modified;
|
|
600
|
-
modified = removeEslintScripts(packageJson.scripts,
|
|
611
|
+
modified = removeEslintScripts(packageJson.scripts, options2) || modified;
|
|
601
612
|
if (modified) {
|
|
602
613
|
writeFileSync3(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}
|
|
603
614
|
`);
|
|
@@ -616,11 +627,11 @@ function removeEslintDeps(deps) {
|
|
|
616
627
|
}
|
|
617
628
|
return modified;
|
|
618
629
|
}
|
|
619
|
-
function removeEslintScripts(scripts,
|
|
630
|
+
function removeEslintScripts(scripts, options2) {
|
|
620
631
|
if (!scripts) return false;
|
|
621
632
|
let modified = false;
|
|
622
633
|
for (const key of Object.keys(scripts)) {
|
|
623
|
-
const isEslintScript = key.includes("eslint") || scripts[key].includes("eslint") ||
|
|
634
|
+
const isEslintScript = key.includes("eslint") || scripts[key].includes("eslint") || options2.removeLintScripts && key.includes("lint");
|
|
624
635
|
if (isEslintScript) {
|
|
625
636
|
delete scripts[key];
|
|
626
637
|
modified = true;
|
|
@@ -716,12 +727,25 @@ async function setupLint(packageJsonPath) {
|
|
|
716
727
|
);
|
|
717
728
|
}
|
|
718
729
|
|
|
719
|
-
// src/commands/verify/setup/
|
|
730
|
+
// src/commands/verify/setup/setupMaintainability.ts
|
|
720
731
|
import * as path7 from "path";
|
|
721
732
|
import chalk13 from "chalk";
|
|
733
|
+
async function setupMaintainability(packageJsonPath) {
|
|
734
|
+
console.log(chalk13.blue("\nSetting up maintainability check..."));
|
|
735
|
+
addToKnipIgnoreBinaries(path7.dirname(packageJsonPath), "assist");
|
|
736
|
+
setupVerifyScript(
|
|
737
|
+
packageJsonPath,
|
|
738
|
+
"verify:maintainability",
|
|
739
|
+
expectedScripts["verify:maintainability"]
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// src/commands/verify/setup/setupTest.ts
|
|
744
|
+
import * as path8 from "path";
|
|
745
|
+
import chalk14 from "chalk";
|
|
722
746
|
async function setupTest(packageJsonPath) {
|
|
723
|
-
console.log(
|
|
724
|
-
const cwd =
|
|
747
|
+
console.log(chalk14.blue("\nSetting up vitest..."));
|
|
748
|
+
const cwd = path8.dirname(packageJsonPath);
|
|
725
749
|
const pkg = readPackageJson(packageJsonPath);
|
|
726
750
|
if (!pkg.devDependencies?.vitest && !installPackage("vitest", cwd)) {
|
|
727
751
|
return;
|
|
@@ -770,34 +794,22 @@ function detectExistingSetup(pkg) {
|
|
|
770
794
|
test: toolStatus(pkg, "verify:test", !!pkg.devDependencies?.vitest),
|
|
771
795
|
hasVite: !!pkg.devDependencies?.vite || !!pkg.dependencies?.vite,
|
|
772
796
|
hasTypescript: !!pkg.devDependencies?.typescript,
|
|
773
|
-
build:
|
|
774
|
-
|
|
775
|
-
hasScript: !!pkg.scripts?.["verify:build"],
|
|
776
|
-
isOutdated: false
|
|
777
|
-
},
|
|
797
|
+
build: toolStatus(pkg, "verify:build", true),
|
|
798
|
+
typecheck: toolStatus(pkg, "verify:typecheck", true),
|
|
778
799
|
hardcodedColors: toolStatus(pkg, "verify:hardcoded-colors", true),
|
|
800
|
+
maintainability: toolStatus(pkg, "verify:maintainability", true),
|
|
779
801
|
hasOpenColor: !!pkg.dependencies?.["open-color"] || !!pkg.devDependencies?.["open-color"]
|
|
780
802
|
};
|
|
781
803
|
}
|
|
782
804
|
|
|
783
|
-
// src/commands/verify/init/
|
|
805
|
+
// src/commands/verify/init/options.ts
|
|
784
806
|
function getBuildDescription(setup) {
|
|
785
807
|
if (setup.hasVite && setup.hasTypescript)
|
|
786
808
|
return "TypeScript + Vite build verification";
|
|
787
809
|
if (setup.hasVite) return "Vite build verification";
|
|
788
|
-
return "
|
|
789
|
-
}
|
|
790
|
-
function shouldInclude(setup, def) {
|
|
791
|
-
return needsSetup(setup[def.toolKey]) && (def.extraCondition ?? true);
|
|
810
|
+
return "Build verification";
|
|
792
811
|
}
|
|
793
|
-
|
|
794
|
-
return {
|
|
795
|
-
name: `${def.label}${getStatusLabel(setup[def.toolKey])}`,
|
|
796
|
-
value: def.value,
|
|
797
|
-
description: def.description
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
var STATIC_OPTIONS = [
|
|
812
|
+
var options = [
|
|
801
813
|
{
|
|
802
814
|
toolKey: "knip",
|
|
803
815
|
value: "knip",
|
|
@@ -821,31 +833,51 @@ var STATIC_OPTIONS = [
|
|
|
821
833
|
value: "hardcoded-colors",
|
|
822
834
|
label: "hardcoded-colors",
|
|
823
835
|
description: "Detect hardcoded hex colors (use open-color instead)"
|
|
836
|
+
},
|
|
837
|
+
{
|
|
838
|
+
toolKey: "test",
|
|
839
|
+
value: "test",
|
|
840
|
+
label: "test",
|
|
841
|
+
description: "Run tests with vitest",
|
|
842
|
+
extraCondition: (s) => s.test.hasPackage
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
toolKey: "build",
|
|
846
|
+
value: "build",
|
|
847
|
+
label: "build",
|
|
848
|
+
description: getBuildDescription,
|
|
849
|
+
extraCondition: (s) => s.hasTypescript || s.hasVite
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
toolKey: "typecheck",
|
|
853
|
+
value: "typecheck",
|
|
854
|
+
label: "typecheck",
|
|
855
|
+
description: "TypeScript type checking",
|
|
856
|
+
extraCondition: (s) => s.hasTypescript && !s.hasVite
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
toolKey: "maintainability",
|
|
860
|
+
value: "maintainability",
|
|
861
|
+
label: "maintainability",
|
|
862
|
+
description: "Maintainability index threshold check"
|
|
824
863
|
}
|
|
825
864
|
];
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
value: "test",
|
|
831
|
-
label: "test",
|
|
832
|
-
description: "Run tests with vitest",
|
|
833
|
-
extraCondition: setup.test.hasPackage
|
|
834
|
-
},
|
|
835
|
-
{
|
|
836
|
-
toolKey: "build",
|
|
837
|
-
value: "build",
|
|
838
|
-
label: "build",
|
|
839
|
-
description: getBuildDescription(setup),
|
|
840
|
-
extraCondition: setup.hasTypescript || setup.hasVite
|
|
841
|
-
}
|
|
842
|
-
];
|
|
865
|
+
|
|
866
|
+
// src/commands/verify/init/getAvailableOptions.ts
|
|
867
|
+
function resolveDescription(desc, setup) {
|
|
868
|
+
return typeof desc === "function" ? desc(setup) : desc;
|
|
843
869
|
}
|
|
844
|
-
function
|
|
845
|
-
return
|
|
870
|
+
function toVerifyOption(def, setup) {
|
|
871
|
+
return {
|
|
872
|
+
name: `${def.label}${getStatusLabel(setup[def.toolKey])}`,
|
|
873
|
+
value: def.value,
|
|
874
|
+
description: resolveDescription(def.description, setup)
|
|
875
|
+
};
|
|
846
876
|
}
|
|
847
877
|
function getAvailableOptions(setup) {
|
|
848
|
-
return
|
|
878
|
+
return options.filter(
|
|
879
|
+
(def) => needsSetup(setup[def.toolKey]) && (def.extraCondition?.(setup) ?? true)
|
|
880
|
+
).map((def) => toVerifyOption(def, setup));
|
|
849
881
|
}
|
|
850
882
|
|
|
851
883
|
// src/commands/verify/init/index.ts
|
|
@@ -856,37 +888,43 @@ function getSetupHandlers(hasVite, hasTypescript, hasOpenColor) {
|
|
|
856
888
|
"duplicate-code": (p) => setupDuplicateCode(p),
|
|
857
889
|
test: (p) => setupTest(p),
|
|
858
890
|
build: (p) => setupBuild(p, hasVite, hasTypescript),
|
|
859
|
-
|
|
891
|
+
typecheck: (p) => setupTypecheck(p),
|
|
892
|
+
"hardcoded-colors": (p) => setupHardcodedColors(p, hasOpenColor),
|
|
893
|
+
maintainability: (p) => setupMaintainability(p)
|
|
860
894
|
};
|
|
861
895
|
}
|
|
862
896
|
async function runSelectedSetups(selected, packageJsonPath, handlers) {
|
|
863
897
|
for (const choice of selected) {
|
|
864
898
|
await handlers[choice]?.(packageJsonPath);
|
|
865
899
|
}
|
|
866
|
-
console.log(
|
|
900
|
+
console.log(chalk15.green(`
|
|
867
901
|
Added ${selected.length} verify script(s):`));
|
|
868
902
|
for (const choice of selected) {
|
|
869
|
-
console.log(
|
|
903
|
+
console.log(chalk15.green(` - verify:${choice}`));
|
|
870
904
|
}
|
|
871
|
-
console.log(
|
|
905
|
+
console.log(chalk15.dim("\nRun 'assist verify' to run all verify scripts"));
|
|
872
906
|
}
|
|
873
|
-
async function
|
|
874
|
-
const { packageJsonPath, pkg } = requirePackageJson();
|
|
875
|
-
const setup = detectExistingSetup(pkg);
|
|
876
|
-
const availableOptions = getAvailableOptions(setup);
|
|
907
|
+
async function promptForScripts(availableOptions) {
|
|
877
908
|
if (availableOptions.length === 0) {
|
|
878
|
-
console.log(
|
|
879
|
-
return;
|
|
909
|
+
console.log(chalk15.green("All verify scripts are already configured!"));
|
|
910
|
+
return null;
|
|
880
911
|
}
|
|
881
|
-
console.log(
|
|
912
|
+
console.log(chalk15.bold("Available verify scripts to add:\n"));
|
|
882
913
|
const selected = await promptMultiselect(
|
|
883
914
|
"Select verify scripts to add:",
|
|
884
915
|
availableOptions
|
|
885
916
|
);
|
|
886
917
|
if (selected.length === 0) {
|
|
887
|
-
console.log(
|
|
888
|
-
return;
|
|
918
|
+
console.log(chalk15.yellow("No scripts selected"));
|
|
919
|
+
return null;
|
|
889
920
|
}
|
|
921
|
+
return selected;
|
|
922
|
+
}
|
|
923
|
+
async function init2() {
|
|
924
|
+
const { packageJsonPath, pkg } = requirePackageJson();
|
|
925
|
+
const setup = detectExistingSetup(pkg);
|
|
926
|
+
const selected = await promptForScripts(getAvailableOptions(setup));
|
|
927
|
+
if (!selected) return;
|
|
890
928
|
const handlers = getSetupHandlers(
|
|
891
929
|
setup.hasVite,
|
|
892
930
|
setup.hasTypescript,
|
|
@@ -896,21 +934,21 @@ async function init2() {
|
|
|
896
934
|
}
|
|
897
935
|
|
|
898
936
|
// src/commands/vscode/init/index.ts
|
|
899
|
-
import
|
|
937
|
+
import chalk17 from "chalk";
|
|
900
938
|
|
|
901
939
|
// src/commands/vscode/init/createLaunchJson.ts
|
|
902
940
|
import * as fs3 from "fs";
|
|
903
|
-
import * as
|
|
904
|
-
import
|
|
941
|
+
import * as path9 from "path";
|
|
942
|
+
import chalk16 from "chalk";
|
|
905
943
|
function ensureVscodeFolder() {
|
|
906
|
-
const vscodeDir =
|
|
944
|
+
const vscodeDir = path9.join(process.cwd(), ".vscode");
|
|
907
945
|
if (!fs3.existsSync(vscodeDir)) {
|
|
908
946
|
fs3.mkdirSync(vscodeDir);
|
|
909
|
-
console.log(
|
|
947
|
+
console.log(chalk16.dim("Created .vscode folder"));
|
|
910
948
|
}
|
|
911
949
|
}
|
|
912
950
|
function removeVscodeFromGitignore() {
|
|
913
|
-
const gitignorePath =
|
|
951
|
+
const gitignorePath = path9.join(process.cwd(), ".gitignore");
|
|
914
952
|
if (!fs3.existsSync(gitignorePath)) {
|
|
915
953
|
return;
|
|
916
954
|
}
|
|
@@ -921,7 +959,7 @@ function removeVscodeFromGitignore() {
|
|
|
921
959
|
);
|
|
922
960
|
if (filteredLines.length !== lines.length) {
|
|
923
961
|
fs3.writeFileSync(gitignorePath, filteredLines.join("\n"));
|
|
924
|
-
console.log(
|
|
962
|
+
console.log(chalk16.dim("Removed .vscode references from .gitignore"));
|
|
925
963
|
}
|
|
926
964
|
}
|
|
927
965
|
function createLaunchJson() {
|
|
@@ -936,10 +974,10 @@ function createLaunchJson() {
|
|
|
936
974
|
}
|
|
937
975
|
]
|
|
938
976
|
};
|
|
939
|
-
const launchPath =
|
|
977
|
+
const launchPath = path9.join(process.cwd(), ".vscode", "launch.json");
|
|
940
978
|
fs3.writeFileSync(launchPath, `${JSON.stringify(launchConfig, null, " ")}
|
|
941
979
|
`);
|
|
942
|
-
console.log(
|
|
980
|
+
console.log(chalk16.green("Created .vscode/launch.json"));
|
|
943
981
|
}
|
|
944
982
|
function createSettingsJson() {
|
|
945
983
|
const settings = {
|
|
@@ -949,33 +987,33 @@ function createSettingsJson() {
|
|
|
949
987
|
"source.organizeImports.biome": "explicit"
|
|
950
988
|
}
|
|
951
989
|
};
|
|
952
|
-
const settingsPath =
|
|
990
|
+
const settingsPath = path9.join(process.cwd(), ".vscode", "settings.json");
|
|
953
991
|
fs3.writeFileSync(settingsPath, `${JSON.stringify(settings, null, " ")}
|
|
954
992
|
`);
|
|
955
|
-
console.log(
|
|
993
|
+
console.log(chalk16.green("Created .vscode/settings.json"));
|
|
956
994
|
}
|
|
957
995
|
function createExtensionsJson() {
|
|
958
996
|
const extensions = {
|
|
959
997
|
recommendations: ["biomejs.biome"]
|
|
960
998
|
};
|
|
961
|
-
const extensionsPath =
|
|
999
|
+
const extensionsPath = path9.join(process.cwd(), ".vscode", "extensions.json");
|
|
962
1000
|
fs3.writeFileSync(
|
|
963
1001
|
extensionsPath,
|
|
964
1002
|
`${JSON.stringify(extensions, null, " ")}
|
|
965
1003
|
`
|
|
966
1004
|
);
|
|
967
|
-
console.log(
|
|
1005
|
+
console.log(chalk16.green("Created .vscode/extensions.json"));
|
|
968
1006
|
}
|
|
969
1007
|
|
|
970
1008
|
// src/commands/vscode/init/detectVscodeSetup.ts
|
|
971
1009
|
import * as fs4 from "fs";
|
|
972
|
-
import * as
|
|
1010
|
+
import * as path10 from "path";
|
|
973
1011
|
function detectVscodeSetup(pkg) {
|
|
974
|
-
const vscodeDir =
|
|
1012
|
+
const vscodeDir = path10.join(process.cwd(), ".vscode");
|
|
975
1013
|
return {
|
|
976
1014
|
hasVscodeFolder: fs4.existsSync(vscodeDir),
|
|
977
|
-
hasLaunchJson: fs4.existsSync(
|
|
978
|
-
hasSettingsJson: fs4.existsSync(
|
|
1015
|
+
hasLaunchJson: fs4.existsSync(path10.join(vscodeDir, "launch.json")),
|
|
1016
|
+
hasSettingsJson: fs4.existsSync(path10.join(vscodeDir, "settings.json")),
|
|
979
1017
|
hasVite: !!pkg.devDependencies?.vite || !!pkg.dependencies?.vite
|
|
980
1018
|
};
|
|
981
1019
|
}
|
|
@@ -989,43 +1027,43 @@ var SETUP_HANDLERS = {
|
|
|
989
1027
|
}
|
|
990
1028
|
};
|
|
991
1029
|
function getAvailableOptions2(setup) {
|
|
992
|
-
const
|
|
1030
|
+
const options2 = [];
|
|
993
1031
|
if (!setup.hasLaunchJson && setup.hasVite)
|
|
994
|
-
|
|
1032
|
+
options2.push({
|
|
995
1033
|
name: "launch",
|
|
996
1034
|
value: "launch",
|
|
997
1035
|
description: "Debug configuration for Vite dev server"
|
|
998
1036
|
});
|
|
999
1037
|
if (!setup.hasSettingsJson)
|
|
1000
|
-
|
|
1038
|
+
options2.push({
|
|
1001
1039
|
name: "settings",
|
|
1002
1040
|
value: "settings",
|
|
1003
1041
|
description: "Biome formatter configuration"
|
|
1004
1042
|
});
|
|
1005
|
-
return
|
|
1043
|
+
return options2;
|
|
1006
1044
|
}
|
|
1007
1045
|
async function init3() {
|
|
1008
1046
|
const { pkg } = requirePackageJson();
|
|
1009
1047
|
const setup = detectVscodeSetup(pkg);
|
|
1010
1048
|
const availableOptions = getAvailableOptions2(setup);
|
|
1011
1049
|
if (availableOptions.length === 0) {
|
|
1012
|
-
console.log(
|
|
1050
|
+
console.log(chalk17.green("VS Code configuration already exists!"));
|
|
1013
1051
|
return;
|
|
1014
1052
|
}
|
|
1015
|
-
console.log(
|
|
1053
|
+
console.log(chalk17.bold("Available VS Code configurations to add:\n"));
|
|
1016
1054
|
const selected = await promptMultiselect(
|
|
1017
1055
|
"Select configurations to add:",
|
|
1018
1056
|
availableOptions
|
|
1019
1057
|
);
|
|
1020
1058
|
if (selected.length === 0) {
|
|
1021
|
-
console.log(
|
|
1059
|
+
console.log(chalk17.yellow("No configurations selected"));
|
|
1022
1060
|
return;
|
|
1023
1061
|
}
|
|
1024
1062
|
removeVscodeFromGitignore();
|
|
1025
1063
|
ensureVscodeFolder();
|
|
1026
1064
|
for (const choice of selected) SETUP_HANDLERS[choice]?.();
|
|
1027
1065
|
console.log(
|
|
1028
|
-
|
|
1066
|
+
chalk17.green(`
|
|
1029
1067
|
Added ${selected.length} VS Code configuration(s)`)
|
|
1030
1068
|
);
|
|
1031
1069
|
}
|
|
@@ -1038,24 +1076,24 @@ async function init4() {
|
|
|
1038
1076
|
|
|
1039
1077
|
// src/commands/lint/lint/runFileNameCheck.ts
|
|
1040
1078
|
import fs6 from "fs";
|
|
1041
|
-
import
|
|
1042
|
-
import
|
|
1079
|
+
import path12 from "path";
|
|
1080
|
+
import chalk18 from "chalk";
|
|
1043
1081
|
|
|
1044
1082
|
// src/shared/findSourceFiles.ts
|
|
1045
1083
|
import fs5 from "fs";
|
|
1046
|
-
import
|
|
1084
|
+
import path11 from "path";
|
|
1047
1085
|
var EXTENSIONS = [".ts", ".tsx"];
|
|
1048
|
-
function findSourceFiles(dir,
|
|
1049
|
-
const { includeTests = true } =
|
|
1086
|
+
function findSourceFiles(dir, options2 = {}) {
|
|
1087
|
+
const { includeTests = true } = options2;
|
|
1050
1088
|
const results = [];
|
|
1051
1089
|
if (!fs5.existsSync(dir)) {
|
|
1052
1090
|
return results;
|
|
1053
1091
|
}
|
|
1054
1092
|
const entries = fs5.readdirSync(dir, { withFileTypes: true });
|
|
1055
1093
|
for (const entry of entries) {
|
|
1056
|
-
const fullPath =
|
|
1094
|
+
const fullPath = path11.join(dir, entry.name);
|
|
1057
1095
|
if (entry.isDirectory() && entry.name !== "node_modules") {
|
|
1058
|
-
results.push(...findSourceFiles(fullPath,
|
|
1096
|
+
results.push(...findSourceFiles(fullPath, options2));
|
|
1059
1097
|
} else if (entry.isFile() && EXTENSIONS.some((ext) => entry.name.endsWith(ext))) {
|
|
1060
1098
|
if (!includeTests && entry.name.includes(".test.")) {
|
|
1061
1099
|
continue;
|
|
@@ -1077,7 +1115,7 @@ function checkFileNames() {
|
|
|
1077
1115
|
const sourceFiles = findSourceFiles("src");
|
|
1078
1116
|
const violations = [];
|
|
1079
1117
|
for (const filePath of sourceFiles) {
|
|
1080
|
-
const fileName =
|
|
1118
|
+
const fileName = path12.basename(filePath);
|
|
1081
1119
|
const nameWithoutExt = fileName.replace(/\.(ts|tsx)$/, "");
|
|
1082
1120
|
if (/^[A-Z]/.test(nameWithoutExt)) {
|
|
1083
1121
|
const content = fs6.readFileSync(filePath, "utf-8");
|
|
@@ -1091,16 +1129,16 @@ function checkFileNames() {
|
|
|
1091
1129
|
function runFileNameCheck() {
|
|
1092
1130
|
const violations = checkFileNames();
|
|
1093
1131
|
if (violations.length > 0) {
|
|
1094
|
-
console.error(
|
|
1132
|
+
console.error(chalk18.red("\nFile name check failed:\n"));
|
|
1095
1133
|
console.error(
|
|
1096
|
-
|
|
1134
|
+
chalk18.red(
|
|
1097
1135
|
" Files without classes or React components should not start with a capital letter.\n"
|
|
1098
1136
|
)
|
|
1099
1137
|
);
|
|
1100
1138
|
for (const violation of violations) {
|
|
1101
|
-
console.error(
|
|
1139
|
+
console.error(chalk18.red(` ${violation.filePath}`));
|
|
1102
1140
|
console.error(
|
|
1103
|
-
|
|
1141
|
+
chalk18.gray(
|
|
1104
1142
|
` Rename to: ${violation.fileName.charAt(0).toLowerCase()}${violation.fileName.slice(1)}
|
|
1105
1143
|
`
|
|
1106
1144
|
)
|
|
@@ -1120,17 +1158,17 @@ function runFileNameCheck() {
|
|
|
1120
1158
|
import fs7 from "fs";
|
|
1121
1159
|
|
|
1122
1160
|
// src/commands/lint/shared.ts
|
|
1123
|
-
import
|
|
1161
|
+
import chalk19 from "chalk";
|
|
1124
1162
|
function reportViolations(violations, checkName, errorMessage, successMessage) {
|
|
1125
1163
|
if (violations.length > 0) {
|
|
1126
|
-
console.error(
|
|
1164
|
+
console.error(chalk19.red(`
|
|
1127
1165
|
${checkName} failed:
|
|
1128
1166
|
`));
|
|
1129
|
-
console.error(
|
|
1167
|
+
console.error(chalk19.red(` ${errorMessage}
|
|
1130
1168
|
`));
|
|
1131
1169
|
for (const violation of violations) {
|
|
1132
|
-
console.error(
|
|
1133
|
-
console.error(
|
|
1170
|
+
console.error(chalk19.red(` ${violation.filePath}:${violation.line}`));
|
|
1171
|
+
console.error(chalk19.gray(` ${violation.content}
|
|
1134
1172
|
`));
|
|
1135
1173
|
}
|
|
1136
1174
|
return false;
|
|
@@ -1223,22 +1261,261 @@ function lint() {
|
|
|
1223
1261
|
}
|
|
1224
1262
|
}
|
|
1225
1263
|
|
|
1226
|
-
// src/commands/new/
|
|
1264
|
+
// src/commands/new/newCli.ts
|
|
1265
|
+
import { execSync as execSync7 } from "child_process";
|
|
1266
|
+
import { basename as basename2, resolve } from "path";
|
|
1267
|
+
|
|
1268
|
+
// src/commands/verify/hardcodedColors.ts
|
|
1269
|
+
import { execSync as execSync5 } from "child_process";
|
|
1270
|
+
var pattern = "0x[0-9a-fA-F]{6}|#[0-9a-fA-F]{3,6}";
|
|
1271
|
+
function hardcodedColors() {
|
|
1272
|
+
try {
|
|
1273
|
+
const output = execSync5(`grep -rEnH '${pattern}' src/`, {
|
|
1274
|
+
encoding: "utf-8"
|
|
1275
|
+
});
|
|
1276
|
+
const lines = output.trim().split("\n");
|
|
1277
|
+
console.log("Hardcoded colors found:\n");
|
|
1278
|
+
for (const line of lines) {
|
|
1279
|
+
const match = line.match(/^(.+):(\d+):(.+)$/);
|
|
1280
|
+
if (match) {
|
|
1281
|
+
const [, file, lineNum, content] = match;
|
|
1282
|
+
const colorMatch = content.match(/0x[0-9a-fA-F]{6}|#[0-9a-fA-F]{3,6}/);
|
|
1283
|
+
const color = colorMatch?.[0] ?? "unknown";
|
|
1284
|
+
console.log(`${file}:${lineNum} \u2192 ${color}`);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
console.log(`
|
|
1288
|
+
Total: ${lines.length} hardcoded color(s)`);
|
|
1289
|
+
console.log("\nUse colors from the 'open-color' (oc) library instead.");
|
|
1290
|
+
console.log("\nExample fix:");
|
|
1291
|
+
console.log(" Before: color: '#228be6'");
|
|
1292
|
+
console.log(" After: color: oc.blue[6]");
|
|
1293
|
+
console.log("\nImport open-color with: import oc from 'open-color'");
|
|
1294
|
+
process.exit(1);
|
|
1295
|
+
} catch {
|
|
1296
|
+
console.log("No hardcoded colors found.");
|
|
1297
|
+
process.exit(0);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// src/commands/verify/run/index.ts
|
|
1302
|
+
import { spawn } from "child_process";
|
|
1303
|
+
import * as path13 from "path";
|
|
1304
|
+
|
|
1305
|
+
// src/commands/verify/run/createTimerCallback/printTaskStatuses.ts
|
|
1306
|
+
function formatDuration(ms) {
|
|
1307
|
+
if (ms < 1e3) {
|
|
1308
|
+
return `${ms}ms`;
|
|
1309
|
+
}
|
|
1310
|
+
const seconds = (ms / 1e3).toFixed(1);
|
|
1311
|
+
return `${seconds}s`;
|
|
1312
|
+
}
|
|
1313
|
+
function printTaskStatuses(tasks) {
|
|
1314
|
+
console.log("\n--- Task Status ---");
|
|
1315
|
+
for (const task of tasks) {
|
|
1316
|
+
if (task.endTime !== void 0) {
|
|
1317
|
+
const duration = formatDuration(task.endTime - task.startTime);
|
|
1318
|
+
const status = task.code === 0 ? "\u2713" : "\u2717";
|
|
1319
|
+
console.log(` ${status} ${task.script}: ${duration}`);
|
|
1320
|
+
} else {
|
|
1321
|
+
const elapsed = formatDuration(Date.now() - task.startTime);
|
|
1322
|
+
console.log(` \u22EF ${task.script}: running (${elapsed})`);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
console.log("-------------------\n");
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// src/commands/verify/run/createTimerCallback/index.ts
|
|
1329
|
+
function logFailedScripts(failed) {
|
|
1330
|
+
console.error(`
|
|
1331
|
+
${failed.length} script(s) failed:`);
|
|
1332
|
+
for (const f of failed) {
|
|
1333
|
+
console.error(` - ${f.script} (exit code ${f.code})`);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
function createTimerCallback(taskStatuses, index) {
|
|
1337
|
+
return (exitCode) => {
|
|
1338
|
+
taskStatuses[index].endTime = Date.now();
|
|
1339
|
+
taskStatuses[index].code = exitCode;
|
|
1340
|
+
printTaskStatuses(taskStatuses);
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
function initTaskStatuses(scripts) {
|
|
1344
|
+
return scripts.map((script) => ({ script, startTime: Date.now() }));
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// src/commands/verify/run/index.ts
|
|
1348
|
+
function spawnScript(script, cwd) {
|
|
1349
|
+
return spawn("npm", ["run", script], { stdio: "inherit", shell: true, cwd });
|
|
1350
|
+
}
|
|
1351
|
+
function onScriptClose(script, onComplete, resolve2) {
|
|
1352
|
+
return (code) => {
|
|
1353
|
+
const exitCode = code ?? 1;
|
|
1354
|
+
onComplete?.(exitCode);
|
|
1355
|
+
resolve2({ script, code: exitCode });
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
function runScript(script, cwd, onComplete) {
|
|
1359
|
+
return new Promise((resolve2) => {
|
|
1360
|
+
spawnScript(script, cwd).on(
|
|
1361
|
+
"close",
|
|
1362
|
+
onScriptClose(script, onComplete, resolve2)
|
|
1363
|
+
);
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
function runAllScripts(verifyScripts, packageDir, timer) {
|
|
1367
|
+
const taskStatuses = initTaskStatuses(verifyScripts);
|
|
1368
|
+
return Promise.all(
|
|
1369
|
+
verifyScripts.map(
|
|
1370
|
+
(script, index) => runScript(
|
|
1371
|
+
script,
|
|
1372
|
+
packageDir,
|
|
1373
|
+
timer ? createTimerCallback(taskStatuses, index) : void 0
|
|
1374
|
+
)
|
|
1375
|
+
)
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
1378
|
+
function printScriptList(scripts) {
|
|
1379
|
+
console.log(`Running ${scripts.length} verify script(s) in parallel:`);
|
|
1380
|
+
for (const script of scripts) {
|
|
1381
|
+
console.log(` - ${script}`);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
function exitIfFailed(failed) {
|
|
1385
|
+
if (failed.length === 0) return;
|
|
1386
|
+
logFailedScripts(failed);
|
|
1387
|
+
process.exit(1);
|
|
1388
|
+
}
|
|
1389
|
+
function handleResults(results, totalCount) {
|
|
1390
|
+
exitIfFailed(results.filter((r) => r.code !== 0));
|
|
1391
|
+
console.log(`
|
|
1392
|
+
All ${totalCount} verify script(s) passed`);
|
|
1393
|
+
}
|
|
1394
|
+
function resolveVerifyScripts() {
|
|
1395
|
+
const result = findPackageJsonWithVerifyScripts(process.cwd());
|
|
1396
|
+
if (!result) {
|
|
1397
|
+
console.log("No package.json with verify:* scripts found");
|
|
1398
|
+
return null;
|
|
1399
|
+
}
|
|
1400
|
+
return result;
|
|
1401
|
+
}
|
|
1402
|
+
function getPackageDir(found) {
|
|
1403
|
+
return path13.dirname(found.packageJsonPath);
|
|
1404
|
+
}
|
|
1405
|
+
async function executeVerifyScripts(found, timer) {
|
|
1406
|
+
printScriptList(found.verifyScripts);
|
|
1407
|
+
const results = await runAllScripts(
|
|
1408
|
+
found.verifyScripts,
|
|
1409
|
+
getPackageDir(found),
|
|
1410
|
+
timer
|
|
1411
|
+
);
|
|
1412
|
+
handleResults(results, found.verifyScripts.length);
|
|
1413
|
+
}
|
|
1414
|
+
async function run(options2 = {}) {
|
|
1415
|
+
const found = resolveVerifyScripts();
|
|
1416
|
+
if (!found) return;
|
|
1417
|
+
await executeVerifyScripts(found, options2.timer ?? false);
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
// src/commands/new/initPackageJson.ts
|
|
1227
1421
|
import { execSync as execSync6 } from "child_process";
|
|
1228
|
-
|
|
1422
|
+
function initPackageJson(name) {
|
|
1423
|
+
console.log("Initializing package.json...");
|
|
1424
|
+
execSync6("npm init -y", { stdio: "inherit" });
|
|
1425
|
+
console.log("Configuring package.json...");
|
|
1426
|
+
execSync6("npm pkg delete main", { stdio: "inherit" });
|
|
1427
|
+
execSync6("npm pkg set type=module", { stdio: "inherit" });
|
|
1428
|
+
execSync6(`npm pkg set bin.${name}=./dist/index.js`, { stdio: "inherit" });
|
|
1429
|
+
execSync6("npm pkg set scripts.build=tsup", { stdio: "inherit" });
|
|
1430
|
+
execSync6('npm pkg set scripts.start="node dist/index.js"', {
|
|
1431
|
+
stdio: "inherit"
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// src/commands/new/writeCliTemplate.ts
|
|
1436
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
1437
|
+
function writeCliTemplate(name) {
|
|
1438
|
+
console.log("Writing tsconfig.json...");
|
|
1439
|
+
writeFileSync6(
|
|
1440
|
+
"tsconfig.json",
|
|
1441
|
+
JSON.stringify(
|
|
1442
|
+
{
|
|
1443
|
+
compilerOptions: {
|
|
1444
|
+
target: "ES2022",
|
|
1445
|
+
module: "ESNext",
|
|
1446
|
+
moduleResolution: "bundler",
|
|
1447
|
+
outDir: "./dist",
|
|
1448
|
+
rootDir: "./src",
|
|
1449
|
+
strict: true,
|
|
1450
|
+
esModuleInterop: true,
|
|
1451
|
+
resolveJsonModule: true,
|
|
1452
|
+
skipLibCheck: true,
|
|
1453
|
+
forceConsistentCasingInFileNames: true
|
|
1454
|
+
},
|
|
1455
|
+
include: ["src/**/*"],
|
|
1456
|
+
exclude: ["node_modules"]
|
|
1457
|
+
},
|
|
1458
|
+
null,
|
|
1459
|
+
" "
|
|
1460
|
+
)
|
|
1461
|
+
);
|
|
1462
|
+
console.log("Writing tsup.config.ts...");
|
|
1463
|
+
writeFileSync6(
|
|
1464
|
+
"tsup.config.ts",
|
|
1465
|
+
`import { defineConfig } from "tsup";
|
|
1466
|
+
export default defineConfig({
|
|
1467
|
+
entry: ["src/index.ts"],
|
|
1468
|
+
format: ["esm"],
|
|
1469
|
+
target: "node22",
|
|
1470
|
+
outDir: "dist",
|
|
1471
|
+
clean: true,
|
|
1472
|
+
shims: true,
|
|
1473
|
+
});
|
|
1474
|
+
`
|
|
1475
|
+
);
|
|
1476
|
+
console.log("Writing src/index.ts...");
|
|
1477
|
+
mkdirSync2("src", { recursive: true });
|
|
1478
|
+
writeFileSync6(
|
|
1479
|
+
"src/index.ts",
|
|
1480
|
+
`#!/usr/bin/env node
|
|
1481
|
+
import { Command } from "commander";
|
|
1482
|
+
const program = new Command();
|
|
1483
|
+
program.name("${name}").description("").version("0.0.0");
|
|
1484
|
+
program.parse();
|
|
1485
|
+
`
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
// src/commands/new/newCli.ts
|
|
1490
|
+
async function newCli() {
|
|
1491
|
+
const name = basename2(resolve("."));
|
|
1492
|
+
initPackageJson(name);
|
|
1493
|
+
console.log("Installing dependencies...");
|
|
1494
|
+
execSync7("npm install commander", { stdio: "inherit" });
|
|
1495
|
+
execSync7("npm install -D tsup typescript @types/node", {
|
|
1496
|
+
stdio: "inherit"
|
|
1497
|
+
});
|
|
1498
|
+
writeCliTemplate(name);
|
|
1499
|
+
await init4();
|
|
1500
|
+
await run();
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// src/commands/new/newProject.ts
|
|
1504
|
+
import { execSync as execSync9 } from "child_process";
|
|
1505
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
1229
1506
|
|
|
1230
1507
|
// src/commands/deploy/init/index.ts
|
|
1231
|
-
import { execSync as
|
|
1232
|
-
import
|
|
1508
|
+
import { execSync as execSync8 } from "child_process";
|
|
1509
|
+
import chalk21 from "chalk";
|
|
1233
1510
|
import enquirer3 from "enquirer";
|
|
1234
1511
|
|
|
1235
1512
|
// src/commands/deploy/init/updateWorkflow.ts
|
|
1236
|
-
import { existsSync as existsSync9, mkdirSync as
|
|
1237
|
-
import { dirname as
|
|
1513
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
1514
|
+
import { dirname as dirname10, join as join7 } from "path";
|
|
1238
1515
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1239
|
-
import
|
|
1516
|
+
import chalk20 from "chalk";
|
|
1240
1517
|
var WORKFLOW_PATH = ".github/workflows/build.yml";
|
|
1241
|
-
var __dirname3 =
|
|
1518
|
+
var __dirname3 = dirname10(fileURLToPath2(import.meta.url));
|
|
1242
1519
|
function getExistingSiteId() {
|
|
1243
1520
|
if (!existsSync9(WORKFLOW_PATH)) {
|
|
1244
1521
|
return null;
|
|
@@ -1256,72 +1533,72 @@ async function updateWorkflow(siteId) {
|
|
|
1256
1533
|
const newContent = getTemplateContent(siteId);
|
|
1257
1534
|
const workflowDir = ".github/workflows";
|
|
1258
1535
|
if (!existsSync9(workflowDir)) {
|
|
1259
|
-
|
|
1536
|
+
mkdirSync3(workflowDir, { recursive: true });
|
|
1260
1537
|
}
|
|
1261
1538
|
if (existsSync9(WORKFLOW_PATH)) {
|
|
1262
1539
|
const oldContent = readFileSync7(WORKFLOW_PATH, "utf-8");
|
|
1263
1540
|
if (oldContent === newContent) {
|
|
1264
|
-
console.log(
|
|
1541
|
+
console.log(chalk20.green("build.yml is already up to date"));
|
|
1265
1542
|
return;
|
|
1266
1543
|
}
|
|
1267
|
-
console.log(
|
|
1544
|
+
console.log(chalk20.yellow("\nbuild.yml will be updated:"));
|
|
1268
1545
|
console.log();
|
|
1269
1546
|
printDiff(oldContent, newContent);
|
|
1270
|
-
const confirm = await promptConfirm(
|
|
1547
|
+
const confirm = await promptConfirm(chalk20.red("Update build.yml?"));
|
|
1271
1548
|
if (!confirm) {
|
|
1272
1549
|
console.log("Skipped build.yml update");
|
|
1273
1550
|
return;
|
|
1274
1551
|
}
|
|
1275
1552
|
}
|
|
1276
|
-
|
|
1277
|
-
console.log(
|
|
1553
|
+
writeFileSync7(WORKFLOW_PATH, newContent);
|
|
1554
|
+
console.log(chalk20.green(`
|
|
1278
1555
|
Created ${WORKFLOW_PATH}`));
|
|
1279
1556
|
}
|
|
1280
1557
|
|
|
1281
1558
|
// src/commands/deploy/init/index.ts
|
|
1282
1559
|
async function ensureNetlifyCli() {
|
|
1283
1560
|
try {
|
|
1284
|
-
|
|
1561
|
+
execSync8("netlify sites:create --disable-linking", { stdio: "inherit" });
|
|
1285
1562
|
} catch (error) {
|
|
1286
1563
|
if (!(error instanceof Error) || !error.message.includes("command not found"))
|
|
1287
1564
|
throw error;
|
|
1288
|
-
console.error(
|
|
1565
|
+
console.error(chalk21.red("\nNetlify CLI is not installed.\n"));
|
|
1289
1566
|
const install = await promptConfirm("Would you like to install it now?");
|
|
1290
1567
|
if (!install) {
|
|
1291
1568
|
console.log(
|
|
1292
|
-
|
|
1569
|
+
chalk21.yellow(
|
|
1293
1570
|
"\nInstall it manually with: npm install -g netlify-cli\n"
|
|
1294
1571
|
)
|
|
1295
1572
|
);
|
|
1296
1573
|
process.exit(1);
|
|
1297
1574
|
}
|
|
1298
|
-
console.log(
|
|
1299
|
-
|
|
1575
|
+
console.log(chalk21.dim("\nInstalling netlify-cli...\n"));
|
|
1576
|
+
execSync8("npm install -g netlify-cli", { stdio: "inherit" });
|
|
1300
1577
|
console.log();
|
|
1301
|
-
|
|
1578
|
+
execSync8("netlify sites:create --disable-linking", { stdio: "inherit" });
|
|
1302
1579
|
}
|
|
1303
1580
|
}
|
|
1304
1581
|
function printSetupInstructions() {
|
|
1305
|
-
console.log(
|
|
1582
|
+
console.log(chalk21.bold("\nDeployment initialized successfully!"));
|
|
1306
1583
|
console.log(
|
|
1307
|
-
|
|
1584
|
+
chalk21.yellow("\nTo complete setup, create a personal access token at:")
|
|
1308
1585
|
);
|
|
1309
1586
|
console.log(
|
|
1310
|
-
|
|
1587
|
+
chalk21.cyan(
|
|
1311
1588
|
"https://app.netlify.com/user/applications#personal-access-tokens"
|
|
1312
1589
|
)
|
|
1313
1590
|
);
|
|
1314
1591
|
console.log(
|
|
1315
|
-
|
|
1592
|
+
chalk21.yellow(
|
|
1316
1593
|
"\nThen add it as NETLIFY_AUTH_TOKEN in your GitHub repository secrets."
|
|
1317
1594
|
)
|
|
1318
1595
|
);
|
|
1319
1596
|
}
|
|
1320
1597
|
async function init5() {
|
|
1321
|
-
console.log(
|
|
1598
|
+
console.log(chalk21.bold("Initializing Netlify deployment...\n"));
|
|
1322
1599
|
const existingSiteId = getExistingSiteId();
|
|
1323
1600
|
if (existingSiteId) {
|
|
1324
|
-
console.log(
|
|
1601
|
+
console.log(chalk21.dim(`Using existing site ID: ${existingSiteId}
|
|
1325
1602
|
`));
|
|
1326
1603
|
await updateWorkflow(existingSiteId);
|
|
1327
1604
|
return;
|
|
@@ -1341,7 +1618,7 @@ async function init5() {
|
|
|
1341
1618
|
// src/commands/new/newProject.ts
|
|
1342
1619
|
async function newProject() {
|
|
1343
1620
|
console.log("Initializing Vite with react-ts template...");
|
|
1344
|
-
|
|
1621
|
+
execSync9("npm create vite@latest . -- --template react-ts", {
|
|
1345
1622
|
stdio: "inherit"
|
|
1346
1623
|
});
|
|
1347
1624
|
removeEslint({ removeLintScripts: true });
|
|
@@ -1365,11 +1642,18 @@ function addViteBaseConfig() {
|
|
|
1365
1642
|
'defineConfig({\n base: "./",'
|
|
1366
1643
|
);
|
|
1367
1644
|
if (updated !== content) {
|
|
1368
|
-
|
|
1645
|
+
writeFileSync8(viteConfigPath, updated);
|
|
1369
1646
|
console.log('Added base: "./" to vite.config.ts');
|
|
1370
1647
|
}
|
|
1371
1648
|
}
|
|
1372
1649
|
|
|
1650
|
+
// src/commands/new/registerNew.ts
|
|
1651
|
+
function registerNew(program2) {
|
|
1652
|
+
const newCommand = program2.command("new").description("Scaffold a new project");
|
|
1653
|
+
newCommand.command("vite").description("Initialize a new Vite React TypeScript project").action(newProject);
|
|
1654
|
+
newCommand.command("cli").description("Initialize a new tsup CLI project").action(newCli);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1373
1657
|
// src/lib/readStdin.ts
|
|
1374
1658
|
import * as readline from "readline";
|
|
1375
1659
|
async function readStdin() {
|
|
@@ -1405,17 +1689,17 @@ function detectPlatform() {
|
|
|
1405
1689
|
}
|
|
1406
1690
|
|
|
1407
1691
|
// src/commands/notify/showNotification/showWindowsNotificationFromWsl.ts
|
|
1408
|
-
import { spawn } from "child_process";
|
|
1692
|
+
import { spawn as spawn2 } from "child_process";
|
|
1409
1693
|
import fs9 from "fs";
|
|
1410
1694
|
import { createRequire } from "module";
|
|
1411
|
-
import
|
|
1695
|
+
import path14 from "path";
|
|
1412
1696
|
var require2 = createRequire(import.meta.url);
|
|
1413
1697
|
function getSnoreToastPath() {
|
|
1414
|
-
const notifierPath =
|
|
1415
|
-
return
|
|
1698
|
+
const notifierPath = path14.dirname(require2.resolve("node-notifier"));
|
|
1699
|
+
return path14.join(notifierPath, "vendor", "snoreToast", "snoretoast-x64.exe");
|
|
1416
1700
|
}
|
|
1417
|
-
function showWindowsNotificationFromWsl(
|
|
1418
|
-
const { title, message, sound } =
|
|
1701
|
+
function showWindowsNotificationFromWsl(options2) {
|
|
1702
|
+
const { title, message, sound } = options2;
|
|
1419
1703
|
const snoreToastPath = getSnoreToastPath();
|
|
1420
1704
|
try {
|
|
1421
1705
|
fs9.chmodSync(snoreToastPath, 493);
|
|
@@ -1427,7 +1711,7 @@ function showWindowsNotificationFromWsl(options) {
|
|
|
1427
1711
|
} else if (sound === "Reminder") {
|
|
1428
1712
|
args.push("-s", "ms-winsoundevent:Notification.Reminder");
|
|
1429
1713
|
}
|
|
1430
|
-
const child =
|
|
1714
|
+
const child = spawn2(snoreToastPath, args, {
|
|
1431
1715
|
detached: true,
|
|
1432
1716
|
stdio: "ignore"
|
|
1433
1717
|
});
|
|
@@ -1436,8 +1720,8 @@ function showWindowsNotificationFromWsl(options) {
|
|
|
1436
1720
|
}
|
|
1437
1721
|
|
|
1438
1722
|
// src/commands/notify/showNotification/index.ts
|
|
1439
|
-
function showNotification(
|
|
1440
|
-
const { title, message, sound } =
|
|
1723
|
+
function showNotification(options2) {
|
|
1724
|
+
const { title, message, sound } = options2;
|
|
1441
1725
|
const platform = detectPlatform();
|
|
1442
1726
|
if (platform === "wsl") {
|
|
1443
1727
|
return showWindowsNotificationFromWsl({ title, message, sound });
|
|
@@ -1493,20 +1777,20 @@ async function notify() {
|
|
|
1493
1777
|
}
|
|
1494
1778
|
|
|
1495
1779
|
// src/commands/complexity/analyze.ts
|
|
1496
|
-
import
|
|
1780
|
+
import chalk27 from "chalk";
|
|
1497
1781
|
|
|
1498
1782
|
// src/commands/complexity/cyclomatic.ts
|
|
1499
|
-
import
|
|
1783
|
+
import chalk23 from "chalk";
|
|
1500
1784
|
|
|
1501
1785
|
// src/commands/complexity/shared/index.ts
|
|
1502
1786
|
import fs11 from "fs";
|
|
1503
|
-
import
|
|
1504
|
-
import
|
|
1787
|
+
import path16 from "path";
|
|
1788
|
+
import chalk22 from "chalk";
|
|
1505
1789
|
import ts5 from "typescript";
|
|
1506
1790
|
|
|
1507
1791
|
// src/commands/complexity/findSourceFiles.ts
|
|
1508
1792
|
import fs10 from "fs";
|
|
1509
|
-
import
|
|
1793
|
+
import path15 from "path";
|
|
1510
1794
|
import { minimatch } from "minimatch";
|
|
1511
1795
|
function applyIgnoreGlobs(files) {
|
|
1512
1796
|
const { complexity } = loadConfig();
|
|
@@ -1521,7 +1805,7 @@ function walk(dir, results) {
|
|
|
1521
1805
|
const extensions = [".ts", ".tsx"];
|
|
1522
1806
|
const entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
1523
1807
|
for (const entry of entries) {
|
|
1524
|
-
const fullPath =
|
|
1808
|
+
const fullPath = path15.join(dir, entry.name);
|
|
1525
1809
|
if (entry.isDirectory()) {
|
|
1526
1810
|
if (entry.name !== "node_modules" && entry.name !== ".git") {
|
|
1527
1811
|
walk(fullPath, results);
|
|
@@ -1737,7 +2021,7 @@ function countSloc(content) {
|
|
|
1737
2021
|
function createSourceFromFile(filePath) {
|
|
1738
2022
|
const content = fs11.readFileSync(filePath, "utf-8");
|
|
1739
2023
|
return ts5.createSourceFile(
|
|
1740
|
-
|
|
2024
|
+
path16.basename(filePath),
|
|
1741
2025
|
content,
|
|
1742
2026
|
ts5.ScriptTarget.Latest,
|
|
1743
2027
|
true,
|
|
@@ -1747,7 +2031,7 @@ function createSourceFromFile(filePath) {
|
|
|
1747
2031
|
function withSourceFiles(pattern2, callback) {
|
|
1748
2032
|
const files = findSourceFiles2(pattern2);
|
|
1749
2033
|
if (files.length === 0) {
|
|
1750
|
-
console.log(
|
|
2034
|
+
console.log(chalk22.yellow("No files found matching pattern"));
|
|
1751
2035
|
return void 0;
|
|
1752
2036
|
}
|
|
1753
2037
|
return callback(files);
|
|
@@ -1766,25 +2050,25 @@ function forEachFunction(files, callback) {
|
|
|
1766
2050
|
}
|
|
1767
2051
|
|
|
1768
2052
|
// src/commands/complexity/cyclomatic.ts
|
|
1769
|
-
async function cyclomatic(pattern2 = "**/*.ts",
|
|
2053
|
+
async function cyclomatic(pattern2 = "**/*.ts", options2 = {}) {
|
|
1770
2054
|
withSourceFiles(pattern2, (files) => {
|
|
1771
2055
|
const results = [];
|
|
1772
2056
|
let hasViolation = false;
|
|
1773
2057
|
forEachFunction(files, (file, name, node) => {
|
|
1774
2058
|
const complexity = calculateCyclomaticComplexity(node);
|
|
1775
2059
|
results.push({ file, name, complexity });
|
|
1776
|
-
if (
|
|
2060
|
+
if (options2.threshold !== void 0 && complexity > options2.threshold) {
|
|
1777
2061
|
hasViolation = true;
|
|
1778
2062
|
}
|
|
1779
2063
|
});
|
|
1780
2064
|
results.sort((a, b) => b.complexity - a.complexity);
|
|
1781
2065
|
for (const { file, name, complexity } of results) {
|
|
1782
|
-
const exceedsThreshold =
|
|
1783
|
-
const color = exceedsThreshold ?
|
|
1784
|
-
console.log(`${color(`${file}:${name}`)} \u2192 ${
|
|
2066
|
+
const exceedsThreshold = options2.threshold !== void 0 && complexity > options2.threshold;
|
|
2067
|
+
const color = exceedsThreshold ? chalk23.red : chalk23.white;
|
|
2068
|
+
console.log(`${color(`${file}:${name}`)} \u2192 ${chalk23.cyan(complexity)}`);
|
|
1785
2069
|
}
|
|
1786
2070
|
console.log(
|
|
1787
|
-
|
|
2071
|
+
chalk23.dim(
|
|
1788
2072
|
`
|
|
1789
2073
|
Analyzed ${results.length} functions across ${files.length} files`
|
|
1790
2074
|
)
|
|
@@ -1796,28 +2080,28 @@ Analyzed ${results.length} functions across ${files.length} files`
|
|
|
1796
2080
|
}
|
|
1797
2081
|
|
|
1798
2082
|
// src/commands/complexity/halstead.ts
|
|
1799
|
-
import
|
|
1800
|
-
async function halstead(pattern2 = "**/*.ts",
|
|
2083
|
+
import chalk24 from "chalk";
|
|
2084
|
+
async function halstead(pattern2 = "**/*.ts", options2 = {}) {
|
|
1801
2085
|
withSourceFiles(pattern2, (files) => {
|
|
1802
2086
|
const results = [];
|
|
1803
2087
|
let hasViolation = false;
|
|
1804
2088
|
forEachFunction(files, (file, name, node) => {
|
|
1805
2089
|
const metrics = calculateHalstead(node);
|
|
1806
2090
|
results.push({ file, name, metrics });
|
|
1807
|
-
if (
|
|
2091
|
+
if (options2.threshold !== void 0 && metrics.volume > options2.threshold) {
|
|
1808
2092
|
hasViolation = true;
|
|
1809
2093
|
}
|
|
1810
2094
|
});
|
|
1811
2095
|
results.sort((a, b) => b.metrics.effort - a.metrics.effort);
|
|
1812
2096
|
for (const { file, name, metrics } of results) {
|
|
1813
|
-
const exceedsThreshold =
|
|
1814
|
-
const color = exceedsThreshold ?
|
|
2097
|
+
const exceedsThreshold = options2.threshold !== void 0 && metrics.volume > options2.threshold;
|
|
2098
|
+
const color = exceedsThreshold ? chalk24.red : chalk24.white;
|
|
1815
2099
|
console.log(
|
|
1816
|
-
`${color(`${file}:${name}`)} \u2192 volume: ${
|
|
2100
|
+
`${color(`${file}:${name}`)} \u2192 volume: ${chalk24.cyan(metrics.volume.toFixed(1))}, difficulty: ${chalk24.yellow(metrics.difficulty.toFixed(1))}, effort: ${chalk24.magenta(metrics.effort.toFixed(1))}`
|
|
1817
2101
|
);
|
|
1818
2102
|
}
|
|
1819
2103
|
console.log(
|
|
1820
|
-
|
|
2104
|
+
chalk24.dim(
|
|
1821
2105
|
`
|
|
1822
2106
|
Analyzed ${results.length} functions across ${files.length} files`
|
|
1823
2107
|
)
|
|
@@ -1832,24 +2116,24 @@ Analyzed ${results.length} functions across ${files.length} files`
|
|
|
1832
2116
|
import fs12 from "fs";
|
|
1833
2117
|
|
|
1834
2118
|
// src/commands/complexity/maintainability/displayMaintainabilityResults.ts
|
|
1835
|
-
import
|
|
2119
|
+
import chalk25 from "chalk";
|
|
1836
2120
|
function displayMaintainabilityResults(results, threshold) {
|
|
1837
2121
|
const filtered = threshold !== void 0 ? results.filter((r) => r.minMaintainability < threshold) : results;
|
|
1838
2122
|
if (threshold !== void 0 && filtered.length === 0) {
|
|
1839
|
-
console.log(
|
|
2123
|
+
console.log(chalk25.green("All files pass maintainability threshold"));
|
|
1840
2124
|
} else {
|
|
1841
2125
|
for (const { file, avgMaintainability, minMaintainability } of filtered) {
|
|
1842
|
-
const color = threshold !== void 0 ?
|
|
2126
|
+
const color = threshold !== void 0 ? chalk25.red : chalk25.white;
|
|
1843
2127
|
console.log(
|
|
1844
|
-
`${color(file)} \u2192 avg: ${
|
|
2128
|
+
`${color(file)} \u2192 avg: ${chalk25.cyan(avgMaintainability.toFixed(1))}, min: ${chalk25.yellow(minMaintainability.toFixed(1))}`
|
|
1845
2129
|
);
|
|
1846
2130
|
}
|
|
1847
2131
|
}
|
|
1848
|
-
console.log(
|
|
2132
|
+
console.log(chalk25.dim(`
|
|
1849
2133
|
Analyzed ${results.length} files`));
|
|
1850
2134
|
if (filtered.length > 0 && threshold !== void 0) {
|
|
1851
2135
|
console.error(
|
|
1852
|
-
|
|
2136
|
+
chalk25.red(
|
|
1853
2137
|
`
|
|
1854
2138
|
Fail: ${filtered.length} file(s) below threshold ${threshold}. Maintainability index (0\u2013100) is derived from Halstead volume, cyclomatic complexity, and lines of code. Focus on one file at a time \u2014 run 'assist complexity <file>' to see all metrics. For larger files, start by extracting responsibilities into smaller files.`
|
|
1855
2139
|
)
|
|
@@ -1898,18 +2182,18 @@ function aggregateResults(fileMetrics) {
|
|
|
1898
2182
|
results.sort((a, b) => a.minMaintainability - b.minMaintainability);
|
|
1899
2183
|
return results;
|
|
1900
2184
|
}
|
|
1901
|
-
async function maintainability(pattern2 = "**/*.ts",
|
|
2185
|
+
async function maintainability(pattern2 = "**/*.ts", options2 = {}) {
|
|
1902
2186
|
withSourceFiles(pattern2, (files) => {
|
|
1903
2187
|
const fileMetrics = collectFileMetrics(files);
|
|
1904
2188
|
const results = aggregateResults(fileMetrics);
|
|
1905
|
-
displayMaintainabilityResults(results,
|
|
2189
|
+
displayMaintainabilityResults(results, options2.threshold);
|
|
1906
2190
|
});
|
|
1907
2191
|
}
|
|
1908
2192
|
|
|
1909
2193
|
// src/commands/complexity/sloc.ts
|
|
1910
2194
|
import fs13 from "fs";
|
|
1911
|
-
import
|
|
1912
|
-
async function sloc(pattern2 = "**/*.ts",
|
|
2195
|
+
import chalk26 from "chalk";
|
|
2196
|
+
async function sloc(pattern2 = "**/*.ts", options2 = {}) {
|
|
1913
2197
|
withSourceFiles(pattern2, (files) => {
|
|
1914
2198
|
const results = [];
|
|
1915
2199
|
let hasViolation = false;
|
|
@@ -1917,19 +2201,19 @@ async function sloc(pattern2 = "**/*.ts", options = {}) {
|
|
|
1917
2201
|
const content = fs13.readFileSync(file, "utf-8");
|
|
1918
2202
|
const lines = countSloc(content);
|
|
1919
2203
|
results.push({ file, lines });
|
|
1920
|
-
if (
|
|
2204
|
+
if (options2.threshold !== void 0 && lines > options2.threshold) {
|
|
1921
2205
|
hasViolation = true;
|
|
1922
2206
|
}
|
|
1923
2207
|
}
|
|
1924
2208
|
results.sort((a, b) => b.lines - a.lines);
|
|
1925
2209
|
for (const { file, lines } of results) {
|
|
1926
|
-
const exceedsThreshold =
|
|
1927
|
-
const color = exceedsThreshold ?
|
|
1928
|
-
console.log(`${color(file)} \u2192 ${
|
|
2210
|
+
const exceedsThreshold = options2.threshold !== void 0 && lines > options2.threshold;
|
|
2211
|
+
const color = exceedsThreshold ? chalk26.red : chalk26.white;
|
|
2212
|
+
console.log(`${color(file)} \u2192 ${chalk26.cyan(lines)} lines`);
|
|
1929
2213
|
}
|
|
1930
2214
|
const total = results.reduce((sum, r) => sum + r.lines, 0);
|
|
1931
2215
|
console.log(
|
|
1932
|
-
|
|
2216
|
+
chalk26.dim(`
|
|
1933
2217
|
Total: ${total} lines across ${files.length} files`)
|
|
1934
2218
|
);
|
|
1935
2219
|
if (hasViolation) {
|
|
@@ -1943,21 +2227,21 @@ async function analyze(pattern2) {
|
|
|
1943
2227
|
const searchPattern = pattern2.includes("*") || pattern2.includes("/") ? pattern2 : `**/${pattern2}`;
|
|
1944
2228
|
const files = findSourceFiles2(searchPattern);
|
|
1945
2229
|
if (files.length === 0) {
|
|
1946
|
-
console.log(
|
|
2230
|
+
console.log(chalk27.yellow("No files found matching pattern"));
|
|
1947
2231
|
return;
|
|
1948
2232
|
}
|
|
1949
2233
|
if (files.length === 1) {
|
|
1950
2234
|
const file = files[0];
|
|
1951
|
-
console.log(
|
|
2235
|
+
console.log(chalk27.bold.underline("SLOC"));
|
|
1952
2236
|
await sloc(file);
|
|
1953
2237
|
console.log();
|
|
1954
|
-
console.log(
|
|
2238
|
+
console.log(chalk27.bold.underline("Cyclomatic Complexity"));
|
|
1955
2239
|
await cyclomatic(file);
|
|
1956
2240
|
console.log();
|
|
1957
|
-
console.log(
|
|
2241
|
+
console.log(chalk27.bold.underline("Halstead Metrics"));
|
|
1958
2242
|
await halstead(file);
|
|
1959
2243
|
console.log();
|
|
1960
|
-
console.log(
|
|
2244
|
+
console.log(chalk27.bold.underline("Maintainability Index"));
|
|
1961
2245
|
await maintainability(file);
|
|
1962
2246
|
return;
|
|
1963
2247
|
}
|
|
@@ -1984,8 +2268,8 @@ function registerComplexity(program2) {
|
|
|
1984
2268
|
}
|
|
1985
2269
|
|
|
1986
2270
|
// src/commands/deploy/redirect.ts
|
|
1987
|
-
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as
|
|
1988
|
-
import
|
|
2271
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
2272
|
+
import chalk28 from "chalk";
|
|
1989
2273
|
var TRAILING_SLASH_SCRIPT = ` <script>
|
|
1990
2274
|
if (!window.location.pathname.endsWith('/')) {
|
|
1991
2275
|
window.location.href = \`\${window.location.pathname}/\${window.location.search}\${window.location.hash}\`;
|
|
@@ -1994,22 +2278,22 @@ var TRAILING_SLASH_SCRIPT = ` <script>
|
|
|
1994
2278
|
function redirect() {
|
|
1995
2279
|
const indexPath = "index.html";
|
|
1996
2280
|
if (!existsSync11(indexPath)) {
|
|
1997
|
-
console.log(
|
|
2281
|
+
console.log(chalk28.yellow("No index.html found"));
|
|
1998
2282
|
return;
|
|
1999
2283
|
}
|
|
2000
2284
|
const content = readFileSync9(indexPath, "utf-8");
|
|
2001
2285
|
if (content.includes("window.location.pathname.endsWith('/')")) {
|
|
2002
|
-
console.log(
|
|
2286
|
+
console.log(chalk28.dim("Trailing slash script already present"));
|
|
2003
2287
|
return;
|
|
2004
2288
|
}
|
|
2005
2289
|
const headCloseIndex = content.indexOf("</head>");
|
|
2006
2290
|
if (headCloseIndex === -1) {
|
|
2007
|
-
console.log(
|
|
2291
|
+
console.log(chalk28.red("Could not find </head> tag in index.html"));
|
|
2008
2292
|
return;
|
|
2009
2293
|
}
|
|
2010
2294
|
const newContent = content.slice(0, headCloseIndex) + TRAILING_SLASH_SCRIPT + "\n " + content.slice(headCloseIndex);
|
|
2011
|
-
|
|
2012
|
-
console.log(
|
|
2295
|
+
writeFileSync9(indexPath, newContent);
|
|
2296
|
+
console.log(chalk28.green("Added trailing slash redirect to index.html"));
|
|
2013
2297
|
}
|
|
2014
2298
|
|
|
2015
2299
|
// src/commands/registerDeploy.ts
|
|
@@ -2020,12 +2304,12 @@ function registerDeploy(program2) {
|
|
|
2020
2304
|
}
|
|
2021
2305
|
|
|
2022
2306
|
// src/commands/devlog/list/index.ts
|
|
2023
|
-
import { execSync as
|
|
2024
|
-
import { basename as
|
|
2307
|
+
import { execSync as execSync11 } from "child_process";
|
|
2308
|
+
import { basename as basename3 } from "path";
|
|
2025
2309
|
|
|
2026
2310
|
// src/commands/devlog/shared.ts
|
|
2027
|
-
import { execSync as
|
|
2028
|
-
import
|
|
2311
|
+
import { execSync as execSync10 } from "child_process";
|
|
2312
|
+
import chalk29 from "chalk";
|
|
2029
2313
|
|
|
2030
2314
|
// src/commands/devlog/loadDevlogEntries.ts
|
|
2031
2315
|
import { readdirSync, readFileSync as readFileSync10 } from "fs";
|
|
@@ -2068,7 +2352,7 @@ function loadDevlogEntries(repoName) {
|
|
|
2068
2352
|
// src/commands/devlog/shared.ts
|
|
2069
2353
|
function getCommitFiles(hash) {
|
|
2070
2354
|
try {
|
|
2071
|
-
const output =
|
|
2355
|
+
const output = execSync10(`git show --name-only --format="" ${hash}`, {
|
|
2072
2356
|
encoding: "utf-8"
|
|
2073
2357
|
});
|
|
2074
2358
|
return output.trim().split("\n").filter(Boolean);
|
|
@@ -2086,13 +2370,13 @@ function shouldIgnoreCommit(files, ignorePaths) {
|
|
|
2086
2370
|
}
|
|
2087
2371
|
function printCommitsWithFiles(commits, ignore2, verbose) {
|
|
2088
2372
|
for (const commit2 of commits) {
|
|
2089
|
-
console.log(` ${
|
|
2373
|
+
console.log(` ${chalk29.yellow(commit2.hash)} ${commit2.message}`);
|
|
2090
2374
|
if (verbose) {
|
|
2091
2375
|
const visibleFiles = commit2.files.filter(
|
|
2092
2376
|
(file) => !ignore2.some((p) => file.startsWith(p))
|
|
2093
2377
|
);
|
|
2094
2378
|
for (const file of visibleFiles) {
|
|
2095
|
-
console.log(` ${
|
|
2379
|
+
console.log(` ${chalk29.dim(file)}`);
|
|
2096
2380
|
}
|
|
2097
2381
|
}
|
|
2098
2382
|
}
|
|
@@ -2117,29 +2401,29 @@ function parseGitLogCommits(output, ignore2, afterDate) {
|
|
|
2117
2401
|
}
|
|
2118
2402
|
|
|
2119
2403
|
// src/commands/devlog/list/printDateHeader.ts
|
|
2120
|
-
import
|
|
2404
|
+
import chalk30 from "chalk";
|
|
2121
2405
|
function printDateHeader(date, isSkipped, entries) {
|
|
2122
2406
|
if (isSkipped) {
|
|
2123
|
-
console.log(`${
|
|
2407
|
+
console.log(`${chalk30.bold.blue(date)} ${chalk30.dim("skipped")}`);
|
|
2124
2408
|
} else if (entries && entries.length > 0) {
|
|
2125
|
-
const entryInfo = entries.map((e) => `${
|
|
2126
|
-
console.log(`${
|
|
2409
|
+
const entryInfo = entries.map((e) => `${chalk30.green(e.version)} ${e.title}`).join(" | ");
|
|
2410
|
+
console.log(`${chalk30.bold.blue(date)} ${entryInfo}`);
|
|
2127
2411
|
} else {
|
|
2128
|
-
console.log(`${
|
|
2412
|
+
console.log(`${chalk30.bold.blue(date)} ${chalk30.red("\u26A0 devlog missing")}`);
|
|
2129
2413
|
}
|
|
2130
2414
|
}
|
|
2131
2415
|
|
|
2132
2416
|
// src/commands/devlog/list/index.ts
|
|
2133
|
-
function list(
|
|
2417
|
+
function list(options2) {
|
|
2134
2418
|
const config = loadConfig();
|
|
2135
|
-
const days =
|
|
2136
|
-
const ignore2 =
|
|
2419
|
+
const days = options2.days ?? 30;
|
|
2420
|
+
const ignore2 = options2.ignore ?? config.devlog?.ignore ?? [];
|
|
2137
2421
|
const skipDays = new Set(config.devlog?.skip?.days ?? []);
|
|
2138
|
-
const repoName =
|
|
2422
|
+
const repoName = basename3(process.cwd());
|
|
2139
2423
|
const devlogEntries = loadDevlogEntries(repoName);
|
|
2140
|
-
const reverseFlag =
|
|
2141
|
-
const limitFlag =
|
|
2142
|
-
const output =
|
|
2424
|
+
const reverseFlag = options2.reverse ? "--reverse " : "";
|
|
2425
|
+
const limitFlag = options2.reverse ? "" : "-n 500 ";
|
|
2426
|
+
const output = execSync11(
|
|
2143
2427
|
`git log ${reverseFlag}${limitFlag}--pretty=format:'%ad|%h|%s' --date=short`,
|
|
2144
2428
|
{ encoding: "utf-8" }
|
|
2145
2429
|
);
|
|
@@ -2147,8 +2431,8 @@ function list(options) {
|
|
|
2147
2431
|
let dateCount = 0;
|
|
2148
2432
|
let isFirst = true;
|
|
2149
2433
|
for (const [date, dateCommits] of commitsByDate) {
|
|
2150
|
-
if (
|
|
2151
|
-
if (date <
|
|
2434
|
+
if (options2.since) {
|
|
2435
|
+
if (date < options2.since) {
|
|
2152
2436
|
break;
|
|
2153
2437
|
}
|
|
2154
2438
|
} else if (dateCount >= days) {
|
|
@@ -2160,16 +2444,16 @@ function list(options) {
|
|
|
2160
2444
|
}
|
|
2161
2445
|
isFirst = false;
|
|
2162
2446
|
printDateHeader(date, skipDays.has(date), devlogEntries.get(date));
|
|
2163
|
-
printCommitsWithFiles(dateCommits, ignore2,
|
|
2447
|
+
printCommitsWithFiles(dateCommits, ignore2, options2.verbose ?? false);
|
|
2164
2448
|
}
|
|
2165
2449
|
}
|
|
2166
2450
|
|
|
2167
2451
|
// src/commands/devlog/getLastVersionInfo.ts
|
|
2168
|
-
import { execSync as
|
|
2452
|
+
import { execSync as execSync12 } from "child_process";
|
|
2169
2453
|
import semver from "semver";
|
|
2170
2454
|
function getVersionAtCommit(hash) {
|
|
2171
2455
|
try {
|
|
2172
|
-
const content =
|
|
2456
|
+
const content = execSync12(`git show ${hash}:package.json`, {
|
|
2173
2457
|
encoding: "utf-8"
|
|
2174
2458
|
});
|
|
2175
2459
|
const pkg = JSON.parse(content);
|
|
@@ -2184,7 +2468,7 @@ function stripToMinor(version2) {
|
|
|
2184
2468
|
}
|
|
2185
2469
|
function getLastVersionInfoFromGit() {
|
|
2186
2470
|
try {
|
|
2187
|
-
const output =
|
|
2471
|
+
const output = execSync12(
|
|
2188
2472
|
"git log -1 --pretty=format:'%ad|%h' --date=short",
|
|
2189
2473
|
{
|
|
2190
2474
|
encoding: "utf-8"
|
|
@@ -2227,25 +2511,25 @@ function bumpVersion(version2, type) {
|
|
|
2227
2511
|
}
|
|
2228
2512
|
|
|
2229
2513
|
// src/commands/devlog/next/displayNextEntry/index.ts
|
|
2230
|
-
import { execSync as
|
|
2231
|
-
import
|
|
2514
|
+
import { execSync as execSync13 } from "child_process";
|
|
2515
|
+
import chalk32 from "chalk";
|
|
2232
2516
|
|
|
2233
2517
|
// src/commands/devlog/next/displayNextEntry/displayVersion.ts
|
|
2234
|
-
import
|
|
2518
|
+
import chalk31 from "chalk";
|
|
2235
2519
|
function displayVersion(conventional, firstHash, patchVersion, minorVersion) {
|
|
2236
2520
|
if (conventional && firstHash) {
|
|
2237
2521
|
const version2 = getVersionAtCommit(firstHash);
|
|
2238
2522
|
if (version2) {
|
|
2239
|
-
console.log(`${
|
|
2523
|
+
console.log(`${chalk31.bold("version:")} ${stripToMinor(version2)}`);
|
|
2240
2524
|
} else {
|
|
2241
|
-
console.log(`${
|
|
2525
|
+
console.log(`${chalk31.bold("version:")} ${chalk31.red("unknown")}`);
|
|
2242
2526
|
}
|
|
2243
2527
|
} else if (patchVersion && minorVersion) {
|
|
2244
2528
|
console.log(
|
|
2245
|
-
`${
|
|
2529
|
+
`${chalk31.bold("version:")} ${patchVersion} (patch) or ${minorVersion} (minor)`
|
|
2246
2530
|
);
|
|
2247
2531
|
} else {
|
|
2248
|
-
console.log(`${
|
|
2532
|
+
console.log(`${chalk31.bold("version:")} v0.1 (initial)`);
|
|
2249
2533
|
}
|
|
2250
2534
|
}
|
|
2251
2535
|
|
|
@@ -2261,7 +2545,7 @@ function findTargetDate(commitsByDate, skipDays) {
|
|
|
2261
2545
|
return Array.from(commitsByDate.keys()).filter((d) => !skipDays.has(d)).sort()[0];
|
|
2262
2546
|
}
|
|
2263
2547
|
function fetchCommitsByDate(ignore2, lastDate) {
|
|
2264
|
-
const output =
|
|
2548
|
+
const output = execSync13(
|
|
2265
2549
|
"git log --pretty=format:'%ad|%h|%s' --date=short -n 500",
|
|
2266
2550
|
{ encoding: "utf-8" }
|
|
2267
2551
|
);
|
|
@@ -2276,8 +2560,8 @@ function printVersionInfo(config, lastInfo, firstHash) {
|
|
|
2276
2560
|
versions.minor
|
|
2277
2561
|
);
|
|
2278
2562
|
}
|
|
2279
|
-
function resolveIgnoreList(
|
|
2280
|
-
return
|
|
2563
|
+
function resolveIgnoreList(options2, config) {
|
|
2564
|
+
return options2.ignore ?? config.devlog?.ignore ?? [];
|
|
2281
2565
|
}
|
|
2282
2566
|
function resolveSkipDays(config) {
|
|
2283
2567
|
return new Set(config.devlog?.skip?.days ?? []);
|
|
@@ -2292,28 +2576,28 @@ function noCommitsMessage(hasLastInfo) {
|
|
|
2292
2576
|
return hasLastInfo ? "No commits after last versioned entry" : "No commits found";
|
|
2293
2577
|
}
|
|
2294
2578
|
function logName(repoName) {
|
|
2295
|
-
console.log(`${
|
|
2579
|
+
console.log(`${chalk32.bold("name:")} ${repoName}`);
|
|
2296
2580
|
}
|
|
2297
2581
|
function displayNextEntry(ctx, targetDate, commits) {
|
|
2298
2582
|
logName(ctx.repoName);
|
|
2299
2583
|
printVersionInfo(ctx.config, ctx.lastInfo, commits[0]?.hash);
|
|
2300
|
-
console.log(
|
|
2584
|
+
console.log(chalk32.bold.blue(targetDate));
|
|
2301
2585
|
printCommitsWithFiles(commits, ctx.ignore, ctx.verbose);
|
|
2302
2586
|
}
|
|
2303
2587
|
function logNoCommits(lastInfo) {
|
|
2304
|
-
console.log(
|
|
2588
|
+
console.log(chalk32.dim(noCommitsMessage(!!lastInfo)));
|
|
2305
2589
|
}
|
|
2306
2590
|
|
|
2307
2591
|
// src/commands/devlog/next/index.ts
|
|
2308
|
-
function resolveContextData(config,
|
|
2592
|
+
function resolveContextData(config, options2) {
|
|
2309
2593
|
const repoName = getRepoName();
|
|
2310
2594
|
const lastInfo = getLastVersionInfo(repoName, config);
|
|
2311
|
-
return { repoName, lastInfo, ignore: resolveIgnoreList(
|
|
2595
|
+
return { repoName, lastInfo, ignore: resolveIgnoreList(options2, config) };
|
|
2312
2596
|
}
|
|
2313
|
-
function buildContext(
|
|
2597
|
+
function buildContext(options2) {
|
|
2314
2598
|
const config = loadConfig();
|
|
2315
|
-
const data = resolveContextData(config,
|
|
2316
|
-
return { config, ...data, verbose:
|
|
2599
|
+
const data = resolveContextData(config, options2);
|
|
2600
|
+
return { config, ...data, verbose: options2.verbose ?? false };
|
|
2317
2601
|
}
|
|
2318
2602
|
function fetchNextCommits(ctx) {
|
|
2319
2603
|
const commitsByDate = fetchCommitsByDate(
|
|
@@ -2330,22 +2614,22 @@ function showResult(ctx, found) {
|
|
|
2330
2614
|
}
|
|
2331
2615
|
displayNextEntry(ctx, found.targetDate, found.commits);
|
|
2332
2616
|
}
|
|
2333
|
-
function next(
|
|
2334
|
-
const ctx = buildContext(
|
|
2617
|
+
function next(options2) {
|
|
2618
|
+
const ctx = buildContext(options2);
|
|
2335
2619
|
showResult(ctx, fetchNextCommits(ctx));
|
|
2336
2620
|
}
|
|
2337
2621
|
|
|
2338
2622
|
// src/commands/devlog/skip.ts
|
|
2339
|
-
import
|
|
2623
|
+
import chalk33 from "chalk";
|
|
2340
2624
|
function skip(date) {
|
|
2341
2625
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
2342
|
-
console.log(
|
|
2626
|
+
console.log(chalk33.red("Invalid date format. Use YYYY-MM-DD"));
|
|
2343
2627
|
process.exit(1);
|
|
2344
2628
|
}
|
|
2345
2629
|
const config = loadConfig();
|
|
2346
2630
|
const skipDays = config.devlog?.skip?.days ?? [];
|
|
2347
2631
|
if (skipDays.includes(date)) {
|
|
2348
|
-
console.log(
|
|
2632
|
+
console.log(chalk33.yellow(`${date} is already in skip list`));
|
|
2349
2633
|
return;
|
|
2350
2634
|
}
|
|
2351
2635
|
skipDays.push(date);
|
|
@@ -2358,20 +2642,20 @@ function skip(date) {
|
|
|
2358
2642
|
}
|
|
2359
2643
|
};
|
|
2360
2644
|
saveConfig(config);
|
|
2361
|
-
console.log(
|
|
2645
|
+
console.log(chalk33.green(`Added ${date} to skip list`));
|
|
2362
2646
|
}
|
|
2363
2647
|
|
|
2364
2648
|
// src/commands/devlog/version.ts
|
|
2365
|
-
import
|
|
2649
|
+
import chalk34 from "chalk";
|
|
2366
2650
|
function version() {
|
|
2367
2651
|
const config = loadConfig();
|
|
2368
2652
|
const name = getRepoName();
|
|
2369
2653
|
const lastInfo = getLastVersionInfo(name, config);
|
|
2370
2654
|
const lastVersion = lastInfo?.version ?? null;
|
|
2371
2655
|
const nextVersion = lastVersion ? bumpVersion(lastVersion, "patch") : null;
|
|
2372
|
-
console.log(`${
|
|
2373
|
-
console.log(`${
|
|
2374
|
-
console.log(`${
|
|
2656
|
+
console.log(`${chalk34.bold("name:")} ${name}`);
|
|
2657
|
+
console.log(`${chalk34.bold("last:")} ${lastVersion ?? chalk34.dim("none")}`);
|
|
2658
|
+
console.log(`${chalk34.bold("next:")} ${nextVersion ?? chalk34.dim("none")}`);
|
|
2375
2659
|
}
|
|
2376
2660
|
|
|
2377
2661
|
// src/commands/registerDevlog.ts
|
|
@@ -2388,11 +2672,11 @@ function registerDevlog(program2) {
|
|
|
2388
2672
|
}
|
|
2389
2673
|
|
|
2390
2674
|
// src/commands/prs/fixed.ts
|
|
2391
|
-
import { execSync as
|
|
2675
|
+
import { execSync as execSync16 } from "child_process";
|
|
2392
2676
|
|
|
2393
2677
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
2394
|
-
import { execSync as
|
|
2395
|
-
import { unlinkSync as unlinkSync3, writeFileSync as
|
|
2678
|
+
import { execSync as execSync15 } from "child_process";
|
|
2679
|
+
import { unlinkSync as unlinkSync3, writeFileSync as writeFileSync10 } from "fs";
|
|
2396
2680
|
import { tmpdir } from "os";
|
|
2397
2681
|
import { join as join10 } from "path";
|
|
2398
2682
|
|
|
@@ -2420,7 +2704,7 @@ function deleteCommentsCache(prNumber) {
|
|
|
2420
2704
|
}
|
|
2421
2705
|
|
|
2422
2706
|
// src/commands/prs/shared.ts
|
|
2423
|
-
import { execSync as
|
|
2707
|
+
import { execSync as execSync14 } from "child_process";
|
|
2424
2708
|
function isGhNotInstalled(error) {
|
|
2425
2709
|
if (error instanceof Error) {
|
|
2426
2710
|
const msg = error.message.toLowerCase();
|
|
@@ -2436,14 +2720,14 @@ function isNotFound(error) {
|
|
|
2436
2720
|
}
|
|
2437
2721
|
function getRepoInfo() {
|
|
2438
2722
|
const repoInfo = JSON.parse(
|
|
2439
|
-
|
|
2723
|
+
execSync14("gh repo view --json owner,name", { encoding: "utf-8" })
|
|
2440
2724
|
);
|
|
2441
2725
|
return { org: repoInfo.owner.login, repo: repoInfo.name };
|
|
2442
2726
|
}
|
|
2443
2727
|
function getCurrentPrNumber() {
|
|
2444
2728
|
try {
|
|
2445
2729
|
const prInfo = JSON.parse(
|
|
2446
|
-
|
|
2730
|
+
execSync14("gh pr view --json number", { encoding: "utf-8" })
|
|
2447
2731
|
);
|
|
2448
2732
|
return prInfo.number;
|
|
2449
2733
|
} catch (error) {
|
|
@@ -2457,7 +2741,7 @@ function getCurrentPrNumber() {
|
|
|
2457
2741
|
|
|
2458
2742
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
2459
2743
|
function replyToComment(org, repo, prNumber, commentId, message) {
|
|
2460
|
-
|
|
2744
|
+
execSync15(
|
|
2461
2745
|
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments -f body="${message.replace(/"/g, '\\"')}" -F in_reply_to=${commentId}`,
|
|
2462
2746
|
{ stdio: "inherit" }
|
|
2463
2747
|
);
|
|
@@ -2465,9 +2749,9 @@ function replyToComment(org, repo, prNumber, commentId, message) {
|
|
|
2465
2749
|
function resolveThread(threadId) {
|
|
2466
2750
|
const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
|
|
2467
2751
|
const queryFile = join10(tmpdir(), `gh-mutation-${Date.now()}.graphql`);
|
|
2468
|
-
|
|
2752
|
+
writeFileSync10(queryFile, mutation);
|
|
2469
2753
|
try {
|
|
2470
|
-
|
|
2754
|
+
execSync15(
|
|
2471
2755
|
`gh api graphql -F query=@${queryFile} -f threadId="${threadId}"`,
|
|
2472
2756
|
{ stdio: "inherit" }
|
|
2473
2757
|
);
|
|
@@ -2519,7 +2803,7 @@ function resolveCommentWithReply(commentId, message) {
|
|
|
2519
2803
|
// src/commands/prs/fixed.ts
|
|
2520
2804
|
function verifySha(sha) {
|
|
2521
2805
|
try {
|
|
2522
|
-
return
|
|
2806
|
+
return execSync16(`git rev-parse --verify ${sha}`, {
|
|
2523
2807
|
encoding: "utf-8"
|
|
2524
2808
|
}).trim();
|
|
2525
2809
|
} catch {
|
|
@@ -2545,7 +2829,7 @@ function fixed(commentId, sha) {
|
|
|
2545
2829
|
}
|
|
2546
2830
|
|
|
2547
2831
|
// src/commands/prs/listComments/index.ts
|
|
2548
|
-
import { existsSync as existsSync13, mkdirSync as
|
|
2832
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, writeFileSync as writeFileSync12 } from "fs";
|
|
2549
2833
|
import { join as join12 } from "path";
|
|
2550
2834
|
import { stringify } from "yaml";
|
|
2551
2835
|
|
|
@@ -2555,16 +2839,16 @@ function isClaudeCode() {
|
|
|
2555
2839
|
}
|
|
2556
2840
|
|
|
2557
2841
|
// src/commands/prs/fetchThreadIds.ts
|
|
2558
|
-
import { execSync as
|
|
2559
|
-
import { unlinkSync as unlinkSync4, writeFileSync as
|
|
2842
|
+
import { execSync as execSync17 } from "child_process";
|
|
2843
|
+
import { unlinkSync as unlinkSync4, writeFileSync as writeFileSync11 } from "fs";
|
|
2560
2844
|
import { tmpdir as tmpdir2 } from "os";
|
|
2561
2845
|
import { join as join11 } from "path";
|
|
2562
2846
|
var THREAD_QUERY = `query($owner: String!, $repo: String!, $prNumber: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $prNumber) { reviewThreads(first: 100) { nodes { id isResolved comments(first: 100) { nodes { databaseId } } } } } } }`;
|
|
2563
2847
|
function fetchThreadIds(org, repo, prNumber) {
|
|
2564
2848
|
const queryFile = join11(tmpdir2(), `gh-query-${Date.now()}.graphql`);
|
|
2565
|
-
|
|
2849
|
+
writeFileSync11(queryFile, THREAD_QUERY);
|
|
2566
2850
|
try {
|
|
2567
|
-
const result =
|
|
2851
|
+
const result = execSync17(
|
|
2568
2852
|
`gh api graphql -F query=@${queryFile} -F owner="${org}" -F repo="${repo}" -F prNumber=${prNumber}`,
|
|
2569
2853
|
{ encoding: "utf-8" }
|
|
2570
2854
|
);
|
|
@@ -2586,9 +2870,9 @@ function fetchThreadIds(org, repo, prNumber) {
|
|
|
2586
2870
|
}
|
|
2587
2871
|
|
|
2588
2872
|
// src/commands/prs/listComments/fetchReviewComments.ts
|
|
2589
|
-
import { execSync as
|
|
2873
|
+
import { execSync as execSync18 } from "child_process";
|
|
2590
2874
|
function fetchJson(endpoint) {
|
|
2591
|
-
const result =
|
|
2875
|
+
const result = execSync18(`gh api ${endpoint}`, { encoding: "utf-8" });
|
|
2592
2876
|
if (!result.trim()) return [];
|
|
2593
2877
|
return JSON.parse(result);
|
|
2594
2878
|
}
|
|
@@ -2628,20 +2912,20 @@ function fetchLineComments(org, repo, prNumber, threadInfo) {
|
|
|
2628
2912
|
}
|
|
2629
2913
|
|
|
2630
2914
|
// src/commands/prs/listComments/formatForHuman.ts
|
|
2631
|
-
import
|
|
2915
|
+
import chalk35 from "chalk";
|
|
2632
2916
|
function formatForHuman(comment) {
|
|
2633
2917
|
if (comment.type === "review") {
|
|
2634
|
-
const stateColor = comment.state === "APPROVED" ?
|
|
2918
|
+
const stateColor = comment.state === "APPROVED" ? chalk35.green : comment.state === "CHANGES_REQUESTED" ? chalk35.red : chalk35.yellow;
|
|
2635
2919
|
return [
|
|
2636
|
-
`${
|
|
2920
|
+
`${chalk35.cyan("Review")} by ${chalk35.bold(comment.user)} ${stateColor(`[${comment.state}]`)}`,
|
|
2637
2921
|
comment.body,
|
|
2638
2922
|
""
|
|
2639
2923
|
].join("\n");
|
|
2640
2924
|
}
|
|
2641
2925
|
const location = comment.line ? `:${comment.line}` : "";
|
|
2642
2926
|
return [
|
|
2643
|
-
`${
|
|
2644
|
-
|
|
2927
|
+
`${chalk35.cyan("Line comment")} by ${chalk35.bold(comment.user)} on ${chalk35.dim(`${comment.path}${location}`)}`,
|
|
2928
|
+
chalk35.dim(comment.diff_hunk.split("\n").slice(-3).join("\n")),
|
|
2645
2929
|
comment.body,
|
|
2646
2930
|
""
|
|
2647
2931
|
].join("\n");
|
|
@@ -2666,7 +2950,7 @@ function printComments(comments) {
|
|
|
2666
2950
|
function writeCommentsCache(prNumber, comments) {
|
|
2667
2951
|
const assistDir = join12(process.cwd(), ".assist");
|
|
2668
2952
|
if (!existsSync13(assistDir)) {
|
|
2669
|
-
|
|
2953
|
+
mkdirSync4(assistDir, { recursive: true });
|
|
2670
2954
|
}
|
|
2671
2955
|
const cacheData = {
|
|
2672
2956
|
prNumber,
|
|
@@ -2674,7 +2958,7 @@ function writeCommentsCache(prNumber, comments) {
|
|
|
2674
2958
|
comments
|
|
2675
2959
|
};
|
|
2676
2960
|
const cachePath = join12(assistDir, `pr-${prNumber}-comments.yaml`);
|
|
2677
|
-
|
|
2961
|
+
writeFileSync12(cachePath, stringify(cacheData));
|
|
2678
2962
|
}
|
|
2679
2963
|
function handleKnownErrors(error) {
|
|
2680
2964
|
if (isGhNotInstalled(error)) {
|
|
@@ -2714,19 +2998,19 @@ async function listComments() {
|
|
|
2714
2998
|
}
|
|
2715
2999
|
|
|
2716
3000
|
// src/commands/prs/prs/index.ts
|
|
2717
|
-
import { execSync as
|
|
3001
|
+
import { execSync as execSync19 } from "child_process";
|
|
2718
3002
|
|
|
2719
3003
|
// src/commands/prs/prs/displayPaginated/index.ts
|
|
2720
3004
|
import enquirer4 from "enquirer";
|
|
2721
3005
|
|
|
2722
3006
|
// src/commands/prs/prs/displayPaginated/printPr.ts
|
|
2723
|
-
import
|
|
3007
|
+
import chalk36 from "chalk";
|
|
2724
3008
|
var STATUS_MAP = {
|
|
2725
|
-
MERGED: (pr) => pr.mergedAt ? { label:
|
|
2726
|
-
CLOSED: (pr) => pr.closedAt ? { label:
|
|
3009
|
+
MERGED: (pr) => pr.mergedAt ? { label: chalk36.magenta("merged"), date: pr.mergedAt } : null,
|
|
3010
|
+
CLOSED: (pr) => pr.closedAt ? { label: chalk36.red("closed"), date: pr.closedAt } : null
|
|
2727
3011
|
};
|
|
2728
3012
|
function defaultStatus(pr) {
|
|
2729
|
-
return { label:
|
|
3013
|
+
return { label: chalk36.green("opened"), date: pr.createdAt };
|
|
2730
3014
|
}
|
|
2731
3015
|
function getStatus(pr) {
|
|
2732
3016
|
return STATUS_MAP[pr.state]?.(pr) ?? defaultStatus(pr);
|
|
@@ -2735,11 +3019,11 @@ function formatDate(dateStr) {
|
|
|
2735
3019
|
return new Date(dateStr).toISOString().split("T")[0];
|
|
2736
3020
|
}
|
|
2737
3021
|
function formatPrHeader(pr, status) {
|
|
2738
|
-
return `${
|
|
3022
|
+
return `${chalk36.cyan(`#${pr.number}`)} ${pr.title} ${chalk36.dim(`(${pr.author.login},`)} ${status.label} ${chalk36.dim(`${formatDate(status.date)})`)}`;
|
|
2739
3023
|
}
|
|
2740
3024
|
function logPrDetails(pr) {
|
|
2741
3025
|
console.log(
|
|
2742
|
-
|
|
3026
|
+
chalk36.dim(` ${pr.changedFiles.toLocaleString()} files | ${pr.url}`)
|
|
2743
3027
|
);
|
|
2744
3028
|
console.log();
|
|
2745
3029
|
}
|
|
@@ -2817,10 +3101,10 @@ async function displayPaginated(pullRequests) {
|
|
|
2817
3101
|
}
|
|
2818
3102
|
|
|
2819
3103
|
// src/commands/prs/prs/index.ts
|
|
2820
|
-
async function prs(
|
|
2821
|
-
const state =
|
|
3104
|
+
async function prs(options2) {
|
|
3105
|
+
const state = options2.open ? "open" : options2.closed ? "closed" : "all";
|
|
2822
3106
|
try {
|
|
2823
|
-
const result =
|
|
3107
|
+
const result = execSync19(
|
|
2824
3108
|
`gh pr list --state ${state} --json number,title,url,author,createdAt,mergedAt,closedAt,state,changedFiles --limit 100`,
|
|
2825
3109
|
{ encoding: "utf-8" }
|
|
2826
3110
|
);
|
|
@@ -2843,7 +3127,7 @@ async function prs(options) {
|
|
|
2843
3127
|
}
|
|
2844
3128
|
|
|
2845
3129
|
// src/commands/prs/wontfix.ts
|
|
2846
|
-
import { execSync as
|
|
3130
|
+
import { execSync as execSync20 } from "child_process";
|
|
2847
3131
|
function validateReason(reason) {
|
|
2848
3132
|
const lowerReason = reason.toLowerCase();
|
|
2849
3133
|
if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
|
|
@@ -2860,7 +3144,7 @@ function validateShaReferences(reason) {
|
|
|
2860
3144
|
const invalidShas = [];
|
|
2861
3145
|
for (const sha of shas) {
|
|
2862
3146
|
try {
|
|
2863
|
-
|
|
3147
|
+
execSync20(`git cat-file -t ${sha}`, { stdio: "pipe" });
|
|
2864
3148
|
} catch {
|
|
2865
3149
|
invalidShas.push(sha);
|
|
2866
3150
|
}
|
|
@@ -2902,11 +3186,11 @@ function registerPrs(program2) {
|
|
|
2902
3186
|
}
|
|
2903
3187
|
|
|
2904
3188
|
// src/commands/refactor/check/index.ts
|
|
2905
|
-
import { spawn as
|
|
2906
|
-
import * as
|
|
3189
|
+
import { spawn as spawn3 } from "child_process";
|
|
3190
|
+
import * as path17 from "path";
|
|
2907
3191
|
|
|
2908
3192
|
// src/commands/refactor/logViolations.ts
|
|
2909
|
-
import
|
|
3193
|
+
import chalk37 from "chalk";
|
|
2910
3194
|
var DEFAULT_MAX_LINES = 100;
|
|
2911
3195
|
function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
2912
3196
|
if (violations.length === 0) {
|
|
@@ -2915,43 +3199,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
|
2915
3199
|
}
|
|
2916
3200
|
return;
|
|
2917
3201
|
}
|
|
2918
|
-
console.error(
|
|
3202
|
+
console.error(chalk37.red(`
|
|
2919
3203
|
Refactor check failed:
|
|
2920
3204
|
`));
|
|
2921
|
-
console.error(
|
|
3205
|
+
console.error(chalk37.red(` The following files exceed ${maxLines} lines:
|
|
2922
3206
|
`));
|
|
2923
3207
|
for (const violation of violations) {
|
|
2924
|
-
console.error(
|
|
3208
|
+
console.error(chalk37.red(` ${violation.file} (${violation.lines} lines)`));
|
|
2925
3209
|
}
|
|
2926
3210
|
console.error(
|
|
2927
|
-
|
|
3211
|
+
chalk37.yellow(
|
|
2928
3212
|
`
|
|
2929
3213
|
Each file needs to be sensibly refactored, or if there is no sensible
|
|
2930
3214
|
way to refactor it, ignore it with:
|
|
2931
3215
|
`
|
|
2932
3216
|
)
|
|
2933
3217
|
);
|
|
2934
|
-
console.error(
|
|
3218
|
+
console.error(chalk37.gray(` assist refactor ignore <file>
|
|
2935
3219
|
`));
|
|
2936
3220
|
if (process.env.CLAUDECODE) {
|
|
2937
|
-
console.error(
|
|
3221
|
+
console.error(chalk37.cyan(`
|
|
2938
3222
|
## Extracting Code to New Files
|
|
2939
3223
|
`));
|
|
2940
3224
|
console.error(
|
|
2941
|
-
|
|
3225
|
+
chalk37.cyan(
|
|
2942
3226
|
` When extracting logic from one file to another, consider where the extracted code belongs:
|
|
2943
3227
|
`
|
|
2944
3228
|
)
|
|
2945
3229
|
);
|
|
2946
3230
|
console.error(
|
|
2947
|
-
|
|
3231
|
+
chalk37.cyan(
|
|
2948
3232
|
` 1. Keep related logic together: If the extracted code is tightly coupled to the
|
|
2949
3233
|
original file's domain, create a new folder containing both the original and extracted files.
|
|
2950
3234
|
`
|
|
2951
3235
|
)
|
|
2952
3236
|
);
|
|
2953
3237
|
console.error(
|
|
2954
|
-
|
|
3238
|
+
chalk37.cyan(
|
|
2955
3239
|
` 2. Share common utilities: If the extracted code can be reused across multiple
|
|
2956
3240
|
domains, move it to a common/shared folder.
|
|
2957
3241
|
`
|
|
@@ -2961,7 +3245,7 @@ Refactor check failed:
|
|
|
2961
3245
|
}
|
|
2962
3246
|
|
|
2963
3247
|
// src/commands/refactor/check/getViolations/index.ts
|
|
2964
|
-
import { execSync as
|
|
3248
|
+
import { execSync as execSync21 } from "child_process";
|
|
2965
3249
|
import fs15 from "fs";
|
|
2966
3250
|
import { minimatch as minimatch2 } from "minimatch";
|
|
2967
3251
|
|
|
@@ -3005,31 +3289,31 @@ function countLines(filePath) {
|
|
|
3005
3289
|
const content = fs15.readFileSync(filePath, "utf-8");
|
|
3006
3290
|
return content.split("\n").length;
|
|
3007
3291
|
}
|
|
3008
|
-
function getGitFiles(
|
|
3009
|
-
if (!
|
|
3292
|
+
function getGitFiles(options2) {
|
|
3293
|
+
if (!options2.modified && !options2.staged && !options2.unstaged) {
|
|
3010
3294
|
return null;
|
|
3011
3295
|
}
|
|
3012
3296
|
const files = /* @__PURE__ */ new Set();
|
|
3013
|
-
if (
|
|
3014
|
-
const staged =
|
|
3297
|
+
if (options2.staged || options2.modified) {
|
|
3298
|
+
const staged = execSync21("git diff --cached --name-only", {
|
|
3015
3299
|
encoding: "utf-8"
|
|
3016
3300
|
});
|
|
3017
3301
|
for (const file of staged.trim().split("\n").filter(Boolean)) {
|
|
3018
3302
|
files.add(file);
|
|
3019
3303
|
}
|
|
3020
3304
|
}
|
|
3021
|
-
if (
|
|
3022
|
-
const unstaged =
|
|
3305
|
+
if (options2.unstaged || options2.modified) {
|
|
3306
|
+
const unstaged = execSync21("git diff --name-only", { encoding: "utf-8" });
|
|
3023
3307
|
for (const file of unstaged.trim().split("\n").filter(Boolean)) {
|
|
3024
3308
|
files.add(file);
|
|
3025
3309
|
}
|
|
3026
3310
|
}
|
|
3027
3311
|
return files;
|
|
3028
3312
|
}
|
|
3029
|
-
function getViolations(pattern2,
|
|
3313
|
+
function getViolations(pattern2, options2 = {}, maxLines = DEFAULT_MAX_LINES) {
|
|
3030
3314
|
let sourceFiles = findSourceFiles("src", { includeTests: false });
|
|
3031
3315
|
const ignoredFiles = getIgnoredFiles();
|
|
3032
|
-
const gitFiles = getGitFiles(
|
|
3316
|
+
const gitFiles = getGitFiles(options2);
|
|
3033
3317
|
if (pattern2) {
|
|
3034
3318
|
sourceFiles = sourceFiles.filter((f) => minimatch2(f, pattern2));
|
|
3035
3319
|
}
|
|
@@ -3048,9 +3332,9 @@ function getViolations(pattern2, options = {}, maxLines = DEFAULT_MAX_LINES) {
|
|
|
3048
3332
|
}
|
|
3049
3333
|
|
|
3050
3334
|
// src/commands/refactor/check/index.ts
|
|
3051
|
-
function
|
|
3052
|
-
return new Promise((
|
|
3053
|
-
const child =
|
|
3335
|
+
function runScript2(script, cwd) {
|
|
3336
|
+
return new Promise((resolve2) => {
|
|
3337
|
+
const child = spawn3("npm", ["run", script], {
|
|
3054
3338
|
stdio: "pipe",
|
|
3055
3339
|
shell: true,
|
|
3056
3340
|
cwd
|
|
@@ -3063,7 +3347,7 @@ function runScript(script, cwd) {
|
|
|
3063
3347
|
output += data.toString();
|
|
3064
3348
|
});
|
|
3065
3349
|
child.on("close", (code) => {
|
|
3066
|
-
|
|
3350
|
+
resolve2({ script, code: code ?? 1, output });
|
|
3067
3351
|
});
|
|
3068
3352
|
});
|
|
3069
3353
|
}
|
|
@@ -3080,9 +3364,9 @@ ${failed.length} verify script(s) failed:`);
|
|
|
3080
3364
|
async function runVerifyQuietly() {
|
|
3081
3365
|
const result = findPackageJsonWithVerifyScripts(process.cwd());
|
|
3082
3366
|
if (!result) return true;
|
|
3083
|
-
const packageDir =
|
|
3367
|
+
const packageDir = path17.dirname(result.packageJsonPath);
|
|
3084
3368
|
const results = await Promise.all(
|
|
3085
|
-
result.verifyScripts.map((script) =>
|
|
3369
|
+
result.verifyScripts.map((script) => runScript2(script, packageDir))
|
|
3086
3370
|
);
|
|
3087
3371
|
const failed = results.filter((r) => r.code !== 0);
|
|
3088
3372
|
if (failed.length > 0) {
|
|
@@ -3091,13 +3375,13 @@ async function runVerifyQuietly() {
|
|
|
3091
3375
|
}
|
|
3092
3376
|
return true;
|
|
3093
3377
|
}
|
|
3094
|
-
async function check(pattern2,
|
|
3378
|
+
async function check(pattern2, options2) {
|
|
3095
3379
|
const verifyPassed = await runVerifyQuietly();
|
|
3096
3380
|
if (!verifyPassed) {
|
|
3097
3381
|
process.exit(1);
|
|
3098
3382
|
}
|
|
3099
|
-
const maxLines =
|
|
3100
|
-
const violations = getViolations(pattern2,
|
|
3383
|
+
const maxLines = options2.maxLines ?? DEFAULT_MAX_LINES;
|
|
3384
|
+
const violations = getViolations(pattern2, options2, maxLines);
|
|
3101
3385
|
violations.sort((a, b) => b.lines - a.lines);
|
|
3102
3386
|
logViolations(violations, maxLines);
|
|
3103
3387
|
if (violations.length > 0) {
|
|
@@ -3107,11 +3391,11 @@ async function check(pattern2, options) {
|
|
|
3107
3391
|
|
|
3108
3392
|
// src/commands/refactor/ignore.ts
|
|
3109
3393
|
import fs16 from "fs";
|
|
3110
|
-
import
|
|
3394
|
+
import chalk38 from "chalk";
|
|
3111
3395
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
3112
3396
|
function ignore(file) {
|
|
3113
3397
|
if (!fs16.existsSync(file)) {
|
|
3114
|
-
console.error(
|
|
3398
|
+
console.error(chalk38.red(`Error: File does not exist: ${file}`));
|
|
3115
3399
|
process.exit(1);
|
|
3116
3400
|
}
|
|
3117
3401
|
const content = fs16.readFileSync(file, "utf-8");
|
|
@@ -3127,18 +3411,18 @@ function ignore(file) {
|
|
|
3127
3411
|
fs16.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
3128
3412
|
}
|
|
3129
3413
|
console.log(
|
|
3130
|
-
|
|
3414
|
+
chalk38.green(
|
|
3131
3415
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
3132
3416
|
)
|
|
3133
3417
|
);
|
|
3134
3418
|
}
|
|
3135
3419
|
|
|
3136
3420
|
// src/commands/refactor/restructure/index.ts
|
|
3137
|
-
import
|
|
3138
|
-
import
|
|
3421
|
+
import path26 from "path";
|
|
3422
|
+
import chalk41 from "chalk";
|
|
3139
3423
|
|
|
3140
3424
|
// src/commands/refactor/restructure/buildImportGraph/index.ts
|
|
3141
|
-
import
|
|
3425
|
+
import path18 from "path";
|
|
3142
3426
|
import ts7 from "typescript";
|
|
3143
3427
|
|
|
3144
3428
|
// src/commands/refactor/restructure/buildImportGraph/getImportSpecifiers.ts
|
|
@@ -3165,7 +3449,7 @@ function loadParsedConfig(tsConfigPath) {
|
|
|
3165
3449
|
return ts7.parseJsonConfigFileContent(
|
|
3166
3450
|
configFile.config,
|
|
3167
3451
|
ts7.sys,
|
|
3168
|
-
|
|
3452
|
+
path18.dirname(tsConfigPath)
|
|
3169
3453
|
);
|
|
3170
3454
|
}
|
|
3171
3455
|
function addToSetMap(map, key, value) {
|
|
@@ -3176,12 +3460,12 @@ function addToSetMap(map, key, value) {
|
|
|
3176
3460
|
}
|
|
3177
3461
|
set.add(value);
|
|
3178
3462
|
}
|
|
3179
|
-
function resolveImport(specifier, filePath,
|
|
3463
|
+
function resolveImport(specifier, filePath, options2) {
|
|
3180
3464
|
if (!specifier.startsWith(".")) return null;
|
|
3181
|
-
const resolved = ts7.resolveModuleName(specifier, filePath,
|
|
3465
|
+
const resolved = ts7.resolveModuleName(specifier, filePath, options2, ts7.sys);
|
|
3182
3466
|
const resolvedPath = resolved.resolvedModule?.resolvedFileName;
|
|
3183
3467
|
if (!resolvedPath || resolvedPath.includes("node_modules")) return null;
|
|
3184
|
-
return
|
|
3468
|
+
return path18.resolve(resolvedPath);
|
|
3185
3469
|
}
|
|
3186
3470
|
function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
3187
3471
|
const parsed = loadParsedConfig(tsConfigPath);
|
|
@@ -3190,7 +3474,7 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
3190
3474
|
const importedBy = /* @__PURE__ */ new Map();
|
|
3191
3475
|
const imports = /* @__PURE__ */ new Map();
|
|
3192
3476
|
for (const sourceFile of program2.getSourceFiles()) {
|
|
3193
|
-
const filePath =
|
|
3477
|
+
const filePath = path18.resolve(sourceFile.fileName);
|
|
3194
3478
|
if (filePath.includes("node_modules")) continue;
|
|
3195
3479
|
for (const specifier of getImportSpecifiers(sourceFile)) {
|
|
3196
3480
|
const absTarget = resolveImport(specifier, filePath, parsed.options);
|
|
@@ -3204,12 +3488,12 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
3204
3488
|
}
|
|
3205
3489
|
|
|
3206
3490
|
// src/commands/refactor/restructure/clusterDirectories.ts
|
|
3207
|
-
import
|
|
3491
|
+
import path19 from "path";
|
|
3208
3492
|
function clusterDirectories(graph) {
|
|
3209
3493
|
const dirImportedBy = /* @__PURE__ */ new Map();
|
|
3210
3494
|
for (const edge of graph.edges) {
|
|
3211
|
-
const sourceDir =
|
|
3212
|
-
const targetDir =
|
|
3495
|
+
const sourceDir = path19.dirname(edge.source);
|
|
3496
|
+
const targetDir = path19.dirname(edge.target);
|
|
3213
3497
|
if (sourceDir === targetDir) continue;
|
|
3214
3498
|
if (!graph.files.has(edge.target)) continue;
|
|
3215
3499
|
const existing = dirImportedBy.get(targetDir) ?? /* @__PURE__ */ new Set();
|
|
@@ -3237,20 +3521,20 @@ function clusterDirectories(graph) {
|
|
|
3237
3521
|
return clusters;
|
|
3238
3522
|
}
|
|
3239
3523
|
function isAncestor(ancestor, descendant) {
|
|
3240
|
-
const rel =
|
|
3524
|
+
const rel = path19.relative(ancestor, descendant);
|
|
3241
3525
|
return !rel.startsWith("..") && rel !== "";
|
|
3242
3526
|
}
|
|
3243
3527
|
|
|
3244
3528
|
// src/commands/refactor/restructure/clusterFiles.ts
|
|
3245
|
-
import
|
|
3529
|
+
import path20 from "path";
|
|
3246
3530
|
function findRootParent(file, importedBy, visited) {
|
|
3247
3531
|
const importers = importedBy.get(file);
|
|
3248
3532
|
if (!importers || importers.size !== 1) return file;
|
|
3249
3533
|
const parent = [...importers][0];
|
|
3250
|
-
const parentDir =
|
|
3251
|
-
const fileDir =
|
|
3534
|
+
const parentDir = path20.dirname(parent);
|
|
3535
|
+
const fileDir = path20.dirname(file);
|
|
3252
3536
|
if (parentDir !== fileDir) return file;
|
|
3253
|
-
if (
|
|
3537
|
+
if (path20.basename(parent, path20.extname(parent)) === "index") return file;
|
|
3254
3538
|
if (visited.has(parent)) return file;
|
|
3255
3539
|
visited.add(parent);
|
|
3256
3540
|
return findRootParent(parent, importedBy, visited);
|
|
@@ -3258,16 +3542,16 @@ function findRootParent(file, importedBy, visited) {
|
|
|
3258
3542
|
function clusterFiles(graph) {
|
|
3259
3543
|
const clusters = /* @__PURE__ */ new Map();
|
|
3260
3544
|
for (const file of graph.files) {
|
|
3261
|
-
const
|
|
3262
|
-
if (
|
|
3545
|
+
const basename7 = path20.basename(file, path20.extname(file));
|
|
3546
|
+
if (basename7 === "index") continue;
|
|
3263
3547
|
const importers = graph.importedBy.get(file);
|
|
3264
3548
|
if (!importers || importers.size !== 1) continue;
|
|
3265
3549
|
const parent = [...importers][0];
|
|
3266
3550
|
if (!graph.files.has(parent)) continue;
|
|
3267
|
-
const parentDir =
|
|
3268
|
-
const fileDir =
|
|
3551
|
+
const parentDir = path20.dirname(parent);
|
|
3552
|
+
const fileDir = path20.dirname(file);
|
|
3269
3553
|
if (parentDir !== fileDir) continue;
|
|
3270
|
-
const parentBasename =
|
|
3554
|
+
const parentBasename = path20.basename(parent, path20.extname(parent));
|
|
3271
3555
|
if (parentBasename === "index") continue;
|
|
3272
3556
|
const root = findRootParent(parent, graph.importedBy, /* @__PURE__ */ new Set([file]));
|
|
3273
3557
|
if (!root || root === file) continue;
|
|
@@ -3279,7 +3563,7 @@ function clusterFiles(graph) {
|
|
|
3279
3563
|
}
|
|
3280
3564
|
|
|
3281
3565
|
// src/commands/refactor/restructure/computeRewrites/index.ts
|
|
3282
|
-
import
|
|
3566
|
+
import path21 from "path";
|
|
3283
3567
|
|
|
3284
3568
|
// src/commands/refactor/restructure/computeRewrites/applyRewrites.ts
|
|
3285
3569
|
import fs17 from "fs";
|
|
@@ -3333,7 +3617,7 @@ function normalizeSpecifier(rel) {
|
|
|
3333
3617
|
);
|
|
3334
3618
|
}
|
|
3335
3619
|
function computeSpecifier(fromFile, toFile) {
|
|
3336
|
-
return normalizeSpecifier(
|
|
3620
|
+
return normalizeSpecifier(path21.relative(path21.dirname(fromFile), toFile));
|
|
3337
3621
|
}
|
|
3338
3622
|
function isAffected(edge, moveMap) {
|
|
3339
3623
|
return moveMap.has(edge.target) || moveMap.has(edge.source);
|
|
@@ -3377,51 +3661,51 @@ function computeRewrites(moves, edges, allProjectFiles) {
|
|
|
3377
3661
|
}
|
|
3378
3662
|
|
|
3379
3663
|
// src/commands/refactor/restructure/displayPlan.ts
|
|
3380
|
-
import
|
|
3381
|
-
import
|
|
3664
|
+
import path22 from "path";
|
|
3665
|
+
import chalk39 from "chalk";
|
|
3382
3666
|
function relPath(filePath) {
|
|
3383
|
-
return
|
|
3667
|
+
return path22.relative(process.cwd(), filePath);
|
|
3384
3668
|
}
|
|
3385
3669
|
function displayMoves(plan) {
|
|
3386
3670
|
if (plan.moves.length === 0) return;
|
|
3387
|
-
console.log(
|
|
3671
|
+
console.log(chalk39.bold("\nFile moves:"));
|
|
3388
3672
|
for (const move of plan.moves) {
|
|
3389
3673
|
console.log(
|
|
3390
|
-
` ${
|
|
3674
|
+
` ${chalk39.red(relPath(move.from))} \u2192 ${chalk39.green(relPath(move.to))}`
|
|
3391
3675
|
);
|
|
3392
|
-
console.log(
|
|
3676
|
+
console.log(chalk39.dim(` ${move.reason}`));
|
|
3393
3677
|
}
|
|
3394
3678
|
}
|
|
3395
3679
|
function displayRewrites(rewrites) {
|
|
3396
3680
|
if (rewrites.length === 0) return;
|
|
3397
3681
|
const affectedFiles = new Set(rewrites.map((r) => r.file));
|
|
3398
|
-
console.log(
|
|
3682
|
+
console.log(chalk39.bold(`
|
|
3399
3683
|
Import rewrites (${affectedFiles.size} files):`));
|
|
3400
3684
|
for (const file of affectedFiles) {
|
|
3401
|
-
console.log(` ${
|
|
3685
|
+
console.log(` ${chalk39.cyan(relPath(file))}:`);
|
|
3402
3686
|
for (const { oldSpecifier, newSpecifier } of rewrites.filter(
|
|
3403
3687
|
(r) => r.file === file
|
|
3404
3688
|
)) {
|
|
3405
3689
|
console.log(
|
|
3406
|
-
` ${
|
|
3690
|
+
` ${chalk39.red(`"${oldSpecifier}"`)} \u2192 ${chalk39.green(`"${newSpecifier}"`)}`
|
|
3407
3691
|
);
|
|
3408
3692
|
}
|
|
3409
3693
|
}
|
|
3410
3694
|
}
|
|
3411
3695
|
function displayPlan(plan) {
|
|
3412
3696
|
if (plan.warnings.length > 0) {
|
|
3413
|
-
console.log(
|
|
3414
|
-
for (const w of plan.warnings) console.log(
|
|
3697
|
+
console.log(chalk39.yellow("\nWarnings:"));
|
|
3698
|
+
for (const w of plan.warnings) console.log(chalk39.yellow(` ${w}`));
|
|
3415
3699
|
}
|
|
3416
3700
|
if (plan.newDirectories.length > 0) {
|
|
3417
|
-
console.log(
|
|
3701
|
+
console.log(chalk39.bold("\nNew directories:"));
|
|
3418
3702
|
for (const dir of plan.newDirectories)
|
|
3419
|
-
console.log(
|
|
3703
|
+
console.log(chalk39.green(` ${dir}/`));
|
|
3420
3704
|
}
|
|
3421
3705
|
displayMoves(plan);
|
|
3422
3706
|
displayRewrites(plan.rewrites);
|
|
3423
3707
|
console.log(
|
|
3424
|
-
|
|
3708
|
+
chalk39.dim(
|
|
3425
3709
|
`
|
|
3426
3710
|
Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rewritten`
|
|
3427
3711
|
)
|
|
@@ -3430,33 +3714,33 @@ Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rew
|
|
|
3430
3714
|
|
|
3431
3715
|
// src/commands/refactor/restructure/executePlan.ts
|
|
3432
3716
|
import fs18 from "fs";
|
|
3433
|
-
import
|
|
3434
|
-
import
|
|
3717
|
+
import path23 from "path";
|
|
3718
|
+
import chalk40 from "chalk";
|
|
3435
3719
|
function executePlan(plan) {
|
|
3436
3720
|
const updatedContents = applyRewrites(plan.rewrites);
|
|
3437
3721
|
for (const [file, content] of updatedContents) {
|
|
3438
3722
|
fs18.writeFileSync(file, content, "utf-8");
|
|
3439
3723
|
console.log(
|
|
3440
|
-
|
|
3724
|
+
chalk40.cyan(` Rewrote imports in ${path23.relative(process.cwd(), file)}`)
|
|
3441
3725
|
);
|
|
3442
3726
|
}
|
|
3443
3727
|
for (const dir of plan.newDirectories) {
|
|
3444
3728
|
fs18.mkdirSync(dir, { recursive: true });
|
|
3445
|
-
console.log(
|
|
3729
|
+
console.log(chalk40.green(` Created ${path23.relative(process.cwd(), dir)}/`));
|
|
3446
3730
|
}
|
|
3447
3731
|
for (const move of plan.moves) {
|
|
3448
|
-
const targetDir =
|
|
3732
|
+
const targetDir = path23.dirname(move.to);
|
|
3449
3733
|
if (!fs18.existsSync(targetDir)) {
|
|
3450
3734
|
fs18.mkdirSync(targetDir, { recursive: true });
|
|
3451
3735
|
}
|
|
3452
3736
|
fs18.renameSync(move.from, move.to);
|
|
3453
3737
|
console.log(
|
|
3454
|
-
|
|
3455
|
-
` Moved ${
|
|
3738
|
+
chalk40.white(
|
|
3739
|
+
` Moved ${path23.relative(process.cwd(), move.from)} \u2192 ${path23.relative(process.cwd(), move.to)}`
|
|
3456
3740
|
)
|
|
3457
3741
|
);
|
|
3458
3742
|
}
|
|
3459
|
-
removeEmptyDirectories(plan.moves.map((m) =>
|
|
3743
|
+
removeEmptyDirectories(plan.moves.map((m) => path23.dirname(m.from)));
|
|
3460
3744
|
}
|
|
3461
3745
|
function removeEmptyDirectories(dirs) {
|
|
3462
3746
|
const unique = [...new Set(dirs)];
|
|
@@ -3466,8 +3750,8 @@ function removeEmptyDirectories(dirs) {
|
|
|
3466
3750
|
if (entries.length === 0) {
|
|
3467
3751
|
fs18.rmdirSync(dir);
|
|
3468
3752
|
console.log(
|
|
3469
|
-
|
|
3470
|
-
` Removed empty directory ${
|
|
3753
|
+
chalk40.dim(
|
|
3754
|
+
` Removed empty directory ${path23.relative(process.cwd(), dir)}`
|
|
3471
3755
|
)
|
|
3472
3756
|
);
|
|
3473
3757
|
}
|
|
@@ -3476,13 +3760,13 @@ function removeEmptyDirectories(dirs) {
|
|
|
3476
3760
|
|
|
3477
3761
|
// src/commands/refactor/restructure/planFileMoves/index.ts
|
|
3478
3762
|
import fs20 from "fs";
|
|
3479
|
-
import
|
|
3763
|
+
import path25 from "path";
|
|
3480
3764
|
|
|
3481
3765
|
// src/commands/refactor/restructure/planFileMoves/planDirectoryMoves.ts
|
|
3482
3766
|
import fs19 from "fs";
|
|
3483
|
-
import
|
|
3767
|
+
import path24 from "path";
|
|
3484
3768
|
function collectEntry(results, dir, entry) {
|
|
3485
|
-
const full =
|
|
3769
|
+
const full = path24.join(dir, entry.name);
|
|
3486
3770
|
const items = entry.isDirectory() ? listFilesRecursive(full) : [full];
|
|
3487
3771
|
results.push(...items);
|
|
3488
3772
|
}
|
|
@@ -3496,15 +3780,15 @@ function listFilesRecursive(dir) {
|
|
|
3496
3780
|
}
|
|
3497
3781
|
function addDirectoryFileMoves(moves, childDir, newLocation, reason) {
|
|
3498
3782
|
for (const file of listFilesRecursive(childDir)) {
|
|
3499
|
-
const rel =
|
|
3500
|
-
moves.push({ from: file, to:
|
|
3783
|
+
const rel = path24.relative(childDir, file);
|
|
3784
|
+
moves.push({ from: file, to: path24.join(newLocation, rel), reason });
|
|
3501
3785
|
}
|
|
3502
3786
|
}
|
|
3503
3787
|
function resolveChildDest(parentDir, childDir) {
|
|
3504
|
-
return
|
|
3788
|
+
return path24.join(parentDir, path24.basename(childDir));
|
|
3505
3789
|
}
|
|
3506
3790
|
function childMoveReason(parentDir) {
|
|
3507
|
-
return `Directory only imported from ${
|
|
3791
|
+
return `Directory only imported from ${path24.basename(parentDir)}/`;
|
|
3508
3792
|
}
|
|
3509
3793
|
function registerDirectoryMove(result, childDir, dest, parentDir) {
|
|
3510
3794
|
result.directories.push(dest);
|
|
@@ -3532,7 +3816,7 @@ function emptyResult() {
|
|
|
3532
3816
|
return { moves: [], directories: [], warnings: [] };
|
|
3533
3817
|
}
|
|
3534
3818
|
function childMoveData(child, newDir, parentBase) {
|
|
3535
|
-
const to =
|
|
3819
|
+
const to = path25.join(newDir, path25.basename(child));
|
|
3536
3820
|
return { from: child, to, reason: `Only imported by ${parentBase}` };
|
|
3537
3821
|
}
|
|
3538
3822
|
function addChildMoves(moves, children, newDir, parentBase) {
|
|
@@ -3545,15 +3829,15 @@ function checkDirConflict(result, label, dir) {
|
|
|
3545
3829
|
return true;
|
|
3546
3830
|
}
|
|
3547
3831
|
function getBaseName(filePath) {
|
|
3548
|
-
return
|
|
3832
|
+
return path25.basename(filePath, path25.extname(filePath));
|
|
3549
3833
|
}
|
|
3550
3834
|
function resolveClusterDir(parent) {
|
|
3551
|
-
return
|
|
3835
|
+
return path25.join(path25.dirname(parent), getBaseName(parent));
|
|
3552
3836
|
}
|
|
3553
3837
|
function createParentMove(parent, newDir) {
|
|
3554
3838
|
return {
|
|
3555
3839
|
from: parent,
|
|
3556
|
-
to:
|
|
3840
|
+
to: path25.join(newDir, `index${path25.extname(parent)}`),
|
|
3557
3841
|
reason: `Main module of new ${getBaseName(parent)}/ directory`
|
|
3558
3842
|
};
|
|
3559
3843
|
}
|
|
@@ -3577,7 +3861,7 @@ function planFileMoves(clusters) {
|
|
|
3577
3861
|
|
|
3578
3862
|
// src/commands/refactor/restructure/index.ts
|
|
3579
3863
|
function buildPlan(candidateFiles, tsConfigPath) {
|
|
3580
|
-
const candidates = new Set(candidateFiles.map((f) =>
|
|
3864
|
+
const candidates = new Set(candidateFiles.map((f) => path26.resolve(f)));
|
|
3581
3865
|
const graph = buildImportGraph(candidates, tsConfigPath);
|
|
3582
3866
|
const allProjectFiles = /* @__PURE__ */ new Set([
|
|
3583
3867
|
...graph.importedBy.keys(),
|
|
@@ -3593,26 +3877,26 @@ function buildPlan(candidateFiles, tsConfigPath) {
|
|
|
3593
3877
|
const rewrites = computeRewrites(moves, graph.edges, allProjectFiles);
|
|
3594
3878
|
return { moves, rewrites, newDirectories: directories, warnings };
|
|
3595
3879
|
}
|
|
3596
|
-
async function restructure(pattern2,
|
|
3880
|
+
async function restructure(pattern2, options2 = {}) {
|
|
3597
3881
|
const targetPattern = pattern2 ?? "src";
|
|
3598
3882
|
const files = findSourceFiles2(targetPattern);
|
|
3599
3883
|
if (files.length === 0) {
|
|
3600
|
-
console.log(
|
|
3884
|
+
console.log(chalk41.yellow("No files found matching pattern"));
|
|
3601
3885
|
return;
|
|
3602
3886
|
}
|
|
3603
|
-
const tsConfigPath =
|
|
3887
|
+
const tsConfigPath = path26.resolve("tsconfig.json");
|
|
3604
3888
|
const plan = buildPlan(files, tsConfigPath);
|
|
3605
3889
|
if (plan.moves.length === 0) {
|
|
3606
|
-
console.log(
|
|
3890
|
+
console.log(chalk41.green("No restructuring needed"));
|
|
3607
3891
|
return;
|
|
3608
3892
|
}
|
|
3609
3893
|
displayPlan(plan);
|
|
3610
|
-
if (
|
|
3611
|
-
console.log(
|
|
3894
|
+
if (options2.apply) {
|
|
3895
|
+
console.log(chalk41.bold("\nApplying changes..."));
|
|
3612
3896
|
executePlan(plan);
|
|
3613
|
-
console.log(
|
|
3897
|
+
console.log(chalk41.green("\nRestructuring complete"));
|
|
3614
3898
|
} else {
|
|
3615
|
-
console.log(
|
|
3899
|
+
console.log(chalk41.dim("\nDry run. Use --apply to execute."));
|
|
3616
3900
|
}
|
|
3617
3901
|
}
|
|
3618
3902
|
|
|
@@ -3636,7 +3920,7 @@ function registerRefactor(program2) {
|
|
|
3636
3920
|
|
|
3637
3921
|
// src/commands/transcript/shared.ts
|
|
3638
3922
|
import { existsSync as existsSync14, readdirSync as readdirSync2, statSync } from "fs";
|
|
3639
|
-
import { basename as
|
|
3923
|
+
import { basename as basename4, join as join13, relative } from "path";
|
|
3640
3924
|
import * as readline2 from "readline";
|
|
3641
3925
|
var DATE_PREFIX_REGEX = /^\d{4}-\d{2}-\d{2}/;
|
|
3642
3926
|
function getDatePrefix(daysOffset = 0) {
|
|
@@ -3667,7 +3951,7 @@ function toFileInfo(baseDir, fullPath) {
|
|
|
3667
3951
|
return {
|
|
3668
3952
|
absolutePath: fullPath,
|
|
3669
3953
|
relativePath: relative(baseDir, fullPath),
|
|
3670
|
-
filename:
|
|
3954
|
+
filename: basename4(fullPath)
|
|
3671
3955
|
};
|
|
3672
3956
|
}
|
|
3673
3957
|
function findVttFilesRecursive(dir, baseDir = dir) {
|
|
@@ -3677,7 +3961,7 @@ function findMdFilesRecursive(dir, baseDir = dir) {
|
|
|
3677
3961
|
return collectFiles(dir, ".md").map((f) => toFileInfo(baseDir, f));
|
|
3678
3962
|
}
|
|
3679
3963
|
function getTranscriptBaseName(transcriptFile) {
|
|
3680
|
-
return
|
|
3964
|
+
return basename4(transcriptFile, ".md").replace(/ Transcription$/, "");
|
|
3681
3965
|
}
|
|
3682
3966
|
function createReadlineInterface() {
|
|
3683
3967
|
return readline2.createInterface({
|
|
@@ -3686,9 +3970,9 @@ function createReadlineInterface() {
|
|
|
3686
3970
|
});
|
|
3687
3971
|
}
|
|
3688
3972
|
function askQuestion(rl, question) {
|
|
3689
|
-
return new Promise((
|
|
3973
|
+
return new Promise((resolve2) => {
|
|
3690
3974
|
rl.question(question, (answer) => {
|
|
3691
|
-
|
|
3975
|
+
resolve2(answer.trim());
|
|
3692
3976
|
});
|
|
3693
3977
|
});
|
|
3694
3978
|
}
|
|
@@ -3751,7 +4035,7 @@ async function configure() {
|
|
|
3751
4035
|
import { existsSync as existsSync16 } from "fs";
|
|
3752
4036
|
|
|
3753
4037
|
// src/commands/transcript/format/fixInvalidDatePrefixes/index.ts
|
|
3754
|
-
import { dirname as
|
|
4038
|
+
import { dirname as dirname12, join as join15 } from "path";
|
|
3755
4039
|
|
|
3756
4040
|
// src/commands/transcript/format/fixInvalidDatePrefixes/promptForDateFix.ts
|
|
3757
4041
|
import { renameSync } from "fs";
|
|
@@ -3801,11 +4085,11 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
3801
4085
|
for (let i = 0; i < vttFiles.length; i++) {
|
|
3802
4086
|
const vttFile = vttFiles[i];
|
|
3803
4087
|
if (!isValidDatePrefix(vttFile.filename)) {
|
|
3804
|
-
const vttFileDir =
|
|
4088
|
+
const vttFileDir = dirname12(vttFile.absolutePath);
|
|
3805
4089
|
const newFilename = await promptForDateFix(vttFile.filename, vttFileDir);
|
|
3806
4090
|
if (newFilename) {
|
|
3807
4091
|
const newRelativePath = join15(
|
|
3808
|
-
|
|
4092
|
+
dirname12(vttFile.relativePath),
|
|
3809
4093
|
newFilename
|
|
3810
4094
|
);
|
|
3811
4095
|
vttFiles[i] = {
|
|
@@ -3822,8 +4106,8 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
3822
4106
|
}
|
|
3823
4107
|
|
|
3824
4108
|
// src/commands/transcript/format/processVttFile/index.ts
|
|
3825
|
-
import { existsSync as existsSync15, mkdirSync as
|
|
3826
|
-
import { basename as
|
|
4109
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync13 } from "fs";
|
|
4110
|
+
import { basename as basename5, dirname as dirname13, join as join16 } from "path";
|
|
3827
4111
|
|
|
3828
4112
|
// src/commands/transcript/cleanText.ts
|
|
3829
4113
|
function cleanText(text) {
|
|
@@ -4030,14 +4314,14 @@ function formatChatLog(messages) {
|
|
|
4030
4314
|
|
|
4031
4315
|
// src/commands/transcript/format/processVttFile/index.ts
|
|
4032
4316
|
function toMdFilename(vttFilename) {
|
|
4033
|
-
return `${
|
|
4317
|
+
return `${basename5(vttFilename, ".vtt").replace(/\s*Transcription\s*/g, " ").trim()}.md`;
|
|
4034
4318
|
}
|
|
4035
4319
|
function resolveOutputDir(relativeDir, transcriptsDir) {
|
|
4036
4320
|
return relativeDir === "." ? transcriptsDir : join16(transcriptsDir, relativeDir);
|
|
4037
4321
|
}
|
|
4038
4322
|
function buildOutputPaths(vttFile, transcriptsDir) {
|
|
4039
4323
|
const mdFile = toMdFilename(vttFile.filename);
|
|
4040
|
-
const relativeDir =
|
|
4324
|
+
const relativeDir = dirname13(vttFile.relativePath);
|
|
4041
4325
|
const outputDir = resolveOutputDir(relativeDir, transcriptsDir);
|
|
4042
4326
|
const outputPath = join16(outputDir, mdFile);
|
|
4043
4327
|
return { outputDir, outputPath, mdFile, relativeDir };
|
|
@@ -4048,7 +4332,7 @@ function logSkipped(relativeDir, mdFile) {
|
|
|
4048
4332
|
}
|
|
4049
4333
|
function ensureDirectory(dir, label) {
|
|
4050
4334
|
if (!existsSync15(dir)) {
|
|
4051
|
-
|
|
4335
|
+
mkdirSync5(dir, { recursive: true });
|
|
4052
4336
|
console.log(`Created ${label}: ${dir}`);
|
|
4053
4337
|
}
|
|
4054
4338
|
}
|
|
@@ -4073,7 +4357,7 @@ function readAndParseCues(inputPath) {
|
|
|
4073
4357
|
return processCues(readFileSync12(inputPath, "utf-8"));
|
|
4074
4358
|
}
|
|
4075
4359
|
function writeFormatted(outputPath, content) {
|
|
4076
|
-
|
|
4360
|
+
writeFileSync13(outputPath, content, "utf-8");
|
|
4077
4361
|
console.log(`Written: ${outputPath}`);
|
|
4078
4362
|
}
|
|
4079
4363
|
function convertVttToMarkdown(inputPath, outputPath) {
|
|
@@ -4142,27 +4426,27 @@ async function format() {
|
|
|
4142
4426
|
|
|
4143
4427
|
// src/commands/transcript/summarise/index.ts
|
|
4144
4428
|
import { existsSync as existsSync18 } from "fs";
|
|
4145
|
-
import { basename as
|
|
4429
|
+
import { basename as basename6, dirname as dirname15, join as join18, relative as relative2 } from "path";
|
|
4146
4430
|
|
|
4147
4431
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
4148
4432
|
import {
|
|
4149
4433
|
existsSync as existsSync17,
|
|
4150
|
-
mkdirSync as
|
|
4434
|
+
mkdirSync as mkdirSync6,
|
|
4151
4435
|
readFileSync as readFileSync13,
|
|
4152
4436
|
renameSync as renameSync2,
|
|
4153
4437
|
rmSync
|
|
4154
4438
|
} from "fs";
|
|
4155
|
-
import { dirname as
|
|
4439
|
+
import { dirname as dirname14, join as join17 } from "path";
|
|
4156
4440
|
|
|
4157
4441
|
// src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
|
|
4158
|
-
import
|
|
4442
|
+
import chalk42 from "chalk";
|
|
4159
4443
|
var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
|
|
4160
4444
|
function validateStagedContent(filename, content) {
|
|
4161
4445
|
const firstLine = content.split("\n")[0];
|
|
4162
4446
|
const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
|
|
4163
4447
|
if (!match) {
|
|
4164
4448
|
console.error(
|
|
4165
|
-
|
|
4449
|
+
chalk42.red(
|
|
4166
4450
|
`Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
|
|
4167
4451
|
)
|
|
4168
4452
|
);
|
|
@@ -4171,7 +4455,7 @@ function validateStagedContent(filename, content) {
|
|
|
4171
4455
|
const contentAfterLink = content.slice(firstLine.length).trim();
|
|
4172
4456
|
if (!contentAfterLink) {
|
|
4173
4457
|
console.error(
|
|
4174
|
-
|
|
4458
|
+
chalk42.red(
|
|
4175
4459
|
`Staged file ${filename} has no summary content after the transcript link.`
|
|
4176
4460
|
)
|
|
4177
4461
|
);
|
|
@@ -4206,9 +4490,9 @@ function processStagedFile() {
|
|
|
4206
4490
|
process.exit(1);
|
|
4207
4491
|
}
|
|
4208
4492
|
const destPath = join17(summaryDir, matchingTranscript.relativePath);
|
|
4209
|
-
const destDir =
|
|
4493
|
+
const destDir = dirname14(destPath);
|
|
4210
4494
|
if (!existsSync17(destDir)) {
|
|
4211
|
-
|
|
4495
|
+
mkdirSync6(destDir, { recursive: true });
|
|
4212
4496
|
}
|
|
4213
4497
|
renameSync2(stagedFile.absolutePath, destPath);
|
|
4214
4498
|
const remaining = findMdFilesRecursive(STAGING_DIR);
|
|
@@ -4220,14 +4504,14 @@ function processStagedFile() {
|
|
|
4220
4504
|
|
|
4221
4505
|
// src/commands/transcript/summarise/index.ts
|
|
4222
4506
|
function buildRelativeKey(relativePath, baseName) {
|
|
4223
|
-
const relDir =
|
|
4507
|
+
const relDir = dirname15(relativePath);
|
|
4224
4508
|
return relDir === "." ? baseName : join18(relDir, baseName);
|
|
4225
4509
|
}
|
|
4226
4510
|
function buildSummaryIndex(summaryDir) {
|
|
4227
4511
|
const summaryFiles = findMdFilesRecursive(summaryDir);
|
|
4228
4512
|
return new Set(
|
|
4229
4513
|
summaryFiles.map(
|
|
4230
|
-
(f) => buildRelativeKey(f.relativePath,
|
|
4514
|
+
(f) => buildRelativeKey(f.relativePath, basename6(f.filename, ".md"))
|
|
4231
4515
|
)
|
|
4232
4516
|
);
|
|
4233
4517
|
}
|
|
@@ -4256,7 +4540,7 @@ function summarise() {
|
|
|
4256
4540
|
const next2 = missing[0];
|
|
4257
4541
|
const outputFilename = `${getTranscriptBaseName(next2.filename)}.md`;
|
|
4258
4542
|
const outputPath = join18(STAGING_DIR, outputFilename);
|
|
4259
|
-
const summaryFileDir = join18(summaryDir,
|
|
4543
|
+
const summaryFileDir = join18(summaryDir, dirname15(next2.relativePath));
|
|
4260
4544
|
const relativeTranscriptPath = encodeURI(
|
|
4261
4545
|
relative2(summaryFileDir, next2.absolutePath).replace(/\\/g, "/")
|
|
4262
4546
|
);
|
|
@@ -4280,161 +4564,9 @@ function registerTranscript(program2) {
|
|
|
4280
4564
|
transcriptCommand.command("summarise").description("List transcripts that do not have summaries").action(summarise);
|
|
4281
4565
|
}
|
|
4282
4566
|
|
|
4283
|
-
// src/commands/verify/hardcodedColors.ts
|
|
4284
|
-
import { execSync as execSync19 } from "child_process";
|
|
4285
|
-
var pattern = "0x[0-9a-fA-F]{6}|#[0-9a-fA-F]{3,6}";
|
|
4286
|
-
function hardcodedColors() {
|
|
4287
|
-
try {
|
|
4288
|
-
const output = execSync19(`grep -rEnH '${pattern}' src/`, {
|
|
4289
|
-
encoding: "utf-8"
|
|
4290
|
-
});
|
|
4291
|
-
const lines = output.trim().split("\n");
|
|
4292
|
-
console.log("Hardcoded colors found:\n");
|
|
4293
|
-
for (const line of lines) {
|
|
4294
|
-
const match = line.match(/^(.+):(\d+):(.+)$/);
|
|
4295
|
-
if (match) {
|
|
4296
|
-
const [, file, lineNum, content] = match;
|
|
4297
|
-
const colorMatch = content.match(/0x[0-9a-fA-F]{6}|#[0-9a-fA-F]{3,6}/);
|
|
4298
|
-
const color = colorMatch?.[0] ?? "unknown";
|
|
4299
|
-
console.log(`${file}:${lineNum} \u2192 ${color}`);
|
|
4300
|
-
}
|
|
4301
|
-
}
|
|
4302
|
-
console.log(`
|
|
4303
|
-
Total: ${lines.length} hardcoded color(s)`);
|
|
4304
|
-
console.log("\nUse colors from the 'open-color' (oc) library instead.");
|
|
4305
|
-
console.log("\nExample fix:");
|
|
4306
|
-
console.log(" Before: color: '#228be6'");
|
|
4307
|
-
console.log(" After: color: oc.blue[6]");
|
|
4308
|
-
console.log("\nImport open-color with: import oc from 'open-color'");
|
|
4309
|
-
process.exit(1);
|
|
4310
|
-
} catch {
|
|
4311
|
-
console.log("No hardcoded colors found.");
|
|
4312
|
-
process.exit(0);
|
|
4313
|
-
}
|
|
4314
|
-
}
|
|
4315
|
-
|
|
4316
|
-
// src/commands/verify/run/index.ts
|
|
4317
|
-
import { spawn as spawn3 } from "child_process";
|
|
4318
|
-
import * as path25 from "path";
|
|
4319
|
-
|
|
4320
|
-
// src/commands/verify/run/createTimerCallback/printTaskStatuses.ts
|
|
4321
|
-
function formatDuration(ms) {
|
|
4322
|
-
if (ms < 1e3) {
|
|
4323
|
-
return `${ms}ms`;
|
|
4324
|
-
}
|
|
4325
|
-
const seconds = (ms / 1e3).toFixed(1);
|
|
4326
|
-
return `${seconds}s`;
|
|
4327
|
-
}
|
|
4328
|
-
function printTaskStatuses(tasks) {
|
|
4329
|
-
console.log("\n--- Task Status ---");
|
|
4330
|
-
for (const task of tasks) {
|
|
4331
|
-
if (task.endTime !== void 0) {
|
|
4332
|
-
const duration = formatDuration(task.endTime - task.startTime);
|
|
4333
|
-
const status = task.code === 0 ? "\u2713" : "\u2717";
|
|
4334
|
-
console.log(` ${status} ${task.script}: ${duration}`);
|
|
4335
|
-
} else {
|
|
4336
|
-
const elapsed = formatDuration(Date.now() - task.startTime);
|
|
4337
|
-
console.log(` \u22EF ${task.script}: running (${elapsed})`);
|
|
4338
|
-
}
|
|
4339
|
-
}
|
|
4340
|
-
console.log("-------------------\n");
|
|
4341
|
-
}
|
|
4342
|
-
|
|
4343
|
-
// src/commands/verify/run/createTimerCallback/index.ts
|
|
4344
|
-
function logFailedScripts(failed) {
|
|
4345
|
-
console.error(`
|
|
4346
|
-
${failed.length} script(s) failed:`);
|
|
4347
|
-
for (const f of failed) {
|
|
4348
|
-
console.error(` - ${f.script} (exit code ${f.code})`);
|
|
4349
|
-
}
|
|
4350
|
-
}
|
|
4351
|
-
function createTimerCallback(taskStatuses, index) {
|
|
4352
|
-
return (exitCode) => {
|
|
4353
|
-
taskStatuses[index].endTime = Date.now();
|
|
4354
|
-
taskStatuses[index].code = exitCode;
|
|
4355
|
-
printTaskStatuses(taskStatuses);
|
|
4356
|
-
};
|
|
4357
|
-
}
|
|
4358
|
-
function initTaskStatuses(scripts) {
|
|
4359
|
-
return scripts.map((script) => ({ script, startTime: Date.now() }));
|
|
4360
|
-
}
|
|
4361
|
-
|
|
4362
|
-
// src/commands/verify/run/index.ts
|
|
4363
|
-
function spawnScript(script, cwd) {
|
|
4364
|
-
return spawn3("npm", ["run", script], { stdio: "inherit", shell: true, cwd });
|
|
4365
|
-
}
|
|
4366
|
-
function onScriptClose(script, onComplete, resolve) {
|
|
4367
|
-
return (code) => {
|
|
4368
|
-
const exitCode = code ?? 1;
|
|
4369
|
-
onComplete?.(exitCode);
|
|
4370
|
-
resolve({ script, code: exitCode });
|
|
4371
|
-
};
|
|
4372
|
-
}
|
|
4373
|
-
function runScript2(script, cwd, onComplete) {
|
|
4374
|
-
return new Promise((resolve) => {
|
|
4375
|
-
spawnScript(script, cwd).on(
|
|
4376
|
-
"close",
|
|
4377
|
-
onScriptClose(script, onComplete, resolve)
|
|
4378
|
-
);
|
|
4379
|
-
});
|
|
4380
|
-
}
|
|
4381
|
-
function runAllScripts(verifyScripts, packageDir, timer) {
|
|
4382
|
-
const taskStatuses = initTaskStatuses(verifyScripts);
|
|
4383
|
-
return Promise.all(
|
|
4384
|
-
verifyScripts.map(
|
|
4385
|
-
(script, index) => runScript2(
|
|
4386
|
-
script,
|
|
4387
|
-
packageDir,
|
|
4388
|
-
timer ? createTimerCallback(taskStatuses, index) : void 0
|
|
4389
|
-
)
|
|
4390
|
-
)
|
|
4391
|
-
);
|
|
4392
|
-
}
|
|
4393
|
-
function printScriptList(scripts) {
|
|
4394
|
-
console.log(`Running ${scripts.length} verify script(s) in parallel:`);
|
|
4395
|
-
for (const script of scripts) {
|
|
4396
|
-
console.log(` - ${script}`);
|
|
4397
|
-
}
|
|
4398
|
-
}
|
|
4399
|
-
function exitIfFailed(failed) {
|
|
4400
|
-
if (failed.length === 0) return;
|
|
4401
|
-
logFailedScripts(failed);
|
|
4402
|
-
process.exit(1);
|
|
4403
|
-
}
|
|
4404
|
-
function handleResults(results, totalCount) {
|
|
4405
|
-
exitIfFailed(results.filter((r) => r.code !== 0));
|
|
4406
|
-
console.log(`
|
|
4407
|
-
All ${totalCount} verify script(s) passed`);
|
|
4408
|
-
}
|
|
4409
|
-
function resolveVerifyScripts() {
|
|
4410
|
-
const result = findPackageJsonWithVerifyScripts(process.cwd());
|
|
4411
|
-
if (!result) {
|
|
4412
|
-
console.log("No package.json with verify:* scripts found");
|
|
4413
|
-
return null;
|
|
4414
|
-
}
|
|
4415
|
-
return result;
|
|
4416
|
-
}
|
|
4417
|
-
function getPackageDir(found) {
|
|
4418
|
-
return path25.dirname(found.packageJsonPath);
|
|
4419
|
-
}
|
|
4420
|
-
async function executeVerifyScripts(found, timer) {
|
|
4421
|
-
printScriptList(found.verifyScripts);
|
|
4422
|
-
const results = await runAllScripts(
|
|
4423
|
-
found.verifyScripts,
|
|
4424
|
-
getPackageDir(found),
|
|
4425
|
-
timer
|
|
4426
|
-
);
|
|
4427
|
-
handleResults(results, found.verifyScripts.length);
|
|
4428
|
-
}
|
|
4429
|
-
async function run(options = {}) {
|
|
4430
|
-
const found = resolveVerifyScripts();
|
|
4431
|
-
if (!found) return;
|
|
4432
|
-
await executeVerifyScripts(found, options.timer ?? false);
|
|
4433
|
-
}
|
|
4434
|
-
|
|
4435
4567
|
// src/commands/registerVerify.ts
|
|
4436
4568
|
function registerVerify(program2) {
|
|
4437
|
-
const verifyCommand = program2.command("verify").description("Run all verify:* scripts from package.json in parallel").option("--timer", "Show timing information for each task as they complete").action((
|
|
4569
|
+
const verifyCommand = program2.command("verify").description("Run all verify:* scripts from package.json in parallel").option("--timer", "Show timing information for each task as they complete").action((options2) => run(options2));
|
|
4438
4570
|
verifyCommand.command("init").description("Add verify scripts to a project").action(init2);
|
|
4439
4571
|
verifyCommand.command("hardcoded-colors").description("Check for hardcoded hex colors in src/").action(hardcodedColors);
|
|
4440
4572
|
}
|
|
@@ -4570,27 +4702,27 @@ async function statusLine() {
|
|
|
4570
4702
|
// src/commands/sync.ts
|
|
4571
4703
|
import * as fs23 from "fs";
|
|
4572
4704
|
import * as os from "os";
|
|
4573
|
-
import * as
|
|
4705
|
+
import * as path29 from "path";
|
|
4574
4706
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4575
4707
|
|
|
4576
4708
|
// src/commands/sync/syncClaudeMd.ts
|
|
4577
4709
|
import * as fs21 from "fs";
|
|
4578
|
-
import * as
|
|
4579
|
-
import
|
|
4710
|
+
import * as path27 from "path";
|
|
4711
|
+
import chalk43 from "chalk";
|
|
4580
4712
|
async function syncClaudeMd(claudeDir, targetBase) {
|
|
4581
|
-
const source =
|
|
4582
|
-
const target =
|
|
4713
|
+
const source = path27.join(claudeDir, "CLAUDE.md");
|
|
4714
|
+
const target = path27.join(targetBase, "CLAUDE.md");
|
|
4583
4715
|
const sourceContent = fs21.readFileSync(source, "utf-8");
|
|
4584
4716
|
if (fs21.existsSync(target)) {
|
|
4585
4717
|
const targetContent = fs21.readFileSync(target, "utf-8");
|
|
4586
4718
|
if (sourceContent !== targetContent) {
|
|
4587
4719
|
console.log(
|
|
4588
|
-
|
|
4720
|
+
chalk43.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
4589
4721
|
);
|
|
4590
4722
|
console.log();
|
|
4591
4723
|
printDiff(targetContent, sourceContent);
|
|
4592
4724
|
const confirm = await promptConfirm(
|
|
4593
|
-
|
|
4725
|
+
chalk43.red("Overwrite existing CLAUDE.md?"),
|
|
4594
4726
|
false
|
|
4595
4727
|
);
|
|
4596
4728
|
if (!confirm) {
|
|
@@ -4605,11 +4737,11 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
4605
4737
|
|
|
4606
4738
|
// src/commands/sync/syncSettings.ts
|
|
4607
4739
|
import * as fs22 from "fs";
|
|
4608
|
-
import * as
|
|
4609
|
-
import
|
|
4740
|
+
import * as path28 from "path";
|
|
4741
|
+
import chalk44 from "chalk";
|
|
4610
4742
|
async function syncSettings(claudeDir, targetBase) {
|
|
4611
|
-
const source =
|
|
4612
|
-
const target =
|
|
4743
|
+
const source = path28.join(claudeDir, "settings.json");
|
|
4744
|
+
const target = path28.join(targetBase, "settings.json");
|
|
4613
4745
|
const sourceContent = fs22.readFileSync(source, "utf-8");
|
|
4614
4746
|
const normalizedSource = JSON.stringify(JSON.parse(sourceContent), null, 2);
|
|
4615
4747
|
if (fs22.existsSync(target)) {
|
|
@@ -4617,12 +4749,12 @@ async function syncSettings(claudeDir, targetBase) {
|
|
|
4617
4749
|
const normalizedTarget = JSON.stringify(JSON.parse(targetContent), null, 2);
|
|
4618
4750
|
if (normalizedSource !== normalizedTarget) {
|
|
4619
4751
|
console.log(
|
|
4620
|
-
|
|
4752
|
+
chalk44.yellow("\n\u26A0\uFE0F Warning: settings.json differs from existing file")
|
|
4621
4753
|
);
|
|
4622
4754
|
console.log();
|
|
4623
4755
|
printDiff(targetContent, sourceContent);
|
|
4624
4756
|
const confirm = await promptConfirm(
|
|
4625
|
-
|
|
4757
|
+
chalk44.red("Overwrite existing settings.json?"),
|
|
4626
4758
|
false
|
|
4627
4759
|
);
|
|
4628
4760
|
if (!confirm) {
|
|
@@ -4637,21 +4769,21 @@ async function syncSettings(claudeDir, targetBase) {
|
|
|
4637
4769
|
|
|
4638
4770
|
// src/commands/sync.ts
|
|
4639
4771
|
var __filename2 = fileURLToPath3(import.meta.url);
|
|
4640
|
-
var __dirname4 =
|
|
4772
|
+
var __dirname4 = path29.dirname(__filename2);
|
|
4641
4773
|
async function sync() {
|
|
4642
|
-
const claudeDir =
|
|
4643
|
-
const targetBase =
|
|
4774
|
+
const claudeDir = path29.join(__dirname4, "..", "claude");
|
|
4775
|
+
const targetBase = path29.join(os.homedir(), ".claude");
|
|
4644
4776
|
syncCommands(claudeDir, targetBase);
|
|
4645
4777
|
await syncSettings(claudeDir, targetBase);
|
|
4646
4778
|
await syncClaudeMd(claudeDir, targetBase);
|
|
4647
4779
|
}
|
|
4648
4780
|
function syncCommands(claudeDir, targetBase) {
|
|
4649
|
-
const sourceDir =
|
|
4650
|
-
const targetDir =
|
|
4781
|
+
const sourceDir = path29.join(claudeDir, "commands");
|
|
4782
|
+
const targetDir = path29.join(targetBase, "commands");
|
|
4651
4783
|
fs23.mkdirSync(targetDir, { recursive: true });
|
|
4652
4784
|
const files = fs23.readdirSync(sourceDir);
|
|
4653
4785
|
for (const file of files) {
|
|
4654
|
-
fs23.copyFileSync(
|
|
4786
|
+
fs23.copyFileSync(path29.join(sourceDir, file), path29.join(targetDir, file));
|
|
4655
4787
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
4656
4788
|
}
|
|
4657
4789
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
@@ -4665,7 +4797,7 @@ program.command("init").description("Initialize VS Code and verify configuration
|
|
|
4665
4797
|
program.command("commit <message>").description("Create a git commit with validation").action(commit);
|
|
4666
4798
|
program.command("update").description("Update claude-code to the latest version").action(() => {
|
|
4667
4799
|
console.log("Updating claude-code...");
|
|
4668
|
-
|
|
4800
|
+
execSync22("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
|
|
4669
4801
|
});
|
|
4670
4802
|
var configCommand = program.command("config").description("View and modify assist.yml configuration");
|
|
4671
4803
|
configCommand.command("set <key> <value>").description("Set a config value (e.g. commit.push true)").action(configSet);
|
|
@@ -4675,7 +4807,7 @@ var runCommand = program.command("run").description("Run a configured command fr
|
|
|
4675
4807
|
run2(name, args);
|
|
4676
4808
|
});
|
|
4677
4809
|
runCommand.command("add").description("Add a new run configuration to assist.yml").allowUnknownOption().allowExcessArguments().action(() => add());
|
|
4678
|
-
program
|
|
4810
|
+
registerNew(program);
|
|
4679
4811
|
var lintCommand = program.command("lint").description("Run lint checks for conventions not enforced by biomejs").action(lint);
|
|
4680
4812
|
lintCommand.command("init").description("Initialize Biome with standard linter config").action(init);
|
|
4681
4813
|
var vscodeCommand = program.command("vscode").description("VS Code configuration utilities");
|