@minhpnq1807/contextos 0.2.0 → 0.3.1

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,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ - Adds `ctx sync --rules` for Ruler-backed project rule/MCP sync across Codex, Claude Code, and Antigravity.
6
+ - Supports `--agents`, `--dry-run`, `--force`, and `--yes` flags for targeted, previewable, idempotent Ruler sync.
7
+ - Injects `ctx-mcp` into `.ruler/ruler.toml` without deleting user-defined MCP servers or agent sections.
8
+ - Imports existing Codex MCP servers from `~/.codex/config.toml` into Ruler so servers like `code-review-graph` and `agentmemory` can propagate to Antigravity and Claude Code.
9
+ - Mirrors Ruler MCP servers into Antigravity app/CLI MCP config files after `ruler apply` so Antigravity receives all imported MCP servers.
10
+ - Imports project `.mcp.json` servers such as `mcp-rtk` so MCPs generated by Ruler or other agents are also propagated.
11
+ - Skips embedding model download during `ctx install` when the required MiniLM files are already present in `~/.ctx/contextos/models`.
12
+ - Writes Antigravity MCP config to the legacy editor path `~/.gemini/config/mcp_config.json` so older editor builds can show ContextOS under `@mcp`.
13
+
14
+ ## 0.2.4
15
+
16
+ - Hardens workspace isolation for Claude Code, Codex, and Antigravity hooks by normalizing project cwd from hook payloads, `workspacePath(s)`, and `CLAUDE_PROJECT_DIR` before writing prompt/report telemetry.
17
+
18
+ ## 0.2.3
19
+
20
+ - Registers `ctx-mcp` for Claude Code by writing a user-scoped MCP server into `~/.claude.json`.
21
+
22
+ ## 0.2.2
23
+
24
+ - Registers `ctx-mcp` for Antigravity app and `agy` CLI by writing `~/.gemini/antigravity/mcp_config.json` and `~/.gemini/antigravity-cli/mcp_config.json`.
25
+
26
+ ## 0.2.1
27
+
28
+ - Adds `ctx install claude` for Claude Code hooks in `~/.claude/settings.json`.
29
+ - Adds `ctx install agy` for Antigravity hooks in `~/.gemini/config/hooks.json`.
30
+ - Adds Antigravity `PreInvocation` and `Stop` adapters so prompt context can be injected through `ephemeralMessage` and reports remain available through `ctx report` / `ctx evidence`.
31
+
3
32
  ## 0.2.0
4
33
 
5
34
  - Adds `ctx benchmark -- "task"` to compare baseline AGENTS.md ordering with ContextOS scheduling and estimate lost-in-the-middle risk.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ContextOS
2
2
 
3
- ContextOS (`ctx`) is a Codex companion plugin for task-aware project context.
3
+ ContextOS (`ctx`) is an agent companion for task-aware project context.
4
4
 
5
5
  It reads `AGENTS.md` guidance, scores the rules against the current prompt, suggests relevant files, records what context would have been injected, and reports lightweight compliance evidence after the task finishes.
6
6
 
@@ -16,10 +16,19 @@ ctx install
16
16
 
17
17
  Restart Codex after installing, then use Codex normally. ContextOS runs through Codex hooks and the `ctx-mcp` MCP server.
18
18
 
19
+ Claude Code and Antigravity are supported through their native hook systems:
20
+
21
+ ```bash
22
+ ctx install claude
23
+ ctx install agy
24
+ ```
25
+
19
26
  You can also run without a global install:
20
27
 
21
28
  ```bash
22
29
  npx @minhpnq1807/contextos@latest install
30
+ npx @minhpnq1807/contextos@latest install claude
31
+ npx @minhpnq1807/contextos@latest install agy
23
32
  ```
24
33
 
25
34
  ## Demo Flow
@@ -31,16 +40,16 @@ ctx install
31
40
  codex
32
41
  ```
33
42
 
34
- Prompt Codex:
43
+ Prompt the agent:
35
44
 
36
45
  ```text
37
- kiểm tra flow kiểm duyệt upload
46
+ Recheck authen flow
38
47
  ```
39
48
 
40
49
  Expected result:
41
50
 
42
51
  - `UserPromptSubmit` injects relevant AGENTS.md rules.
43
- - ContextOS suggests upload/moderation files.
52
+ - ContextOS suggests auth/authentication files.
44
53
  - `Stop` prints a ContextOS report with rule outcomes.
45
54
  - `ctx evidence` shows the specific evidence behind the last report.
46
55
 
@@ -53,16 +62,18 @@ With ContextOS, each prompt gets a compact block:
53
62
  ```text
54
63
  ## Critical ContextOS rules
55
64
  - Use code-review-graph before reading files.
56
- - Check upload moderation flows before editing approval code.
65
+ - Recheck authentication flow before editing auth code.
57
66
 
58
67
  ## Suggested files to check
59
- - services/content-service/src/infrastructure/services/content-moderation.service.ts
60
- - webapp/src/features/dashboard/components/moderation-status-badge.tsx
68
+ - services/auth-service/src/auth.controller.ts
69
+ - services/auth-service/src/auth.service.ts
61
70
  ```
62
71
 
63
72
  ## What It Does
64
73
 
65
74
  - Hooks into Codex `UserPromptSubmit`, `SessionStart`, and `Stop`.
75
+ - Hooks into Claude Code `UserPromptSubmit`, `SessionStart`, and `Stop`.
76
+ - Hooks into Antigravity `PreInvocation` and `Stop` through the `agy` adapter.
66
77
  - Registers a `ctx-mcp` MCP server that owns model loading and semantic scoring.
67
78
  - Reads the active `AGENTS.md` chain for the current workspace.
68
79
  - Scores rules by relevance to the user prompt.
@@ -98,7 +109,22 @@ From this repository during local development:
98
109
  node bin/ctx.js install
99
110
  ```
100
111
 
101
- `ctx install` does three things:
112
+ Agent-specific installers:
113
+
114
+ ```bash
115
+ ctx install codex
116
+ ctx install claude
117
+ ctx install agy
118
+ ctx install --agent codex
119
+ ctx install --agent claude
120
+ ctx install --agent agy
121
+ ```
122
+
123
+ `ctx install` defaults to `ctx install codex`.
124
+
125
+ ### Codex
126
+
127
+ `ctx install codex` does these things:
102
128
 
103
129
  1. Copies this package into `$CODEX_HOME/marketplaces/contextos`.
104
130
  2. Registers and installs `ctx@contextos` through Codex plugin marketplace commands.
@@ -109,13 +135,37 @@ node bin/ctx.js install
109
135
 
110
136
  Restart Codex after installing.
111
137
 
112
- The embedding model is mandatory. `ctx install` intentionally fails if the model cannot be prepared, because otherwise the first prompt hook would have to cold-load or download the model.
138
+ ### Claude Code
139
+
140
+ `ctx install claude` copies this package into `~/.ctx/contextos/agents/claude/contextos`, merges ContextOS hooks into `~/.claude/settings.json`, and registers `ctx-mcp` as a user-scoped Claude Code MCP server in `~/.claude.json`.
141
+
142
+ Claude Code receives prompt context through `UserPromptSubmit` using `hookSpecificOutput.additionalContext`, then ContextOS writes the same local workspace report files used by `ctx report`, `ctx evidence`, and `ctx stats`.
143
+
144
+ Restart Claude Code after installing.
145
+
146
+ ### Antigravity
147
+
148
+ `ctx install agy` copies this package into `~/.ctx/contextos/agents/agy/contextos`, writes a `contextos` hook group into `~/.gemini/config/hooks.json`, and registers `ctx-mcp` in Antigravity MCP config locations:
149
+
150
+ ```text
151
+ ~/.gemini/antigravity/mcp_config.json
152
+ ~/.gemini/antigravity-cli/mcp_config.json
153
+ ~/.gemini/config/mcp_config.json
154
+ ```
155
+
156
+ The third path supports older Antigravity editor builds where `@mcp` reads the legacy Gemini config directory.
157
+
158
+ Antigravity does not use `UserPromptSubmit`; ContextOS injects context through `PreInvocation` as an `ephemeralMessage`. The `Stop` adapter stores the report locally, so use `ctx report` or `ctx evidence` after the task to inspect outcomes.
159
+
160
+ Restart Antigravity or `agy` after installing.
161
+
162
+ The embedding model is mandatory. `ctx install` checks `~/.ctx/contextos/models` first and downloads the MiniLM model only when the required local files are missing. It intentionally fails if the model cannot be prepared, because otherwise the first prompt hook would have to cold-load or download the model.
113
163
 
114
164
  Verify the published package in any project:
115
165
 
116
166
  ```bash
117
167
  npm exec --yes --package=@minhpnq1807/contextos@latest -- ctx --version
118
- npm exec --yes --package=@minhpnq1807/contextos@latest -- ctx debug -- "fix upload moderation flow"
168
+ npm exec --yes --package=@minhpnq1807/contextos@latest -- ctx debug -- "Recheck authen flow"
119
169
  ```
120
170
 
121
171
  ## Modes
@@ -150,6 +200,43 @@ ctx install --copy
150
200
 
151
201
  Copies only the plugin payload into `$CODEX_HOME/plugins/ctx`. This is mostly for local experiments.
152
202
 
203
+ ## Ruler Sync
204
+
205
+ Use Ruler when the project wants one rule/MCP source of truth for multiple agents:
206
+
207
+ ```bash
208
+ ctx sync --rules
209
+ ```
210
+
211
+ Default agents are `codex`, `claude`, and `antigravity`. You can target a subset:
212
+
213
+ ```bash
214
+ ctx sync --rules --agents codex
215
+ ctx sync --rules --agents codex,claude
216
+ ```
217
+
218
+ What it does:
219
+
220
+ 1. Checks that `ruler` is installed. If it is missing, ContextOS asks before running `npm install -g @intellectronica/ruler`; use `--yes` for non-interactive installs.
221
+ 2. Runs `ruler init` when `.ruler/ruler.toml` is missing.
222
+ 3. Adds `ctx-mcp` to `.ruler/ruler.toml` under `[mcp_servers.ctx-mcp]`.
223
+ 4. Imports existing MCP servers from Codex `~/.codex/config.toml` and project `.mcp.json`, such as `code-review-graph`, `agentmemory`, and `mcp-rtk`, into `.ruler/ruler.toml`.
224
+ 5. Adds enabled Ruler agent entries for Codex, Claude Code, and Antigravity using merge strategy.
225
+ 6. Runs `ruler apply --agents ...`.
226
+ 7. Mirrors the Ruler MCP server list into Antigravity app/CLI MCP configs because current Ruler versions do not emit every Antigravity MCP file consistently.
227
+ 8. Verifies that generated agent config contains `ctx-mcp`.
228
+
229
+ Useful flags:
230
+
231
+ ```bash
232
+ ctx sync --rules --dry-run
233
+ ctx sync --rules --force
234
+ ctx sync --rules --yes
235
+ ctx sync --rules --no-import-codex-mcp
236
+ ```
237
+
238
+ `ctx sync --rules` is project-scoped. It writes `.ruler/ruler.toml` in the current project and lets Ruler generate agent files from that project source of truth. ContextOS runtime history still follows the project-path isolation model described below.
239
+
153
240
  ## Troubleshooting
154
241
 
155
242
  ### `ctx-mcp bridge socket not found`
@@ -161,7 +248,7 @@ Restart Codex after `ctx install`. The bridge socket is owned by the long-runnin
161
248
  Run:
162
249
 
163
250
  ```bash
164
- ctx embeddings warm -- "kiểm tra flow kiểm duyệt upload"
251
+ ctx embeddings warm -- "Recheck authen flow"
165
252
  ```
166
253
 
167
254
  Then restart Codex.
@@ -182,15 +269,24 @@ This warning comes from a transitive dependency in the local embedding/WASM stac
182
269
 
183
270
  | Command | Meaning | Use when | Output / side effect |
184
271
  | --- | --- | --- | --- |
185
- | `ctx install` | Installs ContextOS into Codex with prompt context injection enabled. | Normal setup after installing the npm package. | Copies the plugin into `$CODEX_HOME/marketplaces/contextos`, registers `ctx@contextos`, registers `ctx-mcp`, installs global hooks, downloads the embedding model, and warms caches. |
186
- | `ctx install --quiet` | Installs ContextOS in measurement-only mode. | You want reports and stats but do not want a visible `hook context` block in Codex. | Installs the same plugin/hooks, but prompt hooks return empty `additionalContext`. |
187
- | `ctx install --inject` | Installs ContextOS with explicit injection mode. | You want to be explicit in scripts or docs. | Same runtime behavior as `ctx install`. |
272
+ | `ctx install` | Installs ContextOS into Codex with prompt context injection enabled. | Normal Codex setup after installing the npm package. | Same as `ctx install codex`. |
273
+ | `ctx install codex` | Installs ContextOS into Codex. | You use the `codex` CLI. | Copies the plugin into `$CODEX_HOME/marketplaces/contextos`, registers `ctx@contextos`, registers `ctx-mcp`, installs global hooks, downloads the embedding model, and warms caches. |
274
+ | `ctx install claude` | Installs ContextOS into Claude Code. | You use the `claude` CLI. | Copies a stable package root to `~/.ctx/contextos/agents/claude/contextos`, merges hooks into `~/.claude/settings.json`, and registers `ctx-mcp` in `~/.claude.json`. |
275
+ | `ctx install agy` | Installs ContextOS into Antigravity. | You use the `agy` CLI or Antigravity app/editor. | Copies a stable package root to `~/.ctx/contextos/agents/agy/contextos`, writes hooks to `~/.gemini/config/hooks.json`, and registers `ctx-mcp` in Antigravity app, CLI, and legacy editor MCP config paths. |
276
+ | `ctx install --agent <name>` | Installs for a named agent. | You prefer explicit scripts. | Accepts `codex`, `claude`, or `agy`. |
277
+ | `ctx install --quiet` | Installs ContextOS in measurement-only mode. | You want reports and stats but do not want visible injected context. | Installs the same hooks, but prompt hooks return empty context. |
278
+ | `ctx install --inject` | Installs ContextOS with explicit injection mode. | You want to be explicit in scripts or docs. | Same runtime behavior as the default install mode. |
188
279
  | `ctx install --copy` | Copies only the plugin payload to `$CODEX_HOME/plugins/ctx`. | Local development or manual plugin experiments. | Does not register marketplace, MCP, or global hooks. |
189
280
  | `ctx debug -- "task"` | Runs the scheduler locally for a fake prompt. | You want to see which AGENTS.md rules and files ContextOS would inject before using Codex. | Prints rule scores, scoring reasons, suggested files, and final `additionalContext`. |
190
- | `ctx report` | Shows the last Stop-hook compliance report for the current workspace. | A Codex task has finished and you want the summary again. | Reads `~/.ctx/contextos/workspaces/<workspace-id>/last-report.json`. |
281
+ | `ctx report` | Shows the last Stop-hook compliance report for the current workspace. | An agent task has finished and you want the summary again. | Reads `~/.ctx/contextos/workspaces/<workspace-id>/last-report.json`. |
191
282
  | `ctx evidence` | Shows detailed evidence behind the last report for the current workspace. | You want to inspect why a rule was marked `followed`, `ignored`, or `unknown`. | Prints rule text, source file, score, status, and evidence reason. |
192
283
  | `ctx stats` | Shows aggregate runtime metrics for the current workspace. | You want to know whether ContextOS is active and useful over time. | Prints prompt count, report count, injected/quiet ratio, average prompt analysis time, efficiency, rule outcomes, hook events, and last suggested files for the current workspace only. |
193
284
  | `ctx benchmark -- "task"` | Compares baseline AGENTS.md ordering with ContextOS task-aware scheduling. | You want a before/after signal for lost-in-the-middle risk. | Prints parsed/actionable/filtered rule counts, relevant rules in the middle of the original file, scheduled high/mid rules, and top scored rules. |
285
+ | `ctx sync --rules` | Syncs project rules and MCP servers through Ruler. | You want Codex, Claude Code, and Antigravity to share one project rule/MCP source of truth. | Ensures `.ruler/ruler.toml`, injects `ctx-mcp`, imports existing MCP servers from Codex and project `.mcp.json`, runs `ruler apply --agents codex,claude,antigravity`, mirrors MCP servers to Antigravity MCP configs, and verifies generated config. |
286
+ | `ctx sync --rules --agents <list>` | Syncs only selected agents through Ruler. | You want to update one or two agents without touching the others. | Accepts comma-separated values such as `codex`, `claude`, `antigravity`, or `codex,claude`. |
287
+ | `ctx sync --rules --dry-run` | Previews Ruler sync without writing files or running apply. | You want to inspect behavior before changing project config. | Prints the same flow with dry-run status. |
288
+ | `ctx sync --rules --force` | Rewrites ContextOS-owned Ruler sections. | You changed the ContextOS install path or need to refresh `ctx-mcp`. | Removes and re-adds ContextOS-owned `mcp`, `mcp_servers.ctx-mcp`, and selected agent sections. |
289
+ | `ctx sync --rules --no-import-codex-mcp` | Skips Codex MCP import. | You only want ContextOS' own `ctx-mcp` in Ruler. | Does not read `~/.codex/config.toml`. |
194
290
  | `ctx embeddings warm -- "task"` | Prepares local semantic embedding caches. | First install, CI smoke checks, or after changing AGENTS.md/project files. | Loads/downloads `Xenova/all-MiniLM-L6-v2` and writes vectors to `~/.ctx/contextos/embeddings.db`. |
195
291
  | `ctx --version` | Prints the installed ContextOS CLI version. | You want to confirm which npm version is being executed. | Prints the version from package metadata. |
196
292
 
@@ -228,6 +324,8 @@ The workspace id is stored in the target repo at:
228
324
 
229
325
  ContextOS also adds `.contextos/` to the repo `.gitignore` so the local marker is not pushed. If the marker cannot be written, ContextOS falls back to a deterministic id generated from the workspace real path.
230
326
 
327
+ Codex, Claude Code, and Antigravity all write prompt context, reports, evidence, stats, and telemetry through this same workspace id. The same project shares one ContextOS runtime history across agents; different project paths get different workspace directories. Claude Code hooks also use `CLAUDE_PROJECT_DIR` when the hook payload does not include `cwd`, and Antigravity uses `workspacePath` / `workspacePaths` when present.
328
+
231
329
  These files are local telemetry only. Hooks do not make network calls.
232
330
 
233
331
  ## Project Understanding
@@ -331,7 +429,7 @@ npm pack --dry-run
331
429
  Smoke test prompt hook:
332
430
 
333
431
  ```bash
334
- printf '%s' '{"prompt":"fix auth validation","cwd":"'$PWD'","hook_event_name":"UserPromptSubmit"}' \
432
+ printf '%s' '{"prompt":"Recheck authen flow","cwd":"'$PWD'","hook_event_name":"UserPromptSubmit"}' \
335
433
  | node plugins/ctx/bin/on-prompt.js
336
434
  ```
337
435
 
@@ -367,9 +465,10 @@ contextos-plan.jsx implementation plan/reference
367
465
 
368
466
  ## Limitations
369
467
 
370
- - Codex CLI only.
468
+ - Codex and Claude Code get prompt context through `additionalContext`; Antigravity gets prompt context through `PreInvocation` `ephemeralMessage`.
469
+ - Antigravity Stop hooks store reports locally, but they do not display the full report inline unless Antigravity adds a non-continuing Stop message surface.
371
470
  - Local marketplace plugin hooks may not fire reliably in current Codex builds, so `ctx install` also installs global hooks.
372
- - Injection mode may show a visible `hook context` block in Codex.
471
+ - Injection mode may show a visible hook context block in some agents.
373
472
  - Quiet mode does not inject context into the model; it only records and measures.
374
473
  - Compliance is heuristic and mostly based on git diff/status.
375
474
  - Some rules can only be `unknown` unless ContextOS records richer telemetry such as tool calls or shell command metadata.
package/bin/ctx.js CHANGED
@@ -10,12 +10,18 @@ import { scheduleContext } from "../plugins/ctx/lib/scheduler.js";
10
10
  import { formatEvidence, formatReport } from "../plugins/ctx/lib/reporter.js";
11
11
  import { installGlobalHooks } from "../plugins/ctx/lib/global-hooks.js";
12
12
  import { formatStats, loadStats } from "../plugins/ctx/lib/stats.js";
13
- import { modelCacheDir, warmRuleEmbeddings } from "../plugins/ctx/lib/embedding-scorer.js";
13
+ import { isModelCacheReady, modelCacheDir, warmRuleEmbeddings } from "../plugins/ctx/lib/embedding-scorer.js";
14
14
  import { warmFileEmbeddings } from "../plugins/ctx/lib/file-embedding-retriever.js";
15
15
  import { scoreContext } from "../plugins/ctx/lib/score-context.js";
16
16
  import { defaultDataRoot, workspaceDataDir, workspaceMarkerPath } from "../plugins/ctx/lib/workspace-data.js";
17
17
  import { installMcpTelemetryProxies } from "../plugins/ctx/lib/mcp-proxy-install.js";
18
18
  import { benchmarkWorkspace, formatBenchmark } from "../plugins/ctx/lib/benchmark.js";
19
+ import { copyDir, copyPackageRoot } from "../plugins/ctx/lib/package-install.js";
20
+ import { installClaudeHooks } from "../plugins/ctx/lib/claude-hooks.js";
21
+ import { installClaudeMcp } from "../plugins/ctx/lib/claude-mcp.js";
22
+ import { installAntigravityHooks } from "../plugins/ctx/lib/antigravity-hooks.js";
23
+ import { installAntigravityMcp } from "../plugins/ctx/lib/antigravity-mcp.js";
24
+ import { syncRules } from "../plugins/ctx/lib/ruler-sync.js";
19
25
 
20
26
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
27
  const rootDir = path.resolve(__dirname, "..");
@@ -26,6 +32,10 @@ function usage() {
26
32
 
27
33
  Usage:
28
34
  ctx install
35
+ ctx install codex
36
+ ctx install claude
37
+ ctx install agy
38
+ ctx install --agent codex|claude|agy
29
39
  ctx install --quiet
30
40
  ctx install --inject
31
41
  ctx install --copy
@@ -34,6 +44,10 @@ Usage:
34
44
  ctx evidence
35
45
  ctx stats
36
46
  ctx benchmark -- "task"
47
+ ctx sync --rules
48
+ ctx sync --rules --agents codex,claude,antigravity
49
+ ctx sync --rules --dry-run
50
+ ctx sync --rules --no-import-codex-mcp
37
51
  ctx embeddings warm -- "task"
38
52
  ctx --version
39
53
  `;
@@ -52,31 +66,6 @@ function codexHome() {
52
66
  return process.env.CODEX_HOME || path.join(process.env.HOME || process.cwd(), ".codex");
53
67
  }
54
68
 
55
- function copyDir(src, dest) {
56
- fs.mkdirSync(dest, { recursive: true });
57
- for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
58
- const srcPath = path.join(src, entry.name);
59
- const destPath = path.join(dest, entry.name);
60
- if (entry.isDirectory()) {
61
- copyDir(srcPath, destPath);
62
- } else if (entry.isFile()) {
63
- fs.copyFileSync(srcPath, destPath);
64
- fs.chmodSync(destPath, fs.statSync(srcPath).mode);
65
- }
66
- }
67
- }
68
-
69
- function copyPath(src, dest) {
70
- const stat = fs.statSync(src);
71
- if (stat.isDirectory()) {
72
- copyDir(src, dest);
73
- } else {
74
- fs.mkdirSync(path.dirname(dest), { recursive: true });
75
- fs.copyFileSync(src, dest);
76
- fs.chmodSync(dest, stat.mode);
77
- }
78
- }
79
-
80
69
  function copyInstall() {
81
70
  const target = path.join(codexHome(), "plugins", "ctx");
82
71
  fs.rmSync(target, { recursive: true, force: true });
@@ -85,19 +74,57 @@ function copyInstall() {
85
74
  console.log("Restart Codex if it was already running, then submit a task to trigger ContextOS.");
86
75
  }
87
76
 
88
- async function install({ copy = false, inject = true } = {}) {
77
+ function agentInstallRoot(agent) {
78
+ return path.join(contextOSDataDir(), "agents", agent, "contextos");
79
+ }
80
+
81
+ async function install({ copy = false, inject = true, agent = "codex" } = {}) {
89
82
  if (copy) {
90
83
  copyInstall();
91
84
  return;
92
85
  }
93
86
 
94
- const marketplaceRoot = path.join(codexHome(), "marketplaces", "contextos");
95
- fs.rmSync(marketplaceRoot, { recursive: true, force: true });
96
- for (const entry of [".agents", "bin", "plugins", "package.json", "package-lock.json", "README.md", "LICENSE", "node_modules"]) {
97
- const src = path.join(rootDir, entry);
98
- if (fs.existsSync(src)) copyPath(src, path.join(marketplaceRoot, entry));
87
+ if (agent === "claude") {
88
+ const installRoot = copyPackageRoot({ rootDir, targetRoot: agentInstallRoot("claude") });
89
+ const hooksPath = installClaudeHooks({ installRoot, injectPromptContext: inject });
90
+ const mcpConfigPath = installClaudeMcp({ installRoot });
91
+ const warmResult = await warmInstallEmbeddings();
92
+ console.log("Installed ctx hooks for Claude Code.");
93
+ console.log(`Stable install root: ${installRoot}`);
94
+ console.log(`Installed ContextOS hooks to ${hooksPath}`);
95
+ console.log(`Installed ctx-mcp MCP server to ${mcpConfigPath}`);
96
+ console.log(`Embedding model cache: ${modelCacheDir(contextOSDataDir())}`);
97
+ console.log(`Embedding vectors cache: ${warmResult.cachePath}`);
98
+ console.log(`File path embeddings warmed: ${warmResult.fileCount || 0}`);
99
+ console.log(`Prompt context injection: ${inject ? "enabled" : "quiet logging only"}`);
100
+ console.log("Restart Claude Code if it was already running, then submit a task to trigger ContextOS.");
101
+ return;
99
102
  }
100
103
 
104
+ if (agent === "agy") {
105
+ const installRoot = copyPackageRoot({ rootDir, targetRoot: agentInstallRoot("agy") });
106
+ const hooksPath = installAntigravityHooks({ installRoot, injectPromptContext: inject });
107
+ const mcpConfigPaths = installAntigravityMcp({ installRoot });
108
+ const warmResult = await warmInstallEmbeddings();
109
+ console.log("Installed ctx hooks for Antigravity.");
110
+ console.log(`Stable install root: ${installRoot}`);
111
+ console.log(`Installed ContextOS hooks to ${hooksPath}`);
112
+ console.log(`Installed ctx-mcp MCP server to ${mcpConfigPaths.join(", ")}`);
113
+ console.log(`Embedding model cache: ${modelCacheDir(contextOSDataDir())}`);
114
+ console.log(`Embedding vectors cache: ${warmResult.cachePath}`);
115
+ console.log(`File path embeddings warmed: ${warmResult.fileCount || 0}`);
116
+ console.log(`Prompt context injection: ${inject ? "enabled" : "quiet logging only"}`);
117
+ console.log("Restart Antigravity or agy if it was already running, then submit a task to trigger ContextOS.");
118
+ return;
119
+ }
120
+
121
+ if (agent !== "codex") {
122
+ throw new Error(`Unknown agent '${agent}'. Expected codex, claude, or agy.`);
123
+ }
124
+
125
+ const marketplaceRoot = path.join(codexHome(), "marketplaces", "contextos");
126
+ copyPackageRoot({ rootDir, targetRoot: marketplaceRoot });
127
+
101
128
  tryRunCodex(["plugin", "remove", "ctx@contextos"]);
102
129
  tryRunCodex(["plugin", "marketplace", "remove", "contextos"]);
103
130
  tryRunCodex(["mcp", "remove", "ctx-mcp"]);
@@ -107,7 +134,6 @@ async function install({ copy = false, inject = true } = {}) {
107
134
  const proxyResult = installMcpTelemetryProxies({ codexHome: codexHome(), marketplaceRoot });
108
135
  const hooksPath = installGlobalHooks({ codexHome: codexHome(), marketplaceRoot, injectPromptContext: inject });
109
136
 
110
- console.log("Preparing required local embedding model...");
111
137
  const warmResult = await warmInstallEmbeddings();
112
138
  console.log("Installed ctx through Codex plugin marketplace.");
113
139
  console.log(`Stable marketplace root: ${marketplaceRoot}`);
@@ -123,6 +149,10 @@ async function install({ copy = false, inject = true } = {}) {
123
149
 
124
150
  async function warmInstallEmbeddings() {
125
151
  const dataDir = contextOSDataDir();
152
+ const modelReady = isModelCacheReady(dataDir);
153
+ console.log(modelReady
154
+ ? "Required local embedding model already cached."
155
+ : "Preparing required local embedding model...");
126
156
  const result = await warmRuleEmbeddings({
127
157
  rules: [
128
158
  { content: "Always use project rules that are semantically relevant to the user prompt." },
@@ -132,14 +162,14 @@ async function warmInstallEmbeddings() {
132
162
  task: "kiểm duyệt upload moderation semantic code search",
133
163
  dataDir,
134
164
  sources: [],
135
- allowRemote: true
165
+ allowRemote: !modelReady
136
166
  });
137
167
  const fileResult = await warmFileEmbeddings({
138
168
  cwd: process.cwd(),
139
169
  dataDir,
140
- allowRemote: true
170
+ allowRemote: !modelReady
141
171
  });
142
- return { ...result, fileCount: fileResult.count };
172
+ return { ...result, modelAlreadyCached: modelReady, fileCount: fileResult.count };
143
173
  }
144
174
 
145
175
  function tryRunCodex(args) {
@@ -248,13 +278,24 @@ async function warmEmbeddings(task) {
248
278
  const args = process.argv.slice(2);
249
279
  const command = args[0];
250
280
 
281
+ function installAgentFromArgs(args) {
282
+ const agentFlag = args.indexOf("--agent");
283
+ if (agentFlag >= 0) return args[agentFlag + 1] || "";
284
+ const firstValue = args.slice(1).find((arg) => !arg.startsWith("--"));
285
+ return firstValue || "codex";
286
+ }
287
+
251
288
  try {
252
289
  if (!command || command === "--help" || command === "-h") {
253
290
  console.log(usage());
254
291
  } else if (command === "--version" || command === "-v") {
255
292
  console.log(packageVersion());
256
293
  } else if (command === "install") {
257
- await install({ copy: args.includes("--copy"), inject: !args.includes("--quiet") });
294
+ await install({
295
+ copy: args.includes("--copy"),
296
+ inject: !args.includes("--quiet"),
297
+ agent: installAgentFromArgs(args)
298
+ });
258
299
  } else if (command === "debug") {
259
300
  const marker = args.indexOf("--");
260
301
  const task = marker >= 0 ? args.slice(marker + 1).join(" ") : args.slice(1).join(" ");
@@ -279,6 +320,8 @@ try {
279
320
  const task = marker >= 0 ? args.slice(marker + 1).join(" ") : args.slice(1).join(" ");
280
321
  if (!task.trim()) throw new Error('Usage: ctx benchmark -- "task"');
281
322
  console.log(formatBenchmark(benchmarkWorkspace({ cwd: process.cwd(), task })));
323
+ } else if (command === "sync") {
324
+ await syncRules({ cwd: process.cwd(), rootDir, args: args.slice(1) });
282
325
  } else {
283
326
  throw new Error(`Unknown command: ${command}\n\n${usage()}`);
284
327
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@minhpnq1807/contextos",
3
- "version": "0.2.0",
4
- "description": "Task-aware AGENTS.md context injection and compliance reporting for Codex.",
3
+ "version": "0.3.1",
4
+ "description": "Task-aware AGENTS.md context injection and compliance reporting for Codex, Claude Code, and Antigravity.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "ctx": "bin/ctx.js"
@@ -29,6 +29,9 @@
29
29
  },
30
30
  "keywords": [
31
31
  "codex",
32
+ "claude-code",
33
+ "antigravity",
34
+ "agy",
32
35
  "plugin",
33
36
  "agents",
34
37
  "context",
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { readStdinJson, writeJson, failOpen, logDebug, pluginRuntimeFile, pluginDataRoot } from "../lib/hook-io.js";
3
+ import { handlePromptPayload } from "../lib/prompt-hook.js";
4
+ import { appendTelemetry } from "../lib/telemetry.js";
5
+ import { antigravityCwd, extractPromptFromAntigravityPayload } from "../lib/antigravity-adapter.js";
6
+
7
+ const started = Date.now();
8
+
9
+ try {
10
+ const payload = await readStdinJson();
11
+ const cwd = antigravityCwd(payload);
12
+ const prompt = extractPromptFromAntigravityPayload(payload);
13
+ const normalized = {
14
+ ...payload,
15
+ cwd,
16
+ prompt,
17
+ hook_event_name: "PreInvocation"
18
+ };
19
+
20
+ logDebug("Antigravity PreInvocation", normalized);
21
+ appendTelemetry({ telemetryPath: pluginRuntimeFile("telemetry.jsonl", cwd), event: "PreInvocation", payload: normalized });
22
+ const output = await handlePromptPayload(normalized, {
23
+ dataPath: pluginRuntimeFile("last-prompt-context.json", cwd),
24
+ historyPath: pluginRuntimeFile("prompt-history.jsonl", cwd),
25
+ mcpDataDir: pluginDataRoot(),
26
+ started
27
+ });
28
+ const additionalContext = output?.hookSpecificOutput?.additionalContext || "";
29
+ writeJson({
30
+ injectSteps: additionalContext ? [{ ephemeralMessage: additionalContext }] : []
31
+ });
32
+ } catch (error) {
33
+ failOpen("PreInvocation", error, {
34
+ injectSteps: []
35
+ });
36
+ }
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ import { readStdinJson, writeJson, failOpen, logDebug, pluginRuntimeFile } from "../lib/hook-io.js";
3
+ import { handleStopPayload } from "../lib/stop-hook.js";
4
+ import { appendTelemetry } from "../lib/telemetry.js";
5
+ import { antigravityCwd } from "../lib/antigravity-adapter.js";
6
+
7
+ try {
8
+ const payload = await readStdinJson();
9
+ const cwd = antigravityCwd(payload);
10
+ const normalized = {
11
+ ...payload,
12
+ cwd,
13
+ hook_event_name: "Stop"
14
+ };
15
+ logDebug("Antigravity Stop", normalized);
16
+ appendTelemetry({ telemetryPath: pluginRuntimeFile("telemetry.jsonl", cwd), event: "Stop", payload: normalized });
17
+ handleStopPayload(normalized, {
18
+ contextPath: pluginRuntimeFile("last-prompt-context.json", cwd),
19
+ reportPath: pluginRuntimeFile("last-report.json", cwd),
20
+ historyPath: pluginRuntimeFile("report-history.jsonl", cwd),
21
+ telemetryPath: pluginRuntimeFile("telemetry.jsonl", cwd)
22
+ });
23
+ writeJson({ decision: "" });
24
+ } catch (error) {
25
+ failOpen("Stop", error, {
26
+ decision: ""
27
+ });
28
+ }
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { readStdinJson, writeJson, failOpen, logDebug, pluginRuntimeFile, pluginDataRoot } from "../lib/hook-io.js";
2
+ import { readStdinJson, writeJson, failOpen, logDebug, pluginRuntimeFile, pluginDataRoot, resolveHookCwd } from "../lib/hook-io.js";
3
3
  import { handlePromptPayload } from "../lib/prompt-hook.js";
4
4
  import { appendTelemetry } from "../lib/telemetry.js";
5
5
 
@@ -7,11 +7,12 @@ const started = Date.now();
7
7
 
8
8
  try {
9
9
  const payload = await readStdinJson();
10
- const cwd = payload.cwd || payload.working_directory;
10
+ const cwd = resolveHookCwd(payload);
11
+ const normalized = { ...payload, cwd };
11
12
 
12
- logDebug("UserPromptSubmit", payload);
13
- appendTelemetry({ telemetryPath: pluginRuntimeFile("telemetry.jsonl", cwd), event: "UserPromptSubmit", payload });
14
- writeJson(await handlePromptPayload(payload, {
13
+ logDebug("UserPromptSubmit", normalized);
14
+ appendTelemetry({ telemetryPath: pluginRuntimeFile("telemetry.jsonl", cwd), event: "UserPromptSubmit", payload: normalized });
15
+ writeJson(await handlePromptPayload(normalized, {
15
16
  dataPath: pluginRuntimeFile("last-prompt-context.json", cwd),
16
17
  historyPath: pluginRuntimeFile("prompt-history.jsonl", cwd),
17
18
  mcpDataDir: pluginDataRoot(),
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { readStdinJson, writeJson, failOpen, logDebug, pluginRuntimeFile } from "../lib/hook-io.js";
2
+ import { readStdinJson, writeJson, failOpen, logDebug, pluginRuntimeFile, resolveHookCwd } from "../lib/hook-io.js";
3
3
  import { appendTelemetry } from "../lib/telemetry.js";
4
4
 
5
5
  try {
6
6
  const payload = await readStdinJson();
7
- const cwd = payload.cwd || payload.working_directory;
8
- logDebug("SessionStart", payload);
9
- appendTelemetry({ telemetryPath: pluginRuntimeFile("telemetry.jsonl", cwd), event: "SessionStart", payload });
7
+ const cwd = resolveHookCwd(payload);
8
+ const normalized = { ...payload, cwd };
9
+ logDebug("SessionStart", normalized);
10
+ appendTelemetry({ telemetryPath: pluginRuntimeFile("telemetry.jsonl", cwd), event: "SessionStart", payload: normalized });
10
11
  writeJson({
11
12
  continue: true,
12
13
  suppressOutput: true,
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { readStdinJson, writeJson, failOpen, logDebug, pluginRuntimeFile } from "../lib/hook-io.js";
2
+ import { readStdinJson, writeJson, failOpen, logDebug, pluginRuntimeFile, resolveHookCwd } from "../lib/hook-io.js";
3
3
  import { handleStopPayload } from "../lib/stop-hook.js";
4
4
  import { appendTelemetry } from "../lib/telemetry.js";
5
5
 
6
6
  try {
7
7
  const payload = await readStdinJson();
8
- const cwd = payload.cwd || payload.working_directory;
9
- logDebug("Stop", payload);
10
- appendTelemetry({ telemetryPath: pluginRuntimeFile("telemetry.jsonl", cwd), event: "Stop", payload });
11
- writeJson(handleStopPayload(payload, {
8
+ const cwd = resolveHookCwd(payload);
9
+ const normalized = { ...payload, cwd };
10
+ logDebug("Stop", normalized);
11
+ appendTelemetry({ telemetryPath: pluginRuntimeFile("telemetry.jsonl", cwd), event: "Stop", payload: normalized });
12
+ writeJson(handleStopPayload(normalized, {
12
13
  contextPath: pluginRuntimeFile("last-prompt-context.json", cwd),
13
14
  reportPath: pluginRuntimeFile("last-report.json", cwd),
14
15
  historyPath: pluginRuntimeFile("report-history.jsonl", cwd),
@@ -43,7 +43,10 @@ const SEMANTIC_ALIASES = {
43
43
  thong: ["notification", "notify", "message"],
44
44
  bao: ["notification", "notify", "message"],
45
45
  "thong-bao": ["notification", "notify", "message"],
46
- thongbao: ["notification", "notify", "message"]
46
+ thongbao: ["notification", "notify", "message"],
47
+ authen: ["auth", "authentication", "login"],
48
+ authentication: ["auth", "authen", "login"],
49
+ recheck: ["check", "verify", "review"]
47
50
  };
48
51
 
49
52
  const MODERATION_TOKENS = new Set(["moderation", "moderate", "content-moderation", "approval", "approved", "reject", "rejected", "needs_review"]);