lace-mcp 0.0.2-alpha.4 → 0.0.2-alpha.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/cli.js +71 -54
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -51,7 +51,7 @@ function getClaudeDesktopConfigPath() {
51
51
  }
52
52
  function getEditorConfigPath(target) {
53
53
  const home = homedir();
54
- if (target === "claude") return join(home, ".claude", "settings.json");
54
+ if (target === "claude") return join(home, ".claude.json");
55
55
  if (target === "claude-desktop") return getClaudeDesktopConfigPath();
56
56
  if (target === "cursor") return join(home, ".cursor", "mcp.json");
57
57
  return join(home, ".codex", "config.toml");
@@ -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
- return flags;
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(args) {
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
  }
@@ -338,64 +358,48 @@ alwaysApply: false
338
358
 
339
359
  ${body}`;
340
360
  }
341
- function installClaudeSkill() {
361
+ function installSkillFile(dir, filename, label, transform2) {
342
362
  const content = loadSkillContent();
343
363
  if (!content) return;
344
- const skillDir = join3(homedir2(), ".claude", "skills", "lace");
345
- const skillPath = join3(skillDir, "SKILL.md");
346
- if (existsSync2(skillPath)) {
347
- const existing = readFileSync2(skillPath, "utf8");
364
+ const filePath = join3(dir, filename);
365
+ if (existsSync2(filePath)) {
366
+ const existing = readFileSync2(filePath, "utf8");
348
367
  if (extractSkillVersion(existing) >= SKILL_VERSION) {
349
- console.log(" \u2713 /lace skill already up to date");
368
+ console.log(` \u2713 ${label} already up to date`);
350
369
  return;
351
370
  }
352
371
  }
353
- mkdirSync2(skillDir, { recursive: true });
354
- writeFileSync2(skillPath, injectVersion(content.trimStart()));
355
- console.log(` \u2713 /lace skill written to ${skillPath}`);
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());
356
378
  }
357
379
  function installCursorRule() {
358
- const content = loadSkillContent();
359
- if (!content) return;
360
- const rulesDir = join3(homedir2(), ".cursor", "rules");
361
- const rulePath = join3(rulesDir, "lace.mdc");
362
- if (existsSync2(rulePath)) {
363
- const existing = readFileSync2(rulePath, "utf8");
364
- if (extractSkillVersion(existing) >= SKILL_VERSION) {
365
- console.log(" \u2713 Cursor rule already up to date");
366
- return;
367
- }
368
- }
369
- mkdirSync2(rulesDir, { recursive: true });
370
- writeFileSync2(rulePath, injectVersion(buildCursorRule(extractSkillBody(content))));
371
- 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
+ );
372
386
  }
373
387
  function installCodexSkill() {
374
- const content = loadSkillContent();
375
- if (!content) return;
376
- const skillDir = join3(homedir2(), ".agents", "skills", "lace");
377
- const skillPath = join3(skillDir, "SKILL.md");
378
- if (existsSync2(skillPath)) {
379
- const existing = readFileSync2(skillPath, "utf8");
380
- if (extractSkillVersion(existing) >= SKILL_VERSION) {
381
- console.log(" \u2713 Codex skill already up to date");
382
- return;
383
- }
384
- }
385
- mkdirSync2(skillDir, { recursive: true });
386
- writeFileSync2(skillPath, injectVersion(extractSkillBody(content)));
387
- console.log(` \u2713 Codex skill written to ${skillPath}`);
388
+ installSkillFile(join3(homedir2(), ".agents", "skills", "lace"), "SKILL.md", "Codex skill", extractSkillBody);
388
389
  }
389
390
  function mcpArgs(editor) {
390
391
  return ["-y", `${PACKAGE_NAME}@latest`, "mcp", "--editor", editor];
391
392
  }
392
- function installClaude(env) {
393
+ function installClaude(env, scope = "user") {
393
394
  try {
394
395
  const envFlags = Object.entries(env).map(([key, value]) => `-e ${key}=${value}`).join(" ");
395
- execSync2(`claude mcp add --transport stdio --scope user ${envFlags} lace -- npx ${mcpArgs("claude").join(" ")}`, {
396
- stdio: "inherit"
397
- });
398
- console.log(" \u2713 MCP server registered via claude CLI");
396
+ execSync2(
397
+ `claude mcp add lace --transport stdio --scope ${scope} ${envFlags} -- npx ${mcpArgs("claude").join(" ")}`,
398
+ {
399
+ stdio: "inherit"
400
+ }
401
+ );
402
+ console.log(` \u2713 MCP server registered via claude CLI (scope: ${scope})`);
399
403
  } catch {
400
404
  const configPath = getEditorConfigPath("claude");
401
405
  writeJsonMcpEntry(configPath, { command: "npx", args: mcpArgs("claude"), env });
@@ -408,8 +412,12 @@ function installClaudeDesktop(env) {
408
412
  writeJsonMcpEntry(configPath, { command: npx, args: mcpArgs("claude-desktop"), env });
409
413
  installClaudeSkill();
410
414
  }
411
- function installCursor(env) {
412
- const configPath = getEditorConfigPath("cursor");
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);
413
421
  writeJsonMcpEntry(configPath, { command: "npx", args: mcpArgs("cursor"), env });
414
422
  installCursorRule();
415
423
  }
@@ -435,20 +443,23 @@ async function main() {
435
443
  const args = process.argv.slice(2);
436
444
  const target = args[0] === "install" ? args[1] : args[0];
437
445
  if (!target || !TARGETS.includes(target)) {
438
- console.error(`Usage: npx ${PACKAGE_NAME} install <${TARGETS.join("|")}>`);
446
+ console.error(`Usage: npx ${PACKAGE_NAME} install <${TARGETS.join("|")}> [--scope user|project|local]`);
439
447
  process.exit(1);
440
448
  }
449
+ const flags = parseFlags(args);
441
450
  console.log(`
442
451
  Installing Lace MCP for ${target}...`);
443
- const credentials = await resolveCredentials(args);
452
+ const credentials = await resolveCredentials(flags);
444
453
  const env = credentialsToEnv(credentials);
445
- if (target === "claude") installClaude(env);
454
+ const scopeTargets = ["claude", "cursor"];
455
+ const scope = flags.scope ?? (scopeTargets.includes(target) ? await promptScope(target) : "user");
456
+ if (target === "claude") installClaude(env, scope);
446
457
  else if (target === "claude-desktop") installClaudeDesktop(env);
447
- else if (target === "cursor") installCursor(env);
458
+ else if (target === "cursor") installCursor(env, scope);
448
459
  else installCodex(env);
449
460
  console.log('\nDone. Start a new session and say "implement the Lace decision" or type /lace.\n');
450
461
  }
451
- var TARGETS, PACKAGE_NAME, SKILL_VERSION;
462
+ var TARGETS, PACKAGE_NAME, SCOPES, SCOPE_LABELS, SKILL_VERSION;
452
463
  var init_install = __esm({
453
464
  "src/install.ts"() {
454
465
  "use strict";
@@ -457,6 +468,12 @@ var init_install = __esm({
457
468
  init_paths();
458
469
  TARGETS = ["claude", "claude-desktop", "cursor", "codex"];
459
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
+ };
460
477
  SKILL_VERSION = 1;
461
478
  }
462
479
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lace-mcp",
3
- "version": "0.0.2-alpha.4",
3
+ "version": "0.0.2-alpha.6",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/cli.js",