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 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
@@ -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
- return require('../package.json').version;
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 '1.7.1';
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: contextos setup-mcp) │');
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
  };
@@ -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',