contextos-agents 1.6.0 → 1.7.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/.agents/AGENTS.md +5 -0
- package/.agents/adapters/aider/export.js +4 -2
- package/.agents/adapters/copilot/export.js +4 -2
- package/.agents/adapters/cursor/export.js +48 -12
- package/.agents/adapters/zed/export.js +53 -14
- package/.agents/core/profiles/mvp.yaml +1 -0
- package/.agents/core/skills/adapters/skill.yaml +6 -0
- package/.agents/core/skills/architecture-diagrams/skill.yaml +4 -0
- package/.agents/core/skills/brutalist-design/skill.yaml +4 -0
- package/.agents/core/skills/context-manager/references/context-rules.md +59 -0
- package/.agents/core/skills/context-manager/skill.yaml +7 -0
- package/.agents/core/skills/context-os/skill.yaml +11 -0
- package/.agents/core/skills/database/skill.yaml +6 -0
- package/.agents/core/skills/ddd/skill.yaml +7 -0
- package/.agents/core/skills/decisions/skill.yaml +6 -0
- package/.agents/core/skills/docker/skill.yaml +6 -0
- package/.agents/core/skills/engineering-workflow/SKILL.md +1 -1
- package/.agents/core/skills/engineering-workflow/skill.yaml +6 -0
- package/.agents/core/skills/fastapi/skill.yaml +7 -0
- package/.agents/core/skills/gemini-precision/SKILL.md +4 -0
- package/.agents/core/skills/gemini-precision/skill.yaml +4 -0
- package/.agents/core/skills/generators/skill.yaml +15 -0
- package/.agents/core/skills/graphify/SKILL.md +2 -2
- package/.agents/core/skills/graphify/skill.yaml +6 -1
- package/.agents/core/skills/gstack-roles/SKILL.md +3 -1
- package/.agents/core/skills/gstack-roles/skill.yaml +6 -0
- package/.agents/core/skills/impeccable-design/skill.yaml +6 -0
- package/.agents/core/skills/interview-me/skill.yaml +4 -0
- package/.agents/core/skills/microservices/skill.yaml +7 -0
- package/.agents/core/skills/minimalist-design/skill.yaml +4 -0
- package/.agents/core/skills/nestjs/skill.yaml +7 -0
- package/.agents/core/skills/nextjs/skill.yaml +7 -0
- package/.agents/core/skills/node/skill.yaml +7 -0
- package/.agents/core/skills/performance/skill.yaml +7 -0
- package/.agents/core/skills/ponytail-mindset/skill.yaml +6 -0
- package/.agents/core/skills/react/skill.yaml +7 -0
- package/.agents/core/skills/react-best-practices/skill.yaml +4 -0
- package/.agents/core/skills/redesign-audit/skill.yaml +4 -0
- package/.agents/core/skills/security/skill.yaml +7 -0
- package/.agents/core/skills/soft-design/skill.yaml +4 -0
- package/.agents/core/skills/state-management/skill.yaml +6 -0
- package/.agents/core/skills/subagent-orchestrator/SKILL.md +17 -0
- package/.agents/core/skills/subagent-orchestrator/skill.yaml +4 -0
- package/.agents/core/skills/system-design/skill.yaml +7 -0
- package/.agents/core/skills/testing/skill.yaml +6 -0
- package/.agents/core/skills/typescript/skill.yaml +7 -0
- package/.agents/core/skills/ui-design/skill.yaml +7 -0
- package/.agents/core/skills/ui-ux-pro/skill.yaml +6 -0
- package/.agents/core/skills/ux-design/skill.yaml +7 -0
- package/.agents/core/skills/vercel-optimize/scripts/collect-signals.mjs +131 -0
- package/.agents/core/skills/vercel-optimize/scripts/gate-investigations.mjs +142 -0
- package/.agents/core/skills/vercel-optimize/scripts/merge-signals.mjs +143 -0
- package/.agents/core/skills/vercel-optimize/scripts/scan-codebase.mjs +174 -0
- package/.agents/core/skills/vercel-optimize/skill.yaml +8 -0
- package/.agents/core/skills/web-accessibility/skill.yaml +7 -0
- package/.agents/ctx.js +44 -0
- package/.agents/doctor.js +193 -4
- package/.agents/generated/claude/skills/engineering-workflow/SKILL.md +1 -1
- package/.agents/generated/claude/skills/gemini-precision/SKILL.md +4 -0
- package/.agents/generated/claude/skills/graphify/SKILL.md +1 -1
- package/.agents/generated/claude/skills/gstack-roles/SKILL.md +3 -1
- package/.agents/generated/claude/skills/subagent-orchestrator/SKILL.md +17 -0
- package/.agents/generated/gemini/skills/engineering-workflow/SKILL.md +1 -1
- package/.agents/generated/gemini/skills/gemini-precision/SKILL.md +4 -0
- package/.agents/generated/gemini/skills/graphify/SKILL.md +2 -2
- package/.agents/generated/gemini/skills/gstack-roles/SKILL.md +3 -1
- package/.agents/generated/gemini/skills/subagent-orchestrator/SKILL.md +17 -0
- package/.agents/plugins/contextos/hooks.json +25 -0
- package/.agents/plugins/contextos/plugin.json +19 -0
- package/.agents/plugins.js +180 -20
- package/.agents/profiles.js +26 -0
- package/.agents/resolver.js +150 -30
- package/.agents/stats.js +22 -9
- package/.agents/validate.js +152 -13
- package/README.md +77 -47
- package/bin/commands/uninstall.js +207 -0
- package/bin/commands/update.js +262 -0
- package/bin/commands.js +305 -0
- package/bin/index.js +275 -119
- package/bin/lib/detector.js +106 -0
- package/bin/lib/lockfile.js +251 -0
- package/bin/lib/safe-writer.js +259 -0
- package/package.json +6 -9
- package/registry.json +13 -5
- package/registry.schema.json +3 -1
- package/.agents/mcp/runtime.py +0 -470
- package/.agents/mcp/server.mjs +0 -189386
- package/benchmarks/gemini-issues.js +0 -533
package/.agents/resolver.js
CHANGED
|
@@ -20,6 +20,7 @@ const CORE_SKILLS_DIR = path.join(AGENTS_DIR, 'core', 'skills');
|
|
|
20
20
|
const SKILL_RULES = [
|
|
21
21
|
{
|
|
22
22
|
skill: 'nextjs',
|
|
23
|
+
category: 'stack',
|
|
23
24
|
strong: [/\bnext(?:\.js)?\b/i, /\bapp\s*router\b/i, /\bserver\s*actions?\b/i, /\brsc\b/i, /некст/i],
|
|
24
25
|
medium: [/\bpage\.tsx\b/i, /\blayout\.tsx\b/i],
|
|
25
26
|
weak: [],
|
|
@@ -27,6 +28,7 @@ const SKILL_RULES = [
|
|
|
27
28
|
},
|
|
28
29
|
{
|
|
29
30
|
skill: 'react',
|
|
31
|
+
category: 'stack',
|
|
30
32
|
strong: [/\breact\b/i, /\buseOptimistic\b/i, /компонент/i, /хук/i],
|
|
31
33
|
medium: [/\bcomponent\b/i, /\bhooks?\b/i, /\buseState\b/i, /\buseEffect\b/i, /\buseMemo\b/i, /\bprops\b/i, /модал\w*/i],
|
|
32
34
|
weak: [],
|
|
@@ -34,6 +36,7 @@ const SKILL_RULES = [
|
|
|
34
36
|
},
|
|
35
37
|
{
|
|
36
38
|
skill: 'typescript',
|
|
39
|
+
category: 'stack',
|
|
37
40
|
strong: [/\btypescript\b/i, /\btype-?safe\b/i, /\bgenerics?\b/i, /\binterface\b/i, /\btsconfig\b/i, /тайпскрипт/i, /типизац/i],
|
|
38
41
|
medium: [],
|
|
39
42
|
weak: [/\btypes?\b/i],
|
|
@@ -41,6 +44,7 @@ const SKILL_RULES = [
|
|
|
41
44
|
},
|
|
42
45
|
{
|
|
43
46
|
skill: 'ui-ux-pro',
|
|
47
|
+
category: 'ui',
|
|
44
48
|
strong: [/\bui\b/i, /\bux\b/i, /\btailwind\b/i, /\bstyling\b/i, /дизайн/i, /верстк/i, /макет/i, /интерфейс/i],
|
|
45
49
|
medium: [/\bcss\b/i, /\btheme\b/i, /\bmodal\b/i, /\bbutton\b/i, /модал\w*/i, /кнопк/i],
|
|
46
50
|
weak: [/\bdesign\b/i],
|
|
@@ -48,6 +52,7 @@ const SKILL_RULES = [
|
|
|
48
52
|
},
|
|
49
53
|
{
|
|
50
54
|
skill: 'web-accessibility',
|
|
55
|
+
category: 'ui',
|
|
51
56
|
strong: [/\baccessib\w*\b/i, /\ba11y\b/i, /\baria\b/i, /\bfocus\s*trap\b/i, /\bkeyboard\s*nav/i, /\bwcag\b/i, /\bscreen\s*reader\b/i, /доступност/i, /скринридер/i],
|
|
52
57
|
medium: [],
|
|
53
58
|
weak: [],
|
|
@@ -55,6 +60,7 @@ const SKILL_RULES = [
|
|
|
55
60
|
},
|
|
56
61
|
{
|
|
57
62
|
skill: 'impeccable-design',
|
|
63
|
+
category: 'ui',
|
|
58
64
|
strong: [/\bvisual\s*qa\b/i, /\bmicro-?animation\b/i, /\bglassmorphism\b/i, /\btypography\b/i, /анимац/i, /полировк/i],
|
|
59
65
|
medium: [/\bpolish\b/i],
|
|
60
66
|
weak: [],
|
|
@@ -62,6 +68,7 @@ const SKILL_RULES = [
|
|
|
62
68
|
},
|
|
63
69
|
{
|
|
64
70
|
skill: 'database',
|
|
71
|
+
category: 'intent',
|
|
65
72
|
strong: [/\bdatabase\b/i, /\bsql\b/i, /\bpostgres(?:ql)?\b/i, /\bprisma\b/i, /\bdrizzle\b/i, /\bmigration\b/i, /\borm\b/i, /баз.*данн/i, /миграц/i, /таблиц/i],
|
|
66
73
|
medium: [/\bschema\b/i],
|
|
67
74
|
weak: [/\bquery\b/i, /\bindex(?:ing)?\b/i],
|
|
@@ -69,6 +76,7 @@ const SKILL_RULES = [
|
|
|
69
76
|
},
|
|
70
77
|
{
|
|
71
78
|
skill: 'security',
|
|
79
|
+
category: 'intent',
|
|
72
80
|
strong: [/\bauth\b/i, /\bjwt\b/i, /\blogin\b/i, /\bcsrf\b/i, /\bxss\b/i, /\brate\s*limit\b/i, /авториз/i, /аутентифик/i, /парол/i, /безопасност/i],
|
|
73
81
|
medium: [/\bpermission\b/i, /\bsession\b/i, /\btoken\b/i, /токен/i],
|
|
74
82
|
weak: [],
|
|
@@ -76,6 +84,7 @@ const SKILL_RULES = [
|
|
|
76
84
|
},
|
|
77
85
|
{
|
|
78
86
|
skill: 'performance',
|
|
87
|
+
category: 'intent',
|
|
79
88
|
strong: [/\bperformance\b/i, /\blatency\b/i, /\blcp\b/i, /\bcls\b/i, /\binp\b/i, /\bcore\s*web\s*vitals\b/i, /\bwaterfall\b/i, /\bbundle\s*size\b/i, /производительн/i, /ускор/i],
|
|
80
89
|
medium: [/\boptimize\b/i, /\bslow\b/i, /оптимиз/i, /медленн/i],
|
|
81
90
|
weak: [],
|
|
@@ -83,6 +92,7 @@ const SKILL_RULES = [
|
|
|
83
92
|
},
|
|
84
93
|
{
|
|
85
94
|
skill: 'testing',
|
|
95
|
+
category: 'intent',
|
|
86
96
|
strong: [/\bvitest\b/i, /\bjest\b/i, /\bplaywright\b/i, /\btdd\b/i, /\bbdd\b/i, /\be2e\b/i, /тестирован/i, /покрыти/i, /юнит/i],
|
|
87
97
|
medium: [/\btest(?:s|ing)?\b/i, /\bmock\b/i, /тест/i],
|
|
88
98
|
weak: [],
|
|
@@ -90,6 +100,7 @@ const SKILL_RULES = [
|
|
|
90
100
|
},
|
|
91
101
|
{
|
|
92
102
|
skill: 'docker',
|
|
103
|
+
category: 'intent',
|
|
93
104
|
strong: [/\bdocker\b/i, /\bdockerfile\b/i, /\bcompose\b/i, /\bkubernetes\b/i, /\bk8s\b/i, /докер/i],
|
|
94
105
|
medium: [],
|
|
95
106
|
weak: [/\bcontainer\b/i, /контейнер/i],
|
|
@@ -97,6 +108,7 @@ const SKILL_RULES = [
|
|
|
97
108
|
},
|
|
98
109
|
{
|
|
99
110
|
skill: 'fastapi',
|
|
111
|
+
category: 'stack',
|
|
100
112
|
strong: [/\bfastapi\b/i, /\bpydantic\b/i, /\buvicorn\b/i, /\bpytest\b/i, /питон/i],
|
|
101
113
|
medium: [/\bpython\b/i],
|
|
102
114
|
weak: [],
|
|
@@ -104,6 +116,7 @@ const SKILL_RULES = [
|
|
|
104
116
|
},
|
|
105
117
|
{
|
|
106
118
|
skill: 'nestjs',
|
|
119
|
+
category: 'stack',
|
|
107
120
|
strong: [/\bnestjs\b/i, /\b@nestjs\b/i, /нест/i],
|
|
108
121
|
medium: [],
|
|
109
122
|
weak: [/\bmodule\b/i, /\bcontroller\b/i, /\binjectable\b/i],
|
|
@@ -111,6 +124,7 @@ const SKILL_RULES = [
|
|
|
111
124
|
},
|
|
112
125
|
{
|
|
113
126
|
skill: 'node',
|
|
127
|
+
category: 'stack',
|
|
114
128
|
strong: [/\bnode(?:\.js)?\b/i, /\bexpress\b/i, /\bfastify\b/i],
|
|
115
129
|
medium: [/\bbackend\b/i, /\bapi\s*route\b/i, /бэкенд/i, /эндпоинт/i],
|
|
116
130
|
weak: [],
|
|
@@ -118,6 +132,7 @@ const SKILL_RULES = [
|
|
|
118
132
|
},
|
|
119
133
|
{
|
|
120
134
|
skill: 'ddd',
|
|
135
|
+
category: 'intent',
|
|
121
136
|
strong: [/\bddd\b/i, /\bdomain-?driven\b/i, /\baggregate\b/i, /\bvalue\s*object\b/i, /\bbounded\s*context\b/i],
|
|
122
137
|
medium: [/\bentity\b/i, /домен/i, /агрегат/i],
|
|
123
138
|
weak: [],
|
|
@@ -125,6 +140,7 @@ const SKILL_RULES = [
|
|
|
125
140
|
},
|
|
126
141
|
{
|
|
127
142
|
skill: 'microservices',
|
|
143
|
+
category: 'intent',
|
|
128
144
|
strong: [/\bmicroservices?\b/i, /\bevent-?driven\b/i, /\brabbitmq\b/i, /\bkafka\b/i, /\bgrpc\b/i, /микросервис/i],
|
|
129
145
|
medium: [/\bpub\/?sub\b/i, /очеред/i],
|
|
130
146
|
weak: [],
|
|
@@ -132,6 +148,7 @@ const SKILL_RULES = [
|
|
|
132
148
|
},
|
|
133
149
|
{
|
|
134
150
|
skill: 'system-design',
|
|
151
|
+
category: 'intent',
|
|
135
152
|
strong: [/\bsystem\s*design\b/i, /\bhigh\s*concurrency\b/i, /\bcircuit\s*breaker\b/i, /масштабируем/i],
|
|
136
153
|
medium: [/\barchitecture\b/i, /\bscalab(?:le|ility)\b/i, /архитектур/i],
|
|
137
154
|
weak: [],
|
|
@@ -139,6 +156,7 @@ const SKILL_RULES = [
|
|
|
139
156
|
},
|
|
140
157
|
{
|
|
141
158
|
skill: 'graphify',
|
|
159
|
+
category: 'intent',
|
|
142
160
|
strong: [/\bgraphify\b/i, /\bknowledge\s*graph\b/i, /\bcodebase\s*graph\b/i, /\bproject\s*graph\b/i, /\bblast\s*radius\b/i, /\bmap\s*(?:the\s*)?codebase\b/i, /граф\s*проект/i, /граф\s*зависимост/i],
|
|
143
161
|
medium: [],
|
|
144
162
|
weak: [],
|
|
@@ -146,6 +164,7 @@ const SKILL_RULES = [
|
|
|
146
164
|
},
|
|
147
165
|
{
|
|
148
166
|
skill: 'architecture-diagrams',
|
|
167
|
+
category: 'intent',
|
|
149
168
|
strong: [/\bflowchart\b/i, /\bsequence\s*diagram\b/i],
|
|
150
169
|
medium: [/\bdiagrams?\b/i, /диаграмм/i, /схем/i, /нарисуй/i],
|
|
151
170
|
weak: [],
|
|
@@ -153,6 +172,7 @@ const SKILL_RULES = [
|
|
|
153
172
|
},
|
|
154
173
|
{
|
|
155
174
|
skill: 'subagent-orchestrator',
|
|
175
|
+
category: 'intent',
|
|
156
176
|
strong: [/\bsubagent\b/i, /\bparallel\s*tasks\b/i, /субагент/i, /распараллел/i],
|
|
157
177
|
medium: [/\bdelegate\b/i, /делегируй/i],
|
|
158
178
|
weak: [],
|
|
@@ -160,6 +180,7 @@ const SKILL_RULES = [
|
|
|
160
180
|
},
|
|
161
181
|
{
|
|
162
182
|
skill: 'interview-me',
|
|
183
|
+
category: 'intent',
|
|
163
184
|
strong: [/\binterview\b/i, /интервью/i, /расспроси/i],
|
|
164
185
|
medium: [/\bclarify\b/i, /\bquestions\b/i, /уточни\b/i],
|
|
165
186
|
weak: [],
|
|
@@ -335,72 +356,171 @@ function analyzeImportGraph(projectDir = process.cwd()) {
|
|
|
335
356
|
*/
|
|
336
357
|
function resolveSkills({ prompt = '', files = [], phase = 'Build', domain = '', projectDir = process.cwd() } = {}) {
|
|
337
358
|
const promptText = (prompt || '').toLowerCase();
|
|
338
|
-
const
|
|
359
|
+
const ruleMap = new Map();
|
|
360
|
+
const promptScores = new Map();
|
|
361
|
+
const fileScores = new Map();
|
|
339
362
|
|
|
340
363
|
for (const rule of SKILL_RULES) {
|
|
341
|
-
|
|
364
|
+
ruleMap.set(rule.skill, rule);
|
|
365
|
+
let pScore = 0;
|
|
366
|
+
let fScore = 0;
|
|
342
367
|
|
|
343
368
|
// Strong triggers (10 points each match)
|
|
344
369
|
for (const pat of rule.strong || []) {
|
|
345
|
-
if (pat.test(promptText))
|
|
370
|
+
if (pat.test(promptText)) pScore += 10;
|
|
346
371
|
}
|
|
347
372
|
|
|
348
373
|
// Medium triggers (5 points each match)
|
|
349
374
|
for (const pat of rule.medium || []) {
|
|
350
|
-
if (pat.test(promptText))
|
|
375
|
+
if (pat.test(promptText)) pScore += 5;
|
|
351
376
|
}
|
|
352
377
|
|
|
353
378
|
// Weak triggers (2 points each match)
|
|
354
379
|
for (const pat of rule.weak || []) {
|
|
355
|
-
if (pat.test(promptText))
|
|
380
|
+
if (pat.test(promptText)) pScore += 2;
|
|
356
381
|
}
|
|
357
382
|
|
|
358
383
|
// File matches (10 points per matching file)
|
|
359
384
|
for (const file of files) {
|
|
360
385
|
const normalized = file.replace(/\\/g, '/');
|
|
361
386
|
for (const pat of rule.fileGlobs || []) {
|
|
362
|
-
if (pat.test(normalized))
|
|
387
|
+
if (pat.test(normalized)) fScore += 10;
|
|
363
388
|
}
|
|
364
389
|
}
|
|
365
390
|
|
|
366
|
-
|
|
367
|
-
if (
|
|
368
|
-
scores.set(rule.skill, score);
|
|
369
|
-
}
|
|
391
|
+
if (pScore > 0) promptScores.set(rule.skill, pScore);
|
|
392
|
+
if (fScore > 0) fileScores.set(rule.skill, fScore);
|
|
370
393
|
}
|
|
371
394
|
|
|
372
395
|
// Phase 2: Merge AST & Project Dependency Graph Signals
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
396
|
+
const astSignals = projectDir ? analyzeImportGraph(projectDir) : new Map();
|
|
397
|
+
|
|
398
|
+
// Active Profile Resolution Enforcement
|
|
399
|
+
const profiles = require('./profiles.js');
|
|
400
|
+
const activeProfile = projectDir ? profiles.getActiveProfile(projectDir) : null;
|
|
401
|
+
const profileExcluded = new Set(activeProfile?.exclude_skills || []);
|
|
402
|
+
const profilePreferred = new Set(activeProfile?.prefer_skills || []);
|
|
403
|
+
const profileEnforce = activeProfile?.enforce || {};
|
|
404
|
+
const profileDefaults = activeProfile?.defaults || {};
|
|
405
|
+
|
|
406
|
+
const MAX_DOMAIN_SKILLS = 4;
|
|
407
|
+
const selectedSkills = [];
|
|
408
|
+
|
|
409
|
+
// 1. INTENT SLOTS & INTENT PRECEDENCE (Immunity against eviction)
|
|
410
|
+
// Direct user intent in prompt MUST NOT be evicted by ambient stack/file signals.
|
|
411
|
+
// Profile-excluded skills are strictly omitted from candidate intents.
|
|
412
|
+
const candidateIntents = [];
|
|
413
|
+
for (const rule of SKILL_RULES) {
|
|
414
|
+
const s = rule.skill;
|
|
415
|
+
if (profileExcluded.has(s)) continue;
|
|
416
|
+
|
|
417
|
+
const p = promptScores.get(s) || 0;
|
|
418
|
+
const f = fileScores.get(s) || 0;
|
|
419
|
+
if (rule.category === 'intent' && (p >= 5 || (p > 0 && f >= 10))) {
|
|
420
|
+
candidateIntents.push({ skill: s, score: p * 2 + f, priority: 2 });
|
|
421
|
+
} else if (p >= 10) {
|
|
422
|
+
candidateIntents.push({ skill: s, score: p, priority: 1 });
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
candidateIntents.sort((a, b) => b.priority - a.priority || b.score - a.score);
|
|
427
|
+
|
|
428
|
+
// Reserve up to 2 guaranteed intent slots for directly requested capabilities
|
|
429
|
+
for (const item of candidateIntents) {
|
|
430
|
+
if (selectedSkills.length < 2 && !selectedSkills.includes(item.skill)) {
|
|
431
|
+
selectedSkills.push(item.skill);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// 2. CONTEXT SUPPRESSION
|
|
436
|
+
// If task is purely infrastructure (e.g. docker) or pure backend/architecture,
|
|
437
|
+
// suppress ambient UI skills from AST to avoid polluting non-UI tasks.
|
|
438
|
+
const hasPureInfraIntent = selectedSkills.includes('docker');
|
|
439
|
+
const hasBackendOnlyIntent = selectedSkills.some(s => ['database', 'fastapi', 'nestjs', 'ddd'].includes(s)) &&
|
|
440
|
+
!files.some(f => /\.(tsx|jsx|css|scss|html)$/.test(f)) &&
|
|
441
|
+
!/\b(ui|react|css|tailwind|frontend|макет|дизайн|кнопк|стил|компонент)/i.test(promptText);
|
|
442
|
+
|
|
443
|
+
const suppressAmbientUI = hasPureInfraIntent || hasBackendOnlyIntent;
|
|
444
|
+
|
|
445
|
+
// 3. REMAINING SLOTS: Rank candidate skills with user-intent weighting and profile preferences
|
|
446
|
+
const candidateScores = new Map();
|
|
447
|
+
for (const rule of SKILL_RULES) {
|
|
448
|
+
const s = rule.skill;
|
|
449
|
+
if (selectedSkills.includes(s) || profileExcluded.has(s)) continue;
|
|
450
|
+
|
|
451
|
+
const p = promptScores.get(s) || 0;
|
|
452
|
+
const f = fileScores.get(s) || 0;
|
|
453
|
+
const a = astSignals.get(s) || 0;
|
|
454
|
+
|
|
455
|
+
// If ambient UI is suppressed, skip UI skills unless explicitly requested in prompt
|
|
456
|
+
if (suppressAmbientUI && (rule.category === 'ui' || s === 'react') && p < 5 && f === 0) {
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Require minimum unweighted threshold of 5 points to filter casual weak mentions,
|
|
461
|
+
// then apply multiplier for strong/medium prompt matches to prioritize user intent over ambient signals.
|
|
462
|
+
const rawSum = p + f + a;
|
|
463
|
+
if (rawSum >= 5 || profilePreferred.has(s)) {
|
|
464
|
+
let weightedScore = (p >= 5 ? p * 3 : p) + f + a;
|
|
465
|
+
if (profilePreferred.has(s)) {
|
|
466
|
+
weightedScore += 20; // Profile prefer boost
|
|
467
|
+
}
|
|
468
|
+
candidateScores.set(s, weightedScore);
|
|
377
469
|
}
|
|
378
470
|
}
|
|
379
471
|
|
|
380
|
-
|
|
381
|
-
const sortedDomainSkills = Array.from(scores.entries())
|
|
472
|
+
const remainingSorted = Array.from(candidateScores.entries())
|
|
382
473
|
.sort((a, b) => b[1] - a[1])
|
|
383
474
|
.map(([skill]) => skill);
|
|
384
475
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
476
|
+
for (const s of remainingSorted) {
|
|
477
|
+
if (selectedSkills.length >= MAX_DOMAIN_SKILLS) break;
|
|
478
|
+
if (!selectedSkills.includes(s)) {
|
|
479
|
+
selectedSkills.push(s);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// 4. Synergy rules (from AGENTS.md matrix)
|
|
484
|
+
const hasSynergyPrereq = selectedSkills.includes('database') ||
|
|
485
|
+
selectedSkills.includes('microservices') ||
|
|
486
|
+
selectedSkills.includes('ddd') ||
|
|
487
|
+
selectedSkills.includes('graphify');
|
|
488
|
+
|
|
489
|
+
if (hasSynergyPrereq && !selectedSkills.includes('system-design') && !profileExcluded.has('system-design')) {
|
|
490
|
+
if (selectedSkills.length < MAX_DOMAIN_SKILLS + 1) {
|
|
491
|
+
selectedSkills.push('system-design');
|
|
492
|
+
}
|
|
493
|
+
}
|
|
388
494
|
|
|
389
|
-
//
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
495
|
+
// 5. Enforce profile invariants (e.g. enterprise or startup compliance)
|
|
496
|
+
const normPhase = (phase || '').toLowerCase();
|
|
497
|
+
if (profileEnforce.testing === true && ['build', 'verify', 'test'].includes(normPhase)) {
|
|
498
|
+
if (!selectedSkills.includes('testing') && !profileExcluded.has('testing')) {
|
|
499
|
+
selectedSkills.push('testing');
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (profileEnforce.security_audit === true && (selectedSkills.includes('database') || selectedSkills.includes('node') || selectedSkills.includes('fastapi') || selectedSkills.includes('nestjs'))) {
|
|
504
|
+
if (!selectedSkills.includes('security') && !profileExcluded.has('security')) {
|
|
505
|
+
selectedSkills.push('security');
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (profileEnforce.adr === true && (normPhase === 'plan' || selectedSkills.includes('system-design') || selectedSkills.includes('microservices'))) {
|
|
510
|
+
if (!selectedSkills.includes('decisions') && !profileExcluded.has('decisions')) {
|
|
511
|
+
selectedSkills.push('decisions');
|
|
512
|
+
}
|
|
513
|
+
}
|
|
394
514
|
|
|
395
|
-
if (
|
|
396
|
-
if (
|
|
397
|
-
|
|
515
|
+
if (profileEnforce.accessibility_audit === true && (selectedSkills.includes('react') || selectedSkills.includes('ui-ux-pro'))) {
|
|
516
|
+
if (!selectedSkills.includes('web-accessibility') && !profileExcluded.has('web-accessibility')) {
|
|
517
|
+
selectedSkills.push('web-accessibility');
|
|
398
518
|
}
|
|
399
519
|
}
|
|
400
520
|
|
|
401
|
-
// Foundational skills (always active)
|
|
402
|
-
const allSkills = ['ponytail-mindset', 'engineering-workflow', ...
|
|
403
|
-
const finalSkills = Array.from(new Set(allSkills));
|
|
521
|
+
// Foundational skills (always active unless explicitly excluded)
|
|
522
|
+
const allSkills = ['ponytail-mindset', 'engineering-workflow', ...selectedSkills];
|
|
523
|
+
const finalSkills = Array.from(new Set(allSkills)).filter(s => !profileExcluded.has(s));
|
|
404
524
|
|
|
405
525
|
// Infer Domain if not specified
|
|
406
526
|
let inferredDomain = domain;
|
package/.agents/stats.js
CHANGED
|
@@ -64,8 +64,18 @@ function calculateContextStats(projectDir = process.cwd()) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
// Resolved context:
|
|
68
|
-
|
|
67
|
+
// Resolved context: dynamically resolved skills for this project's stack
|
|
68
|
+
let typicalResolvedSkills = ['engineering-workflow', 'ponytail-mindset'];
|
|
69
|
+
try {
|
|
70
|
+
const resolver = require('./resolver.js');
|
|
71
|
+
const resolved = resolver.resolveSkills({ prompt: 'Implement standard feature', projectDir });
|
|
72
|
+
if (resolved && Array.isArray(resolved.skills)) {
|
|
73
|
+
typicalResolvedSkills = resolved.skills;
|
|
74
|
+
}
|
|
75
|
+
} catch (_) {
|
|
76
|
+
typicalResolvedSkills = ['engineering-workflow', 'ponytail-mindset', 'react', 'ui-ux-pro'];
|
|
77
|
+
}
|
|
78
|
+
|
|
69
79
|
let resolvedChars = agentsMdChars;
|
|
70
80
|
let resolvedSkillCount = 0;
|
|
71
81
|
for (const s of typicalResolvedSkills) {
|
|
@@ -86,6 +96,7 @@ function calculateContextStats(projectDir = process.cwd()) {
|
|
|
86
96
|
totalSkillCount,
|
|
87
97
|
profiledSkillCount,
|
|
88
98
|
resolvedSkillCount,
|
|
99
|
+
resolvedSkillList: typicalResolvedSkills,
|
|
89
100
|
profileName,
|
|
90
101
|
full: { chars: fullChars, tokens: fullTokens },
|
|
91
102
|
profile: { chars: profileChars, tokens: profileTokens, savings: profileSavings },
|
|
@@ -96,28 +107,30 @@ function calculateContextStats(projectDir = process.cwd()) {
|
|
|
96
107
|
function runStats(projectDir = process.cwd()) {
|
|
97
108
|
const stats = calculateContextStats(projectDir);
|
|
98
109
|
|
|
99
|
-
console.log('\nContextOS — Context Savings Report\n');
|
|
110
|
+
console.log('\nContextOS — Context Savings Report & Payload Reduction\n');
|
|
100
111
|
console.log('┌──────────────────────────────────────┬─────────────┬──────────┐');
|
|
101
|
-
console.log('│ Mode
|
|
112
|
+
console.log('│ Context Mode │ Est. Tokens │ Savings │');
|
|
102
113
|
console.log('├──────────────────────────────────────┼─────────────┼──────────┤');
|
|
103
114
|
|
|
104
|
-
const fullLabel = `Full (${stats.totalSkillCount} skills)`;
|
|
115
|
+
const fullLabel = `Full (${stats.totalSkillCount} skills catalog)`;
|
|
105
116
|
const fullTok = stats.full.tokens.toLocaleString().padStart(9);
|
|
106
|
-
console.log(`│ ${fullLabel.padEnd(36)} │ ${fullTok} │
|
|
117
|
+
console.log(`│ ${fullLabel.slice(0, 36).padEnd(36)} │ ${fullTok} │ baseline │`);
|
|
107
118
|
|
|
108
119
|
const profLabel = `Profile: ${stats.profileName} (${stats.profiledSkillCount} skills)`;
|
|
109
120
|
const profTok = stats.profile.tokens.toLocaleString().padStart(9);
|
|
110
121
|
const profSav = `${stats.profile.savings}%`.padStart(7);
|
|
111
122
|
console.log(`│ ${profLabel.slice(0, 36).padEnd(36)} │ ${profTok} │ ${profSav} │`);
|
|
112
123
|
|
|
113
|
-
const resLabel = `
|
|
124
|
+
const resLabel = `Dynamic: resolved task (${stats.resolvedSkillCount} skills)`;
|
|
114
125
|
const resTok = stats.resolved.tokens.toLocaleString().padStart(9);
|
|
115
126
|
const resSav = `${stats.resolved.savings}%`.padStart(7);
|
|
116
127
|
console.log(`│ ${resLabel.slice(0, 36).padEnd(36)} │ ${resTok} │ ${resSav} │`);
|
|
117
128
|
|
|
118
129
|
console.log('└──────────────────────────────────────┴─────────────┴──────────┘');
|
|
119
|
-
console.log(
|
|
120
|
-
console.log('
|
|
130
|
+
console.log(`Resolved skills for project: ${stats.resolvedSkillList.join(', ')}`);
|
|
131
|
+
console.log('\nNote: Character-based token estimation (~4 chars/token heuristic).');
|
|
132
|
+
console.log('Measures static rule payload reduction against an uncurated full-catalog dump.');
|
|
133
|
+
console.log('Real workflow cost savings depend on prompt turns, model tokenizer, and task complexity.\n');
|
|
121
134
|
|
|
122
135
|
return stats;
|
|
123
136
|
}
|
package/.agents/validate.js
CHANGED
|
@@ -17,15 +17,26 @@ const fs = require('fs');
|
|
|
17
17
|
const path = require('path');
|
|
18
18
|
const { collectAllSkillDirs } = require('./plugins.js');
|
|
19
19
|
|
|
20
|
-
// ── Paths ─────────────────────────────────────────────────────────────────────
|
|
21
20
|
const ROOT = process.cwd();
|
|
22
21
|
const AGENTS_DIR = path.join(ROOT, '.agents');
|
|
23
22
|
const CORE_SKILLS = path.join(AGENTS_DIR, 'core', 'skills');
|
|
23
|
+
const CORE_PROFILES = path.join(AGENTS_DIR, 'core', 'profiles');
|
|
24
24
|
const GENERATED = path.join(AGENTS_DIR, 'generated');
|
|
25
25
|
|
|
26
26
|
// Adapters that compile to generated/<name>/skills/
|
|
27
27
|
const COMPILED_ADAPTERS = ['gemini', 'claude'];
|
|
28
28
|
|
|
29
|
+
// Well-known external frameworks/tools valid in conflicts/optional/profiles but not internal ContextOS skills
|
|
30
|
+
const EXTERNAL_NAMES = new Set([
|
|
31
|
+
'vue', 'angular', 'svelte', 'remix', 'express', 'koa', 'hapi', 'sails',
|
|
32
|
+
'tailwind', 'prisma', 'drizzle', 'mongoose', 'sequelize', 'typeorm',
|
|
33
|
+
'next-auth', 'react-query', 'zustand', 'redux', 'mobx', 'jotai', 'valtio',
|
|
34
|
+
'vitest', 'jest', 'mocha', 'cypress', 'playwright', 'storybook',
|
|
35
|
+
'graphql', 'trpc', 'apollo', 'relay', 'urql',
|
|
36
|
+
'sqlite', 'simple-auth', 'kubernetes', 'monitoring', 'cicd', 'cqrs',
|
|
37
|
+
'redis', 'postgres', 'postgresql', 'jwt', 'auth'
|
|
38
|
+
]);
|
|
39
|
+
|
|
29
40
|
// ── Tiny ANSI helpers (no deps) ───────────────────────────────────────────────
|
|
30
41
|
const NO_COLOR = process.env.NO_COLOR || !process.stdout.isTTY;
|
|
31
42
|
const c = {
|
|
@@ -68,7 +79,7 @@ function yamlList(text, field) {
|
|
|
68
79
|
const re = new RegExp(`^${field}:\\s*\\[([^\\]]*)]`, 'm');
|
|
69
80
|
const m = text.match(re);
|
|
70
81
|
if (!m || !m[1].trim()) return [];
|
|
71
|
-
return m[1].split(',').map(s => s.trim()).filter(Boolean);
|
|
82
|
+
return m[1].split(',').map(s => s.replace(/\s*#.*$/, '').trim()).filter(Boolean);
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
/**
|
|
@@ -83,7 +94,7 @@ function yamlBlockList(text, field) {
|
|
|
83
94
|
if (!m) return [];
|
|
84
95
|
return m[1]
|
|
85
96
|
.split('\n')
|
|
86
|
-
.map(l => l.replace(/^[ \t]+-\s*/, '').trim())
|
|
97
|
+
.map(l => l.replace(/^[ \t]+-\s*/, '').replace(/\s*#.*$/, '').trim())
|
|
87
98
|
.filter(Boolean);
|
|
88
99
|
}
|
|
89
100
|
|
|
@@ -102,6 +113,8 @@ function parseSkillYaml(yamlText) {
|
|
|
102
113
|
name: yamlField(yamlText, 'name'),
|
|
103
114
|
description: yamlField(yamlText, 'description'),
|
|
104
115
|
version: yamlField(yamlText, 'version'),
|
|
116
|
+
type: yamlField(yamlText, 'type'),
|
|
117
|
+
resources: yamlList(yamlText, 'resources').concat(yamlBlockList(yamlText, 'resources')),
|
|
105
118
|
requires: yamlList(yamlText, 'requires').concat(yamlBlockList(yamlText, 'requires')),
|
|
106
119
|
conflicts: yamlList(yamlText, 'conflicts').concat(yamlBlockList(yamlText, 'conflicts')),
|
|
107
120
|
optional: yamlList(yamlText, 'optional').concat(yamlBlockList(yamlText, 'optional')),
|
|
@@ -204,6 +217,12 @@ function checkSkillYaml(sourceSkills) {
|
|
|
204
217
|
error(`[yaml] ${name}/skill.yaml — missing 'name:' field`);
|
|
205
218
|
continue;
|
|
206
219
|
}
|
|
220
|
+
const VALID_TYPES = new Set(['instruction-only', 'compiler', 'runtime', 'experimental']);
|
|
221
|
+
if (!parsed.type) {
|
|
222
|
+
error(`[yaml] ${name}/skill.yaml — missing 'type:' field (expected instruction-only | compiler | runtime | experimental)`);
|
|
223
|
+
} else if (!VALID_TYPES.has(parsed.type)) {
|
|
224
|
+
error(`[yaml] ${name}/skill.yaml — invalid type '${parsed.type}' (expected instruction-only | compiler | runtime | experimental)`);
|
|
225
|
+
}
|
|
207
226
|
if (false && !parsed.description) {
|
|
208
227
|
// Some skills use block scalars — allow it, just warn
|
|
209
228
|
warn(`[yaml] ${name}/skill.yaml — 'description:' not found or uses multiline block (check manually)`);
|
|
@@ -284,16 +303,6 @@ function checkDependencies(sourceSkills) {
|
|
|
284
303
|
}
|
|
285
304
|
}
|
|
286
305
|
|
|
287
|
-
// externalNames: well-known external frameworks/tools that are valid in
|
|
288
|
-
// conflicts:/optional: but are NOT ContextOS skills. We never error on these.
|
|
289
|
-
const EXTERNAL_NAMES = new Set([
|
|
290
|
-
'vue', 'angular', 'svelte', 'remix', 'express', 'koa', 'hapi', 'sails',
|
|
291
|
-
'tailwind', 'prisma', 'drizzle', 'mongoose', 'sequelize', 'typeorm',
|
|
292
|
-
'next-auth', 'react-query', 'zustand', 'redux', 'mobx', 'jotai', 'valtio',
|
|
293
|
-
'vitest', 'jest', 'mocha', 'cypress', 'playwright', 'storybook',
|
|
294
|
-
'graphql', 'trpc', 'apollo', 'relay', 'urql',
|
|
295
|
-
]);
|
|
296
|
-
|
|
297
306
|
let depChecked = 0;
|
|
298
307
|
|
|
299
308
|
for (const [name, skill] of Object.entries(sourceSkills)) {
|
|
@@ -414,6 +423,133 @@ function checkValidationJson(sourceSkills) {
|
|
|
414
423
|
info(`[validation] ${pass} VALIDATION.json files are valid`);
|
|
415
424
|
}
|
|
416
425
|
|
|
426
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
427
|
+
// CHECK 9 — MCP Bundle Synchronization
|
|
428
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
429
|
+
function checkMcpBundleSync() {
|
|
430
|
+
const mcpSrcDir = path.join(ROOT, 'contextos-mcp', 'src');
|
|
431
|
+
const bundlePath = path.join(AGENTS_DIR, 'mcp', 'server.mjs');
|
|
432
|
+
|
|
433
|
+
if (!fs.existsSync(mcpSrcDir)) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (!fs.existsSync(bundlePath)) {
|
|
438
|
+
warn(`[mcp-sync] Missing compiled MCP server bundle at .agents/mcp/server.mjs. Run: cd contextos-mcp && npm run build:bundle`);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const bundleStat = fs.statSync(bundlePath);
|
|
443
|
+
const bundleMtime = bundleStat.mtimeMs;
|
|
444
|
+
|
|
445
|
+
let newestSrcFile = null;
|
|
446
|
+
let newestSrcMtime = 0;
|
|
447
|
+
|
|
448
|
+
function walk(dir) {
|
|
449
|
+
let entries;
|
|
450
|
+
try {
|
|
451
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
452
|
+
} catch {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
for (const entry of entries) {
|
|
456
|
+
const full = path.join(dir, entry.name);
|
|
457
|
+
if (entry.isDirectory()) {
|
|
458
|
+
walk(full);
|
|
459
|
+
} else if (entry.isFile()) {
|
|
460
|
+
const stat = fs.statSync(full);
|
|
461
|
+
if (stat.mtimeMs > newestSrcMtime) {
|
|
462
|
+
newestSrcMtime = stat.mtimeMs;
|
|
463
|
+
newestSrcFile = path.relative(ROOT, full);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
walk(mcpSrcDir);
|
|
470
|
+
|
|
471
|
+
if (newestSrcMtime > bundleMtime) {
|
|
472
|
+
warn(`[mcp-sync] Compiled MCP bundle (.agents/mcp/server.mjs) is older than source file ${newestSrcFile}. Run: cd contextos-mcp && npm run build:bundle`);
|
|
473
|
+
} else {
|
|
474
|
+
info(`[mcp-sync] Compiled MCP bundle (.agents/mcp/server.mjs) is up to date with contextos-mcp/src/`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
479
|
+
// CHECK 10 — Resources Manifest Integrity
|
|
480
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
481
|
+
function checkResourcesManifest(sourceSkills) {
|
|
482
|
+
let totalResources = 0;
|
|
483
|
+
let skillsWithResources = 0;
|
|
484
|
+
|
|
485
|
+
for (const [name, skill] of Object.entries(sourceSkills)) {
|
|
486
|
+
if (!skill.hasYaml) continue;
|
|
487
|
+
|
|
488
|
+
const text = fs.readFileSync(skill.yamlPath, 'utf8');
|
|
489
|
+
const parsed = parseSkillYaml(text);
|
|
490
|
+
|
|
491
|
+
if (!parsed.resources || parsed.resources.length === 0) {
|
|
492
|
+
warn(`[resources] ${name}/skill.yaml — no resource files declared in manifest`);
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
skillsWithResources++;
|
|
497
|
+
for (const res of parsed.resources) {
|
|
498
|
+
const fullPath = path.join(skill.dir, res);
|
|
499
|
+
if (!fs.existsSync(fullPath)) {
|
|
500
|
+
error(`[resources] ${name}/skill.yaml — declared resource not found on disk: '${res}'`);
|
|
501
|
+
} else {
|
|
502
|
+
totalResources++;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
info(`[resources] ${totalResources} resource files verified across ${skillsWithResources} skills`);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
511
|
+
// CHECK 11 — Profiles Integrity
|
|
512
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
513
|
+
function checkProfilesIntegrity(sourceSkills) {
|
|
514
|
+
if (!fs.existsSync(CORE_PROFILES)) return;
|
|
515
|
+
|
|
516
|
+
const knownIds = new Set();
|
|
517
|
+
for (const [name, skill] of Object.entries(sourceSkills)) {
|
|
518
|
+
knownIds.add(name);
|
|
519
|
+
if (skill.hasYaml) {
|
|
520
|
+
const text = fs.readFileSync(skill.yamlPath, 'utf8');
|
|
521
|
+
const id = yamlField(text, 'id');
|
|
522
|
+
if (id) knownIds.add(id);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
let profilesValidated = 0;
|
|
527
|
+
const files = fs.readdirSync(CORE_PROFILES).filter(f => f.endsWith('.yaml') || f.endsWith('.yml'));
|
|
528
|
+
|
|
529
|
+
for (const file of files) {
|
|
530
|
+
const filePath = path.join(CORE_PROFILES, file);
|
|
531
|
+
const text = fs.readFileSync(filePath, 'utf8');
|
|
532
|
+
|
|
533
|
+
const referenced = [
|
|
534
|
+
...yamlList(text, 'skills'),
|
|
535
|
+
...yamlBlockList(text, 'skills'),
|
|
536
|
+
...yamlList(text, 'prefer_skills'),
|
|
537
|
+
...yamlBlockList(text, 'prefer_skills'),
|
|
538
|
+
...yamlList(text, 'exclude_skills'),
|
|
539
|
+
...yamlBlockList(text, 'exclude_skills'),
|
|
540
|
+
];
|
|
541
|
+
|
|
542
|
+
for (const skillName of referenced) {
|
|
543
|
+
if (!knownIds.has(skillName) && !EXTERNAL_NAMES.has(skillName)) {
|
|
544
|
+
error(`[profiles] ${file} — references unknown skill '${skillName}'`);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
profilesValidated++;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
info(`[profiles] ${profilesValidated} core profiles validated against skill registry`);
|
|
551
|
+
}
|
|
552
|
+
|
|
417
553
|
// ═════════════════════════════════════════════════════════════════════════════
|
|
418
554
|
// REPORT
|
|
419
555
|
// ═════════════════════════════════════════════════════════════════════════════
|
|
@@ -487,6 +623,9 @@ function run() {
|
|
|
487
623
|
checkContentQuality(sourceSkills);
|
|
488
624
|
checkCostarHeaders(sourceSkills);
|
|
489
625
|
checkValidationJson(sourceSkills);
|
|
626
|
+
checkMcpBundleSync();
|
|
627
|
+
checkResourcesManifest(sourceSkills);
|
|
628
|
+
checkProfilesIntegrity(sourceSkills);
|
|
490
629
|
|
|
491
630
|
const passed = printReport();
|
|
492
631
|
process.exit(passed ? 0 : 1);
|