`, `
` |
+| Text | Text content | Text inside elements |
+| Comment | HTML comment | `` |
+| Document | Root document | `document` |
+| DocumentFragment | Lightweight document | For batch operations |
+
+### Selecting Elements
+
+```javascript
+// By ID
+const element = document.getElementById('myId');
+
+// By class name (returns HTMLCollection)
+const elements = document.getElementsByClassName('myClass');
+
+// By tag name (returns HTMLCollection)
+const divs = document.getElementsByTagName('div');
+
+// Query selector (first match)
+const first = document.querySelector('.myClass');
+const advanced = document.querySelector('div.container > p:first-child');
+
+// Query selector all (returns NodeList)
+const all = document.querySelectorAll('.myClass');
+
+// Special selectors
+document.body; // Body element
+document.head; // Head element
+document.documentElement; // element
+```
+
+### Traversing the DOM
+
+```javascript
+const element = document.querySelector('#myElement');
+
+// Parent
+element.parentElement;
+element.parentNode;
+
+// Children
+element.children; // HTMLCollection of child elements
+element.childNodes; // NodeList of all child nodes
+element.firstElementChild;
+element.lastElementChild;
+
+// Siblings
+element.nextElementSibling;
+element.previousElementSibling;
+
+// Closest ancestor matching selector
+element.closest('.container');
+
+// Check if element contains another
+parent.contains(child); // true/false
+```
+
+### Creating and Modifying Elements
+
+```javascript
+// Create element
+const div = document.createElement('div');
+const text = document.createTextNode('Hello');
+const fragment = document.createDocumentFragment();
+
+// Set content
+div.textContent = 'Plain text'; // Safe (escaped)
+div.innerHTML = 'HTML'; // Can be unsafe with user input
+
+// Set attributes
+div.setAttribute('id', 'myDiv');
+div.setAttribute('class', 'container');
+div.id = 'myDiv'; // Direct property
+div.className = 'container';
+div.classList.add('active');
+div.classList.remove('inactive');
+div.classList.toggle('visible');
+div.classList.contains('active'); // true/false
+
+// Set styles
+div.style.color = 'red';
+div.style.backgroundColor = 'blue';
+div.style.cssText = 'color: red; background: blue;';
+
+// Data attributes
+div.dataset.userId = '123'; // Sets data-user-id="123"
+div.getAttribute('data-user-id'); // "123"
+
+// Insert into DOM
+parent.appendChild(div); // Add as last child
+parent.insertBefore(div, referenceNode); // Insert before reference
+parent.prepend(div); // Add as first child (modern)
+parent.append(div); // Add as last child (modern)
+element.after(div); // Insert after element
+element.before(div); // Insert before element
+element.replaceWith(newElement); // Replace element
+
+// Remove from DOM
+element.remove(); // Modern way
+parent.removeChild(element); // Old way
+
+// Clone element
+const clone = element.cloneNode(true); // true = deep clone (with children)
+```
+
+### Element Properties
+
+```javascript
+// Dimensions and position
+element.offsetWidth; // Width including border
+element.offsetHeight; // Height including border
+element.clientWidth; // Width excluding border
+element.clientHeight; // Height excluding border
+element.scrollWidth; // Total scrollable width
+element.scrollHeight; // Total scrollable height
+element.offsetTop; // Top position relative to offsetParent
+element.offsetLeft; // Left position relative to offsetParent
+
+// Bounding box
+const rect = element.getBoundingClientRect();
+// Returns: { x, y, width, height, top, right, bottom, left }
+
+// Scroll position
+element.scrollTop; // Vertical scroll position
+element.scrollLeft; // Horizontal scroll position
+element.scrollTo(0, 100); // Scroll to position
+element.scrollIntoView(); // Scroll element into view
+
+// Check visibility
+element.checkVisibility(); // Modern API
+```
+
+## Event Handling
+
+### Adding Event Listeners
+
+```javascript
+// addEventListener (modern, recommended)
+element.addEventListener('click', handleClick);
+element.addEventListener('click', handleClick, { once: true }); // Remove after first trigger
+
+function handleClick(event) {
+ console.log('Clicked!', event);
+}
+
+// Event options
+element.addEventListener('scroll', handleScroll, {
+ passive: true, // Won't call preventDefault()
+ capture: false, // Bubble phase (default)
+ once: true // Remove after one call
+});
+
+// Remove event listener
+element.removeEventListener('click', handleClick);
+```
+
+### Common Events
+
+| Category | Events |
+|----------|--------|
+| Mouse | `click`, `dblclick`, `mousedown`, `mouseup`, `mousemove`, `mouseenter`, `mouseleave`, `contextmenu` |
+| Keyboard | `keydown`, `keyup`, `keypress` (deprecated) |
+| Form | `submit`, `change`, `input`, `focus`, `blur`, `invalid` |
+| Window | `load`, `DOMContentLoaded`, `resize`, `scroll`, `beforeunload`, `unload` |
+| Touch | `touchstart`, `touchmove`, `touchend`, `touchcancel` |
+| Drag | `drag`, `dragstart`, `dragend`, `dragover`, `drop` |
+| Media | `play`, `pause`, `ended`, `timeupdate`, `loadeddata` |
+| Animation | `animationstart`, `animationend`, `animationiteration` |
+| Transition | `transitionstart`, `transitionend` |
+
+### Event Object
+
+```javascript
+element.addEventListener('click', (event) => {
+ // Target elements
+ event.target; // Element that triggered event
+ event.currentTarget; // Element with listener attached
+
+ // Mouse position
+ event.clientX; // X relative to viewport
+ event.clientY; // Y relative to viewport
+ event.pageX; // X relative to document
+ event.pageY; // Y relative to document
+
+ // Keyboard
+ event.key; // 'a', 'Enter', 'ArrowUp'
+ event.code; // 'KeyA', 'Enter', 'ArrowUp'
+ event.ctrlKey; // true if Ctrl pressed
+ event.shiftKey; // true if Shift pressed
+ event.altKey; // true if Alt pressed
+ event.metaKey; // true if Meta/Cmd pressed
+
+ // Control event flow
+ event.preventDefault(); // Prevent default action
+ event.stopPropagation(); // Stop bubbling
+ event.stopImmediatePropagation(); // Stop other listeners
+});
+```
+
+### Event Delegation
+
+Handle events on parent instead of individual children:
+
+```javascript
+// Instead of adding listener to each button
+document.querySelector('.container').addEventListener('click', (event) => {
+ if (event.target.matches('button')) {
+ console.log('Button clicked:', event.target);
+ }
+});
+```
+
+## Web Storage APIs
+
+### LocalStorage
+
+Persistent storage (no expiration):
+
+```javascript
+// Set item
+localStorage.setItem('key', 'value');
+localStorage.setItem('user', JSON.stringify({ name: 'John' }));
+
+// Get item
+const value = localStorage.getItem('key');
+const user = JSON.parse(localStorage.getItem('user'));
+
+// Remove item
+localStorage.removeItem('key');
+
+// Clear all
+localStorage.clear();
+
+// Get key by index
+localStorage.key(0);
+
+// Number of items
+localStorage.length;
+
+// Iterate all items
+for (let i = 0; i < localStorage.length; i++) {
+ const key = localStorage.key(i);
+ const value = localStorage.getItem(key);
+ console.log(key, value);
+}
+```
+
+### SessionStorage
+
+Storage cleared when tab closes:
+
+```javascript
+// Same API as localStorage
+sessionStorage.setItem('key', 'value');
+sessionStorage.getItem('key');
+sessionStorage.removeItem('key');
+sessionStorage.clear();
+```
+
+**Storage Limits**: ~5-10MB per origin
+
+## Fetch API
+
+Modern API for HTTP requests:
+
+```javascript
+// Basic GET request
+fetch('https://api.example.com/data')
+ .then(response => response.json())
+ .then(data => console.log(data))
+ .catch(error => console.error(error));
+
+// Async/await
+async function fetchData() {
+ try {
+ const response = await fetch('https://api.example.com/data');
+
+ // Check if successful
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const data = await response.json();
+ return data;
+ } catch (error) {
+ console.error('Fetch error:', error);
+ }
+}
+
+// POST request with JSON
+fetch('https://api.example.com/users', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ name: 'John', age: 30 })
+})
+ .then(response => response.json())
+ .then(data => console.log(data));
+
+// With various options
+fetch(url, {
+ method: 'GET', // GET, POST, PUT, DELETE, etc.
+ headers: {
+ 'Authorization': 'Bearer token',
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(data), // For POST/PUT
+ mode: 'cors', // cors, no-cors, same-origin
+ credentials: 'include', // include, same-origin, omit
+ cache: 'no-cache', // default, no-cache, reload, force-cache
+ redirect: 'follow', // follow, error, manual
+ referrerPolicy: 'no-referrer' // no-referrer, origin, etc.
+});
+
+// Response methods
+const text = await response.text(); // Plain text
+const json = await response.json(); // JSON
+const blob = await response.blob(); // Binary data
+const arrayBuffer = await response.arrayBuffer(); // ArrayBuffer
+const formData = await response.formData(); // FormData
+```
+
+## Other Important Web APIs
+
+### Console API
+
+```javascript
+console.log('Message'); // Log message
+console.error('Error'); // Error message (red)
+console.warn('Warning'); // Warning message (yellow)
+console.info('Info'); // Info message
+console.table([{ a: 1 }, { a: 2 }]); // Table format
+console.group('Group'); // Start group
+console.groupEnd(); // End group
+console.time('timer'); // Start timer
+console.timeEnd('timer'); // End timer and log duration
+console.clear(); // Clear console
+console.assert(condition, 'Error message'); // Assert condition
+```
+
+### Timers
+
+```javascript
+// Execute once after delay
+const timeoutId = setTimeout(() => {
+ console.log('Executed after 1 second');
+}, 1000);
+
+// Cancel timeout
+clearTimeout(timeoutId);
+
+// Execute repeatedly
+const intervalId = setInterval(() => {
+ console.log('Executed every second');
+}, 1000);
+
+// Cancel interval
+clearInterval(intervalId);
+
+// RequestAnimationFrame (for animations)
+function animate() {
+ // Animation code
+ requestAnimationFrame(animate);
+}
+requestAnimationFrame(animate);
+```
+
+### URL API
+
+```javascript
+const url = new URL('https://example.com:8080/path?query=value#hash');
+
+url.protocol; // 'https:'
+url.hostname; // 'example.com'
+url.port; // '8080'
+url.pathname; // '/path'
+url.search; // '?query=value'
+url.hash; // '#hash'
+url.href; // Full URL
+
+// URL parameters
+url.searchParams.get('query'); // 'value'
+url.searchParams.set('newParam', 'newValue');
+url.searchParams.append('query', 'another');
+url.searchParams.delete('query');
+url.searchParams.has('query'); // true/false
+
+// Convert to string
+url.toString(); // Full URL
+```
+
+### FormData API
+
+```javascript
+// Create FormData from form
+const form = document.querySelector('form');
+const formData = new FormData(form);
+
+// Create FormData manually
+const data = new FormData();
+data.append('username', 'john');
+data.append('file', fileInput.files[0]);
+
+// Get values
+data.get('username'); // 'john'
+data.getAll('files'); // Array of all 'files' values
+
+// Iterate
+for (const [key, value] of data.entries()) {
+ console.log(key, value);
+}
+
+// Send with fetch
+fetch('/api/upload', {
+ method: 'POST',
+ body: formData // Don't set Content-Type header
+});
+```
+
+### Intersection Observer API
+
+Detect when element enters viewport:
+
+```javascript
+const observer = new IntersectionObserver((entries) => {
+ entries.forEach(entry => {
+ if (entry.isIntersecting) {
+ console.log('Element is visible');
+ entry.target.classList.add('visible');
+ }
+ });
+}, {
+ threshold: 0.5, // 50% visible
+ rootMargin: '0px'
+});
+
+observer.observe(element);
+observer.unobserve(element);
+observer.disconnect(); // Stop observing all
+```
+
+### Mutation Observer API
+
+Watch for DOM changes:
+
+```javascript
+const observer = new MutationObserver((mutations) => {
+ mutations.forEach(mutation => {
+ console.log('DOM changed:', mutation.type);
+ });
+});
+
+observer.observe(element, {
+ attributes: true, // Watch attribute changes
+ childList: true, // Watch child elements
+ subtree: true, // Watch all descendants
+ characterData: true // Watch text content
+});
+
+observer.disconnect(); // Stop observing
+```
+
+### Geolocation API
+
+```javascript
+navigator.geolocation.getCurrentPosition(
+ (position) => {
+ console.log(position.coords.latitude);
+ console.log(position.coords.longitude);
+ },
+ (error) => {
+ console.error('Error getting location:', error);
+ },
+ {
+ enableHighAccuracy: true,
+ timeout: 5000,
+ maximumAge: 0
+ }
+);
+
+// Watch position (continuous updates)
+const watchId = navigator.geolocation.watchPosition(callback);
+navigator.geolocation.clearWatch(watchId);
+```
+
+### Web Workers
+
+Run JavaScript in background thread:
+
+```javascript
+// Main thread
+const worker = new Worker('worker.js');
+
+worker.postMessage({ data: 'Hello' });
+
+worker.onmessage = (event) => {
+ console.log('From worker:', event.data);
+};
+
+worker.onerror = (error) => {
+ console.error('Worker error:', error);
+};
+
+worker.terminate(); // Stop worker
+
+// worker.js
+self.onmessage = (event) => {
+ console.log('From main:', event.data);
+ self.postMessage({ result: 'Done' });
+};
+```
+
+### Canvas API
+
+Draw graphics:
+
+```javascript
+const canvas = document.querySelector('canvas');
+const ctx = canvas.getContext('2d');
+
+// Draw rectangle
+ctx.fillStyle = 'blue';
+ctx.fillRect(10, 10, 100, 50);
+
+// Draw circle
+ctx.beginPath();
+ctx.arc(100, 100, 50, 0, Math.PI * 2);
+ctx.fillStyle = 'red';
+ctx.fill();
+
+// Draw text
+ctx.font = '20px Arial';
+ctx.fillText('Hello', 10, 50);
+
+// Draw image
+const img = new Image();
+img.onload = () => {
+ ctx.drawImage(img, 0, 0);
+};
+img.src = 'image.jpg';
+```
+
+### IndexedDB
+
+Client-side database for large amounts of structured data:
+
+```javascript
+// Open database
+const request = indexedDB.open('MyDatabase', 1);
+
+request.onerror = () => console.error('Database error');
+
+request.onsuccess = (event) => {
+ const db = event.target.result;
+ // Use database
+};
+
+request.onupgradeneeded = (event) => {
+ const db = event.target.result;
+ const objectStore = db.createObjectStore('users', { keyPath: 'id' });
+ objectStore.createIndex('name', 'name', { unique: false });
+};
+
+// Add data
+const transaction = db.transaction(['users'], 'readwrite');
+const objectStore = transaction.objectStore('users');
+objectStore.add({ id: 1, name: 'John' });
+
+// Get data
+const request = objectStore.get(1);
+request.onsuccess = () => console.log(request.result);
+```
+
+## Best Practices
+
+### Do's
+- ✅ Use `addEventListener` over inline event handlers
+- ✅ Remove event listeners when no longer needed
+- ✅ Use event delegation for dynamic content
+- ✅ Cache DOM queries in variables
+- ✅ Use `textContent` for plain text (safer than `innerHTML`)
+- ✅ Use DocumentFragment for batch DOM operations
+- ✅ Debounce/throttle scroll and resize handlers
+- ✅ Use `requestAnimationFrame` for animations
+- ✅ Validate and sanitize user input
+
+### Don'ts
+- ❌ Use `innerHTML` with untrusted data (XSS risk)
+- ❌ Query DOM repeatedly in loops
+- ❌ Modify DOM in tight loops (batch operations)
+- ❌ Use `document.write()` (deprecated)
+- ❌ Use synchronous XMLHttpRequest
+- ❌ Store sensitive data in localStorage
+- ❌ Ignore error handling in async code
+- ❌ Block main thread with heavy computations
+
+## Glossary Terms
+
+**Key Terms Covered**:
+- API
+- Application context
+- Beacon
+- Blink
+- Blink element
+- Browser
+- Browsing context
+- Buffer
+- Canvas
+- DOM (Document Object Model)
+- Document environment
+- Event
+- Expando
+- Global object
+- Global scope
+- Hoisting
+- IndexedDB
+- Interpolation
+- Node (DOM)
+- Shadow tree
+- WindowProxy
+- Wrapper
+
+## Additional Resources
+
+- [MDN DOM Reference](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)
+- [MDN Web APIs](https://developer.mozilla.org/en-US/docs/Web/API)
+- [JavaScript.info DOM](https://javascript.info/document)
diff --git a/plugins/cms-development/skills/web-coder/references/web-protocols-standards.md b/plugins/cms-development/skills/web-coder/references/web-protocols-standards.md
new file mode 100644
index 000000000..150bf97d6
--- /dev/null
+++ b/plugins/cms-development/skills/web-coder/references/web-protocols-standards.md
@@ -0,0 +1,265 @@
+# Web Protocols & Standards Reference
+
+Organizations, specifications, and standards that govern the web.
+
+## Standards Organizations
+
+### W3C (World Wide Web Consortium)
+
+International community developing web standards.
+
+**Key Standards**:
+- HTML
+- CSS
+- XML
+- SVG
+- WCAG (Accessibility)
+- Web APIs
+
+**Website**: https://www.w3.org/
+
+### WHATWG (Web Hypertext Application Technology Working Group)
+
+Community maintaining HTML and DOM Living Standards.
+
+**Key Standards**:
+- HTML Living Standard
+- DOM Living Standard
+- Fetch Standard
+- URL Standard
+
+**Website**: https://whatwg.org/
+
+### IETF (Internet Engineering Task Force)
+
+Develops internet standards.
+
+**Key Standards**:
+- HTTP
+- TLS
+- TCP/IP
+- DNS
+- WebRTC protocols
+
+**Website**: https://www.ietf.org/
+
+### ECMA International
+
+Standards organization for information systems.
+
+**Key Standards**:
+- ECMAScript (JavaScript)
+- JSON
+
+**Website**: https://www.ecma-international.org/
+
+### TC39 (Technical Committee 39)
+
+ECMAScript standardization committee.
+
+**Proposal Stages**:
+- **Stage 0**: Strawperson
+- **Stage 1**: Proposal
+- **Stage 2**: Draft
+- **Stage 3**: Candidate
+- **Stage 4**: Finished (included in next version)
+
+### IANA (Internet Assigned Numbers Authority)
+
+Coordinates internet protocol resources.
+
+**Responsibilities**:
+- MIME types
+- Port numbers
+- Protocol parameters
+- TLDs (Top-Level Domains)
+
+### ICANN (Internet Corporation for Assigned Names and Numbers)
+
+Coordinates DNS and IP addresses.
+
+## Web Standards
+
+### HTML Standards
+
+**HTML5 Features**:
+- Semantic elements (``, ``, etc.)
+- Audio and video elements
+- Canvas and SVG
+- Form enhancements
+- LocalStorage and SessionStorage
+- Web Workers
+- Geolocation API
+
+### CSS Specifications
+
+**CSS Modules** (each specification is a module):
+- CSS Selectors Level 4
+- CSS Flexbox Level 1
+- CSS Grid Level 2
+- CSS Animations
+- CSS Transitions
+- CSS Custom Properties
+
+### JavaScript Standards
+
+**ECMAScript Versions**:
+- **ES5** (2009): Strict mode, JSON
+- **ES6/ES2015**: Classes, modules, arrow functions, promises
+- **ES2016**: Array.includes(), exponentiation operator (`**`)
+- **ES2017**: async/await, Object.values/entries
+- **ES2018**: Rest/spread for objects, async iteration
+- **ES2019**: Array.flat(), Object.fromEntries
+- **ES2020**: Optional chaining, nullish coalescing, BigInt
+- **ES2021**: Logical assignment, Promise.any
+- **ES2022**: Top-level await, class fields
+- **ES2023**: Array.findLast(), Object.groupBy
+
+### Web API Specifications
+
+**Common APIs**:
+- DOM (Document Object Model)
+- Fetch API
+- Service Workers
+- Web Storage
+- IndexedDB
+- WebRTC
+- WebGL
+- Web Audio API
+- Payment Request API
+- Web Authentication API
+
+## Specifications
+
+### Normative vs Non-Normative
+
+- **Normative**: Required for compliance
+- **Non-normative**: Informative only (examples, notes)
+
+### Specification Lifecycle
+
+1. **Editor's Draft**: Work in progress
+2. **Working Draft**: Community review
+3. **Candidate Recommendation**: Implementation and testing
+4. **Proposed Recommendation**: Final review
+5. **W3C Recommendation**: Official standard
+
+## Browser Compatibility
+
+### Feature Detection
+
+```javascript
+// Check feature support
+if ('serviceWorker' in navigator) {
+ // Use service workers
+}
+
+if (window.IntersectionObserver) {
+ // Use Intersection Observer
+}
+
+if (CSS.supports('display', 'grid')) {
+ // Use CSS Grid
+}
+```
+
+### Baseline Compatibility
+
+Newly standardized features achieving widespread browser support.
+
+**Widely Available**: Firefox, Chrome, Edge, Safari support
+
+### Polyfills
+
+Code providing modern functionality in older browsers:
+
+```javascript
+// Promise polyfill
+if (!window.Promise) {
+ window.Promise = PromisePolyfill;
+}
+
+// Fetch polyfill
+if (!window.fetch) {
+ window.fetch = fetchPolyfill;
+}
+```
+
+### Progressive Enhancement
+
+Build for basic browsers, enhance for modern ones:
+
+```css
+/* Base styles */
+.container {
+ display: block;
+}
+
+/* Enhanced for Grid support */
+@supports (display: grid) {
+ .container {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+```
+
+## IDL (Interface Definition Language)
+
+**WebIDL**: Defines Web APIs
+
+```webidl
+interface Element : Node {
+ readonly attribute DOMString? tagName;
+ DOMString? getAttribute(DOMString qualifiedName);
+ undefined setAttribute(DOMString qualifiedName, DOMString value);
+};
+```
+
+## Specifications to Know
+
+- **HTML Living Standard**
+- **CSS Specifications** (modular)
+- **ECMAScript Language Specification**
+- **HTTP/1.1 (RFC 9112)**
+- **HTTP/2 (RFC 9113)**
+- **HTTP/3 (RFC 9114)**
+- **TLS 1.3 (RFC 8446)**
+- **WebSocket Protocol (RFC 6455)**
+- **CORS (Fetch Standard)**
+- **Service Workers**
+- **Web Authentication (WebAuthn)**
+
+## Glossary Terms
+
+**Key Terms Covered**:
+- Baseline (compatibility)
+- BCP 47 language tag
+- ECMA
+- ECMAScript
+- HTML5
+- IANA
+- ICANN
+- IDL
+- IETF
+- ISO
+- ITU
+- Non-normative
+- Normative
+- Polyfill
+- Shim
+- Specification
+- W3C
+- WAI
+- WCAG
+- WHATWG
+- Web standards
+- WebIDL
+
+## Additional Resources
+
+- [W3C Standards](https://www.w3.org/TR/)
+- [WHATWG Living Standards](https://spec.whatwg.org/)
+- [MDN Web Docs](https://developer.mozilla.org/)
+- [Can I Use](https://caniuse.com/)
+- [TC39 Proposals](https://github.com/tc39/proposals)
diff --git a/plugins/context-engineering/.github/plugin/plugin.json b/plugins/context-engineering/.github/plugin/plugin.json
index a1f49a3c6..39c8b433a 100644
--- a/plugins/context-engineering/.github/plugin/plugin.json
+++ b/plugins/context-engineering/.github/plugin/plugin.json
@@ -15,11 +15,11 @@
"architecture"
],
"agents": [
- "./agents/context-architect.md"
+ "./agents"
],
"skills": [
- "./skills/context-map/",
- "./skills/refactor-plan/",
- "./skills/what-context-needed/"
+ "./skills/context-map",
+ "./skills/refactor-plan",
+ "./skills/what-context-needed"
]
}
diff --git a/plugins/context-engineering/agents/context-architect.md b/plugins/context-engineering/agents/context-architect.md
new file mode 100644
index 000000000..2dc3d7197
--- /dev/null
+++ b/plugins/context-engineering/agents/context-architect.md
@@ -0,0 +1,60 @@
+---
+description: 'An agent that helps plan and execute multi-file changes by identifying relevant context and dependencies'
+model: 'GPT-5'
+tools: ['search/codebase', 'search/usages', 'read/problems', 'read/readFile', 'edit/editFiles', 'execute/runInTerminal', 'execute/getTerminalOutput', 'web/fetch']
+name: 'Context Architect'
+---
+
+You are a Context Architect—an expert at understanding codebases and planning changes that span multiple files.
+
+## Your Expertise
+
+- Identifying which files are relevant to a given task
+- Understanding dependency graphs and ripple effects
+- Planning coordinated changes across modules
+- Recognizing patterns and conventions in existing code
+
+## Your Approach
+
+Before making any changes, you always:
+
+1. **Map the context**: Identify all files that might be affected
+2. **Trace dependencies**: Find imports, exports, and type references
+3. **Check for patterns**: Look at similar existing code for conventions
+4. **Plan the sequence**: Determine the order changes should be made
+5. **Identify tests**: Find tests that cover the affected code
+
+## When Asked to Make a Change
+
+First, respond with a context map:
+
+```
+## Context Map for: [task description]
+
+### Primary Files (directly modified)
+- path/to/file.ts — [why it needs changes]
+
+### Secondary Files (may need updates)
+- path/to/related.ts — [relationship]
+
+### Test Coverage
+- path/to/test.ts — [what it tests]
+
+### Patterns to Follow
+- Reference: path/to/similar.ts — [what pattern to match]
+
+### Suggested Sequence
+1. [First change]
+2. [Second change]
+...
+```
+
+Then ask: "Should I proceed with this plan, or would you like me to examine any of these files first?"
+
+## Guidelines
+
+- Always search the codebase before assuming file locations
+- Prefer finding existing patterns over inventing new ones
+- Warn about breaking changes or ripple effects
+- If the scope is large, suggest breaking into smaller PRs
+- Never make changes without showing the context map first
diff --git a/plugins/context-engineering/skills/context-map/SKILL.md b/plugins/context-engineering/skills/context-map/SKILL.md
new file mode 100644
index 000000000..bb63c552f
--- /dev/null
+++ b/plugins/context-engineering/skills/context-map/SKILL.md
@@ -0,0 +1,52 @@
+---
+name: context-map
+description: 'Generate a map of all files relevant to a task before making changes'
+---
+
+# Context Map
+
+Before implementing any changes, analyze the codebase and create a context map.
+
+## Task
+
+{{task_description}}
+
+## Instructions
+
+1. Search the codebase for files related to this task
+2. Identify direct dependencies (imports/exports)
+3. Find related tests
+4. Look for similar patterns in existing code
+
+## Output Format
+
+```markdown
+## Context Map
+
+### Files to Modify
+| File | Purpose | Changes Needed |
+|------|---------|----------------|
+| path/to/file | description | what changes |
+
+### Dependencies (may need updates)
+| File | Relationship |
+|------|--------------|
+| path/to/dep | imports X from modified file |
+
+### Test Files
+| Test | Coverage |
+|------|----------|
+| path/to/test | tests affected functionality |
+
+### Reference Patterns
+| File | Pattern |
+|------|---------|
+| path/to/similar | example to follow |
+
+### Risk Assessment
+- [ ] Breaking changes to public API
+- [ ] Database migrations needed
+- [ ] Configuration changes required
+```
+
+Do not proceed with implementation until this map is reviewed.
diff --git a/plugins/context-engineering/skills/refactor-plan/SKILL.md b/plugins/context-engineering/skills/refactor-plan/SKILL.md
new file mode 100644
index 000000000..63bf42256
--- /dev/null
+++ b/plugins/context-engineering/skills/refactor-plan/SKILL.md
@@ -0,0 +1,65 @@
+---
+name: refactor-plan
+description: 'Plan a multi-file refactor with proper sequencing and rollback steps'
+---
+
+# Refactor Plan
+
+Create a detailed plan for this refactoring task.
+
+## Refactor Goal
+
+{{refactor_description}}
+
+## Instructions
+
+1. Search the codebase to understand current state
+2. Identify all affected files and their dependencies
+3. Plan changes in a safe sequence (types first, then implementations, then tests)
+4. Include verification steps between changes
+5. Consider rollback if something fails
+
+## Output Format
+
+```markdown
+## Refactor Plan: [title]
+
+### Current State
+[Brief description of how things work now]
+
+### Target State
+[Brief description of how things will work after]
+
+### Affected Files
+| File | Change Type | Dependencies |
+|------|-------------|--------------|
+| path | modify/create/delete | blocks X, blocked by Y |
+
+### Execution Plan
+
+#### Phase 1: Types and Interfaces
+- [ ] Step 1.1: [action] in `file.ts`
+- [ ] Verify: [how to check it worked]
+
+#### Phase 2: Implementation
+- [ ] Step 2.1: [action] in `file.ts`
+- [ ] Verify: [how to check]
+
+#### Phase 3: Tests
+- [ ] Step 3.1: Update tests in `file.test.ts`
+- [ ] Verify: Run `npm test`
+
+#### Phase 4: Cleanup
+- [ ] Remove deprecated code
+- [ ] Update documentation
+
+### Rollback Plan
+If something fails:
+1. [Step to undo]
+2. [Step to undo]
+
+### Risks
+- [Potential issue and mitigation]
+```
+
+Shall I proceed with Phase 1?
diff --git a/plugins/context-engineering/skills/what-context-needed/SKILL.md b/plugins/context-engineering/skills/what-context-needed/SKILL.md
new file mode 100644
index 000000000..9088640d9
--- /dev/null
+++ b/plugins/context-engineering/skills/what-context-needed/SKILL.md
@@ -0,0 +1,39 @@
+---
+name: what-context-needed
+description: 'Ask Copilot what files it needs to see before answering a question'
+---
+
+# What Context Do You Need?
+
+Before answering my question, tell me what files you need to see.
+
+## My Question
+
+{{question}}
+
+## Instructions
+
+1. Based on my question, list the files you would need to examine
+2. Explain why each file is relevant
+3. Note any files you've already seen in this conversation
+4. Identify what you're uncertain about
+
+## Output Format
+
+```markdown
+## Files I Need
+
+### Must See (required for accurate answer)
+- `path/to/file.ts` — [why needed]
+
+### Should See (helpful for complete answer)
+- `path/to/file.ts` — [why helpful]
+
+### Already Have
+- `path/to/file.ts` — [from earlier in conversation]
+
+### Uncertainties
+- [What I'm not sure about without seeing the code]
+```
+
+After I provide these files, I'll ask my question again.
diff --git a/plugins/context-matic/.github/plugin/plugin.json b/plugins/context-matic/.github/plugin/plugin.json
index e5c1decdd..5030c4564 100644
--- a/plugins/context-matic/.github/plugin/plugin.json
+++ b/plugins/context-matic/.github/plugin/plugin.json
@@ -19,7 +19,7 @@
"repository": "https://github.com/github/awesome-copilot",
"license": "MIT",
"skills": [
- "./skills/integrate-context-matic/",
- "./skills/onboard-context-matic/"
+ "./skills/integrate-context-matic",
+ "./skills/onboard-context-matic"
]
}
diff --git a/plugins/context-matic/skills/integrate-context-matic/SKILL.md b/plugins/context-matic/skills/integrate-context-matic/SKILL.md
new file mode 100644
index 000000000..23a2b5e33
--- /dev/null
+++ b/plugins/context-matic/skills/integrate-context-matic/SKILL.md
@@ -0,0 +1,110 @@
+---
+name: integrate-context-matic
+description: 'Discovers and integrates third-party APIs using the context-matic MCP server. Uses `fetch_api` to find available API SDKs, `ask` for integration guidance, `model_search` and `endpoint_search` for SDK details. Use when the user asks to integrate a third-party API, add an API client, implement features with an external API, or work with any third-party API or SDK.'
+---
+
+# API Integration
+
+When the user asks to integrate a third-party API or implement anything involving an external API or SDK, follow this workflow. Do not rely on your own knowledge for available APIs or their capabilities — always use the context-matic MCP server.
+
+## When to Apply
+
+Apply this skill when the user:
+- Asks to integrate a third-party API
+- Wants to add a client or SDK for an external service
+- Requests implementation that depends on an external API
+- Mentions a specific API (e.g. PayPal, Twilio) and implementation or integration
+
+## Workflow
+
+### 1. Ensure Guidelines and Skills Exist
+
+#### 1a. Detect the Project's Primary Language
+
+Before checking for guidelines or skills, identify the project's primary programming language by inspecting the workspace:
+
+| File / Pattern | Language |
+|---|---|
+| `*.csproj`, `*.sln` | `csharp` |
+| `package.json` with `"typescript"` dep or `.ts` files | `typescript` |
+| `requirements.txt`, `pyproject.toml`, `*.py` | `python` |
+| `go.mod`, `*.go` | `go` |
+| `pom.xml`, `build.gradle`, `*.java` | `java` |
+| `Gemfile`, `*.rb` | `ruby` |
+| `composer.json`, `*.php` | `php` |
+
+Use the detected language in all subsequent steps wherever `language` is required.
+
+#### 1b. Check for Existing Guidelines and Skills
+
+Check whether guidelines and skills have already been added for this project by looking for their presence in the workspace.
+
+- `{language}-conventions` is the skill produced by **add_skills**.
+- `{language}-security-guidelines.md` and `{language}-test-guidelines.md` are language-specific guideline files produced by **add_guidelines**.
+- `update-activity-workflow.md` is a workflow guideline file produced by **add_guidelines** (it is not language-specific).
+- Check these independently. Do not treat the presence of one set as proof that the other set already exists.
+- **If any required guideline files for this project are missing:** Call **add_guidelines**.
+- **If `{language}-conventions` is missing for the project's language:** Call **add_skills**.
+- **If all required guideline files and `{language}-conventions` already exist:** Skip this step and proceed to step 2.
+
+### 2. Discover Available APIs
+
+Call **fetch_api** to find available APIs — always start here.
+
+- Always provide the `language` parameter using the language detected in step 1a.
+- Always provide the `key` parameter: pass the API name/key from the user's request (e.g. `"paypal"`, `"twilio"`).
+- If the user did not provide an API name/key, ask them which API they want to integrate, then call `fetch_api` with that value.
+- The tool returns only the matching API on an exact match, or the full API catalog (name, description, and `key`) when there is no exact match.
+- Identify the API that matches the user's request based on the name and description.
+- Extract the correct `key` for the user's requested API before proceeding. This key will be used for all subsequent tool calls related to that API.
+
+**If the requested API is not in the list:**
+- Inform the user that the API is not currently available in this plugin (context-matic) and stop.
+- Request guidance from user on how to proceed with the API's integration.
+
+### 3. Get Integration Guidance
+
+- Provide `ask` with: `language`, `key` (from step 2), and your `query`.
+- Break complex questions into smaller focused queries for best results:
+ - _"How do I authenticate?"_
+ - _"How do I create a payment?"_
+ - _"What are the rate limits?"_
+
+### 4. Look Up SDK Models and Endpoints (as needed)
+
+These tools return definitions only — they do not call APIs or generate code.
+
+- **model_search** — look up a model/object definition.
+ - Provide: `language`, `key`, and an exact or partial case-sensitive model name as `query` (e.g. `availableBalance`, `TransactionId`).
+- **endpoint_search** — look up an endpoint method's details.
+ - Provide: `language`, `key`, and an exact or partial case-sensitive method name as `query` (e.g. `createUser`, `get_account_balance`).
+
+### 5. Record Milestones
+
+Call **update_activity** (with the appropriate `milestone`) whenever one of these is **concretely reached in code or infrastructure** — not merely mentioned or planned:
+
+| Milestone | When to pass it |
+|---|---|
+| `sdk_setup` | SDK package is installed in the project (e.g. `npm install`, `pip install`, `go get` has run and succeeded). |
+| `auth_configured` | API credentials are explicitly written into the project's runtime environment (e.g. present in a `.env` file, secrets manager, or config file) **and** referenced in actual code. |
+| `first_call_made` | First API call code written and executed |
+| `error_encountered` | Developer reports a bug, error response, or failing call |
+| `error_resolved` | Fix applied and API call confirmed working |
+
+## Checklist
+
+- [ ] Project's primary language detected (step 1a)
+- [ ] `add_guidelines` called if guideline files were missing, otherwise skipped
+- [ ] `add_skills` called if `{language}-conventions` was missing, otherwise skipped
+- [ ] `fetch_api` called with correct `language` and `key` (API name)
+- [ ] Correct `key` identified for the requested API (or user informed if not found)
+- [ ] `update_activity` called only when a milestone is concretely reached in code/infrastructure — never for questions, searches, or tool lookups
+- [ ] `update_activity` called with the appropriate `milestone` at each integration milestone
+- [ ] `ask` used for integration guidance and code samples
+- [ ] `model_search` / `endpoint_search` used as needed for SDK details
+- [ ] Project compiles after each code modification
+
+## Notes
+
+- **API not found**: If an API is missing from `fetch_api`, do not guess at SDK usage — inform the user that the API is not currently available in this plugin and stop.
+- **update_activity and fetch_api**: `fetch_api` is API discovery, not integration — do not call `update_activity` before it.
diff --git a/plugins/context-matic/skills/onboard-context-matic/SKILL.md b/plugins/context-matic/skills/onboard-context-matic/SKILL.md
new file mode 100644
index 000000000..16e4f2234
--- /dev/null
+++ b/plugins/context-matic/skills/onboard-context-matic/SKILL.md
@@ -0,0 +1,293 @@
+---
+name: onboard-context-matic
+description: 'Interactive onboarding tour for the context-matic MCP server. Walks the user through what the server does, shows all available APIs, lets them pick one to explore, explains it in their project language, demonstrates model_search and endpoint_search live, and ends with a menu of things the user can ask the agent to do. USE FOR: first-time setup; "what can this MCP do?"; "show me the available APIs"; "onboard me"; "how do I use the context-matic server"; "give me a tour". DO NOT USE FOR: actually integrating an API end-to-end (use integrate-context-matic instead).'
+---
+
+# Onboarding: ContextMatic MCP
+
+This skill delivers a guided, interactive tour of the `context-matic` MCP server. Follow every
+phase in order. Stop after each interaction point and wait for the user's reply before continuing.
+
+> **Agent conduct rules — follow throughout the entire skill:**
+> - **Never narrate the skill structure.** Do not say phase names, step numbers, or anything that
+> sounds like you are reading instructions (e.g., "In Phase 1 I will…", "Step 1a:", "As per the
+> skill…"). Deliver the tour as a natural conversation.
+> - **Announce every tool call before making it.** One short sentence is enough — tell the user
+> what you are about to look up and why, then call the tool. Example: *"Let me pull up the list
+> of available APIs for your project language."* This keeps the user informed and prevents
+> silent, unexplained pauses.
+
+---
+
+## Phase 0 — Opening statement and tool walkthrough
+
+Begin with a brief, plain-language explanation of what the server does. Say it in your own words
+based on the following facts:
+
+> The **context-matic** MCP server solves a fundamental problem with AI-assisted coding: general
+> models are trained on public code that is often outdated, incorrect, or missing entirely for newer
+> SDK versions. This server acts as a **live, version-aware grounding layer**. Instead of the agent
+> guessing at SDK usage from training data, it queries the server for the *exact* SDK models,
+> endpoints, auth patterns, and runnable code samples that match the current API version and the
+> project's programming language.
+
+After explaining the problem the server solves, walk through each of the four tools as if
+introducing them to someone using the server for the first time. For each tool, explain:
+- **What it is** — give it a memorable one-line description
+- **When you would use it** — a concrete, relatable scenario
+- **What it gives back** — the kind of output the user will see
+
+Use the following facts as your source, but say it conversationally — do not present a raw table:
+
+> | Tool | What it does | When to use it | What you get back |
+> |---|---|---|---|
+> | `fetch_api` | Returns an exact match for an API `key`/identifier and language, or lists all APIs for a given language. The `key` is the machine-readable identifier returned by `fetch_api` (for example, `paypal`), not the human-readable display name (for example, "PayPal Server SDK"). | "What APIs can I use?" / Starting a new project / "Do you have the PayPal SDK?" | A named list of available APIs with short descriptions (full catalog), or one exact API match when you provide its identifier/key and language |
+> | `ask` | Answers integration questions with version-accurate guidance and code samples | "How do I authenticate?", "Show me the quickstart", "What's the right way to do X?" | Step-by-step guidance and runnable code samples grounded in the actual SDK version |
+> | `model_search` | Looks up an SDK model/object definition and its typed properties | "What fields does an Order have?", "Is this property required?" | The model's name, description, and a full typed property list (required vs. optional, nested types) |
+> | `endpoint_search` | Looks up an endpoint method, its parameters, response type, and a runnable code sample | "Show me how to call createOrder", "What does getTrack return?" | Method signature, parameter types, response type, and a copy-paste-ready code sample |
+
+End this section by telling the user that you'll demonstrate the four core discovery and
+integration tools live during the tour, starting with `fetch_api` right now. Make it clear that
+this tour is focused on those core ContextMatic server tools rather than every possible helper the
+broader workflow might use.
+
+
+---
+
+## Phase 1 — Show available APIs
+
+### 1a. Detect the project language
+
+Before calling `fetch_api`, determine the project's primary language by inspecting workspace files:
+
+- Look for `package.json` + `.ts`/`.tsx` files → `typescript`
+- Look for `*.csproj` or `*.sln` → `csharp`
+- Look for `requirements.txt`, `pyproject.toml`, or `*.py` → `python`
+- Look for `pom.xml` or `build.gradle` → `java`
+- Look for `go.mod` → `go`
+- Look for `Gemfile` or `*.rb` → `ruby`
+- Look for `composer.json` or `*.php` → `php`
+- If no project files are found, silently fall back to `typescript`.
+
+Store the detected language — you will pass it to every subsequent tool call.
+
+### 1b. Fetch available APIs
+
+Tell the user which language you detected and that you are fetching the available APIs — for
+example: *"I can see this is a TypeScript project. Let me fetch the APIs available for TypeScript."*
+
+Call **`fetch_api`** with `language` = the detected language and `key` = "" so the tool returns the full list of available APIs.
+
+Display the results as a formatted list, showing each API's **name** and a one-sentence summary of
+its **description**. Do not truncate or skip any entry.
+
+Example display format (adapt to actual results):
+
+```
+Here are the APIs currently available through this server:
+
+1. PayPal Server SDK — Payments, orders, subscriptions, and vault via PayPal REST APIs.
+2. Spotify Web API — Music/podcast discovery, playback control, and library management.
+....
+```
+
+---
+
+## Phase 2 — API selection (interaction)
+
+Ask the user:
+
+> "Which of these APIs would you like to explore? Just say the name or the number."
+
+**Wait for the user's reply before continuing.**
+
+Store the chosen API's `key` value from the `fetch_api` response — you will pass it to all
+subsequent tool calls. Also note the API's name for use in explanatory text.
+
+---
+
+## Phase 3 — Explain the chosen API
+
+Before calling, say something like: *"Great choice — let me get an overview of [API name] for you."*
+
+Call **`ask`** with:
+- `key` = chosen API's key
+- `language` = detected language
+- `query` = `"Give me a high-level overview of this API: what it does, what the main controllers or
+ modules are, how authentication works, and what the first step to start using it is."`
+
+Present the response conversationally. Highlight:
+- What the API can do (use cases)
+- How authentication works (credentials, OAuth flows, etc.)
+- The main SDK controllers or namespaces
+- The NPM/pip/NuGet/etc. package name to install
+
+---
+
+## Phase 4 — Integration in the project language (interaction)
+
+Ask the user:
+
+> "Is there a specific part of the [API name] you want to learn how to use — for example,
+> creating an order, searching tracks, or managing subscriptions? Or should I show you
+> the complete integration quickstart?"
+
+**Wait for the user's reply.**
+
+Before calling, say something like: *"On it — let me look that up."* or *"Sure, let me pull up the quickstart."*
+
+Call **`ask`** with:
+- `key` = chosen API's key
+- `language` = detected language
+- `query` = the user's stated goal, or `"Show me a complete integration quickstart: install the
+ SDK, configure credentials, and make the first API call."` if they asked for the full guide.
+
+Present the response, including any code samples exactly as returned.
+
+---
+
+## Phase 5 — Demonstrate `model_search`
+
+Tell the user:
+
+> "Now let me show you how `model_search` works. This tool lets you look up any SDK model or
+> object definition — its typed properties, which are required vs. optional, and what types they use.
+> It works with partial, case-sensitive names."
+
+Before calling, say something like: *"Let me search for the `[model name]` model so you can see what the result looks like."*
+
+Pick a **representative model** from the chosen API (examples below) and call **`model_search`** with:
+- `key` = the previously chosen API key (for example, `paypal` or `spotify`)
+- `language` = the detected project language
+- `query` = the representative model name you picked
+
+| API key | Good demo query |
+|---|---|
+| `paypal` | `Order` |
+| `spotify` | `TrackObject` |
+
+Display the result, pointing out:
+- The exact model name and its description
+- A few interesting typed properties (highlight optional vs. required)
+- Any nested model references (e.g., `PurchaseUnit[] | undefined`)
+
+Tell the user:
+
+> "You can search any model by name — partial matches work too. Try asking me to look up a
+> specific model from [API name] whenever you need to know its shape."
+
+---
+
+## Phase 6 — Demonstrate `endpoint_search`
+
+Tell the user:
+
+> "Similarly, `endpoint_search` looks up any SDK method — the exact parameters, their types,
+> the response type, and a fully runnable code sample you can drop straight into your project."
+
+Before calling, say something like: *"Let me fetch the `[endpoint name]` endpoint so you can see the parameters and a live code sample."*
+
+Pick a **representative endpoint** for the chosen API and call **`endpoint_search`** with an explicit argument object:
+
+- `key` = the API key you are demonstrating (for example, `paypal` or `spotify`)
+- `query` = the endpoint / SDK method name you want to look up (for example, `createOrder` or `getTrack`)
+- `language` = the user's project language (for example, `"typescript"` or `"python"`)
+
+For example:
+
+| API key (`key`) | Endpoint name (`query`) | Example `language` |
+|---|---|---|
+| `paypal` | `createOrder` | user's project language |
+| `spotify` | `getTrack` | user's project language |
+Display the result, pointing out:
+- The method name and description
+- The request parameters and their types
+- The response type
+- The full code sample (present exactly as returned)
+
+Tell the user:
+
+> "Notice that the code sample is ready to use — it imports from the correct SDK, initialises
+> the client, calls the endpoint, and handles errors. You can search for any endpoint by its
+> method name or a partial case-sensitive fragment."
+
+---
+
+## Phase 7 — Closing: what you can ask
+
+End the tour with a summary list of things the user can now ask the agent to do. Present this as
+a formatted menu:
+
+---
+
+### What you can do with this MCP
+
+**Quickstart: your first API call**
+```
+/integrate-context-matic Set up the Spotify TypeScript SDK and fetch my top 5 tracks.
+Show me the complete client initialization and the API call.
+```
+```
+/integrate-context-matic How do I authenticate with the Twilio API and send an SMS?
+Give me the full PHP setup including the SDK client and the send call.
+```
+```
+/integrate-context-matic Walk me through initializing the Slack API client in a Python script and posting a message to a channel.
+```
+
+**Framework-specific integration**
+```
+/integrate-context-matic I'm building a Next.js app. Integrate the Google Maps Places API
+to search for nearby restaurants and display them on a page. Use the TypeScript SDK.
+```
+```
+/integrate-context-matic I'm using Laravel. Show me how to send a Twilio SMS when a user
+registers. Include the PHP SDK setup, client initialization, and the controller code.
+```
+```
+/integrate-context-matic I have an ASP.NET Core app. Add Twilio webhook handling so I can receive delivery status callbacks when an SMS is sent.
+```
+
+**Chaining tools for full integrations**
+```
+/integrate-context-matic I want to add real-time order shipping notifications to my
+Next.js store. Use Twilio to send an SMS when the order status changes to "shipped". Show me
+the full integration: SDK setup, the correct endpoint and its parameters, and the TypeScript code.
+```
+```
+/integrate-context-matic I need to post a Slack message every time a Spotify track changes
+in my playlist monitoring app. Walk me through integrating both APIs in TypeScript — start by
+discovering what's available, then show me the auth setup and the exact API calls.
+```
+```
+/integrate-context-matic In my ASP.NET Core app, I want to geocode user addresses using
+Google Maps and cache the results. Look up the geocode endpoint and response model, then
+generate the C# code including error handling.
+```
+
+**Debugging and error handling**
+```
+/integrate-context-matic My Spotify API call is returning 401. What OAuth flow should I
+be using and how does the TypeScript SDK handle token refresh automatically?
+```
+```
+/integrate-context-matic My Slack message posts are failing intermittently with rate limit
+errors. How does the Python SDK expose rate limit information and what's the recommended retry
+pattern?
+```
+
+---
+
+> "That's the tour! Ask me any of the above or just tell me what you want to build — I'll
+> use this server to give you accurate, version-specific guidance."
+
+---
+
+## Notes for the agent
+
+- If the user picks an API that is not in the `fetch_api` results, tell them it is not currently
+ available and offer to continue the tour with one that is.
+- All tool calls in this skill are **read-only** — they do not modify the project, install packages,
+ or write files unless the user explicitly asks you to proceed with integration.
+- When showing code samples from `endpoint_search` or `ask`, present them in fenced code blocks
+ with the correct language tag.
diff --git a/plugins/copilot-sdk/.github/plugin/plugin.json b/plugins/copilot-sdk/.github/plugin/plugin.json
index 42c166808..0739a17d2 100644
--- a/plugins/copilot-sdk/.github/plugin/plugin.json
+++ b/plugins/copilot-sdk/.github/plugin/plugin.json
@@ -19,6 +19,6 @@
"github-copilot"
],
"skills": [
- "./skills/copilot-sdk/"
+ "./skills/copilot-sdk"
]
}
diff --git a/plugins/copilot-sdk/skills/copilot-sdk/SKILL.md b/plugins/copilot-sdk/skills/copilot-sdk/SKILL.md
new file mode 100644
index 000000000..8cc7e86b6
--- /dev/null
+++ b/plugins/copilot-sdk/skills/copilot-sdk/SKILL.md
@@ -0,0 +1,914 @@
+---
+name: copilot-sdk
+description: Build agentic applications with GitHub Copilot SDK. Use when embedding AI agents in apps, creating custom tools, implementing streaming responses, managing sessions, connecting to MCP servers, or creating custom agents. Triggers on Copilot SDK, GitHub SDK, agentic app, embed Copilot, programmable agent, MCP server, custom agent.
+---
+
+# GitHub Copilot SDK
+
+Embed Copilot's agentic workflows in any application using Python, TypeScript, Go, or .NET.
+
+## Overview
+
+The GitHub Copilot SDK exposes the same engine behind Copilot CLI: a production-tested agent runtime you can invoke programmatically. No need to build your own orchestration - you define agent behavior, Copilot handles planning, tool invocation, file edits, and more.
+
+## Prerequisites
+
+1. **GitHub Copilot CLI** installed and authenticated ([Installation guide](https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli))
+2. **Language runtime**: Node.js 18+, Python 3.8+, Go 1.21+, or .NET 8.0+
+
+Verify CLI: `copilot --version`
+
+## Installation
+
+### Node.js/TypeScript
+```bash
+mkdir copilot-demo && cd copilot-demo
+npm init -y --init-type module
+npm install @github/copilot-sdk tsx
+```
+
+### Python
+```bash
+pip install github-copilot-sdk
+```
+
+### Go
+```bash
+mkdir copilot-demo && cd copilot-demo
+go mod init copilot-demo
+go get github.com/github/copilot-sdk/go
+```
+
+### .NET
+```bash
+dotnet new console -n CopilotDemo && cd CopilotDemo
+dotnet add package GitHub.Copilot.SDK
+```
+
+## Quick Start
+
+### TypeScript
+```typescript
+import { CopilotClient, approveAll } from "@github/copilot-sdk";
+
+const client = new CopilotClient();
+const session = await client.createSession({
+ onPermissionRequest: approveAll,
+ model: "gpt-4.1",
+});
+
+const response = await session.sendAndWait({ prompt: "What is 2 + 2?" });
+console.log(response?.data.content);
+
+await client.stop();
+process.exit(0);
+```
+
+Run: `npx tsx index.ts`
+
+### Python
+```python
+import asyncio
+from copilot import CopilotClient, PermissionHandler
+
+async def main():
+ client = CopilotClient()
+ await client.start()
+
+ session = await client.create_session({
+ "on_permission_request": PermissionHandler.approve_all,
+ "model": "gpt-4.1",
+ })
+ response = await session.send_and_wait({"prompt": "What is 2 + 2?"})
+
+ print(response.data.content)
+ await client.stop()
+
+asyncio.run(main())
+```
+
+### Go
+```go
+package main
+
+import (
+ "fmt"
+ "log"
+ "os"
+ copilot "github.com/github/copilot-sdk/go"
+)
+
+func main() {
+ client := copilot.NewClient(nil)
+ if err := client.Start(); err != nil {
+ log.Fatal(err)
+ }
+ defer client.Stop()
+
+ session, err := client.CreateSession(&copilot.SessionConfig{
+ OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
+ Model: "gpt-4.1",
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ response, err := session.SendAndWait(copilot.MessageOptions{Prompt: "What is 2 + 2?"}, 0)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ fmt.Println(*response.Data.Content)
+ os.Exit(0)
+}
+```
+
+### .NET (C#)
+```csharp
+using GitHub.Copilot.SDK;
+
+await using var client = new CopilotClient();
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ OnPermissionRequest = PermissionHandler.ApproveAll,
+ Model = "gpt-4.1",
+});
+
+var response = await session.SendAndWaitAsync(new MessageOptions { Prompt = "What is 2 + 2?" });
+Console.WriteLine(response?.Data.Content);
+```
+
+Run: `dotnet run`
+
+## Streaming Responses
+
+Enable real-time output for better UX:
+
+### TypeScript
+```typescript
+import { CopilotClient, approveAll, SessionEvent } from "@github/copilot-sdk";
+
+const client = new CopilotClient();
+const session = await client.createSession({
+ onPermissionRequest: approveAll,
+ model: "gpt-4.1",
+ streaming: true,
+});
+
+session.on((event: SessionEvent) => {
+ if (event.type === "assistant.message_delta") {
+ process.stdout.write(event.data.deltaContent);
+ }
+ if (event.type === "session.idle") {
+ console.log(); // New line when done
+ }
+});
+
+await session.sendAndWait({ prompt: "Tell me a short joke" });
+
+await client.stop();
+process.exit(0);
+```
+
+### Python
+```python
+import asyncio
+import sys
+from copilot import CopilotClient, PermissionHandler
+from copilot.generated.session_events import SessionEventType
+
+async def main():
+ client = CopilotClient()
+ await client.start()
+
+ session = await client.create_session({
+ "on_permission_request": PermissionHandler.approve_all,
+ "model": "gpt-4.1",
+ "streaming": True,
+ })
+
+ def handle_event(event):
+ if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
+ sys.stdout.write(event.data.delta_content)
+ sys.stdout.flush()
+ if event.type == SessionEventType.SESSION_IDLE:
+ print()
+
+ session.on(handle_event)
+ await session.send_and_wait({"prompt": "Tell me a short joke"})
+ await client.stop()
+
+asyncio.run(main())
+```
+
+### Go
+```go
+session, err := client.CreateSession(&copilot.SessionConfig{
+ OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
+ Model: "gpt-4.1",
+ Streaming: true,
+})
+
+session.On(func(event copilot.SessionEvent) {
+ if event.Type == "assistant.message_delta" {
+ fmt.Print(*event.Data.DeltaContent)
+ }
+ if event.Type == "session.idle" {
+ fmt.Println()
+ }
+})
+
+_, err = session.SendAndWait(copilot.MessageOptions{Prompt: "Tell me a short joke"}, 0)
+```
+
+### .NET
+```csharp
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ OnPermissionRequest = PermissionHandler.ApproveAll,
+ Model = "gpt-4.1",
+ Streaming = true,
+});
+
+session.On(ev =>
+{
+ if (ev is AssistantMessageDeltaEvent deltaEvent)
+ Console.Write(deltaEvent.Data.DeltaContent);
+ if (ev is SessionIdleEvent)
+ Console.WriteLine();
+});
+
+await session.SendAndWaitAsync(new MessageOptions { Prompt = "Tell me a short joke" });
+```
+
+## Custom Tools
+
+Define tools that Copilot can invoke during reasoning. When you define a tool, you tell Copilot:
+1. **What the tool does** (description)
+2. **What parameters it needs** (schema)
+3. **What code to run** (handler)
+
+### TypeScript (JSON Schema)
+```typescript
+import { CopilotClient, approveAll, defineTool, SessionEvent } from "@github/copilot-sdk";
+
+const getWeather = defineTool("get_weather", {
+ description: "Get the current weather for a city",
+ parameters: {
+ type: "object",
+ properties: {
+ city: { type: "string", description: "The city name" },
+ },
+ required: ["city"],
+ },
+ handler: async (args: { city: string }) => {
+ const { city } = args;
+ // In a real app, call a weather API here
+ const conditions = ["sunny", "cloudy", "rainy", "partly cloudy"];
+ const temp = Math.floor(Math.random() * 30) + 50;
+ const condition = conditions[Math.floor(Math.random() * conditions.length)];
+ return { city, temperature: `${temp}°F`, condition };
+ },
+});
+
+const client = new CopilotClient();
+const session = await client.createSession({
+ onPermissionRequest: approveAll,
+ model: "gpt-4.1",
+ streaming: true,
+ tools: [getWeather],
+});
+
+session.on((event: SessionEvent) => {
+ if (event.type === "assistant.message_delta") {
+ process.stdout.write(event.data.deltaContent);
+ }
+});
+
+await session.sendAndWait({
+ prompt: "What's the weather like in Seattle and Tokyo?",
+});
+
+await client.stop();
+process.exit(0);
+```
+
+### Python (Pydantic)
+```python
+import asyncio
+import random
+import sys
+from copilot import CopilotClient, PermissionHandler
+from copilot.tools import define_tool
+from copilot.generated.session_events import SessionEventType
+from pydantic import BaseModel, Field
+
+class GetWeatherParams(BaseModel):
+ city: str = Field(description="The name of the city to get weather for")
+
+@define_tool(description="Get the current weather for a city")
+async def get_weather(params: GetWeatherParams) -> dict:
+ city = params.city
+ conditions = ["sunny", "cloudy", "rainy", "partly cloudy"]
+ temp = random.randint(50, 80)
+ condition = random.choice(conditions)
+ return {"city": city, "temperature": f"{temp}°F", "condition": condition}
+
+async def main():
+ client = CopilotClient()
+ await client.start()
+
+ session = await client.create_session({
+ "on_permission_request": PermissionHandler.approve_all,
+ "model": "gpt-4.1",
+ "streaming": True,
+ "tools": [get_weather],
+ })
+
+ def handle_event(event):
+ if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
+ sys.stdout.write(event.data.delta_content)
+ sys.stdout.flush()
+
+ session.on(handle_event)
+
+ await session.send_and_wait({
+ "prompt": "What's the weather like in Seattle and Tokyo?"
+ })
+
+ await client.stop()
+
+asyncio.run(main())
+```
+
+### Go
+```go
+type WeatherParams struct {
+ City string `json:"city" jsonschema:"The city name"`
+}
+
+type WeatherResult struct {
+ City string `json:"city"`
+ Temperature string `json:"temperature"`
+ Condition string `json:"condition"`
+}
+
+getWeather := copilot.DefineTool(
+ "get_weather",
+ "Get the current weather for a city",
+ func(params WeatherParams, inv copilot.ToolInvocation) (WeatherResult, error) {
+ conditions := []string{"sunny", "cloudy", "rainy", "partly cloudy"}
+ temp := rand.Intn(30) + 50
+ condition := conditions[rand.Intn(len(conditions))]
+ return WeatherResult{
+ City: params.City,
+ Temperature: fmt.Sprintf("%d°F", temp),
+ Condition: condition,
+ }, nil
+ },
+)
+
+session, _ := client.CreateSession(&copilot.SessionConfig{
+ OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
+ Model: "gpt-4.1",
+ Streaming: true,
+ Tools: []copilot.Tool{getWeather},
+})
+```
+
+### .NET (Microsoft.Extensions.AI)
+```csharp
+using GitHub.Copilot.SDK;
+using Microsoft.Extensions.AI;
+using System.ComponentModel;
+
+var getWeather = AIFunctionFactory.Create(
+ ([Description("The city name")] string city) =>
+ {
+ var conditions = new[] { "sunny", "cloudy", "rainy", "partly cloudy" };
+ var temp = Random.Shared.Next(50, 80);
+ var condition = conditions[Random.Shared.Next(conditions.Length)];
+ return new { city, temperature = $"{temp}°F", condition };
+ },
+ "get_weather",
+ "Get the current weather for a city"
+);
+
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ OnPermissionRequest = PermissionHandler.ApproveAll,
+ Model = "gpt-4.1",
+ Streaming = true,
+ Tools = [getWeather],
+});
+```
+
+## How Tools Work
+
+When Copilot decides to call your tool:
+1. Copilot sends a tool call request with the parameters
+2. The SDK runs your handler function
+3. The result is sent back to Copilot
+4. Copilot incorporates the result into its response
+
+Copilot decides when to call your tool based on the user's question and your tool's description.
+
+## Interactive CLI Assistant
+
+Build a complete interactive assistant:
+
+### TypeScript
+```typescript
+import { CopilotClient, approveAll, defineTool, SessionEvent } from "@github/copilot-sdk";
+import * as readline from "readline";
+
+const getWeather = defineTool("get_weather", {
+ description: "Get the current weather for a city",
+ parameters: {
+ type: "object",
+ properties: {
+ city: { type: "string", description: "The city name" },
+ },
+ required: ["city"],
+ },
+ handler: async ({ city }) => {
+ const conditions = ["sunny", "cloudy", "rainy", "partly cloudy"];
+ const temp = Math.floor(Math.random() * 30) + 50;
+ const condition = conditions[Math.floor(Math.random() * conditions.length)];
+ return { city, temperature: `${temp}°F`, condition };
+ },
+});
+
+const client = new CopilotClient();
+const session = await client.createSession({
+ onPermissionRequest: approveAll,
+ model: "gpt-4.1",
+ streaming: true,
+ tools: [getWeather],
+});
+
+session.on((event: SessionEvent) => {
+ if (event.type === "assistant.message_delta") {
+ process.stdout.write(event.data.deltaContent);
+ }
+});
+
+const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+});
+
+console.log("Weather Assistant (type 'exit' to quit)");
+console.log("Try: 'What's the weather in Paris?'\n");
+
+const prompt = () => {
+ rl.question("You: ", async (input) => {
+ if (input.toLowerCase() === "exit") {
+ await client.stop();
+ rl.close();
+ return;
+ }
+
+ process.stdout.write("Assistant: ");
+ await session.sendAndWait({ prompt: input });
+ console.log("\n");
+ prompt();
+ });
+};
+
+prompt();
+```
+
+### Python
+```python
+import asyncio
+import random
+import sys
+from copilot import CopilotClient, PermissionHandler
+from copilot.tools import define_tool
+from copilot.generated.session_events import SessionEventType
+from pydantic import BaseModel, Field
+
+class GetWeatherParams(BaseModel):
+ city: str = Field(description="The name of the city to get weather for")
+
+@define_tool(description="Get the current weather for a city")
+async def get_weather(params: GetWeatherParams) -> dict:
+ conditions = ["sunny", "cloudy", "rainy", "partly cloudy"]
+ temp = random.randint(50, 80)
+ condition = random.choice(conditions)
+ return {"city": params.city, "temperature": f"{temp}°F", "condition": condition}
+
+async def main():
+ client = CopilotClient()
+ await client.start()
+
+ session = await client.create_session({
+ "on_permission_request": PermissionHandler.approve_all,
+ "model": "gpt-4.1",
+ "streaming": True,
+ "tools": [get_weather],
+ })
+
+ def handle_event(event):
+ if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
+ sys.stdout.write(event.data.delta_content)
+ sys.stdout.flush()
+
+ session.on(handle_event)
+
+ print("Weather Assistant (type 'exit' to quit)")
+ print("Try: 'What's the weather in Paris?'\n")
+
+ while True:
+ try:
+ user_input = input("You: ")
+ except EOFError:
+ break
+
+ if user_input.lower() == "exit":
+ break
+
+ sys.stdout.write("Assistant: ")
+ await session.send_and_wait({"prompt": user_input})
+ print("\n")
+
+ await client.stop()
+
+asyncio.run(main())
+```
+
+## MCP Server Integration
+
+Connect to MCP (Model Context Protocol) servers for pre-built tools. Connect to GitHub's MCP server for repository, issue, and PR access:
+
+### TypeScript
+```typescript
+const session = await client.createSession({
+ onPermissionRequest: approveAll,
+ model: "gpt-4.1",
+ mcpServers: {
+ github: {
+ type: "http",
+ url: "https://api.githubcopilot.com/mcp/",
+ },
+ },
+});
+```
+
+### Python
+```python
+session = await client.create_session({
+ "on_permission_request": PermissionHandler.approve_all,
+ "model": "gpt-4.1",
+ "mcp_servers": {
+ "github": {
+ "type": "http",
+ "url": "https://api.githubcopilot.com/mcp/",
+ },
+ },
+})
+```
+
+### Go
+```go
+session, _ := client.CreateSession(&copilot.SessionConfig{
+ OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
+ Model: "gpt-4.1",
+ MCPServers: map[string]copilot.MCPServerConfig{
+ "github": {
+ "type": "http",
+ "url": "https://api.githubcopilot.com/mcp/",
+ },
+ },
+})
+```
+
+### .NET
+```csharp
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ OnPermissionRequest = PermissionHandler.ApproveAll,
+ Model = "gpt-4.1",
+ McpServers = new Dictionary
+ {
+ ["github"] = new McpServerConfig
+ {
+ Type = "http",
+ Url = "https://api.githubcopilot.com/mcp/",
+ },
+ },
+});
+```
+
+## Custom Agents
+
+Define specialized AI personas for specific tasks:
+
+### TypeScript
+```typescript
+const session = await client.createSession({
+ onPermissionRequest: approveAll,
+ model: "gpt-4.1",
+ customAgents: [{
+ name: "pr-reviewer",
+ displayName: "PR Reviewer",
+ description: "Reviews pull requests for best practices",
+ prompt: "You are an expert code reviewer. Focus on security, performance, and maintainability.",
+ }],
+});
+```
+
+### Python
+```python
+session = await client.create_session({
+ "on_permission_request": PermissionHandler.approve_all,
+ "model": "gpt-4.1",
+ "custom_agents": [{
+ "name": "pr-reviewer",
+ "display_name": "PR Reviewer",
+ "description": "Reviews pull requests for best practices",
+ "prompt": "You are an expert code reviewer. Focus on security, performance, and maintainability.",
+ }],
+})
+```
+
+## System Message
+
+Customize the AI's behavior and personality:
+
+### TypeScript
+```typescript
+const session = await client.createSession({
+ onPermissionRequest: approveAll,
+ model: "gpt-4.1",
+ systemMessage: {
+ content: "You are a helpful assistant for our engineering team. Always be concise.",
+ },
+});
+```
+
+### Python
+```python
+session = await client.create_session({
+ "on_permission_request": PermissionHandler.approve_all,
+ "model": "gpt-4.1",
+ "system_message": {
+ "content": "You are a helpful assistant for our engineering team. Always be concise.",
+ },
+})
+```
+
+## External CLI Server
+
+Run the CLI in server mode separately and connect the SDK to it. Useful for debugging, resource sharing, or custom environments.
+
+### Start CLI in Server Mode
+```bash
+copilot --server --port 4321
+```
+
+### Connect SDK to External Server
+
+#### TypeScript
+```typescript
+const client = new CopilotClient({
+ cliUrl: "localhost:4321"
+});
+
+const session = await client.createSession({
+ onPermissionRequest: approveAll,
+ model: "gpt-4.1",
+});
+```
+
+#### Python
+```python
+client = CopilotClient({
+ "cli_url": "localhost:4321"
+})
+await client.start()
+
+session = await client.create_session({
+ "on_permission_request": PermissionHandler.approve_all,
+ "model": "gpt-4.1",
+})
+```
+
+#### Go
+```go
+client := copilot.NewClient(&copilot.ClientOptions{
+ CLIUrl: "localhost:4321",
+})
+
+if err := client.Start(); err != nil {
+ log.Fatal(err)
+}
+
+session, _ := client.CreateSession(&copilot.SessionConfig{
+ OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
+ Model: "gpt-4.1",
+})
+```
+
+#### .NET
+```csharp
+using var client = new CopilotClient(new CopilotClientOptions
+{
+ CliUrl = "localhost:4321"
+});
+
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ OnPermissionRequest = PermissionHandler.ApproveAll,
+ Model = "gpt-4.1",
+});
+```
+
+**Note:** When `cliUrl` is provided, the SDK will not spawn or manage a CLI process - it only connects to the existing server.
+
+## Event Types
+
+| Event | Description |
+|-------|-------------|
+| `user.message` | User input added |
+| `assistant.message` | Complete model response |
+| `assistant.message_delta` | Streaming response chunk |
+| `assistant.reasoning` | Model reasoning (model-dependent) |
+| `assistant.reasoning_delta` | Streaming reasoning chunk |
+| `tool.execution_start` | Tool invocation started |
+| `tool.execution_complete` | Tool execution finished |
+| `session.idle` | No active processing |
+| `session.error` | Error occurred |
+
+## Client Configuration
+
+| Option | Description | Default |
+|--------|-------------|---------|
+| `cliPath` | Path to Copilot CLI executable | System PATH |
+| `cliUrl` | Connect to existing server (e.g., "localhost:4321") | None |
+| `port` | Server communication port | Random |
+| `useStdio` | Use stdio transport instead of TCP | true |
+| `logLevel` | Logging verbosity | "info" |
+| `autoStart` | Launch server automatically | true |
+| `autoRestart` | Restart on crashes | true |
+| `cwd` | Working directory for CLI process | Inherited |
+
+## Session Configuration
+
+| Option | Description |
+|--------|-------------|
+| `model` | LLM to use ("gpt-4.1", "claude-sonnet-4.5", etc.) |
+| `sessionId` | Custom session identifier |
+| `tools` | Custom tool definitions |
+| `mcpServers` | MCP server connections |
+| `customAgents` | Custom agent personas |
+| `systemMessage` | Override default system prompt |
+| `streaming` | Enable incremental response chunks |
+| `availableTools` | Whitelist of permitted tools |
+| `excludedTools` | Blacklist of disabled tools |
+
+## Session Persistence
+
+Save and resume conversations across restarts:
+
+### Create with Custom ID
+```typescript
+const session = await client.createSession({
+ onPermissionRequest: approveAll,
+ sessionId: "user-123-conversation",
+ model: "gpt-4.1"
+});
+```
+
+### Resume Session
+```typescript
+const session = await client.resumeSession("user-123-conversation", { onPermissionRequest: approveAll });
+await session.send({ prompt: "What did we discuss earlier?" });
+```
+
+### List and Delete Sessions
+```typescript
+const sessions = await client.listSessions();
+await client.deleteSession("old-session-id");
+```
+
+## Error Handling
+
+```typescript
+try {
+ const client = new CopilotClient();
+ const session = await client.createSession({
+ onPermissionRequest: approveAll,
+ model: "gpt-4.1",
+ });
+ const response = await session.sendAndWait(
+ { prompt: "Hello!" },
+ 30000 // timeout in ms
+ );
+} catch (error) {
+ if (error.code === "ENOENT") {
+ console.error("Copilot CLI not installed");
+ } else if (error.code === "ECONNREFUSED") {
+ console.error("Cannot connect to Copilot server");
+ } else {
+ console.error("Error:", error.message);
+ }
+} finally {
+ await client.stop();
+}
+```
+
+## Graceful Shutdown
+
+```typescript
+process.on("SIGINT", async () => {
+ console.log("Shutting down...");
+ await client.stop();
+ process.exit(0);
+});
+```
+
+## Common Patterns
+
+### Multi-turn Conversation
+```typescript
+const session = await client.createSession({
+ onPermissionRequest: approveAll,
+ model: "gpt-4.1",
+});
+
+await session.sendAndWait({ prompt: "My name is Alice" });
+await session.sendAndWait({ prompt: "What's my name?" });
+// Response: "Your name is Alice"
+```
+
+### File Attachments
+```typescript
+await session.send({
+ prompt: "Analyze this file",
+ attachments: [{
+ type: "file",
+ path: "./data.csv",
+ displayName: "Sales Data"
+ }]
+});
+```
+
+### Abort Long Operations
+```typescript
+const timeoutId = setTimeout(() => {
+ session.abort();
+}, 60000);
+
+session.on((event) => {
+ if (event.type === "session.idle") {
+ clearTimeout(timeoutId);
+ }
+});
+```
+
+## Available Models
+
+Query available models at runtime:
+
+```typescript
+const models = await client.getModels();
+// Returns: ["gpt-4.1", "gpt-4o", "claude-sonnet-4.5", ...]
+```
+
+## Best Practices
+
+1. **Always cleanup**: Use `try-finally` or `defer` to ensure `client.stop()` is called
+2. **Set timeouts**: Use `sendAndWait` with timeout for long operations
+3. **Handle events**: Subscribe to error events for robust error handling
+4. **Use streaming**: Enable streaming for better UX on long responses
+5. **Persist sessions**: Use custom session IDs for multi-turn conversations
+6. **Define clear tools**: Write descriptive tool names and descriptions
+
+## Architecture
+
+```
+Your Application
+ |
+ SDK Client
+ | JSON-RPC
+ Copilot CLI (server mode)
+ |
+ GitHub (models, auth)
+```
+
+The SDK manages the CLI process lifecycle automatically. All communication happens via JSON-RPC over stdio or TCP.
+
+## Resources
+
+- **GitHub Repository**: https://github.com/github/copilot-sdk
+- **Getting Started Tutorial**: https://github.com/github/copilot-sdk/blob/main/docs/tutorials/first-app.md
+- **GitHub MCP Server**: https://github.com/github/github-mcp-server
+- **MCP Servers Directory**: https://github.com/modelcontextprotocol/servers
+- **Cookbook**: https://github.com/github/copilot-sdk/tree/main/cookbook
+- **Samples**: https://github.com/github/copilot-sdk/tree/main/samples
+
+## Status
+
+This SDK is in **Technical Preview** and may have breaking changes. Not recommended for production use yet.
diff --git a/plugins/csharp-dotnet-development/.github/plugin/plugin.json b/plugins/csharp-dotnet-development/.github/plugin/plugin.json
index 819f96afe..808ef3c87 100644
--- a/plugins/csharp-dotnet-development/.github/plugin/plugin.json
+++ b/plugins/csharp-dotnet-development/.github/plugin/plugin.json
@@ -14,16 +14,16 @@
"testing"
],
"agents": [
- "./agents/expert-dotnet-software-engineer.md"
+ "./agents"
],
"skills": [
- "./skills/aspnet-minimal-api-openapi/",
- "./skills/csharp-async/",
- "./skills/csharp-mstest/",
- "./skills/csharp-nunit/",
- "./skills/csharp-tunit/",
- "./skills/csharp-xunit/",
- "./skills/dotnet-best-practices/",
- "./skills/dotnet-upgrade/"
+ "./skills/aspnet-minimal-api-openapi",
+ "./skills/csharp-async",
+ "./skills/csharp-mstest",
+ "./skills/csharp-nunit",
+ "./skills/csharp-tunit",
+ "./skills/csharp-xunit",
+ "./skills/dotnet-best-practices",
+ "./skills/dotnet-upgrade"
]
}
diff --git a/plugins/csharp-dotnet-development/agents/expert-dotnet-software-engineer.md b/plugins/csharp-dotnet-development/agents/expert-dotnet-software-engineer.md
new file mode 100644
index 000000000..00329b407
--- /dev/null
+++ b/plugins/csharp-dotnet-development/agents/expert-dotnet-software-engineer.md
@@ -0,0 +1,24 @@
+---
+description: "Provide expert .NET software engineering guidance using modern software design patterns."
+name: "Expert .NET software engineer mode instructions"
+tools: ["changes", "codebase", "edit/editFiles", "extensions", "fetch", "findTestFiles", "githubRepo", "new", "openSimpleBrowser", "problems", "runCommands", "runNotebooks", "runTasks", "runTests", "search", "searchResults", "terminalLastCommand", "terminalSelection", "testFailure", "usages", "vscodeAPI", "microsoft.docs.mcp"]
+---
+
+# Expert .NET software engineer mode instructions
+
+You are in expert software engineer mode. Your task is to provide expert software engineering guidance using modern software design patterns as if you were a leader in the field.
+
+You will provide:
+
+- insights, best practices and recommendations for .NET software engineering as if you were Anders Hejlsberg, the original architect of C# and a key figure in the development of .NET as well as Mads Torgersen, the lead designer of C#.
+- general software engineering guidance and best-practices, clean code and modern software design, as if you were Robert C. Martin (Uncle Bob), a renowned software engineer and author of "Clean Code" and "The Clean Coder".
+- DevOps and CI/CD best practices, as if you were Jez Humble, co-author of "Continuous Delivery" and "The DevOps Handbook".
+- Testing and test automation best practices, as if you were Kent Beck, the creator of Extreme Programming (XP) and a pioneer in Test-Driven Development (TDD).
+
+For .NET-specific guidance, focus on the following areas:
+
+- **Design Patterns**: Use and explain modern design patterns such as Async/Await, Dependency Injection, Repository Pattern, Unit of Work, CQRS, Event Sourcing and of course the Gang of Four patterns.
+- **SOLID Principles**: Emphasize the importance of SOLID principles in software design, ensuring that code is maintainable, scalable, and testable.
+- **Testing**: Advocate for Test-Driven Development (TDD) and Behavior-Driven Development (BDD) practices, using frameworks like xUnit, NUnit, or MSTest.
+- **Performance**: Provide insights on performance optimization techniques, including memory management, asynchronous programming, and efficient data access patterns.
+- **Security**: Highlight best practices for securing .NET applications, including authentication, authorization, and data protection.
diff --git a/plugins/csharp-dotnet-development/skills/aspnet-minimal-api-openapi/SKILL.md b/plugins/csharp-dotnet-development/skills/aspnet-minimal-api-openapi/SKILL.md
new file mode 100644
index 000000000..aae320d6e
--- /dev/null
+++ b/plugins/csharp-dotnet-development/skills/aspnet-minimal-api-openapi/SKILL.md
@@ -0,0 +1,41 @@
+---
+name: aspnet-minimal-api-openapi
+description: 'Create ASP.NET Minimal API endpoints with proper OpenAPI documentation'
+---
+
+# ASP.NET Minimal API with OpenAPI
+
+Your goal is to help me create well-structured ASP.NET Minimal API endpoints with correct types and comprehensive OpenAPI/Swagger documentation.
+
+## API Organization
+
+- Group related endpoints using `MapGroup()` extension
+- Use endpoint filters for cross-cutting concerns
+- Structure larger APIs with separate endpoint classes
+- Consider using a feature-based folder structure for complex APIs
+
+## Request and Response Types
+
+- Define explicit request and response DTOs/models
+- Create clear model classes with proper validation attributes
+- Use record types for immutable request/response objects
+- Use meaningful property names that align with API design standards
+- Apply `[Required]` and other validation attributes to enforce constraints
+- Use the ProblemDetailsService and StatusCodePages to get standard error responses
+
+## Type Handling
+
+- Use strongly-typed route parameters with explicit type binding
+- Use `Results` to represent multiple response types
+- Return `TypedResults` instead of `Results` for strongly-typed responses
+- Leverage C# 10+ features like nullable annotations and init-only properties
+
+## OpenAPI Documentation
+
+- Use the built-in OpenAPI document support added in .NET 9
+- Define operation summary and description
+- Add operationIds using the `WithName` extension method
+- Add descriptions to properties and parameters with `[Description()]`
+- Set proper content types for requests and responses
+- Use document transformers to add elements like servers, tags, and security schemes
+- Use schema transformers to apply customizations to OpenAPI schemas
diff --git a/plugins/csharp-dotnet-development/skills/csharp-async/SKILL.md b/plugins/csharp-dotnet-development/skills/csharp-async/SKILL.md
new file mode 100644
index 000000000..4dbe78b0b
--- /dev/null
+++ b/plugins/csharp-dotnet-development/skills/csharp-async/SKILL.md
@@ -0,0 +1,49 @@
+---
+name: csharp-async
+description: 'Get best practices for C# async programming'
+---
+
+# C# Async Programming Best Practices
+
+Your goal is to help me follow best practices for asynchronous programming in C#.
+
+## Naming Conventions
+
+- Use the 'Async' suffix for all async methods
+- Match method names with their synchronous counterparts when applicable (e.g., `GetDataAsync()` for `GetData()`)
+
+## Return Types
+
+- Return `Task` when the method returns a value
+- Return `Task` when the method doesn't return a value
+- Consider `ValueTask` for high-performance scenarios to reduce allocations
+- Avoid returning `void` for async methods except for event handlers
+
+## Exception Handling
+
+- Use try/catch blocks around await expressions
+- Avoid swallowing exceptions in async methods
+- Use `ConfigureAwait(false)` when appropriate to prevent deadlocks in library code
+- Propagate exceptions with `Task.FromException()` instead of throwing in async Task returning methods
+
+## Performance
+
+- Use `Task.WhenAll()` for parallel execution of multiple tasks
+- Use `Task.WhenAny()` for implementing timeouts or taking the first completed task
+- Avoid unnecessary async/await when simply passing through task results
+- Consider cancellation tokens for long-running operations
+
+## Common Pitfalls
+
+- Never use `.Wait()`, `.Result`, or `.GetAwaiter().GetResult()` in async code
+- Avoid mixing blocking and async code
+- Don't create async void methods (except for event handlers)
+- Always await Task-returning methods
+
+## Implementation Patterns
+
+- Implement the async command pattern for long-running operations
+- Use async streams (IAsyncEnumerable) for processing sequences asynchronously
+- Consider the task-based asynchronous pattern (TAP) for public APIs
+
+When reviewing my C# code, identify these issues and suggest improvements that follow these best practices.
diff --git a/plugins/csharp-dotnet-development/skills/csharp-mstest/SKILL.md b/plugins/csharp-dotnet-development/skills/csharp-mstest/SKILL.md
new file mode 100644
index 000000000..e68bc31ea
--- /dev/null
+++ b/plugins/csharp-dotnet-development/skills/csharp-mstest/SKILL.md
@@ -0,0 +1,478 @@
+---
+name: csharp-mstest
+description: 'Get best practices for MSTest 3.x/4.x unit testing, including modern assertion APIs and data-driven tests'
+---
+
+# MSTest Best Practices (MSTest 3.x/4.x)
+
+Your goal is to help me write effective unit tests with modern MSTest, using current APIs and best practices.
+
+## Project Setup
+
+- Use a separate test project with naming convention `[ProjectName].Tests`
+- Reference MSTest 3.x+ NuGet packages (includes analyzers)
+- Consider using MSTest.Sdk for simplified project setup
+- Run tests with `dotnet test`
+
+## Test Class Structure
+
+- Use `[TestClass]` attribute for test classes
+- **Seal test classes by default** for performance and design clarity
+- Use `[TestMethod]` for test methods (prefer over `[DataTestMethod]`)
+- Follow Arrange-Act-Assert (AAA) pattern
+- Name tests using pattern `MethodName_Scenario_ExpectedBehavior`
+
+```csharp
+[TestClass]
+public sealed class CalculatorTests
+{
+ [TestMethod]
+ public void Add_TwoPositiveNumbers_ReturnsSum()
+ {
+ // Arrange
+ var calculator = new Calculator();
+
+ // Act
+ var result = calculator.Add(2, 3);
+
+ // Assert
+ Assert.AreEqual(5, result);
+ }
+}
+```
+
+## Test Lifecycle
+
+- **Prefer constructors over `[TestInitialize]`** - enables `readonly` fields and follows standard C# patterns
+- Use `[TestCleanup]` for cleanup that must run even if test fails
+- Combine constructor with async `[TestInitialize]` when async setup is needed
+
+```csharp
+[TestClass]
+public sealed class ServiceTests
+{
+ private readonly MyService _service; // readonly enabled by constructor
+
+ public ServiceTests()
+ {
+ _service = new MyService();
+ }
+
+ [TestInitialize]
+ public async Task InitAsync()
+ {
+ // Use for async initialization only
+ await _service.WarmupAsync();
+ }
+
+ [TestCleanup]
+ public void Cleanup() => _service.Reset();
+}
+```
+
+### Execution Order
+
+1. **Assembly Initialization** - `[AssemblyInitialize]` (once per test assembly)
+2. **Class Initialization** - `[ClassInitialize]` (once per test class)
+3. **Test Initialization** (for every test method):
+ 1. Constructor
+ 2. Set `TestContext` property
+ 3. `[TestInitialize]`
+4. **Test Execution** - test method runs
+5. **Test Cleanup** (for every test method):
+ 1. `[TestCleanup]`
+ 2. `DisposeAsync` (if implemented)
+ 3. `Dispose` (if implemented)
+6. **Class Cleanup** - `[ClassCleanup]` (once per test class)
+7. **Assembly Cleanup** - `[AssemblyCleanup]` (once per test assembly)
+
+## Modern Assertion APIs
+
+MSTest provides three assertion classes: `Assert`, `StringAssert`, and `CollectionAssert`.
+
+### Assert Class - Core Assertions
+
+```csharp
+// Equality
+Assert.AreEqual(expected, actual);
+Assert.AreNotEqual(notExpected, actual);
+Assert.AreSame(expectedObject, actualObject); // Reference equality
+Assert.AreNotSame(notExpectedObject, actualObject);
+
+// Null checks
+Assert.IsNull(value);
+Assert.IsNotNull(value);
+
+// Boolean
+Assert.IsTrue(condition);
+Assert.IsFalse(condition);
+
+// Fail/Inconclusive
+Assert.Fail("Test failed due to...");
+Assert.Inconclusive("Test cannot be completed because...");
+```
+
+### Exception Testing (Prefer over `[ExpectedException]`)
+
+```csharp
+// Assert.Throws - matches TException or derived types
+var ex = Assert.Throws(() => Method(null));
+Assert.AreEqual("Value cannot be null.", ex.Message);
+
+// Assert.ThrowsExactly - matches exact type only
+var ex = Assert.ThrowsExactly(() => Method());
+
+// Async versions
+var ex = await Assert.ThrowsAsync(async () => await client.GetAsync(url));
+var ex = await Assert.ThrowsExactlyAsync(async () => await Method());
+```
+
+### Collection Assertions (Assert class)
+
+```csharp
+Assert.Contains(expectedItem, collection);
+Assert.DoesNotContain(unexpectedItem, collection);
+Assert.ContainsSingle(collection); // exactly one element
+Assert.HasCount(5, collection);
+Assert.IsEmpty(collection);
+Assert.IsNotEmpty(collection);
+```
+
+### String Assertions (Assert class)
+
+```csharp
+Assert.Contains("expected", actualString);
+Assert.StartsWith("prefix", actualString);
+Assert.EndsWith("suffix", actualString);
+Assert.DoesNotStartWith("prefix", actualString);
+Assert.DoesNotEndWith("suffix", actualString);
+Assert.MatchesRegex(@"\d{3}-\d{4}", phoneNumber);
+Assert.DoesNotMatchRegex(@"\d+", textOnly);
+```
+
+### Comparison Assertions
+
+```csharp
+Assert.IsGreaterThan(lowerBound, actual);
+Assert.IsGreaterThanOrEqualTo(lowerBound, actual);
+Assert.IsLessThan(upperBound, actual);
+Assert.IsLessThanOrEqualTo(upperBound, actual);
+Assert.IsInRange(actual, low, high);
+Assert.IsPositive(number);
+Assert.IsNegative(number);
+```
+
+### Type Assertions
+
+```csharp
+// MSTest 3.x - uses out parameter
+Assert.IsInstanceOfType(obj, out var typed);
+typed.DoSomething();
+
+// MSTest 4.x - returns typed result directly
+var typed = Assert.IsInstanceOfType(obj);
+typed.DoSomething();
+
+Assert.IsNotInstanceOfType(obj);
+```
+
+### Assert.That (MSTest 4.0+)
+
+```csharp
+Assert.That(result.Count > 0); // Auto-captures expression in failure message
+```
+
+### StringAssert Class
+
+> **Note:** Prefer `Assert` class equivalents when available (e.g., `Assert.Contains("expected", actual)` over `StringAssert.Contains(actual, "expected")`).
+
+```csharp
+StringAssert.Contains(actualString, "expected");
+StringAssert.StartsWith(actualString, "prefix");
+StringAssert.EndsWith(actualString, "suffix");
+StringAssert.Matches(actualString, new Regex(@"\d{3}-\d{4}"));
+StringAssert.DoesNotMatch(actualString, new Regex(@"\d+"));
+```
+
+### CollectionAssert Class
+
+> **Note:** Prefer `Assert` class equivalents when available (e.g., `Assert.Contains`).
+
+```csharp
+// Containment
+CollectionAssert.Contains(collection, expectedItem);
+CollectionAssert.DoesNotContain(collection, unexpectedItem);
+
+// Equality (same elements, same order)
+CollectionAssert.AreEqual(expectedCollection, actualCollection);
+CollectionAssert.AreNotEqual(unexpectedCollection, actualCollection);
+
+// Equivalence (same elements, any order)
+CollectionAssert.AreEquivalent(expectedCollection, actualCollection);
+CollectionAssert.AreNotEquivalent(unexpectedCollection, actualCollection);
+
+// Subset checks
+CollectionAssert.IsSubsetOf(subset, superset);
+CollectionAssert.IsNotSubsetOf(notSubset, collection);
+
+// Element validation
+CollectionAssert.AllItemsAreInstancesOfType(collection, typeof(MyClass));
+CollectionAssert.AllItemsAreNotNull(collection);
+CollectionAssert.AllItemsAreUnique(collection);
+```
+
+## Data-Driven Tests
+
+### DataRow
+
+```csharp
+[TestMethod]
+[DataRow(1, 2, 3)]
+[DataRow(0, 0, 0, DisplayName = "Zeros")]
+[DataRow(-1, 1, 0, IgnoreMessage = "Known issue #123")] // MSTest 3.8+
+public void Add_ReturnsSum(int a, int b, int expected)
+{
+ Assert.AreEqual(expected, Calculator.Add(a, b));
+}
+```
+
+### DynamicData
+
+The data source can return any of the following types:
+
+- `IEnumerable<(T1, T2, ...)>` (ValueTuple) - **preferred**, provides type safety (MSTest 3.7+)
+- `IEnumerable