@staff0rd/assist 0.138.0 → 0.140.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/claude/CLAUDE.md +3 -1
- package/claude/commands/commit.md +1 -0
- package/claude/commands/test-cover.md +119 -0
- package/claude/commands/test-review.md +75 -0
- package/claude/settings.json +4 -0
- package/dist/index.js +859 -259
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.140.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -63,6 +63,7 @@ var package_default = {
|
|
|
63
63
|
"@types/react-dom": "^19.2.3",
|
|
64
64
|
"@types/semver": "^7.7.1",
|
|
65
65
|
"@types/shell-quote": "^1.7.5",
|
|
66
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
66
67
|
esbuild: "^0.27.3",
|
|
67
68
|
jotai: "^2.18.0",
|
|
68
69
|
jscpd: "^4.0.5",
|
|
@@ -90,10 +91,10 @@ import { stringify as stringifyYaml } from "yaml";
|
|
|
90
91
|
// src/shared/loadRawYaml.ts
|
|
91
92
|
import { existsSync, readFileSync } from "fs";
|
|
92
93
|
import { parse as parseYaml } from "yaml";
|
|
93
|
-
function loadRawYaml(
|
|
94
|
-
if (!existsSync(
|
|
94
|
+
function loadRawYaml(path49) {
|
|
95
|
+
if (!existsSync(path49)) return {};
|
|
95
96
|
try {
|
|
96
|
-
const content = readFileSync(
|
|
97
|
+
const content = readFileSync(path49, "utf-8");
|
|
97
98
|
return parseYaml(content) || {};
|
|
98
99
|
} catch {
|
|
99
100
|
return {};
|
|
@@ -370,9 +371,9 @@ function isTraversable(value) {
|
|
|
370
371
|
function stepInto(current, key) {
|
|
371
372
|
return isTraversable(current) ? current[key] : void 0;
|
|
372
373
|
}
|
|
373
|
-
function getNestedValue(obj,
|
|
374
|
+
function getNestedValue(obj, path49) {
|
|
374
375
|
let current = obj;
|
|
375
|
-
for (const key of
|
|
376
|
+
for (const key of path49.split(".")) current = stepInto(current, key);
|
|
376
377
|
return current;
|
|
377
378
|
}
|
|
378
379
|
|
|
@@ -413,8 +414,8 @@ function stepIntoNested(container, key, nextKey) {
|
|
|
413
414
|
}
|
|
414
415
|
return ensureObject(container, resolved);
|
|
415
416
|
}
|
|
416
|
-
function setNestedValue(obj,
|
|
417
|
-
const keys =
|
|
417
|
+
function setNestedValue(obj, path49, value) {
|
|
418
|
+
const keys = path49.split(".");
|
|
418
419
|
const result = { ...obj };
|
|
419
420
|
let current = result;
|
|
420
421
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -3222,12 +3223,12 @@ function getCliReadsPath() {
|
|
|
3222
3223
|
var cachedLines;
|
|
3223
3224
|
function getCliReadsLines() {
|
|
3224
3225
|
if (cachedLines) return cachedLines;
|
|
3225
|
-
const
|
|
3226
|
-
if (!existsSync18(
|
|
3226
|
+
const path49 = getCliReadsPath();
|
|
3227
|
+
if (!existsSync18(path49)) {
|
|
3227
3228
|
cachedLines = [];
|
|
3228
3229
|
return cachedLines;
|
|
3229
3230
|
}
|
|
3230
|
-
cachedLines = readFileSync13(
|
|
3231
|
+
cachedLines = readFileSync13(path49, "utf-8").split("\n").filter((line) => line.trim() !== "");
|
|
3231
3232
|
return cachedLines;
|
|
3232
3233
|
}
|
|
3233
3234
|
function loadCliReads() {
|
|
@@ -3582,14 +3583,14 @@ function showProgress(p, label2) {
|
|
|
3582
3583
|
const pct = Math.round(p.done / p.total * 100);
|
|
3583
3584
|
process.stderr.write(`\r\x1B[K[${pct}%] Scanning ${label2}...`);
|
|
3584
3585
|
}
|
|
3585
|
-
async function resolveCommand(cli,
|
|
3586
|
-
showProgress(p,
|
|
3587
|
-
const subHelp = await runHelp([cli, ...
|
|
3586
|
+
async function resolveCommand(cli, path49, description, depth, p) {
|
|
3587
|
+
showProgress(p, path49.join(" "));
|
|
3588
|
+
const subHelp = await runHelp([cli, ...path49]);
|
|
3588
3589
|
if (!subHelp || !hasSubcommands(subHelp)) {
|
|
3589
|
-
return [{ path:
|
|
3590
|
+
return [{ path: path49, description }];
|
|
3590
3591
|
}
|
|
3591
|
-
const children = await discoverAt(cli,
|
|
3592
|
-
return children.length > 0 ? children : [{ path:
|
|
3592
|
+
const children = await discoverAt(cli, path49, depth + 1, p);
|
|
3593
|
+
return children.length > 0 ? children : [{ path: path49, description }];
|
|
3593
3594
|
}
|
|
3594
3595
|
async function discoverAt(cli, parentPath, depth, p) {
|
|
3595
3596
|
if (depth > SAFETY_DEPTH) return [];
|
|
@@ -3737,9 +3738,9 @@ function logPath(cli) {
|
|
|
3737
3738
|
return join13(homedir4(), ".assist", `cli-discover-${safeName}.log`);
|
|
3738
3739
|
}
|
|
3739
3740
|
function readCache(cli) {
|
|
3740
|
-
const
|
|
3741
|
-
if (!existsSync20(
|
|
3742
|
-
return readFileSync15(
|
|
3741
|
+
const path49 = logPath(cli);
|
|
3742
|
+
if (!existsSync20(path49)) return void 0;
|
|
3743
|
+
return readFileSync15(path49, "utf-8");
|
|
3743
3744
|
}
|
|
3744
3745
|
function writeCache(cli, output) {
|
|
3745
3746
|
const dir = join13(homedir4(), ".assist");
|
|
@@ -5570,10 +5571,10 @@ function getStorePath(filename) {
|
|
|
5570
5571
|
return join19(getStoreDir(), filename);
|
|
5571
5572
|
}
|
|
5572
5573
|
function loadJson(filename) {
|
|
5573
|
-
const
|
|
5574
|
-
if (existsSync26(
|
|
5574
|
+
const path49 = getStorePath(filename);
|
|
5575
|
+
if (existsSync26(path49)) {
|
|
5575
5576
|
try {
|
|
5576
|
-
return JSON.parse(readFileSync21(
|
|
5577
|
+
return JSON.parse(readFileSync21(path49, "utf-8"));
|
|
5577
5578
|
} catch {
|
|
5578
5579
|
return {};
|
|
5579
5580
|
}
|
|
@@ -5966,7 +5967,7 @@ function validateLine(line) {
|
|
|
5966
5967
|
process.exit(1);
|
|
5967
5968
|
}
|
|
5968
5969
|
}
|
|
5969
|
-
function comment(
|
|
5970
|
+
function comment(path49, line, body) {
|
|
5970
5971
|
validateBody(body);
|
|
5971
5972
|
validateLine(line);
|
|
5972
5973
|
try {
|
|
@@ -5986,7 +5987,7 @@ function comment(path44, line, body) {
|
|
|
5986
5987
|
"-f",
|
|
5987
5988
|
`body=${body}`,
|
|
5988
5989
|
"-f",
|
|
5989
|
-
`path=${
|
|
5990
|
+
`path=${path49}`,
|
|
5990
5991
|
"-F",
|
|
5991
5992
|
`line=${line}`
|
|
5992
5993
|
],
|
|
@@ -5995,7 +5996,7 @@ function comment(path44, line, body) {
|
|
|
5995
5996
|
if (result.status !== 0) {
|
|
5996
5997
|
throw new Error(result.stderr || result.stdout);
|
|
5997
5998
|
}
|
|
5998
|
-
console.log(`Added review comment on ${
|
|
5999
|
+
console.log(`Added review comment on ${path49}:${line}`);
|
|
5999
6000
|
} finally {
|
|
6000
6001
|
unlinkSync5(queryFile);
|
|
6001
6002
|
}
|
|
@@ -6494,8 +6495,8 @@ function registerPrs(program2) {
|
|
|
6494
6495
|
prsCommand.command("wontfix <comment-id> <reason>").description("Reply with reason and resolve thread").action((commentId, reason) => {
|
|
6495
6496
|
wontfix(Number.parseInt(commentId, 10), reason);
|
|
6496
6497
|
});
|
|
6497
|
-
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((
|
|
6498
|
-
comment(
|
|
6498
|
+
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((path49, line, body) => {
|
|
6499
|
+
comment(path49, Number.parseInt(line, 10), body);
|
|
6499
6500
|
});
|
|
6500
6501
|
}
|
|
6501
6502
|
|
|
@@ -6747,10 +6748,10 @@ function resolveOpSecret(reference) {
|
|
|
6747
6748
|
}
|
|
6748
6749
|
|
|
6749
6750
|
// src/commands/ravendb/ravenFetch.ts
|
|
6750
|
-
async function ravenFetch(connection,
|
|
6751
|
+
async function ravenFetch(connection, path49) {
|
|
6751
6752
|
const apiKey = resolveOpSecret(connection.apiKeyRef);
|
|
6752
6753
|
let accessToken = await getAccessToken(apiKey);
|
|
6753
|
-
const url = `${connection.url}${
|
|
6754
|
+
const url = `${connection.url}${path49}`;
|
|
6754
6755
|
const headers = {
|
|
6755
6756
|
Authorization: `Bearer ${accessToken}`,
|
|
6756
6757
|
"Content-Type": "application/json"
|
|
@@ -6840,16 +6841,16 @@ import chalk82 from "chalk";
|
|
|
6840
6841
|
// src/commands/ravendb/buildQueryPath.ts
|
|
6841
6842
|
function buildQueryPath(opts) {
|
|
6842
6843
|
const db = encodeURIComponent(opts.db);
|
|
6843
|
-
let
|
|
6844
|
+
let path49;
|
|
6844
6845
|
if (opts.collection) {
|
|
6845
|
-
|
|
6846
|
+
path49 = `/databases/${db}/indexes/dynamic/${encodeURIComponent(opts.collection)}?start=${opts.start}&pageSize=${opts.pageSize}&sort=${encodeURIComponent(opts.sort)}`;
|
|
6846
6847
|
} else {
|
|
6847
|
-
|
|
6848
|
+
path49 = `/databases/${db}/queries?start=${opts.start}&pageSize=${opts.pageSize}`;
|
|
6848
6849
|
}
|
|
6849
6850
|
if (opts.query) {
|
|
6850
|
-
|
|
6851
|
+
path49 += `&query=${encodeURIComponent(opts.query)}`;
|
|
6851
6852
|
}
|
|
6852
|
-
return
|
|
6853
|
+
return path49;
|
|
6853
6854
|
}
|
|
6854
6855
|
|
|
6855
6856
|
// src/commands/ravendb/fetchAllPages.ts
|
|
@@ -6858,7 +6859,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
6858
6859
|
let start3 = 0;
|
|
6859
6860
|
while (true) {
|
|
6860
6861
|
const effectivePageSize = opts.limit !== void 0 ? Math.min(opts.pageSize, opts.limit - allResults.length) : opts.pageSize;
|
|
6861
|
-
const
|
|
6862
|
+
const path49 = buildQueryPath({
|
|
6862
6863
|
db: connection.database,
|
|
6863
6864
|
collection: opts.collection,
|
|
6864
6865
|
start: start3,
|
|
@@ -6866,7 +6867,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
6866
6867
|
sort: opts.sort,
|
|
6867
6868
|
query: opts.query
|
|
6868
6869
|
});
|
|
6869
|
-
const data = await ravenFetch(connection,
|
|
6870
|
+
const data = await ravenFetch(connection, path49);
|
|
6870
6871
|
const results = data.Results ?? [];
|
|
6871
6872
|
const totalResults = data.TotalResults ?? 0;
|
|
6872
6873
|
if (results.length === 0) break;
|
|
@@ -7125,98 +7126,685 @@ async function check(pattern2, options2) {
|
|
|
7125
7126
|
}
|
|
7126
7127
|
}
|
|
7127
7128
|
|
|
7128
|
-
// src/commands/refactor/
|
|
7129
|
-
import
|
|
7129
|
+
// src/commands/refactor/extract/index.ts
|
|
7130
|
+
import path32 from "path";
|
|
7131
|
+
import chalk87 from "chalk";
|
|
7132
|
+
|
|
7133
|
+
// src/commands/refactor/extract/applyExtraction.ts
|
|
7134
|
+
import { SyntaxKind as SyntaxKind3 } from "ts-morph";
|
|
7135
|
+
|
|
7136
|
+
// src/commands/refactor/extract/removeStaleImports.ts
|
|
7137
|
+
import { SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
7138
|
+
function collectReferencedNames(sourceFile) {
|
|
7139
|
+
const names = /* @__PURE__ */ new Set();
|
|
7140
|
+
for (const id of sourceFile.getDescendantsOfKind(SyntaxKind2.Identifier)) {
|
|
7141
|
+
names.add(id.getText());
|
|
7142
|
+
}
|
|
7143
|
+
return names;
|
|
7144
|
+
}
|
|
7145
|
+
function isImportEmpty(importDecl, usedNames) {
|
|
7146
|
+
if (importDecl.getNamedImports().length > 0) return false;
|
|
7147
|
+
const def = importDecl.getDefaultImport();
|
|
7148
|
+
if (def && usedNames.has(def.getText())) return false;
|
|
7149
|
+
const ns = importDecl.getNamespaceImport();
|
|
7150
|
+
if (ns && usedNames.has(ns.getText())) return false;
|
|
7151
|
+
return true;
|
|
7152
|
+
}
|
|
7153
|
+
function removeStaleImports(sourceFile) {
|
|
7154
|
+
const usedNames = collectReferencedNames(sourceFile);
|
|
7155
|
+
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
7156
|
+
for (const ni of importDecl.getNamedImports()) {
|
|
7157
|
+
const name = ni.getAliasNode()?.getText() ?? ni.getName();
|
|
7158
|
+
if (!usedNames.has(name)) ni.remove();
|
|
7159
|
+
}
|
|
7160
|
+
if (isImportEmpty(importDecl, usedNames)) {
|
|
7161
|
+
importDecl.remove();
|
|
7162
|
+
}
|
|
7163
|
+
}
|
|
7164
|
+
}
|
|
7165
|
+
|
|
7166
|
+
// src/commands/refactor/extract/updateImporters.ts
|
|
7167
|
+
function updateImporters(functionName, sourceFile, importers) {
|
|
7168
|
+
for (const { file: importerFile, relPath: relPath2 } of importers) {
|
|
7169
|
+
let alias;
|
|
7170
|
+
for (const importDecl of importerFile.getImportDeclarations()) {
|
|
7171
|
+
if (importDecl.getModuleSpecifierSourceFile() !== sourceFile) continue;
|
|
7172
|
+
for (const ni of importDecl.getNamedImports()) {
|
|
7173
|
+
if (ni.getName() === functionName) {
|
|
7174
|
+
alias = ni.getAliasNode()?.getText();
|
|
7175
|
+
ni.remove();
|
|
7176
|
+
break;
|
|
7177
|
+
}
|
|
7178
|
+
}
|
|
7179
|
+
if (importDecl.getNamedImports().length === 0 && !importDecl.getDefaultImport() && !importDecl.getNamespaceImport()) {
|
|
7180
|
+
importDecl.remove();
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
importerFile.addImportDeclaration({
|
|
7184
|
+
moduleSpecifier: relPath2,
|
|
7185
|
+
namedImports: [alias ? { name: functionName, alias } : functionName]
|
|
7186
|
+
});
|
|
7187
|
+
}
|
|
7188
|
+
}
|
|
7189
|
+
|
|
7190
|
+
// src/commands/refactor/extract/applyExtraction.ts
|
|
7191
|
+
function isNameReferencedInSource(sourceFile, name) {
|
|
7192
|
+
return sourceFile.getDescendantsOfKind(SyntaxKind3.Identifier).some((id) => id.getText() === name);
|
|
7193
|
+
}
|
|
7194
|
+
async function applyExtraction(functionName, sourceFile, destPath, plan2, project) {
|
|
7195
|
+
project.createSourceFile(destPath, plan2.destContent, { overwrite: false });
|
|
7196
|
+
for (const fn of [plan2.target, ...plan2.dependencies]) {
|
|
7197
|
+
fn.remove();
|
|
7198
|
+
}
|
|
7199
|
+
for (const stmt of plan2.statementsToRemove) {
|
|
7200
|
+
stmt.remove();
|
|
7201
|
+
}
|
|
7202
|
+
removeStaleImports(sourceFile);
|
|
7203
|
+
if (isNameReferencedInSource(sourceFile, functionName)) {
|
|
7204
|
+
sourceFile.addImportDeclaration({
|
|
7205
|
+
moduleSpecifier: plan2.destRelPath,
|
|
7206
|
+
namedImports: [functionName]
|
|
7207
|
+
});
|
|
7208
|
+
}
|
|
7209
|
+
updateImporters(functionName, sourceFile, plan2.importersToUpdate);
|
|
7210
|
+
if (plan2.barrel) {
|
|
7211
|
+
plan2.barrel.addExportDeclaration({
|
|
7212
|
+
moduleSpecifier: plan2.barrelRelPath,
|
|
7213
|
+
namedExports: [functionName]
|
|
7214
|
+
});
|
|
7215
|
+
}
|
|
7216
|
+
await project.save();
|
|
7217
|
+
}
|
|
7218
|
+
|
|
7219
|
+
// src/commands/refactor/extract/buildPlan.ts
|
|
7220
|
+
import path29 from "path";
|
|
7221
|
+
|
|
7222
|
+
// src/commands/refactor/extract/collectDependencies.ts
|
|
7223
|
+
import {
|
|
7224
|
+
SyntaxKind as SyntaxKind5
|
|
7225
|
+
} from "ts-morph";
|
|
7226
|
+
|
|
7227
|
+
// src/commands/refactor/extract/collectPrivateFunctions.ts
|
|
7228
|
+
import { SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
7229
|
+
function isPrivate(fn) {
|
|
7230
|
+
return !fn.isExported() && !fn.isDefaultExport();
|
|
7231
|
+
}
|
|
7232
|
+
function getCalledFunctionNames(fn) {
|
|
7233
|
+
const names = /* @__PURE__ */ new Set();
|
|
7234
|
+
for (const call of fn.getDescendantsOfKind(SyntaxKind4.CallExpression)) {
|
|
7235
|
+
const expr = call.getExpression();
|
|
7236
|
+
if (expr.getKind() === SyntaxKind4.Identifier) {
|
|
7237
|
+
names.add(expr.getText());
|
|
7238
|
+
}
|
|
7239
|
+
}
|
|
7240
|
+
return names;
|
|
7241
|
+
}
|
|
7242
|
+
function collectPrivateFunctions(target, fnMap) {
|
|
7243
|
+
const collected = /* @__PURE__ */ new Set();
|
|
7244
|
+
const queue = [target];
|
|
7245
|
+
let current = queue.pop();
|
|
7246
|
+
while (current) {
|
|
7247
|
+
for (const name of getCalledFunctionNames(current)) {
|
|
7248
|
+
if (collected.has(name)) continue;
|
|
7249
|
+
const fn = fnMap.get(name);
|
|
7250
|
+
if (fn && isPrivate(fn)) {
|
|
7251
|
+
collected.add(name);
|
|
7252
|
+
queue.push(fn);
|
|
7253
|
+
}
|
|
7254
|
+
}
|
|
7255
|
+
current = queue.pop();
|
|
7256
|
+
}
|
|
7257
|
+
return [...collected].flatMap((name) => {
|
|
7258
|
+
const fn = fnMap.get(name);
|
|
7259
|
+
return fn ? [fn] : [];
|
|
7260
|
+
});
|
|
7261
|
+
}
|
|
7262
|
+
|
|
7263
|
+
// src/commands/refactor/extract/getPrivateStatementMap.ts
|
|
7264
|
+
function getPrivateStatementMap(sourceFile) {
|
|
7265
|
+
const map = /* @__PURE__ */ new Map();
|
|
7266
|
+
for (const stmt of sourceFile.getVariableStatements()) {
|
|
7267
|
+
if (stmt.isExported()) continue;
|
|
7268
|
+
for (const decl of stmt.getDeclarations()) map.set(decl.getName(), stmt);
|
|
7269
|
+
}
|
|
7270
|
+
for (const alias of sourceFile.getTypeAliases()) {
|
|
7271
|
+
if (!alias.isExported()) map.set(alias.getName(), alias);
|
|
7272
|
+
}
|
|
7273
|
+
for (const iface of sourceFile.getInterfaces()) {
|
|
7274
|
+
if (!iface.isExported()) map.set(iface.getName(), iface);
|
|
7275
|
+
}
|
|
7276
|
+
return map;
|
|
7277
|
+
}
|
|
7278
|
+
|
|
7279
|
+
// src/commands/refactor/extract/collectDependencies.ts
|
|
7280
|
+
function getReferencedIdentifiers(fn) {
|
|
7281
|
+
const names = /* @__PURE__ */ new Set();
|
|
7282
|
+
for (const id of fn.getDescendantsOfKind(SyntaxKind5.Identifier)) {
|
|
7283
|
+
names.add(id.getText());
|
|
7284
|
+
}
|
|
7285
|
+
return names;
|
|
7286
|
+
}
|
|
7287
|
+
function getFunctionMap(sourceFile) {
|
|
7288
|
+
const map = /* @__PURE__ */ new Map();
|
|
7289
|
+
for (const fn of sourceFile.getDescendantsOfKind(
|
|
7290
|
+
SyntaxKind5.FunctionDeclaration
|
|
7291
|
+
)) {
|
|
7292
|
+
const name = fn.getName();
|
|
7293
|
+
if (name) map.set(name, fn);
|
|
7294
|
+
}
|
|
7295
|
+
return map;
|
|
7296
|
+
}
|
|
7297
|
+
function collectPrivateStatements(functions, stmtMap) {
|
|
7298
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7299
|
+
for (const fn of functions) {
|
|
7300
|
+
for (const name of getReferencedIdentifiers(fn)) {
|
|
7301
|
+
const stmt = stmtMap.get(name);
|
|
7302
|
+
if (stmt) seen.add(stmt);
|
|
7303
|
+
}
|
|
7304
|
+
}
|
|
7305
|
+
return [...seen];
|
|
7306
|
+
}
|
|
7307
|
+
function getRemainingFunctions(sourceFile, extracted) {
|
|
7308
|
+
return sourceFile.getDescendantsOfKind(SyntaxKind5.FunctionDeclaration).filter((fn) => !extracted.has(fn));
|
|
7309
|
+
}
|
|
7310
|
+
function collectDependencies(target, sourceFile) {
|
|
7311
|
+
const fnMap = getFunctionMap(sourceFile);
|
|
7312
|
+
const depFunctions = collectPrivateFunctions(target, fnMap);
|
|
7313
|
+
const allExtracted = [target, ...depFunctions];
|
|
7314
|
+
const stmtMap = getPrivateStatementMap(sourceFile);
|
|
7315
|
+
const toCopy = collectPrivateStatements(allExtracted, stmtMap);
|
|
7316
|
+
const extractedSet = new Set(allExtracted);
|
|
7317
|
+
const remaining = getRemainingFunctions(sourceFile, extractedSet);
|
|
7318
|
+
const usedByRemaining = new Set(collectPrivateStatements(remaining, stmtMap));
|
|
7319
|
+
const toRemove = toCopy.filter((s) => !usedByRemaining.has(s));
|
|
7320
|
+
return {
|
|
7321
|
+
functions: depFunctions,
|
|
7322
|
+
statements: { toCopy, toRemove }
|
|
7323
|
+
};
|
|
7324
|
+
}
|
|
7325
|
+
|
|
7326
|
+
// src/commands/refactor/extract/findFunction.ts
|
|
7327
|
+
import {
|
|
7328
|
+
SyntaxKind as SyntaxKind6
|
|
7329
|
+
} from "ts-morph";
|
|
7330
|
+
function findFunction(sourceFile, functionName) {
|
|
7331
|
+
return sourceFile.getDescendantsOfKind(SyntaxKind6.FunctionDeclaration).find((fn) => fn.getName() === functionName);
|
|
7332
|
+
}
|
|
7333
|
+
|
|
7334
|
+
// src/commands/refactor/extract/getExportedDependencyNames.ts
|
|
7335
|
+
import { SyntaxKind as SyntaxKind7 } from "ts-morph";
|
|
7336
|
+
function getExportedDependencyNames(target, sourceFile) {
|
|
7337
|
+
const calledNames = /* @__PURE__ */ new Set();
|
|
7338
|
+
for (const call of target.getDescendantsOfKind(SyntaxKind7.CallExpression)) {
|
|
7339
|
+
const expr = call.getExpression();
|
|
7340
|
+
if (expr.getKind() === SyntaxKind7.Identifier) {
|
|
7341
|
+
calledNames.add(expr.getText());
|
|
7342
|
+
}
|
|
7343
|
+
}
|
|
7344
|
+
const exported = [];
|
|
7345
|
+
for (const fn of sourceFile.getDescendantsOfKind(
|
|
7346
|
+
SyntaxKind7.FunctionDeclaration
|
|
7347
|
+
)) {
|
|
7348
|
+
const name = fn.getName();
|
|
7349
|
+
if (name && calledNames.has(name) && fn.isExported()) {
|
|
7350
|
+
exported.push(name);
|
|
7351
|
+
}
|
|
7352
|
+
}
|
|
7353
|
+
return exported;
|
|
7354
|
+
}
|
|
7355
|
+
|
|
7356
|
+
// src/commands/refactor/extract/getStatementNames.ts
|
|
7357
|
+
import { SyntaxKind as SyntaxKind8 } from "ts-morph";
|
|
7358
|
+
function getStatementNames(node) {
|
|
7359
|
+
if (node.getKind() === SyntaxKind8.VariableStatement) {
|
|
7360
|
+
const stmt = node;
|
|
7361
|
+
return stmt.getDeclarations().map((d) => d.getName());
|
|
7362
|
+
}
|
|
7363
|
+
const named = node;
|
|
7364
|
+
return named.getName ? [named.getName()] : [];
|
|
7365
|
+
}
|
|
7366
|
+
|
|
7367
|
+
// src/commands/refactor/extract/resolveImports.ts
|
|
7368
|
+
import {
|
|
7369
|
+
SyntaxKind as SyntaxKind9
|
|
7370
|
+
} from "ts-morph";
|
|
7371
|
+
|
|
7372
|
+
// src/commands/refactor/extract/matchImport.ts
|
|
7373
|
+
function matchNamedImports(importDecl, neededNames) {
|
|
7374
|
+
const matched = [];
|
|
7375
|
+
for (const specifier of importDecl.getNamedImports()) {
|
|
7376
|
+
const name = specifier.getAliasNode()?.getText() ?? specifier.getName();
|
|
7377
|
+
if (!neededNames.has(name)) continue;
|
|
7378
|
+
const original = specifier.getName();
|
|
7379
|
+
const alias = specifier.getAliasNode()?.getText();
|
|
7380
|
+
matched.push(alias ? `${original} as ${alias}` : original);
|
|
7381
|
+
}
|
|
7382
|
+
return matched;
|
|
7383
|
+
}
|
|
7384
|
+
function matchOptionalImport(node, neededNames) {
|
|
7385
|
+
return node && neededNames.has(node.getText()) ? node.getText() : void 0;
|
|
7386
|
+
}
|
|
7387
|
+
function matchImport(importDecl, neededNames) {
|
|
7388
|
+
const namedImports = matchNamedImports(importDecl, neededNames);
|
|
7389
|
+
const defaultImport = matchOptionalImport(
|
|
7390
|
+
importDecl.getDefaultImport(),
|
|
7391
|
+
neededNames
|
|
7392
|
+
);
|
|
7393
|
+
const namespaceImport = matchOptionalImport(
|
|
7394
|
+
importDecl.getNamespaceImport(),
|
|
7395
|
+
neededNames
|
|
7396
|
+
);
|
|
7397
|
+
if (namedImports.length === 0 && !defaultImport && !namespaceImport) {
|
|
7398
|
+
return void 0;
|
|
7399
|
+
}
|
|
7400
|
+
return {
|
|
7401
|
+
moduleSpecifier: importDecl.getModuleSpecifierValue(),
|
|
7402
|
+
namedImports,
|
|
7403
|
+
defaultImport,
|
|
7404
|
+
namespaceImport,
|
|
7405
|
+
isTypeOnly: importDecl.isTypeOnly()
|
|
7406
|
+
};
|
|
7407
|
+
}
|
|
7408
|
+
|
|
7409
|
+
// src/commands/refactor/extract/resolveImports.ts
|
|
7410
|
+
function getReferencedNames(nodes) {
|
|
7411
|
+
const names = /* @__PURE__ */ new Set();
|
|
7412
|
+
for (const node of nodes) {
|
|
7413
|
+
for (const id of node.getDescendantsOfKind(SyntaxKind9.Identifier)) {
|
|
7414
|
+
names.add(id.getText());
|
|
7415
|
+
}
|
|
7416
|
+
}
|
|
7417
|
+
return names;
|
|
7418
|
+
}
|
|
7419
|
+
function getLocallyDeclaredNames(functions) {
|
|
7420
|
+
const names = /* @__PURE__ */ new Set();
|
|
7421
|
+
for (const fn of functions) {
|
|
7422
|
+
const name = fn.getName();
|
|
7423
|
+
if (name) names.add(name);
|
|
7424
|
+
for (const param of fn.getParameters()) {
|
|
7425
|
+
names.add(param.getName());
|
|
7426
|
+
}
|
|
7427
|
+
for (const varDecl of fn.getDescendantsOfKind(
|
|
7428
|
+
SyntaxKind9.VariableDeclaration
|
|
7429
|
+
)) {
|
|
7430
|
+
names.add(varDecl.getName());
|
|
7431
|
+
}
|
|
7432
|
+
}
|
|
7433
|
+
return names;
|
|
7434
|
+
}
|
|
7435
|
+
function resolveImports(target, dependencies, sourceFile, statements = []) {
|
|
7436
|
+
const allFunctions = [target, ...dependencies];
|
|
7437
|
+
const allNodes = [...allFunctions, ...statements];
|
|
7438
|
+
const referencedNames = getReferencedNames(allNodes);
|
|
7439
|
+
const localNames = getLocallyDeclaredNames(allFunctions);
|
|
7440
|
+
const externalNames = /* @__PURE__ */ new Set();
|
|
7441
|
+
for (const name of referencedNames) {
|
|
7442
|
+
if (!localNames.has(name)) externalNames.add(name);
|
|
7443
|
+
}
|
|
7444
|
+
const result = [];
|
|
7445
|
+
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
7446
|
+
const matched = matchImport(importDecl, externalNames);
|
|
7447
|
+
if (matched) result.push(matched);
|
|
7448
|
+
}
|
|
7449
|
+
return result;
|
|
7450
|
+
}
|
|
7451
|
+
|
|
7452
|
+
// src/commands/refactor/extract/analyseTarget.ts
|
|
7453
|
+
function extractTexts(target, allFunctions, statements) {
|
|
7454
|
+
const stmtTexts = statements.map((v) => v.getFullText().trim());
|
|
7455
|
+
const fnTexts = allFunctions.map((fn) => {
|
|
7456
|
+
const text = fn.getFullText().trim();
|
|
7457
|
+
if (fn === target && !text.startsWith("export ")) return `export ${text}`;
|
|
7458
|
+
return text;
|
|
7459
|
+
});
|
|
7460
|
+
return [...stmtTexts, ...fnTexts];
|
|
7461
|
+
}
|
|
7462
|
+
function analyseTarget(sourceFile, functionName) {
|
|
7463
|
+
const target = findFunction(sourceFile, functionName);
|
|
7464
|
+
if (!target) throw new Error(`Function "${functionName}" not found`);
|
|
7465
|
+
const { functions: dependencies, statements } = collectDependencies(
|
|
7466
|
+
target,
|
|
7467
|
+
sourceFile
|
|
7468
|
+
);
|
|
7469
|
+
const all = [target, ...dependencies];
|
|
7470
|
+
return {
|
|
7471
|
+
target,
|
|
7472
|
+
dependencies,
|
|
7473
|
+
statementsToCopy: statements.toCopy,
|
|
7474
|
+
statementsToRemove: statements.toRemove,
|
|
7475
|
+
imports: resolveImports(
|
|
7476
|
+
target,
|
|
7477
|
+
dependencies,
|
|
7478
|
+
sourceFile,
|
|
7479
|
+
statements.toCopy
|
|
7480
|
+
),
|
|
7481
|
+
exportedDeps: getExportedDependencyNames(target, sourceFile),
|
|
7482
|
+
extractedNames: [
|
|
7483
|
+
...statements.toRemove.flatMap(getStatementNames),
|
|
7484
|
+
...all.map((fn) => fn.getName()).filter(Boolean)
|
|
7485
|
+
],
|
|
7486
|
+
functionTexts: extractTexts(target, all, statements.toCopy)
|
|
7487
|
+
};
|
|
7488
|
+
}
|
|
7489
|
+
|
|
7490
|
+
// src/commands/refactor/extract/formatImportLine.ts
|
|
7491
|
+
function formatImportLine(imp) {
|
|
7492
|
+
const parts = [];
|
|
7493
|
+
if (imp.isTypeOnly) parts.push("type ");
|
|
7494
|
+
if (imp.defaultImport) parts.push(imp.defaultImport);
|
|
7495
|
+
if (imp.namespaceImport) parts.push(`* as ${imp.namespaceImport}`);
|
|
7496
|
+
if (imp.namedImports.length > 0) {
|
|
7497
|
+
if (imp.defaultImport) parts.push(", ");
|
|
7498
|
+
parts.push(`{ ${imp.namedImports.join(", ")} }`);
|
|
7499
|
+
}
|
|
7500
|
+
return `import ${parts.join("")} from "${imp.moduleSpecifier}";`;
|
|
7501
|
+
}
|
|
7502
|
+
|
|
7503
|
+
// src/commands/refactor/extract/buildDestinationContent.ts
|
|
7504
|
+
function buildDestinationContent(functionTexts, imports, sourceRelativePath, sourceImportNames) {
|
|
7505
|
+
const lines = [];
|
|
7506
|
+
for (const imp of imports) {
|
|
7507
|
+
lines.push(formatImportLine(imp));
|
|
7508
|
+
}
|
|
7509
|
+
if (sourceImportNames.length > 0) {
|
|
7510
|
+
lines.push(
|
|
7511
|
+
`import { ${sourceImportNames.join(", ")} } from "${sourceRelativePath}";`
|
|
7512
|
+
);
|
|
7513
|
+
}
|
|
7514
|
+
if (lines.length > 0) lines.push("");
|
|
7515
|
+
for (let i = 0; i < functionTexts.length; i++) {
|
|
7516
|
+
if (i > 0) lines.push("");
|
|
7517
|
+
lines.push(functionTexts[i]);
|
|
7518
|
+
}
|
|
7519
|
+
lines.push("");
|
|
7520
|
+
return lines.join("\n");
|
|
7521
|
+
}
|
|
7522
|
+
|
|
7523
|
+
// src/commands/refactor/extract/getRelativeImportPath.ts
|
|
7524
|
+
import path28 from "path";
|
|
7525
|
+
function getRelativeImportPath(from, to) {
|
|
7526
|
+
let rel = path28.relative(path28.dirname(from), to).replace(/\.tsx?$/, "").replace(/\\/g, "/");
|
|
7527
|
+
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
7528
|
+
return rel;
|
|
7529
|
+
}
|
|
7530
|
+
|
|
7531
|
+
// src/commands/refactor/extract/findImporters.ts
|
|
7532
|
+
function findImporters(functionName, sourceFile, destPath, project) {
|
|
7533
|
+
const result = [];
|
|
7534
|
+
for (const sf of project.getSourceFiles()) {
|
|
7535
|
+
if (sf === sourceFile) continue;
|
|
7536
|
+
for (const importDecl of sf.getImportDeclarations()) {
|
|
7537
|
+
if (importDecl.getModuleSpecifierSourceFile() !== sourceFile) continue;
|
|
7538
|
+
if (importDecl.getNamedImports().some((ni) => ni.getName() === functionName)) {
|
|
7539
|
+
result.push({
|
|
7540
|
+
file: sf,
|
|
7541
|
+
relPath: getRelativeImportPath(sf.getFilePath(), destPath)
|
|
7542
|
+
});
|
|
7543
|
+
}
|
|
7544
|
+
}
|
|
7545
|
+
}
|
|
7546
|
+
return result;
|
|
7547
|
+
}
|
|
7548
|
+
|
|
7549
|
+
// src/commands/refactor/extract/sourceReferencesName.ts
|
|
7550
|
+
import { SyntaxKind as SyntaxKind10 } from "ts-morph";
|
|
7551
|
+
var DECLARATION_KINDS = /* @__PURE__ */ new Set([
|
|
7552
|
+
SyntaxKind10.FunctionDeclaration,
|
|
7553
|
+
SyntaxKind10.ImportSpecifier,
|
|
7554
|
+
SyntaxKind10.ExportSpecifier
|
|
7555
|
+
]);
|
|
7556
|
+
function isInsideExtractedFunction(node, extracted) {
|
|
7557
|
+
let current = node;
|
|
7558
|
+
while (current) {
|
|
7559
|
+
if (current.getKind() !== SyntaxKind10.FunctionDeclaration) {
|
|
7560
|
+
current = current.getParent();
|
|
7561
|
+
continue;
|
|
7562
|
+
}
|
|
7563
|
+
const name = current.getName?.();
|
|
7564
|
+
if (name && extracted.has(name)) return true;
|
|
7565
|
+
current = current.getParent();
|
|
7566
|
+
}
|
|
7567
|
+
return false;
|
|
7568
|
+
}
|
|
7569
|
+
function sourceReferencesName(sourceFile, functionName, extractedNames) {
|
|
7570
|
+
const extracted = new Set(extractedNames);
|
|
7571
|
+
for (const id of sourceFile.getDescendantsOfKind(SyntaxKind10.Identifier)) {
|
|
7572
|
+
if (id.getText() !== functionName) continue;
|
|
7573
|
+
const parent = id.getParent();
|
|
7574
|
+
if (!parent || DECLARATION_KINDS.has(parent.getKind())) continue;
|
|
7575
|
+
if (!isInsideExtractedFunction(parent, extracted)) return true;
|
|
7576
|
+
}
|
|
7577
|
+
return false;
|
|
7578
|
+
}
|
|
7579
|
+
|
|
7580
|
+
// src/commands/refactor/extract/buildPlan.ts
|
|
7581
|
+
function resolveBarrel(destPath, project) {
|
|
7582
|
+
const indexPath = path29.join(path29.dirname(destPath), "index.ts");
|
|
7583
|
+
return {
|
|
7584
|
+
barrel: project.getSourceFile(indexPath),
|
|
7585
|
+
barrelRelPath: getRelativeImportPath(indexPath, destPath)
|
|
7586
|
+
};
|
|
7587
|
+
}
|
|
7588
|
+
function buildPlan(functionName, sourceFile, sourcePath, destPath, project) {
|
|
7589
|
+
const analysis = analyseTarget(sourceFile, functionName);
|
|
7590
|
+
const sourceRelPath = getRelativeImportPath(destPath, sourcePath);
|
|
7591
|
+
const { functionTexts: _, ...planFields } = analysis;
|
|
7592
|
+
return {
|
|
7593
|
+
...planFields,
|
|
7594
|
+
destContent: buildDestinationContent(
|
|
7595
|
+
analysis.functionTexts,
|
|
7596
|
+
analysis.imports,
|
|
7597
|
+
sourceRelPath,
|
|
7598
|
+
analysis.exportedDeps
|
|
7599
|
+
),
|
|
7600
|
+
destRelPath: getRelativeImportPath(sourcePath, destPath),
|
|
7601
|
+
sourceRelPath,
|
|
7602
|
+
sourceNeedsReimport: sourceReferencesName(
|
|
7603
|
+
sourceFile,
|
|
7604
|
+
functionName,
|
|
7605
|
+
analysis.extractedNames
|
|
7606
|
+
),
|
|
7607
|
+
importersToUpdate: analysis.target.isExported() ? findImporters(functionName, sourceFile, destPath, project) : [],
|
|
7608
|
+
...resolveBarrel(destPath, project)
|
|
7609
|
+
};
|
|
7610
|
+
}
|
|
7611
|
+
|
|
7612
|
+
// src/commands/refactor/extract/displayPlan.ts
|
|
7613
|
+
import path30 from "path";
|
|
7130
7614
|
import chalk85 from "chalk";
|
|
7615
|
+
function section(title) {
|
|
7616
|
+
return `
|
|
7617
|
+
${chalk85.cyan(title)}`;
|
|
7618
|
+
}
|
|
7619
|
+
function displayImporters(plan2, cwd) {
|
|
7620
|
+
if (plan2.importersToUpdate.length === 0) return;
|
|
7621
|
+
console.log(section("Update importers:"));
|
|
7622
|
+
for (const imp of plan2.importersToUpdate) {
|
|
7623
|
+
const rel = path30.relative(cwd, imp.file.getFilePath());
|
|
7624
|
+
console.log(` ${chalk85.dim(rel)}: \u2192 import from "${imp.relPath}"`);
|
|
7625
|
+
}
|
|
7626
|
+
}
|
|
7627
|
+
function displayPlan(functionName, relDest, plan2, cwd) {
|
|
7628
|
+
console.log(chalk85.bold(`Extract: ${functionName} \u2192 ${relDest}
|
|
7629
|
+
`));
|
|
7630
|
+
console.log(` ${chalk85.cyan("Functions to move:")}`);
|
|
7631
|
+
for (const name of plan2.extractedNames) {
|
|
7632
|
+
console.log(` ${name}`);
|
|
7633
|
+
}
|
|
7634
|
+
if (plan2.imports.length > 0) {
|
|
7635
|
+
console.log(section("Imports to copy:"));
|
|
7636
|
+
for (const imp of plan2.imports) {
|
|
7637
|
+
console.log(` ${formatImportLine(imp)}`);
|
|
7638
|
+
}
|
|
7639
|
+
}
|
|
7640
|
+
if (plan2.exportedDeps.length > 0) {
|
|
7641
|
+
console.log(section("New imports from source:"));
|
|
7642
|
+
console.log(
|
|
7643
|
+
` import { ${plan2.exportedDeps.join(", ")} } from "${plan2.sourceRelPath}";`
|
|
7644
|
+
);
|
|
7645
|
+
}
|
|
7646
|
+
console.log(section("Source file changes:"));
|
|
7647
|
+
console.log(` Remove: ${plan2.extractedNames.join(", ")}`);
|
|
7648
|
+
if (plan2.sourceNeedsReimport) {
|
|
7649
|
+
console.log(
|
|
7650
|
+
` Add: import { ${functionName} } from "${plan2.destRelPath}";`
|
|
7651
|
+
);
|
|
7652
|
+
}
|
|
7653
|
+
displayImporters(plan2, cwd);
|
|
7654
|
+
if (plan2.barrel) {
|
|
7655
|
+
console.log(section("Barrel export:"));
|
|
7656
|
+
console.log(
|
|
7657
|
+
` Add: export { ${functionName} } from "${plan2.barrelRelPath}";`
|
|
7658
|
+
);
|
|
7659
|
+
}
|
|
7660
|
+
}
|
|
7661
|
+
|
|
7662
|
+
// src/commands/refactor/extract/loadProjectFile.ts
|
|
7663
|
+
import fs17 from "fs";
|
|
7664
|
+
import path31 from "path";
|
|
7665
|
+
import chalk86 from "chalk";
|
|
7666
|
+
import { Project as Project2 } from "ts-morph";
|
|
7667
|
+
function findTsConfig(sourcePath) {
|
|
7668
|
+
const rootConfig = path31.resolve("tsconfig.json");
|
|
7669
|
+
if (!fs17.existsSync(rootConfig)) return rootConfig;
|
|
7670
|
+
const raw = fs17.readFileSync(rootConfig, "utf-8");
|
|
7671
|
+
const stripped = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
7672
|
+
let parsed;
|
|
7673
|
+
try {
|
|
7674
|
+
parsed = JSON.parse(stripped);
|
|
7675
|
+
} catch {
|
|
7676
|
+
return rootConfig;
|
|
7677
|
+
}
|
|
7678
|
+
if (!parsed.references?.length) return rootConfig;
|
|
7679
|
+
for (const ref of parsed.references) {
|
|
7680
|
+
const refPath = path31.resolve(ref.path);
|
|
7681
|
+
const configPath = fs17.statSync(refPath, { throwIfNoEntry: false })?.isDirectory() ? path31.join(refPath, "tsconfig.json") : refPath;
|
|
7682
|
+
if (!fs17.existsSync(configPath)) continue;
|
|
7683
|
+
const project = new Project2({ tsConfigFilePath: configPath });
|
|
7684
|
+
if (project.getSourceFile(sourcePath)) return configPath;
|
|
7685
|
+
}
|
|
7686
|
+
return rootConfig;
|
|
7687
|
+
}
|
|
7688
|
+
function loadProjectFile(file) {
|
|
7689
|
+
const sourcePath = path31.resolve(file);
|
|
7690
|
+
const tsConfigPath = findTsConfig(sourcePath);
|
|
7691
|
+
const project = new Project2({
|
|
7692
|
+
tsConfigFilePath: tsConfigPath
|
|
7693
|
+
});
|
|
7694
|
+
const sourceFile = project.getSourceFile(sourcePath);
|
|
7695
|
+
if (!sourceFile) {
|
|
7696
|
+
console.log(chalk86.red(`File not found in project: ${file}`));
|
|
7697
|
+
process.exit(1);
|
|
7698
|
+
}
|
|
7699
|
+
return { project, sourceFile };
|
|
7700
|
+
}
|
|
7701
|
+
|
|
7702
|
+
// src/commands/refactor/extract/index.ts
|
|
7703
|
+
async function extract(file, functionName, destination, options2 = {}) {
|
|
7704
|
+
const sourcePath = path32.resolve(file);
|
|
7705
|
+
const destPath = path32.resolve(destination);
|
|
7706
|
+
const cwd = process.cwd();
|
|
7707
|
+
const relDest = path32.relative(cwd, destPath);
|
|
7708
|
+
const { project, sourceFile } = loadProjectFile(file);
|
|
7709
|
+
const plan2 = buildPlan(
|
|
7710
|
+
functionName,
|
|
7711
|
+
sourceFile,
|
|
7712
|
+
sourcePath,
|
|
7713
|
+
destPath,
|
|
7714
|
+
project
|
|
7715
|
+
);
|
|
7716
|
+
displayPlan(functionName, relDest, plan2, cwd);
|
|
7717
|
+
if (options2.apply) {
|
|
7718
|
+
await applyExtraction(functionName, sourceFile, destPath, plan2, project);
|
|
7719
|
+
console.log(chalk87.green("\nExtraction complete"));
|
|
7720
|
+
} else {
|
|
7721
|
+
console.log(chalk87.dim("\nDry run. Use --apply to execute."));
|
|
7722
|
+
}
|
|
7723
|
+
}
|
|
7724
|
+
|
|
7725
|
+
// src/commands/refactor/ignore.ts
|
|
7726
|
+
import fs18 from "fs";
|
|
7727
|
+
import chalk88 from "chalk";
|
|
7131
7728
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
7132
7729
|
function ignore(file) {
|
|
7133
|
-
if (!
|
|
7134
|
-
console.error(
|
|
7730
|
+
if (!fs18.existsSync(file)) {
|
|
7731
|
+
console.error(chalk88.red(`Error: File does not exist: ${file}`));
|
|
7135
7732
|
process.exit(1);
|
|
7136
7733
|
}
|
|
7137
|
-
const content =
|
|
7734
|
+
const content = fs18.readFileSync(file, "utf-8");
|
|
7138
7735
|
const lineCount = content.split("\n").length;
|
|
7139
7736
|
const maxLines = lineCount + 10;
|
|
7140
7737
|
const entry = `- file: ${file}
|
|
7141
7738
|
maxLines: ${maxLines}
|
|
7142
7739
|
`;
|
|
7143
|
-
if (
|
|
7144
|
-
const existing =
|
|
7145
|
-
|
|
7740
|
+
if (fs18.existsSync(REFACTOR_YML_PATH2)) {
|
|
7741
|
+
const existing = fs18.readFileSync(REFACTOR_YML_PATH2, "utf-8");
|
|
7742
|
+
fs18.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
|
|
7146
7743
|
} else {
|
|
7147
|
-
|
|
7744
|
+
fs18.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
7148
7745
|
}
|
|
7149
7746
|
console.log(
|
|
7150
|
-
|
|
7747
|
+
chalk88.green(
|
|
7151
7748
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
7152
7749
|
)
|
|
7153
7750
|
);
|
|
7154
7751
|
}
|
|
7155
7752
|
|
|
7156
7753
|
// src/commands/refactor/rename/index.ts
|
|
7157
|
-
import
|
|
7158
|
-
import
|
|
7159
|
-
import { Project as Project2 } from "ts-morph";
|
|
7754
|
+
import path33 from "path";
|
|
7755
|
+
import chalk89 from "chalk";
|
|
7160
7756
|
async function rename(source, destination, options2 = {}) {
|
|
7161
|
-
const
|
|
7162
|
-
const destPath = path28.resolve(destination);
|
|
7757
|
+
const destPath = path33.resolve(destination);
|
|
7163
7758
|
const cwd = process.cwd();
|
|
7164
|
-
const relSource =
|
|
7165
|
-
const relDest =
|
|
7166
|
-
const project =
|
|
7167
|
-
|
|
7168
|
-
});
|
|
7169
|
-
const sourceFile = project.getSourceFile(sourcePath);
|
|
7170
|
-
if (!sourceFile) {
|
|
7171
|
-
console.log(chalk86.red(`File not found in project: ${source}`));
|
|
7172
|
-
process.exit(1);
|
|
7173
|
-
}
|
|
7174
|
-
console.log(chalk86.bold(`Rename: ${relSource} \u2192 ${relDest}`));
|
|
7759
|
+
const relSource = path33.relative(cwd, path33.resolve(source));
|
|
7760
|
+
const relDest = path33.relative(cwd, destPath);
|
|
7761
|
+
const { project, sourceFile } = loadProjectFile(source);
|
|
7762
|
+
console.log(chalk89.bold(`Rename: ${relSource} \u2192 ${relDest}`));
|
|
7175
7763
|
if (options2.apply) {
|
|
7176
7764
|
sourceFile.move(destPath);
|
|
7177
7765
|
await project.save();
|
|
7178
|
-
console.log(
|
|
7766
|
+
console.log(chalk89.green("Done"));
|
|
7179
7767
|
} else {
|
|
7180
|
-
console.log(
|
|
7768
|
+
console.log(chalk89.dim("Dry run. Use --apply to execute."));
|
|
7181
7769
|
}
|
|
7182
7770
|
}
|
|
7183
7771
|
|
|
7184
7772
|
// src/commands/refactor/renameSymbol/index.ts
|
|
7185
|
-
import
|
|
7186
|
-
import
|
|
7773
|
+
import path35 from "path";
|
|
7774
|
+
import chalk90 from "chalk";
|
|
7187
7775
|
import { Project as Project3 } from "ts-morph";
|
|
7188
7776
|
|
|
7189
7777
|
// src/commands/refactor/renameSymbol/findSymbol.ts
|
|
7190
|
-
import { SyntaxKind as
|
|
7778
|
+
import { SyntaxKind as SyntaxKind11 } from "ts-morph";
|
|
7191
7779
|
var declarationKinds = [
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7780
|
+
SyntaxKind11.VariableDeclaration,
|
|
7781
|
+
SyntaxKind11.FunctionDeclaration,
|
|
7782
|
+
SyntaxKind11.ClassDeclaration,
|
|
7783
|
+
SyntaxKind11.InterfaceDeclaration,
|
|
7784
|
+
SyntaxKind11.TypeAliasDeclaration,
|
|
7785
|
+
SyntaxKind11.EnumDeclaration,
|
|
7786
|
+
SyntaxKind11.PropertyDeclaration,
|
|
7787
|
+
SyntaxKind11.MethodDeclaration,
|
|
7788
|
+
SyntaxKind11.Parameter
|
|
7201
7789
|
];
|
|
7202
7790
|
function isDeclaration(identifier) {
|
|
7203
7791
|
const parent = identifier.getParent();
|
|
7204
7792
|
return parent !== void 0 && declarationKinds.includes(parent.getKind());
|
|
7205
7793
|
}
|
|
7206
7794
|
function findSymbol(sourceFile, symbolName) {
|
|
7207
|
-
for (const id of sourceFile.getDescendantsOfKind(
|
|
7795
|
+
for (const id of sourceFile.getDescendantsOfKind(SyntaxKind11.Identifier)) {
|
|
7208
7796
|
if (id.getText() === symbolName && isDeclaration(id)) return id;
|
|
7209
7797
|
}
|
|
7210
7798
|
return void 0;
|
|
7211
7799
|
}
|
|
7212
7800
|
|
|
7213
7801
|
// src/commands/refactor/renameSymbol/groupReferences.ts
|
|
7214
|
-
import
|
|
7802
|
+
import path34 from "path";
|
|
7215
7803
|
function groupReferences(symbol, cwd) {
|
|
7216
7804
|
const refs = symbol.findReferencesAsNodes();
|
|
7217
7805
|
const grouped = /* @__PURE__ */ new Map();
|
|
7218
7806
|
for (const ref of refs) {
|
|
7219
|
-
const refFile =
|
|
7807
|
+
const refFile = path34.relative(cwd, ref.getSourceFile().getFilePath());
|
|
7220
7808
|
const lines = grouped.get(refFile) ?? [];
|
|
7221
7809
|
if (!grouped.has(refFile)) grouped.set(refFile, lines);
|
|
7222
7810
|
lines.push(ref.getStartLineNumber());
|
|
@@ -7226,47 +7814,47 @@ function groupReferences(symbol, cwd) {
|
|
|
7226
7814
|
|
|
7227
7815
|
// src/commands/refactor/renameSymbol/index.ts
|
|
7228
7816
|
async function renameSymbol(file, oldName, newName, options2 = {}) {
|
|
7229
|
-
const filePath =
|
|
7230
|
-
const tsConfigPath =
|
|
7817
|
+
const filePath = path35.resolve(file);
|
|
7818
|
+
const tsConfigPath = path35.resolve("tsconfig.json");
|
|
7231
7819
|
const cwd = process.cwd();
|
|
7232
7820
|
const project = new Project3({ tsConfigFilePath: tsConfigPath });
|
|
7233
7821
|
const sourceFile = project.getSourceFile(filePath);
|
|
7234
7822
|
if (!sourceFile) {
|
|
7235
|
-
console.log(
|
|
7823
|
+
console.log(chalk90.red(`File not found in project: ${file}`));
|
|
7236
7824
|
process.exit(1);
|
|
7237
7825
|
}
|
|
7238
7826
|
const symbol = findSymbol(sourceFile, oldName);
|
|
7239
7827
|
if (!symbol) {
|
|
7240
|
-
console.log(
|
|
7828
|
+
console.log(chalk90.red(`Symbol "${oldName}" not found in ${file}`));
|
|
7241
7829
|
process.exit(1);
|
|
7242
7830
|
}
|
|
7243
7831
|
const grouped = groupReferences(symbol, cwd);
|
|
7244
7832
|
const totalRefs = [...grouped.values()].reduce((s, l) => s + l.length, 0);
|
|
7245
7833
|
console.log(
|
|
7246
|
-
|
|
7834
|
+
chalk90.bold(`Rename: ${oldName} \u2192 ${newName} (${totalRefs} references)
|
|
7247
7835
|
`)
|
|
7248
7836
|
);
|
|
7249
7837
|
for (const [refFile, lines] of grouped) {
|
|
7250
7838
|
console.log(
|
|
7251
|
-
` ${
|
|
7839
|
+
` ${chalk90.dim(refFile)}: lines ${chalk90.cyan(lines.join(", "))}`
|
|
7252
7840
|
);
|
|
7253
7841
|
}
|
|
7254
7842
|
if (options2.apply) {
|
|
7255
7843
|
symbol.rename(newName);
|
|
7256
7844
|
await project.save();
|
|
7257
|
-
console.log(
|
|
7845
|
+
console.log(chalk90.green(`
|
|
7258
7846
|
Renamed ${oldName} \u2192 ${newName}`));
|
|
7259
7847
|
} else {
|
|
7260
|
-
console.log(
|
|
7848
|
+
console.log(chalk90.dim("\nDry run. Use --apply to execute."));
|
|
7261
7849
|
}
|
|
7262
7850
|
}
|
|
7263
7851
|
|
|
7264
7852
|
// src/commands/refactor/restructure/index.ts
|
|
7265
|
-
import
|
|
7266
|
-
import
|
|
7853
|
+
import path44 from "path";
|
|
7854
|
+
import chalk93 from "chalk";
|
|
7267
7855
|
|
|
7268
7856
|
// src/commands/refactor/restructure/buildImportGraph/index.ts
|
|
7269
|
-
import
|
|
7857
|
+
import path36 from "path";
|
|
7270
7858
|
import ts7 from "typescript";
|
|
7271
7859
|
|
|
7272
7860
|
// src/commands/refactor/restructure/buildImportGraph/getImportSpecifiers.ts
|
|
@@ -7293,7 +7881,7 @@ function loadParsedConfig(tsConfigPath) {
|
|
|
7293
7881
|
return ts7.parseJsonConfigFileContent(
|
|
7294
7882
|
configFile.config,
|
|
7295
7883
|
ts7.sys,
|
|
7296
|
-
|
|
7884
|
+
path36.dirname(tsConfigPath)
|
|
7297
7885
|
);
|
|
7298
7886
|
}
|
|
7299
7887
|
function addToSetMap(map, key, value) {
|
|
@@ -7309,7 +7897,7 @@ function resolveImport(specifier, filePath, options2) {
|
|
|
7309
7897
|
const resolved = ts7.resolveModuleName(specifier, filePath, options2, ts7.sys);
|
|
7310
7898
|
const resolvedPath = resolved.resolvedModule?.resolvedFileName;
|
|
7311
7899
|
if (!resolvedPath || resolvedPath.includes("node_modules")) return null;
|
|
7312
|
-
return
|
|
7900
|
+
return path36.resolve(resolvedPath);
|
|
7313
7901
|
}
|
|
7314
7902
|
function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
7315
7903
|
const parsed = loadParsedConfig(tsConfigPath);
|
|
@@ -7318,7 +7906,7 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
7318
7906
|
const importedBy = /* @__PURE__ */ new Map();
|
|
7319
7907
|
const imports = /* @__PURE__ */ new Map();
|
|
7320
7908
|
for (const sourceFile of program2.getSourceFiles()) {
|
|
7321
|
-
const filePath =
|
|
7909
|
+
const filePath = path36.resolve(sourceFile.fileName);
|
|
7322
7910
|
if (filePath.includes("node_modules")) continue;
|
|
7323
7911
|
for (const specifier of getImportSpecifiers(sourceFile)) {
|
|
7324
7912
|
const absTarget = resolveImport(specifier, filePath, parsed.options);
|
|
@@ -7332,12 +7920,12 @@ function buildImportGraph(candidateFiles, tsConfigPath) {
|
|
|
7332
7920
|
}
|
|
7333
7921
|
|
|
7334
7922
|
// src/commands/refactor/restructure/clusterDirectories.ts
|
|
7335
|
-
import
|
|
7923
|
+
import path37 from "path";
|
|
7336
7924
|
function clusterDirectories(graph) {
|
|
7337
7925
|
const dirImportedBy = /* @__PURE__ */ new Map();
|
|
7338
7926
|
for (const edge of graph.edges) {
|
|
7339
|
-
const sourceDir =
|
|
7340
|
-
const targetDir =
|
|
7927
|
+
const sourceDir = path37.dirname(edge.source);
|
|
7928
|
+
const targetDir = path37.dirname(edge.target);
|
|
7341
7929
|
if (sourceDir === targetDir) continue;
|
|
7342
7930
|
if (!graph.files.has(edge.target)) continue;
|
|
7343
7931
|
const existing = dirImportedBy.get(targetDir) ?? /* @__PURE__ */ new Set();
|
|
@@ -7365,20 +7953,20 @@ function clusterDirectories(graph) {
|
|
|
7365
7953
|
return clusters;
|
|
7366
7954
|
}
|
|
7367
7955
|
function isAncestor(ancestor, descendant) {
|
|
7368
|
-
const rel =
|
|
7956
|
+
const rel = path37.relative(ancestor, descendant);
|
|
7369
7957
|
return !rel.startsWith("..") && rel !== "";
|
|
7370
7958
|
}
|
|
7371
7959
|
|
|
7372
7960
|
// src/commands/refactor/restructure/clusterFiles.ts
|
|
7373
|
-
import
|
|
7961
|
+
import path38 from "path";
|
|
7374
7962
|
function findRootParent(file, importedBy, visited) {
|
|
7375
7963
|
const importers = importedBy.get(file);
|
|
7376
7964
|
if (!importers || importers.size !== 1) return file;
|
|
7377
7965
|
const parent = [...importers][0];
|
|
7378
|
-
const parentDir =
|
|
7379
|
-
const fileDir =
|
|
7966
|
+
const parentDir = path38.dirname(parent);
|
|
7967
|
+
const fileDir = path38.dirname(file);
|
|
7380
7968
|
if (parentDir !== fileDir) return file;
|
|
7381
|
-
if (
|
|
7969
|
+
if (path38.basename(parent, path38.extname(parent)) === "index") return file;
|
|
7382
7970
|
if (visited.has(parent)) return file;
|
|
7383
7971
|
visited.add(parent);
|
|
7384
7972
|
return findRootParent(parent, importedBy, visited);
|
|
@@ -7386,16 +7974,16 @@ function findRootParent(file, importedBy, visited) {
|
|
|
7386
7974
|
function clusterFiles(graph) {
|
|
7387
7975
|
const clusters = /* @__PURE__ */ new Map();
|
|
7388
7976
|
for (const file of graph.files) {
|
|
7389
|
-
const basename7 =
|
|
7977
|
+
const basename7 = path38.basename(file, path38.extname(file));
|
|
7390
7978
|
if (basename7 === "index") continue;
|
|
7391
7979
|
const importers = graph.importedBy.get(file);
|
|
7392
7980
|
if (!importers || importers.size !== 1) continue;
|
|
7393
7981
|
const parent = [...importers][0];
|
|
7394
7982
|
if (!graph.files.has(parent)) continue;
|
|
7395
|
-
const parentDir =
|
|
7396
|
-
const fileDir =
|
|
7983
|
+
const parentDir = path38.dirname(parent);
|
|
7984
|
+
const fileDir = path38.dirname(file);
|
|
7397
7985
|
if (parentDir !== fileDir) continue;
|
|
7398
|
-
const parentBasename =
|
|
7986
|
+
const parentBasename = path38.basename(parent, path38.extname(parent));
|
|
7399
7987
|
if (parentBasename === "index") continue;
|
|
7400
7988
|
const root = findRootParent(parent, graph.importedBy, /* @__PURE__ */ new Set([file]));
|
|
7401
7989
|
if (!root || root === file) continue;
|
|
@@ -7407,10 +7995,10 @@ function clusterFiles(graph) {
|
|
|
7407
7995
|
}
|
|
7408
7996
|
|
|
7409
7997
|
// src/commands/refactor/restructure/computeRewrites/index.ts
|
|
7410
|
-
import
|
|
7998
|
+
import path39 from "path";
|
|
7411
7999
|
|
|
7412
8000
|
// src/commands/refactor/restructure/computeRewrites/applyRewrites.ts
|
|
7413
|
-
import
|
|
8001
|
+
import fs19 from "fs";
|
|
7414
8002
|
function getOrCreateList(map, key) {
|
|
7415
8003
|
const list4 = map.get(key) ?? [];
|
|
7416
8004
|
if (!map.has(key)) map.set(key, list4);
|
|
@@ -7429,7 +8017,7 @@ function rewriteSpecifier(content, oldSpecifier, newSpecifier) {
|
|
|
7429
8017
|
return content.replace(pattern2, `$1${newSpecifier}$2`);
|
|
7430
8018
|
}
|
|
7431
8019
|
function applyFileRewrites(file, fileRewrites) {
|
|
7432
|
-
let content =
|
|
8020
|
+
let content = fs19.readFileSync(file, "utf-8");
|
|
7433
8021
|
for (const { oldSpecifier, newSpecifier } of fileRewrites) {
|
|
7434
8022
|
content = rewriteSpecifier(content, oldSpecifier, newSpecifier);
|
|
7435
8023
|
}
|
|
@@ -7461,7 +8049,7 @@ function normalizeSpecifier(rel) {
|
|
|
7461
8049
|
);
|
|
7462
8050
|
}
|
|
7463
8051
|
function computeSpecifier(fromFile, toFile) {
|
|
7464
|
-
return normalizeSpecifier(
|
|
8052
|
+
return normalizeSpecifier(path39.relative(path39.dirname(fromFile), toFile));
|
|
7465
8053
|
}
|
|
7466
8054
|
function isAffected(edge, moveMap) {
|
|
7467
8055
|
return moveMap.has(edge.target) || moveMap.has(edge.source);
|
|
@@ -7505,51 +8093,51 @@ function computeRewrites(moves, edges, allProjectFiles) {
|
|
|
7505
8093
|
}
|
|
7506
8094
|
|
|
7507
8095
|
// src/commands/refactor/restructure/displayPlan.ts
|
|
7508
|
-
import
|
|
7509
|
-
import
|
|
8096
|
+
import path40 from "path";
|
|
8097
|
+
import chalk91 from "chalk";
|
|
7510
8098
|
function relPath(filePath) {
|
|
7511
|
-
return
|
|
8099
|
+
return path40.relative(process.cwd(), filePath);
|
|
7512
8100
|
}
|
|
7513
8101
|
function displayMoves(plan2) {
|
|
7514
8102
|
if (plan2.moves.length === 0) return;
|
|
7515
|
-
console.log(
|
|
8103
|
+
console.log(chalk91.bold("\nFile moves:"));
|
|
7516
8104
|
for (const move of plan2.moves) {
|
|
7517
8105
|
console.log(
|
|
7518
|
-
` ${
|
|
8106
|
+
` ${chalk91.red(relPath(move.from))} \u2192 ${chalk91.green(relPath(move.to))}`
|
|
7519
8107
|
);
|
|
7520
|
-
console.log(
|
|
8108
|
+
console.log(chalk91.dim(` ${move.reason}`));
|
|
7521
8109
|
}
|
|
7522
8110
|
}
|
|
7523
8111
|
function displayRewrites(rewrites) {
|
|
7524
8112
|
if (rewrites.length === 0) return;
|
|
7525
8113
|
const affectedFiles = new Set(rewrites.map((r) => r.file));
|
|
7526
|
-
console.log(
|
|
8114
|
+
console.log(chalk91.bold(`
|
|
7527
8115
|
Import rewrites (${affectedFiles.size} files):`));
|
|
7528
8116
|
for (const file of affectedFiles) {
|
|
7529
|
-
console.log(` ${
|
|
8117
|
+
console.log(` ${chalk91.cyan(relPath(file))}:`);
|
|
7530
8118
|
for (const { oldSpecifier, newSpecifier } of rewrites.filter(
|
|
7531
8119
|
(r) => r.file === file
|
|
7532
8120
|
)) {
|
|
7533
8121
|
console.log(
|
|
7534
|
-
` ${
|
|
8122
|
+
` ${chalk91.red(`"${oldSpecifier}"`)} \u2192 ${chalk91.green(`"${newSpecifier}"`)}`
|
|
7535
8123
|
);
|
|
7536
8124
|
}
|
|
7537
8125
|
}
|
|
7538
8126
|
}
|
|
7539
|
-
function
|
|
8127
|
+
function displayPlan2(plan2) {
|
|
7540
8128
|
if (plan2.warnings.length > 0) {
|
|
7541
|
-
console.log(
|
|
7542
|
-
for (const w of plan2.warnings) console.log(
|
|
8129
|
+
console.log(chalk91.yellow("\nWarnings:"));
|
|
8130
|
+
for (const w of plan2.warnings) console.log(chalk91.yellow(` ${w}`));
|
|
7543
8131
|
}
|
|
7544
8132
|
if (plan2.newDirectories.length > 0) {
|
|
7545
|
-
console.log(
|
|
8133
|
+
console.log(chalk91.bold("\nNew directories:"));
|
|
7546
8134
|
for (const dir of plan2.newDirectories)
|
|
7547
|
-
console.log(
|
|
8135
|
+
console.log(chalk91.green(` ${dir}/`));
|
|
7548
8136
|
}
|
|
7549
8137
|
displayMoves(plan2);
|
|
7550
8138
|
displayRewrites(plan2.rewrites);
|
|
7551
8139
|
console.log(
|
|
7552
|
-
|
|
8140
|
+
chalk91.dim(
|
|
7553
8141
|
`
|
|
7554
8142
|
Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports rewritten`
|
|
7555
8143
|
)
|
|
@@ -7557,45 +8145,45 @@ Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports r
|
|
|
7557
8145
|
}
|
|
7558
8146
|
|
|
7559
8147
|
// src/commands/refactor/restructure/executePlan.ts
|
|
7560
|
-
import
|
|
7561
|
-
import
|
|
7562
|
-
import
|
|
8148
|
+
import fs20 from "fs";
|
|
8149
|
+
import path41 from "path";
|
|
8150
|
+
import chalk92 from "chalk";
|
|
7563
8151
|
function executePlan(plan2) {
|
|
7564
8152
|
const updatedContents = applyRewrites(plan2.rewrites);
|
|
7565
8153
|
for (const [file, content] of updatedContents) {
|
|
7566
|
-
|
|
8154
|
+
fs20.writeFileSync(file, content, "utf-8");
|
|
7567
8155
|
console.log(
|
|
7568
|
-
|
|
8156
|
+
chalk92.cyan(` Rewrote imports in ${path41.relative(process.cwd(), file)}`)
|
|
7569
8157
|
);
|
|
7570
8158
|
}
|
|
7571
8159
|
for (const dir of plan2.newDirectories) {
|
|
7572
|
-
|
|
7573
|
-
console.log(
|
|
8160
|
+
fs20.mkdirSync(dir, { recursive: true });
|
|
8161
|
+
console.log(chalk92.green(` Created ${path41.relative(process.cwd(), dir)}/`));
|
|
7574
8162
|
}
|
|
7575
8163
|
for (const move of plan2.moves) {
|
|
7576
|
-
const targetDir =
|
|
7577
|
-
if (!
|
|
7578
|
-
|
|
8164
|
+
const targetDir = path41.dirname(move.to);
|
|
8165
|
+
if (!fs20.existsSync(targetDir)) {
|
|
8166
|
+
fs20.mkdirSync(targetDir, { recursive: true });
|
|
7579
8167
|
}
|
|
7580
|
-
|
|
8168
|
+
fs20.renameSync(move.from, move.to);
|
|
7581
8169
|
console.log(
|
|
7582
|
-
|
|
7583
|
-
` Moved ${
|
|
8170
|
+
chalk92.white(
|
|
8171
|
+
` Moved ${path41.relative(process.cwd(), move.from)} \u2192 ${path41.relative(process.cwd(), move.to)}`
|
|
7584
8172
|
)
|
|
7585
8173
|
);
|
|
7586
8174
|
}
|
|
7587
|
-
removeEmptyDirectories(plan2.moves.map((m) =>
|
|
8175
|
+
removeEmptyDirectories(plan2.moves.map((m) => path41.dirname(m.from)));
|
|
7588
8176
|
}
|
|
7589
8177
|
function removeEmptyDirectories(dirs) {
|
|
7590
8178
|
const unique = [...new Set(dirs)];
|
|
7591
8179
|
for (const dir of unique) {
|
|
7592
|
-
if (!
|
|
7593
|
-
const entries =
|
|
8180
|
+
if (!fs20.existsSync(dir)) continue;
|
|
8181
|
+
const entries = fs20.readdirSync(dir);
|
|
7594
8182
|
if (entries.length === 0) {
|
|
7595
|
-
|
|
8183
|
+
fs20.rmdirSync(dir);
|
|
7596
8184
|
console.log(
|
|
7597
|
-
|
|
7598
|
-
` Removed empty directory ${
|
|
8185
|
+
chalk92.dim(
|
|
8186
|
+
` Removed empty directory ${path41.relative(process.cwd(), dir)}`
|
|
7599
8187
|
)
|
|
7600
8188
|
);
|
|
7601
8189
|
}
|
|
@@ -7603,46 +8191,46 @@ function removeEmptyDirectories(dirs) {
|
|
|
7603
8191
|
}
|
|
7604
8192
|
|
|
7605
8193
|
// src/commands/refactor/restructure/planFileMoves/index.ts
|
|
7606
|
-
import
|
|
8194
|
+
import path43 from "path";
|
|
7607
8195
|
|
|
7608
8196
|
// src/commands/refactor/restructure/planFileMoves/shared.ts
|
|
7609
|
-
import
|
|
8197
|
+
import fs21 from "fs";
|
|
7610
8198
|
function emptyResult() {
|
|
7611
8199
|
return { moves: [], directories: [], warnings: [] };
|
|
7612
8200
|
}
|
|
7613
8201
|
function checkDirConflict(result, label2, dir) {
|
|
7614
|
-
if (!
|
|
8202
|
+
if (!fs21.existsSync(dir)) return false;
|
|
7615
8203
|
result.warnings.push(`Skipping ${label2}: directory ${dir} already exists`);
|
|
7616
8204
|
return true;
|
|
7617
8205
|
}
|
|
7618
8206
|
|
|
7619
8207
|
// src/commands/refactor/restructure/planFileMoves/planDirectoryMoves.ts
|
|
7620
|
-
import
|
|
7621
|
-
import
|
|
8208
|
+
import fs22 from "fs";
|
|
8209
|
+
import path42 from "path";
|
|
7622
8210
|
function collectEntry(results, dir, entry) {
|
|
7623
|
-
const full =
|
|
8211
|
+
const full = path42.join(dir, entry.name);
|
|
7624
8212
|
const items = entry.isDirectory() ? listFilesRecursive(full) : [full];
|
|
7625
8213
|
results.push(...items);
|
|
7626
8214
|
}
|
|
7627
8215
|
function listFilesRecursive(dir) {
|
|
7628
|
-
if (!
|
|
8216
|
+
if (!fs22.existsSync(dir)) return [];
|
|
7629
8217
|
const results = [];
|
|
7630
|
-
for (const entry of
|
|
8218
|
+
for (const entry of fs22.readdirSync(dir, { withFileTypes: true })) {
|
|
7631
8219
|
collectEntry(results, dir, entry);
|
|
7632
8220
|
}
|
|
7633
8221
|
return results;
|
|
7634
8222
|
}
|
|
7635
8223
|
function addDirectoryFileMoves(moves, childDir, newLocation, reason) {
|
|
7636
8224
|
for (const file of listFilesRecursive(childDir)) {
|
|
7637
|
-
const rel =
|
|
7638
|
-
moves.push({ from: file, to:
|
|
8225
|
+
const rel = path42.relative(childDir, file);
|
|
8226
|
+
moves.push({ from: file, to: path42.join(newLocation, rel), reason });
|
|
7639
8227
|
}
|
|
7640
8228
|
}
|
|
7641
8229
|
function resolveChildDest(parentDir, childDir) {
|
|
7642
|
-
return
|
|
8230
|
+
return path42.join(parentDir, path42.basename(childDir));
|
|
7643
8231
|
}
|
|
7644
8232
|
function childMoveReason(parentDir) {
|
|
7645
|
-
return `Directory only imported from ${
|
|
8233
|
+
return `Directory only imported from ${path42.basename(parentDir)}/`;
|
|
7646
8234
|
}
|
|
7647
8235
|
function registerDirectoryMove(result, childDir, dest, parentDir) {
|
|
7648
8236
|
result.directories.push(dest);
|
|
@@ -7667,7 +8255,7 @@ function planDirectoryMoves(clusters) {
|
|
|
7667
8255
|
|
|
7668
8256
|
// src/commands/refactor/restructure/planFileMoves/index.ts
|
|
7669
8257
|
function childMoveData(child, newDir, parentBase) {
|
|
7670
|
-
const to =
|
|
8258
|
+
const to = path43.join(newDir, path43.basename(child));
|
|
7671
8259
|
return { from: child, to, reason: `Only imported by ${parentBase}` };
|
|
7672
8260
|
}
|
|
7673
8261
|
function addChildMoves(moves, children, newDir, parentBase) {
|
|
@@ -7675,15 +8263,15 @@ function addChildMoves(moves, children, newDir, parentBase) {
|
|
|
7675
8263
|
moves.push(childMoveData(child, newDir, parentBase));
|
|
7676
8264
|
}
|
|
7677
8265
|
function getBaseName(filePath) {
|
|
7678
|
-
return
|
|
8266
|
+
return path43.basename(filePath, path43.extname(filePath));
|
|
7679
8267
|
}
|
|
7680
8268
|
function resolveClusterDir(parent) {
|
|
7681
|
-
return
|
|
8269
|
+
return path43.join(path43.dirname(parent), getBaseName(parent));
|
|
7682
8270
|
}
|
|
7683
8271
|
function createParentMove(parent, newDir) {
|
|
7684
8272
|
return {
|
|
7685
8273
|
from: parent,
|
|
7686
|
-
to:
|
|
8274
|
+
to: path43.join(newDir, `index${path43.extname(parent)}`),
|
|
7687
8275
|
reason: `Main module of new ${getBaseName(parent)}/ directory`
|
|
7688
8276
|
};
|
|
7689
8277
|
}
|
|
@@ -7706,8 +8294,8 @@ function planFileMoves(clusters) {
|
|
|
7706
8294
|
}
|
|
7707
8295
|
|
|
7708
8296
|
// src/commands/refactor/restructure/index.ts
|
|
7709
|
-
function
|
|
7710
|
-
const candidates = new Set(candidateFiles.map((f) =>
|
|
8297
|
+
function buildPlan2(candidateFiles, tsConfigPath) {
|
|
8298
|
+
const candidates = new Set(candidateFiles.map((f) => path44.resolve(f)));
|
|
7711
8299
|
const graph = buildImportGraph(candidates, tsConfigPath);
|
|
7712
8300
|
const allProjectFiles = /* @__PURE__ */ new Set([
|
|
7713
8301
|
...graph.importedBy.keys(),
|
|
@@ -7727,40 +8315,42 @@ async function restructure(pattern2, options2 = {}) {
|
|
|
7727
8315
|
const targetPattern = pattern2 ?? "src";
|
|
7728
8316
|
const files = findSourceFiles2(targetPattern);
|
|
7729
8317
|
if (files.length === 0) {
|
|
7730
|
-
console.log(
|
|
8318
|
+
console.log(chalk93.yellow("No files found matching pattern"));
|
|
7731
8319
|
return;
|
|
7732
8320
|
}
|
|
7733
|
-
const tsConfigPath =
|
|
7734
|
-
const plan2 =
|
|
8321
|
+
const tsConfigPath = path44.resolve("tsconfig.json");
|
|
8322
|
+
const plan2 = buildPlan2(files, tsConfigPath);
|
|
7735
8323
|
if (plan2.moves.length === 0) {
|
|
7736
|
-
console.log(
|
|
8324
|
+
console.log(chalk93.green("No restructuring needed"));
|
|
7737
8325
|
return;
|
|
7738
8326
|
}
|
|
7739
|
-
|
|
8327
|
+
displayPlan2(plan2);
|
|
7740
8328
|
if (options2.apply) {
|
|
7741
|
-
console.log(
|
|
8329
|
+
console.log(chalk93.bold("\nApplying changes..."));
|
|
7742
8330
|
executePlan(plan2);
|
|
7743
|
-
console.log(
|
|
8331
|
+
console.log(chalk93.green("\nRestructuring complete"));
|
|
7744
8332
|
} else {
|
|
7745
|
-
console.log(
|
|
8333
|
+
console.log(chalk93.dim("\nDry run. Use --apply to execute."));
|
|
7746
8334
|
}
|
|
7747
8335
|
}
|
|
7748
8336
|
|
|
7749
8337
|
// src/commands/registerRefactor.ts
|
|
7750
|
-
function
|
|
7751
|
-
|
|
7752
|
-
refactorCommand.command("check [pattern]").description("Check for files that exceed the maximum line count").option("--modified", "Check only staged and unstaged files").option("--staged", "Check only staged files").option("--unstaged", "Check only unstaged files").option(
|
|
8338
|
+
function registerCheck(parent) {
|
|
8339
|
+
parent.command("check [pattern]").description("Check for files that exceed the maximum line count").option("--modified", "Check only staged and unstaged files").option("--staged", "Check only staged files").option("--unstaged", "Check only unstaged files").option(
|
|
7753
8340
|
"--max-lines <number>",
|
|
7754
8341
|
"Maximum lines allowed per file (default: 100)",
|
|
7755
8342
|
Number.parseInt
|
|
7756
8343
|
).action(check);
|
|
7757
|
-
|
|
7758
|
-
|
|
8344
|
+
}
|
|
8345
|
+
function registerRename(parent) {
|
|
8346
|
+
const renameCommand = parent.command("rename").description("Rename files or symbols with automatic import updates");
|
|
7759
8347
|
renameCommand.command("file <source> <destination>").description("Rename/move a TypeScript file and update all imports").option("--apply", "Execute the rename (default: dry-run)").action(rename);
|
|
7760
8348
|
renameCommand.command("symbol <file> <oldName> <newName>").description(
|
|
7761
8349
|
"Rename a variable, function, class, or type across the project"
|
|
7762
8350
|
).option("--apply", "Execute the rename (default: dry-run)").action(renameSymbol);
|
|
7763
|
-
|
|
8351
|
+
}
|
|
8352
|
+
function registerRestructure(parent) {
|
|
8353
|
+
parent.command("restructure [pattern]").description(
|
|
7764
8354
|
"Analyze import graph and restructure tightly-coupled files into nested directories"
|
|
7765
8355
|
).option("--apply", "Execute the restructuring (default: dry-run)").option(
|
|
7766
8356
|
"--max-depth <number>",
|
|
@@ -7768,9 +8358,19 @@ function registerRefactor(program2) {
|
|
|
7768
8358
|
Number.parseInt
|
|
7769
8359
|
).action(restructure);
|
|
7770
8360
|
}
|
|
8361
|
+
function registerRefactor(program2) {
|
|
8362
|
+
const refactorCommand = program2.command("refactor").description("Run refactoring checks for code quality");
|
|
8363
|
+
registerCheck(refactorCommand);
|
|
8364
|
+
refactorCommand.command("extract <file> <functionName> <destination>").description(
|
|
8365
|
+
"Extract a function and its private dependencies to a new file"
|
|
8366
|
+
).option("--apply", "Execute the extraction (default: dry-run)").action(extract);
|
|
8367
|
+
refactorCommand.command("ignore <file>").description("Add a file to the refactor ignore list").action(ignore);
|
|
8368
|
+
registerRename(refactorCommand);
|
|
8369
|
+
registerRestructure(refactorCommand);
|
|
8370
|
+
}
|
|
7771
8371
|
|
|
7772
8372
|
// src/commands/seq/seqAuth.ts
|
|
7773
|
-
import
|
|
8373
|
+
import chalk95 from "chalk";
|
|
7774
8374
|
|
|
7775
8375
|
// src/commands/seq/loadConnections.ts
|
|
7776
8376
|
function loadConnections2() {
|
|
@@ -7799,11 +8399,11 @@ function setDefaultConnection(name) {
|
|
|
7799
8399
|
}
|
|
7800
8400
|
|
|
7801
8401
|
// src/commands/seq/promptConnection.ts
|
|
7802
|
-
import
|
|
8402
|
+
import chalk94 from "chalk";
|
|
7803
8403
|
async function promptConnection2(existingNames) {
|
|
7804
8404
|
const name = await promptInput("name", "Connection name:", "default");
|
|
7805
8405
|
if (existingNames.includes(name)) {
|
|
7806
|
-
console.error(
|
|
8406
|
+
console.error(chalk94.red(`Connection "${name}" already exists.`));
|
|
7807
8407
|
process.exit(1);
|
|
7808
8408
|
}
|
|
7809
8409
|
const url = await promptInput("url", "Seq URL:", "http://localhost:5341");
|
|
@@ -7815,32 +8415,32 @@ async function promptConnection2(existingNames) {
|
|
|
7815
8415
|
var seqAuth = createConnectionAuth({
|
|
7816
8416
|
load: loadConnections2,
|
|
7817
8417
|
save: saveConnections2,
|
|
7818
|
-
format: (c) => `${
|
|
8418
|
+
format: (c) => `${chalk95.bold(c.name)} ${c.url}`,
|
|
7819
8419
|
promptNew: promptConnection2,
|
|
7820
8420
|
onFirst: (c) => setDefaultConnection(c.name)
|
|
7821
8421
|
});
|
|
7822
8422
|
|
|
7823
8423
|
// src/commands/seq/seqQuery.ts
|
|
7824
|
-
import
|
|
8424
|
+
import chalk98 from "chalk";
|
|
7825
8425
|
|
|
7826
8426
|
// src/commands/seq/formatEvent.ts
|
|
7827
|
-
import
|
|
8427
|
+
import chalk96 from "chalk";
|
|
7828
8428
|
function levelColor(level) {
|
|
7829
8429
|
switch (level) {
|
|
7830
8430
|
case "Fatal":
|
|
7831
|
-
return
|
|
8431
|
+
return chalk96.bgRed.white;
|
|
7832
8432
|
case "Error":
|
|
7833
|
-
return
|
|
8433
|
+
return chalk96.red;
|
|
7834
8434
|
case "Warning":
|
|
7835
|
-
return
|
|
8435
|
+
return chalk96.yellow;
|
|
7836
8436
|
case "Information":
|
|
7837
|
-
return
|
|
8437
|
+
return chalk96.cyan;
|
|
7838
8438
|
case "Debug":
|
|
7839
|
-
return
|
|
8439
|
+
return chalk96.gray;
|
|
7840
8440
|
case "Verbose":
|
|
7841
|
-
return
|
|
8441
|
+
return chalk96.dim;
|
|
7842
8442
|
default:
|
|
7843
|
-
return
|
|
8443
|
+
return chalk96.white;
|
|
7844
8444
|
}
|
|
7845
8445
|
}
|
|
7846
8446
|
function levelAbbrev(level) {
|
|
@@ -7881,31 +8481,31 @@ function formatTimestamp(iso) {
|
|
|
7881
8481
|
function formatEvent(event) {
|
|
7882
8482
|
const color = levelColor(event.Level);
|
|
7883
8483
|
const abbrev = levelAbbrev(event.Level);
|
|
7884
|
-
const ts8 =
|
|
8484
|
+
const ts8 = chalk96.dim(formatTimestamp(event.Timestamp));
|
|
7885
8485
|
const msg = renderMessage(event);
|
|
7886
8486
|
const lines = [`${ts8} ${color(`[${abbrev}]`)} ${msg}`];
|
|
7887
8487
|
if (event.Exception) {
|
|
7888
8488
|
for (const line of event.Exception.split("\n")) {
|
|
7889
|
-
lines.push(
|
|
8489
|
+
lines.push(chalk96.red(` ${line}`));
|
|
7890
8490
|
}
|
|
7891
8491
|
}
|
|
7892
8492
|
return lines.join("\n");
|
|
7893
8493
|
}
|
|
7894
8494
|
|
|
7895
8495
|
// src/commands/seq/resolveConnection.ts
|
|
7896
|
-
import
|
|
8496
|
+
import chalk97 from "chalk";
|
|
7897
8497
|
function resolveConnection2(name) {
|
|
7898
8498
|
const connections = loadConnections2();
|
|
7899
8499
|
if (connections.length === 0) {
|
|
7900
8500
|
console.error(
|
|
7901
|
-
|
|
8501
|
+
chalk97.red("No Seq connections configured. Run 'assist seq auth' first.")
|
|
7902
8502
|
);
|
|
7903
8503
|
process.exit(1);
|
|
7904
8504
|
}
|
|
7905
8505
|
const target = name ?? getDefaultConnection() ?? connections[0].name;
|
|
7906
8506
|
const connection = connections.find((c) => c.name === target);
|
|
7907
8507
|
if (!connection) {
|
|
7908
|
-
console.error(
|
|
8508
|
+
console.error(chalk97.red(`Seq connection "${target}" not found.`));
|
|
7909
8509
|
process.exit(1);
|
|
7910
8510
|
}
|
|
7911
8511
|
return connection;
|
|
@@ -7925,12 +8525,12 @@ async function seqQuery(filter, options2) {
|
|
|
7925
8525
|
});
|
|
7926
8526
|
if (!response.ok) {
|
|
7927
8527
|
const body = await response.text();
|
|
7928
|
-
console.error(
|
|
8528
|
+
console.error(chalk98.red(`Seq returned ${response.status}: ${body}`));
|
|
7929
8529
|
process.exit(1);
|
|
7930
8530
|
}
|
|
7931
8531
|
const events = await response.json();
|
|
7932
8532
|
if (events.length === 0) {
|
|
7933
|
-
console.log(
|
|
8533
|
+
console.log(chalk98.yellow("No events found."));
|
|
7934
8534
|
return;
|
|
7935
8535
|
}
|
|
7936
8536
|
if (options2.json) {
|
|
@@ -7941,11 +8541,11 @@ async function seqQuery(filter, options2) {
|
|
|
7941
8541
|
for (const event of chronological) {
|
|
7942
8542
|
console.log(formatEvent(event));
|
|
7943
8543
|
}
|
|
7944
|
-
console.log(
|
|
8544
|
+
console.log(chalk98.dim(`
|
|
7945
8545
|
${events.length} events`));
|
|
7946
8546
|
if (events.length >= count) {
|
|
7947
8547
|
console.log(
|
|
7948
|
-
|
|
8548
|
+
chalk98.yellow(
|
|
7949
8549
|
`Results limited to ${count}. Use --count to retrieve more.`
|
|
7950
8550
|
)
|
|
7951
8551
|
);
|
|
@@ -7953,11 +8553,11 @@ ${events.length} events`));
|
|
|
7953
8553
|
}
|
|
7954
8554
|
|
|
7955
8555
|
// src/commands/seq/seqSetConnection.ts
|
|
7956
|
-
import
|
|
8556
|
+
import chalk99 from "chalk";
|
|
7957
8557
|
function seqSetConnection(name) {
|
|
7958
8558
|
const connections = loadConnections2();
|
|
7959
8559
|
if (!connections.find((c) => c.name === name)) {
|
|
7960
|
-
console.error(
|
|
8560
|
+
console.error(chalk99.red(`Connection "${name}" not found.`));
|
|
7961
8561
|
process.exit(1);
|
|
7962
8562
|
}
|
|
7963
8563
|
setDefaultConnection(name);
|
|
@@ -8496,14 +9096,14 @@ import {
|
|
|
8496
9096
|
import { dirname as dirname20, join as join29 } from "path";
|
|
8497
9097
|
|
|
8498
9098
|
// src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
|
|
8499
|
-
import
|
|
9099
|
+
import chalk100 from "chalk";
|
|
8500
9100
|
var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
|
|
8501
9101
|
function validateStagedContent(filename, content) {
|
|
8502
9102
|
const firstLine = content.split("\n")[0];
|
|
8503
9103
|
const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
|
|
8504
9104
|
if (!match) {
|
|
8505
9105
|
console.error(
|
|
8506
|
-
|
|
9106
|
+
chalk100.red(
|
|
8507
9107
|
`Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
|
|
8508
9108
|
)
|
|
8509
9109
|
);
|
|
@@ -8512,7 +9112,7 @@ function validateStagedContent(filename, content) {
|
|
|
8512
9112
|
const contentAfterLink = content.slice(firstLine.length).trim();
|
|
8513
9113
|
if (!contentAfterLink) {
|
|
8514
9114
|
console.error(
|
|
8515
|
-
|
|
9115
|
+
chalk100.red(
|
|
8516
9116
|
`Staged file ${filename} has no summary content after the transcript link.`
|
|
8517
9117
|
)
|
|
8518
9118
|
);
|
|
@@ -8905,7 +9505,7 @@ function registerVoice(program2) {
|
|
|
8905
9505
|
|
|
8906
9506
|
// src/commands/roam/auth.ts
|
|
8907
9507
|
import { randomBytes } from "crypto";
|
|
8908
|
-
import
|
|
9508
|
+
import chalk101 from "chalk";
|
|
8909
9509
|
|
|
8910
9510
|
// src/lib/openBrowser.ts
|
|
8911
9511
|
import { execSync as execSync36 } from "child_process";
|
|
@@ -9080,13 +9680,13 @@ async function auth() {
|
|
|
9080
9680
|
saveGlobalConfig(config);
|
|
9081
9681
|
const state = randomBytes(16).toString("hex");
|
|
9082
9682
|
console.log(
|
|
9083
|
-
|
|
9683
|
+
chalk101.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
9084
9684
|
);
|
|
9085
|
-
console.log(
|
|
9086
|
-
console.log(
|
|
9087
|
-
console.log(
|
|
9685
|
+
console.log(chalk101.white("http://localhost:14523/callback\n"));
|
|
9686
|
+
console.log(chalk101.blue("Opening browser for authorization..."));
|
|
9687
|
+
console.log(chalk101.dim("Waiting for authorization callback..."));
|
|
9088
9688
|
const { code, redirectUri } = await authorizeInBrowser(clientId, state);
|
|
9089
|
-
console.log(
|
|
9689
|
+
console.log(chalk101.dim("Exchanging code for tokens..."));
|
|
9090
9690
|
const tokens = await exchangeToken({
|
|
9091
9691
|
code,
|
|
9092
9692
|
clientId,
|
|
@@ -9102,7 +9702,7 @@ async function auth() {
|
|
|
9102
9702
|
};
|
|
9103
9703
|
saveGlobalConfig(config);
|
|
9104
9704
|
console.log(
|
|
9105
|
-
|
|
9705
|
+
chalk101.green("Roam credentials and tokens saved to ~/.assist.yml")
|
|
9106
9706
|
);
|
|
9107
9707
|
}
|
|
9108
9708
|
|
|
@@ -9323,7 +9923,7 @@ import { execSync as execSync38 } from "child_process";
|
|
|
9323
9923
|
import { existsSync as existsSync38, mkdirSync as mkdirSync13, unlinkSync as unlinkSync10, writeFileSync as writeFileSync27 } from "fs";
|
|
9324
9924
|
import { tmpdir as tmpdir6 } from "os";
|
|
9325
9925
|
import { join as join38, resolve as resolve5 } from "path";
|
|
9326
|
-
import
|
|
9926
|
+
import chalk102 from "chalk";
|
|
9327
9927
|
|
|
9328
9928
|
// src/commands/screenshot/captureWindowPs1.ts
|
|
9329
9929
|
var captureWindowPs1 = `
|
|
@@ -9474,22 +10074,22 @@ function screenshot(processName) {
|
|
|
9474
10074
|
const config = loadConfig();
|
|
9475
10075
|
const outputDir = resolve5(config.screenshot.outputDir);
|
|
9476
10076
|
const outputPath = buildOutputPath(outputDir, processName);
|
|
9477
|
-
console.log(
|
|
10077
|
+
console.log(chalk102.gray(`Capturing window for process "${processName}" ...`));
|
|
9478
10078
|
try {
|
|
9479
10079
|
runPowerShellScript(processName, outputPath);
|
|
9480
|
-
console.log(
|
|
10080
|
+
console.log(chalk102.green(`Screenshot saved: ${outputPath}`));
|
|
9481
10081
|
} catch (error) {
|
|
9482
10082
|
const msg = error instanceof Error ? error.message : String(error);
|
|
9483
|
-
console.error(
|
|
10083
|
+
console.error(chalk102.red(`Failed to capture screenshot: ${msg}`));
|
|
9484
10084
|
process.exit(1);
|
|
9485
10085
|
}
|
|
9486
10086
|
}
|
|
9487
10087
|
|
|
9488
10088
|
// src/commands/statusLine.ts
|
|
9489
|
-
import
|
|
10089
|
+
import chalk104 from "chalk";
|
|
9490
10090
|
|
|
9491
10091
|
// src/commands/buildLimitsSegment.ts
|
|
9492
|
-
import
|
|
10092
|
+
import chalk103 from "chalk";
|
|
9493
10093
|
var FIVE_HOUR_SECONDS = 5 * 3600;
|
|
9494
10094
|
var SEVEN_DAY_SECONDS = 7 * 86400;
|
|
9495
10095
|
function formatTimeLeft(resetsAt) {
|
|
@@ -9512,10 +10112,10 @@ function projectUsage(pct, resetsAt, windowSeconds) {
|
|
|
9512
10112
|
function colorizeRateLimit(pct, resetsAt, windowSeconds) {
|
|
9513
10113
|
const label2 = `${Math.round(pct)}%`;
|
|
9514
10114
|
const projected = projectUsage(pct, resetsAt, windowSeconds);
|
|
9515
|
-
if (projected == null) return
|
|
9516
|
-
if (projected > 100) return
|
|
9517
|
-
if (projected > 75) return
|
|
9518
|
-
return
|
|
10115
|
+
if (projected == null) return chalk103.green(label2);
|
|
10116
|
+
if (projected > 100) return chalk103.red(label2);
|
|
10117
|
+
if (projected > 75) return chalk103.yellow(label2);
|
|
10118
|
+
return chalk103.green(label2);
|
|
9519
10119
|
}
|
|
9520
10120
|
function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel) {
|
|
9521
10121
|
const timeLabel = resetsAt ? formatTimeLeft(resetsAt) : fallbackLabel;
|
|
@@ -9541,14 +10141,14 @@ function buildLimitsSegment(rateLimits) {
|
|
|
9541
10141
|
}
|
|
9542
10142
|
|
|
9543
10143
|
// src/commands/statusLine.ts
|
|
9544
|
-
|
|
10144
|
+
chalk104.level = 3;
|
|
9545
10145
|
function formatNumber(num) {
|
|
9546
10146
|
return num.toLocaleString("en-US");
|
|
9547
10147
|
}
|
|
9548
10148
|
function colorizePercent(pct) {
|
|
9549
10149
|
const label2 = `${Math.round(pct)}%`;
|
|
9550
|
-
if (pct > 80) return
|
|
9551
|
-
if (pct > 40) return
|
|
10150
|
+
if (pct > 80) return chalk104.red(label2);
|
|
10151
|
+
if (pct > 40) return chalk104.yellow(label2);
|
|
9552
10152
|
return label2;
|
|
9553
10153
|
}
|
|
9554
10154
|
async function statusLine() {
|
|
@@ -9563,29 +10163,29 @@ async function statusLine() {
|
|
|
9563
10163
|
}
|
|
9564
10164
|
|
|
9565
10165
|
// src/commands/sync.ts
|
|
9566
|
-
import * as
|
|
10166
|
+
import * as fs25 from "fs";
|
|
9567
10167
|
import * as os from "os";
|
|
9568
|
-
import * as
|
|
10168
|
+
import * as path47 from "path";
|
|
9569
10169
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
9570
10170
|
|
|
9571
10171
|
// src/commands/sync/syncClaudeMd.ts
|
|
9572
|
-
import * as
|
|
9573
|
-
import * as
|
|
9574
|
-
import
|
|
10172
|
+
import * as fs23 from "fs";
|
|
10173
|
+
import * as path45 from "path";
|
|
10174
|
+
import chalk105 from "chalk";
|
|
9575
10175
|
async function syncClaudeMd(claudeDir, targetBase) {
|
|
9576
|
-
const source =
|
|
9577
|
-
const target =
|
|
9578
|
-
const sourceContent =
|
|
9579
|
-
if (
|
|
9580
|
-
const targetContent =
|
|
10176
|
+
const source = path45.join(claudeDir, "CLAUDE.md");
|
|
10177
|
+
const target = path45.join(targetBase, "CLAUDE.md");
|
|
10178
|
+
const sourceContent = fs23.readFileSync(source, "utf-8");
|
|
10179
|
+
if (fs23.existsSync(target)) {
|
|
10180
|
+
const targetContent = fs23.readFileSync(target, "utf-8");
|
|
9581
10181
|
if (sourceContent !== targetContent) {
|
|
9582
10182
|
console.log(
|
|
9583
|
-
|
|
10183
|
+
chalk105.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
9584
10184
|
);
|
|
9585
10185
|
console.log();
|
|
9586
10186
|
printDiff(targetContent, sourceContent);
|
|
9587
10187
|
const confirm = await promptConfirm(
|
|
9588
|
-
|
|
10188
|
+
chalk105.red("Overwrite existing CLAUDE.md?"),
|
|
9589
10189
|
false
|
|
9590
10190
|
);
|
|
9591
10191
|
if (!confirm) {
|
|
@@ -9594,21 +10194,21 @@ async function syncClaudeMd(claudeDir, targetBase) {
|
|
|
9594
10194
|
}
|
|
9595
10195
|
}
|
|
9596
10196
|
}
|
|
9597
|
-
|
|
10197
|
+
fs23.copyFileSync(source, target);
|
|
9598
10198
|
console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
|
|
9599
10199
|
}
|
|
9600
10200
|
|
|
9601
10201
|
// src/commands/sync/syncSettings.ts
|
|
9602
|
-
import * as
|
|
9603
|
-
import * as
|
|
9604
|
-
import
|
|
10202
|
+
import * as fs24 from "fs";
|
|
10203
|
+
import * as path46 from "path";
|
|
10204
|
+
import chalk106 from "chalk";
|
|
9605
10205
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
9606
|
-
const source =
|
|
9607
|
-
const target =
|
|
9608
|
-
const sourceContent =
|
|
10206
|
+
const source = path46.join(claudeDir, "settings.json");
|
|
10207
|
+
const target = path46.join(targetBase, "settings.json");
|
|
10208
|
+
const sourceContent = fs24.readFileSync(source, "utf-8");
|
|
9609
10209
|
const mergedContent = JSON.stringify(JSON.parse(sourceContent), null, " ");
|
|
9610
|
-
if (
|
|
9611
|
-
const targetContent =
|
|
10210
|
+
if (fs24.existsSync(target)) {
|
|
10211
|
+
const targetContent = fs24.readFileSync(target, "utf-8");
|
|
9612
10212
|
const normalizedTarget = JSON.stringify(
|
|
9613
10213
|
JSON.parse(targetContent),
|
|
9614
10214
|
null,
|
|
@@ -9617,14 +10217,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
9617
10217
|
if (mergedContent !== normalizedTarget) {
|
|
9618
10218
|
if (!options2?.yes) {
|
|
9619
10219
|
console.log(
|
|
9620
|
-
|
|
10220
|
+
chalk106.yellow(
|
|
9621
10221
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
9622
10222
|
)
|
|
9623
10223
|
);
|
|
9624
10224
|
console.log();
|
|
9625
10225
|
printDiff(targetContent, mergedContent);
|
|
9626
10226
|
const confirm = await promptConfirm(
|
|
9627
|
-
|
|
10227
|
+
chalk106.red("Overwrite existing settings.json?"),
|
|
9628
10228
|
false
|
|
9629
10229
|
);
|
|
9630
10230
|
if (!confirm) {
|
|
@@ -9634,27 +10234,27 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
9634
10234
|
}
|
|
9635
10235
|
}
|
|
9636
10236
|
}
|
|
9637
|
-
|
|
10237
|
+
fs24.writeFileSync(target, mergedContent);
|
|
9638
10238
|
console.log("Copied settings.json to ~/.claude/settings.json");
|
|
9639
10239
|
}
|
|
9640
10240
|
|
|
9641
10241
|
// src/commands/sync.ts
|
|
9642
10242
|
var __filename4 = fileURLToPath7(import.meta.url);
|
|
9643
|
-
var __dirname7 =
|
|
10243
|
+
var __dirname7 = path47.dirname(__filename4);
|
|
9644
10244
|
async function sync(options2) {
|
|
9645
|
-
const claudeDir =
|
|
9646
|
-
const targetBase =
|
|
10245
|
+
const claudeDir = path47.join(__dirname7, "..", "claude");
|
|
10246
|
+
const targetBase = path47.join(os.homedir(), ".claude");
|
|
9647
10247
|
syncCommands(claudeDir, targetBase);
|
|
9648
10248
|
await syncSettings(claudeDir, targetBase, { yes: options2?.yes });
|
|
9649
10249
|
await syncClaudeMd(claudeDir, targetBase);
|
|
9650
10250
|
}
|
|
9651
10251
|
function syncCommands(claudeDir, targetBase) {
|
|
9652
|
-
const sourceDir =
|
|
9653
|
-
const targetDir =
|
|
9654
|
-
|
|
9655
|
-
const files =
|
|
10252
|
+
const sourceDir = path47.join(claudeDir, "commands");
|
|
10253
|
+
const targetDir = path47.join(targetBase, "commands");
|
|
10254
|
+
fs25.mkdirSync(targetDir, { recursive: true });
|
|
10255
|
+
const files = fs25.readdirSync(sourceDir);
|
|
9656
10256
|
for (const file of files) {
|
|
9657
|
-
|
|
10257
|
+
fs25.copyFileSync(path47.join(sourceDir, file), path47.join(targetDir, file));
|
|
9658
10258
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
9659
10259
|
}
|
|
9660
10260
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
@@ -9662,15 +10262,15 @@ function syncCommands(claudeDir, targetBase) {
|
|
|
9662
10262
|
|
|
9663
10263
|
// src/commands/update.ts
|
|
9664
10264
|
import { execSync as execSync39 } from "child_process";
|
|
9665
|
-
import * as
|
|
10265
|
+
import * as path48 from "path";
|
|
9666
10266
|
function isGlobalNpmInstall(dir) {
|
|
9667
10267
|
try {
|
|
9668
|
-
const resolved =
|
|
9669
|
-
if (resolved.split(
|
|
10268
|
+
const resolved = path48.resolve(dir);
|
|
10269
|
+
if (resolved.split(path48.sep).includes("node_modules")) {
|
|
9670
10270
|
return true;
|
|
9671
10271
|
}
|
|
9672
10272
|
const globalPrefix = execSync39("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
9673
|
-
return resolved.toLowerCase().startsWith(
|
|
10273
|
+
return resolved.toLowerCase().startsWith(path48.resolve(globalPrefix).toLowerCase());
|
|
9674
10274
|
} catch {
|
|
9675
10275
|
return false;
|
|
9676
10276
|
}
|