@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.
- package/.codex/skills/contextos-community/SKILL.md +15 -0
- package/.codex/skills/contextos-community/skill.yaml +20 -0
- package/.codex/skills/contextos-release/SKILL.md +15 -0
- package/.codex/skills/contextos-release/skill.yaml +20 -0
- package/.codex/skills/contextos-routing/SKILL.md +15 -0
- package/.codex/skills/contextos-routing/skill.yaml +20 -0
- package/.codex/workflows/primary.md +13 -0
- package/.codex/workflows/release.md +12 -0
- package/CHANGELOG.md +16 -0
- package/README.md +115 -10
- package/bin/ctx.js +5 -1
- package/community-skills/README.md +42 -0
- package/community-skills/_template/SKILL.md +15 -0
- package/community-skills/_template/skill.yaml +20 -0
- package/community-skills/eas/SKILL.md +15 -0
- package/community-skills/eas/skill.yaml +23 -0
- package/community-skills/jwt-auth/SKILL.md +15 -0
- package/community-skills/jwt-auth/skill.yaml +22 -0
- package/community-skills/oauth-google/SKILL.md +15 -0
- package/community-skills/oauth-google/skill.yaml +22 -0
- package/community-skills/prisma/SKILL.md +15 -0
- package/community-skills/prisma/skill.yaml +22 -0
- package/community-skills/redis/SKILL.md +15 -0
- package/community-skills/redis/skill.yaml +22 -0
- package/community-skills/vercel/SKILL.md +15 -0
- package/community-skills/vercel/skill.yaml +22 -0
- package/docs/launch-demos.md +105 -0
- package/docs/roadmap.md +285 -0
- package/eval/skill-routing/cases.yaml +1 -1
- package/package.json +4 -1
- package/plugins/ctx/.codex-plugin/plugin.json +1 -1
- package/plugins/ctx/lib/certification.js +223 -0
- 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
|
|
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.
|
|
572
|
+
semanticScore * 0.30
|
|
571
573
|
+ promptMatch.score * 0.20
|
|
572
|
-
+ projectEvidenceScore * 0.
|
|
574
|
+
+ projectEvidenceScore * 0.20
|
|
573
575
|
+ fileConfigScore * 0.10
|
|
574
|
-
+
|
|
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
|
-
|
|
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.
|
|
649
|
+
if (hasPromptEvidence && hasProjectEvidence && confidence >= 0.45) {
|
|
643
650
|
confidence = Math.max(confidence, 0.68);
|
|
644
651
|
}
|
|
645
652
|
if (hasDependencyEvidence && hasFileEvidence) {
|