@minhpnq1807/contextos 0.5.16 → 0.5.18

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.18
4
+
5
+ - Prevents `ctx sync --rules` from importing project MCP commands that point into temporary directories such as `/tmp/...`.
6
+ - Prunes stale temporary MCP commands from Antigravity MCP configs during rule sync.
7
+ - Removes the project-scope Claude `ctx-mcp` entry when a user-scope `ctx-mcp` is already installed, avoiding conflicting endpoint warnings.
8
+
9
+ ## 0.5.17
10
+
11
+ - Makes `ctx setup --agents ...` honor the provided agent list without prompting for the same agents again.
12
+
3
13
  ## 0.5.16
4
14
 
5
15
  - Prints a "Rebuilding skill embeddings... started" status before indexing large skillshare catalogs so `ctx sync --skills` no longer looks stuck after skillshare finishes.
package/bin/ctx.js CHANGED
@@ -482,8 +482,12 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
482
482
  console.log("Setup cancelled.");
483
483
  return;
484
484
  }
485
- const agents = await askSetupQuestion(rl, "Install for agents? comma-separated", options.agents.join(","));
486
- options.agents = parseAgentList(agents);
485
+ if (!options.agentsProvided) {
486
+ const agents = await askSetupQuestion(rl, "Install for agents? comma-separated", options.agents.join(","));
487
+ options.agents = parseAgentList(agents);
488
+ } else {
489
+ console.log(`◇ Install for agents:\n│ ${options.agents.join(", ")}`);
490
+ }
487
491
  options.inject = await askSetupYesNo(rl, "Enable prompt context injection?", options.inject);
488
492
  options.syncRules = await askSetupYesNo(rl, "Sync project rules and MCP servers through Ruler?", options.syncRules);
489
493
  options.syncSkills = await askSetupYesNo(rl, "Sync skills through skillshare?", options.syncSkills);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhpnq1807/contextos",
3
- "version": "0.5.16",
3
+ "version": "0.5.18",
4
4
  "description": "Task-aware AGENTS.md context injection and compliance reporting for Codex, Claude Code, and Antigravity.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs";
2
+ import os from "node:os";
2
3
  import path from "node:path";
3
4
  import readline from "node:readline/promises";
4
5
  import { stdin as input, stdout as output } from "node:process";
@@ -56,6 +57,10 @@ function codexConfigPath() {
56
57
  return path.join(process.env.CODEX_HOME || path.join(process.env.HOME || process.cwd(), ".codex"), "config.toml");
57
58
  }
58
59
 
60
+ function claudeUserConfigPath() {
61
+ return process.env.CLAUDE_CONFIG_PATH || path.join(process.env.HOME || process.cwd(), ".claude.json");
62
+ }
63
+
59
64
  export function rulerTomlPath(cwd = process.cwd()) {
60
65
  return path.join(cwd, ".ruler", "ruler.toml");
61
66
  }
@@ -224,10 +229,18 @@ export function readProjectMcpJsonServers({ cwd = process.cwd(), configPath = pa
224
229
  }
225
230
 
226
231
  function isRunnableMcpCommand(command) {
232
+ if (isEphemeralAbsoluteCommand(command)) return false;
227
233
  if (!path.isAbsolute(command)) return true;
228
234
  return fs.existsSync(command);
229
235
  }
230
236
 
237
+ function isEphemeralAbsoluteCommand(command) {
238
+ if (!path.isAbsolute(command)) return false;
239
+ const resolved = path.resolve(command);
240
+ const tmp = path.resolve(os.tmpdir());
241
+ return resolved === tmp || resolved.startsWith(`${tmp}${path.sep}`);
242
+ }
243
+
231
244
  function mergeMcpServers(...groups) {
232
245
  const merged = new Map();
233
246
  for (const group of groups) {
@@ -305,6 +318,28 @@ export function syncAntigravityMcpFromRuler({ tomlPath, configPaths = antigravit
305
318
  return { changed: true, servers: servers.map((server) => server.name), skipped, removed: [...new Set(removed)], configPaths };
306
319
  }
307
320
 
321
+ export function pruneClaudeProjectCtxMcp({
322
+ cwd = process.cwd(),
323
+ projectConfigPath = path.join(cwd, ".mcp.json"),
324
+ userConfigPath = claudeUserConfigPath(),
325
+ dryRun = false
326
+ } = {}) {
327
+ const userConfig = readJsonFile(userConfigPath, {});
328
+ const userHasCtx = Boolean(userConfig?.mcpServers?.[CTX_MCP_NAME]);
329
+ if (!userHasCtx || !fs.existsSync(projectConfigPath)) {
330
+ return { changed: false, removed: false, projectConfigPath };
331
+ }
332
+
333
+ const projectConfig = readJsonFile(projectConfigPath, {});
334
+ if (!projectConfig?.mcpServers?.[CTX_MCP_NAME]) {
335
+ return { changed: false, removed: false, projectConfigPath };
336
+ }
337
+
338
+ delete projectConfig.mcpServers[CTX_MCP_NAME];
339
+ if (!dryRun) writeJsonFile(projectConfigPath, projectConfig);
340
+ return { changed: true, removed: true, projectConfigPath };
341
+ }
342
+
308
343
  export function buildCtxMcpToml({ mcpServerPath, agents = DEFAULT_AGENTS } = {}) {
309
344
  const blocks = [
310
345
  "# Added by ctx sync --rules",
@@ -485,6 +520,12 @@ export async function syncRules({
485
520
  logger("[ctx] Running ruler apply...");
486
521
  runRulerApply({ agents: options.agents, cwd, run, dryRun: options.dryRun });
487
522
 
523
+ let claudePrune = { changed: false, removed: false };
524
+ if (options.agents.includes("claude")) {
525
+ claudePrune = pruneClaudeProjectCtxMcp({ cwd, dryRun: options.dryRun });
526
+ logger(statusLine("Deduping Claude ctx-mcp scope...", claudePrune.removed ? "✓ removed project duplicate" : "✓ no duplicate"));
527
+ }
528
+
488
529
  let antigravityMcp = { changed: false, servers: [], configPaths: [] };
489
530
  if (options.agents.includes("antigravity")) {
490
531
  antigravityMcp = options.dryRun
@@ -2,12 +2,14 @@ const DEFAULT_AGENTS = ["codex", "claude", "agy"];
2
2
 
3
3
  export function parseSetupArgs(args = []) {
4
4
  const agentsFlag = args.indexOf("--agents");
5
+ const agentsProvided = agentsFlag >= 0;
5
6
  const agents = agentsFlag >= 0
6
7
  ? parseAgentList(args[agentsFlag + 1])
7
8
  : DEFAULT_AGENTS;
8
9
 
9
10
  return {
10
11
  agents,
12
+ agentsProvided,
11
13
  yes: args.includes("--yes") || args.includes("-y"),
12
14
  quiet: args.includes("--quiet"),
13
15
  inject: !args.includes("--quiet") && !args.includes("--no-inject"),