@minhpnq1807/contextos 0.5.52 → 0.6.0

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.
Files changed (33) hide show
  1. package/.codex/skills/contextos-community/SKILL.md +15 -0
  2. package/.codex/skills/contextos-community/skill.yaml +20 -0
  3. package/.codex/skills/contextos-release/SKILL.md +15 -0
  4. package/.codex/skills/contextos-release/skill.yaml +20 -0
  5. package/.codex/skills/contextos-routing/SKILL.md +15 -0
  6. package/.codex/skills/contextos-routing/skill.yaml +20 -0
  7. package/.codex/workflows/primary.md +13 -0
  8. package/.codex/workflows/release.md +12 -0
  9. package/CHANGELOG.md +16 -0
  10. package/README.md +115 -10
  11. package/bin/ctx.js +5 -1
  12. package/community-skills/README.md +42 -0
  13. package/community-skills/_template/SKILL.md +15 -0
  14. package/community-skills/_template/skill.yaml +20 -0
  15. package/community-skills/eas/SKILL.md +15 -0
  16. package/community-skills/eas/skill.yaml +23 -0
  17. package/community-skills/jwt-auth/SKILL.md +15 -0
  18. package/community-skills/jwt-auth/skill.yaml +22 -0
  19. package/community-skills/oauth-google/SKILL.md +15 -0
  20. package/community-skills/oauth-google/skill.yaml +22 -0
  21. package/community-skills/prisma/SKILL.md +15 -0
  22. package/community-skills/prisma/skill.yaml +22 -0
  23. package/community-skills/redis/SKILL.md +15 -0
  24. package/community-skills/redis/skill.yaml +22 -0
  25. package/community-skills/vercel/SKILL.md +15 -0
  26. package/community-skills/vercel/skill.yaml +22 -0
  27. package/docs/launch-demos.md +105 -0
  28. package/docs/roadmap.md +285 -0
  29. package/eval/skill-routing/cases.yaml +1 -1
  30. package/package.json +4 -1
  31. package/plugins/ctx/.codex-plugin/plugin.json +1 -1
  32. package/plugins/ctx/lib/certification.js +223 -0
  33. package/plugins/ctx/lib/skill-discoverer.js +13 -6
@@ -0,0 +1,223 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+
5
+ import { filterActionableRules, parseRules } from "./analyzer.js";
6
+ import { readAgentsChain } from "./reader.js";
7
+ import { scanSkills } from "./skill-discoverer.js";
8
+ import { scanWorkflows } from "./workflow-discoverer.js";
9
+
10
+ const PROJECT_SKILL_ROOTS = [
11
+ [".codex", "skills"],
12
+ [".agents", "skills"],
13
+ [".claude", "skills"],
14
+ [".gemini", "skills"],
15
+ [".gemini", "antigravity", "skills"],
16
+ [".gemini", "antigravity-cli", "skills"]
17
+ ];
18
+
19
+ const PROJECT_WORKFLOW_ROOTS = [
20
+ [".claude", "workflows"],
21
+ [".codex", "workflows"],
22
+ [".gemini", "workflows"],
23
+ [".gemini", "antigravity", "workflows"],
24
+ [".gemini", "antigravity-cli", "workflows"]
25
+ ];
26
+
27
+ export function inspectContextOSReady({ cwd = process.cwd(), home = os.homedir() } = {}) {
28
+ const root = findProjectRoot(cwd);
29
+ const rules = inspectRules({ cwd, root, home });
30
+ const skills = inspectSkills({ root });
31
+ const workflows = inspectWorkflows({ root });
32
+ const overall = Math.round((rules.score + skills.score + workflows.score) / 3);
33
+ const tier = readinessTier(overall, { rules, skills, workflows });
34
+
35
+ return {
36
+ root,
37
+ rules,
38
+ skills,
39
+ workflows,
40
+ overall,
41
+ tier,
42
+ badge: tier === "Not Ready" ? "ContextOS Ready: Not Ready" : `ContextOS Ready ${tier}`
43
+ };
44
+ }
45
+
46
+ export function formatContextOSReady(result) {
47
+ const lines = [
48
+ "Repository Score",
49
+ "",
50
+ `Rules: ${result.rules.score}`,
51
+ `Skills: ${result.skills.score}`,
52
+ `Workflows: ${result.workflows.score}`,
53
+ "",
54
+ "Overall:",
55
+ result.badge,
56
+ "",
57
+ "Evidence:",
58
+ `- Rules: ${result.rules.summary}`,
59
+ `- Skills: ${result.skills.summary}`,
60
+ `- Workflows: ${result.workflows.summary}`
61
+ ];
62
+
63
+ const next = [
64
+ ...result.rules.recommendations,
65
+ ...result.skills.recommendations,
66
+ ...result.workflows.recommendations
67
+ ];
68
+ if (next.length) {
69
+ lines.push("", "Next:");
70
+ for (const item of [...new Set(next)].slice(0, 5)) lines.push(`- ${item}`);
71
+ }
72
+
73
+ return lines.join("\n");
74
+ }
75
+
76
+ function inspectRules({ cwd, root, home }) {
77
+ const chain = readAgentsChain({ cwd, home });
78
+ const projectSources = chain.sources.filter((source) => isInsidePath(source, root));
79
+ const rules = parseRules(chain.content || "");
80
+ const actionable = filterActionableRules(rules);
81
+ const imperative = actionable.filter((rule) => /\b(always|never|must|required|use|prefer|avoid|do not|don't)\b/i.test(rule.content));
82
+ let score = 0;
83
+ const recommendations = [];
84
+
85
+ if (projectSources.length) score += 55;
86
+ else recommendations.push("Add a project AGENTS.md with repository-specific operating rules.");
87
+
88
+ if (actionable.length >= 3) score += 20;
89
+ else recommendations.push("Add at least three actionable AGENTS.md rules.");
90
+
91
+ if (imperative.length) score += 15;
92
+ else recommendations.push("Use explicit rule language such as always, never, must, use, prefer, or avoid.");
93
+
94
+ if (projectSources.length > 1 || fs.existsSync(path.join(root, ".ruler"))) score += 10;
95
+
96
+ return {
97
+ score: Math.min(100, score),
98
+ sources: projectSources,
99
+ ruleCount: rules.length,
100
+ actionableCount: actionable.length,
101
+ summary: projectSources.length
102
+ ? `${projectSources.length} AGENTS.md source(s), ${actionable.length} actionable rule(s)`
103
+ : "missing project AGENTS.md",
104
+ recommendations
105
+ };
106
+ }
107
+
108
+ function inspectSkills({ root }) {
109
+ const roots = PROJECT_SKILL_ROOTS.map((parts) => path.join(root, ...parts));
110
+ const skills = scanSkills({ cwd: root, roots, maxSkills: 500 });
111
+ const metadataFiles = findFiles(roots, (filePath) => /skill\.ya?ml$/i.test(path.basename(filePath)));
112
+ const richMetadata = metadataFiles.filter((filePath) => {
113
+ const content = safeRead(filePath);
114
+ return /^positive_triggers:/m.test(content)
115
+ && /^evidence:/m.test(content)
116
+ && /^negative_triggers:/m.test(content)
117
+ && /^workflow:/m.test(content);
118
+ });
119
+ let score = 0;
120
+ const recommendations = [];
121
+
122
+ if (skills.length) score += 50;
123
+ else recommendations.push("Add project skills under .codex/skills/ or .agents/skills/.");
124
+
125
+ if (metadataFiles.length) score += 20;
126
+ else recommendations.push("Add skill.yaml metadata beside important SKILL.md files.");
127
+
128
+ if (richMetadata.length) score += 20;
129
+ else recommendations.push("Include positive_triggers, negative_triggers, evidence, and workflow in skill.yaml.");
130
+
131
+ if (skills.length >= 3) score += 10;
132
+ else recommendations.push("Provide at least three project-relevant skills for common tasks.");
133
+
134
+ return {
135
+ score: Math.min(100, score),
136
+ count: skills.length,
137
+ metadataCount: metadataFiles.length,
138
+ richMetadataCount: richMetadata.length,
139
+ summary: skills.length
140
+ ? `${skills.length} skill(s), ${metadataFiles.length} metadata file(s)`
141
+ : "missing project skill packs",
142
+ recommendations
143
+ };
144
+ }
145
+
146
+ function inspectWorkflows({ root }) {
147
+ const roots = PROJECT_WORKFLOW_ROOTS.map((parts) => path.join(root, ...parts));
148
+ const workflows = scanWorkflows({ cwd: root, roots });
149
+ const withChain = workflows.filter((workflow) => workflow.chain?.length);
150
+ let score = 0;
151
+ const recommendations = [];
152
+
153
+ if (workflows.length) score += 60;
154
+ else recommendations.push("Add project workflows under .codex/workflows/ or .claude/workflows/.");
155
+
156
+ if (withChain.length) score += 25;
157
+ else recommendations.push("Include agent handoff names such as planner, tester, code-reviewer, or docs-manager in workflow files.");
158
+
159
+ if (workflows.length >= 2) score += 15;
160
+ else recommendations.push("Provide more than one workflow when the repo has distinct delivery paths.");
161
+
162
+ return {
163
+ score: Math.min(100, score),
164
+ count: workflows.length,
165
+ chainCount: withChain.length,
166
+ summary: workflows.length
167
+ ? `${workflows.length} workflow(s), ${withChain.length} with agent chain(s)`
168
+ : "missing project workflows",
169
+ recommendations
170
+ };
171
+ }
172
+
173
+ function readinessTier(overall, { rules, skills, workflows }) {
174
+ if (rules.score < 50 || skills.score < 50 || workflows.score < 50) return "Not Ready";
175
+ if (overall >= 85) return "Gold";
176
+ if (overall >= 70) return "Silver";
177
+ if (overall >= 50) return "Bronze";
178
+ return "Not Ready";
179
+ }
180
+
181
+ function findProjectRoot(cwd) {
182
+ let current = path.resolve(cwd);
183
+ while (true) {
184
+ if (fs.existsSync(path.join(current, ".git"))) return current;
185
+ const parent = path.dirname(current);
186
+ if (parent === current) return path.resolve(cwd);
187
+ current = parent;
188
+ }
189
+ }
190
+
191
+ function findFiles(roots, predicate) {
192
+ const files = [];
193
+ for (const root of roots) walk(root, files, predicate, 0);
194
+ return files;
195
+ }
196
+
197
+ function walk(directory, files, predicate, depth) {
198
+ if (depth > 4) return;
199
+ let entries = [];
200
+ try {
201
+ entries = fs.readdirSync(directory, { withFileTypes: true });
202
+ } catch {
203
+ return;
204
+ }
205
+ for (const entry of entries) {
206
+ const filePath = path.join(directory, entry.name);
207
+ if (entry.isDirectory()) walk(filePath, files, predicate, depth + 1);
208
+ else if (entry.isFile() && predicate(filePath)) files.push(filePath);
209
+ }
210
+ }
211
+
212
+ function isInsidePath(filePath, root) {
213
+ const relative = path.relative(path.resolve(root), path.resolve(filePath));
214
+ return relative && !relative.startsWith("..") && !path.isAbsolute(relative);
215
+ }
216
+
217
+ function safeRead(filePath) {
218
+ try {
219
+ return fs.readFileSync(filePath, "utf8");
220
+ } catch {
221
+ return "";
222
+ }
223
+ }
@@ -565,13 +565,17 @@ function hybridSkillScore(skill, { prompt, projectEvidence }) {
565
565
  const negativePenalty = Math.max(negativeDependencies.score, negativeFiles.score, negativePrompts.score);
566
566
  const projectEvidenceScore = dependencyEvidence.score;
567
567
  const fileConfigScore = fileEvidence.score;
568
- const graphScore = 0;
568
+ const importGraphScore = 0;
569
+ const externalGraphScore = 0;
570
+ const memoryScore = 0;
569
571
  const hybridScore = Math.max(0, Math.min(1,
570
- semanticScore * 0.35
572
+ semanticScore * 0.30
571
573
  + promptMatch.score * 0.20
572
- + projectEvidenceScore * 0.25
574
+ + projectEvidenceScore * 0.20
573
575
  + fileConfigScore * 0.10
574
- + graphScore * 0.05
576
+ + importGraphScore * 0.10
577
+ + externalGraphScore * 0.05
578
+ + memoryScore * 0.05
575
579
  - negativePenalty * 0.20
576
580
  ));
577
581
  const explicit = (skill.reasons || []).includes("explicit-skill");
@@ -609,7 +613,10 @@ function hybridSkillScore(skill, { prompt, projectEvidence }) {
609
613
  promptTriggerScore: promptMatch.score,
610
614
  projectEvidenceScore,
611
615
  fileConfigScore,
612
- graphScore,
616
+ importGraphScore,
617
+ externalGraphScore,
618
+ memoryScore,
619
+ graphScore: externalGraphScore,
613
620
  negativePenalty,
614
621
  rankScore,
615
622
  explicit,
@@ -639,7 +646,7 @@ function calibrateSkillConfidence(score, {
639
646
  if (isAmbiguousPrompt(prompt) && !(hasDependencyEvidence && hasFileEvidence) && !explicit) {
640
647
  confidence = Math.min(confidence, 0.64);
641
648
  }
642
- if (hasPromptEvidence && hasProjectEvidence && confidence >= 0.5) {
649
+ if (hasPromptEvidence && hasProjectEvidence && confidence >= 0.45) {
643
650
  confidence = Math.max(confidence, 0.68);
644
651
  }
645
652
  if (hasDependencyEvidence && hasFileEvidence) {