@pulso/companion 0.4.5 → 0.4.6

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 (2) hide show
  1. package/dist/index.js +145 -101
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10966,117 +10966,147 @@ print(result.stdout[:5000])
10966
10966
  }
10967
10967
  }
10968
10968
  // ── IDE Integration ────────────────────────────────────
10969
+ // Helper: extract open workspace paths from a VS Code/Cursor/Windsurf storage.json
10970
+ // Storage format: windowsState.lastActiveWindow / openedWindows
10971
+ // Each window has workspaceIdentifier.configURIPath (workspace file) OR folderUri (folder)
10969
10972
  case "sys_ide_list_open": {
10970
- return new Promise((resolve5) => {
10971
- exec5(
10972
- "ps aux",
10973
- { timeout: 5e3 },
10974
- (err, stdout) => {
10975
- if (err) {
10976
- resolve5({ success: false, error: err.message });
10977
- return;
10973
+ let readIdeWorkspaces2 = function(storagePath) {
10974
+ const storage = JSON.parse(readFileSync4(storagePath, "utf-8"));
10975
+ const ws2 = storage["windowsState"] ?? {};
10976
+ const allWindows = [
10977
+ ws2["lastActiveWindow"],
10978
+ ...Array.isArray(ws2["openedWindows"]) ? ws2["openedWindows"] : []
10979
+ ].filter(Boolean);
10980
+ const seen = /* @__PURE__ */ new Set();
10981
+ const paths = [];
10982
+ for (const w of allWindows) {
10983
+ const configURI = w["workspaceIdentifier"]?.["configURIPath"] ?? "";
10984
+ const folderURI = w["folderUri"] ?? "";
10985
+ for (const uri of [configURI, folderURI]) {
10986
+ if (!uri) continue;
10987
+ let p = uri.replace(/^file:\/\//, "");
10988
+ if (p.endsWith(".code-workspace")) p = dirname(p);
10989
+ if (p && !seen.has(p)) {
10990
+ seen.add(p);
10991
+ paths.push(p);
10978
10992
  }
10979
- const IDE_PATTERNS = {
10980
- "Cursor Helper": "Cursor",
10981
- "Cursor.app": "Cursor",
10982
- "Code Helper": "VS Code",
10983
- "Visual Studio Code": "VS Code",
10984
- "Windsurf Helper": "Windsurf",
10985
- "Windsurf.app": "Windsurf",
10986
- "zed": "Zed",
10987
- "WebStorm": "WebStorm",
10988
- "IntelliJ IDEA": "IntelliJ IDEA",
10989
- "PyCharm": "PyCharm",
10990
- "GoLand": "GoLand"
10991
- };
10992
- const found = {};
10993
- for (const line of stdout.split("\n")) {
10994
- for (const [pattern, ideName] of Object.entries(IDE_PATTERNS)) {
10995
- if (line.includes(pattern) && !line.includes("grep")) {
10996
- if (!found[ideName]) found[ideName] = { ide: ideName, workspaces: [] };
10997
- const matches = line.match(/\/(Users|home)\/[^\s]+/g) ?? [];
10998
- for (const m of matches) {
10999
- if (!m.includes(".app/") && !found[ideName].workspaces.includes(m)) {
11000
- try {
11001
- if (statSync4(m).isDirectory()) {
11002
- found[ideName].workspaces.push(m);
11003
- }
11004
- } catch {
11005
- }
11006
- }
11007
- }
11008
- }
10993
+ }
10994
+ }
10995
+ const activePath = (() => {
10996
+ const aw = ws2["lastActiveWindow"];
10997
+ if (!aw) return null;
10998
+ const configURI = aw["workspaceIdentifier"]?.["configURIPath"] ?? "";
10999
+ const folderURI = aw["folderUri"] ?? "";
11000
+ const raw = configURI || folderURI;
11001
+ if (!raw) return null;
11002
+ let p = raw.replace(/^file:\/\//, "");
11003
+ if (p.endsWith(".code-workspace")) p = dirname(p);
11004
+ return p || null;
11005
+ })();
11006
+ return { active: activePath, all: paths };
11007
+ };
11008
+ var readIdeWorkspaces = readIdeWorkspaces2;
11009
+ return new Promise((resolve5) => {
11010
+ exec5("ps aux", { timeout: 5e3 }, (err, stdout) => {
11011
+ if (err) {
11012
+ resolve5({ success: false, error: err.message });
11013
+ return;
11014
+ }
11015
+ const IDE_PATTERNS = {
11016
+ "Cursor Helper": "Cursor",
11017
+ "Cursor.app": "Cursor",
11018
+ "Code Helper": "VS Code",
11019
+ "Visual Studio Code": "VS Code",
11020
+ "Windsurf Helper": "Windsurf",
11021
+ "Windsurf.app": "Windsurf",
11022
+ "zed": "Zed",
11023
+ "WebStorm": "WebStorm",
11024
+ "IntelliJ IDEA": "IntelliJ IDEA",
11025
+ "PyCharm": "PyCharm",
11026
+ "GoLand": "GoLand"
11027
+ };
11028
+ const running = /* @__PURE__ */ new Set();
11029
+ for (const line of stdout.split("\n")) {
11030
+ for (const [pattern, ideName] of Object.entries(IDE_PATTERNS)) {
11031
+ if (line.includes(pattern) && !line.includes("grep")) {
11032
+ running.add(ideName);
11009
11033
  }
11010
11034
  }
11011
- const ides = Object.values(found);
11012
- const home = homedir4();
11013
- const storagePaths = [
11014
- { ide: "VS Code", path: join5(home, "Library/Application Support/Code/User/globalStorage/storage.json") },
11015
- { ide: "Cursor", path: join5(home, "Library/Application Support/Cursor/User/globalStorage/storage.json") },
11016
- { ide: "Windsurf", path: join5(home, "Library/Application Support/Windsurf/User/globalStorage/storage.json") }
11017
- ];
11018
- for (const { ide: ideName, path: storagePath } of storagePaths) {
11019
- if (!existsSync4(storagePath)) continue;
11020
- try {
11021
- const storage = JSON.parse(readFileSync4(storagePath, "utf-8"));
11022
- const recentFolders = (storage["recently.opened"]?.workspaces ?? []).map(
11023
- (w) => typeof w === "string" ? w : w.folderUri ?? ""
11024
- ).filter(Boolean).map((p) => p.replace(/^file:\/\//, "")).slice(0, 3);
11025
- if (recentFolders.length > 0) {
11026
- const existing = found[ideName];
11027
- if (existing) {
11028
- for (const folder of recentFolders) {
11029
- if (!existing.workspaces.includes(folder)) {
11030
- existing.workspaces.push(folder);
11031
- }
11032
- }
11033
- } else if (ides.find((i) => i.ide === ideName) === void 0) {
11034
- ides.push({ ide: ideName, workspaces: recentFolders });
11035
- }
11036
- }
11037
- } catch {
11038
- }
11035
+ }
11036
+ const home = homedir4();
11037
+ const storagePaths = [
11038
+ { ide: "VS Code", path: join5(home, "Library/Application Support/Code/User/globalStorage/storage.json") },
11039
+ { ide: "Cursor", path: join5(home, "Library/Application Support/Cursor/User/globalStorage/storage.json") },
11040
+ { ide: "Windsurf", path: join5(home, "Library/Application Support/Windsurf/User/globalStorage/storage.json") }
11041
+ ];
11042
+ const ides = [];
11043
+ for (const { ide: ideName, path: storagePath } of storagePaths) {
11044
+ if (!existsSync4(storagePath)) continue;
11045
+ try {
11046
+ const { active, all } = readIdeWorkspaces2(storagePath);
11047
+ ides.push({ ide: ideName, active, workspaces: all, running: running.has(ideName) });
11048
+ } catch {
11039
11049
  }
11040
- resolve5({
11041
- success: true,
11042
- data: {
11043
- ides: ides.length > 0 ? ides : [],
11044
- count: ides.length,
11045
- note: ides.length === 0 ? "No IDEs detected. Open VS Code, Cursor, Windsurf, or Zed." : void 0
11046
- }
11047
- });
11048
11050
  }
11049
- );
11051
+ if (running.has("Zed") && !ides.find((i) => i.ide === "Zed")) {
11052
+ ides.push({ ide: "Zed", active: null, workspaces: [], running: true });
11053
+ }
11054
+ resolve5({
11055
+ success: true,
11056
+ data: {
11057
+ ides: ides.length > 0 ? ides : [],
11058
+ count: ides.length,
11059
+ note: ides.length === 0 ? "No IDEs detected." : void 0
11060
+ }
11061
+ });
11062
+ });
11050
11063
  });
11051
11064
  }
11052
11065
  case "sys_ide_get_context": {
11053
11066
  const targetIde = params.ide ?? "";
11054
11067
  const home = homedir4();
11055
11068
  const storageMap = {
11056
- vscode: join5(home, "Library/Application Support/Code/User/globalStorage/storage.json"),
11057
- cursor: join5(home, "Library/Application Support/Cursor/User/globalStorage/storage.json"),
11058
- windsurf: join5(home, "Library/Application Support/Windsurf/User/globalStorage/storage.json")
11069
+ vscode: { label: "VS Code", path: join5(home, "Library/Application Support/Code/User/globalStorage/storage.json") },
11070
+ cursor: { label: "Cursor", path: join5(home, "Library/Application Support/Cursor/User/globalStorage/storage.json") },
11071
+ windsurf: { label: "Windsurf", path: join5(home, "Library/Application Support/Windsurf/User/globalStorage/storage.json") }
11059
11072
  };
11060
11073
  const ideKey = targetIde.toLowerCase().replace(/[\s-]/g, "");
11061
- const pathsToTry = ideKey && storageMap[ideKey] ? [{ ide: ideKey, path: storageMap[ideKey] }] : Object.entries(storageMap).map(([ide, path]) => ({ ide, path }));
11062
- for (const { ide, path: storagePath } of pathsToTry) {
11074
+ const pathsToTry = ideKey && storageMap[ideKey] ? [{ key: ideKey, ...storageMap[ideKey] }] : Object.entries(storageMap).map(([key, v]) => ({ key, ...v }));
11075
+ for (const { key: _key, label, path: storagePath } of pathsToTry) {
11063
11076
  if (!existsSync4(storagePath)) continue;
11064
11077
  try {
11065
11078
  const storage = JSON.parse(readFileSync4(storagePath, "utf-8"));
11066
- const recentWorkspaces = (storage["recently.opened"]?.workspaces ?? []).map(
11067
- (w) => typeof w === "string" ? w : w.folderUri ?? ""
11068
- ).filter(Boolean).map((p) => p.replace(/^file:\/\//, "")).slice(0, 5);
11069
- const activeWorkspace = recentWorkspaces[0] ?? null;
11070
- const recentFiles = (storage["recently.opened"]?.files ?? []).map(
11071
- (f) => typeof f === "string" ? f : f.fileUri ?? ""
11072
- ).filter(Boolean).map((p) => p.replace(/^file:\/\//, "")).slice(0, 5);
11079
+ const ws2 = storage["windowsState"] ?? {};
11080
+ const allWindows = [
11081
+ ws2["lastActiveWindow"],
11082
+ ...Array.isArray(ws2["openedWindows"]) ? ws2["openedWindows"] : []
11083
+ ].filter(Boolean);
11084
+ const seen = /* @__PURE__ */ new Set();
11085
+ const workspaces = [];
11086
+ for (const w of allWindows) {
11087
+ const configURI = w["workspaceIdentifier"]?.["configURIPath"] ?? "";
11088
+ const folderURI = w["folderUri"] ?? "";
11089
+ for (const uri of [configURI, folderURI]) {
11090
+ if (!uri) continue;
11091
+ let p = uri.replace(/^file:\/\//, "");
11092
+ if (p.endsWith(".code-workspace")) p = dirname(p);
11093
+ if (p && !seen.has(p)) {
11094
+ seen.add(p);
11095
+ workspaces.push(p);
11096
+ }
11097
+ }
11098
+ }
11099
+ const activeWindow = ws2["lastActiveWindow"];
11100
+ const activeConfigURI = activeWindow?.["workspaceIdentifier"]?.["configURIPath"] ?? "";
11101
+ const activeFolderURI = activeWindow?.["folderUri"] ?? "";
11102
+ let activeWorkspace = (activeConfigURI || activeFolderURI).replace(/^file:\/\//, "");
11103
+ if (activeWorkspace.endsWith(".code-workspace")) activeWorkspace = dirname(activeWorkspace);
11073
11104
  return {
11074
11105
  success: true,
11075
11106
  data: {
11076
- ide,
11077
- activeWorkspace,
11078
- recentWorkspaces,
11079
- recentFiles
11107
+ ide: label,
11108
+ activeWorkspace: activeWorkspace || null,
11109
+ openWorkspaces: workspaces
11080
11110
  }
11081
11111
  };
11082
11112
  } catch {
@@ -11084,7 +11114,7 @@ print(result.stdout[:5000])
11084
11114
  }
11085
11115
  return {
11086
11116
  success: false,
11087
- error: "No IDE context found. Make sure VS Code, Cursor, or Windsurf has been used."
11117
+ error: "No IDE context found. Make sure VS Code, Cursor, or Windsurf has been opened."
11088
11118
  };
11089
11119
  }
11090
11120
  case "sys_ide_run_terminal": {
@@ -11101,10 +11131,17 @@ print(result.stdout[:5000])
11101
11131
  if (!existsSync4(storagePath)) continue;
11102
11132
  try {
11103
11133
  const storage = JSON.parse(readFileSync4(storagePath, "utf-8"));
11104
- const firstWorkspace = (storage["recently.opened"]?.workspaces ?? [])[0];
11105
- if (firstWorkspace) {
11106
- const p = typeof firstWorkspace === "string" ? firstWorkspace : firstWorkspace.folderUri ?? "";
11107
- cwd = p.replace(/^file:\/\//, "");
11134
+ const ws2 = storage["windowsState"] ?? {};
11135
+ const aw = ws2["lastActiveWindow"];
11136
+ if (!aw) continue;
11137
+ const configURI = aw["workspaceIdentifier"]?.["configURIPath"] ?? "";
11138
+ const folderURI = aw["folderUri"] ?? "";
11139
+ const raw = configURI || folderURI;
11140
+ if (!raw) continue;
11141
+ let p = raw.replace(/^file:\/\//, "");
11142
+ if (p.endsWith(".code-workspace")) p = dirname(p);
11143
+ if (p) {
11144
+ cwd = p;
11108
11145
  break;
11109
11146
  }
11110
11147
  } catch {
@@ -11179,10 +11216,17 @@ STDERR: ${stderr}` : "")).slice(0, 1e4),
11179
11216
  if (!existsSync4(storagePath)) continue;
11180
11217
  try {
11181
11218
  const storage = JSON.parse(readFileSync4(storagePath, "utf-8"));
11182
- const firstWorkspace = (storage["recently.opened"]?.workspaces ?? [])[0];
11183
- if (firstWorkspace) {
11184
- const p = typeof firstWorkspace === "string" ? firstWorkspace : firstWorkspace.folderUri ?? "";
11185
- cwd = p.replace(/^file:\/\//, "");
11219
+ const ws2 = storage["windowsState"] ?? {};
11220
+ const aw = ws2["lastActiveWindow"];
11221
+ if (!aw) continue;
11222
+ const configURI = aw["workspaceIdentifier"]?.["configURIPath"] ?? "";
11223
+ const folderURI = aw["folderUri"] ?? "";
11224
+ const raw = configURI || folderURI;
11225
+ if (!raw) continue;
11226
+ let p = raw.replace(/^file:\/\//, "");
11227
+ if (p.endsWith(".code-workspace")) p = dirname(p);
11228
+ if (p) {
11229
+ cwd = p;
11186
11230
  break;
11187
11231
  }
11188
11232
  } catch {
@@ -12407,7 +12451,7 @@ process.on("exit", () => {
12407
12451
  });
12408
12452
  console.log("");
12409
12453
  console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
12410
- console.log(` \u2551 Pulso ${platformName} Companion v0.4.3 \u2551`);
12454
+ console.log(` \u2551 Pulso ${platformName} Companion v0.4.5 \u2551`);
12411
12455
  console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
12412
12456
  console.log("");
12413
12457
  console.log(` Platform: ${currentPlatform}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pulso/companion",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "type": "module",
5
5
  "description": "Pulso Companion — gives your AI agent real control over your computer",
6
6
  "bin": {