@minhpnq1807/contextos 0.5.21 → 0.5.23

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.23
4
+
5
+ - **Fix Windows install paths:** Replaces all `process.env.HOME || process.cwd()` fallbacks with `os.homedir()` across `ctx.js`, `claude-hooks.js`, `antigravity-hooks.js`, `claude-mcp.js`, `antigravity-mcp.js`, and `ruler-sync.js`. On Windows, `HOME` is not set, causing `.codex/`, `.claude/`, and `.gemini/` directories (with full `node_modules` and source code) to be created inside the project tree instead of the user's home directory.
6
+ - **Fix ephemeral MCP server path:** `ctx sync --rules` now resolves the MCP server path from stable install roots (`~/.codex/marketplaces/contextos/`, `~/.ctx/contextos/agents/`) instead of `rootDir`, which may point to a temporary npm extraction directory (e.g. `/tmp/contextos/`) that disappears after cleanup.
7
+
8
+ ## 0.5.22
9
+
10
+ - Adds `.gitignore` management to `ctx install`: writes inner `.gitignore` (excludes `node_modules/`, `bin/`, `lib/`, `mcp/`) inside installed agent directories and ensures the project root `.gitignore` excludes `.codex/marketplaces/contextos/`, `.claude/settings.json`, and `.gemini/`.
11
+ - Splits the `npm install -g && ctx setup` one-liner into two separate commands in README and LAUNCH docs to avoid shell PATH resolution failures.
12
+
3
13
  ## 0.5.21
4
14
 
5
15
  - Makes prompt hooks fall back to direct scoring when the `ctx-mcp` bridge socket is missing, stale, or unavailable, avoiding empty `hook context` output.
package/LAUNCH.md CHANGED
@@ -37,7 +37,8 @@ It supports Codex, Claude Code, and Antigravity. It is local-first and uses loca
37
37
 
38
38
  Install:
39
39
 
40
- npm install -g @minhpnq1807/contextos && ctx setup
40
+ npm install -g @minhpnq1807/contextos
41
+ ctx setup
41
42
 
42
43
  Repo: https://github.com/khovan123/contextOS
43
44
  ```
@@ -51,7 +52,8 @@ Codex can read AGENTS.md and still ignore the rule that matters.
51
52
 
52
53
  ContextOS ranks rules per prompt, injects the important ones before work starts, then reports followed / ignored / unknown after the task.
53
54
 
54
- npm install -g @minhpnq1807/contextos && ctx setup
55
+ npm install -g @minhpnq1807/contextos
56
+ ctx setup
55
57
 
56
58
  https://github.com/khovan123/contextOS
57
59
  ```
package/README.md CHANGED
@@ -49,10 +49,11 @@ Rule outcomes: 8 followed, 0 ignored, 0 unknown
49
49
  Runtime telemetry: code-review-graph, code-review-graph.query_graph_tool
50
50
  ```
51
51
 
52
- ## Install In One Line
52
+ ## Quick Install
53
53
 
54
54
  ```bash
55
- npm install -g @minhpnq1807/contextos && ctx setup
55
+ npm install -g @minhpnq1807/contextos
56
+ ctx setup
56
57
  ```
57
58
 
58
59
  No postinstall surprise: `npm install` only installs the CLI. Setup runs only when you call `ctx setup`.
package/bin/ctx.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from "node:fs";
3
+ import os from "node:os";
3
4
  import path from "node:path";
4
5
  import readline from "node:readline/promises";
5
6
  import { stdin as input, stdout as output } from "node:process";
@@ -24,6 +25,7 @@ import { installClaudeMcp } from "../plugins/ctx/lib/claude-mcp.js";
24
25
  import { installAntigravityHooks } from "../plugins/ctx/lib/antigravity-hooks.js";
25
26
  import { installAntigravityMcp } from "../plugins/ctx/lib/antigravity-mcp.js";
26
27
  import { syncRules } from "../plugins/ctx/lib/ruler-sync.js";
28
+ import { writeInnerGitignore, ensureRootGitignore } from "../plugins/ctx/lib/gitignore.js";
27
29
  import { syncSkills } from "../plugins/ctx/lib/skillshare-sync.js";
28
30
  import { scanSkills, warmSkillEmbeddings } from "../plugins/ctx/lib/skill-discoverer.js";
29
31
  import { parsePassthroughArgs, runPassthrough } from "../plugins/ctx/lib/passthrough.js";
@@ -162,7 +164,7 @@ function packageVersion() {
162
164
  }
163
165
 
164
166
  function codexHome() {
165
- return process.env.CODEX_HOME || path.join(process.env.HOME || process.cwd(), ".codex");
167
+ return process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
166
168
  }
167
169
 
168
170
  function copyInstall() {
@@ -194,6 +196,9 @@ async function install({ copy = false, inject = true, agent = "codex" } = {}) {
194
196
  const hooksPath = installClaudeHooks({ installRoot, injectPromptContext: inject });
195
197
  progress.step(40, "installing mcp");
196
198
  const mcpConfigPath = installClaudeMcp({ installRoot });
199
+ progress.step(50, "configuring gitignore");
200
+ writeInnerGitignore(installRoot);
201
+ ensureRootGitignore(process.cwd());
197
202
  progress.step(55, "warming embeddings");
198
203
  const warmResult = await warmInstallEmbeddings();
199
204
  progress.done("claude installed");
@@ -218,6 +223,9 @@ async function install({ copy = false, inject = true, agent = "codex" } = {}) {
218
223
  const hooksPath = installAntigravityHooks({ installRoot, injectPromptContext: inject });
219
224
  progress.step(40, "installing mcp");
220
225
  const mcpConfigPaths = installAntigravityMcp({ installRoot });
226
+ progress.step(50, "configuring gitignore");
227
+ writeInnerGitignore(installRoot);
228
+ ensureRootGitignore(process.cwd());
221
229
  progress.step(55, "warming embeddings");
222
230
  const warmResult = await warmInstallEmbeddings();
223
231
  progress.done("agy installed");
@@ -256,6 +264,10 @@ async function install({ copy = false, inject = true, agent = "codex" } = {}) {
256
264
  progress.step(60, "installing hooks");
257
265
  const hooksPath = installGlobalHooks({ codexHome: codexHome(), marketplaceRoot, injectPromptContext: inject });
258
266
 
267
+ progress.step(65, "configuring gitignore");
268
+ writeInnerGitignore(marketplaceRoot);
269
+ ensureRootGitignore(process.cwd());
270
+
259
271
  progress.step(70, "warming embeddings");
260
272
  const warmResult = await warmInstallEmbeddings();
261
273
  progress.done("codex installed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhpnq1807/contextos",
3
- "version": "0.5.21",
3
+ "version": "0.5.23",
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
 
4
5
  function shellQuote(value) {
@@ -19,7 +20,7 @@ function commandFor(installRoot, scriptName, { injectPromptContext = true } = {}
19
20
 
20
21
  export function antigravityHooksPath() {
21
22
  return process.env.ANTIGRAVITY_HOOKS_PATH
22
- || path.join(process.env.HOME || process.cwd(), ".gemini", "config", "hooks.json");
23
+ || path.join(os.homedir(), ".gemini", "config", "hooks.json");
23
24
  }
24
25
 
25
26
  export function buildAntigravityHooksConfig(existingConfig, { installRoot, injectPromptContext = true } = {}) {
@@ -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
 
4
5
  function readJsonFile(filePath, fallback) {
@@ -12,7 +13,7 @@ export function antigravityMcpConfigPaths() {
12
13
  if (process.env.ANTIGRAVITY_MCP_CONFIG_PATH) {
13
14
  return [process.env.ANTIGRAVITY_MCP_CONFIG_PATH];
14
15
  }
15
- const home = process.env.HOME || process.cwd();
16
+ const home = os.homedir();
16
17
  return [
17
18
  path.join(home, ".gemini", "antigravity", "mcp_config.json"),
18
19
  path.join(home, ".gemini", "antigravity-cli", "mcp_config.json"),
@@ -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
 
4
5
  import { buildGlobalHooksConfig } from "./global-hooks.js";
@@ -11,7 +12,7 @@ function readJsonFile(filePath, fallback) {
11
12
  }
12
13
 
13
14
  export function claudeHome() {
14
- return process.env.CLAUDE_HOME || path.join(process.env.HOME || process.cwd(), ".claude");
15
+ return process.env.CLAUDE_HOME || path.join(os.homedir(), ".claude");
15
16
  }
16
17
 
17
18
  export function installClaudeHooks({ claudeHome: home = claudeHome(), installRoot, injectPromptContext = true } = {}) {
@@ -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
 
4
5
  function readJsonFile(filePath, fallback) {
@@ -9,7 +10,7 @@ function readJsonFile(filePath, fallback) {
9
10
  }
10
11
 
11
12
  export function claudeConfigPath() {
12
- return process.env.CLAUDE_CONFIG_PATH || path.join(process.env.HOME || process.cwd(), ".claude.json");
13
+ return process.env.CLAUDE_CONFIG_PATH || path.join(os.homedir(), ".claude.json");
13
14
  }
14
15
 
15
16
  export function buildClaudeMcpConfig(existingConfig, { installRoot } = {}) {
@@ -0,0 +1,69 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ /**
5
+ * Entries to exclude inside the installed contextos directory.
6
+ * Keeps node_modules, bin, lib, and mcp out of version control.
7
+ */
8
+ const INNER_GITIGNORE_ENTRIES = [
9
+ "node_modules/",
10
+ "bin/",
11
+ "lib/",
12
+ "mcp/",
13
+ ];
14
+
15
+ /**
16
+ * Entries that ctx install should add to the project root .gitignore.
17
+ *
18
+ * .codex/marketplaces/contextos/ — Codex agent install dir
19
+ * .claude/settings.json — Claude hooks written by ctx install
20
+ * .gemini/ — Antigravity hooks/config
21
+ */
22
+ const ROOT_GITIGNORE_ENTRIES = [
23
+ ".codex/marketplaces/contextos/",
24
+ ".claude/settings.json",
25
+ ".gemini/",
26
+ ];
27
+
28
+ /**
29
+ * Write a .gitignore inside `dir` that excludes build/runtime artefacts.
30
+ * Creates the directory if it does not exist yet.
31
+ */
32
+ export function writeInnerGitignore(dir) {
33
+ fs.mkdirSync(dir, { recursive: true });
34
+ const gitignorePath = path.join(dir, ".gitignore");
35
+ const content = INNER_GITIGNORE_ENTRIES.join("\n") + "\n";
36
+ fs.writeFileSync(gitignorePath, content, "utf8");
37
+ return gitignorePath;
38
+ }
39
+
40
+ /**
41
+ * Ensure the project root .gitignore exists and contains the entries
42
+ * needed to keep ctx install artefacts out of version control.
43
+ *
44
+ * Only appends entries that are not already present.
45
+ * Creates the file if it does not exist.
46
+ */
47
+ export function ensureRootGitignore(projectRoot) {
48
+ const gitignorePath = path.join(projectRoot, ".gitignore");
49
+ let existing = "";
50
+ if (fs.existsSync(gitignorePath)) {
51
+ existing = fs.readFileSync(gitignorePath, "utf8");
52
+ }
53
+
54
+ const lines = existing.split("\n");
55
+ const missing = ROOT_GITIGNORE_ENTRIES.filter(
56
+ (entry) => !lines.some((line) => line.trim() === entry)
57
+ );
58
+
59
+ if (missing.length === 0) return gitignorePath;
60
+
61
+ const block = [
62
+ "",
63
+ "# ContextOS install artefacts",
64
+ ...missing,
65
+ ].join("\n") + "\n";
66
+
67
+ fs.writeFileSync(gitignorePath, existing.trimEnd() + "\n" + block, "utf8");
68
+ return gitignorePath;
69
+ }
@@ -5,9 +5,12 @@ import readline from "node:readline/promises";
5
5
  import { stdin as input, stdout as output } from "node:process";
6
6
  import { execFileSync } from "node:child_process";
7
7
 
8
+ import { defaultDataRoot } from "./workspace-data.js";
9
+
8
10
  const DEFAULT_AGENTS = ["codex", "claude", "antigravity"];
9
11
  const CTX_MCP_NAME = "ctx-mcp";
10
12
  const CONTEXTOS_PROXY_MARKER = "/contextos/plugins/ctx/mcp/proxy.js";
13
+ const MCP_SERVER_RELATIVE = path.join("plugins", "ctx", "mcp", "server.js");
11
14
  const AGENT_ALIASES = new Map([
12
15
  ["agy", "antigravity"],
13
16
  ["antigravity", "antigravity"],
@@ -54,11 +57,11 @@ function displayAgentName(agent) {
54
57
  }
55
58
 
56
59
  function codexConfigPath() {
57
- return path.join(process.env.CODEX_HOME || path.join(process.env.HOME || process.cwd(), ".codex"), "config.toml");
60
+ return path.join(process.env.CODEX_HOME || path.join(os.homedir(), ".codex"), "config.toml");
58
61
  }
59
62
 
60
63
  function claudeUserConfigPath() {
61
- return process.env.CLAUDE_CONFIG_PATH || path.join(process.env.HOME || process.cwd(), ".claude.json");
64
+ return process.env.CLAUDE_CONFIG_PATH || path.join(os.homedir(), ".claude.json");
62
65
  }
63
66
 
64
67
  export function rulerTomlPath(cwd = process.cwd()) {
@@ -273,7 +276,7 @@ function readRulerMcpServer({ tomlPath, name } = {}) {
273
276
  }
274
277
 
275
278
  function antigravityMcpConfigPaths() {
276
- const home = process.env.HOME || process.cwd();
279
+ const home = os.homedir();
277
280
  return [
278
281
  path.join(home, ".gemini", "antigravity", "mcp_config.json"),
279
282
  path.join(home, ".gemini", "antigravity-cli", "mcp_config.json"),
@@ -456,7 +459,7 @@ export function verifySync({ cwd = process.cwd(), agents = DEFAULT_AGENTS } = {}
456
459
  const checks = [];
457
460
  const definitions = {
458
461
  codex: [path.join(cwd, ".codex", "config.toml")],
459
- claude: [path.join(cwd, ".mcp.json"), path.join(cwd, ".claude", "settings.json"), path.join(process.env.HOME || "", ".claude.json")],
462
+ claude: [path.join(cwd, ".mcp.json"), path.join(cwd, ".claude", "settings.json"), path.join(os.homedir(), ".claude.json")],
460
463
  antigravity: [
461
464
  path.join(cwd, ".gemini", "settings.json"),
462
465
  path.join(cwd, ".gemini", "mcp.json"),
@@ -473,6 +476,21 @@ export function verifySync({ cwd = process.cwd(), agents = DEFAULT_AGENTS } = {}
473
476
  return checks;
474
477
  }
475
478
 
479
+ function resolveStableMcpServerPath(rootDir) {
480
+ const codexRoot = path.join(process.env.CODEX_HOME || path.join(os.homedir(), ".codex"), "marketplaces", "contextos");
481
+ const dataRoot = defaultDataRoot();
482
+ const candidates = [
483
+ path.join(codexRoot, MCP_SERVER_RELATIVE),
484
+ path.join(dataRoot, "agents", "claude", "contextos", MCP_SERVER_RELATIVE),
485
+ path.join(dataRoot, "agents", "agy", "contextos", MCP_SERVER_RELATIVE),
486
+ path.join(rootDir, MCP_SERVER_RELATIVE)
487
+ ];
488
+ for (const candidate of candidates) {
489
+ if (fs.existsSync(candidate)) return candidate;
490
+ }
491
+ return path.join(rootDir, MCP_SERVER_RELATIVE);
492
+ }
493
+
476
494
  export async function syncRules({
477
495
  cwd = process.cwd(),
478
496
  rootDir,
@@ -495,7 +513,7 @@ export async function syncRules({
495
513
  const init = ensureRulerInit({ cwd, run, dryRun: options.dryRun });
496
514
  logger(statusLine("Checking .ruler/ruler.toml...", init.created ? "✓ created" : "✓ found"));
497
515
 
498
- const mcpServerPath = path.join(rootDir, "plugins", "ctx", "mcp", "server.js");
516
+ const mcpServerPath = resolveStableMcpServerPath(rootDir);
499
517
  const injected = injectCtxMcp({
500
518
  tomlPath: init.tomlPath,
501
519
  mcpServerPath,