@minhpnq1807/contextos 0.5.49 → 0.5.50

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,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.50
4
+
5
+ - **Explicit skill activation:** Prompt skills named with `$skill-name` are now preserved and ranked before semantic suggestions, so user-requested skills such as `$threejs` or `$design-taste-frontend` appear in prompt context even when semantic ranking would not select them.
6
+ - **Agents skill root discovery:** Skill discovery now scans project and global `.agents/skills` roots in addition to Codex, Claude, Gemini, and skillshare roots.
7
+
3
8
  ## 0.5.49
4
9
 
5
10
  - **Cold-cache MCP smoke:** `npm run test:mcp` now verifies the MCP tool contract without requiring a pre-downloaded ContextOS embedding model. When the model exists it still runs the semantic/performance smoke; otherwise it asserts cold-cache fallback behavior so publish CI does not fail before model warmup.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhpnq1807/contextos",
3
- "version": "0.5.49",
3
+ "version": "0.5.50",
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.49",
3
+ "version": "0.5.50",
4
4
  "description": "Inject task-relevant AGENTS.md rules into Codex through plugin hooks.",
5
5
  "author": {
6
6
  "name": "ContextOS"
@@ -22,11 +22,13 @@ const scanCache = new Map();
22
22
  export function skillSearchRoots({ cwd = process.cwd(), home = os.homedir() } = {}) {
23
23
  return [
24
24
  path.join(cwd, ".codex", "skills"),
25
+ path.join(cwd, ".agents", "skills"),
25
26
  path.join(cwd, ".claude", "skills"),
26
27
  path.join(cwd, ".gemini", "skills"),
27
28
  path.join(cwd, ".gemini", "antigravity", "skills"),
28
29
  path.join(cwd, ".gemini", "antigravity-cli", "skills"),
29
30
  path.join(home, ".codex", "skills"),
31
+ path.join(home, ".agents", "skills"),
30
32
  path.join(home, ".claude", "skills"),
31
33
  path.join(home, ".config", "skillshare", "skills"),
32
34
  path.join(home, ".gemini", "skills"),
@@ -171,6 +173,7 @@ export async function suggestSkills({
171
173
  const catalog = dedupeSkills(skills);
172
174
  const query = fusedProjectQuery({ prompt, cwd, dataDir });
173
175
  const byId = new Map(catalog.map((skill) => [skillIndexId(skill), skill]));
176
+ const explicitSkills = explicitSkillSuggestions({ prompt, byId });
174
177
 
175
178
  if (dataDir) {
176
179
  const indexed = await indexedSearcher({
@@ -181,20 +184,23 @@ export async function suggestSkills({
181
184
  allowRemote: false
182
185
  });
183
186
  if (indexed.status === "enabled" && indexed.items.length) {
184
- return finalizeSkillScores(indexed.items
187
+ return finalizeSkillScores([
188
+ ...explicitSkills,
189
+ ...indexed.items
185
190
  .map((item) => {
186
191
  const skill = byId.get(item.id);
187
192
  if (!skill) return null;
188
193
  return skillScoreFromEmbedding(skill, item.embeddingScore, [`embedding:${Number(item.embeddingScore || 0).toFixed(2)}`]);
189
194
  })
190
- .filter(Boolean), limit);
195
+ .filter(Boolean)
196
+ ], limit);
191
197
  }
192
198
  }
193
199
 
194
- if (catalog.length > DEFAULT_EMBEDDING_CANDIDATES) return [];
200
+ if (catalog.length > DEFAULT_EMBEDDING_CANDIDATES) return finalizeSkillScores(explicitSkills, limit);
195
201
 
196
202
  const embeddingCandidates = catalog.map((skill, index) => skillRule({ skill, index }));
197
- if (!embeddingCandidates.length) return [];
203
+ if (!embeddingCandidates.length) return finalizeSkillScores(explicitSkills, limit);
198
204
 
199
205
  const embedding = await embeddingEnhancer(embeddingCandidates, query, {
200
206
  dataDir,
@@ -203,7 +209,30 @@ export async function suggestSkills({
203
209
  allowRemote: false
204
210
  });
205
211
 
206
- return finalizeSkillScores(embedding.rules, limit);
212
+ return finalizeSkillScores([...explicitSkills, ...embedding.rules], limit);
213
+ }
214
+
215
+ function explicitSkillSuggestions({ prompt = "", byId = new Map() } = {}) {
216
+ const names = extractExplicitSkillNames(prompt);
217
+ return names
218
+ .map((name, index) => ({ skill: byId.get(normalize(name)), index }))
219
+ .filter(({ skill }) => Boolean(skill))
220
+ .map(({ skill, index }) => skillScoreFromEmbedding(skill, 1 - index * 0.0001, ["explicit-skill"]));
221
+ }
222
+
223
+ function extractExplicitSkillNames(prompt = "") {
224
+ const names = [];
225
+ const seen = new Set();
226
+ const pattern = /(?:^|[\s([{,])\$([A-Za-z0-9][A-Za-z0-9_.:-]*)/g;
227
+ let match;
228
+ while ((match = pattern.exec(String(prompt || "")))) {
229
+ const name = match[1];
230
+ const key = normalize(name);
231
+ if (!key || seen.has(key)) continue;
232
+ seen.add(key);
233
+ names.push(name);
234
+ }
235
+ return names;
207
236
  }
208
237
 
209
238
  function finalizeSkillScores(skills, limit) {