@minhpnq1807/contextos 0.6.5 → 0.6.7

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,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.7
4
+
5
+ - **MCP proxy test stability:** Fixed the MCP proxy telemetry smoke test so it waits for the telemetry file before reading it and uses a stable stdin/stdout echo child. This removes the CI race where `telemetry.jsonl` could be read before it existed.
6
+
7
+ ## 0.6.6
8
+
9
+ - **Global-first skill coverage:** `ctx doctor` now treats global and community skills as valid skill coverage instead of requiring project-local skill packs. Project skills are reported separately as optional project overrides, so repositories with synced global skills no longer show `Skills: 0` or `Not Ready` just because `.codex/skills` is absent.
10
+ - **Evidence-gated skill routing:** Updated Skill Router scoring so global skills are first-class candidates, community skills get a small source boost, and project skills get an override boost without penalizing global catalogs. Prompt+semantic matches without project evidence now remain medium-confidence candidates instead of disappearing.
11
+ - **Shared project context:** `ctx doctor --fix` and `ctx setup --generate-project-context` now scaffold shared project context under `.agents/skills/` and `.agents/workflows/` instead of Codex-only `.codex/` paths. Workflow discovery and certification now read `.agents/workflows` so the same generated context can be synced to Codex, Claude Code, Gemini, and Antigravity.
12
+
3
13
  ## 0.6.5
4
14
 
5
15
  - **Generated skill frontmatter:** Fixed `ctx doctor --fix` starter `SKILL.md` output so generated project skills include YAML frontmatter with `name` and `description`. This prevents invalid-skill warnings after generated skills are synced through skillshare.
package/README.md CHANGED
@@ -246,14 +246,15 @@ ctx doctor
246
246
  Repository Score
247
247
 
248
248
  Rules: 92
249
- Skills: 88
249
+ Skill Coverage: 88
250
+ Project Skill Overrides: 0
250
251
  Workflows: 84
251
252
 
252
253
  Overall:
253
- ContextOS Ready Gold
254
+ ContextOS Ready Silver
254
255
  ```
255
256
 
256
- The score checks project `AGENTS.md` rules, project skill packs under `.codex/skills/` or `.agents/skills/`, and project workflows under `.codex/workflows/` or `.claude/workflows/`. Use the badge only after `ctx doctor` reports Bronze, Silver, or Gold.
257
+ The score checks project `AGENTS.md` rules, global/community skill coverage, optional project skill overrides, and project workflows. Shared project context should live under `.agents/skills/` and `.agents/workflows/`; ContextOS can sync it to Codex, Claude Code, Gemini, and Antigravity agent roots. Use the badge only after `ctx doctor` reports Bronze, Silver, or Gold.
257
258
 
258
259
  ## Quick Commands
259
260
 
@@ -584,14 +585,14 @@ This warning comes from a transitive dependency in the local embedding/WASM stac
584
585
  | `ctx install --copy` | Copies only the plugin payload to `$CODEX_HOME/plugins/ctx`. | Legacy local development or manual plugin experiments. | Does not sync the active marketplace, rebuild indexes, register MCP, or install global hooks. Prefer `ctx refresh` for active local updates. |
585
586
  | `ctx setup` | Runs the first-run setup wizard. | You want the recommended onboarding flow after `npm install -g @minhpnq1807/contextos`. | Installs selected agents, optionally syncs Ruler rules/MCP and skillshare skills, asks which prompt sections to show, then prints next steps. |
586
587
  | `ctx setup --yes` | Runs setup with defaults non-interactively. | You want scriptable Codex setup. | Uses `codex`, enables injection, syncs rules, syncs skills, skips interactive community-skill installation when no TTY is available, and passes `--yes` to dependency setup prompts. Use `--agents codex,claude,agy` for multi-agent setup. |
587
- | `ctx setup --generate-project-context` | Generates starter project skills and workflow during setup. | Your repo has rules but `ctx doctor` reports missing skills/workflows. | Creates `.codex/skills/<detected-skill>/SKILL.md`, matching `skill.yaml`, and `.codex/workflows/primary.md` without overwriting existing files. |
588
+ | `ctx setup --generate-project-context` | Generates starter shared project skills and workflow during setup. | Your repo has rules but `ctx doctor` reports missing skills/workflows. | Creates `.agents/skills/<detected-skill>/SKILL.md`, matching `skill.yaml`, and `.agents/workflows/primary.md` without overwriting existing files. Run `ctx sync --skills` and `ctx sync --workflows` to push them to selected agents. |
588
589
  | `ctx setup --agents <list>` | Runs setup for selected agents. | You want only part of the default set. | Accepts comma-separated `codex`, `claude`, `agy`, or `antigravity`. |
589
590
  | `ctx setup --no-rules` | Skips Ruler sync during setup. | You only want hooks/MCP install and maybe skill sync. | Does not run `ctx sync --rules`. |
590
591
  | `ctx setup --no-skills` | Skips skillshare sync during setup. | You do not want shared skills configured. | Does not run `ctx sync --skills`. |
591
592
  | `ctx setup --quiet` | Runs setup in measurement-only mode. | You want reports/stats without visible injected prompt context. | Installs hooks with prompt context injection disabled. |
592
593
  | `ctx debug -- "task"` | Runs the scheduler locally for a fake prompt. | You want to see which AGENTS.md rules and files ContextOS would inject before using Codex. | Prints rule scores, scoring reasons, suggested files, and final `additionalContext`. |
593
594
  | `ctx doctor` | Scores repository ContextOS readiness. | You want to add or verify a `ContextOS Ready` badge. | Prints Rules, Skills, Workflows, Overall tier, evidence, and next recommendations. |
594
- | `ctx doctor --fix` | Generates starter ContextOS project context. | `ctx doctor` says skills/workflows are missing and you want explicit local scaffolding. | Detects package/config evidence, creates up to three starter project skills plus `.codex/workflows/primary.md`, then prints the updated readiness score. |
595
+ | `ctx doctor --fix` | Generates starter ContextOS project context. | `ctx doctor` says skills/workflows are missing and you want explicit local scaffolding. | Detects package/config evidence, creates up to three shared project skills plus `.agents/workflows/primary.md`, then prints the updated readiness score. |
595
596
  | `ctx report` | Shows the last Stop-hook compliance report for the current workspace. | An agent task has finished and you want the summary again. | Prints sectioned tables for summary, rule outcomes, suggested files, and runtime telemetry from `~/.ctx/contextos/workspaces/<workspace-id>/last-report.json`. |
596
597
  | `ctx evidence` | Shows detailed evidence behind the last report for the current workspace. | You want to inspect why a rule was marked `followed`, `ignored`, `unknown`, or `unmeasurable`. | Prints a compact evidence table plus per-rule detail tables. |
597
598
  | `ctx stats` | Shows aggregate runtime metrics for the current workspace. | You want to know whether ContextOS is active and useful over time. | Prints sectioned tables for prompt/report counts, injection rate, efficiency, rule outcomes, hook events, last prompt, and last report. |
@@ -611,7 +612,7 @@ This warning comes from a transitive dependency in the local embedding/WASM stac
611
612
  | `ctx sync --skills --no-collect` | Skips collecting existing agent skills into skillshare. | You already manage `~/.config/skillshare/skills` and only want to push it out. | Initializes/syncs skillshare without running `skillshare backup` or `skillshare collect --all`. |
612
613
  | `ctx sync --skills --no-embeddings` | Skips ContextOS skill embedding rebuild after skillshare sync. | You have a very large skill catalog and want sync to finish quickly. | Runs skillshare sync, then leaves embeddings to a later `ctx embeddings warm -- "task"` run. |
613
614
  | `ctx sync --skills --verbose` | Shows native skillshare token budget warnings during sync. | You are diagnosing skillshare path overlap or always-loaded context size. | Omits ContextOS' default `skillshare sync --quiet` behavior. |
614
- | `ctx sync --workflows` | Syncs and indexes agent workflow markdown files for prompt-time workflow suggestions. | You use `.claude/workflows/`, `.codex/workflows/`, or Antigravity workflow folders and want every agent to see the same deduped workflow set. | Scans project/global workflow folders, dedupes by workflow name, copies unique workflows to selected global agent roots, warms workflow embeddings, and makes `ctx debug`/prompt hooks show relevant workflow hints. |
615
+ | `ctx sync --workflows` | Syncs and indexes agent workflow markdown files for prompt-time workflow suggestions. | You use `.agents/workflows/`, `.claude/workflows/`, `.codex/workflows/`, or Gemini/Antigravity workflow folders and want every agent to see the same deduped workflow set. | Scans project/global workflow folders, dedupes by workflow name, copies unique workflows to selected global agent roots, warms workflow embeddings, and makes `ctx debug`/prompt hooks show relevant workflow hints. |
615
616
  | `ctx sync --workflows --agents <list>` | Syncs workflows only for selected agents. | You want a subset such as `codex,claude` or `codex,claude,agy`. | Accepts comma-separated `codex`, `claude`, `agy`, or `antigravity`; `agy` writes the Gemini/Antigravity workflow roots. |
616
617
  | `ctx sync --workflows --dry-run` | Previews workflow sync without writing files. | You want to inspect source workflows and target roots first. | Prints planned sync/index output and skips copying target files. |
617
618
  | `ctx skills` | Installs community skill libraries. | You want curated skills without running the full setup wizard. | Opens the community installer, uses a portable shell on Windows/Linux/macOS, repairs unsafe skill symlinks, and syncs installed skills to selected agents. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhpnq1807/contextos",
3
- "version": "0.6.5",
3
+ "version": "0.6.7",
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.6.5",
3
+ "version": "0.6.7",
4
4
  "description": "Inject task-relevant AGENTS.md rules into Codex through plugin hooks.",
5
5
  "author": {
6
6
  "name": "ContextOS"
@@ -4,7 +4,7 @@ import path from "node:path";
4
4
 
5
5
  import { filterActionableRules, parseRules } from "./analyzer.js";
6
6
  import { readAgentsChain } from "./reader.js";
7
- import { scanSkills } from "./skill-discoverer.js";
7
+ import { scanSkills, skillSearchRoots } from "./skill-discoverer.js";
8
8
  import { scanWorkflows } from "./workflow-discoverer.js";
9
9
 
10
10
  const PROJECT_SKILL_ROOTS = [
@@ -17,6 +17,7 @@ const PROJECT_SKILL_ROOTS = [
17
17
  ];
18
18
 
19
19
  const PROJECT_WORKFLOW_ROOTS = [
20
+ [".agents", "workflows"],
20
21
  [".claude", "workflows"],
21
22
  [".codex", "workflows"],
22
23
  [".gemini", "workflows"],
@@ -27,7 +28,7 @@ const PROJECT_WORKFLOW_ROOTS = [
27
28
  export function inspectContextOSReady({ cwd = process.cwd(), home = os.homedir() } = {}) {
28
29
  const root = findProjectRoot(cwd);
29
30
  const rules = inspectRules({ cwd, root, home });
30
- const skills = inspectSkills({ root });
31
+ const skills = inspectSkills({ root, home });
31
32
  const workflows = inspectWorkflows({ root });
32
33
  const overall = Math.round((rules.score + skills.score + workflows.score) / 3);
33
34
  const tier = readinessTier(overall, { rules, skills, workflows });
@@ -48,7 +49,8 @@ export function formatContextOSReady(result) {
48
49
  "Repository Score",
49
50
  "",
50
51
  `Rules: ${result.rules.score}`,
51
- `Skills: ${result.skills.score}`,
52
+ `Skill Coverage: ${result.skills.score}`,
53
+ `Project Skill Overrides: ${result.skills.projectOverrideScore}`,
52
54
  `Workflows: ${result.workflows.score}`,
53
55
  "",
54
56
  "Overall:",
@@ -56,7 +58,8 @@ export function formatContextOSReady(result) {
56
58
  "",
57
59
  "Evidence:",
58
60
  `- Rules: ${result.rules.summary}`,
59
- `- Skills: ${result.skills.summary}`,
61
+ `- Skill Coverage: ${result.skills.summary}`,
62
+ `- Project Skill Overrides: ${result.skills.projectSummary}`,
60
63
  `- Workflows: ${result.workflows.summary}`
61
64
  ];
62
65
 
@@ -108,10 +111,15 @@ function inspectRules({ cwd, root, home }) {
108
111
  };
109
112
  }
110
113
 
111
- function inspectSkills({ root }) {
112
- const roots = PROJECT_SKILL_ROOTS.map((parts) => path.join(root, ...parts));
113
- const skills = scanSkills({ cwd: root, roots, maxSkills: 500 });
114
- const metadataFiles = findFiles(roots, (filePath) => /skill\.ya?ml$/i.test(path.basename(filePath)));
114
+ function inspectSkills({ root, home }) {
115
+ const projectRoots = PROJECT_SKILL_ROOTS.map((parts) => path.join(root, ...parts));
116
+ const roots = skillSearchRoots({ cwd: root, home });
117
+ const skills = scanSkills({ cwd: root, roots, maxSkills: 5000 });
118
+ const projectSkills = skills.filter((skill) => skill.scope === "project");
119
+ const sharedSkills = skills.filter((skill) => skill.scope !== "project");
120
+ const communitySkills = skills.filter((skill) => isCommunitySkillPath(skill.path));
121
+ const globalSkills = sharedSkills.filter((skill) => !isCommunitySkillPath(skill.path));
122
+ const metadataFiles = findFiles(projectRoots, (filePath) => /skill\.ya?ml$/i.test(path.basename(filePath)));
115
123
  const richMetadata = metadataFiles.filter((filePath) => {
116
124
  const content = safeRead(filePath);
117
125
  return /^positive_triggers:/m.test(content)
@@ -123,29 +131,52 @@ function inspectSkills({ root }) {
123
131
  const recommendations = [];
124
132
 
125
133
  if (skills.length) score += 50;
126
- else recommendations.push("Add project skills under .codex/skills/ or .agents/skills/.");
134
+ else recommendations.push("Sync or install global skills with `ctx setup` or `ctx sync --skills`.");
127
135
 
128
- if (metadataFiles.length) score += 20;
129
- else recommendations.push("Add skill.yaml metadata beside important SKILL.md files.");
136
+ if (sharedSkills.length || projectSkills.length >= 3) score += 25;
130
137
 
131
- if (richMetadata.length) score += 20;
132
- else recommendations.push("Include positive_triggers, negative_triggers, evidence, and workflow in skill.yaml.");
138
+ if (projectSkills.length) score += 10;
133
139
 
134
- if (skills.length >= 3) score += 10;
135
- else recommendations.push("Provide at least three project-relevant skills for common tasks.");
140
+ if (metadataFiles.length) score += 5;
141
+ if (richMetadata.length) score += 10;
142
+ if (projectSkills.length && !metadataFiles.length) {
143
+ recommendations.push("Add skill.yaml metadata beside project-specific SKILL.md files.");
144
+ }
145
+ if (projectSkills.length && !richMetadata.length) {
146
+ recommendations.push("Include positive_triggers, negative_triggers, evidence, and workflow in project skill.yaml files.");
147
+ }
136
148
 
137
149
  return {
138
150
  score: Math.min(100, score),
139
151
  count: skills.length,
152
+ globalCount: globalSkills.length,
153
+ communityCount: communitySkills.length,
154
+ sharedCount: sharedSkills.length,
155
+ projectCount: projectSkills.length,
156
+ projectOverrideScore: projectSkillOverrideScore(projectSkills),
140
157
  metadataCount: metadataFiles.length,
141
158
  richMetadataCount: richMetadata.length,
142
159
  summary: skills.length
143
- ? `${skills.length} skill(s), ${metadataFiles.length} metadata file(s)`
144
- : "missing project skill packs",
160
+ ? `${skills.length} skill(s): ${globalSkills.length} global, ${communitySkills.length} community/shared, ${projectSkills.length} project override(s)`
161
+ : "missing global/community/project skill catalog",
162
+ projectSummary: projectSkills.length
163
+ ? `${projectSkills.length} project override skill(s), ${metadataFiles.length} metadata file(s)`
164
+ : "0 project override skill(s); global/community skills remain valid",
145
165
  recommendations
146
166
  };
147
167
  }
148
168
 
169
+ function projectSkillOverrideScore(projectSkills = []) {
170
+ if (projectSkills.length >= 3) return 100;
171
+ if (projectSkills.length === 2) return 70;
172
+ if (projectSkills.length === 1) return 40;
173
+ return 0;
174
+ }
175
+
176
+ function isCommunitySkillPath(filePath = "") {
177
+ return String(filePath || "").includes(`${path.sep}.config${path.sep}skillshare${path.sep}skills${path.sep}`);
178
+ }
179
+
149
180
  function inspectWorkflows({ root }) {
150
181
  const roots = PROJECT_WORKFLOW_ROOTS.map((parts) => path.join(root, ...parts));
151
182
  const workflows = scanWorkflows({ cwd: root, roots });
@@ -175,6 +206,7 @@ function inspectWorkflows({ root }) {
175
206
 
176
207
  function readinessTier(overall, { rules, skills, workflows }) {
177
208
  if (rules.score < 50 || skills.score < 50 || workflows.score < 50) return "Not Ready";
209
+ if (!skills.projectCount && overall >= 85) return "Silver";
178
210
  if (overall >= 85) return "Gold";
179
211
  if (overall >= 70) return "Silver";
180
212
  if (overall >= 50) return "Bronze";
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { clearSkillScanCache } from "./skill-discoverer.js";
5
5
 
6
6
  const STARTER_SKILL_LIMIT = 3;
7
+ const SHARED_PROJECT_CONTEXT_DIR = ".agents";
7
8
 
8
9
  export function generateProjectContext({ cwd = process.cwd(), force = false } = {}) {
9
10
  const root = findProjectRoot(cwd);
@@ -13,7 +14,7 @@ export function generateProjectContext({ cwd = process.cwd(), force = false } =
13
14
  const skipped = [];
14
15
 
15
16
  for (const skill of skills) {
16
- const dir = path.join(root, ".codex", "skills", skill.id);
17
+ const dir = path.join(root, SHARED_PROJECT_CONTEXT_DIR, "skills", skill.id);
17
18
  const skillPath = path.join(dir, "SKILL.md");
18
19
  const yamlPath = path.join(dir, "skill.yaml");
19
20
  fs.mkdirSync(dir, { recursive: true });
@@ -21,7 +22,7 @@ export function generateProjectContext({ cwd = process.cwd(), force = false } =
21
22
  writeFile({ filePath: yamlPath, content: renderSkillYaml(skill), force, created, skipped });
22
23
  }
23
24
 
24
- const workflowPath = path.join(root, ".codex", "workflows", "primary.md");
25
+ const workflowPath = path.join(root, SHARED_PROJECT_CONTEXT_DIR, "workflows", "primary.md");
25
26
  fs.mkdirSync(path.dirname(workflowPath), { recursive: true });
26
27
  writeFile({
27
28
  filePath: workflowPath,
@@ -61,7 +62,8 @@ export function formatProjectContextGeneration(result) {
61
62
  for (const filePath of result.skipped) lines.push(`- ${path.relative(result.root, filePath)}`);
62
63
  }
63
64
  lines.push("", "Next:");
64
- lines.push("- Review generated skills/workflow and edit project-specific wording.");
65
+ lines.push("- Review generated shared skills/workflow and edit project-specific wording.");
66
+ lines.push("- Run: ctx sync --skills && ctx sync --workflows");
65
67
  lines.push("- Run: ctx doctor");
66
68
  lines.push("- Run: ctx debug -- \"your task\"");
67
69
  return lines.join("\n");
@@ -551,7 +551,16 @@ function inferSkillMetadata(skill = {}) {
551
551
  }
552
552
  if (/\bnext|app router\b/.test(text)) {
553
553
  metadata.dependencies.push("next", "react");
554
- metadata.positivePrompts.push("frontend", "ui", "role", "dashboard", "app router");
554
+ metadata.positivePrompts.push("frontend", "ui", "role", "dashboard", "app router", "page", "webapp");
555
+ }
556
+ if (/\b(frontend|react|ui|component|layout|design)\b/.test(text)) {
557
+ metadata.dependencies.push("react");
558
+ metadata.positivePrompts.push("frontend", "ui", "component", "page", "layout", "button", "modal");
559
+ }
560
+ if (/\b(forum|topic|community|chat|message|realtime|websocket|socket|conversation)\b/.test(text)) {
561
+ metadata.positivePrompts.push("forum", "topic", "new topic", "trending", "chat", "chatting", "message", "realtime", "websocket");
562
+ metadata.files.push("package.json", "webapp/package.json", "services/*/package.json");
563
+ metadata.dependencies.push("next", "react", "socket.io", "ws", "@nestjs/websockets");
555
564
  }
556
565
  return metadata;
557
566
  }
@@ -570,16 +579,18 @@ function hybridSkillScore(skill, { prompt, projectEvidence }) {
570
579
  const projectEvidenceScore = dependencyEvidence.score;
571
580
  const fileConfigScore = fileEvidence.score;
572
581
  const importGraphScore = 0;
582
+ const sourceBoostScore = skillSourceBoostScore(skill);
573
583
  const externalGraphScore = 0;
574
584
  const memoryScore = 0;
575
585
  const hybridScore = Math.max(0, Math.min(1,
576
- semanticScore * 0.30
586
+ semanticScore * 0.25
577
587
  + promptMatch.score * 0.20
578
- + projectEvidenceScore * 0.20
588
+ + projectEvidenceScore * 0.25
579
589
  + fileConfigScore * 0.10
580
590
  + importGraphScore * 0.10
581
- + externalGraphScore * 0.05
582
- + memoryScore * 0.05
591
+ + sourceBoostScore * 0.05
592
+ + externalGraphScore * 0.03
593
+ + memoryScore * 0.02
583
594
  - negativePenalty * 0.20
584
595
  ));
585
596
  const explicit = (skill.reasons || []).includes("explicit-skill");
@@ -590,13 +601,15 @@ function hybridSkillScore(skill, { prompt, projectEvidence }) {
590
601
  dependencyEvidence,
591
602
  fileEvidence,
592
603
  negativePenalty,
593
- explicit
604
+ explicit,
605
+ semanticScore
594
606
  });
595
607
  const evidence = [...new Set([
596
608
  ...(skill.reasons || []),
597
609
  ...promptMatch.matches.map((item) => `prompt:${item}`),
598
610
  ...dependencyEvidence.matches.map((item) => `dependency:${item}`),
599
- ...fileEvidence.matches.map((item) => `file:${item}`)
611
+ ...fileEvidence.matches.map((item) => `file:${item}`),
612
+ ...(sourceBoostScore ? [`source:${skillSourceLabel(skill)}`] : [])
600
613
  ])];
601
614
  const negativeEvidence = [
602
615
  ...negativeDependencies.matches.map((item) => `dependency:${item}`),
@@ -618,6 +631,7 @@ function hybridSkillScore(skill, { prompt, projectEvidence }) {
618
631
  projectEvidenceScore,
619
632
  fileConfigScore,
620
633
  importGraphScore,
634
+ sourceBoostScore,
621
635
  externalGraphScore,
622
636
  memoryScore,
623
637
  graphScore: externalGraphScore,
@@ -636,7 +650,8 @@ function calibrateSkillConfidence(score, {
636
650
  dependencyEvidence,
637
651
  fileEvidence,
638
652
  negativePenalty = 0,
639
- explicit = false
653
+ explicit = false,
654
+ semanticScore = 0
640
655
  } = {}) {
641
656
  let confidence = Math.max(0, Math.min(1, Number(score || 0)));
642
657
  const hasDependencyEvidence = Boolean(dependencyEvidence?.matches?.length);
@@ -647,6 +662,9 @@ function calibrateSkillConfidence(score, {
647
662
  if (!hasProjectEvidence && !explicit) {
648
663
  confidence = Math.min(confidence, 0.62);
649
664
  }
665
+ if (!hasProjectEvidence && hasPromptEvidence && Number(semanticScore || 0) >= 0.75 && !explicit) {
666
+ confidence = Math.max(confidence, 0.56);
667
+ }
650
668
  if (isAmbiguousPrompt(prompt) && !(hasDependencyEvidence && hasFileEvidence) && !explicit) {
651
669
  confidence = Math.min(confidence, 0.64);
652
670
  }
@@ -662,6 +680,22 @@ function calibrateSkillConfidence(score, {
662
680
  return Math.max(0, Math.min(1, confidence));
663
681
  }
664
682
 
683
+ function skillSourceBoostScore(skill = {}) {
684
+ if (skill.scope === "project") return 1;
685
+ if (isCommunitySkill(skill)) return 0.4;
686
+ return 0;
687
+ }
688
+
689
+ function skillSourceLabel(skill = {}) {
690
+ if (skill.scope === "project") return "project";
691
+ if (isCommunitySkill(skill)) return "community";
692
+ return "global";
693
+ }
694
+
695
+ function isCommunitySkill(skill = {}) {
696
+ return String(skill.path || "").includes(`${path.sep}.config${path.sep}skillshare${path.sep}skills${path.sep}`);
697
+ }
698
+
665
699
  function confidenceBand(confidence) {
666
700
  const value = Number(confidence || 0);
667
701
  if (value >= 0.85) return "high";
@@ -30,11 +30,13 @@ const KNOWN_AGENT_NAMES = new Set([
30
30
 
31
31
  export function workflowSearchRoots({ cwd = process.cwd(), home = os.homedir() } = {}) {
32
32
  return [
33
+ path.join(cwd, ".agents", "workflows"),
33
34
  path.join(cwd, ".claude", "workflows"),
34
35
  path.join(cwd, ".codex", "workflows"),
35
36
  path.join(cwd, ".gemini", "workflows"),
36
37
  path.join(cwd, ".gemini", "antigravity", "workflows"),
37
38
  path.join(cwd, ".gemini", "antigravity-cli", "workflows"),
39
+ path.join(home, ".agents", "workflows"),
38
40
  path.join(home, ".claude", "workflows"),
39
41
  path.join(home, ".codex", "workflows"),
40
42
  path.join(home, ".gemini", "workflows"),
@@ -9,6 +9,9 @@ const { serverName, command, args } = parseArgs(process.argv.slice(2));
9
9
  const cwd = process.cwd();
10
10
  const telemetryPath = path.join(workspaceDataDir({ cwd }), "telemetry.jsonl");
11
11
  let inspectBuffer = "";
12
+ let childExit = null;
13
+ let pendingStdinWrites = 0;
14
+ let stdinEnded = false;
12
15
 
13
16
  const child = spawn(command, args, {
14
17
  cwd,
@@ -18,20 +21,32 @@ const child = spawn(command, args, {
18
21
 
19
22
  process.stdin.on("data", (chunk) => {
20
23
  inspectClientChunk(chunk);
21
- child.stdin.write(chunk);
24
+ pendingStdinWrites += 1;
25
+ if (!child.stdin.write(chunk, onChildStdinWrite)) process.stdin.pause();
22
26
  });
23
27
 
24
- process.stdin.on("end", () => {
25
- child.stdin.end();
28
+ child.stdin.on("drain", () => {
29
+ process.stdin.resume();
26
30
  });
27
31
 
28
- child.stdout.on("data", (chunk) => {
29
- process.stdout.write(chunk);
32
+ process.stdin.on("end", () => {
33
+ stdinEnded = true;
34
+ maybeEndChildStdin();
30
35
  });
31
36
 
32
- child.stderr.on("data", (chunk) => {
33
- process.stderr.write(chunk);
34
- });
37
+ function onChildStdinWrite() {
38
+ pendingStdinWrites -= 1;
39
+ maybeEndChildStdin();
40
+ }
41
+
42
+ function maybeEndChildStdin() {
43
+ if (stdinEnded && pendingStdinWrites === 0 && !child.stdin.destroyed) {
44
+ child.stdin.end();
45
+ }
46
+ }
47
+
48
+ child.stdout.pipe(process.stdout);
49
+ child.stderr.pipe(process.stderr);
35
50
 
36
51
  child.on("error", (error) => {
37
52
  process.stderr.write(`contextos mcp proxy failed to start ${serverName}: ${error?.message || String(error)}\n`);
@@ -39,10 +54,16 @@ child.on("error", (error) => {
39
54
  });
40
55
 
41
56
  child.on("exit", (code, signal) => {
42
- if (signal) process.kill(process.pid, signal);
43
- else process.exit(code ?? 0);
57
+ childExit = { code, signal };
58
+ maybeFinish();
44
59
  });
45
60
 
61
+ function maybeFinish() {
62
+ if (!childExit) return;
63
+ if (childExit.signal) process.kill(process.pid, childExit.signal);
64
+ else process.exitCode = childExit.code ?? 0;
65
+ }
66
+
46
67
  function inspectClientChunk(chunk) {
47
68
  inspectBuffer += chunk.toString("utf8");
48
69
  const lines = inspectBuffer.split(/\r?\n/);