contextos-agents 2.0.0-beta.2 → 2.0.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 +15 -49
- package/.agents/adapters/cursor/export.js +20 -3
- package/.agents/ctx.js +32 -70
- package/.agents/doctor.js +144 -23
- 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/LICENSE +201 -21
- package/NOTICE +4 -0
- package/README.md +14 -18
- package/bin/commands/update.js +7 -8
- package/package.json +83 -85
- package/benchmarks/v2/analysis/statistics.js +0 -140
- package/benchmarks/v2/analysis/stats.js +0 -69
- package/benchmarks/v2/arms/arm-definitions.js +0 -79
- package/benchmarks/v2/dataset.schema.json +0 -34
- package/benchmarks/v2/evaluators/index.js +0 -25
- package/benchmarks/v2/evaluators/verified-success.js +0 -116
- package/benchmarks/v2/harness/runner.js +0 -88
- package/benchmarks/v2/pilot-tasks.json +0 -392
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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Implements Milestone 8 Doctor v2 specification:
|
|
6
6
|
* - Standardized status outcomes: PASS, WARN, FAIL, SKIP, UNVERIFIED, RECOVERY_REQUIRED
|
|
7
7
|
* - Deep diagnostic checks:
|
|
8
|
-
* 1. Node.js environment (Node
|
|
8
|
+
* 1. Node.js environment (Node 22+ for the stable Core)
|
|
9
9
|
* 2. Git availability & version
|
|
10
10
|
* 3. Filesystem permissions and path containment
|
|
11
11
|
* 4. Manifests & schema compliance
|
|
@@ -76,33 +76,20 @@ function checkNodeVersion() {
|
|
|
76
76
|
ok: false,
|
|
77
77
|
version,
|
|
78
78
|
message: `${version} (unknown format)`,
|
|
79
|
-
remediation: 'Install Node.js >=
|
|
79
|
+
remediation: 'Install Node.js >= 22.0.0 from https://nodejs.org',
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
const major = parseInt(match[1], 10);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// Core requires Node >= 18.0.0 (or 16.7.0 for basic cpSync). Runtime requires Node >= 20.0.0.
|
|
87
|
-
if (major < 18) {
|
|
84
|
+
// Stable Core requires Node.js 22 or newer.
|
|
85
|
+
if (major < 22) {
|
|
88
86
|
return {
|
|
89
87
|
id: 'node_version',
|
|
90
88
|
status: STATUS.FAIL,
|
|
91
89
|
ok: false,
|
|
92
90
|
version,
|
|
93
|
-
message: `Node.js ${version} (<
|
|
94
|
-
remediation: 'Upgrade Node.js to >=
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (major < 20) {
|
|
99
|
-
return {
|
|
100
|
-
id: 'node_version',
|
|
101
|
-
status: STATUS.WARN,
|
|
102
|
-
ok: true,
|
|
103
|
-
version,
|
|
104
|
-
message: `Node.js ${version} (>= 18.0.0 for Core, Node 20 recommended for Runtime)`,
|
|
105
|
-
remediation: 'Upgrade to Node.js >= 20.0.0 for optimal sandbox and runtime worker support',
|
|
91
|
+
message: `Node.js ${version} (< 22.0.0, Node 22 required for Core)`,
|
|
92
|
+
remediation: 'Upgrade Node.js to >= 22.0.0 from https://nodejs.org',
|
|
106
93
|
};
|
|
107
94
|
}
|
|
108
95
|
|
|
@@ -111,7 +98,7 @@ function checkNodeVersion() {
|
|
|
111
98
|
status: STATUS.PASS,
|
|
112
99
|
ok: true,
|
|
113
100
|
version,
|
|
114
|
-
message: `Node.js ${version} (>=
|
|
101
|
+
message: `Node.js ${version} (>= 22.0.0, supported)`,
|
|
115
102
|
remediation: null,
|
|
116
103
|
};
|
|
117
104
|
}
|
|
@@ -167,6 +154,15 @@ function checkFilesystemPermissions(projectDir) {
|
|
|
167
154
|
function checkPreCommitHook(projectDir) {
|
|
168
155
|
const hookPath = path.join(projectDir, '.git', 'hooks', 'pre-commit');
|
|
169
156
|
if (!fs.existsSync(hookPath)) {
|
|
157
|
+
if (!isContextOSDevRepo(projectDir)) {
|
|
158
|
+
return {
|
|
159
|
+
id: 'pre_commit_hook',
|
|
160
|
+
status: STATUS.SKIP,
|
|
161
|
+
ok: true,
|
|
162
|
+
message: 'optional (not installed in workspace)',
|
|
163
|
+
remediation: null,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
170
166
|
return {
|
|
171
167
|
id: 'pre_commit_hook',
|
|
172
168
|
status: STATUS.WARN,
|
|
@@ -207,6 +203,15 @@ function checkPreCommitHook(projectDir) {
|
|
|
207
203
|
function checkSecretScanner(projectDir) {
|
|
208
204
|
const scriptPath = path.join(projectDir, 'scripts', 'check-secrets.js');
|
|
209
205
|
if (!fs.existsSync(scriptPath)) {
|
|
206
|
+
if (!isContextOSDevRepo(projectDir)) {
|
|
207
|
+
return {
|
|
208
|
+
id: 'secret_scanner',
|
|
209
|
+
status: STATUS.SKIP,
|
|
210
|
+
ok: true,
|
|
211
|
+
message: 'optional (not configured in workspace)',
|
|
212
|
+
remediation: null,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
210
215
|
// Per Milestone 8 spec: "doctor не считает отсутствующий scanner PASS"
|
|
211
216
|
return {
|
|
212
217
|
id: 'secret_scanner',
|
|
@@ -680,6 +685,85 @@ function checkDriftStatus(projectDir) {
|
|
|
680
685
|
}
|
|
681
686
|
}
|
|
682
687
|
|
|
688
|
+
function isContextOSDevRepo(projectDir) {
|
|
689
|
+
try {
|
|
690
|
+
const pkgPath = path.join(projectDir, 'package.json');
|
|
691
|
+
if (!fs.existsSync(pkgPath)) return false;
|
|
692
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
693
|
+
return pkg.name === 'contextos-agents' || pkg.name === 'contextos';
|
|
694
|
+
} catch {
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function checkModuleExecution(projectDir) {
|
|
700
|
+
const errors = [];
|
|
701
|
+
|
|
702
|
+
// Check CanonicalResolver
|
|
703
|
+
try {
|
|
704
|
+
const resolverPath = path.join(projectDir, '.agents', 'resolver', 'canonical-resolver.js');
|
|
705
|
+
if (fs.existsSync(resolverPath)) {
|
|
706
|
+
const resolver = require(resolverPath);
|
|
707
|
+
if (!resolver || typeof resolver.CanonicalResolver !== 'function') {
|
|
708
|
+
errors.push('CanonicalResolver is not exported correctly');
|
|
709
|
+
}
|
|
710
|
+
} else {
|
|
711
|
+
errors.push('.agents/resolver/canonical-resolver.js missing');
|
|
712
|
+
}
|
|
713
|
+
} catch (err) {
|
|
714
|
+
errors.push(`CanonicalResolver import failed: ${err.message}`);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// Check RuleCatalog
|
|
718
|
+
try {
|
|
719
|
+
const rulesPath = path.join(projectDir, '.agents', 'rules', 'rule-catalog.js');
|
|
720
|
+
if (fs.existsSync(rulesPath)) {
|
|
721
|
+
const rules = require(rulesPath);
|
|
722
|
+
if (!rules || typeof rules.RuleCatalog !== 'function') {
|
|
723
|
+
errors.push('RuleCatalog is not exported correctly');
|
|
724
|
+
}
|
|
725
|
+
} else {
|
|
726
|
+
errors.push('.agents/rules/rule-catalog.js missing');
|
|
727
|
+
}
|
|
728
|
+
} catch (err) {
|
|
729
|
+
errors.push(`RuleCatalog import failed: ${err.message}`);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// Check ManifestCompiler
|
|
733
|
+
try {
|
|
734
|
+
const compilerPath = path.join(projectDir, '.agents', 'compiler', 'manifest-compiler.js');
|
|
735
|
+
if (fs.existsSync(compilerPath)) {
|
|
736
|
+
const compiler = require(compilerPath);
|
|
737
|
+
if (!compiler || typeof compiler.ManifestCompiler !== 'function') {
|
|
738
|
+
errors.push('ManifestCompiler is not exported correctly');
|
|
739
|
+
}
|
|
740
|
+
} else {
|
|
741
|
+
errors.push('.agents/compiler/manifest-compiler.js missing');
|
|
742
|
+
}
|
|
743
|
+
} catch (err) {
|
|
744
|
+
errors.push(`ManifestCompiler import failed: ${err.message}`);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (errors.length > 0) {
|
|
748
|
+
return {
|
|
749
|
+
id: 'module_execution',
|
|
750
|
+
status: STATUS.FAIL,
|
|
751
|
+
ok: false,
|
|
752
|
+
message: errors.join('; '),
|
|
753
|
+
errors,
|
|
754
|
+
remediation: 'Reinstall package via `npx contextos-agents init --force` to restore core modules',
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
return {
|
|
759
|
+
id: 'module_execution',
|
|
760
|
+
status: STATUS.PASS,
|
|
761
|
+
ok: true,
|
|
762
|
+
message: 'all core modules verified (resolver, rules, compiler)',
|
|
763
|
+
remediation: null,
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
|
|
683
767
|
function inspectSkills(projectDir, activeProfile) {
|
|
684
768
|
const skillsDir = path.join(projectDir, '.agents', 'core', 'skills');
|
|
685
769
|
if (!fs.existsSync(skillsDir)) return { total: 0, active: 0, excluded: 0, byDomain: { frontend: [], backend: [], cross: [] } };
|
|
@@ -782,9 +866,27 @@ function applyDoctorFix(projectDir = process.cwd(), options = {}) {
|
|
|
782
866
|
function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
783
867
|
const version = (() => {
|
|
784
868
|
try {
|
|
785
|
-
|
|
869
|
+
const parentPkgPath = path.join(__dirname, '..', 'package.json');
|
|
870
|
+
if (fs.existsSync(parentPkgPath)) {
|
|
871
|
+
const parentPkg = JSON.parse(fs.readFileSync(parentPkgPath, 'utf8'));
|
|
872
|
+
if (parentPkg.name === 'contextos-agents' || parentPkg.name === 'contextos') {
|
|
873
|
+
return parentPkg.version;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
const lockfileV2Path = path.join(projectDir, '.agents', 'lockfile.v2.json');
|
|
877
|
+
if (fs.existsSync(lockfileV2Path)) {
|
|
878
|
+
const lf = JSON.parse(fs.readFileSync(lockfileV2Path, 'utf8'));
|
|
879
|
+
if (lf.package && lf.package.compilerVersion) return lf.package.compilerVersion;
|
|
880
|
+
}
|
|
881
|
+
const lockfilePath = path.join(projectDir, '.agents', 'lockfile.json');
|
|
882
|
+
if (fs.existsSync(lockfilePath)) {
|
|
883
|
+
const lf = JSON.parse(fs.readFileSync(lockfilePath, 'utf8'));
|
|
884
|
+
if (lf.generator && lf.generator.version) return lf.generator.version;
|
|
885
|
+
if (lf.version) return lf.version;
|
|
886
|
+
}
|
|
887
|
+
return '2.0.0';
|
|
786
888
|
} catch {
|
|
787
|
-
return '
|
|
889
|
+
return '2.0.0';
|
|
788
890
|
}
|
|
789
891
|
})();
|
|
790
892
|
|
|
@@ -811,6 +913,18 @@ function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
|
811
913
|
errors.push('.agents/ directory missing (run: npx contextos-agents init)');
|
|
812
914
|
}
|
|
813
915
|
|
|
916
|
+
const moduleExecCheck = hasAgents ? checkModuleExecution(projectDir) : {
|
|
917
|
+
id: 'module_execution',
|
|
918
|
+
status: STATUS.FAIL,
|
|
919
|
+
ok: false,
|
|
920
|
+
message: '.agents directory missing',
|
|
921
|
+
remediation: 'Run: npx contextos-agents init',
|
|
922
|
+
};
|
|
923
|
+
checks.push(moduleExecCheck);
|
|
924
|
+
if (!moduleExecCheck.ok) {
|
|
925
|
+
errors.push(moduleExecCheck.message);
|
|
926
|
+
}
|
|
927
|
+
|
|
814
928
|
const nodeCheck = checkNodeVersion();
|
|
815
929
|
checks.push(nodeCheck);
|
|
816
930
|
if (!nodeCheck.ok) errors.push(nodeCheck.message);
|
|
@@ -947,6 +1061,11 @@ function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
|
947
1061
|
console.log('│ ✗ .agents/ directory missing (run: npx contextos-agents) │');
|
|
948
1062
|
}
|
|
949
1063
|
|
|
1064
|
+
// Execution check
|
|
1065
|
+
const execIcon = moduleExecCheck.ok ? '✓' : '✗';
|
|
1066
|
+
const execStr = `Core modules: ${moduleExecCheck.message}`;
|
|
1067
|
+
console.log(`│ ${execIcon} ${execStr.slice(0, 58).padEnd(58)}│`);
|
|
1068
|
+
|
|
950
1069
|
// Node version
|
|
951
1070
|
const nodeIcon = nodeCheck.ok ? '✓' : '✗';
|
|
952
1071
|
console.log(`│ ${nodeIcon} ${nodeCheck.message.padEnd(58)}│`);
|
|
@@ -990,7 +1109,7 @@ function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
|
990
1109
|
if (mcpCheck.status === STATUS.PASS) {
|
|
991
1110
|
console.log('│ ✓ MCP server: installed (.agents/mcp/server.mjs) │');
|
|
992
1111
|
} else {
|
|
993
|
-
console.log('│ • MCP server: not installed (run:
|
|
1112
|
+
console.log('│ • MCP server: not installed (run: npm i -D @contextos/mcp) │');
|
|
994
1113
|
}
|
|
995
1114
|
|
|
996
1115
|
// Pre-commit hook
|
|
@@ -1109,5 +1228,7 @@ module.exports = {
|
|
|
1109
1228
|
checkSandboxAvailability,
|
|
1110
1229
|
checkClaimEvidence,
|
|
1111
1230
|
checkProjectLock,
|
|
1231
|
+
checkModuleExecution,
|
|
1232
|
+
isContextOSDevRepo,
|
|
1112
1233
|
inspectSkills,
|
|
1113
1234
|
};
|
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',
|