fasterdev 0.1.7 → 0.1.8

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.
Files changed (3) hide show
  1. package/README.md +64 -138
  2. package/dist/cli.js +778 -84
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -91,6 +91,13 @@ var FasterAPI = class {
91
91
  if (options?.tool) params.set("tool", options.tool);
92
92
  return this.request(`/packages/search?${params}`);
93
93
  }
94
+ /**
95
+ * Get all packages in a scope (e.g., "audit" returns all @audit/* packages)
96
+ */
97
+ async getPackagesByScope(scope) {
98
+ const params = new URLSearchParams({ scope });
99
+ return this.request(`/packages/search?${params}`);
100
+ }
94
101
  /**
95
102
  * Get package info
96
103
  */
@@ -134,12 +141,15 @@ var FasterAPI = class {
134
141
 
135
142
  // src/config.ts
136
143
  import Conf from "conf";
144
+ import { homedir } from "os";
145
+ import { join } from "path";
137
146
  var DEFAULT_CONFIG = {
138
147
  apiUrl: "https://faster.dev/api/v1",
139
148
  analytics: true
140
149
  };
141
150
  var store = new Conf({
142
- projectName: "faster",
151
+ projectName: "faster-dev",
152
+ cwd: join(homedir(), ".faster-dev"),
143
153
  defaults: DEFAULT_CONFIG
144
154
  });
145
155
  function getConfig() {
@@ -177,6 +187,7 @@ function getConfigPath() {
177
187
 
178
188
  // src/utils.ts
179
189
  import { spawn } from "child_process";
190
+ import * as readline from "readline";
180
191
  function parsePackageSpec(input) {
181
192
  if (input.startsWith("@")) {
182
193
  const match2 = input.match(/^(@[^/]+\/[^@]+)(?:@(.+))?$/);
@@ -191,6 +202,10 @@ function parsePackageSpec(input) {
191
202
  }
192
203
  return { name: input };
193
204
  }
205
+ function getScopeFromInput(input) {
206
+ const match = input.match(/^@([^/@]+)$/);
207
+ return match ? match[1] : null;
208
+ }
194
209
  function resolveInstallType(asSkill) {
195
210
  return asSkill ? "skill" : "rule";
196
211
  }
@@ -221,6 +236,18 @@ function openBrowser(url) {
221
236
  return false;
222
237
  }
223
238
  }
239
+ async function confirm(message) {
240
+ const rl = readline.createInterface({
241
+ input: process.stdin,
242
+ output: process.stdout
243
+ });
244
+ return new Promise((resolve) => {
245
+ rl.question(`${message} (y/N) `, (answer) => {
246
+ rl.close();
247
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
248
+ });
249
+ });
250
+ }
224
251
 
225
252
  // src/lib/command-utils.ts
226
253
  import chalk from "chalk";
@@ -371,7 +398,7 @@ function registerWhoamiCommand(program2) {
371
398
  outputJson({ authenticated: false });
372
399
  } else {
373
400
  console.log(chalk4.yellow("Not logged in"));
374
- console.log(chalk4.dim("Run `faster login` to authenticate"));
401
+ console.log(chalk4.dim("Run `fasterdev login` to authenticate"));
375
402
  }
376
403
  setExitCode(EXIT_CODES.AUTH_REQUIRED);
377
404
  return;
@@ -634,8 +661,8 @@ var TOOL_CONFIGS = {
634
661
  id: "amp",
635
662
  name: "Amp (Sourcegraph)",
636
663
  detect: {
637
- projectDirs: [".amp", ".claude"],
638
- globalDirs: [path.join(home, ".config", "amp"), path.join(home, ".claude")],
664
+ projectDirs: [".agents", ".amp", ".claude"],
665
+ globalDirs: [path.join(home, ".config", "amp"), path.join(home, ".config", "agents")],
639
666
  configFiles: ["AGENTS.md", "AGENT.md"]
640
667
  },
641
668
  rules: {
@@ -645,8 +672,8 @@ var TOOL_CONFIGS = {
645
672
  fileExtension: ".md"
646
673
  },
647
674
  skills: {
648
- projectPath: ".amp/skills",
649
- globalPath: path.join(home, ".claude", "skills")
675
+ projectPath: ".agents/skills",
676
+ globalPath: path.join(home, ".config", "agents", "skills")
650
677
  }
651
678
  },
652
679
  opencode: {
@@ -667,6 +694,387 @@ var TOOL_CONFIGS = {
667
694
  projectPath: ".opencode/skill",
668
695
  globalPath: path.join(home, ".config", "opencode", "skill")
669
696
  }
697
+ },
698
+ antigravity: {
699
+ id: "antigravity",
700
+ name: "Antigravity",
701
+ detect: {
702
+ projectDirs: [".agent"],
703
+ globalDirs: [path.join(home, ".gemini", "antigravity")],
704
+ configFiles: []
705
+ },
706
+ rules: {
707
+ projectPath: ".agent/rules",
708
+ globalPath: path.join(home, ".gemini", "antigravity", "rules"),
709
+ format: "markdown",
710
+ fileExtension: ".md"
711
+ },
712
+ skills: {
713
+ projectPath: ".agent/skills",
714
+ globalPath: path.join(home, ".gemini", "antigravity", "skills")
715
+ }
716
+ },
717
+ // New agents from Vercel Skills
718
+ windsurf: {
719
+ id: "windsurf",
720
+ name: "Windsurf",
721
+ detect: {
722
+ projectDirs: [".windsurf"],
723
+ globalDirs: [path.join(home, ".codeium", "windsurf")],
724
+ configFiles: []
725
+ },
726
+ rules: {
727
+ projectPath: ".windsurf/rules",
728
+ globalPath: path.join(home, ".codeium", "windsurf", "rules"),
729
+ format: "markdown",
730
+ fileExtension: ".md"
731
+ },
732
+ skills: {
733
+ projectPath: ".windsurf/skills",
734
+ globalPath: path.join(home, ".codeium", "windsurf", "skills")
735
+ }
736
+ },
737
+ "github-copilot": {
738
+ id: "github-copilot",
739
+ name: "GitHub Copilot",
740
+ detect: {
741
+ projectDirs: [".github"],
742
+ globalDirs: [path.join(home, ".copilot")],
743
+ configFiles: [".github/copilot-instructions.md"]
744
+ },
745
+ rules: {
746
+ projectPath: ".github/rules",
747
+ globalPath: path.join(home, ".copilot", "rules"),
748
+ format: "markdown",
749
+ fileExtension: ".md"
750
+ },
751
+ skills: {
752
+ projectPath: ".github/skills",
753
+ globalPath: path.join(home, ".copilot", "skills")
754
+ }
755
+ },
756
+ goose: {
757
+ id: "goose",
758
+ name: "Goose",
759
+ detect: {
760
+ projectDirs: [".goose"],
761
+ globalDirs: [path.join(home, ".config", "goose")],
762
+ configFiles: []
763
+ },
764
+ rules: {
765
+ projectPath: ".goose/rules",
766
+ globalPath: path.join(home, ".config", "goose", "rules"),
767
+ format: "markdown",
768
+ fileExtension: ".md"
769
+ },
770
+ skills: {
771
+ projectPath: ".goose/skills",
772
+ globalPath: path.join(home, ".config", "goose", "skills")
773
+ }
774
+ },
775
+ kilo: {
776
+ id: "kilo",
777
+ name: "Kilo Code",
778
+ detect: {
779
+ projectDirs: [".kilocode"],
780
+ globalDirs: [path.join(home, ".kilocode")],
781
+ configFiles: []
782
+ },
783
+ rules: {
784
+ projectPath: ".kilocode/rules",
785
+ globalPath: path.join(home, ".kilocode", "rules"),
786
+ format: "markdown",
787
+ fileExtension: ".md"
788
+ },
789
+ skills: {
790
+ projectPath: ".kilocode/skills",
791
+ globalPath: path.join(home, ".kilocode", "skills")
792
+ }
793
+ },
794
+ kiro: {
795
+ id: "kiro",
796
+ name: "Kiro CLI",
797
+ detect: {
798
+ projectDirs: [".kiro"],
799
+ globalDirs: [path.join(home, ".kiro")],
800
+ configFiles: []
801
+ },
802
+ rules: {
803
+ projectPath: ".kiro/rules",
804
+ globalPath: path.join(home, ".kiro", "rules"),
805
+ format: "markdown",
806
+ fileExtension: ".md"
807
+ },
808
+ skills: {
809
+ projectPath: ".kiro/skills",
810
+ globalPath: path.join(home, ".kiro", "skills")
811
+ }
812
+ },
813
+ qwen: {
814
+ id: "qwen",
815
+ name: "Qwen Code",
816
+ detect: {
817
+ projectDirs: [".qwen"],
818
+ globalDirs: [path.join(home, ".qwen")],
819
+ configFiles: []
820
+ },
821
+ rules: {
822
+ projectPath: ".qwen/rules",
823
+ globalPath: path.join(home, ".qwen", "rules"),
824
+ format: "markdown",
825
+ fileExtension: ".md"
826
+ },
827
+ skills: {
828
+ projectPath: ".qwen/skills",
829
+ globalPath: path.join(home, ".qwen", "skills")
830
+ }
831
+ },
832
+ trae: {
833
+ id: "trae",
834
+ name: "Trae",
835
+ detect: {
836
+ projectDirs: [".trae"],
837
+ globalDirs: [path.join(home, ".trae")],
838
+ configFiles: []
839
+ },
840
+ rules: {
841
+ projectPath: ".trae/rules",
842
+ globalPath: path.join(home, ".trae", "rules"),
843
+ format: "markdown",
844
+ fileExtension: ".md"
845
+ },
846
+ skills: {
847
+ projectPath: ".trae/skills",
848
+ globalPath: path.join(home, ".trae", "skills")
849
+ }
850
+ },
851
+ crush: {
852
+ id: "crush",
853
+ name: "Crush",
854
+ detect: {
855
+ projectDirs: [".crush"],
856
+ globalDirs: [path.join(home, ".config", "crush")],
857
+ configFiles: []
858
+ },
859
+ rules: {
860
+ projectPath: ".crush/rules",
861
+ globalPath: path.join(home, ".config", "crush", "rules"),
862
+ format: "markdown",
863
+ fileExtension: ".md"
864
+ },
865
+ skills: {
866
+ projectPath: ".crush/skills",
867
+ globalPath: path.join(home, ".config", "crush", "skills")
868
+ }
869
+ },
870
+ droid: {
871
+ id: "droid",
872
+ name: "Droid",
873
+ detect: {
874
+ projectDirs: [".factory"],
875
+ globalDirs: [path.join(home, ".factory")],
876
+ configFiles: []
877
+ },
878
+ rules: {
879
+ projectPath: ".factory/rules",
880
+ globalPath: path.join(home, ".factory", "rules"),
881
+ format: "markdown",
882
+ fileExtension: ".md"
883
+ },
884
+ skills: {
885
+ projectPath: ".factory/skills",
886
+ globalPath: path.join(home, ".factory", "skills")
887
+ }
888
+ },
889
+ mcpjam: {
890
+ id: "mcpjam",
891
+ name: "MCPJam",
892
+ detect: {
893
+ projectDirs: [".mcpjam"],
894
+ globalDirs: [path.join(home, ".mcpjam")],
895
+ configFiles: []
896
+ },
897
+ rules: {
898
+ projectPath: ".mcpjam/rules",
899
+ globalPath: path.join(home, ".mcpjam", "rules"),
900
+ format: "markdown",
901
+ fileExtension: ".md"
902
+ },
903
+ skills: {
904
+ projectPath: ".mcpjam/skills",
905
+ globalPath: path.join(home, ".mcpjam", "skills")
906
+ }
907
+ },
908
+ mux: {
909
+ id: "mux",
910
+ name: "Mux",
911
+ detect: {
912
+ projectDirs: [".mux"],
913
+ globalDirs: [path.join(home, ".mux")],
914
+ configFiles: []
915
+ },
916
+ rules: {
917
+ projectPath: ".mux/rules",
918
+ globalPath: path.join(home, ".mux", "rules"),
919
+ format: "markdown",
920
+ fileExtension: ".md"
921
+ },
922
+ skills: {
923
+ projectPath: ".mux/skills",
924
+ globalPath: path.join(home, ".mux", "skills")
925
+ }
926
+ },
927
+ openhands: {
928
+ id: "openhands",
929
+ name: "OpenHands",
930
+ detect: {
931
+ projectDirs: [".openhands"],
932
+ globalDirs: [path.join(home, ".openhands")],
933
+ configFiles: []
934
+ },
935
+ rules: {
936
+ projectPath: ".openhands/rules",
937
+ globalPath: path.join(home, ".openhands", "rules"),
938
+ format: "markdown",
939
+ fileExtension: ".md"
940
+ },
941
+ skills: {
942
+ projectPath: ".openhands/skills",
943
+ globalPath: path.join(home, ".openhands", "skills")
944
+ }
945
+ },
946
+ pi: {
947
+ id: "pi",
948
+ name: "Pi",
949
+ detect: {
950
+ projectDirs: [".pi"],
951
+ globalDirs: [path.join(home, ".pi", "agent")],
952
+ configFiles: []
953
+ },
954
+ rules: {
955
+ projectPath: ".pi/rules",
956
+ globalPath: path.join(home, ".pi", "agent", "rules"),
957
+ format: "markdown",
958
+ fileExtension: ".md"
959
+ },
960
+ skills: {
961
+ projectPath: ".pi/skills",
962
+ globalPath: path.join(home, ".pi", "agent", "skills")
963
+ }
964
+ },
965
+ qoder: {
966
+ id: "qoder",
967
+ name: "Qoder",
968
+ detect: {
969
+ projectDirs: [".qoder"],
970
+ globalDirs: [path.join(home, ".qoder")],
971
+ configFiles: []
972
+ },
973
+ rules: {
974
+ projectPath: ".qoder/rules",
975
+ globalPath: path.join(home, ".qoder", "rules"),
976
+ format: "markdown",
977
+ fileExtension: ".md"
978
+ },
979
+ skills: {
980
+ projectPath: ".qoder/skills",
981
+ globalPath: path.join(home, ".qoder", "skills")
982
+ }
983
+ },
984
+ clawdbot: {
985
+ id: "clawdbot",
986
+ name: "Clawdbot",
987
+ detect: {
988
+ projectDirs: ["skills"],
989
+ globalDirs: [path.join(home, ".clawdbot")],
990
+ configFiles: []
991
+ },
992
+ rules: {
993
+ projectPath: "skills",
994
+ globalPath: path.join(home, ".clawdbot", "skills"),
995
+ format: "markdown",
996
+ fileExtension: ".md"
997
+ },
998
+ skills: {
999
+ projectPath: "skills",
1000
+ globalPath: path.join(home, ".clawdbot", "skills")
1001
+ }
1002
+ },
1003
+ codebuddy: {
1004
+ id: "codebuddy",
1005
+ name: "CodeBuddy",
1006
+ detect: {
1007
+ projectDirs: [".codebuddy"],
1008
+ globalDirs: [path.join(home, ".codebuddy")],
1009
+ configFiles: []
1010
+ },
1011
+ rules: {
1012
+ projectPath: ".codebuddy/rules",
1013
+ globalPath: path.join(home, ".codebuddy", "rules"),
1014
+ format: "markdown",
1015
+ fileExtension: ".md"
1016
+ },
1017
+ skills: {
1018
+ projectPath: ".codebuddy/skills",
1019
+ globalPath: path.join(home, ".codebuddy", "skills")
1020
+ }
1021
+ },
1022
+ "command-code": {
1023
+ id: "command-code",
1024
+ name: "Command Code",
1025
+ detect: {
1026
+ projectDirs: [".commandcode"],
1027
+ globalDirs: [path.join(home, ".commandcode")],
1028
+ configFiles: []
1029
+ },
1030
+ rules: {
1031
+ projectPath: ".commandcode/rules",
1032
+ globalPath: path.join(home, ".commandcode", "rules"),
1033
+ format: "markdown",
1034
+ fileExtension: ".md"
1035
+ },
1036
+ skills: {
1037
+ projectPath: ".commandcode/skills",
1038
+ globalPath: path.join(home, ".commandcode", "skills")
1039
+ }
1040
+ },
1041
+ zencoder: {
1042
+ id: "zencoder",
1043
+ name: "Zencoder",
1044
+ detect: {
1045
+ projectDirs: [".zencoder"],
1046
+ globalDirs: [path.join(home, ".zencoder")],
1047
+ configFiles: []
1048
+ },
1049
+ rules: {
1050
+ projectPath: ".zencoder/rules",
1051
+ globalPath: path.join(home, ".zencoder", "rules"),
1052
+ format: "markdown",
1053
+ fileExtension: ".md"
1054
+ },
1055
+ skills: {
1056
+ projectPath: ".zencoder/skills",
1057
+ globalPath: path.join(home, ".zencoder", "skills")
1058
+ }
1059
+ },
1060
+ neovate: {
1061
+ id: "neovate",
1062
+ name: "Neovate",
1063
+ detect: {
1064
+ projectDirs: [".neovate"],
1065
+ globalDirs: [path.join(home, ".neovate")],
1066
+ configFiles: []
1067
+ },
1068
+ rules: {
1069
+ projectPath: ".neovate/rules",
1070
+ globalPath: path.join(home, ".neovate", "rules"),
1071
+ format: "markdown",
1072
+ fileExtension: ".md"
1073
+ },
1074
+ skills: {
1075
+ projectPath: ".neovate/skills",
1076
+ globalPath: path.join(home, ".neovate", "skills")
1077
+ }
670
1078
  }
671
1079
  };
672
1080
  var RULE_TOOLS = Object.keys(TOOL_CONFIGS);
@@ -674,13 +1082,33 @@ var DEFAULT_TOOL_PRIORITY = [
674
1082
  "claude-code",
675
1083
  "cursor",
676
1084
  "codex",
1085
+ "windsurf",
1086
+ "github-copilot",
677
1087
  "cline",
678
1088
  "roo-code",
679
1089
  "continue",
680
1090
  "aider",
681
1091
  "gemini",
682
1092
  "amp",
683
- "opencode"
1093
+ "opencode",
1094
+ "antigravity",
1095
+ "goose",
1096
+ "kilo",
1097
+ "kiro",
1098
+ "qwen",
1099
+ "trae",
1100
+ "crush",
1101
+ "droid",
1102
+ "mcpjam",
1103
+ "mux",
1104
+ "openhands",
1105
+ "pi",
1106
+ "qoder",
1107
+ "clawdbot",
1108
+ "codebuddy",
1109
+ "command-code",
1110
+ "zencoder",
1111
+ "neovate"
684
1112
  ];
685
1113
 
686
1114
  // src/detector.ts
@@ -794,8 +1222,8 @@ function registerDiscoveryCommands(program2) {
794
1222
  import chalk8 from "chalk";
795
1223
 
796
1224
  // src/installer.ts
797
- import fs2 from "fs/promises";
798
- import path3 from "path";
1225
+ import fs3 from "fs/promises";
1226
+ import path4 from "path";
799
1227
 
800
1228
  // src/converter.ts
801
1229
  import YAML from "yaml";
@@ -896,10 +1324,18 @@ function convertToToolFormat(content, toolConfig, packageName) {
896
1324
  }
897
1325
  }
898
1326
 
899
- // src/installer.ts
900
- import YAML2 from "yaml";
901
- async function ensureDir(dir) {
902
- await fs2.mkdir(dir, { recursive: true });
1327
+ // src/symlinker.ts
1328
+ import fs2 from "fs/promises";
1329
+ import path3 from "path";
1330
+ import os2 from "os";
1331
+ var CANONICAL_DIR = path3.join(os2.homedir(), ".faster-dev", "packages");
1332
+ async function isSymlink(filePath) {
1333
+ try {
1334
+ const stats = await fs2.lstat(filePath);
1335
+ return stats.isSymbolicLink();
1336
+ } catch {
1337
+ return false;
1338
+ }
903
1339
  }
904
1340
  async function fileExists(p) {
905
1341
  try {
@@ -909,9 +1345,106 @@ async function fileExists(p) {
909
1345
  return false;
910
1346
  }
911
1347
  }
1348
+ async function ensureDir(dir) {
1349
+ await fs2.mkdir(dir, { recursive: true });
1350
+ }
1351
+ async function writeCanonicalPackage(packageName, originalContent, toolConfigs) {
1352
+ const packageDir = path3.join(CANONICAL_DIR, packageName);
1353
+ await ensureDir(packageDir);
1354
+ const files = /* @__PURE__ */ new Map();
1355
+ const originalPath = path3.join(packageDir, "original.md");
1356
+ await fs2.writeFile(originalPath, originalContent, "utf-8");
1357
+ files.set("original.md", originalPath);
1358
+ for (const config of toolConfigs) {
1359
+ const ext = config.rules.fileExtension;
1360
+ const filename = `rule-${config.id}${ext}`;
1361
+ const converted = convertToToolFormat(originalContent, config, packageName);
1362
+ const filePath = path3.join(packageDir, filename);
1363
+ await fs2.writeFile(filePath, converted, "utf-8");
1364
+ files.set(filename, filePath);
1365
+ }
1366
+ return { packageDir, files };
1367
+ }
1368
+ function getCanonicalFilePath(packageName, toolConfig) {
1369
+ const ext = toolConfig.rules.fileExtension;
1370
+ return path3.join(CANONICAL_DIR, packageName, `rule-${toolConfig.id}${ext}`);
1371
+ }
1372
+ async function createSymlink(canonicalPath, targetPath, options = {}) {
1373
+ if (await fileExists(targetPath)) {
1374
+ if (await isSymlink(targetPath)) {
1375
+ const existingTarget = await fs2.readlink(targetPath);
1376
+ if (existingTarget === canonicalPath) {
1377
+ return;
1378
+ }
1379
+ }
1380
+ if (!options.force) {
1381
+ throw new Error(`File already exists at ${targetPath}`);
1382
+ }
1383
+ await fs2.unlink(targetPath);
1384
+ }
1385
+ await ensureDir(path3.dirname(targetPath));
1386
+ await fs2.symlink(canonicalPath, targetPath);
1387
+ }
1388
+ async function installWithSymlink(packageName, content, toolConfig, targetDir, options = {}) {
1389
+ await writeCanonicalPackage(packageName, content, [toolConfig]);
1390
+ const canonicalPath = getCanonicalFilePath(packageName, toolConfig);
1391
+ const filename = `${packageName}${toolConfig.rules.fileExtension}`;
1392
+ const targetPath = path3.join(targetDir, filename);
1393
+ await createSymlink(canonicalPath, targetPath, options);
1394
+ return targetPath;
1395
+ }
1396
+ async function installWithCopy(packageName, content, toolConfig, targetDir, options = {}) {
1397
+ const filename = `${packageName}${toolConfig.rules.fileExtension}`;
1398
+ const targetPath = path3.join(targetDir, filename);
1399
+ if (await fileExists(targetPath) && !options.force) {
1400
+ throw new Error(`File already exists at ${targetPath}`);
1401
+ }
1402
+ const converted = convertToToolFormat(content, toolConfig, packageName);
1403
+ await ensureDir(targetDir);
1404
+ await fs2.writeFile(targetPath, converted, "utf-8");
1405
+ return targetPath;
1406
+ }
1407
+ async function symlinkSupported() {
1408
+ const testDir = path3.join(CANONICAL_DIR, ".symlink-test");
1409
+ const testSource = path3.join(testDir, "source.txt");
1410
+ const testLink = path3.join(testDir, "link.txt");
1411
+ try {
1412
+ await ensureDir(testDir);
1413
+ await fs2.writeFile(testSource, "test", "utf-8");
1414
+ await fs2.symlink(testSource, testLink);
1415
+ await fs2.unlink(testLink);
1416
+ await fs2.unlink(testSource);
1417
+ await fs2.rmdir(testDir);
1418
+ return true;
1419
+ } catch (error) {
1420
+ try {
1421
+ await fs2.rm(testDir, { recursive: true, force: true });
1422
+ } catch {
1423
+ }
1424
+ const err = error;
1425
+ if (err.code === "EPERM" || err.code === "ENOTSUP") {
1426
+ return false;
1427
+ }
1428
+ return true;
1429
+ }
1430
+ }
1431
+
1432
+ // src/installer.ts
1433
+ import YAML2 from "yaml";
1434
+ async function ensureDir2(dir) {
1435
+ await fs3.mkdir(dir, { recursive: true });
1436
+ }
1437
+ async function fileExists2(p) {
1438
+ try {
1439
+ await fs3.access(p);
1440
+ return true;
1441
+ } catch {
1442
+ return false;
1443
+ }
1444
+ }
912
1445
  async function readFile(p) {
913
1446
  try {
914
- return await fs2.readFile(p, "utf-8");
1447
+ return await fs3.readFile(p, "utf-8");
915
1448
  } catch {
916
1449
  return null;
917
1450
  }
@@ -1027,15 +1560,14 @@ async function installPackage(pkg, detectedTools, projectRoot, options) {
1027
1560
  async function installRule(pkg, tool, content, projectRoot, options) {
1028
1561
  const toolId = tool.config.id;
1029
1562
  const rulesConfig = tool.config.rules;
1030
- const basePath = options.global ? rulesConfig.globalPath : path3.join(projectRoot, rulesConfig.projectPath);
1563
+ const basePath = options.global ? rulesConfig.globalPath : path4.join(projectRoot, rulesConfig.projectPath);
1031
1564
  const override = pkg.manifest.install?.[toolId];
1032
1565
  if (override?.action) {
1033
1566
  return handleSpecialAction(pkg, tool, content, projectRoot, options, override.action);
1034
1567
  }
1035
- const convertedContent = convertToToolFormat(content, tool.config, pkg.manifest.name);
1036
1568
  const filename = `${pkg.manifest.name}${rulesConfig.fileExtension}`;
1037
- const targetPath = path3.join(basePath, filename);
1038
- if (!options.force && await fileExists(targetPath)) {
1569
+ const targetPath = path4.join(basePath, filename);
1570
+ if (!options.force && await fileExists2(targetPath)) {
1039
1571
  return {
1040
1572
  tool: toolId,
1041
1573
  toolName: tool.config.name,
@@ -1047,6 +1579,7 @@ async function installRule(pkg, tool, content, projectRoot, options) {
1047
1579
  };
1048
1580
  }
1049
1581
  if (options.dryRun) {
1582
+ const method = options.installMethod ?? "symlink";
1050
1583
  return {
1051
1584
  tool: toolId,
1052
1585
  toolName: tool.config.name,
@@ -1054,18 +1587,68 @@ async function installRule(pkg, tool, content, projectRoot, options) {
1054
1587
  path: targetPath,
1055
1588
  success: true,
1056
1589
  skipped: true,
1057
- skipReason: "Dry run"
1590
+ skipReason: `Dry run (would use ${method})`
1591
+ };
1592
+ }
1593
+ const useSymlink = options.installMethod !== "copy";
1594
+ if (useSymlink) {
1595
+ try {
1596
+ const supported = await symlinkSupported();
1597
+ if (!supported) {
1598
+ return await installWithCopyMode(pkg, tool, content, basePath, options);
1599
+ }
1600
+ const installedPath = await installWithSymlink(
1601
+ pkg.manifest.name,
1602
+ content,
1603
+ tool.config,
1604
+ basePath,
1605
+ { force: options.force }
1606
+ );
1607
+ return {
1608
+ tool: toolId,
1609
+ toolName: tool.config.name,
1610
+ type: "rule",
1611
+ path: installedPath,
1612
+ success: true
1613
+ };
1614
+ } catch (error) {
1615
+ const err = error;
1616
+ if (err.code === "EPERM" || err.code === "ENOTSUP") {
1617
+ return await installWithCopyMode(pkg, tool, content, basePath, options);
1618
+ }
1619
+ throw error;
1620
+ }
1621
+ } else {
1622
+ return await installWithCopyMode(pkg, tool, content, basePath, options);
1623
+ }
1624
+ }
1625
+ async function installWithCopyMode(pkg, tool, content, basePath, options) {
1626
+ const toolId = tool.config.id;
1627
+ try {
1628
+ const installedPath = await installWithCopy(
1629
+ pkg.manifest.name,
1630
+ content,
1631
+ tool.config,
1632
+ basePath,
1633
+ { force: options.force }
1634
+ );
1635
+ return {
1636
+ tool: toolId,
1637
+ toolName: tool.config.name,
1638
+ type: "rule",
1639
+ path: installedPath,
1640
+ success: true
1641
+ };
1642
+ } catch (error) {
1643
+ return {
1644
+ tool: toolId,
1645
+ toolName: tool.config.name,
1646
+ type: "rule",
1647
+ path: "",
1648
+ success: false,
1649
+ error: error instanceof Error ? error.message : String(error)
1058
1650
  };
1059
1651
  }
1060
- await ensureDir(basePath);
1061
- await fs2.writeFile(targetPath, convertedContent, "utf-8");
1062
- return {
1063
- tool: toolId,
1064
- toolName: tool.config.name,
1065
- type: "rule",
1066
- path: targetPath,
1067
- success: true
1068
- };
1069
1652
  }
1070
1653
  async function installSkill(pkg, tool, content, projectRoot, options) {
1071
1654
  const toolId = tool.config.id;
@@ -1081,10 +1664,10 @@ async function installSkill(pkg, tool, content, projectRoot, options) {
1081
1664
  skipReason: "Tool does not support skills"
1082
1665
  };
1083
1666
  }
1084
- const basePath = options.global ? skillsConfig.globalPath : path3.join(projectRoot, skillsConfig.projectPath);
1085
- const skillDir = path3.join(basePath, pkg.manifest.name);
1086
- const skillPath = path3.join(skillDir, "SKILL.md");
1087
- if (!options.force && await fileExists(skillPath)) {
1667
+ const basePath = options.global ? skillsConfig.globalPath : path4.join(projectRoot, skillsConfig.projectPath);
1668
+ const skillDir = path4.join(basePath, pkg.manifest.name);
1669
+ const skillPath = path4.join(skillDir, "SKILL.md");
1670
+ if (!options.force && await fileExists2(skillPath)) {
1088
1671
  return {
1089
1672
  tool: toolId,
1090
1673
  toolName: tool.config.name,
@@ -1106,13 +1689,13 @@ async function installSkill(pkg, tool, content, projectRoot, options) {
1106
1689
  skipReason: "Dry run"
1107
1690
  };
1108
1691
  }
1109
- await ensureDir(skillDir);
1110
- await fs2.writeFile(skillPath, content, "utf-8");
1692
+ await ensureDir2(skillDir);
1693
+ await fs3.writeFile(skillPath, content, "utf-8");
1111
1694
  for (const file of pkg.files) {
1112
1695
  if (file.path !== "SKILL.md" && file.path !== "rule.md" && file.path !== "manifest.json") {
1113
- const targetFile = path3.join(skillDir, file.path);
1114
- await ensureDir(path3.dirname(targetFile));
1115
- await fs2.writeFile(targetFile, file.content, "utf-8");
1696
+ const targetFile = path4.join(skillDir, file.path);
1697
+ await ensureDir2(path4.dirname(targetFile));
1698
+ await fs3.writeFile(targetFile, file.content, "utf-8");
1116
1699
  }
1117
1700
  }
1118
1701
  return {
@@ -1128,7 +1711,7 @@ async function handleSpecialAction(pkg, tool, content, projectRoot, options, act
1128
1711
  const { body } = parseFrontmatter(content);
1129
1712
  switch (action) {
1130
1713
  case "append-to-agents-md": {
1131
- const agentsPath = options.global ? path3.join(tool.config.rules.globalPath, "AGENTS.md") : path3.join(projectRoot, "AGENTS.md");
1714
+ const agentsPath = options.global ? path4.join(tool.config.rules.globalPath, "AGENTS.md") : path4.join(projectRoot, "AGENTS.md");
1132
1715
  if (options.dryRun) {
1133
1716
  return {
1134
1717
  tool: toolId,
@@ -1163,9 +1746,9 @@ ${body}
1163
1746
  [\\s\\S]*?(?=
1164
1747
  ## |$)`, "g");
1165
1748
  const updated = existing.replace(regex, "") + section;
1166
- await fs2.writeFile(agentsPath, updated, "utf-8");
1749
+ await fs3.writeFile(agentsPath, updated, "utf-8");
1167
1750
  } else {
1168
- await fs2.writeFile(agentsPath, existing + section, "utf-8");
1751
+ await fs3.writeFile(agentsPath, existing + section, "utf-8");
1169
1752
  }
1170
1753
  return {
1171
1754
  tool: toolId,
@@ -1176,7 +1759,7 @@ ${body}
1176
1759
  };
1177
1760
  }
1178
1761
  case "append-to-gemini-md": {
1179
- const geminiPath = options.global ? path3.join(tool.config.rules.globalPath, "GEMINI.md") : path3.join(projectRoot, "GEMINI.md");
1762
+ const geminiPath = options.global ? path4.join(tool.config.rules.globalPath, "GEMINI.md") : path4.join(projectRoot, "GEMINI.md");
1180
1763
  if (options.dryRun) {
1181
1764
  return {
1182
1765
  tool: toolId,
@@ -1205,7 +1788,7 @@ ${body}
1205
1788
  skipReason: "Section already exists in GEMINI.md"
1206
1789
  };
1207
1790
  }
1208
- await fs2.writeFile(geminiPath, existing + section, "utf-8");
1791
+ await fs3.writeFile(geminiPath, existing + section, "utf-8");
1209
1792
  return {
1210
1793
  tool: toolId,
1211
1794
  toolName: tool.config.name,
@@ -1215,8 +1798,8 @@ ${body}
1215
1798
  };
1216
1799
  }
1217
1800
  case "add-to-read-config": {
1218
- const rulePath = path3.join(projectRoot, `${pkg.manifest.name}.md`);
1219
- const configPath = path3.join(projectRoot, ".aider.conf.yml");
1801
+ const rulePath = path4.join(projectRoot, `${pkg.manifest.name}.md`);
1802
+ const configPath = path4.join(projectRoot, ".aider.conf.yml");
1220
1803
  if (options.dryRun) {
1221
1804
  return {
1222
1805
  tool: toolId,
@@ -1228,7 +1811,7 @@ ${body}
1228
1811
  skipReason: "Dry run - would create file and update .aider.conf.yml"
1229
1812
  };
1230
1813
  }
1231
- await fs2.writeFile(rulePath, body, "utf-8");
1814
+ await fs3.writeFile(rulePath, body, "utf-8");
1232
1815
  const existingConfig = await readFile(configPath);
1233
1816
  let config = {};
1234
1817
  if (existingConfig) {
@@ -1250,7 +1833,49 @@ ${body}
1250
1833
  } else {
1251
1834
  config.read = fileName;
1252
1835
  }
1253
- await fs2.writeFile(configPath, YAML2.stringify(config), "utf-8");
1836
+ await fs3.writeFile(configPath, YAML2.stringify(config), "utf-8");
1837
+ return {
1838
+ tool: toolId,
1839
+ toolName: tool.config.name,
1840
+ type: "rule",
1841
+ path: rulePath,
1842
+ success: true
1843
+ };
1844
+ }
1845
+ case "add-with-gemini-import": {
1846
+ const rulesDir = options.global ? tool.config.rules.globalPath : path4.join(projectRoot, ".gemini", "rules");
1847
+ const rulePath = path4.join(rulesDir, `${pkg.manifest.name}.md`);
1848
+ const geminiMdPath = options.global ? path4.join(path4.dirname(tool.config.rules.globalPath), "GEMINI.md") : path4.join(projectRoot, "GEMINI.md");
1849
+ if (options.dryRun) {
1850
+ return {
1851
+ tool: toolId,
1852
+ toolName: tool.config.name,
1853
+ type: "rule",
1854
+ path: rulePath,
1855
+ success: true,
1856
+ skipped: true,
1857
+ skipReason: "Dry run - would create rule file and add @import to GEMINI.md"
1858
+ };
1859
+ }
1860
+ if (!options.force && await fileExists2(rulePath)) {
1861
+ return {
1862
+ tool: toolId,
1863
+ toolName: tool.config.name,
1864
+ type: "rule",
1865
+ path: rulePath,
1866
+ success: false,
1867
+ skipped: true,
1868
+ skipReason: "File already exists (use --force to overwrite)"
1869
+ };
1870
+ }
1871
+ await ensureDir2(rulesDir);
1872
+ await fs3.writeFile(rulePath, body, "utf-8");
1873
+ const existingGemini = await readFile(geminiMdPath) || "";
1874
+ const importLine = `@rules/${pkg.manifest.name}.md`;
1875
+ if (!existingGemini.includes(importLine)) {
1876
+ const newContent = existingGemini.trim() ? existingGemini.trimEnd() + "\n" + importLine + "\n" : importLine + "\n";
1877
+ await fs3.writeFile(geminiMdPath, newContent, "utf-8");
1878
+ }
1254
1879
  return {
1255
1880
  tool: toolId,
1256
1881
  toolName: tool.config.name,
@@ -1275,12 +1900,12 @@ async function uninstallPackage(packageName, detectedTools, projectRoot, options
1275
1900
  for (const tool of detectedTools) {
1276
1901
  const toolId = tool.config.id;
1277
1902
  const rulesConfig = tool.config.rules;
1278
- const ruleBasePath = options.global ? rulesConfig.globalPath : path3.join(projectRoot, rulesConfig.projectPath);
1903
+ const ruleBasePath = options.global ? rulesConfig.globalPath : path4.join(projectRoot, rulesConfig.projectPath);
1279
1904
  const ruleFilename = `${packageName}${rulesConfig.fileExtension}`;
1280
- const rulePath = path3.join(ruleBasePath, ruleFilename);
1281
- if (await fileExists(rulePath)) {
1905
+ const rulePath = path4.join(ruleBasePath, ruleFilename);
1906
+ if (await fileExists2(rulePath)) {
1282
1907
  if (!options.dryRun) {
1283
- await fs2.unlink(rulePath);
1908
+ await fs3.unlink(rulePath);
1284
1909
  }
1285
1910
  results.push({
1286
1911
  tool: toolId,
@@ -1293,11 +1918,11 @@ async function uninstallPackage(packageName, detectedTools, projectRoot, options
1293
1918
  });
1294
1919
  }
1295
1920
  if (tool.config.skills) {
1296
- const skillBasePath = options.global ? tool.config.skills.globalPath : path3.join(projectRoot, tool.config.skills.projectPath);
1297
- const skillDir = path3.join(skillBasePath, packageName);
1298
- if (await fileExists(skillDir)) {
1921
+ const skillBasePath = options.global ? tool.config.skills.globalPath : path4.join(projectRoot, tool.config.skills.projectPath);
1922
+ const skillDir = path4.join(skillBasePath, packageName);
1923
+ if (await fileExists2(skillDir)) {
1299
1924
  if (!options.dryRun) {
1300
- await fs2.rm(skillDir, { recursive: true });
1925
+ await fs3.rm(skillDir, { recursive: true });
1301
1926
  }
1302
1927
  results.push({
1303
1928
  tool: toolId,
@@ -1320,9 +1945,9 @@ async function listInstalled(detectedTools, projectRoot, options) {
1320
1945
  const rulesConfig = tool.config.rules;
1321
1946
  const rules = [];
1322
1947
  const skills = [];
1323
- const ruleBasePath = options.global ? rulesConfig.globalPath : path3.join(projectRoot, rulesConfig.projectPath);
1948
+ const ruleBasePath = options.global ? rulesConfig.globalPath : path4.join(projectRoot, rulesConfig.projectPath);
1324
1949
  try {
1325
- const files = await fs2.readdir(ruleBasePath);
1950
+ const files = await fs3.readdir(ruleBasePath);
1326
1951
  for (const file of files) {
1327
1952
  if (file.endsWith(rulesConfig.fileExtension)) {
1328
1953
  rules.push(file.replace(rulesConfig.fileExtension, ""));
@@ -1331,12 +1956,12 @@ async function listInstalled(detectedTools, projectRoot, options) {
1331
1956
  } catch {
1332
1957
  }
1333
1958
  if (tool.config.skills) {
1334
- const skillBasePath = options.global ? tool.config.skills.globalPath : path3.join(projectRoot, tool.config.skills.projectPath);
1959
+ const skillBasePath = options.global ? tool.config.skills.globalPath : path4.join(projectRoot, tool.config.skills.projectPath);
1335
1960
  try {
1336
- const dirs = await fs2.readdir(skillBasePath);
1961
+ const dirs = await fs3.readdir(skillBasePath);
1337
1962
  for (const dir of dirs) {
1338
- const skillPath = path3.join(skillBasePath, dir, "SKILL.md");
1339
- if (await fileExists(skillPath)) {
1963
+ const skillPath = path4.join(skillBasePath, dir, "SKILL.md");
1964
+ if (await fileExists2(skillPath)) {
1340
1965
  skills.push(dir);
1341
1966
  }
1342
1967
  }
@@ -1351,9 +1976,9 @@ async function listInstalled(detectedTools, projectRoot, options) {
1351
1976
  }
1352
1977
 
1353
1978
  // src/registry.ts
1354
- import fs3 from "fs/promises";
1355
- import path4 from "path";
1356
- import os2 from "os";
1979
+ import fs4 from "fs/promises";
1980
+ import path5 from "path";
1981
+ import os3 from "os";
1357
1982
  var SCHEMA_VERSION = 1;
1358
1983
  function emptyRegistry() {
1359
1984
  return { schemaVersion: SCHEMA_VERSION, packages: {} };
@@ -1363,17 +1988,17 @@ function registryKey(name, installType) {
1363
1988
  }
1364
1989
  function getRegistryPath(projectRoot, global) {
1365
1990
  if (global) {
1366
- return path4.join(os2.homedir(), ".faster", "installed.json");
1991
+ return path5.join(os3.homedir(), ".faster", "installed.json");
1367
1992
  }
1368
- return path4.join(projectRoot, ".faster", "installed.json");
1993
+ return path5.join(projectRoot, ".faster", "installed.json");
1369
1994
  }
1370
- async function ensureDir2(dir) {
1371
- await fs3.mkdir(dir, { recursive: true });
1995
+ async function ensureDir3(dir) {
1996
+ await fs4.mkdir(dir, { recursive: true });
1372
1997
  }
1373
1998
  async function readRegistry(projectRoot, global) {
1374
1999
  const registryPath = getRegistryPath(projectRoot, global);
1375
2000
  try {
1376
- const raw = await fs3.readFile(registryPath, "utf-8");
2001
+ const raw = await fs4.readFile(registryPath, "utf-8");
1377
2002
  const parsed = JSON.parse(raw);
1378
2003
  if (!parsed || parsed.schemaVersion !== SCHEMA_VERSION || typeof parsed.packages !== "object") {
1379
2004
  return emptyRegistry();
@@ -1385,8 +2010,8 @@ async function readRegistry(projectRoot, global) {
1385
2010
  }
1386
2011
  async function writeRegistry(projectRoot, global, registry) {
1387
2012
  const registryPath = getRegistryPath(projectRoot, global);
1388
- await ensureDir2(path4.dirname(registryPath));
1389
- await fs3.writeFile(registryPath, JSON.stringify(registry, null, 2), "utf-8");
2013
+ await ensureDir3(path5.dirname(registryPath));
2014
+ await fs4.writeFile(registryPath, JSON.stringify(registry, null, 2), "utf-8");
1390
2015
  }
1391
2016
  function upsertInstalledPackage(registry, record) {
1392
2017
  registry.packages[registryKey(record.name, record.installType)] = record;
@@ -1404,13 +2029,13 @@ function listInstalledPackages(registry) {
1404
2029
  }
1405
2030
 
1406
2031
  // src/commands/shared/package-helpers.ts
1407
- import path5 from "path";
2032
+ import path6 from "path";
1408
2033
  async function resolveDetectedTools(projectRoot, options, defaultTools) {
1409
2034
  let detectedTools = await detectTools(projectRoot);
1410
2035
  if (detectedTools.length === 0) {
1411
2036
  detectedTools = Object.values(TOOL_CONFIGS).map((config) => ({
1412
2037
  config,
1413
- projectPath: path5.join(projectRoot, config.rules.projectPath),
2038
+ projectPath: path6.join(projectRoot, config.rules.projectPath),
1414
2039
  globalPath: config.rules.globalPath
1415
2040
  }));
1416
2041
  }
@@ -1431,24 +2056,93 @@ async function resolveDetectedTools(projectRoot, options, defaultTools) {
1431
2056
  }
1432
2057
 
1433
2058
  // src/commands/package/install.ts
1434
- import fs4 from "fs/promises";
1435
- import path6 from "path";
2059
+ import fs5 from "fs/promises";
2060
+ import path7 from "path";
1436
2061
  function registerInstallCommand(program2) {
1437
- program2.command("install <package>").alias("add").description("Install a skill or rule from faster.dev").option("-g, --global", "Install globally instead of to project").option("-t, --tools <tools>", "Comma-separated list of tools to install to").option("--as-skill", "Install as a skill (where supported)").option("-f, --force", "Overwrite existing installations").option("--dry-run", "Show what would be installed without making changes").option("--from-file <path>", "Install from a local package directory").action(async (packageInput, opts) => {
2062
+ program2.command("install <package>").alias("add").description("Install a skill or rule from faster.dev").option("-g, --global", "Install globally instead of to project").option("-t, --tools <tools>", "Comma-separated list of tools to install to").option("--as-skill", "Install as a skill (where supported)").option("-f, --force", "Overwrite existing installations").option("--dry-run", "Show what would be installed without making changes").option("--from-file <path>", "Install from a local package directory").option("--copy", "Install as copies instead of symlinks").action(async (packageInput, opts) => {
1438
2063
  const { json, verbose } = program2.opts();
1439
2064
  const projectRoot = process.cwd();
1440
- const { name: packageName, version } = parsePackageSpec(packageInput);
1441
2065
  const options = {
1442
2066
  global: opts.global ?? false,
1443
2067
  tools: opts.tools ? opts.tools.split(",") : void 0,
1444
2068
  asSkill: opts.asSkill ?? false,
1445
2069
  force: opts.force ?? false,
1446
- dryRun: opts.dryRun ?? false
2070
+ dryRun: opts.dryRun ?? false,
2071
+ installMethod: opts.copy ? "copy" : "symlink"
1447
2072
  };
1448
2073
  const defaultTools = getDefaultTools();
1449
2074
  const spinner = new SpinnerManager("Detecting tools...", json ?? false);
1450
2075
  try {
1451
2076
  const detectedTools = await resolveDetectedTools(projectRoot, options, defaultTools);
2077
+ const scope = getScopeFromInput(packageInput);
2078
+ if (scope && !opts.fromFile) {
2079
+ const api = new FasterAPI(getConfig());
2080
+ spinner.text(`Fetching packages in @${scope}...`);
2081
+ const scopePackages = await api.getPackagesByScope(scope);
2082
+ if (scopePackages.length === 0) {
2083
+ spinner.fail(`No packages found in scope @${scope}`);
2084
+ if (json) outputJson({ ok: false, error: `No packages found in scope @${scope}` });
2085
+ setExitCode(EXIT_CODES.ERROR);
2086
+ return;
2087
+ }
2088
+ spinner.stop();
2089
+ console.log(chalk8.bold(`
2090
+ Found ${scopePackages.length} package(s) in @${scope}:
2091
+ `));
2092
+ for (const scopePkg of scopePackages) {
2093
+ console.log(` ${chalk8.cyan(scopePkg.name)} ${chalk8.dim(`v${scopePkg.version}`)}`);
2094
+ console.log(` ${chalk8.dim(scopePkg.description)}`);
2095
+ }
2096
+ console.log();
2097
+ if (!options.dryRun && !json) {
2098
+ const confirmed = await confirm("Install all packages?");
2099
+ if (!confirmed) {
2100
+ console.log(chalk8.yellow("\nInstallation cancelled."));
2101
+ return;
2102
+ }
2103
+ console.log();
2104
+ }
2105
+ const allResults = [];
2106
+ for (const scopePkg of scopePackages) {
2107
+ const pkgSpinner = new SpinnerManager(`Installing ${scopePkg.name}...`, json ?? false);
2108
+ try {
2109
+ const pkg2 = await api.downloadPackage(scopePkg.name);
2110
+ const results2 = await installPackage(pkg2, detectedTools, projectRoot, options);
2111
+ const installType2 = resolveInstallType(options.asSkill);
2112
+ const successTools2 = results2.filter((r) => r.success && !r.skipped).map((r) => r.tool);
2113
+ if (!options.dryRun && successTools2.length > 0) {
2114
+ const registry = await readRegistry(projectRoot, options.global);
2115
+ upsertInstalledPackage(registry, {
2116
+ name: pkg2.manifest.name,
2117
+ version: pkg2.manifest.version,
2118
+ installType: installType2,
2119
+ tools: successTools2,
2120
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
2121
+ source: "registry"
2122
+ });
2123
+ await writeRegistry(projectRoot, options.global, registry);
2124
+ }
2125
+ pkgSpinner.stop();
2126
+ allResults.push({ pkg: pkg2, results: results2 });
2127
+ if (!json) {
2128
+ printInstallResults(pkg2, results2);
2129
+ }
2130
+ } catch (error) {
2131
+ pkgSpinner.fail(`Failed to install ${scopePkg.name}: ${stringifyError(error, verbose)}`);
2132
+ }
2133
+ }
2134
+ if (json) {
2135
+ outputJson({
2136
+ scope,
2137
+ packages: allResults.map(({ pkg: pkg2, results: results2 }) => ({
2138
+ package: pkg2.manifest,
2139
+ results: results2
2140
+ }))
2141
+ });
2142
+ }
2143
+ return;
2144
+ }
2145
+ const { name: packageName, version } = parsePackageSpec(packageInput);
1452
2146
  spinner.text(`Fetching package: ${packageName}...`);
1453
2147
  let pkg;
1454
2148
  const isLocal = Boolean(opts.fromFile);
@@ -1537,25 +2231,25 @@ function printInstallResults(pkg, results) {
1537
2231
  }
1538
2232
  async function loadLocalPackage(dir) {
1539
2233
  const files = [];
1540
- const manifestPath = path6.join(dir, "manifest.json");
1541
- const manifestContent = await fs4.readFile(manifestPath, "utf-8");
2234
+ const manifestPath = path7.join(dir, "manifest.json");
2235
+ const manifestContent = await fs5.readFile(manifestPath, "utf-8");
1542
2236
  files.push({ path: "manifest.json", content: manifestContent });
1543
2237
  const manifest = JSON.parse(manifestContent);
1544
- const entries = await fs4.readdir(dir, { withFileTypes: true });
2238
+ const entries = await fs5.readdir(dir, { withFileTypes: true });
1545
2239
  for (const entry of entries) {
1546
2240
  if (entry.isFile() && entry.name !== "manifest.json") {
1547
2241
  if (entry.name.endsWith(".md") || entry.name.endsWith(".mdc") || entry.name.endsWith(".txt")) {
1548
- const content = await fs4.readFile(path6.join(dir, entry.name), "utf-8");
2242
+ const content = await fs5.readFile(path7.join(dir, entry.name), "utf-8");
1549
2243
  files.push({ path: entry.name, content });
1550
2244
  }
1551
2245
  }
1552
2246
  }
1553
- const assetsDir = path6.join(dir, "assets");
2247
+ const assetsDir = path7.join(dir, "assets");
1554
2248
  try {
1555
- const assetEntries = await fs4.readdir(assetsDir, { withFileTypes: true });
2249
+ const assetEntries = await fs5.readdir(assetsDir, { withFileTypes: true });
1556
2250
  for (const entry of assetEntries) {
1557
2251
  if (entry.isFile()) {
1558
- const content = await fs4.readFile(path6.join(assetsDir, entry.name), "utf-8");
2252
+ const content = await fs5.readFile(path7.join(assetsDir, entry.name), "utf-8");
1559
2253
  files.push({ path: `assets/${entry.name}`, content });
1560
2254
  }
1561
2255
  }