@minhpnq1807/contextos 0.5.15 → 0.5.16
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 +7 -0
- package/README.md +4 -0
- package/bin/ctx.js +2 -0
- package/package.json +1 -1
- package/plugins/ctx/lib/skill-discoverer.js +43 -15
- package/plugins/ctx/lib/skillshare-sync.js +10 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.16
|
|
4
|
+
|
|
5
|
+
- Prints a "Rebuilding skill embeddings... started" status before indexing large skillshare catalogs so `ctx sync --skills` no longer looks stuck after skillshare finishes.
|
|
6
|
+
- Adds `ctx sync --skills --no-embeddings` for fast skill sync when users want to warm embeddings later.
|
|
7
|
+
- Caches oversized skill catalog scans correctly and truncates skill descriptions before scoring, keeping prompt-time skill discovery fast with large skillshare catalogs.
|
|
8
|
+
- Runs native `skillshare sync` in quiet mode by default to avoid flooding ContextOS output with budget warnings; use `--verbose` to show them.
|
|
9
|
+
|
|
3
10
|
## 0.5.15
|
|
4
11
|
|
|
5
12
|
- Replaces the misleading `ctx install --agent codex|claude|agy` usage text with separate commands.
|
package/README.md
CHANGED
|
@@ -252,6 +252,8 @@ Useful variants:
|
|
|
252
252
|
```bash
|
|
253
253
|
ctx sync --skills --dry-run
|
|
254
254
|
ctx sync --skills --no-collect
|
|
255
|
+
ctx sync --skills --no-embeddings
|
|
256
|
+
ctx sync --skills --verbose
|
|
255
257
|
ctx sync --skills --agents codex,claude
|
|
256
258
|
ctx sync --skills --agents codex,claude,agy
|
|
257
259
|
```
|
|
@@ -432,6 +434,8 @@ This warning comes from a transitive dependency in the local embedding/WASM stac
|
|
|
432
434
|
| `ctx sync --skills --agents <list>` | Syncs skills only for selected agents. | You want to target a subset such as `codex,claude` or `codex,claude,agy`. | Runs `skillshare sync --agents <list>` with `agy` normalized to `antigravity`, then refreshes skill embeddings. |
|
|
433
435
|
| `ctx sync --skills --dry-run` | Previews skillshare sync. | You want to inspect behavior before changing skill directories. | Runs `skillshare sync --dry-run` and skips embedding rebuild. |
|
|
434
436
|
| `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`. |
|
|
437
|
+
| `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. |
|
|
438
|
+
| `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. |
|
|
435
439
|
| `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. |
|
|
436
440
|
| `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. |
|
|
437
441
|
| `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. |
|
package/bin/ctx.js
CHANGED
|
@@ -69,6 +69,8 @@ Usage:
|
|
|
69
69
|
ctx sync --workflows --dry-run
|
|
70
70
|
ctx sync --skills --dry-run
|
|
71
71
|
ctx sync --skills --no-collect
|
|
72
|
+
ctx sync --skills --no-embeddings
|
|
73
|
+
ctx sync --skills --verbose
|
|
72
74
|
ctx sync --skills --agents codex,claude,antigravity
|
|
73
75
|
ctx embeddings warm -- "task"
|
|
74
76
|
ctx ruler -- <ruler args>
|
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ const DEFAULT_MAX_SKILLS = 2000;
|
|
|
9
9
|
const DEFAULT_EMBEDDING_CANDIDATES = 120;
|
|
10
10
|
const DEFAULT_SEMANTIC_CATALOG_LIMIT = 300;
|
|
11
11
|
const SCAN_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
12
|
+
const MAX_DESCRIPTION_CHARS = 500;
|
|
12
13
|
|
|
13
14
|
const scanCache = new Map();
|
|
14
15
|
|
|
@@ -36,11 +37,15 @@ export function parseSkillFrontmatter(content = "", { fallbackName = "", skillPa
|
|
|
36
37
|
const fallbackDescription = firstParagraph(body);
|
|
37
38
|
return {
|
|
38
39
|
name: fields.name || fallbackName || path.basename(path.dirname(skillPath)),
|
|
39
|
-
description: fields.description || fallbackDescription,
|
|
40
|
+
description: truncateDescription(fields.description || fallbackDescription),
|
|
40
41
|
path: skillPath
|
|
41
42
|
};
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
function truncateDescription(value) {
|
|
46
|
+
return String(value || "").replace(/\s+/g, " ").trim().slice(0, MAX_DESCRIPTION_CHARS);
|
|
47
|
+
}
|
|
48
|
+
|
|
44
49
|
function parseYamlishFields(frontmatter) {
|
|
45
50
|
const fields = {};
|
|
46
51
|
const lines = String(frontmatter || "").split(/\r?\n/);
|
|
@@ -65,7 +70,7 @@ function firstParagraph(body) {
|
|
|
65
70
|
export function scanSkills({ cwd = process.cwd(), roots = skillSearchRoots({ cwd }), maxSkills = DEFAULT_MAX_SKILLS } = {}) {
|
|
66
71
|
const cacheKey = `${path.resolve(cwd)}\0${maxSkills}\0${roots.map((root) => path.resolve(root)).join("\0")}`;
|
|
67
72
|
const cached = scanCache.get(cacheKey);
|
|
68
|
-
if (cached &&
|
|
73
|
+
if (cached && monotonicNow() - cached.createdAt < SCAN_CACHE_TTL_MS) {
|
|
69
74
|
return cached.skills;
|
|
70
75
|
}
|
|
71
76
|
|
|
@@ -73,7 +78,7 @@ export function scanSkills({ cwd = process.cwd(), roots = skillSearchRoots({ cwd
|
|
|
73
78
|
const seen = new Set();
|
|
74
79
|
for (const root of roots) {
|
|
75
80
|
for (const skillPath of findSkillFiles(root)) {
|
|
76
|
-
if (skills.length >= maxSkills) return skills;
|
|
81
|
+
if (skills.length >= maxSkills) return cacheAndReturnSkills(cacheKey, skills);
|
|
77
82
|
const realPath = safeRealpath(skillPath) || skillPath;
|
|
78
83
|
if (seen.has(realPath)) continue;
|
|
79
84
|
seen.add(realPath);
|
|
@@ -88,15 +93,23 @@ export function scanSkills({ cwd = process.cwd(), roots = skillSearchRoots({ cwd
|
|
|
88
93
|
skillPath
|
|
89
94
|
});
|
|
90
95
|
if (!skill.name || !skill.description) continue;
|
|
91
|
-
skills.push({
|
|
96
|
+
skills.push(enrichSkill({
|
|
92
97
|
...skill,
|
|
93
98
|
root,
|
|
94
99
|
scope: isInsidePath(skillPath, cwd) ? "project" : "global",
|
|
95
100
|
relativePath: path.relative(cwd, skillPath)
|
|
96
|
-
});
|
|
101
|
+
}));
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
|
-
|
|
104
|
+
return cacheAndReturnSkills(cacheKey, skills);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function monotonicNow() {
|
|
108
|
+
return globalThis.performance?.now?.() || Date.now();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function cacheAndReturnSkills(cacheKey, skills) {
|
|
112
|
+
scanCache.set(cacheKey, { createdAt: monotonicNow(), skills });
|
|
100
113
|
return skills;
|
|
101
114
|
}
|
|
102
115
|
|
|
@@ -208,23 +221,23 @@ function scoreSkillsByKeyword({ prompt, skills }) {
|
|
|
208
221
|
const normalizedPrompt = normalize(prompt);
|
|
209
222
|
const promptTokens = new Set(normalizedPrompt.split(/\s+/).filter(Boolean));
|
|
210
223
|
return skills.map((skill, index) => {
|
|
211
|
-
const
|
|
212
|
-
const
|
|
224
|
+
const enriched = skill.searchTokens ? skill : enrichSkill(skill);
|
|
225
|
+
const name = String(enriched.name || "");
|
|
226
|
+
const description = truncateDescription(enriched.description || "");
|
|
213
227
|
const content = `${name} ${description}`;
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
const nameTokens = normalizedName.split(/\s+/).filter((token) => token.length > 2);
|
|
228
|
+
const matches = enriched.searchTokens.filter((token) => promptTokens.has(token) && token.length > 2);
|
|
229
|
+
const normalizedName = enriched.normalizedName;
|
|
230
|
+
const nameTokens = enriched.nameTokens;
|
|
218
231
|
const nameHit = normalizedPrompt.includes(normalizedName);
|
|
219
232
|
const nameTokenHit = nameTokens.length > 1 && nameTokens.every((token) => promptTokens.has(token));
|
|
220
|
-
const scopeBonus =
|
|
233
|
+
const scopeBonus = enriched.scope === "project" ? 0.08 : 0;
|
|
221
234
|
const score = Math.min(1, (matches.length ? 0.25 + matches.length * 0.08 : 0) + (nameHit ? 0.2 : 0) + (nameTokenHit ? 0.18 : 0) + scopeBonus);
|
|
222
235
|
return {
|
|
223
236
|
id: `skill-${index + 1}`,
|
|
224
237
|
name,
|
|
225
238
|
description,
|
|
226
|
-
path:
|
|
227
|
-
scope:
|
|
239
|
+
path: enriched.path,
|
|
240
|
+
scope: enriched.scope,
|
|
228
241
|
content,
|
|
229
242
|
score,
|
|
230
243
|
keywordScore: score,
|
|
@@ -237,6 +250,21 @@ function scoreSkillsByKeyword({ prompt, skills }) {
|
|
|
237
250
|
});
|
|
238
251
|
}
|
|
239
252
|
|
|
253
|
+
function enrichSkill(skill) {
|
|
254
|
+
const name = String(skill.name || "");
|
|
255
|
+
const description = truncateDescription(skill.description || "");
|
|
256
|
+
const normalizedName = normalize(name);
|
|
257
|
+
const searchTokens = [...new Set(normalize(`${name} ${description}`).split(/\s+/).filter(Boolean))];
|
|
258
|
+
const nameTokens = normalizedName.split(/\s+/).filter((token) => token.length > 2);
|
|
259
|
+
return {
|
|
260
|
+
...skill,
|
|
261
|
+
description,
|
|
262
|
+
normalizedName,
|
|
263
|
+
nameTokens,
|
|
264
|
+
searchTokens
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
240
268
|
function normalize(value) {
|
|
241
269
|
return String(value || "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
242
270
|
}
|
|
@@ -41,6 +41,8 @@ export function parseSyncSkillsArgs(args = []) {
|
|
|
41
41
|
agents,
|
|
42
42
|
dryRun: args.includes("--dry-run"),
|
|
43
43
|
noCollect: args.includes("--no-collect"),
|
|
44
|
+
noEmbeddings: args.includes("--no-embeddings"),
|
|
45
|
+
verbose: args.includes("--verbose"),
|
|
44
46
|
yes: args.includes("--yes") || args.includes("-y")
|
|
45
47
|
};
|
|
46
48
|
}
|
|
@@ -367,13 +369,18 @@ export async function syncSkills({
|
|
|
367
369
|
|
|
368
370
|
const syncArgs = ["sync"];
|
|
369
371
|
if (options.dryRun) syncArgs.push("--dry-run");
|
|
372
|
+
if (!options.verbose) syncArgs.push("--quiet");
|
|
370
373
|
if (options.agents.length) syncArgs.push("--agents", options.agents.join(","));
|
|
371
|
-
|
|
374
|
+
logger(statusLine("Running skillshare sync...", "started"));
|
|
375
|
+
run("skillshare", syncArgs, { cwd, stdio: options.verbose ? "inherit" : "pipe", dryRun: false });
|
|
372
376
|
const syncedCount = countSkillFiles(skillshareSourceDir({ home }));
|
|
373
377
|
logger(statusLine("Running skillshare sync...", options.dryRun ? "dry-run" : `✓ ${syncedCount} skills → ${options.agents.join(", ")}`));
|
|
374
378
|
|
|
375
|
-
let embeddings = { count: 0, cachePath: null, skipped: options.dryRun };
|
|
376
|
-
if (
|
|
379
|
+
let embeddings = { count: 0, cachePath: null, skipped: options.dryRun || options.noEmbeddings };
|
|
380
|
+
if (options.noEmbeddings) {
|
|
381
|
+
logger(statusLine("Rebuilding skill embeddings...", "skipped by --no-embeddings"));
|
|
382
|
+
} else if (!options.dryRun) {
|
|
383
|
+
logger(statusLine("Rebuilding skill embeddings...", `started (${syncedCount} skills)`));
|
|
377
384
|
embeddings = await rebuildSkillEmbeddings({ cwd, home, sourceDir: skillshareSourceDir({ home }) });
|
|
378
385
|
logger(statusLine("Rebuilding skill embeddings...", `✓ ${embeddings.count || 0} skills indexed`));
|
|
379
386
|
} else {
|