@minhpnq1807/contextos 0.5.33 → 0.5.35

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,10 +1,18 @@
1
1
  # Changelog
2
2
 
3
- ## 0.5.33
3
+ ## 0.5.35
4
+
5
+ - **Add GitHub Copilot agent support:** New `copilot` agent for `ctx install --agent copilot` and `ctx setup`. Creates `.github/copilot-instructions.md` with ContextOS integration marker and configures `ctx-mcp` MCP server in `.vscode/mcp.json`. Copilot is now recognized by Ruler (`ctx sync --rules`) and Skillshare (`ctx sync --skills`) alongside existing codex, claude, and agy agents.
6
+ - **Agent selection defaults to none:** `ctx setup` and `ctx install` no longer pre-select all agents. Users must explicitly choose which agents to install via the interactive multiSelect prompt or `--agents` flag. This prevents accidental installation of unwanted agent hooks.
7
+ - **copilot-hooks.js:** Writes a managed `copilot-instructions.md` file under `.github/`, appending to existing content if present. Uses a marker comment (`<!-- managed by ContextOS -->`) to avoid duplicate sections.
8
+ - **copilot-mcp.js:** Configures `ctx-mcp` server in `.vscode/mcp.json` using the same pattern as existing claude/antigravity MCP modules.
9
+
10
+ ## 0.5.34
4
11
 
5
12
  - **Real-time streaming output during install/setup:** Replaced `captureSetupOutput` (buffered) with `streamSetupOutput` — now prints each line immediately with `│ ` prefix as it arrives, eliminating the perceived "hang" during long-running downloads and installs.
6
13
  - **Fix codex CLI output missing `│` prefix:** Changed `runCodex` from `stdio: "inherit"` to `stdio: ["ignore", "pipe", "pipe"]`. Output now flows through `console.log` → `streamSetupOutput` → `│ ` prefix, ensuring lines like "Added marketplace..." are consistently formatted.
7
14
  - **Async streaming for skillshare/ruler install:** Replaced blocking `execSync`/`runShell` calls in `installSkillshare` and `installRuler` with async `spawn` + line-by-line streaming. Download progress from PowerShell/curl/npm is now visible in real time instead of being buffered until completion.
15
+ - **Fix `skillshare init` hang on Windows:** `skillshare init` is interactive by default (prompts for copy source, git, skill install). With stdin routed to NUL (deadlock prevention), the Go binary hangs waiting for terminal input that never arrives. Fixed by passing `--no-copy --no-git --no-skill --all-targets` flags for fully non-interactive initialization.
8
16
 
9
17
  ## 0.5.32
10
18
 
package/bin/ctx.js CHANGED
@@ -24,6 +24,8 @@ 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
+ import { installCopilotHooks } from "../plugins/ctx/lib/copilot-hooks.js";
28
+ import { installCopilotMcp } from "../plugins/ctx/lib/copilot-mcp.js";
27
29
  import { syncRules } from "../plugins/ctx/lib/ruler-sync.js";
28
30
  import { writeInnerGitignore, ensureRootGitignore } from "../plugins/ctx/lib/gitignore.js";
29
31
  import { syncSkills } from "../plugins/ctx/lib/skillshare-sync.js";
@@ -42,11 +44,11 @@ function usage() {
42
44
 
43
45
  Usage:
44
46
  ctx install Interactive multi-select agent installer
45
- ctx install --agent <name> Install a specific agent (codex|claude|agy)
47
+ ctx install --agent <name> Install a specific agent (codex|claude|agy|copilot)
46
48
  ctx install --copy Legacy: copy plugin folder only (no hooks/mcp)
47
49
  ctx setup Interactive full setup wizard
48
50
  ctx setup --yes Auto-confirm all setup prompts
49
- ctx setup --agents codex,claude,agy Pre-select agents to install
51
+ ctx setup --agents codex,claude,agy,copilot Pre-select agents to install
50
52
  ctx setup --no-rules Skip AGENTS.md rule sync
51
53
  ctx setup --no-skills Skip skill sync
52
54
  ctx setup --quiet Quiet mode (minimal output)
@@ -56,17 +58,17 @@ Usage:
56
58
  ctx stats Show workspace statistics
57
59
  ctx benchmark -- "task" Benchmark workspace for a task
58
60
  ctx sync --rules Sync AGENTS.md rules to all agents
59
- ctx sync --rules --agents codex,claude,agy Sync rules to specific agents only
61
+ ctx sync --rules --agents codex,claude,agy,copilot Sync rules to specific agents only
60
62
  ctx sync --rules --dry-run Preview rule sync without writing
61
63
  ctx sync --rules --no-import-codex-mcp Skip importing Codex MCP servers
62
64
  ctx sync --skills Sync skills across agents
63
- ctx sync --skills --agents codex,claude,agy Sync skills to specific agents only
65
+ ctx sync --skills --agents codex,claude,agy,copilot Sync skills to specific agents only
64
66
  ctx sync --skills --dry-run Preview skill sync without writing
65
67
  ctx sync --skills --no-collect Skip collecting new skills
66
68
  ctx sync --skills --no-embeddings Skip embedding generation
67
69
  ctx sync --skills --verbose Verbose skill sync output
68
70
  ctx sync --workflows Sync workflows across agents
69
- ctx sync --workflows --agents codex,claude,agy Sync workflows to specific agents
71
+ ctx sync --workflows --agents codex,claude,agy,copilot Sync workflows to specific agents
70
72
  ctx sync --workflows --dry-run Preview workflow sync without writing
71
73
  ctx embeddings warm -- "task" Pre-warm embedding caches for a task
72
74
  ctx ruler -- <ruler args> Passthrough to ruler CLI
@@ -76,9 +78,10 @@ Usage:
76
78
  }
77
79
 
78
80
  const SUPPORTED_AGENTS = [
79
- { label: "Codex", value: "codex", selected: true },
80
- { label: "Claude Code", value: "claude", selected: true },
81
- { label: "Antigravity (agy)", value: "agy", selected: true }
81
+ { label: "Codex", value: "codex", selected: false },
82
+ { label: "Claude Code", value: "claude", selected: false },
83
+ { label: "Antigravity (agy)", value: "agy", selected: false },
84
+ { label: "GitHub Copilot", value: "copilot", selected: false }
82
85
  ];
83
86
 
84
87
  function normalizeInstallAgent(agent) {
@@ -90,8 +93,9 @@ function normalizeInstallAgent(agent) {
90
93
  " ctx install --agent codex",
91
94
  " ctx install --agent claude",
92
95
  " ctx install --agent agy",
96
+ " ctx install --agent copilot",
93
97
  "",
94
- "Do not run `ctx install --agent codex|claude|agy`: `|` is a shell pipe."
98
+ "Do not run `ctx install --agent codex|claude|agy|copilot`: `|` is a shell pipe."
95
99
  ].join("\n"));
96
100
  }
97
101
  if (normalized === "antigravity") return "agy";
@@ -271,8 +275,35 @@ async function install({ copy = false, agent = "codex" } = {}) {
271
275
  return;
272
276
  }
273
277
 
278
+ if (agent === "copilot") {
279
+ progress.step(10, "copying package");
280
+ const installRoot = copyPackageRoot({ rootDir, targetRoot: agentInstallRoot("copilot") });
281
+ progress.step(25, "installing hooks");
282
+ const hooksPath = installCopilotHooks({ cwd: process.cwd(), installRoot });
283
+ progress.step(40, "installing mcp");
284
+ const mcpConfigPath = installCopilotMcp({ cwd: process.cwd(), installRoot });
285
+ progress.step(50, "configuring gitignore");
286
+ writeInnerGitignore(installRoot);
287
+ ensureRootGitignore(process.cwd());
288
+ progress.step(55, "warming embeddings");
289
+ 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.");
302
+ return;
303
+ }
304
+
274
305
  if (agent !== "codex") {
275
- throw new Error(`Unknown agent '${agent}'. Expected codex, claude, or agy.`);
306
+ throw new Error(`Unknown agent '${agent}'. Expected codex, claude, agy, or copilot.`);
276
307
  }
277
308
 
278
309
  progress.step(10, "copying marketplace");
@@ -547,9 +578,10 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
547
578
  const selected = await multiSelect({
548
579
  message: "Select agents to install:",
549
580
  options: [
550
- { label: "Codex", value: "codex", selected: options.agents.includes("codex") },
551
- { label: "Claude", value: "claude", selected: options.agents.includes("claude") },
552
- { label: "Antigravity (agy)", value: "agy", selected: options.agents.includes("agy") }
581
+ { label: "Codex", value: "codex", selected: options.agents.includes("codex") },
582
+ { label: "Claude", value: "claude", selected: options.agents.includes("claude") },
583
+ { label: "Antigravity (agy)", value: "agy", selected: options.agents.includes("agy") },
584
+ { label: "GitHub Copilot", value: "copilot", selected: options.agents.includes("copilot") }
553
585
  ]
554
586
  });
555
587
  options.agents = selected;
@@ -576,7 +608,7 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
576
608
  for (const line of setupSummaryLines({ cwd, ...options })) console.log(`│ ${line}`);
577
609
  console.log("");
578
610
 
579
- if (!options.agents.length) throw new Error("No agents selected. Use --agents codex,claude,agy.");
611
+ if (!options.agents.length) throw new Error("No agents selected. Use --agents codex,claude,agy,copilot.");
580
612
 
581
613
  for (const agent of options.agents) {
582
614
  console.log(`◇ Setting up ${agent}...`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhpnq1807/contextos",
3
- "version": "0.5.33",
3
+ "version": "0.5.35",
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.33",
3
+ "version": "0.5.35",
4
4
  "description": "Inject task-relevant AGENTS.md rules into Codex through plugin hooks.",
5
5
  "author": {
6
6
  "name": "ContextOS"
@@ -0,0 +1,58 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ /**
5
+ * Copilot reads instructions from:
6
+ * 1. .github/copilot-instructions.md (repo-level)
7
+ * 2. AGENTS.md files (nearest in directory tree)
8
+ *
9
+ * Since ContextOS already syncs AGENTS.md through Ruler,
10
+ * this module writes a copilot-instructions.md that signals
11
+ * ContextOS integration and points Copilot at the ctx-mcp MCP server.
12
+ */
13
+
14
+ const MARKER = "<!-- managed by ContextOS -->";
15
+
16
+ function buildCopilotInstructions({ installRoot } = {}) {
17
+ return [
18
+ MARKER,
19
+ "# ContextOS Integration",
20
+ "",
21
+ "This project uses [ContextOS](https://github.com/khovan123/contextOS) for task-aware context injection.",
22
+ "",
23
+ "## MCP Server",
24
+ "",
25
+ "The `ctx-mcp` MCP server is configured in `.vscode/mcp.json`.",
26
+ "It provides semantic file search, skill discovery, and rule scoring for this workspace.",
27
+ "",
28
+ "## Rules",
29
+ "",
30
+ "Project rules are defined in `AGENTS.md` files managed by Ruler.",
31
+ "These rules are automatically injected into your prompt context.",
32
+ ""
33
+ ].join("\n");
34
+ }
35
+
36
+ export function copilotInstructionsPath(cwd = process.cwd()) {
37
+ return path.join(cwd, ".github", "copilot-instructions.md");
38
+ }
39
+
40
+ export function installCopilotHooks({ cwd = process.cwd(), installRoot } = {}) {
41
+ const instructionsPath = copilotInstructionsPath(cwd);
42
+ const dir = path.dirname(instructionsPath);
43
+ fs.mkdirSync(dir, { recursive: true });
44
+
45
+ // If the file exists and wasn't created by us, don't overwrite
46
+ if (fs.existsSync(instructionsPath)) {
47
+ const existing = fs.readFileSync(instructionsPath, "utf8");
48
+ if (!existing.includes(MARKER)) {
49
+ // Append our section
50
+ const content = existing.trimEnd() + "\n\n" + buildCopilotInstructions({ installRoot });
51
+ fs.writeFileSync(instructionsPath, content, "utf8");
52
+ return instructionsPath;
53
+ }
54
+ }
55
+
56
+ fs.writeFileSync(instructionsPath, buildCopilotInstructions({ installRoot }), "utf8");
57
+ return instructionsPath;
58
+ }
@@ -0,0 +1,43 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ /**
5
+ * Copilot MCP configuration lives at .vscode/mcp.json (workspace-level).
6
+ * This is the standard location for VS Code / GitHub Copilot agent mode.
7
+ */
8
+
9
+ function readJsonFile(filePath, fallback) {
10
+ if (!fs.existsSync(filePath)) return fallback;
11
+ const raw = fs.readFileSync(filePath, "utf8").trim();
12
+ if (!raw) return fallback;
13
+ try {
14
+ return JSON.parse(raw);
15
+ } catch {
16
+ console.warn(`[ctx] warning: corrupt JSON in ${filePath}, overwriting with defaults`);
17
+ return fallback;
18
+ }
19
+ }
20
+
21
+ export function copilotMcpConfigPath(cwd = process.cwd()) {
22
+ return path.join(cwd, ".vscode", "mcp.json");
23
+ }
24
+
25
+ export function buildCopilotMcpConfig(existingConfig, { installRoot } = {}) {
26
+ const config = existingConfig && typeof existingConfig === "object" ? structuredClone(existingConfig) : {};
27
+ if (!config.mcpServers || typeof config.mcpServers !== "object") config.mcpServers = {};
28
+ config.mcpServers["ctx-mcp"] = {
29
+ type: "stdio",
30
+ command: "node",
31
+ args: [path.join(installRoot, "plugins", "ctx", "mcp", "server.js")]
32
+ };
33
+ return config;
34
+ }
35
+
36
+ export function installCopilotMcp({ cwd = process.cwd(), configPath, installRoot } = {}) {
37
+ const mcpPath = configPath || copilotMcpConfigPath(cwd);
38
+ const existing = readJsonFile(mcpPath, {});
39
+ const next = buildCopilotMcpConfig(existing, { installRoot });
40
+ fs.mkdirSync(path.dirname(mcpPath), { recursive: true });
41
+ fs.writeFileSync(mcpPath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
42
+ return mcpPath;
43
+ }
@@ -7,7 +7,7 @@ import { execFileSync, spawn } from "node:child_process";
7
7
 
8
8
  import { defaultDataRoot } from "./workspace-data.js";
9
9
 
10
- const DEFAULT_AGENTS = ["codex", "claude", "antigravity"];
10
+ const DEFAULT_AGENTS = ["codex", "claude", "antigravity", "copilot"];
11
11
  const CTX_MCP_NAME = "ctx-mcp";
12
12
  const CONTEXTOS_PROXY_MARKER = "/contextos/plugins/ctx/mcp/proxy.js";
13
13
  const MCP_SERVER_RELATIVE = path.join("plugins", "ctx", "mcp", "server.js");
@@ -15,7 +15,8 @@ const AGENT_ALIASES = new Map([
15
15
  ["agy", "antigravity"],
16
16
  ["antigravity", "antigravity"],
17
17
  ["codex", "codex"],
18
- ["claude", "claude"]
18
+ ["claude", "claude"],
19
+ ["copilot", "copilot"]
19
20
  ]);
20
21
 
21
22
  function statusLine(label, value) {
@@ -508,6 +509,10 @@ export function verifySync({ cwd = process.cwd(), agents = DEFAULT_AGENTS } = {}
508
509
  path.join(cwd, ".gemini", "mcp.json"),
509
510
  ...antigravityMcpConfigPaths(),
510
511
  path.join(cwd, "AGENTS.md")
512
+ ],
513
+ copilot: [
514
+ path.join(cwd, ".vscode", "mcp.json"),
515
+ path.join(cwd, ".github", "copilot-instructions.md")
511
516
  ]
512
517
  };
513
518
 
@@ -526,6 +531,7 @@ function resolveStableMcpServerPath(rootDir) {
526
531
  path.join(codexRoot, MCP_SERVER_RELATIVE),
527
532
  path.join(dataRoot, "agents", "claude", "contextos", MCP_SERVER_RELATIVE),
528
533
  path.join(dataRoot, "agents", "agy", "contextos", MCP_SERVER_RELATIVE),
534
+ path.join(dataRoot, "agents", "copilot", "contextos", MCP_SERVER_RELATIVE),
529
535
  path.join(rootDir, MCP_SERVER_RELATIVE)
530
536
  ];
531
537
  for (const candidate of candidates) {
@@ -542,7 +548,7 @@ export async function syncRules({
542
548
  logger = console.log
543
549
  } = {}) {
544
550
  const options = parseSyncRulesArgs(args);
545
- if (!options.rules) throw new Error("Usage: ctx sync --rules [--agents codex,claude,antigravity] [--dry-run] [--force]");
551
+ if (!options.rules) throw new Error("Usage: ctx sync --rules [--agents codex,claude,antigravity,copilot] [--dry-run] [--force]");
546
552
 
547
553
  logger("");
548
554
  const ruler = checkRulerInstalled({ run });
@@ -608,6 +614,23 @@ export async function syncRules({
608
614
  logger(statusLine("Syncing Antigravity MCP config...", antigravityMcp.servers.length ? `✓ ${antigravityMcp.servers.join(", ")}` : "none found"));
609
615
  }
610
616
 
617
+ let copilotMcp = { changed: false };
618
+ if (options.agents.includes("copilot")) {
619
+ // Copilot MCP is managed by copilot-mcp.js during install,
620
+ // but we verify it's still in place during sync.
621
+ const vscodeMcpPath = path.join(cwd, ".vscode", "mcp.json");
622
+ if (fs.existsSync(vscodeMcpPath)) {
623
+ try {
624
+ const content = JSON.parse(fs.readFileSync(vscodeMcpPath, "utf8"));
625
+ copilotMcp.changed = Boolean(content?.mcpServers?.[CTX_MCP_NAME]);
626
+ logger(statusLine("Verifying Copilot MCP config...", copilotMcp.changed ? "✓ ctx-mcp found" : "not configured"));
627
+ } catch {
628
+ logger(statusLine("Verifying Copilot MCP config...", "⚠ parse error"));
629
+ }
630
+ } else {
631
+ logger(statusLine("Verifying Copilot MCP config...", "not installed (run ctx install --agent copilot)"));
632
+ }
633
+ }
611
634
  logger("[ctx] Verifying sync...");
612
635
  const checks = options.dryRun ? options.agents.map((agent) => ({ agent, ok: true, filePath: "(dry-run)" })) : verifySync({ cwd, agents: options.agents });
613
636
  for (const check of checks) {
@@ -1,4 +1,5 @@
1
- const DEFAULT_AGENTS = ["codex", "claude", "agy"];
1
+ // No agents pre-selected by default — user must choose explicitly
2
+ const DEFAULT_AGENTS = [];
2
3
 
3
4
  export function parseSetupArgs(args = []) {
4
5
  const agentsFlag = args.indexOf("--agents");
@@ -5,14 +5,15 @@ import readline from "node:readline/promises";
5
5
  import { stdin as input, stdout as output } from "node:process";
6
6
  import { execFileSync, execSync, spawn } from "node:child_process";
7
7
 
8
- const DEFAULT_AGENTS = ["codex", "claude", "antigravity"];
8
+ const DEFAULT_AGENTS = ["codex", "claude", "antigravity", "copilot"];
9
9
  const INSTALL_SH_URL = "https://raw.githubusercontent.com/runkids/skillshare/main/install.sh";
10
10
  const INSTALL_PS_URL = "https://raw.githubusercontent.com/runkids/skillshare/main/install.ps1";
11
11
  const AGENT_ALIASES = new Map([
12
12
  ["agy", "antigravity"],
13
13
  ["antigravity", "antigravity"],
14
14
  ["codex", "codex"],
15
- ["claude", "claude"]
15
+ ["claude", "claude"],
16
+ ["copilot", "copilot"]
16
17
  ]);
17
18
 
18
19
  function statusLine(label, value) {
@@ -412,7 +413,10 @@ export async function syncSkills({
412
413
  logger("[ctx] No existing skills found.");
413
414
  }
414
415
 
415
- run("skillshare", ["init"], { cwd, stdio: "pipe", dryRun: options.dryRun });
416
+ // --no-copy --no-git --no-skill --all-targets: fully non-interactive init.
417
+ // skillshare init is interactive by default; with stdin routed to NUL
418
+ // (deadlock prevention) the Go binary hangs waiting for terminal input.
419
+ run("skillshare", ["init", "--no-copy", "--no-git", "--no-skill", "--all-targets"], { cwd, stdio: "pipe", dryRun: options.dryRun });
416
420
  logger(statusLine("Initializing skillshare...", options.dryRun ? "dry-run" : "✓ initialized"));
417
421
 
418
422
  if (existing.length && !options.noCollect) {