lace-mcp 0.0.2-alpha.3 → 0.0.2-alpha.5
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/dist/cli.js +82 -58
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -256,17 +256,37 @@ import { execSync as execSync2 } from "node:child_process";
|
|
|
256
256
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
257
257
|
import { homedir as homedir2 } from "node:os";
|
|
258
258
|
import { join as join3, resolve } from "node:path";
|
|
259
|
+
import { createInterface } from "node:readline";
|
|
259
260
|
function parseFlags(args) {
|
|
260
261
|
const flags = {};
|
|
261
262
|
for (let i = 0; i < args.length; i++) {
|
|
262
263
|
if (args[i] === "--token" && args[i + 1]) flags.token = args[++i];
|
|
263
264
|
else if (args[i] === "--org" && args[i + 1]) flags.org = args[++i];
|
|
264
265
|
else if (args[i] === "--api-base" && args[i + 1]) flags.apiBase = args[++i];
|
|
265
|
-
|
|
266
|
-
|
|
266
|
+
else if (args[i] === "--scope" && args[i + 1]) flags.scope = args[++i];
|
|
267
|
+
}
|
|
268
|
+
const scope = SCOPES.includes(flags.scope) ? flags.scope : void 0;
|
|
269
|
+
return { ...flags, scope };
|
|
270
|
+
}
|
|
271
|
+
function promptScope(target) {
|
|
272
|
+
const available = target === "claude" ? SCOPES : ["user", "project"];
|
|
273
|
+
return new Promise((promptResolve) => {
|
|
274
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
275
|
+
console.log("\n Where should Lace be installed?\n");
|
|
276
|
+
for (let i = 0; i < available.length; i++) {
|
|
277
|
+
console.log(` ${i + 1}. ${SCOPE_LABELS[available[i]]}`);
|
|
278
|
+
}
|
|
279
|
+
rl.question("\n > ", (answer) => {
|
|
280
|
+
rl.close();
|
|
281
|
+
const idx = parseInt(answer.trim(), 10) - 1;
|
|
282
|
+
const scope = available[idx] ?? available[0];
|
|
283
|
+
if (!available[idx]) console.log(` Defaulting to ${scope}`);
|
|
284
|
+
console.log("");
|
|
285
|
+
promptResolve(scope);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
267
288
|
}
|
|
268
|
-
async function resolveCredentials(
|
|
269
|
-
const flags = parseFlags(args);
|
|
289
|
+
async function resolveCredentials(flags) {
|
|
270
290
|
if (flags.token && flags.org) {
|
|
271
291
|
return { apiBase: flags.apiBase ?? DEFAULT_API_BASE, token: flags.token, orgId: flags.org };
|
|
272
292
|
}
|
|
@@ -280,12 +300,19 @@ function credentialsToEnv(creds) {
|
|
|
280
300
|
};
|
|
281
301
|
}
|
|
282
302
|
function extractSkillVersion(content) {
|
|
283
|
-
const match = content.match(
|
|
303
|
+
const match = content.match(/lace-mcp-skill-version: (\d+)/);
|
|
284
304
|
return match ? parseInt(match[1], 10) : 0;
|
|
285
305
|
}
|
|
286
|
-
function
|
|
287
|
-
|
|
288
|
-
|
|
306
|
+
function injectVersion(content) {
|
|
307
|
+
if (content.startsWith("---\n")) {
|
|
308
|
+
return content.replace(/^---\n/, `---
|
|
309
|
+
lace-mcp-skill-version: ${SKILL_VERSION}
|
|
310
|
+
`);
|
|
311
|
+
}
|
|
312
|
+
return `${content.trimEnd()}
|
|
313
|
+
|
|
314
|
+
<!-- lace-mcp-skill-version: ${SKILL_VERSION} -->
|
|
315
|
+
`;
|
|
289
316
|
}
|
|
290
317
|
function loadSkillContent() {
|
|
291
318
|
const skillPath = join3(resolve(import.meta.dirname, ".."), "SKILL.md");
|
|
@@ -331,64 +358,48 @@ alwaysApply: false
|
|
|
331
358
|
|
|
332
359
|
${body}`;
|
|
333
360
|
}
|
|
334
|
-
function
|
|
361
|
+
function installSkillFile(dir, filename, label, transform2) {
|
|
335
362
|
const content = loadSkillContent();
|
|
336
363
|
if (!content) return;
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const existing = readFileSync2(skillPath, "utf8");
|
|
364
|
+
const filePath = join3(dir, filename);
|
|
365
|
+
if (existsSync2(filePath)) {
|
|
366
|
+
const existing = readFileSync2(filePath, "utf8");
|
|
341
367
|
if (extractSkillVersion(existing) >= SKILL_VERSION) {
|
|
342
|
-
console.log(
|
|
368
|
+
console.log(` \u2713 ${label} already up to date`);
|
|
343
369
|
return;
|
|
344
370
|
}
|
|
345
371
|
}
|
|
346
|
-
mkdirSync2(
|
|
347
|
-
writeFileSync2(
|
|
348
|
-
console.log(` \u2713
|
|
372
|
+
mkdirSync2(dir, { recursive: true });
|
|
373
|
+
writeFileSync2(filePath, injectVersion(transform2(content)));
|
|
374
|
+
console.log(` \u2713 ${label} written to ${filePath}`);
|
|
375
|
+
}
|
|
376
|
+
function installClaudeSkill() {
|
|
377
|
+
installSkillFile(join3(homedir2(), ".claude", "skills", "lace"), "SKILL.md", "/lace skill", (c) => c.trimStart());
|
|
349
378
|
}
|
|
350
379
|
function installCursorRule() {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
if (extractSkillVersion(existing) >= SKILL_VERSION) {
|
|
358
|
-
console.log(" \u2713 Cursor rule already up to date");
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
mkdirSync2(rulesDir, { recursive: true });
|
|
363
|
-
writeFileSync2(rulePath, prependVersion(buildCursorRule(extractSkillBody(content))));
|
|
364
|
-
console.log(` \u2713 Cursor rule written to ${rulePath}`);
|
|
380
|
+
installSkillFile(
|
|
381
|
+
join3(homedir2(), ".cursor", "rules"),
|
|
382
|
+
"lace.mdc",
|
|
383
|
+
"Cursor rule",
|
|
384
|
+
(c) => buildCursorRule(extractSkillBody(c))
|
|
385
|
+
);
|
|
365
386
|
}
|
|
366
387
|
function installCodexSkill() {
|
|
367
|
-
|
|
368
|
-
if (!content) return;
|
|
369
|
-
const skillDir = join3(homedir2(), ".agents", "skills", "lace");
|
|
370
|
-
const skillPath = join3(skillDir, "SKILL.md");
|
|
371
|
-
if (existsSync2(skillPath)) {
|
|
372
|
-
const existing = readFileSync2(skillPath, "utf8");
|
|
373
|
-
if (extractSkillVersion(existing) >= SKILL_VERSION) {
|
|
374
|
-
console.log(" \u2713 Codex skill already up to date");
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
mkdirSync2(skillDir, { recursive: true });
|
|
379
|
-
writeFileSync2(skillPath, prependVersion(extractSkillBody(content)));
|
|
380
|
-
console.log(` \u2713 Codex skill written to ${skillPath}`);
|
|
388
|
+
installSkillFile(join3(homedir2(), ".agents", "skills", "lace"), "SKILL.md", "Codex skill", extractSkillBody);
|
|
381
389
|
}
|
|
382
390
|
function mcpArgs(editor) {
|
|
383
391
|
return ["-y", `${PACKAGE_NAME}@latest`, "mcp", "--editor", editor];
|
|
384
392
|
}
|
|
385
|
-
function installClaude(env) {
|
|
393
|
+
function installClaude(env, scope = "user") {
|
|
386
394
|
try {
|
|
387
|
-
const envFlags = Object.entries(env).map(([key, value]) =>
|
|
388
|
-
execSync2(
|
|
389
|
-
stdio
|
|
390
|
-
|
|
391
|
-
|
|
395
|
+
const envFlags = Object.entries(env).map(([key, value]) => `-e ${key}=${value}`).join(" ");
|
|
396
|
+
execSync2(
|
|
397
|
+
`claude mcp add --transport stdio --scope ${scope} ${envFlags} lace -- npx ${mcpArgs("claude").join(" ")}`,
|
|
398
|
+
{
|
|
399
|
+
stdio: "inherit"
|
|
400
|
+
}
|
|
401
|
+
);
|
|
402
|
+
console.log(` \u2713 MCP server registered via claude CLI (scope: ${scope})`);
|
|
392
403
|
} catch {
|
|
393
404
|
const configPath = getEditorConfigPath("claude");
|
|
394
405
|
writeJsonMcpEntry(configPath, { command: "npx", args: mcpArgs("claude"), env });
|
|
@@ -401,8 +412,12 @@ function installClaudeDesktop(env) {
|
|
|
401
412
|
writeJsonMcpEntry(configPath, { command: npx, args: mcpArgs("claude-desktop"), env });
|
|
402
413
|
installClaudeSkill();
|
|
403
414
|
}
|
|
404
|
-
function
|
|
405
|
-
|
|
415
|
+
function getCursorConfigPath(scope) {
|
|
416
|
+
if (scope === "project") return join3(process.cwd(), ".cursor", "mcp.json");
|
|
417
|
+
return getEditorConfigPath("cursor");
|
|
418
|
+
}
|
|
419
|
+
function installCursor(env, scope = "user") {
|
|
420
|
+
const configPath = getCursorConfigPath(scope);
|
|
406
421
|
writeJsonMcpEntry(configPath, { command: "npx", args: mcpArgs("cursor"), env });
|
|
407
422
|
installCursorRule();
|
|
408
423
|
}
|
|
@@ -428,20 +443,23 @@ async function main() {
|
|
|
428
443
|
const args = process.argv.slice(2);
|
|
429
444
|
const target = args[0] === "install" ? args[1] : args[0];
|
|
430
445
|
if (!target || !TARGETS.includes(target)) {
|
|
431
|
-
console.error(`Usage: npx ${PACKAGE_NAME} install <${TARGETS.join("|")}
|
|
446
|
+
console.error(`Usage: npx ${PACKAGE_NAME} install <${TARGETS.join("|")}> [--scope user|project|local]`);
|
|
432
447
|
process.exit(1);
|
|
433
448
|
}
|
|
449
|
+
const flags = parseFlags(args);
|
|
434
450
|
console.log(`
|
|
435
451
|
Installing Lace MCP for ${target}...`);
|
|
436
|
-
const credentials = await resolveCredentials(
|
|
452
|
+
const credentials = await resolveCredentials(flags);
|
|
437
453
|
const env = credentialsToEnv(credentials);
|
|
438
|
-
|
|
454
|
+
const scopeTargets = ["claude", "cursor"];
|
|
455
|
+
const scope = flags.scope ?? (scopeTargets.includes(target) ? await promptScope(target) : "user");
|
|
456
|
+
if (target === "claude") installClaude(env, scope);
|
|
439
457
|
else if (target === "claude-desktop") installClaudeDesktop(env);
|
|
440
|
-
else if (target === "cursor") installCursor(env);
|
|
458
|
+
else if (target === "cursor") installCursor(env, scope);
|
|
441
459
|
else installCodex(env);
|
|
442
460
|
console.log('\nDone. Start a new session and say "implement the Lace decision" or type /lace.\n');
|
|
443
461
|
}
|
|
444
|
-
var TARGETS, PACKAGE_NAME, SKILL_VERSION;
|
|
462
|
+
var TARGETS, PACKAGE_NAME, SCOPES, SCOPE_LABELS, SKILL_VERSION;
|
|
445
463
|
var init_install = __esm({
|
|
446
464
|
"src/install.ts"() {
|
|
447
465
|
"use strict";
|
|
@@ -450,6 +468,12 @@ var init_install = __esm({
|
|
|
450
468
|
init_paths();
|
|
451
469
|
TARGETS = ["claude", "claude-desktop", "cursor", "codex"];
|
|
452
470
|
PACKAGE_NAME = "lace-mcp";
|
|
471
|
+
SCOPES = ["user", "project", "local"];
|
|
472
|
+
SCOPE_LABELS = {
|
|
473
|
+
user: "User \u2014 available in all projects",
|
|
474
|
+
project: "Project \u2014 this project only (shared via .mcp.json)",
|
|
475
|
+
local: "Local \u2014 this machine only (not shared)"
|
|
476
|
+
};
|
|
453
477
|
SKILL_VERSION = 1;
|
|
454
478
|
}
|
|
455
479
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lace-mcp",
|
|
3
|
-
"version": "0.0.2-alpha.
|
|
3
|
+
"version": "0.0.2-alpha.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/cli.js",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"install-claude-desktop": "tsx src/cli.ts install claude-desktop",
|
|
28
28
|
"install-cursor": "tsx src/cli.ts install cursor",
|
|
29
29
|
"install-codex": "tsx src/cli.ts install codex",
|
|
30
|
-
"build": "rm -rf dist && esbuild src/cli.ts --bundle --platform=node --format=esm --outfile=dist/cli.js"
|
|
30
|
+
"build": "rm -rf dist && esbuild src/cli.ts --bundle --platform=node --format=esm --outfile=dist/cli.js",
|
|
31
|
+
"build:mcpb": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=mcpb/server/index.js && npx @anthropic-ai/mcpb pack mcpb dist/lace.mcpb"
|
|
31
32
|
}
|
|
32
33
|
}
|