rush-ai 0.3.1 → 0.4.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/dist/index.js CHANGED
@@ -33,7 +33,7 @@ import {
33
33
  } from "./chunk-UJLVFMWM.js";
34
34
 
35
35
  // src/index.ts
36
- import chalk6 from "chalk";
36
+ import chalk8 from "chalk";
37
37
  import { Command } from "commander";
38
38
 
39
39
  // src/output/formatters/csv.ts
@@ -286,7 +286,7 @@ function getApiBaseUrl() {
286
286
  async function loginViaBrowser(jsonMode) {
287
287
  const baseUrl = getApiBaseUrl();
288
288
  const state = randomBytes(16).toString("hex");
289
- return new Promise((resolve7, reject) => {
289
+ return new Promise((resolve9, reject) => {
290
290
  const server = createServer(async (req, res) => {
291
291
  try {
292
292
  const url = new URL(req.url ?? "/", "http://localhost");
@@ -299,12 +299,13 @@ async function loginViaBrowser(jsonMode) {
299
299
  const returnedState = url.searchParams.get("state");
300
300
  const error = url.searchParams.get("error");
301
301
  if (error) {
302
- const desc = url.searchParams.get("error_description") ?? "Authentication failed";
302
+ const desc = url.searchParams.get("error_description") ?? "\u8BA4\u8BC1\u5931\u8D25";
303
303
  res.writeHead(200, { "Content-Type": "text/html" });
304
304
  res.end(
305
305
  getHtmlPage(
306
- "Authentication Failed",
307
- `Error: ${desc}. You can close this window.`
306
+ "\u8BA4\u8BC1\u5931\u8D25",
307
+ `\u9519\u8BEF\uFF1A${desc}\u3002\u4F60\u53EF\u4EE5\u5173\u95ED\u6B64\u9875\u9762\u3002`,
308
+ baseUrl
308
309
  )
309
310
  );
310
311
  server.close();
@@ -315,8 +316,9 @@ async function loginViaBrowser(jsonMode) {
315
316
  res.writeHead(400, { "Content-Type": "text/html" });
316
317
  res.end(
317
318
  getHtmlPage(
318
- "Authentication Failed",
319
- "Invalid callback parameters. You can close this window."
319
+ "\u8BA4\u8BC1\u5931\u8D25",
320
+ "\u56DE\u8C03\u53C2\u6570\u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u3002\u4F60\u53EF\u4EE5\u5173\u95ED\u6B64\u9875\u9762\u3002",
321
+ baseUrl
320
322
  )
321
323
  );
322
324
  server.close();
@@ -336,8 +338,9 @@ async function loginViaBrowser(jsonMode) {
336
338
  res.writeHead(200, { "Content-Type": "text/html" });
337
339
  res.end(
338
340
  getHtmlPage(
339
- "Authentication Failed",
340
- "Token exchange failed. You can close this window."
341
+ "\u8BA4\u8BC1\u5931\u8D25",
342
+ "Token \u4EA4\u6362\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002\u4F60\u53EF\u4EE5\u5173\u95ED\u6B64\u9875\u9762\u3002",
343
+ baseUrl
341
344
  )
342
345
  );
343
346
  server.close();
@@ -356,8 +359,10 @@ async function loginViaBrowser(jsonMode) {
356
359
  res.writeHead(200, { "Content-Type": "text/html" });
357
360
  res.end(
358
361
  getHtmlPage(
359
- "Authentication Successful",
360
- "You are now logged in. You can close this window and return to the terminal."
362
+ "\u8BA4\u8BC1\u6210\u529F",
363
+ "\u4F60\u5DF2\u6210\u529F\u767B\u5F55\uFF0C\u53EF\u4EE5\u5173\u95ED\u6B64\u9875\u9762\u5E76\u8FD4\u56DE\u7EC8\u7AEF\u3002",
364
+ baseUrl,
365
+ expiresAt ?? void 0
361
366
  )
362
367
  );
363
368
  server.close();
@@ -377,7 +382,7 @@ async function loginViaBrowser(jsonMode) {
377
382
  );
378
383
  }
379
384
  }
380
- resolve7();
385
+ resolve9();
381
386
  } catch (err) {
382
387
  server.close();
383
388
  reject(
@@ -415,21 +420,37 @@ async function loginViaBrowser(jsonMode) {
415
420
  function escapeHtml(text) {
416
421
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
417
422
  }
418
- function getHtmlPage(title, message) {
423
+ function getHtmlPage(title, message, serverUrl, expiresAt) {
424
+ const metaLines = [
425
+ `<span class="label">\u670D\u52A1\u5668</span> <span class="value">${escapeHtml(serverUrl)}</span>`
426
+ ];
427
+ if (expiresAt) {
428
+ metaLines.push(
429
+ `<span class="label">\u6709\u6548\u671F\u81F3</span> <span class="value">${escapeHtml(new Date(expiresAt).toLocaleString("zh-CN"))}</span>`
430
+ );
431
+ }
432
+ const metaHtml = metaLines.map((line) => `<div class="meta-row">${line}</div>`).join("\n ");
419
433
  return `<!DOCTYPE html>
420
- <html>
421
- <head><title>Rush CLI - ${escapeHtml(title)}</title>
434
+ <html lang="zh-CN">
435
+ <head><meta charset="utf-8"><title>Rush CLI - ${escapeHtml(title)}</title>
422
436
  <style>
423
- body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #f5f5f5; }
424
- .card { background: white; border-radius: 12px; padding: 40px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); text-align: center; max-width: 400px; }
425
- h1 { color: #333; font-size: 1.5em; }
437
+ body { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #f5f5f5; }
438
+ .card { background: white; border-radius: 12px; padding: 40px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); text-align: center; max-width: 420px; }
439
+ h1 { color: #333; font-size: 1.5em; margin-bottom: 8px; }
426
440
  p { color: #666; line-height: 1.6; }
441
+ .meta { margin-top: 16px; padding-top: 16px; border-top: 1px solid #eee; text-align: left; }
442
+ .meta-row { color: #888; font-size: 0.85em; line-height: 2; }
443
+ .meta-row .label { color: #999; }
444
+ .meta-row .value { color: #555; font-family: "SF Mono", Menlo, monospace; }
427
445
  </style>
428
446
  </head>
429
447
  <body>
430
448
  <div class="card">
431
449
  <h1>${escapeHtml(title)}</h1>
432
450
  <p>${escapeHtml(message)}</p>
451
+ <div class="meta">
452
+ ${metaHtml}
453
+ </div>
433
454
  </div>
434
455
  </body>
435
456
  </html>`;
@@ -571,11 +592,458 @@ function registerAuthCommand(program) {
571
592
  });
572
593
  }
573
594
 
595
+ // src/commands/check/index.ts
596
+ import { resolve } from "path";
597
+ import chalk2 from "chalk";
598
+
599
+ // src/commands/check/checks/database.ts
600
+ import { existsSync, readFileSync } from "fs";
601
+ import { join } from "path";
602
+ var PLACEHOLDER_PATTERNS = [
603
+ "your-",
604
+ "placeholder",
605
+ "xxx",
606
+ "example",
607
+ "<",
608
+ ">",
609
+ "${",
610
+ "todo"
611
+ ];
612
+ function isPlaceholder(value) {
613
+ const lower = value.toLowerCase();
614
+ return PLACEHOLDER_PATTERNS.some((p) => lower.includes(p));
615
+ }
616
+ function parseEnvFile(filePath) {
617
+ if (!existsSync(filePath)) return {};
618
+ try {
619
+ const content = readFileSync(filePath, "utf-8");
620
+ const vars = {};
621
+ for (const line of content.split("\n")) {
622
+ const trimmed = line.trim();
623
+ if (!trimmed || trimmed.startsWith("#")) continue;
624
+ const match = trimmed.match(/^([^=]+)=(.*)$/);
625
+ if (match) vars[match[1]] = match[2];
626
+ }
627
+ return vars;
628
+ } catch {
629
+ return {};
630
+ }
631
+ }
632
+ function checkDatabase(projectPath, framework) {
633
+ const defaultInfo = {
634
+ detected: false,
635
+ type: null,
636
+ autoCreate: false
637
+ };
638
+ if (framework !== "nextjs") {
639
+ return {
640
+ checks: [],
641
+ info: defaultInfo
642
+ };
643
+ }
644
+ const checks = [];
645
+ const envVars = parseEnvFile(join(projectPath, ".env.local"));
646
+ const postgresUrl = envVars.USER_POSTGRESQL_URL;
647
+ if (postgresUrl && !isPlaceholder(postgresUrl) && (postgresUrl.startsWith("postgresql://") || postgresUrl.startsWith("postgres://"))) {
648
+ checks.push({
649
+ name: "database_config",
650
+ severity: "info",
651
+ message: "Valid PostgreSQL config found in .env.local \u2014 Rush will use your existing database"
652
+ });
653
+ return {
654
+ checks,
655
+ info: { detected: true, type: "postgresql", autoCreate: false }
656
+ };
657
+ }
658
+ const supabaseUrl = envVars.SUPABASE_URL;
659
+ const supabaseKey = envVars.SUPABASE_SERVICE_ROLE_KEY || envVars.SUPABASE_KEY;
660
+ if (supabaseUrl && supabaseKey && !isPlaceholder(supabaseUrl) && !isPlaceholder(supabaseKey) && supabaseUrl.startsWith("https://") && supabaseKey.length > 20) {
661
+ checks.push({
662
+ name: "database_config",
663
+ severity: "info",
664
+ message: "Valid Supabase config found in .env.local \u2014 Rush will use your existing database"
665
+ });
666
+ return {
667
+ checks,
668
+ info: { detected: true, type: "supabase", autoCreate: false }
669
+ };
670
+ }
671
+ const hasPlaceholder = postgresUrl && isPlaceholder(postgresUrl) || supabaseUrl && isPlaceholder(supabaseUrl);
672
+ if (hasPlaceholder) {
673
+ checks.push({
674
+ name: "database_config",
675
+ severity: "warning",
676
+ message: "Database env vars in .env.local contain placeholder values \u2014 Rush will auto-create a Supabase database and overwrite them",
677
+ fix: "Either set real database credentials or remove the placeholder values"
678
+ });
679
+ } else {
680
+ checks.push({
681
+ name: "database_config",
682
+ severity: "info",
683
+ message: "No database config detected \u2014 Rush will auto-create a Supabase database for this Next.js project"
684
+ });
685
+ }
686
+ return {
687
+ checks,
688
+ info: { detected: false, type: null, autoCreate: true }
689
+ };
690
+ }
691
+
692
+ // src/commands/check/checks/framework.ts
693
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
694
+ import { join as join2 } from "path";
695
+ var NEXT_CONFIG_FILES = [
696
+ "next.config.js",
697
+ "next.config.ts",
698
+ "next.config.mjs"
699
+ ];
700
+ var VITE_CONFIG_FILES = ["vite.config.ts", "vite.config.js"];
701
+ function detectFramework(projectPath) {
702
+ const pkgPath = join2(projectPath, "package.json");
703
+ if (!existsSync2(pkgPath)) return "unknown";
704
+ try {
705
+ const raw = JSON.parse(readFileSync2(pkgPath, "utf-8"));
706
+ const deps = { ...raw.dependencies, ...raw.devDependencies };
707
+ if (deps.next) return "nextjs";
708
+ if (deps.vite || VITE_CONFIG_FILES.some((f) => existsSync2(join2(projectPath, f)))) {
709
+ return "vite";
710
+ }
711
+ return "unknown";
712
+ } catch {
713
+ return "unknown";
714
+ }
715
+ }
716
+ function checkFramework(projectPath) {
717
+ const checks = [];
718
+ const framework = detectFramework(projectPath);
719
+ checks.push({
720
+ name: "framework_detected",
721
+ severity: "info",
722
+ message: `Detected framework: ${framework}`
723
+ });
724
+ if (framework === "nextjs") {
725
+ const hasConfig = NEXT_CONFIG_FILES.some(
726
+ (f) => existsSync2(join2(projectPath, f))
727
+ );
728
+ if (!hasConfig) {
729
+ checks.push({
730
+ name: "config_file",
731
+ severity: "warning",
732
+ message: "No next.config.js/ts/mjs found",
733
+ fix: "Create a next.config.js file"
734
+ });
735
+ }
736
+ } else if (framework === "vite") {
737
+ const hasConfig = VITE_CONFIG_FILES.some(
738
+ (f) => existsSync2(join2(projectPath, f))
739
+ );
740
+ if (hasConfig) {
741
+ checks.push({
742
+ name: "config_file",
743
+ severity: "info",
744
+ message: "Vite config file found"
745
+ });
746
+ }
747
+ if (existsSync2(join2(projectPath, "index.html"))) {
748
+ checks.push({
749
+ name: "index_html",
750
+ severity: "info",
751
+ message: "index.html found (Vite entry point)"
752
+ });
753
+ } else {
754
+ checks.push({
755
+ name: "index_html",
756
+ severity: "warning",
757
+ message: "index.html not found \u2014 Vite projects typically need this as entry point",
758
+ fix: "Create an index.html at the project root"
759
+ });
760
+ }
761
+ }
762
+ return checks;
763
+ }
764
+
765
+ // src/commands/check/checks/lock-file.ts
766
+ import { existsSync as existsSync3 } from "fs";
767
+ import { join as join3 } from "path";
768
+ function detectPackageManager(projectPath) {
769
+ if (existsSync3(join3(projectPath, "pnpm-lock.yaml"))) return "pnpm";
770
+ if (existsSync3(join3(projectPath, "yarn.lock"))) return "yarn";
771
+ if (existsSync3(join3(projectPath, "package-lock.json"))) return "npm";
772
+ return null;
773
+ }
774
+ function checkLockFile(projectPath) {
775
+ const checks = [];
776
+ const pm = detectPackageManager(projectPath);
777
+ if (pm) {
778
+ checks.push({
779
+ name: "lock_file_exists",
780
+ severity: "info",
781
+ message: `Package manager: ${pm}`
782
+ });
783
+ } else {
784
+ checks.push({
785
+ name: "lock_file_exists",
786
+ severity: "warning",
787
+ message: "No lock file found (pnpm-lock.yaml, yarn.lock, or package-lock.json)",
788
+ fix: "Run `npm install` to generate package-lock.json",
789
+ autoFixable: true
790
+ });
791
+ }
792
+ return checks;
793
+ }
794
+
795
+ // src/commands/check/checks/package-json.ts
796
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
797
+ import { join as join4 } from "path";
798
+ function readPackageJson(projectPath) {
799
+ const pkgPath = join4(projectPath, "package.json");
800
+ if (!existsSync4(pkgPath)) {
801
+ return { exists: false };
802
+ }
803
+ try {
804
+ const raw = JSON.parse(readFileSync3(pkgPath, "utf-8"));
805
+ return {
806
+ exists: true,
807
+ scripts: raw.scripts,
808
+ dependencies: raw.dependencies,
809
+ devDependencies: raw.devDependencies
810
+ };
811
+ } catch {
812
+ return { exists: false };
813
+ }
814
+ }
815
+ function checkPackageJson(projectPath) {
816
+ const checks = [];
817
+ const pkg = readPackageJson(projectPath);
818
+ if (!pkg.exists) {
819
+ checks.push({
820
+ name: "package_json_exists",
821
+ severity: "error",
822
+ message: "package.json not found",
823
+ fix: "Run `npm init -y` to create one"
824
+ });
825
+ return checks;
826
+ }
827
+ if (!pkg.scripts?.dev) {
828
+ checks.push({
829
+ name: "scripts_dev",
830
+ severity: "error",
831
+ message: 'Missing "dev" script in package.json',
832
+ fix: 'Add a dev script (e.g., "dev": "vite" or "dev": "next dev")',
833
+ autoFixable: true
834
+ });
835
+ }
836
+ if (!pkg.scripts?.build) {
837
+ checks.push({
838
+ name: "scripts_build",
839
+ severity: "warning",
840
+ message: 'Missing "build" script in package.json \u2014 deployment may skip build step',
841
+ fix: 'Add a build script (e.g., "build": "vite build" or "build": "next build")',
842
+ autoFixable: true
843
+ });
844
+ }
845
+ if (pkg.scripts?.build) {
846
+ const build = pkg.scripts.build;
847
+ if (/^(?:vue-)?tsc\s.*&&/.test(build)) {
848
+ checks.push({
849
+ name: "build_tsc_prefix",
850
+ severity: "info",
851
+ message: `Build script starts with tsc: "${build}" \u2014 Rush will auto-strip the tsc check during build`
852
+ });
853
+ }
854
+ }
855
+ return checks;
856
+ }
857
+
858
+ // src/commands/check/checks/port-env.ts
859
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
860
+ import { join as join5 } from "path";
861
+ var VITE_CONFIG_FILES2 = ["vite.config.ts", "vite.config.js"];
862
+ function readDevScript(projectPath) {
863
+ const pkgPath = join5(projectPath, "package.json");
864
+ if (!existsSync5(pkgPath)) return null;
865
+ try {
866
+ const raw = JSON.parse(readFileSync4(pkgPath, "utf-8"));
867
+ return raw.scripts?.dev ?? null;
868
+ } catch {
869
+ return null;
870
+ }
871
+ }
872
+ function viteConfigReadsPort(projectPath) {
873
+ for (const file of VITE_CONFIG_FILES2) {
874
+ const filePath = join5(projectPath, file);
875
+ if (!existsSync5(filePath)) continue;
876
+ try {
877
+ const content = readFileSync4(filePath, "utf-8");
878
+ if (/process\.env\.PORT/.test(content)) return true;
879
+ if (/env\s*\(\s*['"]PORT['"]\s*\)/.test(content)) return true;
880
+ } catch {
881
+ }
882
+ }
883
+ return false;
884
+ }
885
+ function devScriptReadsPort(devScript) {
886
+ if (/\$PORT|\$\{PORT\}|%PORT%/.test(devScript)) return true;
887
+ if (/--port\s+\$/.test(devScript)) return true;
888
+ return false;
889
+ }
890
+ function checkPortEnv(projectPath, framework) {
891
+ const checks = [];
892
+ if (framework === "nextjs") {
893
+ checks.push({
894
+ name: "port_env",
895
+ severity: "info",
896
+ message: "Next.js natively supports PORT env \u2014 no config change needed"
897
+ });
898
+ return checks;
899
+ }
900
+ if (framework === "vite") {
901
+ if (viteConfigReadsPort(projectPath)) {
902
+ checks.push({
903
+ name: "port_env",
904
+ severity: "info",
905
+ message: "Vite config reads process.env.PORT"
906
+ });
907
+ return checks;
908
+ }
909
+ const devScript2 = readDevScript(projectPath);
910
+ if (devScript2 && devScriptReadsPort(devScript2)) {
911
+ checks.push({
912
+ name: "port_env",
913
+ severity: "info",
914
+ message: "Dev script references PORT"
915
+ });
916
+ return checks;
917
+ }
918
+ checks.push({
919
+ name: "port_env",
920
+ severity: "error",
921
+ message: "Vite config does not read process.env.PORT \u2014 Rush Pod passes PORT=8000",
922
+ fix: 'Add `server: { port: parseInt(process.env.PORT || "5173") }` to vite.config.ts',
923
+ autoFixable: true
924
+ });
925
+ return checks;
926
+ }
927
+ const devScript = readDevScript(projectPath);
928
+ if (devScript && devScriptReadsPort(devScript)) {
929
+ checks.push({
930
+ name: "port_env",
931
+ severity: "info",
932
+ message: "Dev script references PORT"
933
+ });
934
+ } else {
935
+ checks.push({
936
+ name: "port_env",
937
+ severity: "error",
938
+ message: "Dev server must read PORT env var \u2014 Rush Pod passes PORT=8000",
939
+ fix: "Ensure your dev server reads process.env.PORT or accepts --port $PORT",
940
+ autoFixable: false
941
+ });
942
+ }
943
+ return checks;
944
+ }
945
+
946
+ // src/commands/check/index.ts
947
+ function severityIcon(severity) {
948
+ switch (severity) {
949
+ case "error":
950
+ return chalk2.red("\u2717");
951
+ case "warning":
952
+ return chalk2.yellow("!");
953
+ case "info":
954
+ return chalk2.dim("\xB7");
955
+ }
956
+ }
957
+ function displayResults(report) {
958
+ output.newline();
959
+ output.log(chalk2.bold(" Rush Deploy Check"));
960
+ output.log(
961
+ chalk2.dim(
962
+ ` Framework: ${report.framework} | Package Manager: ${report.packageManager ?? "unknown"}`
963
+ )
964
+ );
965
+ output.newline();
966
+ const grouped = {
967
+ error: report.checks.filter((c) => c.severity === "error"),
968
+ warning: report.checks.filter((c) => c.severity === "warning"),
969
+ info: report.checks.filter((c) => c.severity === "info")
970
+ };
971
+ for (const [severity, checks] of Object.entries(grouped)) {
972
+ if (checks.length === 0) continue;
973
+ for (const check of checks) {
974
+ const icon = severityIcon(check.severity);
975
+ output.log(` ${icon} ${check.message}`);
976
+ if (check.fix && severity !== "info") {
977
+ output.log(chalk2.dim(` \u2192 ${check.fix}`));
978
+ }
979
+ }
980
+ }
981
+ output.newline();
982
+ const { errors, warnings } = report.summary;
983
+ const parts = [];
984
+ if (errors > 0) parts.push(chalk2.red(`${errors} errors`));
985
+ if (warnings > 0) parts.push(chalk2.yellow(`${warnings} warnings`));
986
+ if (errors === 0 && warnings === 0)
987
+ parts.push(chalk2.green("All checks passed"));
988
+ output.log(` ${parts.join(", ")}`);
989
+ if (report.deployable) {
990
+ output.newline();
991
+ output.log(
992
+ chalk2.green(" Ready to deploy! Run `rush-ai task deploy` to proceed.")
993
+ );
994
+ } else {
995
+ output.newline();
996
+ output.log(chalk2.red(" Fix the errors above before deploying."));
997
+ }
998
+ output.newline();
999
+ }
1000
+ function runChecks(projectPath) {
1001
+ const framework = detectFramework(projectPath);
1002
+ const packageManager = detectPackageManager(projectPath);
1003
+ const checks = [
1004
+ ...checkPackageJson(projectPath),
1005
+ ...checkFramework(projectPath),
1006
+ ...checkLockFile(projectPath),
1007
+ ...checkPortEnv(projectPath, framework)
1008
+ ];
1009
+ const { checks: dbChecks, info: dbInfo } = checkDatabase(
1010
+ projectPath,
1011
+ framework
1012
+ );
1013
+ checks.push(...dbChecks);
1014
+ const errors = checks.filter((c) => c.severity === "error").length;
1015
+ const warnings = checks.filter((c) => c.severity === "warning").length;
1016
+ const info = checks.filter((c) => c.severity === "info").length;
1017
+ return {
1018
+ framework,
1019
+ packageManager,
1020
+ database: dbInfo,
1021
+ checks,
1022
+ summary: { errors, warnings, info },
1023
+ deployable: errors === 0
1024
+ };
1025
+ }
1026
+ function registerCheckCommand(program) {
1027
+ program.command("check").description("Check if the current project is ready for Rush deployment").option("-p, --path <dir>", "Project directory to check", ".").action(async (opts) => {
1028
+ const projectPath = resolve(opts.path);
1029
+ const jsonMode = program.opts().json;
1030
+ const report = runChecks(projectPath);
1031
+ if (jsonMode) {
1032
+ output.log(JSON.stringify(report, null, 2));
1033
+ } else {
1034
+ displayResults(report);
1035
+ }
1036
+ if (!report.deployable) {
1037
+ process.exit(1);
1038
+ }
1039
+ });
1040
+ }
1041
+
574
1042
  // src/commands/completion/index.ts
575
- import { existsSync } from "fs";
1043
+ import { existsSync as existsSync6 } from "fs";
576
1044
  import { appendFile, mkdir, readFile, writeFile } from "fs/promises";
577
1045
  import { homedir } from "os";
578
- import { basename, dirname, join } from "path";
1046
+ import { basename, dirname, join as join6 } from "path";
579
1047
  var MARKER_BEGIN = "###-begin-rush-ai-completion-###";
580
1048
  var MARKER_END = "###-end-rush-ai-completion-###";
581
1049
  var COMPLETION_SCRIPTS = {
@@ -644,11 +1112,11 @@ function getShellRcFile(shell) {
644
1112
  const home = homedir();
645
1113
  switch (shell) {
646
1114
  case "bash":
647
- return existsSync(join(home, ".bashrc")) ? join(home, ".bashrc") : join(home, ".bash_profile");
1115
+ return existsSync6(join6(home, ".bashrc")) ? join6(home, ".bashrc") : join6(home, ".bash_profile");
648
1116
  case "zsh":
649
- return join(home, ".zshrc");
1117
+ return join6(home, ".zshrc");
650
1118
  case "fish":
651
- return join(home, ".config", "fish", "completions", "rush-ai.fish");
1119
+ return join6(home, ".config", "fish", "completions", "rush-ai.fish");
652
1120
  default:
653
1121
  throw new Error(`Unsupported shell: ${shell}`);
654
1122
  }
@@ -904,7 +1372,7 @@ function registerConfigCommand(program) {
904
1372
  }
905
1373
 
906
1374
  // src/commands/doctor/index.ts
907
- import chalk2 from "chalk";
1375
+ import chalk3 from "chalk";
908
1376
 
909
1377
  // src/commands/doctor/checks/auth.ts
910
1378
  var checkAuth = async () => {
@@ -1019,11 +1487,11 @@ var checkAuth = async () => {
1019
1487
  };
1020
1488
 
1021
1489
  // src/commands/doctor/checks/config.ts
1022
- import { accessSync, constants, existsSync as existsSync2, mkdirSync } from "fs";
1490
+ import { accessSync, constants, existsSync as existsSync7, mkdirSync } from "fs";
1023
1491
  var checkConfig = async () => {
1024
1492
  const checks = [];
1025
1493
  const configDir = getConfigDir();
1026
- const dirExists = existsSync2(configDir);
1494
+ const dirExists = existsSync7(configDir);
1027
1495
  let writable = false;
1028
1496
  if (dirExists) {
1029
1497
  try {
@@ -1273,18 +1741,18 @@ var checkEnvironment = async () => {
1273
1741
  };
1274
1742
 
1275
1743
  // src/commands/doctor/checks/plugins.ts
1276
- import { existsSync as existsSync3, readFileSync, writeFileSync } from "fs";
1744
+ import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync } from "fs";
1277
1745
  import { homedir as homedir2 } from "os";
1278
- import { resolve } from "path";
1279
- var PLUGINS_FILE = resolve(homedir2(), ".rush", "plugins", "installed.json");
1746
+ import { resolve as resolve2 } from "path";
1747
+ var PLUGINS_FILE = resolve2(homedir2(), ".rush", "plugins", "installed.json");
1280
1748
  var MCP_CONFIG_PATHS = {
1281
- "claude-code": resolve(homedir2(), ".claude", "settings.json"),
1282
- cursor: resolve(homedir2(), ".cursor", "mcp.json")
1749
+ "claude-code": resolve2(homedir2(), ".claude", "settings.json"),
1750
+ cursor: resolve2(homedir2(), ".cursor", "mcp.json")
1283
1751
  };
1284
1752
  function safeReadJson(filePath, fallback) {
1285
- if (!existsSync3(filePath)) return fallback;
1753
+ if (!existsSync8(filePath)) return fallback;
1286
1754
  try {
1287
- return JSON.parse(readFileSync(filePath, "utf-8"));
1755
+ return JSON.parse(readFileSync5(filePath, "utf-8"));
1288
1756
  } catch {
1289
1757
  return fallback;
1290
1758
  }
@@ -1314,7 +1782,7 @@ function checkMcpForPlugin(name, manifest) {
1314
1782
  value: `Installed (v${manifest.version}), unknown type: ${manifest.type}`
1315
1783
  };
1316
1784
  }
1317
- if (!existsSync3(configPath)) {
1785
+ if (!existsSync8(configPath)) {
1318
1786
  return {
1319
1787
  name: `plugin_${name}`,
1320
1788
  group: "Plugins",
@@ -1328,7 +1796,7 @@ function checkMcpForPlugin(name, manifest) {
1328
1796
  };
1329
1797
  }
1330
1798
  try {
1331
- const config = JSON.parse(readFileSync(configPath, "utf-8"));
1799
+ const config = JSON.parse(readFileSync5(configPath, "utf-8"));
1332
1800
  const servers = config.mcpServers;
1333
1801
  if (!servers || !("rush" in servers)) {
1334
1802
  return {
@@ -1431,17 +1899,17 @@ function getSummary(checks) {
1431
1899
  function statusIcon(status) {
1432
1900
  switch (status) {
1433
1901
  case "pass":
1434
- return chalk2.green("\u2713");
1902
+ return chalk3.green("\u2713");
1435
1903
  case "warn":
1436
- return chalk2.yellow("!");
1904
+ return chalk3.yellow("!");
1437
1905
  case "fail":
1438
- return chalk2.red("\u2717");
1906
+ return chalk3.red("\u2717");
1439
1907
  case "info":
1440
- return chalk2.dim("\xB7");
1908
+ return chalk3.dim("\xB7");
1441
1909
  }
1442
1910
  }
1443
1911
  var LABEL_WIDTH = 20;
1444
- function displayResults(checks) {
1912
+ function displayResults2(checks) {
1445
1913
  const groups = /* @__PURE__ */ new Map();
1446
1914
  for (const check of checks) {
1447
1915
  const existing = groups.get(check.group) ?? [];
@@ -1450,7 +1918,7 @@ function displayResults(checks) {
1450
1918
  }
1451
1919
  output.newline();
1452
1920
  for (const [groupName, groupChecks] of groups) {
1453
- output.log(` ${chalk2.bold(groupName)}`);
1921
+ output.log(` ${chalk3.bold(groupName)}`);
1454
1922
  for (const check of groupChecks) {
1455
1923
  const icon = statusIcon(check.status);
1456
1924
  const label = check.label.padEnd(LABEL_WIDTH);
@@ -1460,21 +1928,21 @@ function displayResults(checks) {
1460
1928
  }
1461
1929
  const summary = getSummary(checks);
1462
1930
  const parts = [];
1463
- parts.push(chalk2.green(`${summary.pass} passed`));
1464
- if (summary.warn > 0) parts.push(chalk2.yellow(`${summary.warn} warnings`));
1465
- if (summary.fail > 0) parts.push(chalk2.red(`${summary.fail} failed`));
1931
+ parts.push(chalk3.green(`${summary.pass} passed`));
1932
+ if (summary.warn > 0) parts.push(chalk3.yellow(`${summary.warn} warnings`));
1933
+ if (summary.fail > 0) parts.push(chalk3.red(`${summary.fail} failed`));
1466
1934
  if (summary.warn === 0 && summary.fail === 0) {
1467
1935
  parts.push("0 warnings");
1468
1936
  parts.push("0 failed");
1469
1937
  }
1470
- output.log(` ${chalk2.bold("Summary:")} ${parts.join(", ")}`);
1938
+ output.log(` ${chalk3.bold("Summary:")} ${parts.join(", ")}`);
1471
1939
  if (summary.fail > 0 || summary.warn > 0) {
1472
1940
  const fixable = checks.filter(
1473
1941
  (c) => c.status !== "pass" && c.status !== "info" && c.autoFix
1474
1942
  );
1475
1943
  if (fixable.length > 0) {
1476
1944
  output.log(
1477
- chalk2.dim(` Run \`rush-ai doctor --fix\` to attempt auto-fixes.`)
1945
+ chalk3.dim(` Run \`rush-ai doctor --fix\` to attempt auto-fixes.`)
1478
1946
  );
1479
1947
  }
1480
1948
  }
@@ -1528,7 +1996,7 @@ function registerDoctorCommand(program) {
1528
1996
  if (jsonMode) {
1529
1997
  outputJson(checks);
1530
1998
  } else {
1531
- displayResults(checks);
1999
+ displayResults2(checks);
1532
2000
  }
1533
2001
  const hasFail = checks.some((c) => c.status === "fail");
1534
2002
  if (hasFail) {
@@ -1677,34 +2145,34 @@ function registerMcpCommand(program) {
1677
2145
  }
1678
2146
 
1679
2147
  // src/commands/plugin/index.ts
1680
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
2148
+ import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
1681
2149
  import { homedir as homedir8 } from "os";
1682
- import { resolve as resolve6 } from "path";
1683
- import chalk4 from "chalk";
2150
+ import { resolve as resolve7 } from "path";
2151
+ import chalk5 from "chalk";
1684
2152
 
1685
2153
  // src/commands/plugin/adapters/claude-code.ts
1686
- import { existsSync as existsSync5 } from "fs";
2154
+ import { existsSync as existsSync10 } from "fs";
1687
2155
  import { homedir as homedir3 } from "os";
1688
- import { resolve as resolve3 } from "path";
2156
+ import { resolve as resolve4 } from "path";
1689
2157
 
1690
2158
  // src/commands/plugin/adapters/base.ts
1691
2159
  import {
1692
2160
  copyFileSync,
1693
- existsSync as existsSync4,
2161
+ existsSync as existsSync9,
1694
2162
  mkdirSync as mkdirSync2,
1695
- readFileSync as readFileSync2,
2163
+ readFileSync as readFileSync6,
1696
2164
  writeFileSync as writeFileSync2
1697
2165
  } from "fs";
1698
- import { resolve as resolve2 } from "path";
2166
+ import { resolve as resolve3 } from "path";
1699
2167
  var BaseJsonAdapter = class {
1700
2168
  detect() {
1701
- return existsSync4(this.configDir);
2169
+ return existsSync9(this.configDir);
1702
2170
  }
1703
2171
  readConfig() {
1704
2172
  const file = this.configPath();
1705
- if (!existsSync4(file)) return {};
2173
+ if (!existsSync9(file)) return {};
1706
2174
  try {
1707
- return JSON.parse(readFileSync2(file, "utf-8"));
2175
+ return JSON.parse(readFileSync6(file, "utf-8"));
1708
2176
  } catch (err) {
1709
2177
  throw new Error(
1710
2178
  `Failed to parse ${file}: ${err instanceof Error ? err.message : "invalid JSON"}`
@@ -1734,16 +2202,16 @@ var BaseJsonAdapter = class {
1734
2202
  }
1735
2203
  writeConfig(config) {
1736
2204
  const file = this.configPath();
1737
- if (!existsSync4(this.configDir)) {
2205
+ if (!existsSync9(this.configDir)) {
1738
2206
  mkdirSync2(this.configDir, { recursive: true });
1739
2207
  }
1740
- if (existsSync4(file)) {
2208
+ if (existsSync9(file)) {
1741
2209
  copyFileSync(file, `${file}.bak`);
1742
2210
  }
1743
2211
  writeFileSync2(file, JSON.stringify(config, null, 2), "utf-8");
1744
2212
  }
1745
2213
  configPath() {
1746
- return resolve2(this.configDir, this.configFile);
2214
+ return resolve3(this.configDir, this.configFile);
1747
2215
  }
1748
2216
  };
1749
2217
 
@@ -1752,25 +2220,25 @@ var ClaudeCodeAdapter = class extends BaseJsonAdapter {
1752
2220
  name = "claude-code";
1753
2221
  description = "Claude Code integration (MCP config + SKILL.md + commands)";
1754
2222
  version = "0.2.0";
1755
- configDir = resolve3(homedir3(), ".claude");
2223
+ configDir = resolve4(homedir3(), ".claude");
1756
2224
  configFile = "settings.json";
1757
2225
  detect() {
1758
- return existsSync5(this.configDir);
2226
+ return existsSync10(this.configDir);
1759
2227
  }
1760
2228
  };
1761
2229
 
1762
2230
  // src/commands/plugin/adapters/cursor.ts
1763
- import { existsSync as existsSync6 } from "fs";
2231
+ import { existsSync as existsSync11 } from "fs";
1764
2232
  import { homedir as homedir4 } from "os";
1765
- import { resolve as resolve4 } from "path";
2233
+ import { resolve as resolve5 } from "path";
1766
2234
  var CursorAdapter = class extends BaseJsonAdapter {
1767
2235
  name = "cursor";
1768
2236
  description = "Cursor integration (MCP config)";
1769
2237
  version = "0.2.0";
1770
- configDir = resolve4(homedir4(), ".cursor");
2238
+ configDir = resolve5(homedir4(), ".cursor");
1771
2239
  configFile = "mcp.json";
1772
2240
  detect() {
1773
- return existsSync6(this.configDir);
2241
+ return existsSync11(this.configDir);
1774
2242
  }
1775
2243
  };
1776
2244
 
@@ -1801,11 +2269,11 @@ function getAdapterDescriptions() {
1801
2269
  import { createHash } from "crypto";
1802
2270
  import {
1803
2271
  copyFileSync as copyFileSync2,
1804
- existsSync as existsSync7,
2272
+ existsSync as existsSync12,
1805
2273
  lstatSync,
1806
2274
  mkdirSync as mkdirSync3,
1807
2275
  readdirSync,
1808
- readFileSync as readFileSync3,
2276
+ readFileSync as readFileSync7,
1809
2277
  readlinkSync,
1810
2278
  rmSync,
1811
2279
  symlinkSync,
@@ -1813,24 +2281,24 @@ import {
1813
2281
  writeFileSync as writeFileSync3
1814
2282
  } from "fs";
1815
2283
  import { homedir as homedir5 } from "os";
1816
- import { dirname as dirname2, join as join2, relative, resolve as resolve5 } from "path";
2284
+ import { dirname as dirname2, join as join7, relative, resolve as resolve6 } from "path";
1817
2285
  var ASSET_SCHEMA_VERSION = 1;
1818
- var PLUGINS_BASE = resolve5(homedir5(), ".rush", "plugins");
2286
+ var PLUGINS_BASE = resolve6(homedir5(), ".rush", "plugins");
1819
2287
  function getAssetManifestPath(pluginName) {
1820
- return resolve5(PLUGINS_BASE, pluginName, "asset-manifest.json");
2288
+ return resolve6(PLUGINS_BASE, pluginName, "asset-manifest.json");
1821
2289
  }
1822
2290
  function getAssetStorePath(pluginName) {
1823
- return resolve5(PLUGINS_BASE, pluginName, "assets");
2291
+ return resolve6(PLUGINS_BASE, pluginName, "assets");
1824
2292
  }
1825
2293
  function computeChecksum(filePath) {
1826
- const content = readFileSync3(filePath);
2294
+ const content = readFileSync7(filePath);
1827
2295
  return createHash("sha256").update(content).digest("hex");
1828
2296
  }
1829
2297
  function loadAssetManifest(pluginName) {
1830
2298
  const manifestPath = getAssetManifestPath(pluginName);
1831
- if (!existsSync7(manifestPath)) return null;
2299
+ if (!existsSync12(manifestPath)) return null;
1832
2300
  try {
1833
- return JSON.parse(readFileSync3(manifestPath, "utf-8"));
2301
+ return JSON.parse(readFileSync7(manifestPath, "utf-8"));
1834
2302
  } catch {
1835
2303
  return null;
1836
2304
  }
@@ -1844,10 +2312,10 @@ function installAssets(options) {
1844
2312
  const existing = loadAssetManifest(pluginName);
1845
2313
  const sourceFiles = collectFiles(sourceDir);
1846
2314
  for (const relPath of sourceFiles) {
1847
- const srcFile = resolve5(sourceDir, relPath);
1848
- const destFile = resolve5(storePath, relPath);
2315
+ const srcFile = resolve6(sourceDir, relPath);
2316
+ const destFile = resolve6(storePath, relPath);
1849
2317
  const newChecksum = computeChecksum(srcFile);
1850
- if (existsSync7(destFile) && !force && existing) {
2318
+ if (existsSync12(destFile) && !force && existing) {
1851
2319
  const existingEntry = existing.files.find((f) => f.path === relPath);
1852
2320
  if (existingEntry) {
1853
2321
  const currentChecksum = computeChecksum(destFile);
@@ -1872,8 +2340,8 @@ function installAssets(options) {
1872
2340
  type: "file"
1873
2341
  });
1874
2342
  }
1875
- const skillTarget = resolve5(homedir5(), ".claude", "skills", "rush-task");
1876
- if (!existsSync7(skillTarget)) {
2343
+ const skillTarget = resolve6(homedir5(), ".claude", "skills", "rush-task");
2344
+ if (!existsSync12(skillTarget)) {
1877
2345
  mkdirSync3(dirname2(skillTarget), { recursive: true });
1878
2346
  try {
1879
2347
  symlinkSync(storePath, skillTarget, "dir");
@@ -1893,7 +2361,7 @@ function installAssets(options) {
1893
2361
  const stat = lstatSync(skillTarget);
1894
2362
  if (stat.isSymbolicLink()) {
1895
2363
  const linkTarget = readlinkSync(skillTarget);
1896
- if (linkTarget === storePath || resolve5(linkTarget) === storePath) {
2364
+ if (linkTarget === storePath || resolve6(linkTarget) === storePath) {
1897
2365
  files.push({
1898
2366
  path: "_symlink_skill",
1899
2367
  checksum: "",
@@ -1925,11 +2393,11 @@ function installAssets(options) {
1925
2393
  return manifest;
1926
2394
  }
1927
2395
  var ALLOWED_DELETE_PREFIXES = [
1928
- resolve5(homedir5(), ".rush", "plugins"),
1929
- resolve5(homedir5(), ".claude", "skills")
2396
+ resolve6(homedir5(), ".rush", "plugins"),
2397
+ resolve6(homedir5(), ".claude", "skills")
1930
2398
  ];
1931
2399
  function isPathAllowed(targetPath) {
1932
- const resolved = resolve5(targetPath);
2400
+ const resolved = resolve6(targetPath);
1933
2401
  return ALLOWED_DELETE_PREFIXES.some(
1934
2402
  (prefix) => resolved.startsWith(`${prefix}/`)
1935
2403
  );
@@ -1944,7 +2412,7 @@ function uninstallAssets(pluginName) {
1944
2412
  return;
1945
2413
  }
1946
2414
  for (const entry of manifest.files) {
1947
- if (!existsSync7(entry.target)) continue;
2415
+ if (!existsSync12(entry.target)) continue;
1948
2416
  if (!isPathAllowed(entry.target)) {
1949
2417
  output.warn(
1950
2418
  ` Refusing to delete ${entry.target}: outside allowed directories`
@@ -1974,7 +2442,7 @@ function uninstallAssets(pluginName) {
1974
2442
  }
1975
2443
  }
1976
2444
  const manifestPath = getAssetManifestPath(pluginName);
1977
- if (existsSync7(manifestPath)) {
2445
+ if (existsSync12(manifestPath)) {
1978
2446
  rmSync(manifestPath);
1979
2447
  }
1980
2448
  const storePath = getAssetStorePath(pluginName);
@@ -1990,7 +2458,7 @@ function collectFiles(dir, base) {
1990
2458
  const files = [];
1991
2459
  const baseDir = base ?? dir;
1992
2460
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
1993
- const fullPath = join2(dir, entry.name);
2461
+ const fullPath = join7(dir, entry.name);
1994
2462
  if (entry.isDirectory()) {
1995
2463
  files.push(...collectFiles(fullPath, baseDir));
1996
2464
  } else if (entry.isFile()) {
@@ -2001,14 +2469,14 @@ function collectFiles(dir, base) {
2001
2469
  }
2002
2470
 
2003
2471
  // src/commands/plugin/doctor.ts
2004
- import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
2472
+ import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
2005
2473
  import { homedir as homedir6 } from "os";
2006
2474
  import path from "path";
2007
2475
  function findCliInPath() {
2008
2476
  const pathDirs = (process.env.PATH || "").split(path.delimiter);
2009
2477
  for (const dir of pathDirs) {
2010
2478
  const candidate = path.join(dir, "rush-ai");
2011
- if (existsSync8(candidate)) return candidate;
2479
+ if (existsSync13(candidate)) return candidate;
2012
2480
  }
2013
2481
  return null;
2014
2482
  }
@@ -2103,7 +2571,7 @@ function checkMcpConfig(ctx) {
2103
2571
  detail: `Unknown plugin type: ${type2}`
2104
2572
  };
2105
2573
  }
2106
- if (!existsSync8(configPath)) {
2574
+ if (!existsSync13(configPath)) {
2107
2575
  return {
2108
2576
  name: "mcp_config",
2109
2577
  label: "MCP config",
@@ -2113,7 +2581,7 @@ function checkMcpConfig(ctx) {
2113
2581
  };
2114
2582
  }
2115
2583
  try {
2116
- const config = JSON.parse(readFileSync4(configPath, "utf-8"));
2584
+ const config = JSON.parse(readFileSync8(configPath, "utf-8"));
2117
2585
  const servers = config.mcpServers;
2118
2586
  if (servers && "rush" in servers) {
2119
2587
  const rushServer = servers.rush;
@@ -2188,10 +2656,10 @@ async function runDoctorChecks(ctx) {
2188
2656
  }
2189
2657
 
2190
2658
  // src/commands/plugin/preflight.ts
2191
- import { accessSync as accessSync2, constants as constants2, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
2659
+ import { accessSync as accessSync2, constants as constants2, existsSync as existsSync14, mkdirSync as mkdirSync4 } from "fs";
2192
2660
  import { homedir as homedir7 } from "os";
2193
2661
  import path2 from "path";
2194
- import chalk3 from "chalk";
2662
+ import chalk4 from "chalk";
2195
2663
  async function runPreflightChecks(options) {
2196
2664
  const results = [];
2197
2665
  try {
@@ -2233,7 +2701,7 @@ async function runPreflightChecks(options) {
2233
2701
  }
2234
2702
  const rushDir = path2.resolve(homedir7(), ".rush");
2235
2703
  try {
2236
- if (!existsSync9(rushDir)) {
2704
+ if (!existsSync14(rushDir)) {
2237
2705
  mkdirSync4(rushDir, { recursive: true });
2238
2706
  }
2239
2707
  accessSync2(rushDir, constants2.W_OK);
@@ -2254,7 +2722,7 @@ async function runPreflightChecks(options) {
2254
2722
  } else {
2255
2723
  output.info("Preflight checks...");
2256
2724
  for (const r of results) {
2257
- const icon = r.status === "pass" ? chalk3.green("[PASS]") : r.status === "warn" ? chalk3.yellow("[WARN]") : chalk3.red("[FAIL]");
2725
+ const icon = r.status === "pass" ? chalk4.green("[PASS]") : r.status === "warn" ? chalk4.yellow("[WARN]") : chalk4.red("[FAIL]");
2258
2726
  output.log(` ${icon} ${r.name}: ${r.detail}`);
2259
2727
  }
2260
2728
  }
@@ -2263,7 +2731,7 @@ async function runPreflightChecks(options) {
2263
2731
 
2264
2732
  // src/commands/plugin/verify.ts
2265
2733
  import { execFileSync as execFileSync2 } from "child_process";
2266
- import { existsSync as existsSync10 } from "fs";
2734
+ import { existsSync as existsSync15 } from "fs";
2267
2735
  async function runVerification(options) {
2268
2736
  const results = [];
2269
2737
  results.push(checkConfigFile(options.adapter));
@@ -2274,7 +2742,7 @@ async function runVerification(options) {
2274
2742
  }
2275
2743
  function checkConfigFile(adapter) {
2276
2744
  const configFile = adapter.configPath();
2277
- if (!existsSync10(configFile)) {
2745
+ if (!existsSync15(configFile)) {
2278
2746
  return {
2279
2747
  name: "config_file",
2280
2748
  label: "IDE config",
@@ -2399,12 +2867,12 @@ function checkAuthStatus() {
2399
2867
 
2400
2868
  // src/commands/plugin/index.ts
2401
2869
  var CURRENT_SCHEMA_VERSION = 2;
2402
- var PLUGINS_DIR = resolve6(homedir8(), ".rush", "plugins");
2403
- var PLUGINS_FILE2 = resolve6(PLUGINS_DIR, "installed.json");
2870
+ var PLUGINS_DIR = resolve7(homedir8(), ".rush", "plugins");
2871
+ var PLUGINS_FILE2 = resolve7(PLUGINS_DIR, "installed.json");
2404
2872
  function safeReadJson2(filePath, fallback) {
2405
- if (!existsSync11(filePath)) return fallback;
2873
+ if (!existsSync16(filePath)) return fallback;
2406
2874
  try {
2407
- return JSON.parse(readFileSync5(filePath, "utf-8"));
2875
+ return JSON.parse(readFileSync9(filePath, "utf-8"));
2408
2876
  } catch {
2409
2877
  return fallback;
2410
2878
  }
@@ -2424,7 +2892,7 @@ function loadInstalledPlugins(autoMigrate = true) {
2424
2892
  return raw;
2425
2893
  }
2426
2894
  function saveInstalledPlugins(data) {
2427
- if (!existsSync11(PLUGINS_DIR)) {
2895
+ if (!existsSync16(PLUGINS_DIR)) {
2428
2896
  mkdirSync5(PLUGINS_DIR, { recursive: true });
2429
2897
  }
2430
2898
  writeFileSync4(PLUGINS_FILE2, JSON.stringify(data, null, 2), "utf-8");
@@ -2455,21 +2923,21 @@ function resolvePluginAssetsDir() {
2455
2923
  );
2456
2924
  return null;
2457
2925
  }
2458
- const bundled = resolve6(baseDir, "plugin-assets");
2459
- if (existsSync11(bundled)) return bundled;
2460
- const monorepo = resolve6(baseDir, "..", "..", "..", "..", "rush-plugin");
2461
- if (existsSync11(monorepo)) return monorepo;
2926
+ const bundled = resolve7(baseDir, "plugin-assets");
2927
+ if (existsSync16(bundled)) return bundled;
2928
+ const monorepo = resolve7(baseDir, "..", "..", "..", "..", "rush-plugin");
2929
+ if (existsSync16(monorepo)) return monorepo;
2462
2930
  return null;
2463
2931
  }
2464
2932
  function getCliVersion() {
2465
2933
  try {
2466
- const pkgPath = resolve6(
2934
+ const pkgPath = resolve7(
2467
2935
  import.meta.dirname ?? __dirname,
2468
2936
  "..",
2469
2937
  "..",
2470
2938
  "package.json"
2471
2939
  );
2472
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
2940
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
2473
2941
  return pkg.version ?? "0.2.0";
2474
2942
  } catch {
2475
2943
  return "0.2.0";
@@ -2747,7 +3215,7 @@ function registerPluginCommand(program) {
2747
3215
  let warn = 0;
2748
3216
  let fail = 0;
2749
3217
  for (const check of checks) {
2750
- const icon = check.status === "pass" ? chalk4.green("[PASS]") : check.status === "warn" ? chalk4.yellow("[WARN]") : chalk4.red("[FAIL]");
3218
+ const icon = check.status === "pass" ? chalk5.green("[PASS]") : check.status === "warn" ? chalk5.yellow("[WARN]") : chalk5.red("[FAIL]");
2751
3219
  output.log(` ${icon} ${check.label}: ${check.detail}`);
2752
3220
  if (check.fix) {
2753
3221
  output.dim(` Fix: ${check.fix}`);
@@ -2768,7 +3236,7 @@ function registerPluginCommand(program) {
2768
3236
  }
2769
3237
  function printVerifyResults(results) {
2770
3238
  for (const r of results) {
2771
- const icon = r.status === "pass" ? chalk4.green("[PASS]") : r.status === "warn" ? chalk4.yellow("[WARN]") : chalk4.red("[FAIL]");
3239
+ const icon = r.status === "pass" ? chalk5.green("[PASS]") : r.status === "warn" ? chalk5.yellow("[WARN]") : chalk5.red("[FAIL]");
2772
3240
  output.log(` ${icon} ${r.label}: ${r.detail}`);
2773
3241
  if (r.fix) {
2774
3242
  output.dim(` Fix: ${r.fix}`);
@@ -2784,17 +3252,17 @@ import { Readable } from "stream";
2784
3252
  import { pipeline } from "stream/promises";
2785
3253
 
2786
3254
  // src/output/diff.ts
2787
- import chalk5 from "chalk";
3255
+ import chalk6 from "chalk";
2788
3256
  function containsDiff(text) {
2789
3257
  return /^@@\s+-\d+/m.test(text) || /^---\s+a\//m.test(text);
2790
3258
  }
2791
3259
  function colorizeDiff(text) {
2792
3260
  return text.split("\n").map((line) => {
2793
3261
  if (line.startsWith("+++") || line.startsWith("---"))
2794
- return chalk5.bold(line);
2795
- if (line.startsWith("+")) return chalk5.green(line);
2796
- if (line.startsWith("-")) return chalk5.red(line);
2797
- if (line.startsWith("@@")) return chalk5.cyan(line);
3262
+ return chalk6.bold(line);
3263
+ if (line.startsWith("+")) return chalk6.green(line);
3264
+ if (line.startsWith("-")) return chalk6.red(line);
3265
+ if (line.startsWith("@@")) return chalk6.cyan(line);
2798
3266
  return line;
2799
3267
  }).join("\n");
2800
3268
  }
@@ -2817,18 +3285,228 @@ async function readStdinIfPiped() {
2817
3285
  if (process.stdin.isTTY) {
2818
3286
  return null;
2819
3287
  }
2820
- return new Promise((resolve7, reject) => {
3288
+ return new Promise((resolve9, reject) => {
2821
3289
  const chunks = [];
2822
3290
  process.stdin.on("data", (chunk) => {
2823
3291
  chunks.push(chunk);
2824
3292
  });
2825
3293
  process.stdin.on("end", () => {
2826
- resolve7(Buffer.concat(chunks).toString("utf-8").trim());
3294
+ resolve9(Buffer.concat(chunks).toString("utf-8").trim());
2827
3295
  });
2828
3296
  process.stdin.on("error", reject);
2829
3297
  });
2830
3298
  }
2831
3299
 
3300
+ // src/commands/task/deploy.ts
3301
+ import { resolve as resolve8 } from "path";
3302
+ import chalk7 from "chalk";
3303
+
3304
+ // src/util/git.ts
3305
+ import { execFileSync as execFileSync3 } from "child_process";
3306
+ import { existsSync as existsSync17 } from "fs";
3307
+ import { join as join8 } from "path";
3308
+ function isGitRepo(projectPath) {
3309
+ return existsSync17(join8(projectPath, ".git"));
3310
+ }
3311
+ function gitInit(projectPath) {
3312
+ execFileSync3("git", ["init"], { cwd: projectPath, stdio: "pipe" });
3313
+ }
3314
+ function hasUncommittedChanges(projectPath) {
3315
+ try {
3316
+ const status = execFileSync3("git", ["status", "--porcelain"], {
3317
+ cwd: projectPath,
3318
+ encoding: "utf-8"
3319
+ });
3320
+ return status.trim().length > 0;
3321
+ } catch {
3322
+ return false;
3323
+ }
3324
+ }
3325
+ function gitAddAndCommit(projectPath, message) {
3326
+ execFileSync3("git", ["add", "-A"], { cwd: projectPath, stdio: "pipe" });
3327
+ execFileSync3("git", ["commit", "-m", message, "--allow-empty"], {
3328
+ cwd: projectPath,
3329
+ stdio: "pipe",
3330
+ env: {
3331
+ ...process.env,
3332
+ GIT_AUTHOR_NAME: process.env.GIT_AUTHOR_NAME || "rush-ai",
3333
+ GIT_AUTHOR_EMAIL: process.env.GIT_AUTHOR_EMAIL || "rush-ai@rush.dev",
3334
+ GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME || "rush-ai",
3335
+ GIT_COMMITTER_EMAIL: process.env.GIT_COMMITTER_EMAIL || "rush-ai@rush.dev"
3336
+ }
3337
+ });
3338
+ }
3339
+ function gitPushUrl(projectPath, url) {
3340
+ try {
3341
+ execFileSync3("git", ["push", url, "HEAD:main", "--force"], {
3342
+ cwd: projectPath,
3343
+ stdio: "pipe",
3344
+ timeout: 3e5,
3345
+ maxBuffer: 10 * 1024 * 1024
3346
+ });
3347
+ return { success: true, stderr: "" };
3348
+ } catch (err) {
3349
+ const rawStderr = err && typeof err === "object" && "stderr" in err ? String(err.stderr) : "unknown error";
3350
+ return { success: false, stderr: sanitizeGitOutput(rawStderr) };
3351
+ }
3352
+ }
3353
+ function sanitizeGitOutput(text) {
3354
+ return text.replace(/https?:\/\/[^@]*@/g, "https://***@");
3355
+ }
3356
+
3357
+ // src/commands/task/deploy.ts
3358
+ function sleep(ms) {
3359
+ return new Promise((r) => setTimeout(r, ms));
3360
+ }
3361
+ function maskToken2(url) {
3362
+ return url.replace(/:([^@]{4})[^@]*@/, ":$1****@");
3363
+ }
3364
+ function registerDeploySubcommand(task, program) {
3365
+ task.command("deploy").description("Deploy a local project to Rush platform").option("-n, --name <name>", "Project name").option("-p, --path <dir>", "Project directory", ".").action(async (opts) => {
3366
+ requireAuth();
3367
+ const format = resolveFormat(program.opts());
3368
+ const client = createClient();
3369
+ const projectPath = resolve8(opts.path);
3370
+ if (format !== "json") {
3371
+ output.info("Checking project readiness...");
3372
+ }
3373
+ const report = runChecks(projectPath);
3374
+ if (!report.deployable) {
3375
+ if (format === "json") {
3376
+ output.log(
3377
+ JSON.stringify(
3378
+ { error: "Project has errors that must be fixed", report },
3379
+ null,
3380
+ 2
3381
+ )
3382
+ );
3383
+ } else {
3384
+ output.error(
3385
+ "Project has errors that must be fixed before deploying."
3386
+ );
3387
+ output.dim("Run `rush-ai check` for details.");
3388
+ }
3389
+ process.exit(1);
3390
+ }
3391
+ if (format !== "json") {
3392
+ output.info("Creating Rush project...");
3393
+ }
3394
+ const projectName = opts.name || `deploy-${Date.now().toString(36)}`;
3395
+ const { data } = await client.post(
3396
+ "/api/projects/deploy-cli",
3397
+ {
3398
+ name: projectName,
3399
+ framework: report.framework,
3400
+ packageManager: report.packageManager
3401
+ }
3402
+ );
3403
+ if (!data.success || !data.gitPushUrl) {
3404
+ throw new RushError(
3405
+ "Failed to create project \u2014 no git push URL returned"
3406
+ );
3407
+ }
3408
+ if (format !== "json") {
3409
+ output.success(`Project created: ${data.projectId}`);
3410
+ }
3411
+ if (format !== "json") {
3412
+ output.info("Pushing code to Rush...");
3413
+ }
3414
+ if (!isGitRepo(projectPath)) {
3415
+ gitInit(projectPath);
3416
+ }
3417
+ if (hasUncommittedChanges(projectPath)) {
3418
+ gitAddAndCommit(projectPath, "deploy to rush");
3419
+ }
3420
+ const pushResult = gitPushUrl(projectPath, data.gitPushUrl);
3421
+ if (!pushResult.success) {
3422
+ if (format === "json") {
3423
+ output.log(
3424
+ JSON.stringify(
3425
+ {
3426
+ error: "Git push failed",
3427
+ details: pushResult.stderr,
3428
+ projectId: data.projectId
3429
+ },
3430
+ null,
3431
+ 2
3432
+ )
3433
+ );
3434
+ } else {
3435
+ output.error("Git push failed:");
3436
+ output.log(pushResult.stderr);
3437
+ output.dim(
3438
+ "Check your VPN connection and network. The project was created but code was not pushed."
3439
+ );
3440
+ output.dim(`Project ID: ${data.projectId}`);
3441
+ }
3442
+ process.exit(1);
3443
+ }
3444
+ if (format !== "json") {
3445
+ output.success("Code pushed successfully");
3446
+ }
3447
+ if (format !== "json") {
3448
+ output.info("Initializing project on Rush...");
3449
+ }
3450
+ try {
3451
+ await client.post(
3452
+ `/api/projects/${encodeURIComponent(data.projectId)}/init-clone`,
3453
+ {
3454
+ projectId: data.projectId,
3455
+ sourceProjectId: data.projectId
3456
+ }
3457
+ );
3458
+ } catch {
3459
+ if (format !== "json") {
3460
+ output.warn(
3461
+ "Init-clone request failed \u2014 the pod may auto-initialize on startup"
3462
+ );
3463
+ }
3464
+ }
3465
+ if (format !== "json") {
3466
+ output.info("Waiting for project to be ready...");
3467
+ }
3468
+ const maxWait = 15e4;
3469
+ const pollInterval = 3e3;
3470
+ const startTime = Date.now();
3471
+ let ready = false;
3472
+ while (Date.now() - startTime < maxWait) {
3473
+ try {
3474
+ const { data: readyData } = await client.get(
3475
+ `/api/projects/${encodeURIComponent(data.projectId)}/pod-ready`
3476
+ );
3477
+ if (readyData?.data?.ready) {
3478
+ ready = true;
3479
+ break;
3480
+ }
3481
+ } catch {
3482
+ }
3483
+ await sleep(pollInterval);
3484
+ }
3485
+ const result = {
3486
+ projectId: data.projectId,
3487
+ previewUrl: data.podPreviewUrl,
3488
+ chatUrl: data.podChatUrl,
3489
+ status: ready ? "ready" : "timeout",
3490
+ gitRemote: maskToken2(data.gitPushUrl)
3491
+ };
3492
+ if (format === "json") {
3493
+ output.log(JSON.stringify(result, null, 2));
3494
+ } else {
3495
+ output.newline();
3496
+ if (ready) {
3497
+ output.success("Deployment complete!");
3498
+ } else {
3499
+ output.warn("Pod is still starting up \u2014 it may take another minute.");
3500
+ }
3501
+ output.log(` ${chalk7.bold("Project ID:")} ${result.projectId}`);
3502
+ output.log(` ${chalk7.bold("Preview:")} ${result.previewUrl}`);
3503
+ output.log(` ${chalk7.bold("Chat:")} ${result.chatUrl}`);
3504
+ output.log(` ${chalk7.bold("Git remote:")} ${result.gitRemote}`);
3505
+ output.newline();
3506
+ }
3507
+ });
3508
+ }
3509
+
2832
3510
  // src/commands/task/index.ts
2833
3511
  var VALID_CATEGORIES = ["code", "image", "document", "other"];
2834
3512
  function sanitizeFilePath(filePath, outputDir) {
@@ -2869,6 +3547,7 @@ function buildCategorySummary(files) {
2869
3547
  }
2870
3548
  function registerTaskCommand(program) {
2871
3549
  const task = program.command("task").description("Create and manage tasks");
3550
+ registerDeploySubcommand(task, program);
2872
3551
  task.command("create").description("Create a task asynchronously").requiredOption("-a, --agent <name>", "Agent name to execute the task").option("-p, --prompt <text>", "Task prompt").option("--skills <skills>", "Comma-separated skills to add", commaSplit).option("--mcp <servers>", "Comma-separated MCP servers to add", commaSplit).action(
2873
3552
  async (options) => {
2874
3553
  requireAuth();
@@ -3253,6 +3932,7 @@ function registerCommands(program) {
3253
3932
  registerAuthCommand(program);
3254
3933
  registerAgentCommand(program);
3255
3934
  registerTaskCommand(program);
3935
+ registerCheckCommand(program);
3256
3936
  registerMcpCommand(program);
3257
3937
  registerPluginCommand(program);
3258
3938
  registerCompletionCommand(program);
@@ -3263,7 +3943,7 @@ function registerCommands(program) {
3263
3943
  // src/util/update-check.ts
3264
3944
  import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
3265
3945
  import { homedir as homedir9 } from "os";
3266
- import { dirname as dirname3, join as join3 } from "path";
3946
+ import { dirname as dirname3, join as join9 } from "path";
3267
3947
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
3268
3948
  var FETCH_TIMEOUT_MS = 3e3;
3269
3949
  var REGISTRY_URL = "https://registry.npmjs.org/rush-ai/latest";
@@ -3292,8 +3972,8 @@ async function writeLastCheck(checkFile) {
3292
3972
  }
3293
3973
  }
3294
3974
  async function checkForUpdate(currentVersion) {
3295
- const rushDir = join3(homedir9(), ".rush");
3296
- const checkFile = join3(rushDir, "update-check.json");
3975
+ const rushDir = join9(homedir9(), ".rush");
3976
+ const checkFile = join9(rushDir, "update-check.json");
3297
3977
  try {
3298
3978
  if (process.env.CI) return;
3299
3979
  let lastCheck = 0;
@@ -3322,28 +4002,28 @@ async function checkForUpdate(currentVersion) {
3322
4002
 
3323
4003
  // src/index.ts
3324
4004
  var BANNER = `
3325
- ${chalk6.bold.cyan("Rush CLI")} ${chalk6.dim(`v${VERSION}`)}
3326
- ${chalk6.dim("The command-line interface for the Rush AI platform")}
4005
+ ${chalk8.bold.cyan("Rush CLI")} ${chalk8.dim(`v${VERSION}`)}
4006
+ ${chalk8.dim("The command-line interface for the Rush AI platform")}
3327
4007
  `;
3328
4008
  function showWelcomeGuide() {
3329
4009
  const lines = [
3330
4010
  "",
3331
- ` ${chalk6.bold.cyan("Rush CLI")} ${chalk6.dim(`v${VERSION}`)}`,
4011
+ ` ${chalk8.bold.cyan("Rush CLI")} ${chalk8.dim(`v${VERSION}`)}`,
3332
4012
  "",
3333
- chalk6.dim(" Quick start:"),
3334
- ` ${chalk6.cyan("rush-ai auth login")} Log in to Rush platform`,
3335
- ` ${chalk6.cyan("rush-ai agent list")} Browse available agents`,
3336
- ` ${chalk6.cyan("rush-ai task create -a <agent>")} Create a task`,
4013
+ chalk8.dim(" Quick start:"),
4014
+ ` ${chalk8.cyan("rush-ai auth login")} Log in to Rush platform`,
4015
+ ` ${chalk8.cyan("rush-ai agent list")} Browse available agents`,
4016
+ ` ${chalk8.cyan("rush-ai task create -a <agent>")} Create a task`,
3337
4017
  ""
3338
4018
  ];
3339
4019
  try {
3340
4020
  const method = getAuthMethod();
3341
4021
  if (method) {
3342
- lines.push(chalk6.dim(" Status:"), ` Logged in via ${method}`, "");
4022
+ lines.push(chalk8.dim(" Status:"), ` Logged in via ${method}`, "");
3343
4023
  }
3344
4024
  } catch {
3345
4025
  }
3346
- lines.push(chalk6.dim(" Run rush-ai --help for all commands."), "");
4026
+ lines.push(chalk8.dim(" Run rush-ai --help for all commands."), "");
3347
4027
  console.error(lines.join("\n"));
3348
4028
  }
3349
4029
  function createProgram() {