@staff0rd/assist 0.42.2 → 0.43.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 +1 -0
- package/dist/commands/deploy/init.ts +1 -54
- package/dist/commands/deploy/updateWorkflow.ts +56 -0
- package/dist/index.js +841 -379
- package/package.json +2 -2
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 execSync19 } 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.43.0",
|
|
11
11
|
type: "module",
|
|
12
12
|
main: "dist/index.js",
|
|
13
13
|
bin: {
|
|
@@ -28,7 +28,7 @@ var package_default = {
|
|
|
28
28
|
"verify:types": "tsc --noEmit",
|
|
29
29
|
"verify:knip": "knip --no-progress --treat-config-hints-as-errors",
|
|
30
30
|
"verify:duplicate-code": "jscpd --format 'typescript,tsx' --exitCode 1 --ignore '**/*.test.*' -r consoleFull src",
|
|
31
|
-
"verify:maintainability": "assist complexity maintainability ./src --threshold
|
|
31
|
+
"verify:maintainability": "assist complexity maintainability ./src --threshold 60",
|
|
32
32
|
"verify:custom-lint": "assist lint"
|
|
33
33
|
},
|
|
34
34
|
keywords: [],
|
|
@@ -107,6 +107,9 @@ var assistConfigSchema = z.strictObject({
|
|
|
107
107
|
complexity: z.strictObject({
|
|
108
108
|
ignore: z.array(z.string()).default(["**/*test.ts*"])
|
|
109
109
|
}).default({ ignore: ["**/*test.ts*"] }),
|
|
110
|
+
restructure: z.strictObject({
|
|
111
|
+
ignore: z.array(z.string()).default([])
|
|
112
|
+
}).optional(),
|
|
110
113
|
run: z.array(runConfigSchema).optional(),
|
|
111
114
|
transcript: transcriptConfigSchema.optional()
|
|
112
115
|
});
|
|
@@ -549,57 +552,67 @@ function calculateMaintainabilityIndex(halsteadVolume, cyclomaticComplexity, slo
|
|
|
549
552
|
const mi = 171 - 5.2 * Math.log(halsteadVolume) - 0.23 * cyclomaticComplexity - 16.2 * Math.log(sloc2);
|
|
550
553
|
return Math.max(0, Math.min(100, mi));
|
|
551
554
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
metrics.functions.push(mi);
|
|
570
|
-
}
|
|
571
|
-
});
|
|
572
|
-
const results = [];
|
|
573
|
-
const { threshold } = options;
|
|
574
|
-
for (const [file, metrics] of fileMetrics) {
|
|
575
|
-
if (metrics.functions.length === 0) continue;
|
|
576
|
-
const avgMaintainability = metrics.functions.reduce((a, b) => a + b, 0) / metrics.functions.length;
|
|
577
|
-
const minMaintainability = Math.min(...metrics.functions);
|
|
578
|
-
results.push({ file, avgMaintainability, minMaintainability });
|
|
579
|
-
}
|
|
580
|
-
results.sort((a, b) => a.minMaintainability - b.minMaintainability);
|
|
581
|
-
const filtered = threshold !== void 0 ? results.filter((r) => r.minMaintainability < threshold) : results;
|
|
582
|
-
if (threshold !== void 0 && filtered.length === 0) {
|
|
583
|
-
console.log(chalk5.green("All files pass maintainability threshold"));
|
|
584
|
-
} else {
|
|
585
|
-
for (const { file, avgMaintainability, minMaintainability } of filtered) {
|
|
586
|
-
const color = threshold !== void 0 ? chalk5.red : chalk5.white;
|
|
587
|
-
console.log(
|
|
588
|
-
`${color(file)} \u2192 avg: ${chalk5.cyan(avgMaintainability.toFixed(1))}, min: ${chalk5.yellow(minMaintainability.toFixed(1))}`
|
|
589
|
-
);
|
|
590
|
-
}
|
|
555
|
+
function collectFileMetrics(files) {
|
|
556
|
+
const fileMetrics = /* @__PURE__ */ new Map();
|
|
557
|
+
for (const file of files) {
|
|
558
|
+
const content = fs3.readFileSync(file, "utf-8");
|
|
559
|
+
fileMetrics.set(file, { sloc: countSloc(content), functions: [] });
|
|
560
|
+
}
|
|
561
|
+
forEachFunction(files, (file, _name, node) => {
|
|
562
|
+
const metrics = fileMetrics.get(file);
|
|
563
|
+
if (metrics) {
|
|
564
|
+
const complexity = calculateCyclomaticComplexity(node);
|
|
565
|
+
const halstead2 = calculateHalstead(node);
|
|
566
|
+
const mi = calculateMaintainabilityIndex(
|
|
567
|
+
halstead2.volume,
|
|
568
|
+
complexity,
|
|
569
|
+
metrics.sloc
|
|
570
|
+
);
|
|
571
|
+
metrics.functions.push(mi);
|
|
591
572
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
573
|
+
});
|
|
574
|
+
return fileMetrics;
|
|
575
|
+
}
|
|
576
|
+
function aggregateResults(fileMetrics) {
|
|
577
|
+
const results = [];
|
|
578
|
+
for (const [file, metrics] of fileMetrics) {
|
|
579
|
+
if (metrics.functions.length === 0) continue;
|
|
580
|
+
const avgMaintainability = metrics.functions.reduce((a, b) => a + b, 0) / metrics.functions.length;
|
|
581
|
+
const minMaintainability = Math.min(...metrics.functions);
|
|
582
|
+
results.push({ file, avgMaintainability, minMaintainability });
|
|
583
|
+
}
|
|
584
|
+
results.sort((a, b) => a.minMaintainability - b.minMaintainability);
|
|
585
|
+
return results;
|
|
586
|
+
}
|
|
587
|
+
function displayResults(results, threshold) {
|
|
588
|
+
const filtered = threshold !== void 0 ? results.filter((r) => r.minMaintainability < threshold) : results;
|
|
589
|
+
if (threshold !== void 0 && filtered.length === 0) {
|
|
590
|
+
console.log(chalk5.green("All files pass maintainability threshold"));
|
|
591
|
+
} else {
|
|
592
|
+
for (const { file, avgMaintainability, minMaintainability } of filtered) {
|
|
593
|
+
const color = threshold !== void 0 ? chalk5.red : chalk5.white;
|
|
594
|
+
console.log(
|
|
595
|
+
`${color(file)} \u2192 avg: ${chalk5.cyan(avgMaintainability.toFixed(1))}, min: ${chalk5.yellow(minMaintainability.toFixed(1))}`
|
|
600
596
|
);
|
|
601
|
-
process.exit(1);
|
|
602
597
|
}
|
|
598
|
+
}
|
|
599
|
+
console.log(chalk5.dim(`
|
|
600
|
+
Analyzed ${results.length} files`));
|
|
601
|
+
if (filtered.length > 0 && threshold !== void 0) {
|
|
602
|
+
console.error(
|
|
603
|
+
chalk5.red(
|
|
604
|
+
`
|
|
605
|
+
Fail: ${filtered.length} file(s) below threshold ${threshold}. Maintainability index (0\u2013100) is derived from Halstead volume, cyclomatic complexity, and lines of code. Try 'complexity cyclomatic', 'complexity halstead', or 'complexity sloc' to help identify which metric is contributing most. For larger files, start by extracting responsibilities into smaller files.`
|
|
606
|
+
)
|
|
607
|
+
);
|
|
608
|
+
process.exit(1);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
async function maintainability(pattern2 = "**/*.ts", options = {}) {
|
|
612
|
+
withSourceFiles(pattern2, (files) => {
|
|
613
|
+
const fileMetrics = collectFileMetrics(files);
|
|
614
|
+
const results = aggregateResults(fileMetrics);
|
|
615
|
+
displayResults(results, options.threshold);
|
|
603
616
|
});
|
|
604
617
|
}
|
|
605
618
|
|
|
@@ -638,8 +651,8 @@ Total: ${total} lines across ${files.length} files`)
|
|
|
638
651
|
// src/commands/config/index.ts
|
|
639
652
|
import chalk7 from "chalk";
|
|
640
653
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
641
|
-
function getNestedValue(obj,
|
|
642
|
-
const keys =
|
|
654
|
+
function getNestedValue(obj, path28) {
|
|
655
|
+
const keys = path28.split(".");
|
|
643
656
|
let current = obj;
|
|
644
657
|
for (const key of keys) {
|
|
645
658
|
if (current === null || current === void 0 || typeof current !== "object") {
|
|
@@ -649,8 +662,8 @@ function getNestedValue(obj, path20) {
|
|
|
649
662
|
}
|
|
650
663
|
return current;
|
|
651
664
|
}
|
|
652
|
-
function setNestedValue(obj,
|
|
653
|
-
const keys =
|
|
665
|
+
function setNestedValue(obj, path28, value) {
|
|
666
|
+
const keys = path28.split(".");
|
|
654
667
|
const result = { ...obj };
|
|
655
668
|
let current = result;
|
|
656
669
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -677,8 +690,8 @@ function configSet(key, value) {
|
|
|
677
690
|
const result = assistConfigSchema.safeParse(updated);
|
|
678
691
|
if (!result.success) {
|
|
679
692
|
for (const issue of result.error.issues) {
|
|
680
|
-
const
|
|
681
|
-
console.error(chalk7.red(`${
|
|
693
|
+
const path28 = issue.path.length > 0 ? issue.path.join(".") : key;
|
|
694
|
+
console.error(chalk7.red(`${path28}: ${issue.message}`));
|
|
682
695
|
}
|
|
683
696
|
process.exit(1);
|
|
684
697
|
}
|
|
@@ -705,10 +718,7 @@ function configList() {
|
|
|
705
718
|
|
|
706
719
|
// src/commands/deploy/init.ts
|
|
707
720
|
import { execSync as execSync2 } from "child_process";
|
|
708
|
-
import
|
|
709
|
-
import { dirname, join as join2 } from "path";
|
|
710
|
-
import { fileURLToPath } from "url";
|
|
711
|
-
import chalk9 from "chalk";
|
|
721
|
+
import chalk10 from "chalk";
|
|
712
722
|
import enquirer2 from "enquirer";
|
|
713
723
|
|
|
714
724
|
// src/shared/promptConfirm.ts
|
|
@@ -728,6 +738,12 @@ async function promptConfirm(message, initial = true) {
|
|
|
728
738
|
return confirmed;
|
|
729
739
|
}
|
|
730
740
|
|
|
741
|
+
// src/commands/deploy/updateWorkflow.ts
|
|
742
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
743
|
+
import { dirname, join as join2 } from "path";
|
|
744
|
+
import { fileURLToPath } from "url";
|
|
745
|
+
import chalk9 from "chalk";
|
|
746
|
+
|
|
731
747
|
// src/utils/printDiff.ts
|
|
732
748
|
import chalk8 from "chalk";
|
|
733
749
|
import * as diff from "diff";
|
|
@@ -757,7 +773,7 @@ function printDiff(oldContent, newContent) {
|
|
|
757
773
|
}
|
|
758
774
|
}
|
|
759
775
|
|
|
760
|
-
// src/commands/deploy/
|
|
776
|
+
// src/commands/deploy/updateWorkflow.ts
|
|
761
777
|
var WORKFLOW_PATH = ".github/workflows/build.yml";
|
|
762
778
|
var __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
763
779
|
function getExistingSiteId() {
|
|
@@ -798,11 +814,13 @@ async function updateWorkflow(siteId) {
|
|
|
798
814
|
console.log(chalk9.green(`
|
|
799
815
|
Created ${WORKFLOW_PATH}`));
|
|
800
816
|
}
|
|
817
|
+
|
|
818
|
+
// src/commands/deploy/init.ts
|
|
801
819
|
async function init() {
|
|
802
|
-
console.log(
|
|
820
|
+
console.log(chalk10.bold("Initializing Netlify deployment...\n"));
|
|
803
821
|
const existingSiteId = getExistingSiteId();
|
|
804
822
|
if (existingSiteId) {
|
|
805
|
-
console.log(
|
|
823
|
+
console.log(chalk10.dim(`Using existing site ID: ${existingSiteId}
|
|
806
824
|
`));
|
|
807
825
|
await updateWorkflow(existingSiteId);
|
|
808
826
|
return;
|
|
@@ -814,10 +832,10 @@ async function init() {
|
|
|
814
832
|
});
|
|
815
833
|
} catch (error) {
|
|
816
834
|
if (error instanceof Error && error.message.includes("command not found")) {
|
|
817
|
-
console.error(
|
|
835
|
+
console.error(chalk10.red("\nNetlify CLI is not installed.\n"));
|
|
818
836
|
const install = await promptConfirm("Would you like to install it now?");
|
|
819
837
|
if (install) {
|
|
820
|
-
console.log(
|
|
838
|
+
console.log(chalk10.dim("\nInstalling netlify-cli...\n"));
|
|
821
839
|
execSync2("npm install -g netlify-cli", { stdio: "inherit" });
|
|
822
840
|
console.log();
|
|
823
841
|
execSync2("netlify sites:create --disable-linking", {
|
|
@@ -825,7 +843,7 @@ async function init() {
|
|
|
825
843
|
});
|
|
826
844
|
} else {
|
|
827
845
|
console.log(
|
|
828
|
-
|
|
846
|
+
chalk10.yellow(
|
|
829
847
|
"\nInstall it manually with: npm install -g netlify-cli\n"
|
|
830
848
|
)
|
|
831
849
|
);
|
|
@@ -842,17 +860,17 @@ async function init() {
|
|
|
842
860
|
validate: (value) => /^[a-f0-9-]+$/i.test(value) || "Invalid site ID format"
|
|
843
861
|
});
|
|
844
862
|
await updateWorkflow(siteId);
|
|
845
|
-
console.log(
|
|
863
|
+
console.log(chalk10.bold("\nDeployment initialized successfully!"));
|
|
846
864
|
console.log(
|
|
847
|
-
|
|
865
|
+
chalk10.yellow("\nTo complete setup, create a personal access token at:")
|
|
848
866
|
);
|
|
849
867
|
console.log(
|
|
850
|
-
|
|
868
|
+
chalk10.cyan(
|
|
851
869
|
"https://app.netlify.com/user/applications#personal-access-tokens"
|
|
852
870
|
)
|
|
853
871
|
);
|
|
854
872
|
console.log(
|
|
855
|
-
|
|
873
|
+
chalk10.yellow(
|
|
856
874
|
"\nThen add it as NETLIFY_AUTH_TOKEN in your GitHub repository secrets."
|
|
857
875
|
)
|
|
858
876
|
);
|
|
@@ -860,7 +878,7 @@ async function init() {
|
|
|
860
878
|
|
|
861
879
|
// src/commands/deploy/redirect.ts
|
|
862
880
|
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
863
|
-
import
|
|
881
|
+
import chalk11 from "chalk";
|
|
864
882
|
var TRAILING_SLASH_SCRIPT = ` <script>
|
|
865
883
|
if (!window.location.pathname.endsWith('/')) {
|
|
866
884
|
window.location.href = \`\${window.location.pathname}/\${window.location.search}\${window.location.hash}\`;
|
|
@@ -869,32 +887,32 @@ var TRAILING_SLASH_SCRIPT = ` <script>
|
|
|
869
887
|
function redirect() {
|
|
870
888
|
const indexPath = "index.html";
|
|
871
889
|
if (!existsSync3(indexPath)) {
|
|
872
|
-
console.log(
|
|
890
|
+
console.log(chalk11.yellow("No index.html found"));
|
|
873
891
|
return;
|
|
874
892
|
}
|
|
875
893
|
const content = readFileSync3(indexPath, "utf-8");
|
|
876
894
|
if (content.includes("window.location.pathname.endsWith('/')")) {
|
|
877
|
-
console.log(
|
|
895
|
+
console.log(chalk11.dim("Trailing slash script already present"));
|
|
878
896
|
return;
|
|
879
897
|
}
|
|
880
898
|
const headCloseIndex = content.indexOf("</head>");
|
|
881
899
|
if (headCloseIndex === -1) {
|
|
882
|
-
console.log(
|
|
900
|
+
console.log(chalk11.red("Could not find </head> tag in index.html"));
|
|
883
901
|
return;
|
|
884
902
|
}
|
|
885
903
|
const newContent = content.slice(0, headCloseIndex) + TRAILING_SLASH_SCRIPT + "\n " + content.slice(headCloseIndex);
|
|
886
904
|
writeFileSync3(indexPath, newContent);
|
|
887
|
-
console.log(
|
|
905
|
+
console.log(chalk11.green("Added trailing slash redirect to index.html"));
|
|
888
906
|
}
|
|
889
907
|
|
|
890
908
|
// src/commands/devlog/list.ts
|
|
891
909
|
import { execSync as execSync4 } from "child_process";
|
|
892
910
|
import { basename as basename2 } from "path";
|
|
893
|
-
import
|
|
911
|
+
import chalk13 from "chalk";
|
|
894
912
|
|
|
895
913
|
// src/commands/devlog/shared.ts
|
|
896
914
|
import { execSync as execSync3 } from "child_process";
|
|
897
|
-
import
|
|
915
|
+
import chalk12 from "chalk";
|
|
898
916
|
|
|
899
917
|
// src/commands/devlog/loadDevlogEntries.ts
|
|
900
918
|
import { readdirSync, readFileSync as readFileSync4 } from "fs";
|
|
@@ -955,17 +973,35 @@ function shouldIgnoreCommit(files, ignorePaths) {
|
|
|
955
973
|
}
|
|
956
974
|
function printCommitsWithFiles(commits, ignore2, verbose) {
|
|
957
975
|
for (const commit2 of commits) {
|
|
958
|
-
console.log(` ${
|
|
976
|
+
console.log(` ${chalk12.yellow(commit2.hash)} ${commit2.message}`);
|
|
959
977
|
if (verbose) {
|
|
960
978
|
const visibleFiles = commit2.files.filter(
|
|
961
979
|
(file) => !ignore2.some((p) => file.startsWith(p))
|
|
962
980
|
);
|
|
963
981
|
for (const file of visibleFiles) {
|
|
964
|
-
console.log(` ${
|
|
982
|
+
console.log(` ${chalk12.dim(file)}`);
|
|
965
983
|
}
|
|
966
984
|
}
|
|
967
985
|
}
|
|
968
986
|
}
|
|
987
|
+
function parseGitLogCommits(output, ignore2, afterDate) {
|
|
988
|
+
const lines = output.trim().split("\n");
|
|
989
|
+
const commitsByDate = /* @__PURE__ */ new Map();
|
|
990
|
+
for (const line of lines) {
|
|
991
|
+
const [date, hash, ...messageParts] = line.split("|");
|
|
992
|
+
const message = messageParts.join("|");
|
|
993
|
+
if (afterDate && date <= afterDate) {
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
const files = getCommitFiles(hash);
|
|
997
|
+
if (!shouldIgnoreCommit(files, ignore2)) {
|
|
998
|
+
const existing = commitsByDate.get(date) || [];
|
|
999
|
+
existing.push({ date, hash, message, files });
|
|
1000
|
+
commitsByDate.set(date, existing);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
return commitsByDate;
|
|
1004
|
+
}
|
|
969
1005
|
|
|
970
1006
|
// src/commands/devlog/list.ts
|
|
971
1007
|
function list(options) {
|
|
@@ -981,22 +1017,7 @@ function list(options) {
|
|
|
981
1017
|
`git log ${reverseFlag}${limitFlag}--pretty=format:'%ad|%h|%s' --date=short`,
|
|
982
1018
|
{ encoding: "utf-8" }
|
|
983
1019
|
);
|
|
984
|
-
const
|
|
985
|
-
const commits = [];
|
|
986
|
-
for (const line of lines) {
|
|
987
|
-
const [date, hash, ...messageParts] = line.split("|");
|
|
988
|
-
const message = messageParts.join("|");
|
|
989
|
-
const files = getCommitFiles(hash);
|
|
990
|
-
if (!shouldIgnoreCommit(files, ignore2)) {
|
|
991
|
-
commits.push({ date, hash, message, files });
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
const commitsByDate = /* @__PURE__ */ new Map();
|
|
995
|
-
for (const commit2 of commits) {
|
|
996
|
-
const existing = commitsByDate.get(commit2.date) || [];
|
|
997
|
-
existing.push(commit2);
|
|
998
|
-
commitsByDate.set(commit2.date, existing);
|
|
999
|
-
}
|
|
1020
|
+
const commitsByDate = parseGitLogCommits(output, ignore2);
|
|
1000
1021
|
let dateCount = 0;
|
|
1001
1022
|
let isFirst = true;
|
|
1002
1023
|
for (const [date, dateCommits] of commitsByDate) {
|
|
@@ -1014,12 +1035,12 @@ function list(options) {
|
|
|
1014
1035
|
isFirst = false;
|
|
1015
1036
|
const entries = devlogEntries.get(date);
|
|
1016
1037
|
if (skipDays.has(date)) {
|
|
1017
|
-
console.log(`${
|
|
1038
|
+
console.log(`${chalk13.bold.blue(date)} ${chalk13.dim("skipped")}`);
|
|
1018
1039
|
} else if (entries && entries.length > 0) {
|
|
1019
|
-
const entryInfo = entries.map((e) => `${
|
|
1020
|
-
console.log(`${
|
|
1040
|
+
const entryInfo = entries.map((e) => `${chalk13.green(e.version)} ${e.title}`).join(" | ");
|
|
1041
|
+
console.log(`${chalk13.bold.blue(date)} ${entryInfo}`);
|
|
1021
1042
|
} else {
|
|
1022
|
-
console.log(`${
|
|
1043
|
+
console.log(`${chalk13.bold.blue(date)} ${chalk13.red("\u26A0 devlog missing")}`);
|
|
1023
1044
|
}
|
|
1024
1045
|
printCommitsWithFiles(dateCommits, ignore2, options.verbose ?? false);
|
|
1025
1046
|
}
|
|
@@ -1027,7 +1048,7 @@ function list(options) {
|
|
|
1027
1048
|
|
|
1028
1049
|
// src/commands/devlog/next.ts
|
|
1029
1050
|
import { execSync as execSync6 } from "child_process";
|
|
1030
|
-
import
|
|
1051
|
+
import chalk14 from "chalk";
|
|
1031
1052
|
|
|
1032
1053
|
// src/commands/devlog/getLastVersionInfo.ts
|
|
1033
1054
|
import { execSync as execSync5 } from "child_process";
|
|
@@ -1104,6 +1125,22 @@ function bumpVersion(version2, type) {
|
|
|
1104
1125
|
}
|
|
1105
1126
|
|
|
1106
1127
|
// src/commands/devlog/next.ts
|
|
1128
|
+
function displayVersion(conventional, firstHash, patchVersion, minorVersion) {
|
|
1129
|
+
if (conventional && firstHash) {
|
|
1130
|
+
const version2 = getVersionAtCommit(firstHash);
|
|
1131
|
+
if (version2) {
|
|
1132
|
+
console.log(`${chalk14.bold("version:")} ${stripToMinor(version2)}`);
|
|
1133
|
+
} else {
|
|
1134
|
+
console.log(`${chalk14.bold("version:")} ${chalk14.red("unknown")}`);
|
|
1135
|
+
}
|
|
1136
|
+
} else if (patchVersion && minorVersion) {
|
|
1137
|
+
console.log(
|
|
1138
|
+
`${chalk14.bold("version:")} ${patchVersion} (patch) or ${minorVersion} (minor)`
|
|
1139
|
+
);
|
|
1140
|
+
} else {
|
|
1141
|
+
console.log(`${chalk14.bold("version:")} v0.1 (initial)`);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1107
1144
|
function next(options) {
|
|
1108
1145
|
const config = loadConfig();
|
|
1109
1146
|
const ignore2 = options.ignore ?? config.devlog?.ignore ?? [];
|
|
@@ -1117,62 +1154,40 @@ function next(options) {
|
|
|
1117
1154
|
"git log --pretty=format:'%ad|%h|%s' --date=short -n 500",
|
|
1118
1155
|
{ encoding: "utf-8" }
|
|
1119
1156
|
);
|
|
1120
|
-
const
|
|
1121
|
-
const commitsByDate = /* @__PURE__ */ new Map();
|
|
1122
|
-
for (const line of lines) {
|
|
1123
|
-
const [date, hash, ...messageParts] = line.split("|");
|
|
1124
|
-
const message = messageParts.join("|");
|
|
1125
|
-
if (lastDate && date <= lastDate) {
|
|
1126
|
-
continue;
|
|
1127
|
-
}
|
|
1128
|
-
const files = getCommitFiles(hash);
|
|
1129
|
-
if (!shouldIgnoreCommit(files, ignore2)) {
|
|
1130
|
-
const existing = commitsByDate.get(date) || [];
|
|
1131
|
-
existing.push({ date, hash, message, files });
|
|
1132
|
-
commitsByDate.set(date, existing);
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1157
|
+
const commitsByDate = parseGitLogCommits(output, ignore2, lastDate);
|
|
1135
1158
|
const dates = Array.from(commitsByDate.keys()).filter((d) => !skipDays.has(d)).sort();
|
|
1136
1159
|
const targetDate = dates[0];
|
|
1137
1160
|
if (!targetDate) {
|
|
1138
1161
|
if (lastInfo) {
|
|
1139
|
-
console.log(
|
|
1162
|
+
console.log(chalk14.dim("No commits after last versioned entry"));
|
|
1140
1163
|
} else {
|
|
1141
|
-
console.log(
|
|
1164
|
+
console.log(chalk14.dim("No commits found"));
|
|
1142
1165
|
}
|
|
1143
1166
|
return;
|
|
1144
1167
|
}
|
|
1145
1168
|
const commits = commitsByDate.get(targetDate) ?? [];
|
|
1146
|
-
console.log(`${
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
} else if (patchVersion && minorVersion) {
|
|
1155
|
-
console.log(
|
|
1156
|
-
`${chalk13.bold("version:")} ${patchVersion} (patch) or ${minorVersion} (minor)`
|
|
1157
|
-
);
|
|
1158
|
-
} else {
|
|
1159
|
-
console.log(`${chalk13.bold("version:")} v0.1 (initial)`);
|
|
1160
|
-
}
|
|
1161
|
-
console.log(`${chalk13.bold.blue(targetDate)}`);
|
|
1169
|
+
console.log(`${chalk14.bold("name:")} ${repoName}`);
|
|
1170
|
+
displayVersion(
|
|
1171
|
+
!!config.commit?.conventional,
|
|
1172
|
+
commits[0]?.hash,
|
|
1173
|
+
patchVersion,
|
|
1174
|
+
minorVersion
|
|
1175
|
+
);
|
|
1176
|
+
console.log(`${chalk14.bold.blue(targetDate)}`);
|
|
1162
1177
|
printCommitsWithFiles(commits, ignore2, options.verbose ?? false);
|
|
1163
1178
|
}
|
|
1164
1179
|
|
|
1165
1180
|
// src/commands/devlog/skip.ts
|
|
1166
|
-
import
|
|
1181
|
+
import chalk15 from "chalk";
|
|
1167
1182
|
function skip(date) {
|
|
1168
1183
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
1169
|
-
console.log(
|
|
1184
|
+
console.log(chalk15.red("Invalid date format. Use YYYY-MM-DD"));
|
|
1170
1185
|
process.exit(1);
|
|
1171
1186
|
}
|
|
1172
1187
|
const config = loadConfig();
|
|
1173
1188
|
const skipDays = config.devlog?.skip?.days ?? [];
|
|
1174
1189
|
if (skipDays.includes(date)) {
|
|
1175
|
-
console.log(
|
|
1190
|
+
console.log(chalk15.yellow(`${date} is already in skip list`));
|
|
1176
1191
|
return;
|
|
1177
1192
|
}
|
|
1178
1193
|
skipDays.push(date);
|
|
@@ -1185,27 +1200,27 @@ function skip(date) {
|
|
|
1185
1200
|
}
|
|
1186
1201
|
};
|
|
1187
1202
|
saveConfig(config);
|
|
1188
|
-
console.log(
|
|
1203
|
+
console.log(chalk15.green(`Added ${date} to skip list`));
|
|
1189
1204
|
}
|
|
1190
1205
|
|
|
1191
1206
|
// src/commands/devlog/version.ts
|
|
1192
|
-
import
|
|
1207
|
+
import chalk16 from "chalk";
|
|
1193
1208
|
function version() {
|
|
1194
1209
|
const config = loadConfig();
|
|
1195
1210
|
const name = getRepoName();
|
|
1196
1211
|
const lastInfo = getLastVersionInfo(name, config);
|
|
1197
1212
|
const lastVersion = lastInfo?.version ?? null;
|
|
1198
1213
|
const nextVersion = lastVersion ? bumpVersion(lastVersion, "patch") : null;
|
|
1199
|
-
console.log(`${
|
|
1200
|
-
console.log(`${
|
|
1201
|
-
console.log(`${
|
|
1214
|
+
console.log(`${chalk16.bold("name:")} ${name}`);
|
|
1215
|
+
console.log(`${chalk16.bold("last:")} ${lastVersion ?? chalk16.dim("none")}`);
|
|
1216
|
+
console.log(`${chalk16.bold("next:")} ${nextVersion ?? chalk16.dim("none")}`);
|
|
1202
1217
|
}
|
|
1203
1218
|
|
|
1204
1219
|
// src/commands/verify/init.ts
|
|
1205
|
-
import
|
|
1220
|
+
import chalk27 from "chalk";
|
|
1206
1221
|
|
|
1207
1222
|
// src/shared/promptMultiselect.ts
|
|
1208
|
-
import
|
|
1223
|
+
import chalk17 from "chalk";
|
|
1209
1224
|
import enquirer3 from "enquirer";
|
|
1210
1225
|
async function promptMultiselect(message, options) {
|
|
1211
1226
|
const { selected } = await enquirer3.prompt({
|
|
@@ -1214,7 +1229,7 @@ async function promptMultiselect(message, options) {
|
|
|
1214
1229
|
message,
|
|
1215
1230
|
choices: options.map((opt) => ({
|
|
1216
1231
|
name: opt.value,
|
|
1217
|
-
message: `${opt.name} - ${
|
|
1232
|
+
message: `${opt.name} - ${chalk17.dim(opt.description)}`
|
|
1218
1233
|
})),
|
|
1219
1234
|
// @ts-expect-error - enquirer types don't include symbols but it's supported
|
|
1220
1235
|
symbols: {
|
|
@@ -1230,7 +1245,7 @@ async function promptMultiselect(message, options) {
|
|
|
1230
1245
|
// src/shared/readPackageJson.ts
|
|
1231
1246
|
import * as fs5 from "fs";
|
|
1232
1247
|
import * as path3 from "path";
|
|
1233
|
-
import
|
|
1248
|
+
import chalk18 from "chalk";
|
|
1234
1249
|
function findPackageJson() {
|
|
1235
1250
|
const packageJsonPath = path3.join(process.cwd(), "package.json");
|
|
1236
1251
|
if (fs5.existsSync(packageJsonPath)) {
|
|
@@ -1244,7 +1259,7 @@ function readPackageJson(filePath) {
|
|
|
1244
1259
|
function requirePackageJson() {
|
|
1245
1260
|
const packageJsonPath = findPackageJson();
|
|
1246
1261
|
if (!packageJsonPath) {
|
|
1247
|
-
console.error(
|
|
1262
|
+
console.error(chalk18.red("No package.json found in current directory"));
|
|
1248
1263
|
process.exit(1);
|
|
1249
1264
|
}
|
|
1250
1265
|
const pkg = readPackageJson(packageJsonPath);
|
|
@@ -1282,13 +1297,13 @@ var expectedScripts = {
|
|
|
1282
1297
|
};
|
|
1283
1298
|
|
|
1284
1299
|
// src/commands/verify/setup/setupBuild.ts
|
|
1285
|
-
import
|
|
1300
|
+
import chalk20 from "chalk";
|
|
1286
1301
|
|
|
1287
1302
|
// src/commands/verify/installPackage.ts
|
|
1288
1303
|
import { execSync as execSync7 } from "child_process";
|
|
1289
1304
|
import * as fs6 from "fs";
|
|
1290
1305
|
import * as path4 from "path";
|
|
1291
|
-
import
|
|
1306
|
+
import chalk19 from "chalk";
|
|
1292
1307
|
function writePackageJson(filePath, pkg) {
|
|
1293
1308
|
fs6.writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}
|
|
1294
1309
|
`);
|
|
@@ -1303,12 +1318,12 @@ function addScript(pkg, name, command) {
|
|
|
1303
1318
|
};
|
|
1304
1319
|
}
|
|
1305
1320
|
function installPackage(name, cwd) {
|
|
1306
|
-
console.log(
|
|
1321
|
+
console.log(chalk19.dim(`Installing ${name}...`));
|
|
1307
1322
|
try {
|
|
1308
1323
|
execSync7(`npm install -D ${name}`, { stdio: "inherit", cwd });
|
|
1309
1324
|
return true;
|
|
1310
1325
|
} catch {
|
|
1311
|
-
console.error(
|
|
1326
|
+
console.error(chalk19.red(`Failed to install ${name}`));
|
|
1312
1327
|
return false;
|
|
1313
1328
|
}
|
|
1314
1329
|
}
|
|
@@ -1329,10 +1344,10 @@ function addToKnipIgnoreBinaries(cwd, binary) {
|
|
|
1329
1344
|
`${JSON.stringify(knipConfig, null, " ")}
|
|
1330
1345
|
`
|
|
1331
1346
|
);
|
|
1332
|
-
console.log(
|
|
1347
|
+
console.log(chalk19.dim(`Added '${binary}' to knip.json ignoreBinaries`));
|
|
1333
1348
|
}
|
|
1334
1349
|
} catch {
|
|
1335
|
-
console.log(
|
|
1350
|
+
console.log(chalk19.yellow("Warning: Could not update knip.json"));
|
|
1336
1351
|
}
|
|
1337
1352
|
}
|
|
1338
1353
|
function setupVerifyScript(packageJsonPath, scriptName, command) {
|
|
@@ -1344,7 +1359,7 @@ function setupVerifyScript(packageJsonPath, scriptName, command) {
|
|
|
1344
1359
|
|
|
1345
1360
|
// src/commands/verify/setup/setupBuild.ts
|
|
1346
1361
|
async function setupBuild(packageJsonPath, hasVite, hasTypescript) {
|
|
1347
|
-
console.log(
|
|
1362
|
+
console.log(chalk20.blue("\nSetting up build verification..."));
|
|
1348
1363
|
let command;
|
|
1349
1364
|
if (hasVite && hasTypescript) {
|
|
1350
1365
|
command = "tsc -b && vite build --logLevel error";
|
|
@@ -1353,16 +1368,16 @@ async function setupBuild(packageJsonPath, hasVite, hasTypescript) {
|
|
|
1353
1368
|
} else {
|
|
1354
1369
|
command = "tsc --noEmit";
|
|
1355
1370
|
}
|
|
1356
|
-
console.log(
|
|
1371
|
+
console.log(chalk20.dim(`Using: ${command}`));
|
|
1357
1372
|
const pkg = readPackageJson(packageJsonPath);
|
|
1358
1373
|
writePackageJson(packageJsonPath, addScript(pkg, "verify:build", command));
|
|
1359
1374
|
}
|
|
1360
1375
|
|
|
1361
1376
|
// src/commands/verify/setup/setupDuplicateCode.ts
|
|
1362
1377
|
import * as path5 from "path";
|
|
1363
|
-
import
|
|
1378
|
+
import chalk21 from "chalk";
|
|
1364
1379
|
async function setupDuplicateCode(packageJsonPath) {
|
|
1365
|
-
console.log(
|
|
1380
|
+
console.log(chalk21.blue("\nSetting up jscpd..."));
|
|
1366
1381
|
const cwd = path5.dirname(packageJsonPath);
|
|
1367
1382
|
const pkg = readPackageJson(packageJsonPath);
|
|
1368
1383
|
const hasJscpd = !!pkg.dependencies?.jscpd || !!pkg.devDependencies?.jscpd;
|
|
@@ -1378,9 +1393,9 @@ async function setupDuplicateCode(packageJsonPath) {
|
|
|
1378
1393
|
|
|
1379
1394
|
// src/commands/verify/setup/setupHardcodedColors.ts
|
|
1380
1395
|
import * as path6 from "path";
|
|
1381
|
-
import
|
|
1396
|
+
import chalk22 from "chalk";
|
|
1382
1397
|
async function setupHardcodedColors(packageJsonPath, hasOpenColor) {
|
|
1383
|
-
console.log(
|
|
1398
|
+
console.log(chalk22.blue("\nSetting up hardcoded colors check..."));
|
|
1384
1399
|
const cwd = path6.dirname(packageJsonPath);
|
|
1385
1400
|
if (!hasOpenColor) {
|
|
1386
1401
|
installPackage("open-color", cwd);
|
|
@@ -1395,9 +1410,9 @@ async function setupHardcodedColors(packageJsonPath, hasOpenColor) {
|
|
|
1395
1410
|
|
|
1396
1411
|
// src/commands/verify/setup/setupKnip.ts
|
|
1397
1412
|
import * as path7 from "path";
|
|
1398
|
-
import
|
|
1413
|
+
import chalk23 from "chalk";
|
|
1399
1414
|
async function setupKnip(packageJsonPath) {
|
|
1400
|
-
console.log(
|
|
1415
|
+
console.log(chalk23.blue("\nSetting up knip..."));
|
|
1401
1416
|
const cwd = path7.dirname(packageJsonPath);
|
|
1402
1417
|
const pkg = readPackageJson(packageJsonPath);
|
|
1403
1418
|
if (!pkg.devDependencies?.knip && !installPackage("knip", cwd)) {
|
|
@@ -1412,14 +1427,14 @@ async function setupKnip(packageJsonPath) {
|
|
|
1412
1427
|
|
|
1413
1428
|
// src/commands/verify/setup/setupLint.ts
|
|
1414
1429
|
import * as path8 from "path";
|
|
1415
|
-
import
|
|
1430
|
+
import chalk25 from "chalk";
|
|
1416
1431
|
|
|
1417
1432
|
// src/commands/lint/init.ts
|
|
1418
1433
|
import { execSync as execSync9 } from "child_process";
|
|
1419
1434
|
import { existsSync as existsSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
1420
1435
|
import { dirname as dirname6, join as join6 } from "path";
|
|
1421
1436
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1422
|
-
import
|
|
1437
|
+
import chalk24 from "chalk";
|
|
1423
1438
|
|
|
1424
1439
|
// src/shared/removeEslint.ts
|
|
1425
1440
|
import { execSync as execSync8 } from "child_process";
|
|
@@ -1525,10 +1540,10 @@ async function init2() {
|
|
|
1525
1540
|
console.log("biome.json already has the correct linter config");
|
|
1526
1541
|
return;
|
|
1527
1542
|
}
|
|
1528
|
-
console.log(
|
|
1543
|
+
console.log(chalk24.yellow("\n\u26A0\uFE0F biome.json will be updated:"));
|
|
1529
1544
|
console.log();
|
|
1530
1545
|
printDiff(oldContent, newContent);
|
|
1531
|
-
const confirm = await promptConfirm(
|
|
1546
|
+
const confirm = await promptConfirm(chalk24.red("Update biome.json?"));
|
|
1532
1547
|
if (!confirm) {
|
|
1533
1548
|
console.log("Skipped biome.json update");
|
|
1534
1549
|
return;
|
|
@@ -1539,7 +1554,7 @@ async function init2() {
|
|
|
1539
1554
|
|
|
1540
1555
|
// src/commands/verify/setup/setupLint.ts
|
|
1541
1556
|
async function setupLint(packageJsonPath) {
|
|
1542
|
-
console.log(
|
|
1557
|
+
console.log(chalk25.blue("\nSetting up biome..."));
|
|
1543
1558
|
const cwd = path8.dirname(packageJsonPath);
|
|
1544
1559
|
const pkg = readPackageJson(packageJsonPath);
|
|
1545
1560
|
if (!pkg.devDependencies?.["@biomejs/biome"]) {
|
|
@@ -1557,9 +1572,9 @@ async function setupLint(packageJsonPath) {
|
|
|
1557
1572
|
|
|
1558
1573
|
// src/commands/verify/setup/setupTest.ts
|
|
1559
1574
|
import * as path9 from "path";
|
|
1560
|
-
import
|
|
1575
|
+
import chalk26 from "chalk";
|
|
1561
1576
|
async function setupTest(packageJsonPath) {
|
|
1562
|
-
console.log(
|
|
1577
|
+
console.log(chalk26.blue("\nSetting up vitest..."));
|
|
1563
1578
|
const cwd = path9.dirname(packageJsonPath);
|
|
1564
1579
|
const pkg = readPackageJson(packageJsonPath);
|
|
1565
1580
|
if (!pkg.devDependencies?.vitest && !installPackage("vitest", cwd)) {
|
|
@@ -1702,16 +1717,16 @@ async function init3() {
|
|
|
1702
1717
|
const setup = detectExistingSetup(pkg);
|
|
1703
1718
|
const availableOptions = getAvailableOptions(setup);
|
|
1704
1719
|
if (availableOptions.length === 0) {
|
|
1705
|
-
console.log(
|
|
1720
|
+
console.log(chalk27.green("All verify scripts are already configured!"));
|
|
1706
1721
|
return;
|
|
1707
1722
|
}
|
|
1708
|
-
console.log(
|
|
1723
|
+
console.log(chalk27.bold("Available verify scripts to add:\n"));
|
|
1709
1724
|
const selected = await promptMultiselect(
|
|
1710
1725
|
"Select verify scripts to add:",
|
|
1711
1726
|
availableOptions
|
|
1712
1727
|
);
|
|
1713
1728
|
if (selected.length === 0) {
|
|
1714
|
-
console.log(
|
|
1729
|
+
console.log(chalk27.yellow("No scripts selected"));
|
|
1715
1730
|
return;
|
|
1716
1731
|
}
|
|
1717
1732
|
for (const choice of selected) {
|
|
@@ -1736,28 +1751,28 @@ async function init3() {
|
|
|
1736
1751
|
break;
|
|
1737
1752
|
}
|
|
1738
1753
|
}
|
|
1739
|
-
console.log(
|
|
1754
|
+
console.log(chalk27.green(`
|
|
1740
1755
|
Added ${selected.length} verify script(s):`));
|
|
1741
1756
|
for (const choice of selected) {
|
|
1742
|
-
console.log(
|
|
1757
|
+
console.log(chalk27.green(` - verify:${choice}`));
|
|
1743
1758
|
}
|
|
1744
|
-
console.log(
|
|
1759
|
+
console.log(chalk27.dim("\nRun 'assist verify' to run all verify scripts"));
|
|
1745
1760
|
}
|
|
1746
1761
|
|
|
1747
1762
|
// src/commands/vscode/init.ts
|
|
1748
1763
|
import * as fs8 from "fs";
|
|
1749
1764
|
import * as path11 from "path";
|
|
1750
|
-
import
|
|
1765
|
+
import chalk29 from "chalk";
|
|
1751
1766
|
|
|
1752
1767
|
// src/commands/vscode/createLaunchJson.ts
|
|
1753
1768
|
import * as fs7 from "fs";
|
|
1754
1769
|
import * as path10 from "path";
|
|
1755
|
-
import
|
|
1770
|
+
import chalk28 from "chalk";
|
|
1756
1771
|
function ensureVscodeFolder() {
|
|
1757
1772
|
const vscodeDir = path10.join(process.cwd(), ".vscode");
|
|
1758
1773
|
if (!fs7.existsSync(vscodeDir)) {
|
|
1759
1774
|
fs7.mkdirSync(vscodeDir);
|
|
1760
|
-
console.log(
|
|
1775
|
+
console.log(chalk28.dim("Created .vscode folder"));
|
|
1761
1776
|
}
|
|
1762
1777
|
}
|
|
1763
1778
|
function removeVscodeFromGitignore() {
|
|
@@ -1772,7 +1787,7 @@ function removeVscodeFromGitignore() {
|
|
|
1772
1787
|
);
|
|
1773
1788
|
if (filteredLines.length !== lines.length) {
|
|
1774
1789
|
fs7.writeFileSync(gitignorePath, filteredLines.join("\n"));
|
|
1775
|
-
console.log(
|
|
1790
|
+
console.log(chalk28.dim("Removed .vscode references from .gitignore"));
|
|
1776
1791
|
}
|
|
1777
1792
|
}
|
|
1778
1793
|
function createLaunchJson() {
|
|
@@ -1790,7 +1805,7 @@ function createLaunchJson() {
|
|
|
1790
1805
|
const launchPath = path10.join(process.cwd(), ".vscode", "launch.json");
|
|
1791
1806
|
fs7.writeFileSync(launchPath, `${JSON.stringify(launchConfig, null, " ")}
|
|
1792
1807
|
`);
|
|
1793
|
-
console.log(
|
|
1808
|
+
console.log(chalk28.green("Created .vscode/launch.json"));
|
|
1794
1809
|
}
|
|
1795
1810
|
function createSettingsJson() {
|
|
1796
1811
|
const settings = {
|
|
@@ -1803,7 +1818,7 @@ function createSettingsJson() {
|
|
|
1803
1818
|
const settingsPath = path10.join(process.cwd(), ".vscode", "settings.json");
|
|
1804
1819
|
fs7.writeFileSync(settingsPath, `${JSON.stringify(settings, null, " ")}
|
|
1805
1820
|
`);
|
|
1806
|
-
console.log(
|
|
1821
|
+
console.log(chalk28.green("Created .vscode/settings.json"));
|
|
1807
1822
|
}
|
|
1808
1823
|
function createExtensionsJson() {
|
|
1809
1824
|
const extensions = {
|
|
@@ -1815,7 +1830,7 @@ function createExtensionsJson() {
|
|
|
1815
1830
|
`${JSON.stringify(extensions, null, " ")}
|
|
1816
1831
|
`
|
|
1817
1832
|
);
|
|
1818
|
-
console.log(
|
|
1833
|
+
console.log(chalk28.green("Created .vscode/extensions.json"));
|
|
1819
1834
|
}
|
|
1820
1835
|
|
|
1821
1836
|
// src/commands/vscode/init.ts
|
|
@@ -1847,16 +1862,16 @@ async function init4() {
|
|
|
1847
1862
|
});
|
|
1848
1863
|
}
|
|
1849
1864
|
if (availableOptions.length === 0) {
|
|
1850
|
-
console.log(
|
|
1865
|
+
console.log(chalk29.green("VS Code configuration already exists!"));
|
|
1851
1866
|
return;
|
|
1852
1867
|
}
|
|
1853
|
-
console.log(
|
|
1868
|
+
console.log(chalk29.bold("Available VS Code configurations to add:\n"));
|
|
1854
1869
|
const selected = await promptMultiselect(
|
|
1855
1870
|
"Select configurations to add:",
|
|
1856
1871
|
availableOptions
|
|
1857
1872
|
);
|
|
1858
1873
|
if (selected.length === 0) {
|
|
1859
|
-
console.log(
|
|
1874
|
+
console.log(chalk29.yellow("No configurations selected"));
|
|
1860
1875
|
return;
|
|
1861
1876
|
}
|
|
1862
1877
|
removeVscodeFromGitignore();
|
|
@@ -1873,7 +1888,7 @@ async function init4() {
|
|
|
1873
1888
|
}
|
|
1874
1889
|
}
|
|
1875
1890
|
console.log(
|
|
1876
|
-
|
|
1891
|
+
chalk29.green(`
|
|
1877
1892
|
Added ${selected.length} VS Code configuration(s)`)
|
|
1878
1893
|
);
|
|
1879
1894
|
}
|
|
@@ -1887,7 +1902,7 @@ async function init5() {
|
|
|
1887
1902
|
// src/commands/lint/runFileNameCheck.ts
|
|
1888
1903
|
import fs10 from "fs";
|
|
1889
1904
|
import path13 from "path";
|
|
1890
|
-
import
|
|
1905
|
+
import chalk30 from "chalk";
|
|
1891
1906
|
|
|
1892
1907
|
// src/shared/findSourceFiles.ts
|
|
1893
1908
|
import fs9 from "fs";
|
|
@@ -1939,16 +1954,16 @@ function checkFileNames() {
|
|
|
1939
1954
|
function runFileNameCheck() {
|
|
1940
1955
|
const violations = checkFileNames();
|
|
1941
1956
|
if (violations.length > 0) {
|
|
1942
|
-
console.error(
|
|
1957
|
+
console.error(chalk30.red("\nFile name check failed:\n"));
|
|
1943
1958
|
console.error(
|
|
1944
|
-
|
|
1959
|
+
chalk30.red(
|
|
1945
1960
|
" Files without classes or React components should not start with a capital letter.\n"
|
|
1946
1961
|
)
|
|
1947
1962
|
);
|
|
1948
1963
|
for (const violation of violations) {
|
|
1949
|
-
console.error(
|
|
1964
|
+
console.error(chalk30.red(` ${violation.filePath}`));
|
|
1950
1965
|
console.error(
|
|
1951
|
-
|
|
1966
|
+
chalk30.gray(
|
|
1952
1967
|
` Rename to: ${violation.fileName.charAt(0).toLowerCase()}${violation.fileName.slice(1)}
|
|
1953
1968
|
`
|
|
1954
1969
|
)
|
|
@@ -1968,17 +1983,17 @@ function runFileNameCheck() {
|
|
|
1968
1983
|
import fs11 from "fs";
|
|
1969
1984
|
|
|
1970
1985
|
// src/commands/lint/shared.ts
|
|
1971
|
-
import
|
|
1986
|
+
import chalk31 from "chalk";
|
|
1972
1987
|
function reportViolations(violations, checkName, errorMessage, successMessage) {
|
|
1973
1988
|
if (violations.length > 0) {
|
|
1974
|
-
console.error(
|
|
1989
|
+
console.error(chalk31.red(`
|
|
1975
1990
|
${checkName} failed:
|
|
1976
1991
|
`));
|
|
1977
|
-
console.error(
|
|
1992
|
+
console.error(chalk31.red(` ${errorMessage}
|
|
1978
1993
|
`));
|
|
1979
1994
|
for (const violation of violations) {
|
|
1980
|
-
console.error(
|
|
1981
|
-
console.error(
|
|
1995
|
+
console.error(chalk31.red(` ${violation.filePath}:${violation.line}`));
|
|
1996
|
+
console.error(chalk31.gray(` ${violation.content}
|
|
1982
1997
|
`));
|
|
1983
1998
|
}
|
|
1984
1999
|
return false;
|
|
@@ -2227,12 +2242,15 @@ async function notify() {
|
|
|
2227
2242
|
console.log(`Notification sent: ${notification_type} for ${projectName}`);
|
|
2228
2243
|
}
|
|
2229
2244
|
|
|
2230
|
-
// src/commands/prs/
|
|
2231
|
-
import { execSync as
|
|
2245
|
+
// src/commands/prs/resolveCommentWithReply.ts
|
|
2246
|
+
import { execSync as execSync12 } from "child_process";
|
|
2232
2247
|
import { existsSync as existsSync11, readFileSync as readFileSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
2233
2248
|
import { tmpdir } from "os";
|
|
2234
2249
|
import { join as join9 } from "path";
|
|
2235
2250
|
import { parse } from "yaml";
|
|
2251
|
+
|
|
2252
|
+
// src/commands/prs/shared.ts
|
|
2253
|
+
import { execSync as execSync11 } from "child_process";
|
|
2236
2254
|
function isGhNotInstalled(error) {
|
|
2237
2255
|
if (error instanceof Error) {
|
|
2238
2256
|
const msg = error.message.toLowerCase();
|
|
@@ -2266,8 +2284,10 @@ function getCurrentPrNumber() {
|
|
|
2266
2284
|
throw error;
|
|
2267
2285
|
}
|
|
2268
2286
|
}
|
|
2287
|
+
|
|
2288
|
+
// src/commands/prs/resolveCommentWithReply.ts
|
|
2269
2289
|
function replyToComment(org, repo, prNumber, commentId, message) {
|
|
2270
|
-
|
|
2290
|
+
execSync12(
|
|
2271
2291
|
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments -f body="${message.replace(/"/g, '\\"')}" -F in_reply_to=${commentId}`,
|
|
2272
2292
|
{ stdio: "inherit" }
|
|
2273
2293
|
);
|
|
@@ -2277,7 +2297,7 @@ function resolveThread(threadId) {
|
|
|
2277
2297
|
const queryFile = join9(tmpdir(), `gh-mutation-${Date.now()}.graphql`);
|
|
2278
2298
|
writeFileSync9(queryFile, mutation);
|
|
2279
2299
|
try {
|
|
2280
|
-
|
|
2300
|
+
execSync12(
|
|
2281
2301
|
`gh api graphql -F query=@${queryFile} -f threadId="${threadId}"`,
|
|
2282
2302
|
{ stdio: "inherit" }
|
|
2283
2303
|
);
|
|
@@ -2356,7 +2376,7 @@ function fixed(commentId, sha) {
|
|
|
2356
2376
|
// src/commands/prs/listComments.ts
|
|
2357
2377
|
import { existsSync as existsSync12, mkdirSync as mkdirSync3, writeFileSync as writeFileSync11 } from "fs";
|
|
2358
2378
|
import { join as join11 } from "path";
|
|
2359
|
-
import
|
|
2379
|
+
import chalk32 from "chalk";
|
|
2360
2380
|
import { stringify } from "yaml";
|
|
2361
2381
|
|
|
2362
2382
|
// src/lib/isClaudeCode.ts
|
|
@@ -2365,9 +2385,9 @@ function isClaudeCode() {
|
|
|
2365
2385
|
}
|
|
2366
2386
|
|
|
2367
2387
|
// src/commands/prs/fetchReviewComments.ts
|
|
2368
|
-
import { execSync as
|
|
2388
|
+
import { execSync as execSync13 } from "child_process";
|
|
2369
2389
|
function fetchReviewComments(org, repo, prNumber) {
|
|
2370
|
-
const result =
|
|
2390
|
+
const result = execSync13(
|
|
2371
2391
|
`gh api repos/${org}/${repo}/pulls/${prNumber}/reviews`,
|
|
2372
2392
|
{ encoding: "utf-8" }
|
|
2373
2393
|
);
|
|
@@ -2384,7 +2404,7 @@ function fetchReviewComments(org, repo, prNumber) {
|
|
|
2384
2404
|
);
|
|
2385
2405
|
}
|
|
2386
2406
|
function fetchLineComments(org, repo, prNumber, threadInfo) {
|
|
2387
|
-
const result =
|
|
2407
|
+
const result = execSync13(
|
|
2388
2408
|
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments`,
|
|
2389
2409
|
{ encoding: "utf-8" }
|
|
2390
2410
|
);
|
|
@@ -2410,7 +2430,7 @@ function fetchLineComments(org, repo, prNumber, threadInfo) {
|
|
|
2410
2430
|
}
|
|
2411
2431
|
|
|
2412
2432
|
// src/commands/prs/fetchThreadIds.ts
|
|
2413
|
-
import { execSync as
|
|
2433
|
+
import { execSync as execSync14 } from "child_process";
|
|
2414
2434
|
import { unlinkSync as unlinkSync3, writeFileSync as writeFileSync10 } from "fs";
|
|
2415
2435
|
import { tmpdir as tmpdir2 } from "os";
|
|
2416
2436
|
import { join as join10 } from "path";
|
|
@@ -2419,7 +2439,7 @@ function fetchThreadIds(org, repo, prNumber) {
|
|
|
2419
2439
|
const queryFile = join10(tmpdir2(), `gh-query-${Date.now()}.graphql`);
|
|
2420
2440
|
writeFileSync10(queryFile, THREAD_QUERY);
|
|
2421
2441
|
try {
|
|
2422
|
-
const result =
|
|
2442
|
+
const result = execSync14(
|
|
2423
2443
|
`gh api graphql -F query=@${queryFile} -F owner="${org}" -F repo="${repo}" -F prNumber=${prNumber}`,
|
|
2424
2444
|
{ encoding: "utf-8" }
|
|
2425
2445
|
);
|
|
@@ -2443,17 +2463,17 @@ function fetchThreadIds(org, repo, prNumber) {
|
|
|
2443
2463
|
// src/commands/prs/listComments.ts
|
|
2444
2464
|
function formatForHuman(comment) {
|
|
2445
2465
|
if (comment.type === "review") {
|
|
2446
|
-
const stateColor = comment.state === "APPROVED" ?
|
|
2466
|
+
const stateColor = comment.state === "APPROVED" ? chalk32.green : comment.state === "CHANGES_REQUESTED" ? chalk32.red : chalk32.yellow;
|
|
2447
2467
|
return [
|
|
2448
|
-
`${
|
|
2468
|
+
`${chalk32.cyan("Review")} by ${chalk32.bold(comment.user)} ${stateColor(`[${comment.state}]`)}`,
|
|
2449
2469
|
comment.body,
|
|
2450
2470
|
""
|
|
2451
2471
|
].join("\n");
|
|
2452
2472
|
}
|
|
2453
2473
|
const location = comment.line ? `:${comment.line}` : "";
|
|
2454
2474
|
return [
|
|
2455
|
-
`${
|
|
2456
|
-
|
|
2475
|
+
`${chalk32.cyan("Line comment")} by ${chalk32.bold(comment.user)} on ${chalk32.dim(`${comment.path}${location}`)}`,
|
|
2476
|
+
chalk32.dim(comment.diff_hunk.split("\n").slice(-3).join("\n")),
|
|
2457
2477
|
comment.body,
|
|
2458
2478
|
""
|
|
2459
2479
|
].join("\n");
|
|
@@ -2520,67 +2540,45 @@ async function listComments() {
|
|
|
2520
2540
|
}
|
|
2521
2541
|
|
|
2522
2542
|
// src/commands/prs/prs.ts
|
|
2523
|
-
import { execSync as
|
|
2524
|
-
|
|
2543
|
+
import { execSync as execSync15 } from "child_process";
|
|
2544
|
+
|
|
2545
|
+
// src/commands/prs/displayPaginated.ts
|
|
2546
|
+
import chalk33 from "chalk";
|
|
2525
2547
|
import enquirer4 from "enquirer";
|
|
2526
2548
|
var PAGE_SIZE = 10;
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2549
|
+
function getStatus(pr) {
|
|
2550
|
+
if (pr.state === "MERGED" && pr.mergedAt) {
|
|
2551
|
+
return { label: chalk33.magenta("merged"), date: pr.mergedAt };
|
|
2552
|
+
}
|
|
2553
|
+
if (pr.state === "CLOSED" && pr.closedAt) {
|
|
2554
|
+
return { label: chalk33.red("closed"), date: pr.closedAt };
|
|
2555
|
+
}
|
|
2556
|
+
return { label: chalk33.green("opened"), date: pr.createdAt };
|
|
2557
|
+
}
|
|
2558
|
+
function displayPage(pullRequests, totalPages, page) {
|
|
2559
|
+
const start = page * PAGE_SIZE;
|
|
2560
|
+
const end = Math.min(start + PAGE_SIZE, pullRequests.length);
|
|
2561
|
+
const pagePrs = pullRequests.slice(start, end);
|
|
2562
|
+
console.log(
|
|
2563
|
+
`
|
|
2564
|
+
Page ${page + 1} of ${totalPages} (${pullRequests.length} total)
|
|
2565
|
+
`
|
|
2566
|
+
);
|
|
2567
|
+
for (const pr of pagePrs) {
|
|
2568
|
+
const status = getStatus(pr);
|
|
2569
|
+
const formattedDate = new Date(status.date).toISOString().split("T")[0];
|
|
2570
|
+
const fileCount = pr.changedFiles.toLocaleString();
|
|
2571
|
+
console.log(
|
|
2572
|
+
`${chalk33.cyan(`#${pr.number}`)} ${pr.title} ${chalk33.dim(`(${pr.author.login},`)} ${status.label} ${chalk33.dim(`${formattedDate})`)}`
|
|
2533
2573
|
);
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
console.log(
|
|
2537
|
-
`No ${state === "all" ? "" : `${state} `}pull requests found.`
|
|
2538
|
-
);
|
|
2539
|
-
return;
|
|
2540
|
-
}
|
|
2541
|
-
await displayPaginated(pullRequests);
|
|
2542
|
-
} catch (error) {
|
|
2543
|
-
if (isGhNotInstalled(error)) {
|
|
2544
|
-
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
2545
|
-
console.error("Install it from https://cli.github.com/");
|
|
2546
|
-
return;
|
|
2547
|
-
}
|
|
2548
|
-
throw error;
|
|
2574
|
+
console.log(chalk33.dim(` ${fileCount} files | ${pr.url}`));
|
|
2575
|
+
console.log();
|
|
2549
2576
|
}
|
|
2550
2577
|
}
|
|
2551
2578
|
async function displayPaginated(pullRequests) {
|
|
2552
2579
|
const totalPages = Math.ceil(pullRequests.length / PAGE_SIZE);
|
|
2553
2580
|
let currentPage = 0;
|
|
2554
|
-
|
|
2555
|
-
if (pr.state === "MERGED" && pr.mergedAt) {
|
|
2556
|
-
return { label: chalk32.magenta("merged"), date: pr.mergedAt };
|
|
2557
|
-
}
|
|
2558
|
-
if (pr.state === "CLOSED" && pr.closedAt) {
|
|
2559
|
-
return { label: chalk32.red("closed"), date: pr.closedAt };
|
|
2560
|
-
}
|
|
2561
|
-
return { label: chalk32.green("opened"), date: pr.createdAt };
|
|
2562
|
-
};
|
|
2563
|
-
const displayPage = (page) => {
|
|
2564
|
-
const start = page * PAGE_SIZE;
|
|
2565
|
-
const end = Math.min(start + PAGE_SIZE, pullRequests.length);
|
|
2566
|
-
const pagePrs = pullRequests.slice(start, end);
|
|
2567
|
-
console.log(
|
|
2568
|
-
`
|
|
2569
|
-
Page ${page + 1} of ${totalPages} (${pullRequests.length} total)
|
|
2570
|
-
`
|
|
2571
|
-
);
|
|
2572
|
-
for (const pr of pagePrs) {
|
|
2573
|
-
const status = getStatus(pr);
|
|
2574
|
-
const formattedDate = new Date(status.date).toISOString().split("T")[0];
|
|
2575
|
-
const fileCount = pr.changedFiles.toLocaleString();
|
|
2576
|
-
console.log(
|
|
2577
|
-
`${chalk32.cyan(`#${pr.number}`)} ${pr.title} ${chalk32.dim(`(${pr.author.login},`)} ${status.label} ${chalk32.dim(`${formattedDate})`)}`
|
|
2578
|
-
);
|
|
2579
|
-
console.log(chalk32.dim(` ${fileCount} files | ${pr.url}`));
|
|
2580
|
-
console.log();
|
|
2581
|
-
}
|
|
2582
|
-
};
|
|
2583
|
-
displayPage(currentPage);
|
|
2581
|
+
displayPage(pullRequests, totalPages, currentPage);
|
|
2584
2582
|
if (totalPages <= 1) {
|
|
2585
2583
|
return;
|
|
2586
2584
|
}
|
|
@@ -2599,18 +2597,44 @@ Page ${page + 1} of ${totalPages} (${pullRequests.length} total)
|
|
|
2599
2597
|
});
|
|
2600
2598
|
if (action === "Next page") {
|
|
2601
2599
|
currentPage++;
|
|
2602
|
-
displayPage(currentPage);
|
|
2600
|
+
displayPage(pullRequests, totalPages, currentPage);
|
|
2603
2601
|
} else if (action === "Previous page") {
|
|
2604
2602
|
currentPage--;
|
|
2605
|
-
displayPage(currentPage);
|
|
2603
|
+
displayPage(pullRequests, totalPages, currentPage);
|
|
2606
2604
|
} else {
|
|
2607
2605
|
break;
|
|
2608
2606
|
}
|
|
2609
2607
|
}
|
|
2610
2608
|
}
|
|
2611
2609
|
|
|
2610
|
+
// src/commands/prs/prs.ts
|
|
2611
|
+
async function prs(options) {
|
|
2612
|
+
const state = options.open ? "open" : options.closed ? "closed" : "all";
|
|
2613
|
+
try {
|
|
2614
|
+
const result = execSync15(
|
|
2615
|
+
`gh pr list --state ${state} --json number,title,url,author,createdAt,mergedAt,closedAt,state,changedFiles --limit 100`,
|
|
2616
|
+
{ encoding: "utf-8" }
|
|
2617
|
+
);
|
|
2618
|
+
const pullRequests = JSON.parse(result);
|
|
2619
|
+
if (pullRequests.length === 0) {
|
|
2620
|
+
console.log(
|
|
2621
|
+
`No ${state === "all" ? "" : `${state} `}pull requests found.`
|
|
2622
|
+
);
|
|
2623
|
+
return;
|
|
2624
|
+
}
|
|
2625
|
+
await displayPaginated(pullRequests);
|
|
2626
|
+
} catch (error) {
|
|
2627
|
+
if (isGhNotInstalled(error)) {
|
|
2628
|
+
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
2629
|
+
console.error("Install it from https://cli.github.com/");
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
throw error;
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2612
2636
|
// src/commands/prs/wontfix.ts
|
|
2613
|
-
import { execSync as
|
|
2637
|
+
import { execSync as execSync16 } from "child_process";
|
|
2614
2638
|
function validateReason(reason) {
|
|
2615
2639
|
const lowerReason = reason.toLowerCase();
|
|
2616
2640
|
if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
|
|
@@ -2627,7 +2651,7 @@ function validateShaReferences(reason) {
|
|
|
2627
2651
|
const invalidShas = [];
|
|
2628
2652
|
for (const sha of shas) {
|
|
2629
2653
|
try {
|
|
2630
|
-
|
|
2654
|
+
execSync16(`git cat-file -t ${sha}`, { stdio: "pipe" });
|
|
2631
2655
|
} catch {
|
|
2632
2656
|
invalidShas.push(sha);
|
|
2633
2657
|
}
|
|
@@ -2659,7 +2683,7 @@ import { spawn as spawn2 } from "child_process";
|
|
|
2659
2683
|
import * as path15 from "path";
|
|
2660
2684
|
|
|
2661
2685
|
// src/commands/refactor/getViolations.ts
|
|
2662
|
-
import { execSync as
|
|
2686
|
+
import { execSync as execSync17 } from "child_process";
|
|
2663
2687
|
import fs15 from "fs";
|
|
2664
2688
|
import { minimatch as minimatch2 } from "minimatch";
|
|
2665
2689
|
|
|
@@ -2699,7 +2723,7 @@ function getIgnoredFiles() {
|
|
|
2699
2723
|
}
|
|
2700
2724
|
|
|
2701
2725
|
// src/commands/refactor/logViolations.ts
|
|
2702
|
-
import
|
|
2726
|
+
import chalk34 from "chalk";
|
|
2703
2727
|
var DEFAULT_MAX_LINES = 100;
|
|
2704
2728
|
function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
2705
2729
|
if (violations.length === 0) {
|
|
@@ -2708,43 +2732,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES) {
|
|
|
2708
2732
|
}
|
|
2709
2733
|
return;
|
|
2710
2734
|
}
|
|
2711
|
-
console.error(
|
|
2735
|
+
console.error(chalk34.red(`
|
|
2712
2736
|
Refactor check failed:
|
|
2713
2737
|
`));
|
|
2714
|
-
console.error(
|
|
2738
|
+
console.error(chalk34.red(` The following files exceed ${maxLines} lines:
|
|
2715
2739
|
`));
|
|
2716
2740
|
for (const violation of violations) {
|
|
2717
|
-
console.error(
|
|
2741
|
+
console.error(chalk34.red(` ${violation.file} (${violation.lines} lines)`));
|
|
2718
2742
|
}
|
|
2719
2743
|
console.error(
|
|
2720
|
-
|
|
2744
|
+
chalk34.yellow(
|
|
2721
2745
|
`
|
|
2722
2746
|
Each file needs to be sensibly refactored, or if there is no sensible
|
|
2723
2747
|
way to refactor it, ignore it with:
|
|
2724
2748
|
`
|
|
2725
2749
|
)
|
|
2726
2750
|
);
|
|
2727
|
-
console.error(
|
|
2751
|
+
console.error(chalk34.gray(` assist refactor ignore <file>
|
|
2728
2752
|
`));
|
|
2729
2753
|
if (process.env.CLAUDECODE) {
|
|
2730
|
-
console.error(
|
|
2754
|
+
console.error(chalk34.cyan(`
|
|
2731
2755
|
## Extracting Code to New Files
|
|
2732
2756
|
`));
|
|
2733
2757
|
console.error(
|
|
2734
|
-
|
|
2758
|
+
chalk34.cyan(
|
|
2735
2759
|
` When extracting logic from one file to another, consider where the extracted code belongs:
|
|
2736
2760
|
`
|
|
2737
2761
|
)
|
|
2738
2762
|
);
|
|
2739
2763
|
console.error(
|
|
2740
|
-
|
|
2764
|
+
chalk34.cyan(
|
|
2741
2765
|
` 1. Keep related logic together: If the extracted code is tightly coupled to the
|
|
2742
2766
|
original file's domain, create a new folder containing both the original and extracted files.
|
|
2743
2767
|
`
|
|
2744
2768
|
)
|
|
2745
2769
|
);
|
|
2746
2770
|
console.error(
|
|
2747
|
-
|
|
2771
|
+
chalk34.cyan(
|
|
2748
2772
|
` 2. Share common utilities: If the extracted code can be reused across multiple
|
|
2749
2773
|
domains, move it to a common/shared folder.
|
|
2750
2774
|
`
|
|
@@ -2764,7 +2788,7 @@ function getGitFiles(options) {
|
|
|
2764
2788
|
}
|
|
2765
2789
|
const files = /* @__PURE__ */ new Set();
|
|
2766
2790
|
if (options.staged || options.modified) {
|
|
2767
|
-
const staged =
|
|
2791
|
+
const staged = execSync17("git diff --cached --name-only", {
|
|
2768
2792
|
encoding: "utf-8"
|
|
2769
2793
|
});
|
|
2770
2794
|
for (const file of staged.trim().split("\n").filter(Boolean)) {
|
|
@@ -2772,7 +2796,7 @@ function getGitFiles(options) {
|
|
|
2772
2796
|
}
|
|
2773
2797
|
}
|
|
2774
2798
|
if (options.unstaged || options.modified) {
|
|
2775
|
-
const unstaged =
|
|
2799
|
+
const unstaged = execSync17("git diff --name-only", { encoding: "utf-8" });
|
|
2776
2800
|
for (const file of unstaged.trim().split("\n").filter(Boolean)) {
|
|
2777
2801
|
files.add(file);
|
|
2778
2802
|
}
|
|
@@ -2861,11 +2885,11 @@ async function check(pattern2, options) {
|
|
|
2861
2885
|
|
|
2862
2886
|
// src/commands/refactor/ignore.ts
|
|
2863
2887
|
import fs16 from "fs";
|
|
2864
|
-
import
|
|
2888
|
+
import chalk35 from "chalk";
|
|
2865
2889
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
2866
2890
|
function ignore(file) {
|
|
2867
2891
|
if (!fs16.existsSync(file)) {
|
|
2868
|
-
console.error(
|
|
2892
|
+
console.error(chalk35.red(`Error: File does not exist: ${file}`));
|
|
2869
2893
|
process.exit(1);
|
|
2870
2894
|
}
|
|
2871
2895
|
const content = fs16.readFileSync(file, "utf-8");
|
|
@@ -2881,12 +2905,430 @@ function ignore(file) {
|
|
|
2881
2905
|
fs16.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
2882
2906
|
}
|
|
2883
2907
|
console.log(
|
|
2884
|
-
|
|
2908
|
+
chalk35.green(
|
|
2885
2909
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
2886
2910
|
)
|
|
2887
2911
|
);
|
|
2888
2912
|
}
|
|
2889
2913
|
|
|
2914
|
+
// src/commands/refactor/restructure/index.ts
|
|
2915
|
+
import path23 from "path";
|
|
2916
|
+
import chalk38 from "chalk";
|
|
2917
|
+
|
|
2918
|
+
// src/commands/refactor/restructure/buildImportGraph.ts
|
|
2919
|
+
import path16 from "path";
|
|
2920
|
+
import ts5 from "typescript";
|
|
2921
|
+
function loadCompilerOptions(tsConfigPath) {
|
|
2922
|
+
const configFile = ts5.readConfigFile(tsConfigPath, ts5.sys.readFile);
|
|
2923
|
+
const parsed = ts5.parseJsonConfigFileContent(
|
|
2924
|
+
configFile.config,
|
|
2925
|
+
ts5.sys,
|
|
2926
|
+
path16.dirname(tsConfigPath)
|
|
2927
|
+
);
|
|
2928
|
+
return parsed.options;
|
|
2929
|
+
}
|
|
2930
|
+
function getImportSpecifiers(sourceFile) {
|
|
2931
|
+
const specifiers = [];
|
|
2932
|
+
const visit = (node) => {
|
|
2933
|
+
if (ts5.isImportDeclaration(node) && ts5.isStringLiteral(node.moduleSpecifier)) {
|
|
2934
|
+
specifiers.push(node.moduleSpecifier.text);
|
|
2935
|
+
} else if (ts5.isExportDeclaration(node) && node.moduleSpecifier && ts5.isStringLiteral(node.moduleSpecifier)) {
|
|
2936
|
+
specifiers.push(node.moduleSpecifier.text);
|
|
2937
|
+
} else if (ts5.isCallExpression(node) && node.expression.kind === ts5.SyntaxKind.ImportKeyword && node.arguments.length === 1 && ts5.isStringLiteral(node.arguments[0])) {
|
|
2938
|
+
specifiers.push(node.arguments[0].text);
|
|
2939
|
+
}
|
|
2940
|
+
ts5.forEachChild(node, visit);
|
|
2941
|
+
};
|
|
2942
|
+
visit(sourceFile);
|
|
2943
|
+
return specifiers;
|
|
2944
|
+
}
|
|
2945
|
+
function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
2946
|
+
const options = loadCompilerOptions(tsConfigPath);
|
|
2947
|
+
const configFile = ts5.readConfigFile(tsConfigPath, ts5.sys.readFile);
|
|
2948
|
+
const parsed = ts5.parseJsonConfigFileContent(
|
|
2949
|
+
configFile.config,
|
|
2950
|
+
ts5.sys,
|
|
2951
|
+
path16.dirname(tsConfigPath)
|
|
2952
|
+
);
|
|
2953
|
+
const program2 = ts5.createProgram(parsed.fileNames, options);
|
|
2954
|
+
const allFiles = /* @__PURE__ */ new Set();
|
|
2955
|
+
const edges = [];
|
|
2956
|
+
const importedBy = /* @__PURE__ */ new Map();
|
|
2957
|
+
const imports = /* @__PURE__ */ new Map();
|
|
2958
|
+
for (const sourceFile of program2.getSourceFiles()) {
|
|
2959
|
+
const filePath = path16.resolve(sourceFile.fileName);
|
|
2960
|
+
if (filePath.includes("node_modules")) continue;
|
|
2961
|
+
allFiles.add(filePath);
|
|
2962
|
+
const specifiers = getImportSpecifiers(sourceFile);
|
|
2963
|
+
for (const specifier of specifiers) {
|
|
2964
|
+
if (!specifier.startsWith(".")) continue;
|
|
2965
|
+
const resolved = ts5.resolveModuleName(
|
|
2966
|
+
specifier,
|
|
2967
|
+
filePath,
|
|
2968
|
+
options,
|
|
2969
|
+
ts5.sys
|
|
2970
|
+
);
|
|
2971
|
+
const resolvedPath = resolved.resolvedModule?.resolvedFileName;
|
|
2972
|
+
if (!resolvedPath || resolvedPath.includes("node_modules")) continue;
|
|
2973
|
+
const absTarget = path16.resolve(resolvedPath);
|
|
2974
|
+
edges.push({ source: filePath, target: absTarget, specifier });
|
|
2975
|
+
const targetSet = importedBy.get(absTarget) ?? /* @__PURE__ */ new Set();
|
|
2976
|
+
if (!importedBy.has(absTarget)) importedBy.set(absTarget, targetSet);
|
|
2977
|
+
targetSet.add(filePath);
|
|
2978
|
+
const sourceSet = imports.get(filePath) ?? /* @__PURE__ */ new Set();
|
|
2979
|
+
if (!imports.has(filePath)) imports.set(filePath, sourceSet);
|
|
2980
|
+
sourceSet.add(absTarget);
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
return { files: candidateFiles, edges, importedBy, imports };
|
|
2984
|
+
}
|
|
2985
|
+
|
|
2986
|
+
// src/commands/refactor/restructure/clusterDirectories.ts
|
|
2987
|
+
import path17 from "path";
|
|
2988
|
+
function clusterDirectories(graph) {
|
|
2989
|
+
const dirImportedBy = /* @__PURE__ */ new Map();
|
|
2990
|
+
for (const edge of graph.edges) {
|
|
2991
|
+
const sourceDir = path17.dirname(edge.source);
|
|
2992
|
+
const targetDir = path17.dirname(edge.target);
|
|
2993
|
+
if (sourceDir === targetDir) continue;
|
|
2994
|
+
if (!graph.files.has(edge.target)) continue;
|
|
2995
|
+
const existing = dirImportedBy.get(targetDir) ?? /* @__PURE__ */ new Set();
|
|
2996
|
+
if (!dirImportedBy.has(targetDir)) dirImportedBy.set(targetDir, existing);
|
|
2997
|
+
existing.add(sourceDir);
|
|
2998
|
+
}
|
|
2999
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
3000
|
+
for (const [dir, importers] of dirImportedBy) {
|
|
3001
|
+
if (importers.size !== 1) continue;
|
|
3002
|
+
const parentDir = [...importers][0];
|
|
3003
|
+
if (isAncestor(dir, parentDir)) continue;
|
|
3004
|
+
if (isAncestor(parentDir, dir)) continue;
|
|
3005
|
+
const cluster = clusters.get(parentDir) ?? [];
|
|
3006
|
+
if (!clusters.has(parentDir)) clusters.set(parentDir, cluster);
|
|
3007
|
+
cluster.push(dir);
|
|
3008
|
+
}
|
|
3009
|
+
for (const [parentDir, children] of clusters) {
|
|
3010
|
+
const filtered = children.filter((child) => !clusters.has(child));
|
|
3011
|
+
if (filtered.length === 0) {
|
|
3012
|
+
clusters.delete(parentDir);
|
|
3013
|
+
} else {
|
|
3014
|
+
clusters.set(parentDir, filtered);
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
return clusters;
|
|
3018
|
+
}
|
|
3019
|
+
function isAncestor(ancestor, descendant) {
|
|
3020
|
+
const rel = path17.relative(ancestor, descendant);
|
|
3021
|
+
return !rel.startsWith("..") && rel !== "";
|
|
3022
|
+
}
|
|
3023
|
+
|
|
3024
|
+
// src/commands/refactor/restructure/clusterFiles.ts
|
|
3025
|
+
import path18 from "path";
|
|
3026
|
+
function findRootParent(file, importedBy, visited) {
|
|
3027
|
+
const importers = importedBy.get(file);
|
|
3028
|
+
if (!importers || importers.size !== 1) return file;
|
|
3029
|
+
const parent = [...importers][0];
|
|
3030
|
+
const parentDir = path18.dirname(parent);
|
|
3031
|
+
const fileDir = path18.dirname(file);
|
|
3032
|
+
if (parentDir !== fileDir) return file;
|
|
3033
|
+
if (path18.basename(parent, path18.extname(parent)) === "index") return file;
|
|
3034
|
+
if (visited.has(parent)) return file;
|
|
3035
|
+
visited.add(parent);
|
|
3036
|
+
return findRootParent(parent, importedBy, visited);
|
|
3037
|
+
}
|
|
3038
|
+
function clusterFiles(graph) {
|
|
3039
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
3040
|
+
for (const file of graph.files) {
|
|
3041
|
+
const basename6 = path18.basename(file, path18.extname(file));
|
|
3042
|
+
if (basename6 === "index") continue;
|
|
3043
|
+
const importers = graph.importedBy.get(file);
|
|
3044
|
+
if (!importers || importers.size !== 1) continue;
|
|
3045
|
+
const parent = [...importers][0];
|
|
3046
|
+
if (!graph.files.has(parent)) continue;
|
|
3047
|
+
const parentDir = path18.dirname(parent);
|
|
3048
|
+
const fileDir = path18.dirname(file);
|
|
3049
|
+
if (parentDir !== fileDir) continue;
|
|
3050
|
+
const parentBasename = path18.basename(parent, path18.extname(parent));
|
|
3051
|
+
if (parentBasename === "index") continue;
|
|
3052
|
+
const root = findRootParent(parent, graph.importedBy, /* @__PURE__ */ new Set([file]));
|
|
3053
|
+
if (!root || root === file) continue;
|
|
3054
|
+
const cluster = clusters.get(root) ?? [];
|
|
3055
|
+
if (!clusters.has(root)) clusters.set(root, cluster);
|
|
3056
|
+
cluster.push(file);
|
|
3057
|
+
}
|
|
3058
|
+
return clusters;
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
// src/commands/refactor/restructure/computeRewrites.ts
|
|
3062
|
+
import fs17 from "fs";
|
|
3063
|
+
import path19 from "path";
|
|
3064
|
+
function computeRewrites(moves, edges, allProjectFiles) {
|
|
3065
|
+
const moveMap = /* @__PURE__ */ new Map();
|
|
3066
|
+
for (const move of moves) {
|
|
3067
|
+
moveMap.set(move.from, move.to);
|
|
3068
|
+
}
|
|
3069
|
+
const rewrites = [];
|
|
3070
|
+
for (const file of allProjectFiles) {
|
|
3071
|
+
const newFile = moveMap.get(file) ?? file;
|
|
3072
|
+
const edgesFromFile = edges.filter((e) => e.source === file);
|
|
3073
|
+
for (const edge of edgesFromFile) {
|
|
3074
|
+
const newTarget = moveMap.get(edge.target);
|
|
3075
|
+
if (!newTarget && !moveMap.has(file)) continue;
|
|
3076
|
+
const targetPath = newTarget ?? edge.target;
|
|
3077
|
+
const newSpecifier = computeSpecifier(newFile, targetPath);
|
|
3078
|
+
if (newSpecifier === edge.specifier) continue;
|
|
3079
|
+
rewrites.push({
|
|
3080
|
+
file,
|
|
3081
|
+
oldSpecifier: edge.specifier,
|
|
3082
|
+
newSpecifier
|
|
3083
|
+
});
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
return rewrites;
|
|
3087
|
+
}
|
|
3088
|
+
function computeSpecifier(fromFile, toFile) {
|
|
3089
|
+
const fromDir = path19.dirname(fromFile);
|
|
3090
|
+
let rel = path19.relative(fromDir, toFile);
|
|
3091
|
+
rel = rel.replace(/\\/g, "/");
|
|
3092
|
+
rel = rel.replace(/\.tsx?$/, "");
|
|
3093
|
+
if (rel.endsWith("/index")) {
|
|
3094
|
+
rel = rel.slice(0, -"/index".length);
|
|
3095
|
+
}
|
|
3096
|
+
if (!rel.startsWith(".")) {
|
|
3097
|
+
rel = `./${rel}`;
|
|
3098
|
+
}
|
|
3099
|
+
return rel;
|
|
3100
|
+
}
|
|
3101
|
+
function applyRewrites(rewrites) {
|
|
3102
|
+
const fileRewrites = /* @__PURE__ */ new Map();
|
|
3103
|
+
for (const rewrite of rewrites) {
|
|
3104
|
+
const existing = fileRewrites.get(rewrite.file) ?? [];
|
|
3105
|
+
if (!fileRewrites.has(rewrite.file))
|
|
3106
|
+
fileRewrites.set(rewrite.file, existing);
|
|
3107
|
+
existing.push(rewrite);
|
|
3108
|
+
}
|
|
3109
|
+
const updatedContents = /* @__PURE__ */ new Map();
|
|
3110
|
+
for (const [file, fileSpecificRewrites] of fileRewrites) {
|
|
3111
|
+
let content = fs17.readFileSync(file, "utf-8");
|
|
3112
|
+
for (const { oldSpecifier, newSpecifier } of fileSpecificRewrites) {
|
|
3113
|
+
const escaped = oldSpecifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3114
|
+
const pattern2 = new RegExp(`(from\\s+["'])${escaped}(["'])`, "g");
|
|
3115
|
+
content = content.replace(pattern2, `$1${newSpecifier}$2`);
|
|
3116
|
+
}
|
|
3117
|
+
updatedContents.set(file, content);
|
|
3118
|
+
}
|
|
3119
|
+
return updatedContents;
|
|
3120
|
+
}
|
|
3121
|
+
|
|
3122
|
+
// src/commands/refactor/restructure/displayPlan.ts
|
|
3123
|
+
import path20 from "path";
|
|
3124
|
+
import chalk36 from "chalk";
|
|
3125
|
+
function displayPlan(plan) {
|
|
3126
|
+
if (plan.warnings.length > 0) {
|
|
3127
|
+
console.log(chalk36.yellow("\nWarnings:"));
|
|
3128
|
+
for (const warning of plan.warnings) {
|
|
3129
|
+
console.log(chalk36.yellow(` ${warning}`));
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
if (plan.newDirectories.length > 0) {
|
|
3133
|
+
console.log(chalk36.bold("\nNew directories:"));
|
|
3134
|
+
for (const dir of plan.newDirectories) {
|
|
3135
|
+
console.log(chalk36.green(` ${dir}/`));
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
if (plan.moves.length > 0) {
|
|
3139
|
+
console.log(chalk36.bold("\nFile moves:"));
|
|
3140
|
+
for (const move of plan.moves) {
|
|
3141
|
+
const fromRel = path20.relative(process.cwd(), move.from);
|
|
3142
|
+
const toRel = path20.relative(process.cwd(), move.to);
|
|
3143
|
+
console.log(` ${chalk36.red(fromRel)} \u2192 ${chalk36.green(toRel)}`);
|
|
3144
|
+
console.log(chalk36.dim(` ${move.reason}`));
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
if (plan.rewrites.length > 0) {
|
|
3148
|
+
const affectedFiles = new Set(plan.rewrites.map((r) => r.file));
|
|
3149
|
+
console.log(chalk36.bold(`
|
|
3150
|
+
Import rewrites (${affectedFiles.size} files):`));
|
|
3151
|
+
for (const file of affectedFiles) {
|
|
3152
|
+
const fileRewrites = plan.rewrites.filter((r) => r.file === file);
|
|
3153
|
+
const rel = path20.relative(process.cwd(), file);
|
|
3154
|
+
console.log(` ${chalk36.cyan(rel)}:`);
|
|
3155
|
+
for (const { oldSpecifier, newSpecifier } of fileRewrites) {
|
|
3156
|
+
console.log(
|
|
3157
|
+
` ${chalk36.red(`"${oldSpecifier}"`)} \u2192 ${chalk36.green(`"${newSpecifier}"`)}`
|
|
3158
|
+
);
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
console.log(
|
|
3163
|
+
chalk36.dim(
|
|
3164
|
+
`
|
|
3165
|
+
Summary: ${plan.moves.length} file(s) moved, ${plan.rewrites.length} imports rewritten`
|
|
3166
|
+
)
|
|
3167
|
+
);
|
|
3168
|
+
}
|
|
3169
|
+
|
|
3170
|
+
// src/commands/refactor/restructure/executePlan.ts
|
|
3171
|
+
import fs18 from "fs";
|
|
3172
|
+
import path21 from "path";
|
|
3173
|
+
import chalk37 from "chalk";
|
|
3174
|
+
function executePlan(plan) {
|
|
3175
|
+
const updatedContents = applyRewrites(plan.rewrites);
|
|
3176
|
+
for (const [file, content] of updatedContents) {
|
|
3177
|
+
fs18.writeFileSync(file, content, "utf-8");
|
|
3178
|
+
console.log(
|
|
3179
|
+
chalk37.cyan(` Rewrote imports in ${path21.relative(process.cwd(), file)}`)
|
|
3180
|
+
);
|
|
3181
|
+
}
|
|
3182
|
+
for (const dir of plan.newDirectories) {
|
|
3183
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
3184
|
+
console.log(chalk37.green(` Created ${path21.relative(process.cwd(), dir)}/`));
|
|
3185
|
+
}
|
|
3186
|
+
for (const move of plan.moves) {
|
|
3187
|
+
const targetDir = path21.dirname(move.to);
|
|
3188
|
+
if (!fs18.existsSync(targetDir)) {
|
|
3189
|
+
fs18.mkdirSync(targetDir, { recursive: true });
|
|
3190
|
+
}
|
|
3191
|
+
fs18.renameSync(move.from, move.to);
|
|
3192
|
+
console.log(
|
|
3193
|
+
chalk37.white(
|
|
3194
|
+
` Moved ${path21.relative(process.cwd(), move.from)} \u2192 ${path21.relative(process.cwd(), move.to)}`
|
|
3195
|
+
)
|
|
3196
|
+
);
|
|
3197
|
+
}
|
|
3198
|
+
removeEmptyDirectories(plan.moves.map((m) => path21.dirname(m.from)));
|
|
3199
|
+
}
|
|
3200
|
+
function removeEmptyDirectories(dirs) {
|
|
3201
|
+
const unique = [...new Set(dirs)];
|
|
3202
|
+
for (const dir of unique) {
|
|
3203
|
+
if (!fs18.existsSync(dir)) continue;
|
|
3204
|
+
const entries = fs18.readdirSync(dir);
|
|
3205
|
+
if (entries.length === 0) {
|
|
3206
|
+
fs18.rmdirSync(dir);
|
|
3207
|
+
console.log(
|
|
3208
|
+
chalk37.dim(
|
|
3209
|
+
` Removed empty directory ${path21.relative(process.cwd(), dir)}`
|
|
3210
|
+
)
|
|
3211
|
+
);
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
|
|
3216
|
+
// src/commands/refactor/restructure/planFileMoves.ts
|
|
3217
|
+
import fs19 from "fs";
|
|
3218
|
+
import path22 from "path";
|
|
3219
|
+
function emptyResult() {
|
|
3220
|
+
return { moves: [], directories: [], warnings: [] };
|
|
3221
|
+
}
|
|
3222
|
+
function planFileMoves(clusters) {
|
|
3223
|
+
const result = emptyResult();
|
|
3224
|
+
for (const [parent, children] of clusters) {
|
|
3225
|
+
const parentBase = path22.basename(parent, path22.extname(parent));
|
|
3226
|
+
const newDirName = parentBase;
|
|
3227
|
+
const newDir = path22.join(path22.dirname(parent), newDirName);
|
|
3228
|
+
if (fs19.existsSync(newDir)) {
|
|
3229
|
+
result.warnings.push(
|
|
3230
|
+
`Skipping ${parent}: directory ${newDir} already exists`
|
|
3231
|
+
);
|
|
3232
|
+
continue;
|
|
3233
|
+
}
|
|
3234
|
+
result.directories.push(newDir);
|
|
3235
|
+
result.moves.push({
|
|
3236
|
+
from: parent,
|
|
3237
|
+
to: path22.join(newDir, `index${path22.extname(parent)}`),
|
|
3238
|
+
reason: `Main module of new ${newDirName}/ directory`
|
|
3239
|
+
});
|
|
3240
|
+
for (const child of children) {
|
|
3241
|
+
result.moves.push({
|
|
3242
|
+
from: child,
|
|
3243
|
+
to: path22.join(newDir, path22.basename(child)),
|
|
3244
|
+
reason: `Only imported by ${parentBase}`
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
return result;
|
|
3249
|
+
}
|
|
3250
|
+
function planDirectoryMoves(clusters) {
|
|
3251
|
+
const result = emptyResult();
|
|
3252
|
+
for (const [parentDir, childDirs] of clusters) {
|
|
3253
|
+
for (const childDir of childDirs) {
|
|
3254
|
+
const childName = path22.basename(childDir);
|
|
3255
|
+
const newLocation = path22.join(parentDir, childName);
|
|
3256
|
+
if (fs19.existsSync(newLocation)) {
|
|
3257
|
+
result.warnings.push(
|
|
3258
|
+
`Skipping ${childDir}: ${newLocation} already exists`
|
|
3259
|
+
);
|
|
3260
|
+
continue;
|
|
3261
|
+
}
|
|
3262
|
+
result.directories.push(newLocation);
|
|
3263
|
+
const files = listFilesRecursive(childDir);
|
|
3264
|
+
for (const file of files) {
|
|
3265
|
+
const rel = path22.relative(childDir, file);
|
|
3266
|
+
result.moves.push({
|
|
3267
|
+
from: file,
|
|
3268
|
+
to: path22.join(newLocation, rel),
|
|
3269
|
+
reason: `Directory only imported from ${path22.basename(parentDir)}/`
|
|
3270
|
+
});
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
return result;
|
|
3275
|
+
}
|
|
3276
|
+
function listFilesRecursive(dir) {
|
|
3277
|
+
const results = [];
|
|
3278
|
+
if (!fs19.existsSync(dir)) return results;
|
|
3279
|
+
const entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
3280
|
+
for (const entry of entries) {
|
|
3281
|
+
const full = path22.join(dir, entry.name);
|
|
3282
|
+
if (entry.isDirectory()) {
|
|
3283
|
+
results.push(...listFilesRecursive(full));
|
|
3284
|
+
} else {
|
|
3285
|
+
results.push(full);
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
return results;
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
// src/commands/refactor/restructure/index.ts
|
|
3292
|
+
function buildPlan(candidateFiles, tsConfigPath) {
|
|
3293
|
+
const candidates = new Set(candidateFiles.map((f) => path23.resolve(f)));
|
|
3294
|
+
const graph = buildImportGraph(candidates, tsConfigPath);
|
|
3295
|
+
const allProjectFiles = /* @__PURE__ */ new Set([
|
|
3296
|
+
...graph.importedBy.keys(),
|
|
3297
|
+
...graph.imports.keys()
|
|
3298
|
+
]);
|
|
3299
|
+
const fileClusters = clusterFiles(graph);
|
|
3300
|
+
const dirClusters = clusterDirectories(graph);
|
|
3301
|
+
const fileResult = planFileMoves(fileClusters);
|
|
3302
|
+
const dirResult = planDirectoryMoves(dirClusters);
|
|
3303
|
+
const moves = [...fileResult.moves, ...dirResult.moves];
|
|
3304
|
+
const directories = [...fileResult.directories, ...dirResult.directories];
|
|
3305
|
+
const warnings = [...fileResult.warnings, ...dirResult.warnings];
|
|
3306
|
+
const rewrites = computeRewrites(moves, graph.edges, allProjectFiles);
|
|
3307
|
+
return { moves, rewrites, newDirectories: directories, warnings };
|
|
3308
|
+
}
|
|
3309
|
+
async function restructure(pattern2, options = {}) {
|
|
3310
|
+
const targetPattern = pattern2 ?? "src";
|
|
3311
|
+
const files = findSourceFiles(targetPattern);
|
|
3312
|
+
if (files.length === 0) {
|
|
3313
|
+
console.log(chalk38.yellow("No files found matching pattern"));
|
|
3314
|
+
return;
|
|
3315
|
+
}
|
|
3316
|
+
const tsConfigPath = path23.resolve("tsconfig.json");
|
|
3317
|
+
const plan = buildPlan(files, tsConfigPath);
|
|
3318
|
+
if (plan.moves.length === 0) {
|
|
3319
|
+
console.log(chalk38.green("No restructuring needed"));
|
|
3320
|
+
return;
|
|
3321
|
+
}
|
|
3322
|
+
displayPlan(plan);
|
|
3323
|
+
if (options.apply) {
|
|
3324
|
+
console.log(chalk38.bold("\nApplying changes..."));
|
|
3325
|
+
executePlan(plan);
|
|
3326
|
+
console.log(chalk38.green("\nRestructuring complete"));
|
|
3327
|
+
} else {
|
|
3328
|
+
console.log(chalk38.dim("\nDry run. Use --apply to execute."));
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
|
|
2890
3332
|
// src/commands/run.ts
|
|
2891
3333
|
import { spawn as spawn3 } from "child_process";
|
|
2892
3334
|
function run(name, args) {
|
|
@@ -2972,29 +3414,29 @@ async function statusLine() {
|
|
|
2972
3414
|
}
|
|
2973
3415
|
|
|
2974
3416
|
// src/commands/sync.ts
|
|
2975
|
-
import * as
|
|
3417
|
+
import * as fs22 from "fs";
|
|
2976
3418
|
import * as os from "os";
|
|
2977
|
-
import * as
|
|
3419
|
+
import * as path26 from "path";
|
|
2978
3420
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2979
3421
|
|
|
2980
3422
|
// src/commands/sync/syncClaudeMd.ts
|
|
2981
|
-
import * as
|
|
2982
|
-
import * as
|
|
2983
|
-
import
|
|
3423
|
+
import * as fs20 from "fs";
|
|
3424
|
+
import * as path24 from "path";
|
|
3425
|
+
import chalk39 from "chalk";
|
|
2984
3426
|
async function syncClaudeMd(claudeDir, targetBase) {
|
|
2985
|
-
const source =
|
|
2986
|
-
const target =
|
|
2987
|
-
const sourceContent =
|
|
2988
|
-
if (
|
|
2989
|
-
const targetContent =
|
|
3427
|
+
const source = path24.join(claudeDir, "CLAUDE.md");
|
|
3428
|
+
const target = path24.join(targetBase, "CLAUDE.md");
|
|
3429
|
+
const sourceContent = fs20.readFileSync(source, "utf-8");
|
|
3430
|
+
if (fs20.existsSync(target)) {
|
|
3431
|
+
const targetContent = fs20.readFileSync(target, "utf-8");
|
|
2990
3432
|
if (sourceContent !== targetContent) {
|
|
2991
3433
|
console.log(
|
|
2992
|
-
|
|
3434
|
+
chalk39.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
2993
3435
|
);
|
|
2994
3436
|
console.log();
|
|
2995
3437
|
printDiff(targetContent, sourceContent);
|
|
2996
3438
|
const confirm = await promptConfirm(
|
|
2997
|
-
|
|
3439
|
+
chalk39.red("Overwrite existing CLAUDE.md?"),
|
|
2998
3440
|
false
|
|
2999
3441
|
);
|
|
3000
3442
|
if (!confirm) {
|
|
@@ -3003,30 +3445,30 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
3003
3445
|
}
|
|
3004
3446
|
}
|
|
3005
3447
|
}
|
|
3006
|
-
|
|
3448
|
+
fs20.copyFileSync(source, target);
|
|
3007
3449
|
console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
|
|
3008
3450
|
}
|
|
3009
3451
|
|
|
3010
3452
|
// src/commands/sync/syncSettings.ts
|
|
3011
|
-
import * as
|
|
3012
|
-
import * as
|
|
3013
|
-
import
|
|
3453
|
+
import * as fs21 from "fs";
|
|
3454
|
+
import * as path25 from "path";
|
|
3455
|
+
import chalk40 from "chalk";
|
|
3014
3456
|
async function syncSettings(claudeDir, targetBase) {
|
|
3015
|
-
const source =
|
|
3016
|
-
const target =
|
|
3017
|
-
const sourceContent =
|
|
3457
|
+
const source = path25.join(claudeDir, "settings.json");
|
|
3458
|
+
const target = path25.join(targetBase, "settings.json");
|
|
3459
|
+
const sourceContent = fs21.readFileSync(source, "utf-8");
|
|
3018
3460
|
const normalizedSource = JSON.stringify(JSON.parse(sourceContent), null, 2);
|
|
3019
|
-
if (
|
|
3020
|
-
const targetContent =
|
|
3461
|
+
if (fs21.existsSync(target)) {
|
|
3462
|
+
const targetContent = fs21.readFileSync(target, "utf-8");
|
|
3021
3463
|
const normalizedTarget = JSON.stringify(JSON.parse(targetContent), null, 2);
|
|
3022
3464
|
if (normalizedSource !== normalizedTarget) {
|
|
3023
3465
|
console.log(
|
|
3024
|
-
|
|
3466
|
+
chalk40.yellow("\n\u26A0\uFE0F Warning: settings.json differs from existing file")
|
|
3025
3467
|
);
|
|
3026
3468
|
console.log();
|
|
3027
3469
|
printDiff(targetContent, sourceContent);
|
|
3028
3470
|
const confirm = await promptConfirm(
|
|
3029
|
-
|
|
3471
|
+
chalk40.red("Overwrite existing settings.json?"),
|
|
3030
3472
|
false
|
|
3031
3473
|
);
|
|
3032
3474
|
if (!confirm) {
|
|
@@ -3035,27 +3477,27 @@ async function syncSettings(claudeDir, targetBase) {
|
|
|
3035
3477
|
}
|
|
3036
3478
|
}
|
|
3037
3479
|
}
|
|
3038
|
-
|
|
3480
|
+
fs21.copyFileSync(source, target);
|
|
3039
3481
|
console.log("Copied settings.json to ~/.claude/settings.json");
|
|
3040
3482
|
}
|
|
3041
3483
|
|
|
3042
3484
|
// src/commands/sync.ts
|
|
3043
3485
|
var __filename2 = fileURLToPath3(import.meta.url);
|
|
3044
|
-
var __dirname4 =
|
|
3486
|
+
var __dirname4 = path26.dirname(__filename2);
|
|
3045
3487
|
async function sync() {
|
|
3046
|
-
const claudeDir =
|
|
3047
|
-
const targetBase =
|
|
3488
|
+
const claudeDir = path26.join(__dirname4, "..", "claude");
|
|
3489
|
+
const targetBase = path26.join(os.homedir(), ".claude");
|
|
3048
3490
|
syncCommands(claudeDir, targetBase);
|
|
3049
3491
|
await syncSettings(claudeDir, targetBase);
|
|
3050
3492
|
await syncClaudeMd(claudeDir, targetBase);
|
|
3051
3493
|
}
|
|
3052
3494
|
function syncCommands(claudeDir, targetBase) {
|
|
3053
|
-
const sourceDir =
|
|
3054
|
-
const targetDir =
|
|
3055
|
-
|
|
3056
|
-
const files =
|
|
3495
|
+
const sourceDir = path26.join(claudeDir, "commands");
|
|
3496
|
+
const targetDir = path26.join(targetBase, "commands");
|
|
3497
|
+
fs22.mkdirSync(targetDir, { recursive: true });
|
|
3498
|
+
const files = fs22.readdirSync(sourceDir);
|
|
3057
3499
|
for (const file of files) {
|
|
3058
|
-
|
|
3500
|
+
fs22.copyFileSync(path26.join(sourceDir, file), path26.join(targetDir, file));
|
|
3059
3501
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
3060
3502
|
}
|
|
3061
3503
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
@@ -3177,6 +3619,27 @@ async function configure() {
|
|
|
3177
3619
|
import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
|
|
3178
3620
|
import { basename as basename4, dirname as dirname11, join as join17 } from "path";
|
|
3179
3621
|
|
|
3622
|
+
// src/commands/transcript/cleanText.ts
|
|
3623
|
+
function cleanText(text) {
|
|
3624
|
+
const words = text.split(/\s+/);
|
|
3625
|
+
const cleaned = [];
|
|
3626
|
+
for (let i = 0; i < words.length; i++) {
|
|
3627
|
+
let isRepeat = false;
|
|
3628
|
+
for (let len = 3; len <= 8 && i + len <= words.length; len++) {
|
|
3629
|
+
const phrase = words.slice(i, i + len).join(" ").toLowerCase();
|
|
3630
|
+
const remaining = words.slice(i + len).join(" ").toLowerCase();
|
|
3631
|
+
if (remaining.startsWith(phrase)) {
|
|
3632
|
+
isRepeat = true;
|
|
3633
|
+
break;
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
if (!isRepeat) {
|
|
3637
|
+
cleaned.push(words[i]);
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
return cleaned.join(" ").replace(/\s+/g, " ").trim();
|
|
3641
|
+
}
|
|
3642
|
+
|
|
3180
3643
|
// src/commands/transcript/deduplicateCues.ts
|
|
3181
3644
|
function removeSubstringDuplicates(cues) {
|
|
3182
3645
|
const toRemove = /* @__PURE__ */ new Set();
|
|
@@ -3198,6 +3661,16 @@ function removeSubstringDuplicates(cues) {
|
|
|
3198
3661
|
}
|
|
3199
3662
|
return cues.filter((_, i) => !toRemove.has(i));
|
|
3200
3663
|
}
|
|
3664
|
+
function findWordOverlap(currentWords, nextWords) {
|
|
3665
|
+
for (let j = Math.min(5, currentWords.length); j >= 1; j--) {
|
|
3666
|
+
const suffix = currentWords.slice(-j).join(" ");
|
|
3667
|
+
const prefix = nextWords.slice(0, j).join(" ");
|
|
3668
|
+
if (suffix === prefix) {
|
|
3669
|
+
return j;
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
return 0;
|
|
3673
|
+
}
|
|
3201
3674
|
function mergeOverlappingCues(cues) {
|
|
3202
3675
|
if (cues.length === 0) return [];
|
|
3203
3676
|
const result = [];
|
|
@@ -3209,15 +3682,7 @@ function mergeOverlappingCues(cues) {
|
|
|
3209
3682
|
if (sameSpeaker && overlaps) {
|
|
3210
3683
|
const currentWords = current.text.toLowerCase().split(/\s+/);
|
|
3211
3684
|
const nextWords = next2.text.toLowerCase().split(/\s+/);
|
|
3212
|
-
|
|
3213
|
-
for (let j = Math.min(5, currentWords.length); j >= 1; j--) {
|
|
3214
|
-
const suffix = currentWords.slice(-j).join(" ");
|
|
3215
|
-
const prefix = nextWords.slice(0, j).join(" ");
|
|
3216
|
-
if (suffix === prefix) {
|
|
3217
|
-
overlapIndex = j;
|
|
3218
|
-
break;
|
|
3219
|
-
}
|
|
3220
|
-
}
|
|
3685
|
+
const overlapIndex = findWordOverlap(currentWords, nextWords);
|
|
3221
3686
|
if (overlapIndex > 0) {
|
|
3222
3687
|
const nextOriginalWords = next2.text.split(/\s+/);
|
|
3223
3688
|
current.text = `${current.text} ${nextOriginalWords.slice(overlapIndex).join(" ")}`;
|
|
@@ -3233,25 +3698,6 @@ function mergeOverlappingCues(cues) {
|
|
|
3233
3698
|
result.push(current);
|
|
3234
3699
|
return result;
|
|
3235
3700
|
}
|
|
3236
|
-
function cleanText(text) {
|
|
3237
|
-
const words = text.split(/\s+/);
|
|
3238
|
-
const cleaned = [];
|
|
3239
|
-
for (let i = 0; i < words.length; i++) {
|
|
3240
|
-
let isRepeat = false;
|
|
3241
|
-
for (let len = 3; len <= 8 && i + len <= words.length; len++) {
|
|
3242
|
-
const phrase = words.slice(i, i + len).join(" ").toLowerCase();
|
|
3243
|
-
const remaining = words.slice(i + len).join(" ").toLowerCase();
|
|
3244
|
-
if (remaining.startsWith(phrase)) {
|
|
3245
|
-
isRepeat = true;
|
|
3246
|
-
break;
|
|
3247
|
-
}
|
|
3248
|
-
}
|
|
3249
|
-
if (!isRepeat) {
|
|
3250
|
-
cleaned.push(words[i]);
|
|
3251
|
-
}
|
|
3252
|
-
}
|
|
3253
|
-
return cleaned.join(" ").replace(/\s+/g, " ").trim();
|
|
3254
|
-
}
|
|
3255
3701
|
function deduplicateCues(cues) {
|
|
3256
3702
|
if (cues.length === 0) return [];
|
|
3257
3703
|
const sorted = [...cues].sort((a, b) => a.startMs - b.startMs);
|
|
@@ -3264,8 +3710,8 @@ function deduplicateCues(cues) {
|
|
|
3264
3710
|
}
|
|
3265
3711
|
|
|
3266
3712
|
// src/commands/transcript/parseVtt.ts
|
|
3267
|
-
function parseTimestamp(
|
|
3268
|
-
const parts =
|
|
3713
|
+
function parseTimestamp(ts6) {
|
|
3714
|
+
const parts = ts6.split(":");
|
|
3269
3715
|
let hours = 0;
|
|
3270
3716
|
let minutes = 0;
|
|
3271
3717
|
let seconds = 0;
|
|
@@ -3411,23 +3857,7 @@ function processFile(inputPath, outputPath) {
|
|
|
3411
3857
|
`
|
|
3412
3858
|
);
|
|
3413
3859
|
}
|
|
3414
|
-
async function
|
|
3415
|
-
const { vttDir, transcriptsDir } = getTranscriptConfig();
|
|
3416
|
-
if (!existsSync16(vttDir)) {
|
|
3417
|
-
console.error(`VTT directory not found: ${vttDir}`);
|
|
3418
|
-
process.exit(1);
|
|
3419
|
-
}
|
|
3420
|
-
if (!existsSync16(transcriptsDir)) {
|
|
3421
|
-
mkdirSync5(transcriptsDir, { recursive: true });
|
|
3422
|
-
console.log(`Created output directory: ${transcriptsDir}`);
|
|
3423
|
-
}
|
|
3424
|
-
let vttFiles = findVttFilesRecursive(vttDir);
|
|
3425
|
-
if (vttFiles.length === 0) {
|
|
3426
|
-
console.log("No VTT files found in vtt directory.");
|
|
3427
|
-
return;
|
|
3428
|
-
}
|
|
3429
|
-
console.log(`Found ${vttFiles.length} VTT file(s) in ${vttDir}
|
|
3430
|
-
`);
|
|
3860
|
+
async function fixInvalidDatePrefixes(vttFiles) {
|
|
3431
3861
|
for (let i = 0; i < vttFiles.length; i++) {
|
|
3432
3862
|
const vttFile = vttFiles[i];
|
|
3433
3863
|
if (!isValidDatePrefix(vttFile.filename)) {
|
|
@@ -3448,7 +3878,26 @@ async function format() {
|
|
|
3448
3878
|
}
|
|
3449
3879
|
}
|
|
3450
3880
|
}
|
|
3451
|
-
|
|
3881
|
+
return vttFiles.filter((f) => f.absolutePath !== "");
|
|
3882
|
+
}
|
|
3883
|
+
async function format() {
|
|
3884
|
+
const { vttDir, transcriptsDir } = getTranscriptConfig();
|
|
3885
|
+
if (!existsSync16(vttDir)) {
|
|
3886
|
+
console.error(`VTT directory not found: ${vttDir}`);
|
|
3887
|
+
process.exit(1);
|
|
3888
|
+
}
|
|
3889
|
+
if (!existsSync16(transcriptsDir)) {
|
|
3890
|
+
mkdirSync5(transcriptsDir, { recursive: true });
|
|
3891
|
+
console.log(`Created output directory: ${transcriptsDir}`);
|
|
3892
|
+
}
|
|
3893
|
+
let vttFiles = findVttFilesRecursive(vttDir);
|
|
3894
|
+
if (vttFiles.length === 0) {
|
|
3895
|
+
console.log("No VTT files found in vtt directory.");
|
|
3896
|
+
return;
|
|
3897
|
+
}
|
|
3898
|
+
console.log(`Found ${vttFiles.length} VTT file(s) in ${vttDir}
|
|
3899
|
+
`);
|
|
3900
|
+
vttFiles = await fixInvalidDatePrefixes(vttFiles);
|
|
3452
3901
|
let processed = 0;
|
|
3453
3902
|
let skipped = 0;
|
|
3454
3903
|
for (const vttFile of vttFiles) {
|
|
@@ -3475,6 +3924,10 @@ Summary: ${processed} processed, ${skipped} skipped`);
|
|
|
3475
3924
|
}
|
|
3476
3925
|
|
|
3477
3926
|
// src/commands/transcript/summarise.ts
|
|
3927
|
+
import { existsSync as existsSync18 } from "fs";
|
|
3928
|
+
import { basename as basename5, dirname as dirname13, join as join19, relative as relative2 } from "path";
|
|
3929
|
+
|
|
3930
|
+
// src/commands/transcript/processStagedFile.ts
|
|
3478
3931
|
import {
|
|
3479
3932
|
existsSync as existsSync17,
|
|
3480
3933
|
mkdirSync as mkdirSync6,
|
|
@@ -3482,8 +3935,8 @@ import {
|
|
|
3482
3935
|
renameSync as renameSync2,
|
|
3483
3936
|
rmSync
|
|
3484
3937
|
} from "fs";
|
|
3485
|
-
import {
|
|
3486
|
-
import
|
|
3938
|
+
import { dirname as dirname12, join as join18 } from "path";
|
|
3939
|
+
import chalk41 from "chalk";
|
|
3487
3940
|
var STAGING_DIR = join18(process.cwd(), ".assist", "transcript");
|
|
3488
3941
|
var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
|
|
3489
3942
|
function processStagedFile() {
|
|
@@ -3501,7 +3954,7 @@ function processStagedFile() {
|
|
|
3501
3954
|
const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
|
|
3502
3955
|
if (!match) {
|
|
3503
3956
|
console.error(
|
|
3504
|
-
|
|
3957
|
+
chalk41.red(
|
|
3505
3958
|
`Staged file ${stagedFile.filename} missing [Full Transcript](<path>) link on first line.`
|
|
3506
3959
|
)
|
|
3507
3960
|
);
|
|
@@ -3510,7 +3963,7 @@ function processStagedFile() {
|
|
|
3510
3963
|
const contentAfterLink = content.slice(firstLine.length).trim();
|
|
3511
3964
|
if (!contentAfterLink) {
|
|
3512
3965
|
console.error(
|
|
3513
|
-
|
|
3966
|
+
chalk41.red(
|
|
3514
3967
|
`Staged file ${stagedFile.filename} has no summary content after the transcript link.`
|
|
3515
3968
|
)
|
|
3516
3969
|
);
|
|
@@ -3523,7 +3976,7 @@ function processStagedFile() {
|
|
|
3523
3976
|
);
|
|
3524
3977
|
if (!matchingTranscript) {
|
|
3525
3978
|
console.error(
|
|
3526
|
-
|
|
3979
|
+
chalk41.red(
|
|
3527
3980
|
`No transcript found matching staged file: ${stagedFile.filename}`
|
|
3528
3981
|
)
|
|
3529
3982
|
);
|
|
@@ -3542,10 +3995,12 @@ function processStagedFile() {
|
|
|
3542
3995
|
}
|
|
3543
3996
|
return true;
|
|
3544
3997
|
}
|
|
3998
|
+
|
|
3999
|
+
// src/commands/transcript/summarise.ts
|
|
3545
4000
|
function summarise() {
|
|
3546
4001
|
processStagedFile();
|
|
3547
4002
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
3548
|
-
if (!
|
|
4003
|
+
if (!existsSync18(transcriptsDir)) {
|
|
3549
4004
|
console.log("No transcripts directory found.");
|
|
3550
4005
|
return;
|
|
3551
4006
|
}
|
|
@@ -3557,16 +4012,16 @@ function summarise() {
|
|
|
3557
4012
|
const summaryFiles = findMdFilesRecursive(summaryDir);
|
|
3558
4013
|
const summaryRelativePaths = new Set(
|
|
3559
4014
|
summaryFiles.map((f) => {
|
|
3560
|
-
const relDir =
|
|
4015
|
+
const relDir = dirname13(f.relativePath);
|
|
3561
4016
|
const baseName = basename5(f.filename, ".md");
|
|
3562
|
-
return relDir === "." ? baseName :
|
|
4017
|
+
return relDir === "." ? baseName : join19(relDir, baseName);
|
|
3563
4018
|
})
|
|
3564
4019
|
);
|
|
3565
4020
|
const missing = [];
|
|
3566
4021
|
for (const transcript of transcriptFiles) {
|
|
3567
4022
|
const transcriptBaseName = getTranscriptBaseName(transcript.filename);
|
|
3568
|
-
const relDir =
|
|
3569
|
-
const fullKey = relDir === "." ? transcriptBaseName :
|
|
4023
|
+
const relDir = dirname13(transcript.relativePath);
|
|
4024
|
+
const fullKey = relDir === "." ? transcriptBaseName : join19(relDir, transcriptBaseName);
|
|
3570
4025
|
if (!summaryRelativePaths.has(fullKey)) {
|
|
3571
4026
|
missing.push(transcript);
|
|
3572
4027
|
}
|
|
@@ -3577,8 +4032,8 @@ function summarise() {
|
|
|
3577
4032
|
}
|
|
3578
4033
|
const next2 = missing[0];
|
|
3579
4034
|
const outputFilename = `${getTranscriptBaseName(next2.filename)}.md`;
|
|
3580
|
-
const outputPath =
|
|
3581
|
-
const summaryFileDir =
|
|
4035
|
+
const outputPath = join19(STAGING_DIR, outputFilename);
|
|
4036
|
+
const summaryFileDir = join19(summaryDir, dirname13(next2.relativePath));
|
|
3582
4037
|
const relativeTranscriptPath = encodeURI(
|
|
3583
4038
|
relative2(summaryFileDir, next2.absolutePath).replace(/\\/g, "/")
|
|
3584
4039
|
);
|
|
@@ -3595,11 +4050,11 @@ function summarise() {
|
|
|
3595
4050
|
}
|
|
3596
4051
|
|
|
3597
4052
|
// src/commands/verify/hardcodedColors.ts
|
|
3598
|
-
import { execSync as
|
|
4053
|
+
import { execSync as execSync18 } from "child_process";
|
|
3599
4054
|
var pattern = "0x[0-9a-fA-F]{6}|#[0-9a-fA-F]{3,6}";
|
|
3600
4055
|
function hardcodedColors() {
|
|
3601
4056
|
try {
|
|
3602
|
-
const output =
|
|
4057
|
+
const output = execSync18(`grep -rEnH '${pattern}' src/`, {
|
|
3603
4058
|
encoding: "utf-8"
|
|
3604
4059
|
});
|
|
3605
4060
|
const lines = output.trim().split("\n");
|
|
@@ -3629,7 +4084,7 @@ Total: ${lines.length} hardcoded color(s)`);
|
|
|
3629
4084
|
|
|
3630
4085
|
// src/commands/verify/run.ts
|
|
3631
4086
|
import { spawn as spawn4 } from "child_process";
|
|
3632
|
-
import * as
|
|
4087
|
+
import * as path27 from "path";
|
|
3633
4088
|
function formatDuration(ms) {
|
|
3634
4089
|
if (ms < 1e3) {
|
|
3635
4090
|
return `${ms}ms`;
|
|
@@ -3659,7 +4114,7 @@ async function run2(options = {}) {
|
|
|
3659
4114
|
return;
|
|
3660
4115
|
}
|
|
3661
4116
|
const { packageJsonPath, verifyScripts } = result;
|
|
3662
|
-
const packageDir =
|
|
4117
|
+
const packageDir = path27.dirname(packageJsonPath);
|
|
3663
4118
|
console.log(`Running ${verifyScripts.length} verify script(s) in parallel:`);
|
|
3664
4119
|
for (const script of verifyScripts) {
|
|
3665
4120
|
console.log(` - ${script}`);
|
|
@@ -3709,7 +4164,7 @@ program.command("init").description("Initialize VS Code and verify configuration
|
|
|
3709
4164
|
program.command("commit <message>").description("Create a git commit with validation").action(commit);
|
|
3710
4165
|
program.command("update").description("Update claude-code to the latest version").action(() => {
|
|
3711
4166
|
console.log("Updating claude-code...");
|
|
3712
|
-
|
|
4167
|
+
execSync19("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
|
|
3713
4168
|
});
|
|
3714
4169
|
var configCommand = program.command("config").description("View and modify assist.yml configuration");
|
|
3715
4170
|
configCommand.command("set <key> <value>").description("Set a config value (e.g. commit.push true)").action(configSet);
|
|
@@ -3742,6 +4197,13 @@ refactorCommand.command("check [pattern]").description("Check for files that exc
|
|
|
3742
4197
|
Number.parseInt
|
|
3743
4198
|
).action(check);
|
|
3744
4199
|
refactorCommand.command("ignore <file>").description("Add a file to the refactor ignore list").action(ignore);
|
|
4200
|
+
refactorCommand.command("restructure [pattern]").description(
|
|
4201
|
+
"Analyze import graph and restructure tightly-coupled files into nested directories"
|
|
4202
|
+
).option("--apply", "Execute the restructuring (default: dry-run)").option(
|
|
4203
|
+
"--max-depth <number>",
|
|
4204
|
+
"Maximum nesting iterations (default: 3)",
|
|
4205
|
+
Number.parseInt
|
|
4206
|
+
).action(restructure);
|
|
3745
4207
|
var devlogCommand = program.command("devlog").description("Development log utilities");
|
|
3746
4208
|
devlogCommand.command("list").description("Group git commits by date").option(
|
|
3747
4209
|
"--days <number>",
|