@minhpnq1807/contextos 0.5.36 → 0.5.38

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,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.38
4
+
5
+ - **Unified agent branding:** All user-facing text now shows `antigravity` instead of `agy`. Internal value remains `agy` for backward compatibility. Interactive prompts display "Antigravity" without the parenthetical alias.
6
+ - **`ctx help` command:** Added `help` as a recognized command (alongside `--help` and `-h`). Previously returned "Unknown command".
7
+ - **`--help` in usage:** Added `ctx --help` line to the usage display so it's discoverable.
8
+ - **Fix `--agents` flag parsing:** `ctx install --agents antigravity` now correctly skips the interactive prompt. Previously only `--agent` (singular) was recognized.
9
+ - **Cleaner usage text:** Replaced hardcoded agent lists (`codex,claude,antigravity,copilot`) with `<names>` placeholder for future-proof documentation.
10
+
11
+ ## 0.5.37
12
+
13
+ - **Real-time animated progress bar for `ctx install`:** The progress spinner now updates in-place using raw stderr writes (`\r`) instead of being captured by `streamSetupOutput`. Uses a smooth 10-frame Braille spinner (`⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏`) with a visual bar (`[████████░░░░]`) that animates at 80ms intervals.
14
+ - **Clean install output:** Reduced verbose per-agent install summary from 10+ lines of paths to a compact 4-line summary (Hooks →, MCP →, Embeddings count, restart instruction). Removed redundant "embedding model already cached" log line from `warmInstallEmbeddings`.
15
+ - **Fix `streamSetupOutput` breaking spinner:** Previously intercepted `process.stderr.write` and converted `\r` carriage returns to newlines, preventing in-place updates. Now only intercepts `console.log` for `│ ` prefixed output, leaving stderr untouched for spinner rendering.
16
+ - **Suppress codex command noise:** `runCodex()` no longer prints verbose stdout lines like "Added marketplace `contextos`" and "Added global MCP server 'ctx-mcp'" — the progress spinner already provides feedback.
17
+ - **Clean context formatting:** Removed absolute paths from rules, skills, and workflows in scheduler output. Removed duplicate "ContextOS reminders" section. Truncated long skill descriptions to 80 chars. Capped rules at 5 per section instead of 8. Context now renders as clean, readable markdown:
18
+
3
19
  ## 0.5.36
4
20
 
5
21
  - **Fix ctx_score_context MCP output not rendering in Antigravity editor:** The `content` text block returned by the MCP tool previously only contained raw telemetry JSON, which editors cannot render as user-facing context. Now the tool uses `scheduleContext()` to produce the same human-readable markdown (Critical ContextOS rules, Suggested files, Skills, Workflows) that the hook path generates, and returns it as the primary `content[0].text` block. Telemetry JSON is pushed to a secondary content block. This ensures Antigravity (and any MCP-compatible editor) displays the scored rules and file suggestions.
package/bin/ctx.js CHANGED
@@ -43,44 +43,45 @@ function usage() {
43
43
  return `ContextOS (ctx)
44
44
 
45
45
  Usage:
46
- ctx install Interactive multi-select agent installer
47
- ctx install --agent <name> Install a specific agent (codex|claude|agy|copilot)
48
- ctx install --copy Legacy: copy plugin folder only (no hooks/mcp)
49
- ctx setup Interactive full setup wizard
50
- ctx setup --yes Auto-confirm all setup prompts
51
- ctx setup --agents codex,claude,agy,copilot Pre-select agents to install
52
- ctx setup --no-rules Skip AGENTS.md rule sync
53
- ctx setup --no-skills Skip skill sync
54
- ctx setup --quiet Quiet mode (minimal output)
55
- ctx debug -- "task" Debug a task with ContextOS tracing
56
- ctx report Show last ContextOS compliance report
57
- ctx evidence Show evidence from last report
58
- ctx stats Show workspace statistics
59
- ctx benchmark -- "task" Benchmark workspace for a task
60
- ctx sync --rules Sync AGENTS.md rules to all agents
61
- ctx sync --rules --agents codex,claude,agy,copilot Sync rules to specific agents only
62
- ctx sync --rules --dry-run Preview rule sync without writing
63
- ctx sync --rules --no-import-codex-mcp Skip importing Codex MCP servers
64
- ctx sync --skills Sync skills across agents
65
- ctx sync --skills --agents codex,claude,agy,copilot Sync skills to specific agents only
66
- ctx sync --skills --dry-run Preview skill sync without writing
67
- ctx sync --skills --no-collect Skip collecting new skills
68
- ctx sync --skills --no-embeddings Skip embedding generation
69
- ctx sync --skills --verbose Verbose skill sync output
70
- ctx sync --workflows Sync workflows across agents
71
- ctx sync --workflows --agents codex,claude,agy,copilot Sync workflows to specific agents
72
- ctx sync --workflows --dry-run Preview workflow sync without writing
73
- ctx embeddings warm -- "task" Pre-warm embedding caches for a task
74
- ctx ruler -- <ruler args> Passthrough to ruler CLI
75
- ctx skillshare -- <skillshare args> Passthrough to skillshare CLI
76
- ctx --version Show installed version
46
+ ctx install Interactive multi-select agent installer
47
+ ctx install --agent <name> Install a specific agent (codex|claude|antigravity|copilot)
48
+ ctx install --copy Legacy: copy plugin folder only (no hooks/mcp)
49
+ ctx setup Interactive full setup wizard
50
+ ctx setup --yes Auto-confirm all setup prompts
51
+ ctx setup --agents <names> Pre-select agents to install
52
+ ctx setup --no-rules Skip AGENTS.md rule sync
53
+ ctx setup --no-skills Skip skill sync
54
+ ctx setup --quiet Quiet mode (minimal output)
55
+ ctx debug -- "task" Debug a task with ContextOS tracing
56
+ ctx report Show last ContextOS compliance report
57
+ ctx evidence Show evidence from last report
58
+ ctx stats Show workspace statistics
59
+ ctx benchmark -- "task" Benchmark workspace for a task
60
+ ctx sync --rules Sync AGENTS.md rules to all agents
61
+ ctx sync --rules --agents <names> Sync rules to specific agents only
62
+ ctx sync --rules --dry-run Preview rule sync without writing
63
+ ctx sync --rules --no-import-codex-mcp Skip importing Codex MCP servers
64
+ ctx sync --skills Sync skills across agents
65
+ ctx sync --skills --agents <names> Sync skills to specific agents only
66
+ ctx sync --skills --dry-run Preview skill sync without writing
67
+ ctx sync --skills --no-collect Skip collecting new skills
68
+ ctx sync --skills --no-embeddings Skip embedding generation
69
+ ctx sync --skills --verbose Verbose skill sync output
70
+ ctx sync --workflows Sync workflows across agents
71
+ ctx sync --workflows --agents <names> Sync workflows to specific agents
72
+ ctx sync --workflows --dry-run Preview workflow sync without writing
73
+ ctx embeddings warm -- "task" Pre-warm embedding caches for a task
74
+ ctx ruler -- <ruler args> Passthrough to ruler CLI
75
+ ctx skillshare -- <skillshare args> Passthrough to skillshare CLI
76
+ ctx --help Show this help message
77
+ ctx --version Show installed version
77
78
  `;
78
79
  }
79
80
 
80
81
  const SUPPORTED_AGENTS = [
81
82
  { label: "Codex", value: "codex", selected: false },
82
83
  { label: "Claude Code", value: "claude", selected: false },
83
- { label: "Antigravity (agy)", value: "agy", selected: false },
84
+ { label: "Antigravity", value: "agy", selected: false },
84
85
  { label: "GitHub Copilot", value: "copilot", selected: false }
85
86
  ];
86
87
 
@@ -92,55 +93,52 @@ function normalizeInstallAgent(agent) {
92
93
  "Install one agent per command:",
93
94
  " ctx install --agent codex",
94
95
  " ctx install --agent claude",
95
- " ctx install --agent agy",
96
+ " ctx install --agent antigravity",
96
97
  " ctx install --agent copilot",
97
98
  "",
98
- "Do not run `ctx install --agent codex|claude|agy|copilot`: `|` is a shell pipe."
99
+ "Do not run `ctx install --agent codex|claude|antigravity|copilot`: `|` is a shell pipe."
99
100
  ].join("\n"));
100
101
  }
101
102
  if (normalized === "antigravity") return "agy";
102
103
  return normalized;
103
104
  }
104
105
  /**
105
- * Intercept console.log and process.stderr.write from an async fn,
106
+ * Intercept console.log from an async fn,
106
107
  * printing each line immediately with "│ " prefix for real-time feedback.
108
+ * stderr is left untouched so \r-based spinner writes render in-place.
107
109
  * Returns the collected lines array (for callers that inspect it).
108
110
  */
109
111
  async function streamSetupOutput(fn) {
110
112
  const lines = [];
111
113
  const origLog = console.log;
112
- const origStderrWrite = process.stderr.write;
113
114
  const emit = (text) => {
114
115
  lines.push(text);
115
116
  origLog(`│ ${text}`);
116
117
  };
117
118
  console.log = (...args) => emit(args.map(String).join(" "));
118
- process.stderr.write = (chunk) => {
119
- const text = String(chunk).replace(/\r/g, "").trim();
120
- if (text) emit(text);
121
- return true;
122
- };
123
119
  try {
124
120
  await fn();
125
121
  } finally {
126
122
  console.log = origLog;
127
- process.stderr.write = origStderrWrite;
128
123
  }
129
124
  return lines;
130
125
  }
131
126
 
132
127
  function createInstallProgress({ quiet = false } = {}) {
133
- const enabled = !quiet && process.stderr.isTTY;
134
- const frames = ["-", "\\", "|", "/"];
128
+ const isTTY = !quiet && process.stderr.isTTY;
129
+ const frames = ["", "", "", "", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
135
130
  let percent = 0;
136
131
  let label = "starting";
137
132
  let frame = 0;
138
133
  let timer = null;
134
+ // Use the raw stderr binding so streamSetupOutput cannot intercept spinner writes.
135
+ const rawStderrWrite = process.stderr.write.bind(process.stderr);
139
136
 
140
137
  function render() {
141
- if (!enabled) return;
142
- const text = `[ctx] install ${String(percent).padStart(3)}% ${frames[frame % frames.length]} ${label}`;
143
- process.stderr.write(`\r${text.padEnd(92)}`);
138
+ if (!isTTY) return;
139
+ const bar = progressBar(percent);
140
+ const text = ` ${frames[frame % frames.length]} ${bar} ${label}`;
141
+ rawStderrWrite(`\r${text.padEnd(72)}`);
144
142
  frame += 1;
145
143
  }
146
144
 
@@ -148,43 +146,48 @@ function createInstallProgress({ quiet = false } = {}) {
148
146
  start(initialLabel = "starting") {
149
147
  label = initialLabel;
150
148
  percent = 0;
151
- if (enabled) {
149
+ if (isTTY) {
152
150
  render();
153
- timer = setInterval(render, 120);
151
+ timer = setInterval(render, 80);
154
152
  } else if (!quiet) {
155
- console.log(`[ctx] install 0% ${label}`);
153
+ console.log(`[ctx] ${label}...`);
156
154
  }
157
155
  },
158
156
  step(nextPercent, nextLabel) {
159
157
  percent = Math.max(percent, Math.min(100, nextPercent));
160
158
  label = nextLabel;
161
- if (enabled) render();
162
- else if (!quiet) console.log(`[ctx] install ${percent}% ${label}`);
159
+ if (isTTY) render();
163
160
  },
164
161
  done(finalLabel = "done") {
165
162
  percent = 100;
166
163
  label = finalLabel;
167
164
  if (timer) clearInterval(timer);
168
165
  timer = null;
169
- if (enabled) {
170
- render();
171
- process.stderr.write("\n");
166
+ if (isTTY) {
167
+ const bar = progressBar(100);
168
+ rawStderrWrite(`\r ✓ ${bar} ${label}`.padEnd(72) + "\n");
172
169
  } else if (!quiet) {
173
- console.log(`[ctx] install 100% ${label}`);
170
+ console.log(`[ctx] ${label}`);
174
171
  }
175
172
  },
176
173
  fail(errorLabel = "failed") {
177
174
  label = errorLabel;
178
175
  if (timer) clearInterval(timer);
179
176
  timer = null;
180
- if (enabled) {
181
- render();
182
- process.stderr.write("\n");
177
+ if (isTTY) {
178
+ rawStderrWrite(`\r ✗ ${errorLabel}`.padEnd(72) + "\n");
183
179
  }
184
180
  }
185
181
  };
186
182
  }
187
183
 
184
+ function progressBar(percent) {
185
+ const width = 20;
186
+ const filled = Math.round(width * percent / 100);
187
+ const empty = width - filled;
188
+ return `[${'█'.repeat(filled)}${'░'.repeat(empty)}] ${String(percent).padStart(3)}%`;
189
+ }
190
+
188
191
  function packageVersion() {
189
192
  try {
190
193
  const packageJson = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8"));
@@ -224,81 +227,60 @@ async function install({ copy = false, agent = "codex" } = {}) {
224
227
  if (agent === "claude") {
225
228
  progress.step(10, "copying package");
226
229
  const installRoot = copyPackageRoot({ rootDir, targetRoot: agentInstallRoot("claude") });
227
- progress.step(25, "installing hooks");
230
+ progress.step(30, "installing hooks");
228
231
  const hooksPath = installClaudeHooks({ installRoot, injectPromptContext: inject });
229
- progress.step(40, "installing mcp");
232
+ progress.step(50, "installing mcp");
230
233
  const mcpConfigPath = installClaudeMcp({ installRoot });
231
- progress.step(50, "configuring gitignore");
234
+ progress.step(60, "configuring gitignore");
232
235
  writeInnerGitignore(installRoot);
233
236
  ensureRootGitignore(process.cwd());
234
- progress.step(55, "warming embeddings");
237
+ progress.step(70, "warming embeddings");
235
238
  const warmResult = await warmInstallEmbeddings();
236
- progress.done("claude installed");
237
- console.log("Installed ctx hooks for Claude Code.");
238
- console.log(`Stable install root: ${installRoot}`);
239
- console.log(`Installed ContextOS hooks to ${hooksPath}`);
240
- console.log(`Installed ctx-mcp MCP server to ${mcpConfigPath}`);
241
- console.log(`Embedding model cache: ${modelCacheDir(contextOSDataDir())}`);
242
- console.log(`Embedding vectors cache: ${warmResult.cachePath}`);
243
- console.log(`File path embeddings warmed: ${warmResult.fileCount || 0}`);
244
- console.log(`Skill embeddings warmed: ${warmResult.skillCount || 0}`);
245
- console.log(`Workflow embeddings warmed: ${warmResult.workflowCount || 0}`);
246
- console.log(`Prompt context injection: ${inject ? "enabled" : "quiet logging only"}`);
247
- console.log("Restart Claude Code if it was already running, then submit a task to trigger ContextOS.");
239
+ progress.done("claude ");
240
+ console.log(`Hooks ${hooksPath}`);
241
+ console.log(`MCP → ${mcpConfigPath}`);
242
+ console.log(`Embeddings: ${warmResult.fileCount || 0} files, ${warmResult.skillCount || 0} skills`);
243
+ console.log("Restart Claude Code to activate ContextOS.");
248
244
  return;
249
245
  }
250
246
 
251
247
  if (agent === "agy") {
252
248
  progress.step(10, "copying package");
253
249
  const installRoot = copyPackageRoot({ rootDir, targetRoot: agentInstallRoot("agy") });
254
- progress.step(25, "installing hooks");
250
+ progress.step(30, "installing hooks");
255
251
  const hooksPath = installAntigravityHooks({ installRoot, injectPromptContext: inject });
256
- progress.step(40, "installing mcp");
252
+ progress.step(50, "installing mcp");
257
253
  const mcpConfigPaths = installAntigravityMcp({ installRoot });
258
- progress.step(50, "configuring gitignore");
254
+ progress.step(60, "configuring gitignore");
259
255
  writeInnerGitignore(installRoot);
260
256
  ensureRootGitignore(process.cwd());
261
- progress.step(55, "warming embeddings");
257
+ progress.step(70, "warming embeddings");
262
258
  const warmResult = await warmInstallEmbeddings();
263
- progress.done("agy installed");
264
- console.log("Installed ctx hooks for Antigravity.");
265
- console.log(`Stable install root: ${installRoot}`);
266
- console.log(`Installed ContextOS hooks to ${hooksPath}`);
267
- console.log(`Installed ctx-mcp MCP server to ${mcpConfigPaths.join(", ")}`);
268
- console.log(`Embedding model cache: ${modelCacheDir(contextOSDataDir())}`);
269
- console.log(`Embedding vectors cache: ${warmResult.cachePath}`);
270
- console.log(`File path embeddings warmed: ${warmResult.fileCount || 0}`);
271
- console.log(`Skill embeddings warmed: ${warmResult.skillCount || 0}`);
272
- console.log(`Workflow embeddings warmed: ${warmResult.workflowCount || 0}`);
273
- console.log(`Prompt context injection: ${inject ? "enabled" : "quiet logging only"}`);
274
- console.log("Restart Antigravity or agy if it was already running, then submit a task to trigger ContextOS.");
259
+ progress.done("antigravity ");
260
+ console.log(`Hooks ${hooksPath}`);
261
+ console.log(`MCP → ${mcpConfigPaths.join(", ")}`);
262
+ console.log(`Embeddings: ${warmResult.fileCount || 0} files, ${warmResult.skillCount || 0} skills`);
263
+ console.log("Restart Antigravity to activate ContextOS.");
275
264
  return;
276
265
  }
277
266
 
278
267
  if (agent === "copilot") {
279
268
  progress.step(10, "copying package");
280
269
  const installRoot = copyPackageRoot({ rootDir, targetRoot: agentInstallRoot("copilot") });
281
- progress.step(25, "installing hooks");
270
+ progress.step(30, "installing hooks");
282
271
  const hooksPath = installCopilotHooks({ cwd: process.cwd(), installRoot });
283
- progress.step(40, "installing mcp");
272
+ progress.step(50, "installing mcp");
284
273
  const mcpConfigPath = installCopilotMcp({ cwd: process.cwd(), installRoot });
285
- progress.step(50, "configuring gitignore");
274
+ progress.step(60, "configuring gitignore");
286
275
  writeInnerGitignore(installRoot);
287
276
  ensureRootGitignore(process.cwd());
288
- progress.step(55, "warming embeddings");
277
+ progress.step(70, "warming embeddings");
289
278
  const warmResult = await warmInstallEmbeddings();
290
- progress.done("copilot installed");
291
- console.log("Installed ctx hooks for GitHub Copilot.");
292
- console.log(`Stable install root: ${installRoot}`);
293
- console.log(`Installed ContextOS instructions to ${hooksPath}`);
294
- console.log(`Installed ctx-mcp MCP server to ${mcpConfigPath}`);
295
- console.log(`Embedding model cache: ${modelCacheDir(contextOSDataDir())}`);
296
- console.log(`Embedding vectors cache: ${warmResult.cachePath}`);
297
- console.log(`File path embeddings warmed: ${warmResult.fileCount || 0}`);
298
- console.log(`Skill embeddings warmed: ${warmResult.skillCount || 0}`);
299
- console.log(`Workflow embeddings warmed: ${warmResult.workflowCount || 0}`);
300
- console.log(`Prompt context injection: ${inject ? "enabled" : "quiet logging only"}`);
301
- console.log("Restart VS Code or Copilot if it was already running, then submit a task to trigger ContextOS.");
279
+ progress.done("copilot ");
280
+ console.log(`Instructions ${hooksPath}`);
281
+ console.log(`MCP → ${mcpConfigPath}`);
282
+ console.log(`Embeddings: ${warmResult.fileCount || 0} files, ${warmResult.skillCount || 0} skills`);
283
+ console.log("Restart VS Code to activate ContextOS.");
302
284
  return;
303
285
  }
304
286
 
@@ -310,38 +292,31 @@ async function install({ copy = false, agent = "codex" } = {}) {
310
292
  const marketplaceRoot = path.join(codexHome(), "marketplaces", "contextos");
311
293
  copyPackageRoot({ rootDir, targetRoot: marketplaceRoot });
312
294
 
313
- progress.step(20, "refreshing codex plugin");
295
+ progress.step(25, "refreshing codex plugin");
314
296
  tryRunCodex(["plugin", "remove", "ctx@contextos"]);
315
297
  tryRunCodex(["plugin", "marketplace", "remove", "contextos"]);
316
298
  tryRunCodex(["mcp", "remove", "ctx-mcp"]);
317
299
  runCodex(["plugin", "marketplace", "add", marketplaceRoot]);
318
300
  runCodex(["plugin", "add", "ctx@contextos"]);
319
- progress.step(40, "installing mcp");
301
+ progress.step(45, "installing mcp");
320
302
  runCodex(["mcp", "add", "ctx-mcp", "--", "node", path.join(marketplaceRoot, "plugins", "ctx", "mcp", "server.js")]);
321
- progress.step(50, "installing telemetry proxies");
303
+ progress.step(55, "installing telemetry proxies");
322
304
  const proxyResult = installMcpTelemetryProxies({ codexHome: codexHome(), marketplaceRoot });
323
- progress.step(60, "installing hooks");
305
+ progress.step(65, "installing hooks");
324
306
  const hooksPath = installGlobalHooks({ codexHome: codexHome(), marketplaceRoot, injectPromptContext: inject });
325
307
 
326
- progress.step(65, "configuring gitignore");
308
+ progress.step(70, "configuring gitignore");
327
309
  writeInnerGitignore(marketplaceRoot);
328
310
  ensureRootGitignore(process.cwd());
329
311
 
330
- progress.step(70, "warming embeddings");
312
+ progress.step(80, "warming embeddings");
331
313
  const warmResult = await warmInstallEmbeddings();
332
- progress.done("codex installed");
333
- console.log("Installed ctx through Codex plugin marketplace.");
334
- console.log(`Stable marketplace root: ${marketplaceRoot}`);
335
- console.log(`Installed ContextOS global hooks to ${hooksPath}`);
336
- console.log("Installed ctx-mcp MCP server.");
337
- console.log(`MCP telemetry proxies: ${proxyResult.wrapped.length ? proxyResult.wrapped.map((item) => item.name).join(", ") : "none changed"}`);
338
- console.log(`Embedding model cache: ${modelCacheDir(contextOSDataDir())}`);
339
- console.log(`Embedding vectors cache: ${warmResult.cachePath}`);
340
- console.log(`File path embeddings warmed: ${warmResult.fileCount || 0}`);
341
- console.log(`Skill embeddings warmed: ${warmResult.skillCount || 0}`);
342
- console.log(`Workflow embeddings warmed: ${warmResult.workflowCount || 0}`);
343
- console.log(`Prompt context injection: ${inject ? "enabled" : "quiet logging only"}`);
344
- console.log("Restart Codex if it was already running, then submit a task to trigger ContextOS.");
314
+ progress.done("codex ");
315
+ console.log(`Hooks → ${hooksPath}`);
316
+ console.log(`MCP → ctx-mcp installed`);
317
+ console.log(`Proxies ${proxyResult.wrapped.length ? proxyResult.wrapped.map((item) => item.name).join(", ") : "none changed"}`);
318
+ console.log(`Embeddings: ${warmResult.fileCount || 0} files, ${warmResult.skillCount || 0} skills`);
319
+ console.log("Restart Codex to activate ContextOS.");
345
320
  } catch (error) {
346
321
  progress.fail("install failed");
347
322
  throw error;
@@ -351,9 +326,6 @@ async function install({ copy = false, agent = "codex" } = {}) {
351
326
  async function warmInstallEmbeddings() {
352
327
  const dataDir = contextOSDataDir();
353
328
  const modelReady = isModelCacheReady(dataDir);
354
- console.log(modelReady
355
- ? "Required local embedding model already cached."
356
- : "Preparing required local embedding model...");
357
329
  const result = await warmRuleEmbeddings({
358
330
  rules: [
359
331
  { content: "Always use project rules that are semantically relevant to the user prompt." },
@@ -398,23 +370,14 @@ function tryRunCodex(args) {
398
370
 
399
371
  function runCodex(args) {
400
372
  try {
401
- const stdout = execFileSync("codex", args, {
373
+ execFileSync("codex", args, {
402
374
  stdio: ["ignore", "pipe", "pipe"],
403
375
  encoding: "utf8",
404
376
  shell: true
405
377
  });
406
- if (stdout && stdout.trim()) {
407
- for (const line of stdout.trim().split(/\r?\n/)) {
408
- console.log(line);
409
- }
410
- }
378
+ // Suppress stdout (e.g. "Added marketplace…", "Added global MCP server…")
379
+ // the progress spinner already provides feedback.
411
380
  } catch (error) {
412
- // Log any output captured before the error
413
- if (error.stdout && error.stdout.trim()) {
414
- for (const line of error.stdout.trim().split(/\r?\n/)) {
415
- console.log(line);
416
- }
417
- }
418
381
  const status = typeof error.status === "number" ? error.status : 1;
419
382
  throw new Error(`codex ${args.join(" ")} failed with exit code ${status}. Make sure Codex CLI is installed and authenticated.`);
420
383
  }
@@ -580,7 +543,7 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
580
543
  options: [
581
544
  { label: "Codex", value: "codex", selected: options.agents.includes("codex") },
582
545
  { label: "Claude", value: "claude", selected: options.agents.includes("claude") },
583
- { label: "Antigravity (agy)", value: "agy", selected: options.agents.includes("agy") },
546
+ { label: "Antigravity", value: "agy", selected: options.agents.includes("agy") },
584
547
  { label: "GitHub Copilot", value: "copilot", selected: options.agents.includes("copilot") }
585
548
  ]
586
549
  });
@@ -608,7 +571,7 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
608
571
  for (const line of setupSummaryLines({ cwd, ...options })) console.log(`│ ${line}`);
609
572
  console.log("");
610
573
 
611
- if (!options.agents.length) throw new Error("No agents selected. Use --agents codex,claude,agy,copilot.");
574
+ if (!options.agents.length) throw new Error("No agents selected. Use --agents codex,claude,antigravity,copilot.");
612
575
 
613
576
  for (const agent of options.agents) {
614
577
  console.log(`◇ Setting up ${agent}...`);
@@ -650,26 +613,33 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
650
613
  const args = process.argv.slice(2);
651
614
  const command = args[0];
652
615
 
653
- function installAgentFromArgs(args) {
654
- const agentFlag = args.indexOf("--agent");
655
- if (agentFlag >= 0) return normalizeInstallAgent(args[agentFlag + 1] || "");
656
- return null; // no --agent flag interactive selection
616
+ function installAgentsFromArgs(args) {
617
+ const agentFlag = Math.max(args.indexOf("--agent"), args.indexOf("--agents"));
618
+ if (agentFlag >= 0) {
619
+ const value = args[agentFlag + 1] || "";
620
+ return parseAgentList(value).map(normalizeInstallAgent).filter(Boolean);
621
+ }
622
+ return null; // no flag → interactive selection
657
623
  }
658
624
 
659
625
  try {
660
- if (!command || command === "--help" || command === "-h") {
626
+ if (!command || command === "--help" || command === "-h" || command === "help") {
661
627
  console.log(usage());
662
628
  } else if (command === "--version" || command === "-v") {
663
629
  console.log(packageVersion());
664
630
  } else if (command === "install") {
665
631
  const copy = args.includes("--copy");
666
- const explicitAgent = installAgentFromArgs(args);
667
-
668
- if (explicitAgent) {
669
- // Direct mode: ctx install --agent <name>
670
- console.log(`◇ Installing ${explicitAgent}...`);
671
- await streamSetupOutput(() => install({ copy, agent: explicitAgent }));
672
- console.log("");
632
+ const explicitAgents = installAgentsFromArgs(args);
633
+
634
+ if (explicitAgents && explicitAgents.length) {
635
+ // Direct mode: ctx install --agents antigravity,codex
636
+ for (const agent of explicitAgents) {
637
+ console.log(`◇ Installing ${agent}...`);
638
+ await streamSetupOutput(() => install({ copy, agent }));
639
+ console.log("");
640
+ }
641
+ } else if (explicitAgents && !explicitAgents.length) {
642
+ console.log("No valid agents specified. Use --agents codex,claude,antigravity,copilot.");
673
643
  } else {
674
644
  // Interactive mode: ctx install
675
645
  const selected = await multiSelect({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhpnq1807/contextos",
3
- "version": "0.5.36",
3
+ "version": "0.5.38",
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,6 +1,6 @@
1
1
  {
2
2
  "name": "ctx",
3
- "version": "0.5.36",
3
+ "version": "0.5.37",
4
4
  "description": "Inject task-relevant AGENTS.md rules into Codex through plugin hooks.",
5
5
  "author": {
6
6
  "name": "ContextOS"
@@ -1,3 +1,4 @@
1
+
1
2
  const MAX_CONTEXT_CHARS = 4000;
2
3
 
3
4
  export function scheduleContext({ rules = [], relevantFiles = [], suggestedSkills = [], suggestedWorkflows = [], maxChars = MAX_CONTEXT_CHARS } = {}) {
@@ -8,7 +9,7 @@ export function scheduleContext({ rules = [], relevantFiles = [], suggestedSkill
8
9
 
9
10
  const sections = [];
10
11
  if (high.length) {
11
- sections.push(section("Critical ContextOS rules", high.slice(0, 8).map(formatRule)));
12
+ sections.push(section("Critical ContextOS rules", high.slice(0, 5).map(formatRule)));
12
13
  }
13
14
  if (relevantFiles.length) {
14
15
  sections.push(section("Suggested files to check", relevantFiles.map((file) => `- ${file.path}`)));
@@ -20,10 +21,7 @@ export function scheduleContext({ rules = [], relevantFiles = [], suggestedSkill
20
21
  sections.push(section("Suggested workflow for this task", suggestedWorkflows.map(formatWorkflow)));
21
22
  }
22
23
  if (mid.length) {
23
- sections.push(section("Additional relevant rules", mid.slice(0, 8).map(formatRule)));
24
- }
25
- if (high.length) {
26
- sections.push(section("ContextOS reminders", high.slice(0, 5).map(formatRule)));
24
+ sections.push(section("Additional relevant rules", mid.slice(0, 5).map(formatRule)));
27
25
  }
28
26
 
29
27
  const additionalContext = trimToLimit(sections.filter(Boolean).join("\n\n"), maxChars);
@@ -57,24 +55,28 @@ function section(title, lines) {
57
55
  return `## ${title}\n${lines.join("\n")}`;
58
56
  }
59
57
 
58
+
60
59
  function formatRule(rule) {
61
- const source = rule.sourcePath && rule.sourcePath !== "unknown" ? ` (${rule.sourcePath})` : "";
62
- return `- ${rule.content}${source}`;
60
+ return `- ${rule.content}`;
63
61
  }
64
62
 
65
63
  function formatSkill(skill) {
66
- const description = skill.description ? `: ${skill.description}` : "";
67
- const location = skill.path ? ` (${skill.path})` : "";
68
- return `- ${skill.name}${description}${location}`;
64
+ const desc = skill.description
65
+ ? `: ${truncate(skill.description, 80)}`
66
+ : "";
67
+ return `- ${skill.name}${desc}`;
69
68
  }
70
69
 
71
70
  function formatWorkflow(workflow) {
72
71
  const name = workflow.title || workflow.name;
73
72
  const hint = workflow.hint ? `: ${workflow.hint}` : "";
74
73
  const chain = workflow.chain?.length ? `\n chain: ${workflow.chain.join(" -> ")}` : "";
75
- const location = workflow.relativePath || workflow.path;
76
- const source = location ? `\n see: ${location}` : "";
77
- return `- ${name}${hint}${chain}${source}`;
74
+ return `- ${name}${hint}${chain}`;
75
+ }
76
+
77
+ function truncate(text, maxLen) {
78
+ if (text.length <= maxLen) return text;
79
+ return `${text.slice(0, maxLen - 1)}…`;
78
80
  }
79
81
 
80
82
  function trimToLimit(value, maxChars) {