@minhpnq1807/contextos 0.5.41 → 0.5.44
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 +31 -0
- package/README.md +41 -10
- package/bin/ctx.js +262 -138
- package/package.json +2 -1
- package/plugins/ctx/.codex-plugin/plugin.json +1 -1
- package/plugins/ctx/bin/on-prompt.js +12 -9
- package/plugins/ctx/lib/analyzer.js +12 -94
- package/plugins/ctx/lib/auto-warm.js +74 -0
- package/plugins/ctx/lib/ctx-mcp-client.js +58 -9
- package/plugins/ctx/lib/embedding-scorer.js +109 -7
- package/plugins/ctx/lib/file-embedding-retriever.js +20 -23
- package/plugins/ctx/lib/global-hooks.js +20 -0
- package/plugins/ctx/lib/graph-retriever.js +82 -15
- package/plugins/ctx/lib/graph-strategy.js +107 -0
- package/plugins/ctx/lib/hook-io.js +29 -1
- package/plugins/ctx/lib/import-graph.js +37 -40
- package/plugins/ctx/lib/mcp-proxy-install.js +18 -90
- package/plugins/ctx/lib/output-config.js +85 -0
- package/plugins/ctx/lib/package-install.js +8 -0
- package/plugins/ctx/lib/prompt-hook.js +95 -20
- package/plugins/ctx/lib/ruler-sync.js +9 -71
- package/plugins/ctx/lib/scheduler.js +33 -21
- package/plugins/ctx/lib/score-context.js +60 -32
- package/plugins/ctx/lib/setup-wizard.js +5 -2
- package/plugins/ctx/lib/shell-runner.js +88 -0
- package/plugins/ctx/lib/skill-discoverer.js +110 -10
- package/plugins/ctx/lib/skillshare-sync.js +51 -2
- package/plugins/ctx/lib/stop-hook.js +2 -3
- package/plugins/ctx/lib/toml-config.js +116 -0
- package/plugins/ctx/mcp/server.js +6 -2
package/bin/ctx.js
CHANGED
|
@@ -5,7 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import readline from "node:readline/promises";
|
|
6
6
|
import { stdin as input, stdout as output } from "node:process";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { execFileSync, execSync
|
|
8
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
9
9
|
|
|
10
10
|
import { readAgentsChain } from "../plugins/ctx/lib/reader.js";
|
|
11
11
|
import { filterActionableRules, parseRules, scoreRules } from "../plugins/ctx/lib/analyzer.js";
|
|
@@ -19,23 +19,27 @@ import { scoreContext } from "../plugins/ctx/lib/score-context.js";
|
|
|
19
19
|
import { defaultDataRoot, workspaceDataDir, workspaceMarkerPath } from "../plugins/ctx/lib/workspace-data.js";
|
|
20
20
|
import { installMcpTelemetryProxies } from "../plugins/ctx/lib/mcp-proxy-install.js";
|
|
21
21
|
import { benchmarkWorkspace, formatBenchmark } from "../plugins/ctx/lib/benchmark.js";
|
|
22
|
-
import { copyDir, copyPackageRoot } from "../plugins/ctx/lib/package-install.js";
|
|
22
|
+
import { copyDir, copyPackageRoot, syncPackageRoot } from "../plugins/ctx/lib/package-install.js";
|
|
23
23
|
import { installClaudeHooks } from "../plugins/ctx/lib/claude-hooks.js";
|
|
24
24
|
import { installClaudeMcp } from "../plugins/ctx/lib/claude-mcp.js";
|
|
25
25
|
import { installAntigravityHooks } from "../plugins/ctx/lib/antigravity-hooks.js";
|
|
26
26
|
import { installAntigravityMcp } from "../plugins/ctx/lib/antigravity-mcp.js";
|
|
27
27
|
import { installCopilotHooks } from "../plugins/ctx/lib/copilot-hooks.js";
|
|
28
28
|
import { installCopilotMcp } from "../plugins/ctx/lib/copilot-mcp.js";
|
|
29
|
-
import { syncRules } from "../plugins/ctx/lib/ruler-sync.js";
|
|
29
|
+
import { readCodexMcpServers, syncRules } from "../plugins/ctx/lib/ruler-sync.js";
|
|
30
|
+
import { detectGraphStrategy, embedCodeReviewGraph, formatCodeReviewGraphEmbedding, formatGraphStrategy } from "../plugins/ctx/lib/graph-strategy.js";
|
|
30
31
|
import { writeInnerGitignore, ensureRootGitignore } from "../plugins/ctx/lib/gitignore.js";
|
|
31
|
-
import { syncSkills } from "../plugins/ctx/lib/skillshare-sync.js";
|
|
32
|
+
import { repairSkillSymlinks, syncSkills, detectExistingSkills } from "../plugins/ctx/lib/skillshare-sync.js";
|
|
32
33
|
import { scanSkills, warmSkillEmbeddings } from "../plugins/ctx/lib/skill-discoverer.js";
|
|
33
34
|
import { parsePassthroughArgs, runPassthrough } from "../plugins/ctx/lib/passthrough.js";
|
|
34
35
|
import { parseAgentList, parseSetupArgs, setupSummaryLines } from "../plugins/ctx/lib/setup-wizard.js";
|
|
35
36
|
import { multiSelect } from "../plugins/ctx/lib/multi-select.js";
|
|
37
|
+
import { configureOutputSections, enabledOutputSectionsLabel, loadOutputConfig } from "../plugins/ctx/lib/output-config.js";
|
|
36
38
|
import { syncWorkflows, warmWorkflowEmbeddings } from "../plugins/ctx/lib/workflow-discoverer.js";
|
|
37
39
|
import { checkForUpdate } from "../plugins/ctx/lib/update-notifier.js";
|
|
38
40
|
import { fetchSkillsForAgents, printSkillRecommendations, getAllLibraries, getInstallCommands } from "../plugins/ctx/lib/skill-library.js";
|
|
41
|
+
import { invalidateCtxMcpSocket } from "../plugins/ctx/lib/ctx-mcp-client.js";
|
|
42
|
+
import { runPrefixedCommand } from "../plugins/ctx/lib/shell-runner.js";
|
|
39
43
|
|
|
40
44
|
/**
|
|
41
45
|
* Run a shell command with all output lines prefixed by │
|
|
@@ -43,28 +47,124 @@ import { fetchSkillsForAgents, printSkillRecommendations, getAllLibraries, getIn
|
|
|
43
47
|
* stdin is inherited so interactive prompts (e.g. npx "Ok to proceed?") still work.
|
|
44
48
|
*/
|
|
45
49
|
function runPrefixed(cmd) {
|
|
50
|
+
return runPrefixedCommand(cmd);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Interactive community skill library installer.
|
|
55
|
+
* Fetches library metadata, shows a multiSelect, and runs install commands.
|
|
56
|
+
* @param {string[]} agents - Agent names to filter libraries for.
|
|
57
|
+
* @returns {Promise<number>} Number of successfully installed sources.
|
|
58
|
+
*/
|
|
59
|
+
async function runCommunitySkillInstaller(agents = []) {
|
|
60
|
+
const RESET = "\x1B[0m";
|
|
46
61
|
const DIM = "\x1B[2m";
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
const CYAN = "\x1B[36m";
|
|
63
|
+
const GREEN = "\x1B[32m";
|
|
64
|
+
const YELLOW = "\x1B[33m";
|
|
65
|
+
const BOLD = "\x1B[1m";
|
|
66
|
+
|
|
67
|
+
console.log("Fetching community skill libraries...\n");
|
|
68
|
+
const libraryResults = await fetchSkillsForAgents(agents, { dataDir: contextOSDataDir() });
|
|
69
|
+
|
|
70
|
+
const totalSkills = libraryResults.reduce((sum, r) => sum + r.count, 0);
|
|
71
|
+
if (totalSkills === 0) {
|
|
72
|
+
console.log("No skills found. Check your network connection or try --refresh.");
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Compact header
|
|
77
|
+
console.log(`${CYAN}◇${RESET} ${BOLD}Community skill libraries available:${RESET}`);
|
|
78
|
+
console.log(`${DIM}│${RESET} Browse and install curated skills from the community.`);
|
|
79
|
+
console.log(`${DIM}│${RESET}`);
|
|
80
|
+
|
|
81
|
+
const allLibs = getAllLibraries();
|
|
82
|
+
const availableLibs = allLibs.filter((lib) => {
|
|
83
|
+
const result = libraryResults.find((r) => r.library.id === lib.id);
|
|
84
|
+
return result && result.count > 0;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (availableLibs.length === 0) {
|
|
88
|
+
console.log("No installable libraries available.");
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const selectedSources = await multiSelect({
|
|
93
|
+
message: "Select skill sources to install:",
|
|
94
|
+
options: availableLibs.map((lib) => {
|
|
95
|
+
const result = libraryResults.find((r) => r.library.id === lib.id);
|
|
96
|
+
return {
|
|
97
|
+
label: `${lib.name} (${result?.count || 0} skills)`,
|
|
98
|
+
value: lib.id,
|
|
99
|
+
hint: lib.url,
|
|
100
|
+
selected: false
|
|
101
|
+
};
|
|
102
|
+
})
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (!selectedSources || selectedSources.length === 0) {
|
|
106
|
+
console.log(`\n${DIM}No sources selected.${RESET}`);
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Install each selected source
|
|
111
|
+
let successCount = 0;
|
|
112
|
+
for (const libId of selectedSources) {
|
|
113
|
+
const lib = allLibs.find((l) => l.id === libId);
|
|
114
|
+
if (!lib) continue;
|
|
115
|
+
|
|
116
|
+
const installInfo = getInstallCommands(libId);
|
|
117
|
+
if (!installInfo) {
|
|
118
|
+
console.log(`${YELLOW}⚠${RESET} No install info for ${lib.name}. Visit: ${lib.url}`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log("");
|
|
123
|
+
console.log(`${CYAN}◇${RESET} ${BOLD}Installing from ${lib.name}${RESET}`);
|
|
124
|
+
|
|
125
|
+
if (installInfo.type === "manual") {
|
|
126
|
+
console.log(`${DIM}│${RESET} ${installInfo.instructions}`);
|
|
127
|
+
console.log(`${DIM}│${RESET} ${DIM}URL: ${lib.url}${RESET}`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const installCmd = installInfo.fullInstall;
|
|
132
|
+
if (installCmd) {
|
|
133
|
+
console.log(`${DIM}│${RESET} ${GREEN}$ ${installCmd}${RESET}`);
|
|
134
|
+
console.log(`${DIM}│${RESET}`);
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const beforeRepair = repairSkillSymlinks({ cwd: process.cwd(), home: os.homedir() });
|
|
138
|
+
if (beforeRepair.repaired.length || beforeRepair.removedBroken.length) {
|
|
139
|
+
console.log(`${DIM}│${RESET} Repaired ${beforeRepair.repaired.length} skill links before install.`);
|
|
60
140
|
}
|
|
61
|
-
|
|
62
|
-
|
|
141
|
+
await runPrefixed(installCmd);
|
|
142
|
+
const afterRepair = repairSkillSymlinks({ cwd: process.cwd(), home: os.homedir() });
|
|
143
|
+
if (afterRepair.repaired.length || afterRepair.removedBroken.length) {
|
|
144
|
+
console.log(`${DIM}│${RESET} Repaired ${afterRepair.repaired.length} skill links after install.`);
|
|
145
|
+
}
|
|
146
|
+
successCount++;
|
|
147
|
+
|
|
148
|
+
if (installInfo.verify) {
|
|
149
|
+
try { await runPrefixed(installInfo.verify); } catch { /* best-effort */ }
|
|
150
|
+
}
|
|
151
|
+
console.log(`${DIM}│${RESET}`);
|
|
152
|
+
console.log(`${GREEN}✔${RESET} ${lib.name} installed successfully.`);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
console.error(`${YELLOW}⚠${RESET} Install failed for ${lib.name}.`);
|
|
155
|
+
console.error(`${DIM}│${RESET} ${DIM}${err.message}${RESET}`);
|
|
156
|
+
console.error(`${DIM}│${RESET} ContextOS will continue setup; rerun \`ctx skills\` after fixing the environment.`);
|
|
157
|
+
}
|
|
63
158
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Summary
|
|
162
|
+
console.log("");
|
|
163
|
+
if (successCount > 0) {
|
|
164
|
+
console.log(`${GREEN}✔${RESET} ${BOLD}${successCount} source${successCount > 1 ? "s" : ""} installed.${RESET}`);
|
|
165
|
+
console.log(`${DIM}│${RESET} Restart your agent to pick up new skills.`);
|
|
166
|
+
}
|
|
167
|
+
return successCount;
|
|
68
168
|
}
|
|
69
169
|
|
|
70
170
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -105,6 +205,8 @@ Usage:
|
|
|
105
205
|
ctx skills Browse community skill libraries
|
|
106
206
|
ctx skills --agents <names> Filter skills for specific agents
|
|
107
207
|
ctx skills --refresh Force refresh skill library cache
|
|
208
|
+
ctx --config Choose prompt context sections to show
|
|
209
|
+
ctx refresh Sync active Codex marketplace and rebuild indexes
|
|
108
210
|
ctx embeddings warm -- "task" Pre-warm embedding caches for a task
|
|
109
211
|
ctx ruler -- <ruler args> Passthrough to ruler CLI
|
|
110
212
|
ctx skillshare -- <skillshare args> Passthrough to skillshare CLI
|
|
@@ -257,8 +359,12 @@ async function install({ copy = false, agent = "codex" } = {}) {
|
|
|
257
359
|
}
|
|
258
360
|
const progress = createInstallProgress({ quiet: false });
|
|
259
361
|
progress.start(`installing ${agent || "codex"}`);
|
|
362
|
+
const graphStrategy = graphStrategyForInstall();
|
|
260
363
|
|
|
261
364
|
try {
|
|
365
|
+
progress.step(5, "syncing active marketplace");
|
|
366
|
+
syncActiveCodexMarketplace();
|
|
367
|
+
|
|
262
368
|
if (agent === "claude") {
|
|
263
369
|
progress.step(10, "copying package");
|
|
264
370
|
const installRoot = copyPackageRoot({ rootDir, targetRoot: agentInstallRoot("claude") });
|
|
@@ -274,7 +380,9 @@ async function install({ copy = false, agent = "codex" } = {}) {
|
|
|
274
380
|
progress.done("claude ✓");
|
|
275
381
|
console.log(`Hooks → ${hooksPath}`);
|
|
276
382
|
console.log(`MCP → ${mcpConfigPath}`);
|
|
383
|
+
console.log(`Graph → ${graphStrategy}`);
|
|
277
384
|
console.log(`Embeddings: ${warmResult.fileCount || 0} files, ${warmResult.skillCount || 0} skills`);
|
|
385
|
+
console.log(`Graph embeddings: ${formatCodeReviewGraphEmbedding(warmResult.graphEmbedding)}`);
|
|
278
386
|
console.log("Restart Claude Code to activate ContextOS.");
|
|
279
387
|
return;
|
|
280
388
|
}
|
|
@@ -294,7 +402,9 @@ async function install({ copy = false, agent = "codex" } = {}) {
|
|
|
294
402
|
progress.done("antigravity ✓");
|
|
295
403
|
console.log(`Hooks → ${hooksPath}`);
|
|
296
404
|
console.log(`MCP → ${mcpConfigPaths.join(", ")}`);
|
|
405
|
+
console.log(`Graph → ${graphStrategy}`);
|
|
297
406
|
console.log(`Embeddings: ${warmResult.fileCount || 0} files, ${warmResult.skillCount || 0} skills`);
|
|
407
|
+
console.log(`Graph embeddings: ${formatCodeReviewGraphEmbedding(warmResult.graphEmbedding)}`);
|
|
298
408
|
console.log("Restart Antigravity to activate ContextOS.");
|
|
299
409
|
return;
|
|
300
410
|
}
|
|
@@ -314,7 +424,9 @@ async function install({ copy = false, agent = "codex" } = {}) {
|
|
|
314
424
|
progress.done("copilot ✓");
|
|
315
425
|
console.log(`Instructions → ${hooksPath}`);
|
|
316
426
|
console.log(`MCP → ${mcpConfigPath}`);
|
|
427
|
+
console.log(`Graph → ${graphStrategy}`);
|
|
317
428
|
console.log(`Embeddings: ${warmResult.fileCount || 0} files, ${warmResult.skillCount || 0} skills`);
|
|
429
|
+
console.log(`Graph embeddings: ${formatCodeReviewGraphEmbedding(warmResult.graphEmbedding)}`);
|
|
318
430
|
console.log("Restart VS Code to activate ContextOS.");
|
|
319
431
|
return;
|
|
320
432
|
}
|
|
@@ -324,8 +436,7 @@ async function install({ copy = false, agent = "codex" } = {}) {
|
|
|
324
436
|
}
|
|
325
437
|
|
|
326
438
|
progress.step(10, "copying marketplace");
|
|
327
|
-
const marketplaceRoot =
|
|
328
|
-
copyPackageRoot({ rootDir, targetRoot: marketplaceRoot });
|
|
439
|
+
const marketplaceRoot = activeCodexMarketplaceRoot();
|
|
329
440
|
|
|
330
441
|
progress.step(25, "refreshing codex plugin");
|
|
331
442
|
tryRunCodex(["plugin", "remove", "ctx@contextos"]);
|
|
@@ -350,7 +461,9 @@ async function install({ copy = false, agent = "codex" } = {}) {
|
|
|
350
461
|
console.log(`Hooks → ${hooksPath}`);
|
|
351
462
|
console.log(`MCP → ctx-mcp installed`);
|
|
352
463
|
console.log(`Proxies → ${proxyResult.wrapped.length ? proxyResult.wrapped.map((item) => item.name).join(", ") : "none changed"}`);
|
|
464
|
+
console.log(`Graph → ${graphStrategy}`);
|
|
353
465
|
console.log(`Embeddings: ${warmResult.fileCount || 0} files, ${warmResult.skillCount || 0} skills`);
|
|
466
|
+
console.log(`Graph embeddings: ${formatCodeReviewGraphEmbedding(warmResult.graphEmbedding)}`);
|
|
354
467
|
console.log("Restart Codex to activate ContextOS.");
|
|
355
468
|
} catch (error) {
|
|
356
469
|
progress.fail("install failed");
|
|
@@ -358,6 +471,19 @@ async function install({ copy = false, agent = "codex" } = {}) {
|
|
|
358
471
|
}
|
|
359
472
|
}
|
|
360
473
|
|
|
474
|
+
function graphStrategyForInstall() {
|
|
475
|
+
let mcpServerNames = [];
|
|
476
|
+
try {
|
|
477
|
+
mcpServerNames = readCodexMcpServers().map((server) => server.name);
|
|
478
|
+
} catch {
|
|
479
|
+
// Graph detection is diagnostic and must not block installation.
|
|
480
|
+
}
|
|
481
|
+
return formatGraphStrategy(detectGraphStrategy({
|
|
482
|
+
cwd: process.cwd(),
|
|
483
|
+
mcpServerNames
|
|
484
|
+
}));
|
|
485
|
+
}
|
|
486
|
+
|
|
361
487
|
async function warmInstallEmbeddings() {
|
|
362
488
|
const dataDir = contextOSDataDir();
|
|
363
489
|
const modelReady = isModelCacheReady(dataDir);
|
|
@@ -392,7 +518,21 @@ async function warmInstallEmbeddings() {
|
|
|
392
518
|
allowRemote: !modelReady
|
|
393
519
|
})
|
|
394
520
|
: { count: 0 };
|
|
395
|
-
|
|
521
|
+
const graphEmbedding = embedCodeReviewGraph({ cwd: process.cwd() });
|
|
522
|
+
return { ...result, modelAlreadyCached: modelReady, fileCount: fileResult.count, skillCount: skillResult.count, workflowCount: workflowResult.count, graphEmbedding };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function activeCodexMarketplaceRoot() {
|
|
526
|
+
return path.join(codexHome(), "marketplaces", "contextos");
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function syncActiveCodexMarketplace() {
|
|
530
|
+
const result = syncPackageRoot({
|
|
531
|
+
rootDir,
|
|
532
|
+
targetRoot: activeCodexMarketplaceRoot()
|
|
533
|
+
});
|
|
534
|
+
writeInnerGitignore(result.targetRoot);
|
|
535
|
+
return result;
|
|
396
536
|
}
|
|
397
537
|
|
|
398
538
|
function tryRunCodex(args) {
|
|
@@ -502,7 +642,23 @@ async function debug(task) {
|
|
|
502
642
|
console.log(scheduled.additionalContext || "(empty)");
|
|
503
643
|
}
|
|
504
644
|
|
|
505
|
-
async function warmEmbeddings(task) {
|
|
645
|
+
async function warmEmbeddings(task, { syncMarketplace = true, quiet = false } = {}) {
|
|
646
|
+
const warmResult = await warmWorkspaceIndexes({ task });
|
|
647
|
+
const marketplaceSync = syncMarketplace ? syncActiveCodexMarketplace() : null;
|
|
648
|
+
if (quiet) return { ...warmResult, marketplaceSync };
|
|
649
|
+
console.log(`Warmed ${warmResult.ruleCount} embeddings`);
|
|
650
|
+
console.log(`Warmed ${warmResult.fileCount} file path embeddings`);
|
|
651
|
+
console.log(`Warmed ${warmResult.skillCount} skill embeddings`);
|
|
652
|
+
console.log(`Warmed ${warmResult.workflowCount} workflow embeddings`);
|
|
653
|
+
console.log(`Cache: ${warmResult.cachePath}`);
|
|
654
|
+
console.log(`Graph embeddings: ${formatCodeReviewGraphEmbedding(warmResult.graphEmbedding)}`);
|
|
655
|
+
if (marketplaceSync) {
|
|
656
|
+
console.log(`Marketplace: ${marketplaceSync.synced ? "synced" : "already active"} (${marketplaceSync.targetRoot})`);
|
|
657
|
+
}
|
|
658
|
+
return { ...warmResult, marketplaceSync };
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
async function warmWorkspaceIndexes({ task = "project context" } = {}) {
|
|
506
662
|
const cwd = process.cwd();
|
|
507
663
|
const merged = readAgentsChain({ cwd });
|
|
508
664
|
const rules = scoreRules(filterActionableRules(parseRules(merged.content)), task, []);
|
|
@@ -528,11 +684,26 @@ async function warmEmbeddings(task) {
|
|
|
528
684
|
dataDir: contextOSDataDir(),
|
|
529
685
|
allowRemote: true
|
|
530
686
|
});
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
687
|
+
const graphEmbedding = embedCodeReviewGraph({ cwd });
|
|
688
|
+
return {
|
|
689
|
+
ruleCount: result.count,
|
|
690
|
+
fileCount: fileResult.count,
|
|
691
|
+
skillCount: skillResult.count,
|
|
692
|
+
workflowCount: workflowResult.count,
|
|
693
|
+
cachePath: result.cachePath,
|
|
694
|
+
graphEmbedding
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
async function refresh() {
|
|
699
|
+
const marketplaceSync = syncActiveCodexMarketplace();
|
|
700
|
+
const invalidatedBridge = invalidateCtxMcpSocket(contextOSDataDir());
|
|
701
|
+
const warmResult = await warmInstallEmbeddings();
|
|
702
|
+
console.log(`Marketplace: ${marketplaceSync.synced ? "synced" : "already active"} (${marketplaceSync.targetRoot})`);
|
|
703
|
+
console.log(`Indexes: ${warmResult.fileCount || 0} file paths rebuilt`);
|
|
704
|
+
console.log(`Graph embeddings: ${formatCodeReviewGraphEmbedding(warmResult.graphEmbedding)}`);
|
|
705
|
+
if (invalidatedBridge) console.log("Bridge: stale private socket invalidated");
|
|
706
|
+
console.log("Restart Codex if ctx-mcp was already running.");
|
|
536
707
|
}
|
|
537
708
|
|
|
538
709
|
function printSetupBanner() {
|
|
@@ -559,6 +730,7 @@ async function askSetupYesNo(rl, question, defaultValue = true) {
|
|
|
559
730
|
async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
560
731
|
const options = parseSetupArgs(args);
|
|
561
732
|
const interactive = !options.yes && process.stdin.isTTY;
|
|
733
|
+
let outputConfig = loadOutputConfig({ dataRoot: contextOSDataDir() });
|
|
562
734
|
|
|
563
735
|
printSetupBanner();
|
|
564
736
|
console.log(`◇ Installation directory:\n│ ${cwd}`);
|
|
@@ -599,11 +771,22 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
599
771
|
rl.close();
|
|
600
772
|
}
|
|
601
773
|
}
|
|
774
|
+
|
|
775
|
+
console.log("");
|
|
776
|
+
console.log("◇ Configure prompt output:");
|
|
777
|
+
outputConfig = await configureOutputSections({
|
|
778
|
+
dataRoot: contextOSDataDir(),
|
|
779
|
+
select: multiSelect
|
|
780
|
+
});
|
|
602
781
|
}
|
|
603
782
|
|
|
604
783
|
console.log("");
|
|
605
784
|
console.log("◇ Ready to setup:");
|
|
606
|
-
for (const line of setupSummaryLines({
|
|
785
|
+
for (const line of setupSummaryLines({
|
|
786
|
+
cwd,
|
|
787
|
+
...options,
|
|
788
|
+
promptSections: enabledOutputSectionsLabel(outputConfig)
|
|
789
|
+
})) console.log(`│ ${line}`);
|
|
607
790
|
console.log("");
|
|
608
791
|
|
|
609
792
|
if (!options.agents.length) throw new Error("No agents selected. Use --agents codex,claude,antigravity,copilot.");
|
|
@@ -626,7 +809,8 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
626
809
|
const skillAgents = options.agents.map((agent) => agent === "agy" ? "antigravity" : agent).join(",");
|
|
627
810
|
const syncArgs = ["--skills", "--agents", skillAgents];
|
|
628
811
|
if (options.yes) syncArgs.push("--yes");
|
|
629
|
-
|
|
812
|
+
|
|
813
|
+
const doSyncSkills = async () => streamSetupOutput(() => syncSkills({
|
|
630
814
|
cwd,
|
|
631
815
|
args: syncArgs,
|
|
632
816
|
rebuildSkillEmbeddings: async ({ cwd: skillCwd, sourceDir }) => warmSkillEmbeddings({
|
|
@@ -636,6 +820,25 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
636
820
|
skills: scanSkills({ cwd: skillCwd, roots: [sourceDir] })
|
|
637
821
|
})
|
|
638
822
|
}));
|
|
823
|
+
|
|
824
|
+
await doSyncSkills();
|
|
825
|
+
|
|
826
|
+
// Fallback: if no skills were found, offer community library installer
|
|
827
|
+
const existing = detectExistingSkills({ cwd });
|
|
828
|
+
const totalExisting = existing.reduce((sum, e) => sum + e.count, 0);
|
|
829
|
+
if (totalExisting === 0) {
|
|
830
|
+
console.log("");
|
|
831
|
+
console.log(`${YELLOW}⚠${RESET} No skills found on this machine.`);
|
|
832
|
+
console.log(`${DIM}│${RESET} Install community skills to get started.`);
|
|
833
|
+
console.log("");
|
|
834
|
+
|
|
835
|
+
const installed = await runCommunitySkillInstaller(options.agents);
|
|
836
|
+
if (installed > 0) {
|
|
837
|
+
console.log("");
|
|
838
|
+
console.log("◇ Re-syncing skills after install...");
|
|
839
|
+
await doSyncSkills();
|
|
840
|
+
}
|
|
841
|
+
}
|
|
639
842
|
}
|
|
640
843
|
|
|
641
844
|
console.log("");
|
|
@@ -643,12 +846,6 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
643
846
|
console.log("│ Next: restart/open your agent from this project directory.");
|
|
644
847
|
console.log("│ Try: ctx debug -- \"Recheck authen flow\"");
|
|
645
848
|
console.log("");
|
|
646
|
-
|
|
647
|
-
// Recommend community skills based on selected agents
|
|
648
|
-
try {
|
|
649
|
-
const libraryResults = await fetchSkillsForAgents(options.agents, { dataDir: contextOSDataDir() });
|
|
650
|
-
printSkillRecommendations(libraryResults);
|
|
651
|
-
} catch { /* skill library is best-effort */ }
|
|
652
849
|
}
|
|
653
850
|
|
|
654
851
|
const args = process.argv.slice(2);
|
|
@@ -670,6 +867,11 @@ try {
|
|
|
670
867
|
console.log(usage());
|
|
671
868
|
} else if (command === "--version" || command === "-v") {
|
|
672
869
|
console.log(packageVersion());
|
|
870
|
+
} else if (command === "--config" || command === "config") {
|
|
871
|
+
await configureOutputSections({
|
|
872
|
+
dataRoot: contextOSDataDir(),
|
|
873
|
+
select: multiSelect
|
|
874
|
+
});
|
|
673
875
|
} else if (command === "install") {
|
|
674
876
|
const copy = args.includes("--copy");
|
|
675
877
|
const explicitAgents = installAgentsFromArgs(args);
|
|
@@ -711,6 +913,12 @@ try {
|
|
|
711
913
|
const task = marker >= 0 ? args.slice(marker + 1).join(" ") : args.slice(1).join(" ");
|
|
712
914
|
if (!task.trim()) throw new Error('Usage: ctx debug -- "task"');
|
|
713
915
|
await debug(task);
|
|
916
|
+
} else if (command === "refresh") {
|
|
917
|
+
await refresh();
|
|
918
|
+
} else if (command === "autowarm") {
|
|
919
|
+
const marker = args.indexOf("--");
|
|
920
|
+
const task = marker >= 0 ? args.slice(marker + 1).join(" ") : args.slice(1).join(" ");
|
|
921
|
+
await warmEmbeddings(task || "project context", { syncMarketplace: false, quiet: true });
|
|
714
922
|
} else if (command === "embeddings") {
|
|
715
923
|
if (args[1] === "warm") {
|
|
716
924
|
const marker = args.indexOf("--");
|
|
@@ -748,105 +956,21 @@ try {
|
|
|
748
956
|
const YELLOW = "\x1B[33m";
|
|
749
957
|
const BOLD = "\x1B[1m";
|
|
750
958
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
const totalSkills = libraryResults.reduce((sum, r) => sum + r.count, 0);
|
|
757
|
-
if (totalSkills === 0) {
|
|
758
|
-
console.log("No skills found. Check your network connection or try --refresh.");
|
|
759
|
-
process.exit(1);
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
// Compact header
|
|
763
|
-
console.log(`${CYAN}◇${RESET} ${BOLD}Community skill libraries available:${RESET}`);
|
|
764
|
-
console.log(`${DIM}│${RESET} Browse and install curated skills from the community.`);
|
|
765
|
-
console.log(`${DIM}│${RESET}`);
|
|
766
|
-
|
|
767
|
-
// Multi-select which sources to install from
|
|
768
|
-
const allLibs = getAllLibraries();
|
|
769
|
-
const availableLibs = allLibs.filter((lib) => {
|
|
770
|
-
const result = libraryResults.find((r) => r.library.id === lib.id);
|
|
771
|
-
return result && result.count > 0;
|
|
772
|
-
});
|
|
773
|
-
|
|
774
|
-
if (availableLibs.length === 0) {
|
|
775
|
-
console.log("No installable libraries available.");
|
|
776
|
-
process.exit(0);
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
const selectedSources = await multiSelect({
|
|
780
|
-
message: "Select skill sources to install:",
|
|
781
|
-
options: availableLibs.map((lib) => {
|
|
782
|
-
const result = libraryResults.find((r) => r.library.id === lib.id);
|
|
783
|
-
return {
|
|
784
|
-
label: `${lib.name} (${result?.count || 0} skills)`,
|
|
785
|
-
value: lib.id,
|
|
786
|
-
hint: lib.url,
|
|
787
|
-
selected: false
|
|
788
|
-
};
|
|
789
|
-
})
|
|
790
|
-
});
|
|
791
|
-
|
|
792
|
-
if (!selectedSources || selectedSources.length === 0) {
|
|
793
|
-
console.log(`\n${DIM}No sources selected.${RESET}`);
|
|
794
|
-
process.exit(0);
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
// Install each selected source using its provided commands
|
|
798
|
-
let successCount = 0;
|
|
799
|
-
for (const libId of selectedSources) {
|
|
800
|
-
const lib = allLibs.find((l) => l.id === libId);
|
|
801
|
-
if (!lib) continue;
|
|
802
|
-
|
|
803
|
-
const installInfo = getInstallCommands(libId);
|
|
804
|
-
if (!installInfo) {
|
|
805
|
-
console.log(`${YELLOW}⚠${RESET} No install info for ${lib.name}. Visit: ${lib.url}`);
|
|
806
|
-
continue;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
console.log("");
|
|
810
|
-
console.log(`${CYAN}◇${RESET} ${BOLD}Installing from ${lib.name}${RESET}`);
|
|
811
|
-
|
|
812
|
-
if (installInfo.type === "manual") {
|
|
813
|
-
console.log(`${DIM}│${RESET} ${installInfo.instructions}`);
|
|
814
|
-
console.log(`${DIM}│${RESET} ${DIM}URL: ${lib.url}${RESET}`);
|
|
815
|
-
continue;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
const installCmd = installInfo.fullInstall;
|
|
819
|
-
if (installCmd) {
|
|
820
|
-
console.log(`${DIM}│${RESET} ${GREEN}$ ${installCmd}${RESET}`);
|
|
821
|
-
console.log(`${DIM}│${RESET}`);
|
|
822
|
-
|
|
823
|
-
try {
|
|
824
|
-
await runPrefixed(installCmd);
|
|
825
|
-
successCount++;
|
|
826
|
-
|
|
827
|
-
// Run verify command if available
|
|
828
|
-
if (installInfo.verify) {
|
|
829
|
-
try {
|
|
830
|
-
await runPrefixed(installInfo.verify);
|
|
831
|
-
} catch { /* verify is best-effort */ }
|
|
832
|
-
}
|
|
833
|
-
console.log(`${DIM}│${RESET}`);
|
|
834
|
-
console.log(`${GREEN}✔${RESET} ${lib.name} installed successfully.`);
|
|
835
|
-
} catch (err) {
|
|
836
|
-
console.error(`${YELLOW}⚠${RESET} Install failed for ${lib.name}. Try manually:`);
|
|
837
|
-
console.error(` ${installCmd}`);
|
|
838
|
-
console.error(` ${DIM}${err.message}${RESET}`);
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
// Final summary
|
|
844
|
-
console.log("");
|
|
845
|
-
if (successCount > 0) {
|
|
846
|
-
console.log(`${GREEN}✔${RESET} ${BOLD}${successCount} source${successCount > 1 ? "s" : ""} installed.${RESET}`);
|
|
847
|
-
console.log(`${DIM}│${RESET} Restart your agent to pick up new skills.`);
|
|
959
|
+
const installed = await runCommunitySkillInstaller(agents);
|
|
960
|
+
if (installed === 0) {
|
|
961
|
+
console.log(`\n${DIM}No installations were completed.${RESET}`);
|
|
848
962
|
} else {
|
|
849
|
-
console.log(`${
|
|
963
|
+
console.log(`${CYAN}◇${RESET} ${BOLD}Syncing installed skills${RESET}`);
|
|
964
|
+
await streamSetupOutput(() => syncSkills({
|
|
965
|
+
cwd: process.cwd(),
|
|
966
|
+
args: ["--skills", "--agents", agents.map((agent) => agent === "agy" ? "antigravity" : agent).join(","), "--yes"],
|
|
967
|
+
rebuildSkillEmbeddings: async ({ cwd, sourceDir }) => warmSkillEmbeddings({
|
|
968
|
+
cwd,
|
|
969
|
+
dataDir: contextOSDataDir(),
|
|
970
|
+
allowRemote: !isModelCacheReady(contextOSDataDir()),
|
|
971
|
+
skills: scanSkills({ cwd, roots: [sourceDir] })
|
|
972
|
+
})
|
|
973
|
+
}));
|
|
850
974
|
}
|
|
851
975
|
console.log("");
|
|
852
976
|
} else if (command === "sync") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minhpnq1807/contextos",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.44",
|
|
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": {
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
54
54
|
"@xenova/transformers": "^2.17.2",
|
|
55
|
+
"smol-toml": "^1.6.1",
|
|
55
56
|
"sql.js": "^1.14.1",
|
|
56
57
|
"zod": "^4.4.3"
|
|
57
58
|
},
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readStdinJson, writeJson, failOpen, logDebug, pluginRuntimeFile, pluginDataRoot, resolveHookCwd } from "../lib/hook-io.js";
|
|
2
|
+
import { armHookDeadline, exitAfterStdout, 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
|
|
|
6
6
|
const started = Date.now();
|
|
7
|
+
const fallback = {
|
|
8
|
+
continue: true,
|
|
9
|
+
suppressOutput: true
|
|
10
|
+
};
|
|
11
|
+
let deadline;
|
|
7
12
|
|
|
8
13
|
try {
|
|
9
14
|
const payload = await readStdinJson();
|
|
15
|
+
deadline = armHookDeadline("UserPromptSubmit", fallback);
|
|
10
16
|
const cwd = resolveHookCwd(payload);
|
|
11
17
|
const normalized = { ...payload, cwd };
|
|
12
18
|
|
|
@@ -18,13 +24,10 @@ try {
|
|
|
18
24
|
mcpDataDir: pluginDataRoot(),
|
|
19
25
|
started
|
|
20
26
|
}));
|
|
27
|
+
deadline.clear();
|
|
28
|
+
exitAfterStdout(0);
|
|
21
29
|
} catch (error) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
hookSpecificOutput: {
|
|
26
|
-
hookEventName: "UserPromptSubmit",
|
|
27
|
-
additionalContext: ""
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
+
deadline?.clear();
|
|
31
|
+
failOpen("UserPromptSubmit", error, fallback);
|
|
32
|
+
exitAfterStdout(0);
|
|
30
33
|
}
|