contextos-agents 2.0.0-beta.2 → 2.0.0-beta.3
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 +15 -49
- package/.agents/adapters/cursor/export.js +20 -3
- package/.agents/ctx.js +32 -70
- package/.agents/doctor.js +137 -3
- package/.agents/plugins.js +20 -5
- package/.agents/resolver/canonical-resolver.js +1348 -0
- package/.agents/rules/rule-catalog.js +525 -0
- package/.agents/skills-index.json +6 -166
- package/.agents/stats.js +9 -9
- package/package.json +87 -85
package/.agents/AGENTS.md
CHANGED
|
@@ -37,71 +37,37 @@ Skills are loaded on-demand. To keep the agent context window efficient:
|
|
|
37
37
|
|
|
38
38
|
## Skill Registry
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
### Built-in Core Skills (Bundled in `.agents/core/skills/`)
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
These 7 skills are always present in ContextOS Core:
|
|
43
43
|
|
|
44
44
|
| Skill | File | Activate When |
|
|
45
45
|
|-------|------|--------------|
|
|
46
46
|
| **engineering-workflow** | `engineering-workflow/SKILL.md` | **Every task** — defines the DEFINE→PLAN→BUILD→VERIFY→REVIEW→SHIP pipeline |
|
|
47
47
|
| **gstack-roles** | `gstack-roles/SKILL.md` | **Every task** — declare your specialist role before each phase |
|
|
48
48
|
| **ponytail-mindset** | `ponytail-mindset/SKILL.md` | **Every BUILD phase** — run the 7-rung ladder before writing any code |
|
|
49
|
-
| **interview-me** | `interview-me/SKILL.md` | **Before /spec** — when requirements are ambiguous or need interactive clarification |
|
|
50
|
-
| **subagent-orchestrator** | `subagent-orchestrator/SKILL.md` | **Parallel tasks** — decompose and delegate work across isolated subagents |
|
|
51
49
|
| **gemini-precision** | `gemini-precision/SKILL.md` | **All Gemini tasks** — zero assumptions, zero placeholders, surgical blast radius, test proof |
|
|
50
|
+
| **security** | `security/SKILL.md` | Any feature with auth, data access, user input, external integration |
|
|
51
|
+
| **context-os** | `context-os/SKILL.md` | Managing context rules, compiler pipelines, project policies |
|
|
52
|
+
| **context-manager** | `context-manager/SKILL.md` | Context selection, dynamic skill resolution, project graph traversal |
|
|
52
53
|
|
|
53
|
-
###
|
|
54
|
+
### Extended Catalog Skills (Installed on Demand via `contextos skill add <name>`)
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|-------|------|--------------|
|
|
57
|
-
| **ui-ux-pro** | `ui-ux-pro/SKILL.md` | Any UI task — planning guide, design decisions, color systems |
|
|
58
|
-
| **brutalist-design** | `brutalist-design/SKILL.md` | When explicitly asked for brutalist, high contrast, industrial, or sharp UI |
|
|
59
|
-
| **minimalist-design** | `minimalist-design/SKILL.md` | When explicitly asked for clean, minimal, editorial, or Notion-like UI |
|
|
60
|
-
| **soft-design** | `soft-design/SKILL.md` | When explicitly asked for calm, soft, premium, or low-contrast UI |
|
|
61
|
-
| **redesign-audit** | `redesign-audit/SKILL.md` | When tasked to audit or redesign an existing complex UI codebase without breaking it |
|
|
62
|
-
| **impeccable-design** | `impeccable-design/SKILL.md` | REVIEW phase for UI — run as hard QA checklist before shipping |
|
|
63
|
-
| **react** | `react/SKILL.md` | React component work |
|
|
64
|
-
| **react-best-practices** | `react-best-practices/SKILL.md` | Deep React component refactoring, hooks, and strict pattern enforcement |
|
|
65
|
-
| **nextjs** | `nextjs/SKILL.md` | Next.js App Router, Server Actions, routing |
|
|
66
|
-
| **typescript** | `typescript/SKILL.md` | Type-safe code, generics, config |
|
|
67
|
-
| **state-management** | `state-management/SKILL.md` | Zustand, TanStack Query, client/server state |
|
|
68
|
-
| **ui-design** | `ui-design/SKILL.md` | Component library design, tokens |
|
|
69
|
-
| **ux-design** | `ux-design/SKILL.md` | User flow design, interaction patterns |
|
|
70
|
-
| **web-accessibility** | `web-accessibility/SKILL.md` | ARIA, WCAG compliance |
|
|
71
|
-
|
|
72
|
-
### Backend Skills
|
|
73
|
-
|
|
74
|
-
| Skill | File | Activate When |
|
|
75
|
-
|-------|------|--------------|
|
|
76
|
-
| **system-design** | `system-design/SKILL.md` | Any backend architecture — mandatory pre-design checklist |
|
|
77
|
-
| **node** | `node/SKILL.md` | Node.js server code |
|
|
78
|
-
| **fastapi** | `fastapi/SKILL.md` | FastAPI / Python backend |
|
|
79
|
-
| **nestjs** | `nestjs/SKILL.md` | NestJS framework |
|
|
80
|
-
| **microservices** | `microservices/SKILL.md` | Service decomposition |
|
|
81
|
-
| **ddd** | `ddd/SKILL.md` | Domain modeling, bounded contexts |
|
|
82
|
-
| **database** | `database/SKILL.md` | PostgreSQL, Prisma, Drizzle, migrations, indexing |
|
|
56
|
+
The following domain skills are available in the extended catalog and can be added to your project on demand:
|
|
83
57
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
|
87
|
-
|
|
88
|
-
| **
|
|
89
|
-
| **performance** | `performance/SKILL.md` | Optimization tasks, Core Web Vitals |
|
|
90
|
-
| **vercel-optimize** | `vercel-optimize/SKILL.md` | Edge caching, Vercel deployments, Next.js optimization |
|
|
91
|
-
| **testing** | `testing/SKILL.md` | Vitest, RTL, Playwright, TDD/BDD testing |
|
|
92
|
-
| **docker** | `docker/SKILL.md` | Dockerfiles, multi-stage, container security, compose |
|
|
93
|
-
| **decisions** | `decisions/SKILL.md` | Making architectural choices |
|
|
94
|
-
| **architecture-diagrams** | `architecture-diagrams/SKILL.md` | Interactive animated SVG/HTML architecture and sequence diagrams |
|
|
95
|
-
| **adapters** | `adapters/SKILL.md` | Building system integrations |
|
|
96
|
-
| **generators** | `generators/SKILL.md` | Code generation patterns |
|
|
97
|
-
| **graphify** | `graphify/SKILL.md` | Codebase mapping, AST dependency knowledge graph, blast-radius analysis |
|
|
58
|
+
| Domain | Skills | Install Command |
|
|
59
|
+
|--------|--------|-----------------|
|
|
60
|
+
| **Frontend** | `react`, `react-best-practices`, `nextjs`, `typescript`, `ui-ux-pro`, `impeccable-design`, `state-management`, `ui-design`, `ux-design`, `web-accessibility`, `brutalist-design`, `minimalist-design`, `soft-design`, `redesign-audit` | `contextos skill add <name>` |
|
|
61
|
+
| **Backend** | `system-design`, `node`, `fastapi`, `nestjs`, `microservices`, `ddd`, `database` | `contextos skill add <name>` |
|
|
62
|
+
| **Cross-Cutting** | `testing`, `docker`, `decisions`, `architecture-diagrams`, `adapters`, `generators`, `graphify`, `performance`, `vercel-optimize` | `contextos skill add <name>` |
|
|
98
63
|
|
|
99
64
|
---
|
|
100
65
|
|
|
101
66
|
## Automatic Skill Activation Rules (Proactive Routing)
|
|
102
67
|
|
|
103
|
-
The following rules are **deterministic** — no judgment needed. If the condition is true, the skill is loaded.
|
|
104
|
-
|
|
68
|
+
The following rules are **deterministic** — no judgment needed. If the condition is true, the skill is loaded.
|
|
69
|
+
|
|
70
|
+
> **Availability Guard**: Load only skills that are physically installed in `.agents/core/skills/`. For uninstalled catalog skills, rely on core skills (`engineering-workflow`, `ponytail-mindset`, `gemini-precision`, `security`) and recommend `contextos skill add <name>` when deep domain checklists are needed.
|
|
105
71
|
|
|
106
72
|
### By Task Type
|
|
107
73
|
|
|
@@ -19,12 +19,29 @@ function describe() {
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function getSkillGlobsAndFlags(skillName) {
|
|
23
|
-
const ALWAYS_APPLY_SKILLS = ['engineering-workflow', 'gstack-roles', 'ponytail-mindset'];
|
|
22
|
+
function getSkillGlobsAndFlags(skillName, skillDir = null) {
|
|
23
|
+
const ALWAYS_APPLY_SKILLS = ['engineering-workflow', 'gstack-roles', 'ponytail-mindset', 'gemini-precision'];
|
|
24
24
|
if (ALWAYS_APPLY_SKILLS.includes(skillName)) {
|
|
25
25
|
return { globs: '', alwaysApply: true };
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// 1. Dynamic extraction from skill.yaml if available
|
|
29
|
+
if (skillDir) {
|
|
30
|
+
const yamlPath = path.join(skillDir, 'skill.yaml');
|
|
31
|
+
if (fs.existsSync(yamlPath)) {
|
|
32
|
+
try {
|
|
33
|
+
const yamlContent = fs.readFileSync(yamlPath, 'utf8');
|
|
34
|
+
const match = yamlContent.match(/(?:fileGlobs|triggers\s*:\s*(?:files|globs)|globs)\s*:\s*\[?([^\r\n\]]+)\]?/i);
|
|
35
|
+
if (match && match[1]) {
|
|
36
|
+
const globsVal = match[1].replace(/['"]/g, '').trim();
|
|
37
|
+
if (globsVal) {
|
|
38
|
+
return { globs: globsVal, alwaysApply: false };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} catch {}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
28
45
|
const GLOB_MAP = {
|
|
29
46
|
'react': '**/*.{tsx,jsx}',
|
|
30
47
|
'react-best-practices': '**/*.{tsx,jsx}',
|
|
@@ -110,7 +127,7 @@ function generateCursorMdc(skillDir) {
|
|
|
110
127
|
if (troubleshooting) raw += '\n\n' + troubleshooting;
|
|
111
128
|
|
|
112
129
|
const body = stripFrontmatter(raw);
|
|
113
|
-
const { globs, alwaysApply } = getSkillGlobsAndFlags(skillName);
|
|
130
|
+
const { globs, alwaysApply } = getSkillGlobsAndFlags(skillName, skillDir);
|
|
114
131
|
|
|
115
132
|
let cleanDescription = description.replace(/\r?\n+/g, ' ').replace(/"/g, "'").trim();
|
|
116
133
|
if (!globs && !alwaysApply) {
|
package/.agents/ctx.js
CHANGED
|
@@ -643,9 +643,31 @@ if (command === 'export') {
|
|
|
643
643
|
console.error(`[ERROR] ${err.message}`);
|
|
644
644
|
process.exit(1);
|
|
645
645
|
});
|
|
646
|
+
} else if (subcommand === 'override' || subcommand === 'eject' || subcommand === 'diff') {
|
|
647
|
+
const { SkillCustomizationManager } = require('./customization-dx.js');
|
|
648
|
+
const manager = new SkillCustomizationManager(process.cwd());
|
|
649
|
+
try {
|
|
650
|
+
if (subcommand === 'override') {
|
|
651
|
+
const res = manager.override(ref);
|
|
652
|
+
console.log(`✓ Created skill override for '${ref}' at: ${res.overridePath}`);
|
|
653
|
+
} else if (subcommand === 'eject') {
|
|
654
|
+
const res = manager.eject(ref);
|
|
655
|
+
console.log(`✓ Ejected skill '${ref}' to project layer at: ${res.ejectedPath}`);
|
|
656
|
+
} else if (subcommand === 'diff') {
|
|
657
|
+
const res = manager.diff(ref);
|
|
658
|
+
if (!res.hasDiff) {
|
|
659
|
+
console.log(`No diff for skill '${ref}' against upstream.`);
|
|
660
|
+
} else {
|
|
661
|
+
console.log(`Diff for skill '${ref}':\n${res.diff}`);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
} catch (err) {
|
|
665
|
+
console.error(`[ERROR] ${err.message}`);
|
|
666
|
+
process.exit(1);
|
|
667
|
+
}
|
|
646
668
|
} else {
|
|
647
669
|
console.error(`Unknown skill subcommand: ${subcommand}`);
|
|
648
|
-
console.error('Valid subcommands: add, remove, list, search');
|
|
670
|
+
console.error('Valid subcommands: add, remove, list, search, override, eject, diff');
|
|
649
671
|
process.exit(1);
|
|
650
672
|
}
|
|
651
673
|
|
|
@@ -800,75 +822,15 @@ if (command === 'export') {
|
|
|
800
822
|
|
|
801
823
|
// ── thread ──────────────────────────────────────────────────────────────────
|
|
802
824
|
} else if (command === 'thread') {
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
console.log(JSON.stringify(list, null, 2));
|
|
813
|
-
} else {
|
|
814
|
-
console.log('\n══════════════════════════════════════════');
|
|
815
|
-
console.log(' ContextOS — Runtime Execution Threads');
|
|
816
|
-
console.log('══════════════════════════════════════════\n');
|
|
817
|
-
if (list.length === 0) {
|
|
818
|
-
console.log(' No runtime threads recorded yet.');
|
|
819
|
-
} else {
|
|
820
|
-
for (const th of list) {
|
|
821
|
-
const ready = th.merge?.status === 'READY' ? ' [READY TO MERGE]' : '';
|
|
822
|
-
console.log(` • ID: ${th.id} (rev: ${th.revision})`);
|
|
823
|
-
console.log(` Status : Exec: ${th.execution.status} | Verify: ${th.verification.status} | Review: ${th.review.status} | Merge: ${th.merge.status}${ready}`);
|
|
824
|
-
console.log(` Task : ${(th.config?.task || '').slice(0, 70)}`);
|
|
825
|
-
console.log(` Created : ${new Date(th.createdAt).toISOString()}`);
|
|
826
|
-
console.log('');
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
console.log('──────────────────────────────────────────\n');
|
|
830
|
-
}
|
|
831
|
-
} else if (subcmd === 'get') {
|
|
832
|
-
const threadId = args[2];
|
|
833
|
-
if (!threadId) {
|
|
834
|
-
console.error('Usage: node ctx.js thread get <threadId> [--json]');
|
|
835
|
-
process.exit(1);
|
|
836
|
-
}
|
|
837
|
-
const th = store.get(threadId);
|
|
838
|
-
if (!th) {
|
|
839
|
-
console.error(`Thread "${threadId}" not found.`);
|
|
840
|
-
process.exit(1);
|
|
841
|
-
}
|
|
842
|
-
console.log(JSON.stringify(th, null, 2));
|
|
843
|
-
} else if (subcmd === 'readiness') {
|
|
844
|
-
const threadId = args[2];
|
|
845
|
-
if (!threadId) {
|
|
846
|
-
console.error('Usage: node ctx.js thread readiness <threadId> [--json]');
|
|
847
|
-
process.exit(1);
|
|
848
|
-
}
|
|
849
|
-
const th = store.get(threadId);
|
|
850
|
-
if (!th) {
|
|
851
|
-
console.error(`Thread "${threadId}" not found.`);
|
|
852
|
-
process.exit(1);
|
|
853
|
-
}
|
|
854
|
-
const readiness = evaluateMergeReadiness(th);
|
|
855
|
-
if (isJson) {
|
|
856
|
-
console.log(JSON.stringify(readiness, null, 2));
|
|
857
|
-
} else {
|
|
858
|
-
console.log('\n══════════════════════════════════════════');
|
|
859
|
-
console.log(` ContextOS — Merge Readiness (${th.id})`);
|
|
860
|
-
console.log('══════════════════════════════════════════\n');
|
|
861
|
-
console.log(` Overall Verdict: ${readiness.isReady ? 'READY TO MERGE ✓' : 'NOT READY ✗'}\n`);
|
|
862
|
-
for (const c of readiness.checks) {
|
|
863
|
-
const mark = c.passed ? '✓' : '✗';
|
|
864
|
-
console.log(` ${mark} [${c.name.padEnd(24)}] ${c.message}`);
|
|
865
|
-
}
|
|
866
|
-
console.log('\n──────────────────────────────────────────\n');
|
|
867
|
-
}
|
|
868
|
-
} else {
|
|
869
|
-
console.error(`Unknown thread subcommand: ${subcmd}. Available: list, get, readiness`);
|
|
870
|
-
process.exit(1);
|
|
871
|
-
}
|
|
825
|
+
console.log('\n══════════════════════════════════════════');
|
|
826
|
+
console.log(' ContextOS — Runtime Execution Threads');
|
|
827
|
+
console.log('══════════════════════════════════════════\n');
|
|
828
|
+
console.log('[INFO] Execution runtime and thread management have moved to @contextos/mcp in v2.0.');
|
|
829
|
+
console.log(' To view, manage, and verify execution threads, install and run:');
|
|
830
|
+
console.log(' npm install --save-dev @contextos/mcp');
|
|
831
|
+
console.log(' npx contextos-mcp thread ' + (args.slice(1).join(' ') || 'list'));
|
|
832
|
+
console.log('\n──────────────────────────────────────────\n');
|
|
833
|
+
process.exit(0);
|
|
872
834
|
|
|
873
835
|
// ── unknown ───────────────────────────────────────────────────────────────────
|
|
874
836
|
} else {
|
package/.agents/doctor.js
CHANGED
|
@@ -167,6 +167,15 @@ function checkFilesystemPermissions(projectDir) {
|
|
|
167
167
|
function checkPreCommitHook(projectDir) {
|
|
168
168
|
const hookPath = path.join(projectDir, '.git', 'hooks', 'pre-commit');
|
|
169
169
|
if (!fs.existsSync(hookPath)) {
|
|
170
|
+
if (!isContextOSDevRepo(projectDir)) {
|
|
171
|
+
return {
|
|
172
|
+
id: 'pre_commit_hook',
|
|
173
|
+
status: STATUS.SKIP,
|
|
174
|
+
ok: true,
|
|
175
|
+
message: 'optional (not installed in workspace)',
|
|
176
|
+
remediation: null,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
170
179
|
return {
|
|
171
180
|
id: 'pre_commit_hook',
|
|
172
181
|
status: STATUS.WARN,
|
|
@@ -207,6 +216,15 @@ function checkPreCommitHook(projectDir) {
|
|
|
207
216
|
function checkSecretScanner(projectDir) {
|
|
208
217
|
const scriptPath = path.join(projectDir, 'scripts', 'check-secrets.js');
|
|
209
218
|
if (!fs.existsSync(scriptPath)) {
|
|
219
|
+
if (!isContextOSDevRepo(projectDir)) {
|
|
220
|
+
return {
|
|
221
|
+
id: 'secret_scanner',
|
|
222
|
+
status: STATUS.SKIP,
|
|
223
|
+
ok: true,
|
|
224
|
+
message: 'optional (not configured in workspace)',
|
|
225
|
+
remediation: null,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
210
228
|
// Per Milestone 8 spec: "doctor не считает отсутствующий scanner PASS"
|
|
211
229
|
return {
|
|
212
230
|
id: 'secret_scanner',
|
|
@@ -680,6 +698,85 @@ function checkDriftStatus(projectDir) {
|
|
|
680
698
|
}
|
|
681
699
|
}
|
|
682
700
|
|
|
701
|
+
function isContextOSDevRepo(projectDir) {
|
|
702
|
+
try {
|
|
703
|
+
const pkgPath = path.join(projectDir, 'package.json');
|
|
704
|
+
if (!fs.existsSync(pkgPath)) return false;
|
|
705
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
706
|
+
return pkg.name === 'contextos-agents' || pkg.name === 'contextos';
|
|
707
|
+
} catch {
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function checkModuleExecution(projectDir) {
|
|
713
|
+
const errors = [];
|
|
714
|
+
|
|
715
|
+
// Check CanonicalResolver
|
|
716
|
+
try {
|
|
717
|
+
const resolverPath = path.join(projectDir, '.agents', 'resolver', 'canonical-resolver.js');
|
|
718
|
+
if (fs.existsSync(resolverPath)) {
|
|
719
|
+
const resolver = require(resolverPath);
|
|
720
|
+
if (!resolver || typeof resolver.CanonicalResolver !== 'function') {
|
|
721
|
+
errors.push('CanonicalResolver is not exported correctly');
|
|
722
|
+
}
|
|
723
|
+
} else {
|
|
724
|
+
errors.push('.agents/resolver/canonical-resolver.js missing');
|
|
725
|
+
}
|
|
726
|
+
} catch (err) {
|
|
727
|
+
errors.push(`CanonicalResolver import failed: ${err.message}`);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Check RuleCatalog
|
|
731
|
+
try {
|
|
732
|
+
const rulesPath = path.join(projectDir, '.agents', 'rules', 'rule-catalog.js');
|
|
733
|
+
if (fs.existsSync(rulesPath)) {
|
|
734
|
+
const rules = require(rulesPath);
|
|
735
|
+
if (!rules || typeof rules.RuleCatalog !== 'function') {
|
|
736
|
+
errors.push('RuleCatalog is not exported correctly');
|
|
737
|
+
}
|
|
738
|
+
} else {
|
|
739
|
+
errors.push('.agents/rules/rule-catalog.js missing');
|
|
740
|
+
}
|
|
741
|
+
} catch (err) {
|
|
742
|
+
errors.push(`RuleCatalog import failed: ${err.message}`);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Check ManifestCompiler
|
|
746
|
+
try {
|
|
747
|
+
const compilerPath = path.join(projectDir, '.agents', 'compiler', 'manifest-compiler.js');
|
|
748
|
+
if (fs.existsSync(compilerPath)) {
|
|
749
|
+
const compiler = require(compilerPath);
|
|
750
|
+
if (!compiler || typeof compiler.ManifestCompiler !== 'function') {
|
|
751
|
+
errors.push('ManifestCompiler is not exported correctly');
|
|
752
|
+
}
|
|
753
|
+
} else {
|
|
754
|
+
errors.push('.agents/compiler/manifest-compiler.js missing');
|
|
755
|
+
}
|
|
756
|
+
} catch (err) {
|
|
757
|
+
errors.push(`ManifestCompiler import failed: ${err.message}`);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
if (errors.length > 0) {
|
|
761
|
+
return {
|
|
762
|
+
id: 'module_execution',
|
|
763
|
+
status: STATUS.FAIL,
|
|
764
|
+
ok: false,
|
|
765
|
+
message: errors.join('; '),
|
|
766
|
+
errors,
|
|
767
|
+
remediation: 'Reinstall package via `npx contextos-agents init --force` to restore core modules',
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
return {
|
|
772
|
+
id: 'module_execution',
|
|
773
|
+
status: STATUS.PASS,
|
|
774
|
+
ok: true,
|
|
775
|
+
message: 'all core modules verified (resolver, rules, compiler)',
|
|
776
|
+
remediation: null,
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
|
|
683
780
|
function inspectSkills(projectDir, activeProfile) {
|
|
684
781
|
const skillsDir = path.join(projectDir, '.agents', 'core', 'skills');
|
|
685
782
|
if (!fs.existsSync(skillsDir)) return { total: 0, active: 0, excluded: 0, byDomain: { frontend: [], backend: [], cross: [] } };
|
|
@@ -782,9 +879,27 @@ function applyDoctorFix(projectDir = process.cwd(), options = {}) {
|
|
|
782
879
|
function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
783
880
|
const version = (() => {
|
|
784
881
|
try {
|
|
785
|
-
|
|
882
|
+
const parentPkgPath = path.join(__dirname, '..', 'package.json');
|
|
883
|
+
if (fs.existsSync(parentPkgPath)) {
|
|
884
|
+
const parentPkg = JSON.parse(fs.readFileSync(parentPkgPath, 'utf8'));
|
|
885
|
+
if (parentPkg.name === 'contextos-agents' || parentPkg.name === 'contextos') {
|
|
886
|
+
return parentPkg.version;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
const lockfileV2Path = path.join(projectDir, '.agents', 'lockfile.v2.json');
|
|
890
|
+
if (fs.existsSync(lockfileV2Path)) {
|
|
891
|
+
const lf = JSON.parse(fs.readFileSync(lockfileV2Path, 'utf8'));
|
|
892
|
+
if (lf.package && lf.package.compilerVersion) return lf.package.compilerVersion;
|
|
893
|
+
}
|
|
894
|
+
const lockfilePath = path.join(projectDir, '.agents', 'lockfile.json');
|
|
895
|
+
if (fs.existsSync(lockfilePath)) {
|
|
896
|
+
const lf = JSON.parse(fs.readFileSync(lockfilePath, 'utf8'));
|
|
897
|
+
if (lf.generator && lf.generator.version) return lf.generator.version;
|
|
898
|
+
if (lf.version) return lf.version;
|
|
899
|
+
}
|
|
900
|
+
return '2.0.0';
|
|
786
901
|
} catch {
|
|
787
|
-
return '
|
|
902
|
+
return '2.0.0';
|
|
788
903
|
}
|
|
789
904
|
})();
|
|
790
905
|
|
|
@@ -811,6 +926,18 @@ function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
|
811
926
|
errors.push('.agents/ directory missing (run: npx contextos-agents init)');
|
|
812
927
|
}
|
|
813
928
|
|
|
929
|
+
const moduleExecCheck = hasAgents ? checkModuleExecution(projectDir) : {
|
|
930
|
+
id: 'module_execution',
|
|
931
|
+
status: STATUS.FAIL,
|
|
932
|
+
ok: false,
|
|
933
|
+
message: '.agents directory missing',
|
|
934
|
+
remediation: 'Run: npx contextos-agents init',
|
|
935
|
+
};
|
|
936
|
+
checks.push(moduleExecCheck);
|
|
937
|
+
if (!moduleExecCheck.ok) {
|
|
938
|
+
errors.push(moduleExecCheck.message);
|
|
939
|
+
}
|
|
940
|
+
|
|
814
941
|
const nodeCheck = checkNodeVersion();
|
|
815
942
|
checks.push(nodeCheck);
|
|
816
943
|
if (!nodeCheck.ok) errors.push(nodeCheck.message);
|
|
@@ -947,6 +1074,11 @@ function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
|
947
1074
|
console.log('│ ✗ .agents/ directory missing (run: npx contextos-agents) │');
|
|
948
1075
|
}
|
|
949
1076
|
|
|
1077
|
+
// Execution check
|
|
1078
|
+
const execIcon = moduleExecCheck.ok ? '✓' : '✗';
|
|
1079
|
+
const execStr = `Core modules: ${moduleExecCheck.message}`;
|
|
1080
|
+
console.log(`│ ${execIcon} ${execStr.slice(0, 58).padEnd(58)}│`);
|
|
1081
|
+
|
|
950
1082
|
// Node version
|
|
951
1083
|
const nodeIcon = nodeCheck.ok ? '✓' : '✗';
|
|
952
1084
|
console.log(`│ ${nodeIcon} ${nodeCheck.message.padEnd(58)}│`);
|
|
@@ -990,7 +1122,7 @@ function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
|
990
1122
|
if (mcpCheck.status === STATUS.PASS) {
|
|
991
1123
|
console.log('│ ✓ MCP server: installed (.agents/mcp/server.mjs) │');
|
|
992
1124
|
} else {
|
|
993
|
-
console.log('│ • MCP server: not installed (run:
|
|
1125
|
+
console.log('│ • MCP server: not installed (run: npm i -D @contextos/mcp) │');
|
|
994
1126
|
}
|
|
995
1127
|
|
|
996
1128
|
// Pre-commit hook
|
|
@@ -1109,5 +1241,7 @@ module.exports = {
|
|
|
1109
1241
|
checkSandboxAvailability,
|
|
1110
1242
|
checkClaimEvidence,
|
|
1111
1243
|
checkProjectLock,
|
|
1244
|
+
checkModuleExecution,
|
|
1245
|
+
isContextOSDevRepo,
|
|
1112
1246
|
inspectSkills,
|
|
1113
1247
|
};
|
package/.agents/plugins.js
CHANGED
|
@@ -62,12 +62,10 @@ const c = {
|
|
|
62
62
|
const PROMPT_INJECTION_PATTERNS = [
|
|
63
63
|
/ignore\s+(all\s+)?previous\s+instructions/i,
|
|
64
64
|
/forget\s+(all\s+)?previous/i,
|
|
65
|
-
/you\s+are\s+now/i,
|
|
66
|
-
/system\s*:\s*/i,
|
|
65
|
+
/you\s+are\s+now\s+(?:an?\s+)?(?:unrestricted|jailbreak|evil|dan|developer\s+mode|bypassed)/i,
|
|
67
66
|
/\[SYSTEM\]/i,
|
|
68
|
-
/
|
|
69
|
-
/https?:\/\/[^\s)]+\/(?:exfil|steal|leak|webhook|collect)/i,
|
|
70
|
-
/\b(?:eval|exec|Function)\s*\(/i,
|
|
67
|
+
/(?:^|[\r\n])\s*system\s*:\s*/i,
|
|
68
|
+
/https?:\/\/[^\s)]+\/(?:exfil|steal|leak|webhook|collect)\b/i,
|
|
71
69
|
];
|
|
72
70
|
|
|
73
71
|
// ── Shell Escapes & Script Injection Scanner ──────────────────────────────────
|
|
@@ -411,6 +409,23 @@ async function installFromGitHub(descriptor, skillName, dryRun, checksum, forceU
|
|
|
411
409
|
manifest['skill.yaml'] = yamlHash;
|
|
412
410
|
}
|
|
413
411
|
|
|
412
|
+
// Also try to fetch standard ancillary files if present
|
|
413
|
+
const ancillaryFiles = ['EXAMPLES.md', 'TROUBLESHOOTING.md', 'VALIDATION.json'];
|
|
414
|
+
for (const f of ancillaryFiles) {
|
|
415
|
+
const fUrl = skillPath ? `${base}/${skillPath}/${f}` : `${base}/${f}`;
|
|
416
|
+
try {
|
|
417
|
+
const fContent = await fetchText(fUrl);
|
|
418
|
+
if (fContent && fContent.length > 0) {
|
|
419
|
+
scanForPromptInjection(fContent, { throwOnMatch: !forceUnsafe });
|
|
420
|
+
const fHash = crypto.createHash('sha256').update(fContent).digest('hex');
|
|
421
|
+
tx.stageWrite(`${relTargetDir}/${f}`, fContent);
|
|
422
|
+
manifest[f] = fHash;
|
|
423
|
+
}
|
|
424
|
+
} catch {
|
|
425
|
+
// Ancillary files are optional
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
414
429
|
// Write a source marker with manifest
|
|
415
430
|
const sourceContent = JSON.stringify({
|
|
416
431
|
type: 'github',
|