@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
|
@@ -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
|
|
21
|
-
*
|
|
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(
|
|
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
|
|
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 {
|
|
198
|
-
|
|
199
|
-
const { workspacePath, folders: workspaceFolders } = await writeWorkspaceDocument(
|
|
200
|
-
companionPath,
|
|
201
|
-
repoPath,
|
|
202
|
-
);
|
|
132
|
+
const { binary } = resolved;
|
|
203
133
|
|
|
204
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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`
|
|
106
|
-
* install entirely. Otherwise the map is written and
|
|
107
|
-
* for the whole workspace.
|
|
108
|
-
* `npm update` every run, regardless of whether
|
|
109
|
-
* re-resolve on every run. The shared, committed
|
|
110
|
-
* sole reproducibility record; nothing else pins
|
|
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
|
|
138
|
-
.filter((declaration) => declaration.version
|
|
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 =
|
|
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 (
|
|
160
|
-
const updateOutcome = await runNpmUpdate(workspaceRoot,
|
|
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
|
|
172
|
-
const failure = installFailure ?? (
|
|
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) ?? []),
|