@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 +16 -0
- package/bin/ctx.js +125 -155
- package/package.json +1 -1
- package/plugins/ctx/.codex-plugin/plugin.json +1 -1
- package/plugins/ctx/lib/scheduler.js +15 -13
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
|
|
47
|
-
ctx install --agent <name>
|
|
48
|
-
ctx install --copy
|
|
49
|
-
ctx setup
|
|
50
|
-
ctx setup --yes
|
|
51
|
-
ctx setup --agents
|
|
52
|
-
ctx setup --no-rules
|
|
53
|
-
ctx setup --no-skills
|
|
54
|
-
ctx setup --quiet
|
|
55
|
-
ctx debug -- "task"
|
|
56
|
-
ctx report
|
|
57
|
-
ctx evidence
|
|
58
|
-
ctx stats
|
|
59
|
-
ctx benchmark -- "task"
|
|
60
|
-
ctx sync --rules
|
|
61
|
-
ctx sync --rules --agents
|
|
62
|
-
ctx sync --rules --dry-run
|
|
63
|
-
ctx sync --rules --no-import-codex-mcp
|
|
64
|
-
ctx sync --skills
|
|
65
|
-
ctx sync --skills --agents
|
|
66
|
-
ctx sync --skills --dry-run
|
|
67
|
-
ctx sync --skills --no-collect
|
|
68
|
-
ctx sync --skills --no-embeddings
|
|
69
|
-
ctx sync --skills --verbose
|
|
70
|
-
ctx sync --workflows
|
|
71
|
-
ctx sync --workflows --agents
|
|
72
|
-
ctx sync --workflows --dry-run
|
|
73
|
-
ctx embeddings warm -- "task"
|
|
74
|
-
ctx ruler -- <ruler args>
|
|
75
|
-
ctx skillshare -- <skillshare args>
|
|
76
|
-
ctx --
|
|
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
|
|
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
|
|
96
|
+
" ctx install --agent antigravity",
|
|
96
97
|
" ctx install --agent copilot",
|
|
97
98
|
"",
|
|
98
|
-
"Do not run `ctx install --agent codex|claude|
|
|
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
|
|
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
|
|
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 (!
|
|
142
|
-
const
|
|
143
|
-
|
|
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 (
|
|
149
|
+
if (isTTY) {
|
|
152
150
|
render();
|
|
153
|
-
timer = setInterval(render,
|
|
151
|
+
timer = setInterval(render, 80);
|
|
154
152
|
} else if (!quiet) {
|
|
155
|
-
console.log(`[ctx]
|
|
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 (
|
|
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 (
|
|
170
|
-
|
|
171
|
-
|
|
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]
|
|
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 (
|
|
181
|
-
|
|
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(
|
|
230
|
+
progress.step(30, "installing hooks");
|
|
228
231
|
const hooksPath = installClaudeHooks({ installRoot, injectPromptContext: inject });
|
|
229
|
-
progress.step(
|
|
232
|
+
progress.step(50, "installing mcp");
|
|
230
233
|
const mcpConfigPath = installClaudeMcp({ installRoot });
|
|
231
|
-
progress.step(
|
|
234
|
+
progress.step(60, "configuring gitignore");
|
|
232
235
|
writeInnerGitignore(installRoot);
|
|
233
236
|
ensureRootGitignore(process.cwd());
|
|
234
|
-
progress.step(
|
|
237
|
+
progress.step(70, "warming embeddings");
|
|
235
238
|
const warmResult = await warmInstallEmbeddings();
|
|
236
|
-
progress.done("claude
|
|
237
|
-
console.log(
|
|
238
|
-
console.log(`
|
|
239
|
-
console.log(`
|
|
240
|
-
console.log(
|
|
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(
|
|
250
|
+
progress.step(30, "installing hooks");
|
|
255
251
|
const hooksPath = installAntigravityHooks({ installRoot, injectPromptContext: inject });
|
|
256
|
-
progress.step(
|
|
252
|
+
progress.step(50, "installing mcp");
|
|
257
253
|
const mcpConfigPaths = installAntigravityMcp({ installRoot });
|
|
258
|
-
progress.step(
|
|
254
|
+
progress.step(60, "configuring gitignore");
|
|
259
255
|
writeInnerGitignore(installRoot);
|
|
260
256
|
ensureRootGitignore(process.cwd());
|
|
261
|
-
progress.step(
|
|
257
|
+
progress.step(70, "warming embeddings");
|
|
262
258
|
const warmResult = await warmInstallEmbeddings();
|
|
263
|
-
progress.done("
|
|
264
|
-
console.log(
|
|
265
|
-
console.log(`
|
|
266
|
-
console.log(`
|
|
267
|
-
console.log(
|
|
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(
|
|
270
|
+
progress.step(30, "installing hooks");
|
|
282
271
|
const hooksPath = installCopilotHooks({ cwd: process.cwd(), installRoot });
|
|
283
|
-
progress.step(
|
|
272
|
+
progress.step(50, "installing mcp");
|
|
284
273
|
const mcpConfigPath = installCopilotMcp({ cwd: process.cwd(), installRoot });
|
|
285
|
-
progress.step(
|
|
274
|
+
progress.step(60, "configuring gitignore");
|
|
286
275
|
writeInnerGitignore(installRoot);
|
|
287
276
|
ensureRootGitignore(process.cwd());
|
|
288
|
-
progress.step(
|
|
277
|
+
progress.step(70, "warming embeddings");
|
|
289
278
|
const warmResult = await warmInstallEmbeddings();
|
|
290
|
-
progress.done("copilot
|
|
291
|
-
console.log(
|
|
292
|
-
console.log(`
|
|
293
|
-
console.log(`
|
|
294
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
303
|
+
progress.step(55, "installing telemetry proxies");
|
|
322
304
|
const proxyResult = installMcpTelemetryProxies({ codexHome: codexHome(), marketplaceRoot });
|
|
323
|
-
progress.step(
|
|
305
|
+
progress.step(65, "installing hooks");
|
|
324
306
|
const hooksPath = installGlobalHooks({ codexHome: codexHome(), marketplaceRoot, injectPromptContext: inject });
|
|
325
307
|
|
|
326
|
-
progress.step(
|
|
308
|
+
progress.step(70, "configuring gitignore");
|
|
327
309
|
writeInnerGitignore(marketplaceRoot);
|
|
328
310
|
ensureRootGitignore(process.cwd());
|
|
329
311
|
|
|
330
|
-
progress.step(
|
|
312
|
+
progress.step(80, "warming embeddings");
|
|
331
313
|
const warmResult = await warmInstallEmbeddings();
|
|
332
|
-
progress.done("codex
|
|
333
|
-
console.log(
|
|
334
|
-
console.log(`
|
|
335
|
-
console.log(`
|
|
336
|
-
console.log(
|
|
337
|
-
console.log(
|
|
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
|
-
|
|
373
|
+
execFileSync("codex", args, {
|
|
402
374
|
stdio: ["ignore", "pipe", "pipe"],
|
|
403
375
|
encoding: "utf8",
|
|
404
376
|
shell: true
|
|
405
377
|
});
|
|
406
|
-
|
|
407
|
-
|
|
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
|
|
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,
|
|
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
|
|
654
|
-
const agentFlag = args.indexOf("--agent");
|
|
655
|
-
if (agentFlag >= 0)
|
|
656
|
-
|
|
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
|
|
667
|
-
|
|
668
|
-
if (
|
|
669
|
-
// Direct mode: ctx install --
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
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,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,
|
|
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,
|
|
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
|
-
|
|
62
|
-
return `- ${rule.content}${source}`;
|
|
60
|
+
return `- ${rule.content}`;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
function formatSkill(skill) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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) {
|