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 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
- All skills live in `.agents/core/skills/`. Here is what each does and when to use it:
40
+ ### Built-in Core Skills (Bundled in `.agents/core/skills/`)
41
41
 
42
- ### Core Skills (Always Considered)
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
- ### Frontend Skills
54
+ ### Extended Catalog Skills (Installed on Demand via `contextos skill add <name>`)
54
55
 
55
- | Skill | File | Activate When |
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
- ### Cross-Cutting Skills
85
-
86
- | Skill | File | Activate When |
87
- |-------|------|--------------|
88
- | **security** | `security/SKILL.md` | Any feature with auth, data access, user input |
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
- **PROACTIVE ROUTING RULE**: If a user request matches a skill trigger, DO NOT simply answer them ad-hoc. You must proactively load the skill and follow its workflow. Skills contain multi-step workflows, checklists, and quality gates that always produce better results than an unstructured response.
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
- const { ThreadStore } = require('./runtime/thread-store.js');
804
- const { evaluateMergeReadiness } = require('./runtime/state-machine.js');
805
- const store = new ThreadStore({ baseDir: process.cwd() });
806
- const subcmd = args[1] || 'list';
807
- const isJson = args.includes('--json');
808
-
809
- if (subcmd === 'list') {
810
- const list = store.list();
811
- if (isJson) {
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 18 for Core, Node 20 for Runtime)
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 >= 18.0.0 from https://nodejs.org',
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
- const minor = parseInt(match[2], 10);
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} (< 18.0.0, Node 18 required for Core)`,
94
- remediation: 'Upgrade Node.js to >= 18.0.0 (recommended >= 20.0.0 for ContextOS Runtime)',
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} (>= 20.0.0, optimal)`,
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
- return require('../package.json').version;
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 '1.7.1';
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: contextos setup-mcp) │');
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
  };
@@ -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
- /base64\s*,\s*[A-Za-z0-9+/=]{40,}/i,
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',