@uniqbit/mate-core 0.15.5-canary.1 → 0.15.5-canary.2

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": "@uniqbit/mate-core",
3
- "version": "0.15.5-canary.1",
3
+ "version": "0.15.5-canary.2",
4
4
  "description": "Core framework and plugin APIs for Mate.",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -17,8 +17,8 @@ export const workspaceOpenCommandDeps = {
17
17
  /**
18
18
  * @command mate companion open
19
19
  * @description Updates the managed `.mate/workspace.code-workspace` file for
20
- * the active working repository and companion. When that workspace is open,
21
- * the editor reloads it in place; otherwise the command falls back to `--add`.
20
+ * the active working repository and companion, then adds both folders to the
21
+ * current editor window via `--add`.
22
22
  * @remarks Throws if the active repository (from `MATE_REPO_ID`) isn't found
23
23
  * in the registry; sets a non-zero exit code if the editor CLI is unavailable,
24
24
  * or if the working repo is linked from multiple companions and the ambiguity
@@ -59,5 +59,7 @@ export async function runWorkspaceOpenCommand(): Promise<void> {
59
59
  return;
60
60
  }
61
61
 
62
- console.log('Workspace: updated .mate/workspace.code-workspace; open it via "Open Workspace"');
62
+ console.log(
63
+ "Workspace: added to the current editor window; persistent file at .mate/workspace.code-workspace",
64
+ );
63
65
  }
@@ -1,14 +1,9 @@
1
1
  import { execFileSync, spawn } from "node:child_process";
2
- import { existsSync, readFileSync } from "node:fs";
2
+ import { existsSync } from "node:fs";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
- import { pathToFileURL } from "node:url";
6
5
 
7
6
  type SpawnLike = typeof spawn;
8
- type StatusProcess = typeof execFileSync;
9
- type ReadStorage = typeof readFileSync;
10
-
11
- export type EditorWorkspaceState = "open" | "not-open" | "undetermined";
12
7
 
13
8
  const CURSOR_FALLBACK_PATHS = [
14
9
  "/usr/local/bin/cursor",
@@ -84,62 +79,6 @@ export function editorWorkspacePath(repoPath: string): string {
84
79
  return path.join(path.resolve(repoPath), ".mate", "workspace.code-workspace");
85
80
  }
86
81
 
87
- function editorStoragePath(cli: string): string {
88
- const appName =
89
- cli === "cursor" ? "Cursor" : cli === "code-insiders" ? "Code - Insiders" : "Code";
90
- if (process.platform === "darwin") {
91
- return path.join(
92
- process.env.HOME ?? "",
93
- "Library",
94
- "Application Support",
95
- appName,
96
- "User",
97
- "globalStorage",
98
- "storage.json",
99
- );
100
- }
101
- if (process.platform === "win32") {
102
- return path.join(process.env.APPDATA ?? "", appName, "User", "globalStorage", "storage.json");
103
- }
104
- return path.join(
105
- process.env.XDG_CONFIG_HOME ?? path.join(process.env.HOME ?? "", ".config"),
106
- appName,
107
- "User",
108
- "globalStorage",
109
- "storage.json",
110
- );
111
- }
112
-
113
- export function detectEditorWorkspaceState(
114
- workspacePath: string,
115
- cli: string,
116
- statusProcess: StatusProcess = execFileSync,
117
- readStorage: ReadStorage = readFileSync,
118
- ): EditorWorkspaceState {
119
- const binary = resolveEditorBinary(cli);
120
- if (!binary) return "undetermined";
121
-
122
- try {
123
- statusProcess(binary, ["--status"], { stdio: ["ignore", "pipe", "ignore"] });
124
- const storage = JSON.parse(readStorage(editorStoragePath(cli), "utf8").toString()) as {
125
- windowsState?: {
126
- lastActiveWindow?: { workspaceIdentifier?: { configURIPath?: string } };
127
- openedWindows?: Array<{ workspaceIdentifier?: { configURIPath?: string } }>;
128
- };
129
- };
130
- const workspaceUri = pathToFileURL(path.resolve(workspacePath)).toString();
131
- const windows = [
132
- storage.windowsState?.lastActiveWindow,
133
- ...(storage.windowsState?.openedWindows ?? []),
134
- ];
135
- return windows.some((window) => window?.workspaceIdentifier?.configURIPath === workspaceUri)
136
- ? "open"
137
- : "not-open";
138
- } catch {
139
- return "undetermined";
140
- }
141
- }
142
-
143
82
  export interface WorkspaceDocument {
144
83
  workspacePath: string;
145
84
  folders: [string, string];
@@ -178,10 +117,6 @@ export async function injectEditorFolder(
178
117
  repoPath: string,
179
118
  cli = "code",
180
119
  spawnProcess: SpawnLike = spawn,
181
- detectWorkspace: (
182
- workspacePath: string,
183
- cli: string,
184
- ) => EditorWorkspaceState = detectEditorWorkspaceState,
185
120
  ): Promise<boolean> {
186
121
  const resolved = resolveEditorCli(cli);
187
122
  if (!resolved) {
@@ -194,14 +129,9 @@ export async function injectEditorFolder(
194
129
  return false;
195
130
  }
196
131
 
197
- const { cli: resolvedCli, binary } = resolved;
198
-
199
- const { workspacePath, folders: workspaceFolders } = await writeWorkspaceDocument(
200
- companionPath,
201
- repoPath,
202
- );
132
+ const { binary } = resolved;
203
133
 
204
- if (detectWorkspace(workspacePath, resolvedCli) === "open") return true;
134
+ const { folders: workspaceFolders } = await writeWorkspaceDocument(companionPath, repoPath);
205
135
 
206
136
  const child = spawnProcess(binary, ["--add", ...workspaceFolders], {
207
137
  stdio: "ignore",
@@ -102,7 +102,11 @@ export type PluginDeclarationPolicy = "default" | "optional";
102
102
  export interface PluginDeclaration {
103
103
  /** npm package name (e.g. `@acme/custom-plugin`). */
104
104
  package: string;
105
- /** Exact version, semver range, or the literal `latest`. */
105
+ /**
106
+ * Exact version, semver range, or a moving tag (`latest`, `canary`). Moving
107
+ * tags are re-resolved via `npm update` on every `mate install`; anything
108
+ * else is pinned and only re-resolves when the declaration changes.
109
+ */
106
110
  version: string;
107
111
  /** Registration policy; absent means `optional`. */
108
112
  policy?: PluginDeclarationPolicy;
@@ -44,7 +44,10 @@ function defaultNpmInstall(workspaceRoot: string): { ok: boolean; detail?: strin
44
44
  return { ok: true };
45
45
  }
46
46
 
47
- /** Runs `npm update <pkg...>` to force re-resolution of `latest`-declared plugins every run. */
47
+ /** Version literals treated as moving targets: re-resolved via `npm update` on every run. */
48
+ const MOVING_VERSION_TAGS = new Set(["latest", "canary"]);
49
+
50
+ /** Runs `npm update <pkg...>` to force re-resolution of moving-tag-declared plugins every run. */
48
51
  function defaultNpmUpdate(
49
52
  workspaceRoot: string,
50
53
  packages: string[],
@@ -102,12 +105,13 @@ async function writeWorkspaceManifest(
102
105
  * `.mate/plugins/`. Builds a single `dependencies` map (package
103
106
  * → declared version, sorted by name) and diffs it against the workspace's
104
107
  * current `package.json` plus each package's actual installed presence; an
105
- * identical, fully-installed map with no `latest` declaration skips the
106
- * install entirely. Otherwise the map is written and `npm install` runs once
107
- * for the whole workspace. `latest`-declared plugins additionally get an
108
- * `npm update` every run, regardless of whether the map changed, so they
109
- * re-resolve on every run. The shared, committed `package-lock.json` is the
110
- * sole reproducibility record; nothing else pins versions. Private
108
+ * identical, fully-installed map with no moving-tag declaration (`latest` or
109
+ * `canary`) skips the install entirely. Otherwise the map is written and
110
+ * `npm install` runs once for the whole workspace. Moving-tag-declared
111
+ * plugins additionally get an `npm update` every run, regardless of whether
112
+ * the map changed, so they re-resolve on every run. The shared, committed
113
+ * `package-lock.json` is the sole reproducibility record; nothing else pins
114
+ * versions. Private
111
115
  * registries are never Mate's concern: installs run through the operator's
112
116
  * own ambient npm config, or a project-local, gitignored `.npmrc` dropped
113
117
  * next to the workspace's `package.json`.
@@ -134,12 +138,12 @@ export async function installDeclaredPlugins(
134
138
  ),
135
139
  ]);
136
140
 
137
- const latestPackages = sorted
138
- .filter((declaration) => declaration.version === "latest")
141
+ const movingPackages = sorted
142
+ .filter((declaration) => MOVING_VERSION_TAGS.has(declaration.version))
139
143
  .map((declaration) => declaration.package);
140
144
  const manifestMatches = JSON.stringify(desired) === JSON.stringify(current);
141
145
  const allInstalled = installedBefore.every((version) => version !== null);
142
- const unchanged = latestPackages.length === 0 && manifestMatches && allInstalled;
146
+ const unchanged = movingPackages.length === 0 && manifestMatches && allInstalled;
143
147
 
144
148
  if (unchanged) {
145
149
  return sorted.map((declaration, index) => ({
@@ -156,8 +160,8 @@ export async function installDeclaredPlugins(
156
160
  : (installOutcome.detail ?? "npm install failed");
157
161
 
158
162
  let updateFailure: string | undefined;
159
- if (latestPackages.length > 0) {
160
- const updateOutcome = await runNpmUpdate(workspaceRoot, latestPackages);
163
+ if (movingPackages.length > 0) {
164
+ const updateOutcome = await runNpmUpdate(workspaceRoot, movingPackages);
161
165
  updateFailure = updateOutcome.ok ? undefined : (updateOutcome.detail ?? "npm update failed");
162
166
  }
163
167
 
@@ -168,8 +172,8 @@ export async function installDeclaredPlugins(
168
172
  );
169
173
 
170
174
  return sorted.map((declaration, index) => {
171
- const isLatest = declaration.version === "latest";
172
- const failure = installFailure ?? (isLatest ? updateFailure : undefined);
175
+ const isMoving = MOVING_VERSION_TAGS.has(declaration.version);
176
+ const failure = installFailure ?? (isMoving ? updateFailure : undefined);
173
177
  if (failure) {
174
178
  return { package: declaration.package, status: "failed", error: failure };
175
179
  }
@@ -73,7 +73,7 @@ export function collectManagedGitignoreEntries(ctx: SetupContext, plugins: Plugi
73
73
  // Holds absolute local paths of every linked working repo; never shared
74
74
  // across machines or committed to the companion repo.
75
75
  `.${FRAMEWORK_NAME}/config/registry.yaml`,
76
- ".mcp.json*",
76
+ ".mcp.json.bak",
77
77
  ...plugins
78
78
  .filter((p) => p.kind !== "root" && (p.isEnabled(ctx.config) || p.persistGitignoreEntries))
79
79
  .flatMap((p) => p.gitignoreEntries?.(ctx) ?? []),