library-skills 0.0.10 → 0.0.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "library-skills",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "Library Skills, AI Agents using libraries, as intended, always up to date.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+
4
+ interface Skill {
5
+ name: string;
6
+ description: string;
7
+ path: string;
8
+ packageName: string;
9
+ skillDir: string;
10
+ packageVersion: string;
11
+ }
12
+
13
+ interface InstallTarget {
14
+ name: string;
15
+ path: string;
16
+ }
17
+
18
+ interface UvWorkspace {
19
+ root: string;
20
+ members: string[];
21
+ currentMember: string | null;
22
+ }
23
+ interface NodeWorkspace {
24
+ root: string;
25
+ members: string[];
26
+ currentMember: string | null;
27
+ }
28
+
29
+ interface ProjectContext {
30
+ cwd: string;
31
+ projectRoot: string;
32
+ targetEnvironment: string | null;
33
+ sitePackagesDir: string | null;
34
+ nodeModulesDir: string | null;
35
+ workspace: UvWorkspace | null;
36
+ nodeWorkspace: NodeWorkspace | null;
37
+ workspaceDependencyFiles: string[];
38
+ nodeWorkspaceDependencyFiles: string[];
39
+ dependencyFiles: string[];
40
+ }
41
+ interface InstalledStatus {
42
+ target: InstallTarget;
43
+ name: string;
44
+ type: "symlink" | "directory" | "missing";
45
+ path: string;
46
+ targetPath: string | null;
47
+ status: "up to date" | "broken" | "outdated" | "orphaned" | "name mismatch" | "hand-authored" | "new";
48
+ skill: Skill | null;
49
+ }
50
+ interface GlobalOptions {
51
+ claude?: boolean;
52
+ yes?: boolean;
53
+ check?: boolean;
54
+ all?: boolean;
55
+ skill?: string[];
56
+ }
57
+ interface ListOptions {
58
+ installed?: boolean;
59
+ json?: boolean;
60
+ claude?: boolean;
61
+ all?: boolean;
62
+ }
63
+ interface ScanOptions {
64
+ all?: boolean;
65
+ json?: boolean;
66
+ }
67
+ declare function getProjectContext(cwd?: string): ProjectContext;
68
+ declare function topLevelSkills({ context, skills, includeAll, }: {
69
+ context: ProjectContext;
70
+ skills: Skill[];
71
+ includeAll: boolean;
72
+ }): Skill[];
73
+ declare function displayPath(path: string | null | undefined, projectRoot: string): string;
74
+ declare function printTable(columns: string[], rows: Array<Record<string, string>>): void;
75
+ declare function findCollisions(skills: Skill[]): Set<string>;
76
+ declare function filterInstallableSkills({ skills, selectedNames, includeAll, }: {
77
+ skills: Skill[];
78
+ selectedNames: string[];
79
+ includeAll: boolean;
80
+ }): Skill[];
81
+ declare function installedStatuses({ targets, skills, }: {
82
+ targets: InstallTarget[];
83
+ skills: Skill[];
84
+ }): InstalledStatus[];
85
+ declare function installSelected({ skills, targets, projectRoot, copy, }: {
86
+ skills: Skill[];
87
+ targets: InstallTarget[];
88
+ projectRoot: string;
89
+ copy?: boolean;
90
+ }): number;
91
+ declare function sync(options: GlobalOptions): Promise<void>;
92
+ declare function scanCommand(options: ScanOptions): void;
93
+ declare function listCommand(options: ListOptions): void;
94
+ declare function createProgram(): Command;
95
+ declare function main(argv?: string[]): Promise<void>;
96
+ declare const testing: {
97
+ filterInstallableSkills: typeof filterInstallableSkills;
98
+ findCollisions: typeof findCollisions;
99
+ getProjectContext: typeof getProjectContext;
100
+ installSelected: typeof installSelected;
101
+ installedStatuses: typeof installedStatuses;
102
+ listCommand: typeof listCommand;
103
+ scanCommand: typeof scanCommand;
104
+ sync: typeof sync;
105
+ topLevelSkills: typeof topLevelSkills;
106
+ displayPath: typeof displayPath;
107
+ printTable: typeof printTable;
108
+ };
109
+
110
+ export { createProgram, main, testing };
package/ts/dist/cli.js CHANGED
@@ -522,6 +522,28 @@ function getNodeTopLevelDeps(projectRoot) {
522
522
  if (!existsSync2(packageJson)) {
523
523
  return null;
524
524
  }
525
+ return getNodeTopLevelDepsFromFiles([packageJson]);
526
+ }
527
+ function getNodeTopLevelDepsFromFiles(packageJsonFiles) {
528
+ if (packageJsonFiles.length === 0) {
529
+ return null;
530
+ }
531
+ const deps = /* @__PURE__ */ new Set();
532
+ let found = false;
533
+ for (const packageJson of packageJsonFiles) {
534
+ if (!existsSync2(packageJson)) {
535
+ continue;
536
+ }
537
+ const data = readPackageJson(packageJson);
538
+ if (data === null) {
539
+ return null;
540
+ }
541
+ found = true;
542
+ extractNodeTopLevelDeps(data, deps);
543
+ }
544
+ return found ? deps : null;
545
+ }
546
+ function readPackageJson(packageJson) {
525
547
  let data;
526
548
  try {
527
549
  data = JSON.parse(readFileSync2(packageJson, "utf8"));
@@ -529,9 +551,11 @@ function getNodeTopLevelDeps(projectRoot) {
529
551
  return null;
530
552
  }
531
553
  if (!isRecord2(data)) {
532
- return /* @__PURE__ */ new Set();
554
+ return {};
533
555
  }
534
- const deps = /* @__PURE__ */ new Set();
556
+ return data;
557
+ }
558
+ function extractNodeTopLevelDeps(data, deps) {
535
559
  for (const field of [
536
560
  "dependencies",
537
561
  "devDependencies",
@@ -546,7 +570,6 @@ function getNodeTopLevelDeps(projectRoot) {
546
570
  deps.add(normalizePackageName(packageName));
547
571
  }
548
572
  }
549
- return deps;
550
573
  }
551
574
  function getTopLevelDeps(projectRoot) {
552
575
  const dependencySets = [
@@ -561,6 +584,9 @@ function getTopLevelDeps(projectRoot) {
561
584
  function getWorkspaceTopLevelDeps(pyprojects) {
562
585
  return getPythonTopLevelDepsFromFiles(pyprojects);
563
586
  }
587
+ function getNodeWorkspaceTopLevelDeps(packageJsonFiles) {
588
+ return getNodeTopLevelDepsFromFiles(packageJsonFiles);
589
+ }
564
590
  function extractDepsFromSpecs(depSpecs, deps) {
565
591
  for (const depSpec of depSpecs) {
566
592
  if (typeof depSpec !== "string") {
@@ -736,6 +762,26 @@ function findUvWorkspace(cwd) {
736
762
  }
737
763
  return null;
738
764
  }
765
+ function findNodeWorkspace(cwd) {
766
+ for (const directory of ancestors(cwd)) {
767
+ const packageJson = `${directory}/package.json`;
768
+ if (!existsSync4(packageJson)) {
769
+ continue;
770
+ }
771
+ const data = readPackageJson2(packageJson);
772
+ const memberGlobs = getNodeWorkspaceGlobs(data);
773
+ if (memberGlobs === null) {
774
+ continue;
775
+ }
776
+ const members = findNodeWorkspaceMembers(directory, memberGlobs);
777
+ return {
778
+ root: directory,
779
+ members,
780
+ currentMember: findCurrentMember(cwd, members)
781
+ };
782
+ }
783
+ return null;
784
+ }
739
785
  function workspaceDependencyFiles(workspace) {
740
786
  if (workspace.currentMember !== null) {
741
787
  return [`${workspace.currentMember}/pyproject.toml`];
@@ -745,6 +791,15 @@ function workspaceDependencyFiles(workspace) {
745
791
  ...workspace.members.map((member) => `${member}/pyproject.toml`)
746
792
  ].filter((path) => existsSync4(path));
747
793
  }
794
+ function nodeWorkspaceDependencyFiles(workspace) {
795
+ if (workspace.currentMember !== null) {
796
+ return [`${workspace.currentMember}/package.json`];
797
+ }
798
+ return [
799
+ `${workspace.root}/package.json`,
800
+ ...workspace.members.map((member) => `${member}/package.json`)
801
+ ].filter((path) => existsSync4(path));
802
+ }
748
803
  function readPyproject(path) {
749
804
  try {
750
805
  const data = parse3(readFileSync3(path, "utf8"));
@@ -753,6 +808,14 @@ function readPyproject(path) {
753
808
  return {};
754
809
  }
755
810
  }
811
+ function readPackageJson2(path) {
812
+ try {
813
+ const data = JSON.parse(readFileSync3(path, "utf8"));
814
+ return isRecord3(data) ? data : {};
815
+ } catch {
816
+ return {};
817
+ }
818
+ }
756
819
  function hasUvWorkspace(data) {
757
820
  const tool = data["tool"];
758
821
  if (!isRecord3(tool)) {
@@ -789,6 +852,36 @@ function findWorkspaceMembers(root, data) {
789
852
  }
790
853
  return [...members].sort();
791
854
  }
855
+ function getNodeWorkspaceGlobs(data) {
856
+ const workspaces = data["workspaces"];
857
+ if (Array.isArray(workspaces)) {
858
+ return workspaces.filter((value) => typeof value === "string");
859
+ }
860
+ if (isRecord3(workspaces)) {
861
+ const packages = workspaces["packages"];
862
+ if (Array.isArray(packages)) {
863
+ return packages.filter((value) => typeof value === "string");
864
+ }
865
+ }
866
+ return null;
867
+ }
868
+ function findNodeWorkspaceMembers(root, memberGlobs) {
869
+ const includes = memberGlobs.filter((pattern) => !pattern.startsWith("!"));
870
+ const excludes = memberGlobs.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
871
+ const members = /* @__PURE__ */ new Set();
872
+ for (const memberGlob of includes) {
873
+ for (const member of expandMemberGlob(root, memberGlob)) {
874
+ const relativePath = relative3(root, member).split(sep).join("/");
875
+ if (isExcluded(relativePath, excludes)) {
876
+ continue;
877
+ }
878
+ if (isFile(`${member}/package.json`)) {
879
+ members.add(resolve3(member));
880
+ }
881
+ }
882
+ }
883
+ return [...members].sort();
884
+ }
792
885
  function getWorkspaceTable(data) {
793
886
  const tool = data["tool"];
794
887
  if (!isRecord3(tool)) {
@@ -896,6 +989,10 @@ function findProjectRoot(cwd) {
896
989
  if (workspace !== null) {
897
990
  return workspace.root;
898
991
  }
992
+ const nodeWorkspace = findNodeWorkspace(cwd);
993
+ if (nodeWorkspace !== null) {
994
+ return nodeWorkspace.root;
995
+ }
899
996
  for (const directory of ancestors2(cwd)) {
900
997
  if (isFile2(join4(directory, "pyproject.toml")) || isFile2(join4(directory, "uv.lock")) || venvFromDotVenv(directory) !== null || isFile2(join4(directory, "package.json")) || isDirectory4(join4(directory, "node_modules"))) {
901
998
  return directory;
@@ -1023,7 +1120,10 @@ function isDirectory4(path) {
1023
1120
  // ts/src/cli.ts
1024
1121
  function getProjectContext(cwd = process.cwd()) {
1025
1122
  const workspace = findUvWorkspace(cwd);
1026
- const projectRoot = workspace?.root ?? findProjectRoot(cwd) ?? cwd;
1123
+ const nodeWorkspace = findNodeWorkspace(cwd);
1124
+ const projectRoot = workspace?.root ?? nodeWorkspace?.root ?? findProjectRoot(cwd) ?? cwd;
1125
+ const workspaceFiles = workspace === null ? [] : workspaceDependencyFiles(workspace);
1126
+ const nodeWorkspaceFiles = nodeWorkspace === null ? [] : nodeWorkspaceDependencyFiles(nodeWorkspace);
1027
1127
  const targetEnvironment = findVenv(cwd);
1028
1128
  const sitePackagesDir = targetEnvironment === null ? null : getSitePackagesDir(targetEnvironment);
1029
1129
  const nodeModulesDir = findNodeModules(cwd);
@@ -1034,7 +1134,10 @@ function getProjectContext(cwd = process.cwd()) {
1034
1134
  sitePackagesDir,
1035
1135
  nodeModulesDir,
1036
1136
  workspace,
1037
- dependencyFiles: workspace ? workspaceDependencyFiles(workspace) : []
1137
+ nodeWorkspace,
1138
+ workspaceDependencyFiles: workspaceFiles,
1139
+ nodeWorkspaceDependencyFiles: nodeWorkspaceFiles,
1140
+ dependencyFiles: [...workspaceFiles, ...nodeWorkspaceFiles]
1038
1141
  };
1039
1142
  }
1040
1143
  function scanContext(context) {
@@ -1077,8 +1180,8 @@ function topLevelSkills({
1077
1180
  return skills;
1078
1181
  }
1079
1182
  const topLevelDeps = getTopLevelDeps(context.projectRoot);
1080
- const workspaceTopLevelDeps = context.workspace === null ? null : getWorkspaceTopLevelDeps(context.dependencyFiles);
1081
- const selectedTopLevelDeps = context.workspace === null ? topLevelDeps : workspaceTopLevelDeps;
1183
+ const workspaceTopLevelDeps = getWorkspaceTopLevelDepsForContext(context);
1184
+ const selectedTopLevelDeps = context.workspace === null && context.nodeWorkspace === null ? topLevelDeps : workspaceTopLevelDeps;
1082
1185
  if (selectedTopLevelDeps === null) {
1083
1186
  return skills;
1084
1187
  }
@@ -1086,6 +1189,16 @@ function topLevelSkills({
1086
1189
  (skill) => selectedTopLevelDeps.has(normalizePackageName(skill.packageName))
1087
1190
  );
1088
1191
  }
1192
+ function getWorkspaceTopLevelDepsForContext(context) {
1193
+ const dependencySets = [
1194
+ context.workspace === null ? null : getWorkspaceTopLevelDeps(context.workspaceDependencyFiles),
1195
+ context.nodeWorkspace === null ? null : getNodeWorkspaceTopLevelDeps(context.nodeWorkspaceDependencyFiles)
1196
+ ].filter((deps) => deps !== null);
1197
+ if (dependencySets.length === 0) {
1198
+ return null;
1199
+ }
1200
+ return new Set(dependencySets.flatMap((deps) => [...deps]));
1201
+ }
1089
1202
  function printWarnings(warnings) {
1090
1203
  for (const warning of warnings) {
1091
1204
  console.log(`Warning: ${warning}`);
@@ -1111,6 +1224,11 @@ function printContext(context) {
1111
1224
  if (context.workspace.currentMember !== null) {
1112
1225
  console.log(`Workspace member: ${context.workspace.currentMember}`);
1113
1226
  }
1227
+ } else if (context.nodeWorkspace !== null) {
1228
+ console.log(`Workspace root: ${context.nodeWorkspace.root}`);
1229
+ if (context.nodeWorkspace.currentMember !== null) {
1230
+ console.log(`Workspace member: ${context.nodeWorkspace.currentMember}`);
1231
+ }
1114
1232
  }
1115
1233
  console.log(
1116
1234
  `Target Python environment: ${context.targetEnvironment ? displayPath(context.targetEnvironment, context.projectRoot) : "not found"}`
@@ -1151,8 +1269,8 @@ function scanJsonPayload({
1151
1269
  }) {
1152
1270
  return {
1153
1271
  project_root: context.projectRoot,
1154
- workspace_root: context.workspace?.root ?? "",
1155
- workspace_member: context.workspace?.currentMember ?? "",
1272
+ workspace_root: context.workspace?.root ?? context.nodeWorkspace?.root ?? "",
1273
+ workspace_member: context.workspace?.currentMember ?? context.nodeWorkspace?.currentMember ?? "",
1156
1274
  dependency_files: context.dependencyFiles,
1157
1275
  target_environment: context.targetEnvironment ?? "",
1158
1276
  node_modules: context.nodeModulesDir ?? "",
package/ts/dist/deps.cjs CHANGED
@@ -21,6 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var deps_exports = {};
22
22
  __export(deps_exports, {
23
23
  getNodeTopLevelDeps: () => getNodeTopLevelDeps,
24
+ getNodeTopLevelDepsFromFiles: () => getNodeTopLevelDepsFromFiles,
25
+ getNodeWorkspaceTopLevelDeps: () => getNodeWorkspaceTopLevelDeps,
24
26
  getPythonTopLevelDeps: () => getPythonTopLevelDeps,
25
27
  getPythonTopLevelDepsFromFiles: () => getPythonTopLevelDepsFromFiles,
26
28
  getTopLevelDeps: () => getTopLevelDeps,
@@ -98,6 +100,28 @@ function getNodeTopLevelDeps(projectRoot) {
98
100
  if (!(0, import_node_fs2.existsSync)(packageJson)) {
99
101
  return null;
100
102
  }
103
+ return getNodeTopLevelDepsFromFiles([packageJson]);
104
+ }
105
+ function getNodeTopLevelDepsFromFiles(packageJsonFiles) {
106
+ if (packageJsonFiles.length === 0) {
107
+ return null;
108
+ }
109
+ const deps = /* @__PURE__ */ new Set();
110
+ let found = false;
111
+ for (const packageJson of packageJsonFiles) {
112
+ if (!(0, import_node_fs2.existsSync)(packageJson)) {
113
+ continue;
114
+ }
115
+ const data = readPackageJson(packageJson);
116
+ if (data === null) {
117
+ return null;
118
+ }
119
+ found = true;
120
+ extractNodeTopLevelDeps(data, deps);
121
+ }
122
+ return found ? deps : null;
123
+ }
124
+ function readPackageJson(packageJson) {
101
125
  let data;
102
126
  try {
103
127
  data = JSON.parse((0, import_node_fs2.readFileSync)(packageJson, "utf8"));
@@ -105,9 +129,11 @@ function getNodeTopLevelDeps(projectRoot) {
105
129
  return null;
106
130
  }
107
131
  if (!isRecord(data)) {
108
- return /* @__PURE__ */ new Set();
132
+ return {};
109
133
  }
110
- const deps = /* @__PURE__ */ new Set();
134
+ return data;
135
+ }
136
+ function extractNodeTopLevelDeps(data, deps) {
111
137
  for (const field of [
112
138
  "dependencies",
113
139
  "devDependencies",
@@ -122,7 +148,6 @@ function getNodeTopLevelDeps(projectRoot) {
122
148
  deps.add(normalizePackageName(packageName));
123
149
  }
124
150
  }
125
- return deps;
126
151
  }
127
152
  function getTopLevelDeps(projectRoot) {
128
153
  const dependencySets = [
@@ -137,6 +162,9 @@ function getTopLevelDeps(projectRoot) {
137
162
  function getWorkspaceTopLevelDeps(pyprojects) {
138
163
  return getPythonTopLevelDepsFromFiles(pyprojects);
139
164
  }
165
+ function getNodeWorkspaceTopLevelDeps(packageJsonFiles) {
166
+ return getNodeTopLevelDepsFromFiles(packageJsonFiles);
167
+ }
140
168
  function extractDepsFromSpecs(depSpecs, deps) {
141
169
  for (const depSpec of depSpecs) {
142
170
  if (typeof depSpec !== "string") {
@@ -184,6 +212,8 @@ var testing = {
184
212
  // Annotate the CommonJS export names for ESM import in node:
185
213
  0 && (module.exports = {
186
214
  getNodeTopLevelDeps,
215
+ getNodeTopLevelDepsFromFiles,
216
+ getNodeWorkspaceTopLevelDeps,
187
217
  getPythonTopLevelDeps,
188
218
  getPythonTopLevelDepsFromFiles,
189
219
  getTopLevelDeps,
@@ -1,8 +1,10 @@
1
1
  declare function getPythonTopLevelDeps(projectRoot: string): Set<string> | null;
2
2
  declare function getPythonTopLevelDepsFromFiles(pyprojects: string[]): Set<string> | null;
3
3
  declare function getNodeTopLevelDeps(projectRoot: string): Set<string> | null;
4
+ declare function getNodeTopLevelDepsFromFiles(packageJsonFiles: string[]): Set<string> | null;
4
5
  declare function getTopLevelDeps(projectRoot: string): Set<string> | null;
5
6
  declare function getWorkspaceTopLevelDeps(pyprojects: string[]): Set<string> | null;
7
+ declare function getNodeWorkspaceTopLevelDeps(packageJsonFiles: string[]): Set<string> | null;
6
8
  declare function extractDepsFromSpecs(depSpecs: unknown[], deps: Set<string>): void;
7
9
  declare function extractDepsFromDependencyGroup(groupName: unknown, dependencyGroups: Record<string, unknown>, deps: Set<string>, visited: Set<unknown>): void;
8
10
  declare function isRecord(value: unknown): value is Record<string, unknown>;
@@ -12,4 +14,4 @@ declare const testing: {
12
14
  isRecord: typeof isRecord;
13
15
  };
14
16
 
15
- export { getNodeTopLevelDeps, getPythonTopLevelDeps, getPythonTopLevelDepsFromFiles, getTopLevelDeps, getWorkspaceTopLevelDeps, testing };
17
+ export { getNodeTopLevelDeps, getNodeTopLevelDepsFromFiles, getNodeWorkspaceTopLevelDeps, getPythonTopLevelDeps, getPythonTopLevelDepsFromFiles, getTopLevelDeps, getWorkspaceTopLevelDeps, testing };
package/ts/dist/deps.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  declare function getPythonTopLevelDeps(projectRoot: string): Set<string> | null;
2
2
  declare function getPythonTopLevelDepsFromFiles(pyprojects: string[]): Set<string> | null;
3
3
  declare function getNodeTopLevelDeps(projectRoot: string): Set<string> | null;
4
+ declare function getNodeTopLevelDepsFromFiles(packageJsonFiles: string[]): Set<string> | null;
4
5
  declare function getTopLevelDeps(projectRoot: string): Set<string> | null;
5
6
  declare function getWorkspaceTopLevelDeps(pyprojects: string[]): Set<string> | null;
7
+ declare function getNodeWorkspaceTopLevelDeps(packageJsonFiles: string[]): Set<string> | null;
6
8
  declare function extractDepsFromSpecs(depSpecs: unknown[], deps: Set<string>): void;
7
9
  declare function extractDepsFromDependencyGroup(groupName: unknown, dependencyGroups: Record<string, unknown>, deps: Set<string>, visited: Set<unknown>): void;
8
10
  declare function isRecord(value: unknown): value is Record<string, unknown>;
@@ -12,4 +14,4 @@ declare const testing: {
12
14
  isRecord: typeof isRecord;
13
15
  };
14
16
 
15
- export { getNodeTopLevelDeps, getPythonTopLevelDeps, getPythonTopLevelDepsFromFiles, getTopLevelDeps, getWorkspaceTopLevelDeps, testing };
17
+ export { getNodeTopLevelDeps, getNodeTopLevelDepsFromFiles, getNodeWorkspaceTopLevelDeps, getPythonTopLevelDeps, getPythonTopLevelDepsFromFiles, getTopLevelDeps, getWorkspaceTopLevelDeps, testing };
package/ts/dist/deps.js CHANGED
@@ -62,6 +62,28 @@ function getNodeTopLevelDeps(projectRoot) {
62
62
  if (!existsSync(packageJson)) {
63
63
  return null;
64
64
  }
65
+ return getNodeTopLevelDepsFromFiles([packageJson]);
66
+ }
67
+ function getNodeTopLevelDepsFromFiles(packageJsonFiles) {
68
+ if (packageJsonFiles.length === 0) {
69
+ return null;
70
+ }
71
+ const deps = /* @__PURE__ */ new Set();
72
+ let found = false;
73
+ for (const packageJson of packageJsonFiles) {
74
+ if (!existsSync(packageJson)) {
75
+ continue;
76
+ }
77
+ const data = readPackageJson(packageJson);
78
+ if (data === null) {
79
+ return null;
80
+ }
81
+ found = true;
82
+ extractNodeTopLevelDeps(data, deps);
83
+ }
84
+ return found ? deps : null;
85
+ }
86
+ function readPackageJson(packageJson) {
65
87
  let data;
66
88
  try {
67
89
  data = JSON.parse(readFileSync(packageJson, "utf8"));
@@ -69,9 +91,11 @@ function getNodeTopLevelDeps(projectRoot) {
69
91
  return null;
70
92
  }
71
93
  if (!isRecord(data)) {
72
- return /* @__PURE__ */ new Set();
94
+ return {};
73
95
  }
74
- const deps = /* @__PURE__ */ new Set();
96
+ return data;
97
+ }
98
+ function extractNodeTopLevelDeps(data, deps) {
75
99
  for (const field of [
76
100
  "dependencies",
77
101
  "devDependencies",
@@ -86,7 +110,6 @@ function getNodeTopLevelDeps(projectRoot) {
86
110
  deps.add(normalizePackageName(packageName));
87
111
  }
88
112
  }
89
- return deps;
90
113
  }
91
114
  function getTopLevelDeps(projectRoot) {
92
115
  const dependencySets = [
@@ -101,6 +124,9 @@ function getTopLevelDeps(projectRoot) {
101
124
  function getWorkspaceTopLevelDeps(pyprojects) {
102
125
  return getPythonTopLevelDepsFromFiles(pyprojects);
103
126
  }
127
+ function getNodeWorkspaceTopLevelDeps(packageJsonFiles) {
128
+ return getNodeTopLevelDepsFromFiles(packageJsonFiles);
129
+ }
104
130
  function extractDepsFromSpecs(depSpecs, deps) {
105
131
  for (const depSpec of depSpecs) {
106
132
  if (typeof depSpec !== "string") {
@@ -147,6 +173,8 @@ var testing = {
147
173
  };
148
174
  export {
149
175
  getNodeTopLevelDeps,
176
+ getNodeTopLevelDepsFromFiles,
177
+ getNodeWorkspaceTopLevelDeps,
150
178
  getPythonTopLevelDeps,
151
179
  getPythonTopLevelDepsFromFiles,
152
180
  getTopLevelDeps,
@@ -53,6 +53,26 @@ function findUvWorkspace(cwd) {
53
53
  }
54
54
  return null;
55
55
  }
56
+ function findNodeWorkspace(cwd) {
57
+ for (const directory of ancestors(cwd)) {
58
+ const packageJson = `${directory}/package.json`;
59
+ if (!(0, import_node_fs.existsSync)(packageJson)) {
60
+ continue;
61
+ }
62
+ const data = readPackageJson(packageJson);
63
+ const memberGlobs = getNodeWorkspaceGlobs(data);
64
+ if (memberGlobs === null) {
65
+ continue;
66
+ }
67
+ const members = findNodeWorkspaceMembers(directory, memberGlobs);
68
+ return {
69
+ root: directory,
70
+ members,
71
+ currentMember: findCurrentMember(cwd, members)
72
+ };
73
+ }
74
+ return null;
75
+ }
56
76
  function readPyproject(path) {
57
77
  try {
58
78
  const data = (0, import_smol_toml.parse)((0, import_node_fs.readFileSync)(path, "utf8"));
@@ -61,6 +81,14 @@ function readPyproject(path) {
61
81
  return {};
62
82
  }
63
83
  }
84
+ function readPackageJson(path) {
85
+ try {
86
+ const data = JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
87
+ return isRecord(data) ? data : {};
88
+ } catch {
89
+ return {};
90
+ }
91
+ }
64
92
  function hasUvWorkspace(data) {
65
93
  const tool = data["tool"];
66
94
  if (!isRecord(tool)) {
@@ -97,6 +125,36 @@ function findWorkspaceMembers(root, data) {
97
125
  }
98
126
  return [...members].sort();
99
127
  }
128
+ function getNodeWorkspaceGlobs(data) {
129
+ const workspaces = data["workspaces"];
130
+ if (Array.isArray(workspaces)) {
131
+ return workspaces.filter((value) => typeof value === "string");
132
+ }
133
+ if (isRecord(workspaces)) {
134
+ const packages = workspaces["packages"];
135
+ if (Array.isArray(packages)) {
136
+ return packages.filter((value) => typeof value === "string");
137
+ }
138
+ }
139
+ return null;
140
+ }
141
+ function findNodeWorkspaceMembers(root, memberGlobs) {
142
+ const includes = memberGlobs.filter((pattern) => !pattern.startsWith("!"));
143
+ const excludes = memberGlobs.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
144
+ const members = /* @__PURE__ */ new Set();
145
+ for (const memberGlob of includes) {
146
+ for (const member of expandMemberGlob(root, memberGlob)) {
147
+ const relativePath = (0, import_node_path.relative)(root, member).split(import_node_path.sep).join("/");
148
+ if (isExcluded(relativePath, excludes)) {
149
+ continue;
150
+ }
151
+ if (isFile(`${member}/package.json`)) {
152
+ members.add((0, import_node_path.resolve)(member));
153
+ }
154
+ }
155
+ }
156
+ return [...members].sort();
157
+ }
100
158
  function getWorkspaceTable(data) {
101
159
  const tool = data["tool"];
102
160
  if (!isRecord(tool)) {
@@ -204,6 +262,10 @@ function findProjectRoot(cwd) {
204
262
  if (workspace !== null) {
205
263
  return workspace.root;
206
264
  }
265
+ const nodeWorkspace = findNodeWorkspace(cwd);
266
+ if (nodeWorkspace !== null) {
267
+ return nodeWorkspace.root;
268
+ }
207
269
  for (const directory of ancestors2(cwd)) {
208
270
  if (isFile2((0, import_node_path2.join)(directory, "pyproject.toml")) || isFile2((0, import_node_path2.join)(directory, "uv.lock")) || venvFromDotVenv(directory) !== null || isFile2((0, import_node_path2.join)(directory, "package.json")) || isDirectory2((0, import_node_path2.join)(directory, "node_modules"))) {
209
271
  return directory;
@@ -25,6 +25,26 @@ function findUvWorkspace(cwd) {
25
25
  }
26
26
  return null;
27
27
  }
28
+ function findNodeWorkspace(cwd) {
29
+ for (const directory of ancestors(cwd)) {
30
+ const packageJson = `${directory}/package.json`;
31
+ if (!existsSync(packageJson)) {
32
+ continue;
33
+ }
34
+ const data = readPackageJson(packageJson);
35
+ const memberGlobs = getNodeWorkspaceGlobs(data);
36
+ if (memberGlobs === null) {
37
+ continue;
38
+ }
39
+ const members = findNodeWorkspaceMembers(directory, memberGlobs);
40
+ return {
41
+ root: directory,
42
+ members,
43
+ currentMember: findCurrentMember(cwd, members)
44
+ };
45
+ }
46
+ return null;
47
+ }
28
48
  function readPyproject(path) {
29
49
  try {
30
50
  const data = parse(readFileSync(path, "utf8"));
@@ -33,6 +53,14 @@ function readPyproject(path) {
33
53
  return {};
34
54
  }
35
55
  }
56
+ function readPackageJson(path) {
57
+ try {
58
+ const data = JSON.parse(readFileSync(path, "utf8"));
59
+ return isRecord(data) ? data : {};
60
+ } catch {
61
+ return {};
62
+ }
63
+ }
36
64
  function hasUvWorkspace(data) {
37
65
  const tool = data["tool"];
38
66
  if (!isRecord(tool)) {
@@ -69,6 +97,36 @@ function findWorkspaceMembers(root, data) {
69
97
  }
70
98
  return [...members].sort();
71
99
  }
100
+ function getNodeWorkspaceGlobs(data) {
101
+ const workspaces = data["workspaces"];
102
+ if (Array.isArray(workspaces)) {
103
+ return workspaces.filter((value) => typeof value === "string");
104
+ }
105
+ if (isRecord(workspaces)) {
106
+ const packages = workspaces["packages"];
107
+ if (Array.isArray(packages)) {
108
+ return packages.filter((value) => typeof value === "string");
109
+ }
110
+ }
111
+ return null;
112
+ }
113
+ function findNodeWorkspaceMembers(root, memberGlobs) {
114
+ const includes = memberGlobs.filter((pattern) => !pattern.startsWith("!"));
115
+ const excludes = memberGlobs.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
116
+ const members = /* @__PURE__ */ new Set();
117
+ for (const memberGlob of includes) {
118
+ for (const member of expandMemberGlob(root, memberGlob)) {
119
+ const relativePath = relative(root, member).split(sep).join("/");
120
+ if (isExcluded(relativePath, excludes)) {
121
+ continue;
122
+ }
123
+ if (isFile(`${member}/package.json`)) {
124
+ members.add(resolve(member));
125
+ }
126
+ }
127
+ }
128
+ return [...members].sort();
129
+ }
72
130
  function getWorkspaceTable(data) {
73
131
  const tool = data["tool"];
74
132
  if (!isRecord(tool)) {
@@ -176,6 +234,10 @@ function findProjectRoot(cwd) {
176
234
  if (workspace !== null) {
177
235
  return workspace.root;
178
236
  }
237
+ const nodeWorkspace = findNodeWorkspace(cwd);
238
+ if (nodeWorkspace !== null) {
239
+ return nodeWorkspace.root;
240
+ }
179
241
  for (const directory of ancestors2(cwd)) {
180
242
  if (isFile2(join(directory, "pyproject.toml")) || isFile2(join(directory, "uv.lock")) || venvFromDotVenv(directory) !== null || isFile2(join(directory, "package.json")) || isDirectory2(join(directory, "node_modules"))) {
181
243
  return directory;