mcollina/skills
repository·main·Indexed 23 days ago
https://github.com/mcollina/skillsA collection of specialized expertise (skills) designed to enhance AI-assisted development. It provides high-signal guidance across domains including Node.js internals, Fastify best practices, advanced TypeScript type systems, and the Diátaxis documentation framework. The repository includes a skill-optimizer for improving AI activation and a comprehensive benchmarking workflow to evaluate performance and prevent regressions across different AI models.
What's inside @matteo.collina/skills
- Matteo Collina's Skills is a collection of specialized skills designed for AI-assisted development. It provides high-signal expertise across various domains including Node.js internals, Fastify best practices, TypeScript advanced type systems, and documentation using the Diátaxis framework. Developers can use these skills to improve the quality of AI-generated code, documentation, and workflows.
When to use the skill-optimizer
mainThe
skill-optimizeris designed to improve the effectiveness of AI skills. Use this skill when you need to:- Improve activation: Ensure a skill is actually applied by models.
- Diagnose failures: Understand why certain criteria fail across different models.
- Prevent regressions: Ensure a skill update does not make model outputs worse.
- Refactor for context pressure: Improve skill text so it is retrieved and followed more effectively when context is limited.
- Establish benchmarks: Build repeatable benchmark loops and release gates for skill deployment.
What is N-API (Node-API)?
mainN-API is Node.js's ABI-stable API for building native addons. Unlike older V8-based addons that require recompilation when Node.js or V8 versions change, N-API code remains compatible across different Node.js versions because it interacts with a stable interface rather than internal engine structures.Understand the Node.js Child Process Architecture
mainThe Node.js child process implementation operates across three distinct layers:
- JavaScript Layer (
lib/child_process.js): Provides the high-level API includingspawn(),fork(),exec(), andexecFile(). - Internal JS (
lib/internal/child_process.js): Manages theChildProcessclass, channel setup, and synchronous spawning. - C++ Bindings (
src/process_wrap.cc): Interfaces with the OS via theProcessWrapclass to handleSpawn()andKill()operations. - libuv: The underlying abstraction layer that handles actual process spawning (
uv_spawn), killing (uv_process_kill), and standard I/O pipes (uv_pipe_t).
JavaScript Layer (lib/child_process.js) ├── spawn() ├── fork() ├── exec() └── execFile() │ ▼ Internal JS (lib/internal/child_process.js) ├── ChildProcess class ├── setupChannel() └── spawnSync │ ▼ C++ Bindings (src/process_wrap.cc) ├── ProcessWrap ├── Spawn(), Kill() │ ▼ libuv ├── uv_spawn() ├── uv_process_kill() └── uv_pipe_t (for stdio)- JavaScript Layer (
Use Async Notifications for Thread Signaling
mainIn C/libuv,
uv_async_tallows one thread (e.g., a worker thread) to wake up the main event loop thread. This is used for signaling between threads. In Node.js, this pattern is used internally for Worker thread communication and N-API async callbacks.#include <uv.h> uv_async_t async; void async_cb(uv_async_t* handle) { printf("Received async notification\n"); } void worker_thread(void* arg) { // Do some work... uv_async_send(&async); // Wake up main thread } int main() { uv_loop_t* loop = uv_default_loop(); uv_async_init(loop, &async, async_cb); // Start worker thread... return uv_run(loop, UV_RUN_DEFAULT); }Resolve overloads using top-to-bottom ordering
mainTypeScript attempts to match function calls to overloads in the order they are defined, from top to bottom. To ensure correct type inference, always place more specific overloads before more general ones.
// More specific overloads first function processValue(value: string): string; function processValue(value: number): number; // General implementation signature function processValue(value: string | number): string | number { if (typeof value === "string") { return value.toUpperCase(); } return value * 2; } const str = processValue("hello"); // Type: string const num = processValue(42); // Type: numberfunction processValue(value: string): string; function processValue(value: number): number; function processValue(value: string | number): string | number { if (typeof value === "string") { return value.toUpperCase(); } return value * 2; } const str = processValue("hello"); const num = processValue(42);Default Content Type Parsers in Fastify
mainFastify includes built-in parsers for common content types. By default, it handles
application/json(parsing the body into a JavaScript object) andtext/plain(parsing the body into a string).import Fastify from 'fastify'; const app = Fastify(); // Built-in parsers: // - application/json // - text/plain app.post('/json', async (request) => { // request.body is parsed JSON object return { received: request.body }; }); app.post('/text', async (request) => { // request.body is string for text/plain return { text: request.body }; });Pattern matching with `infer` in template literals
mainYou can use the
inferkeyword within a template literal type to extract specific parts of a string type.Remove Prefix
To remove a specific prefix, use
inferto capture the remainder of the string:type RemoveMaps<T> = T extends `maps:${infer Rest}` ? Rest : T; type Test1 = RemoveMaps<"maps:longitude">; // "longitude"Remove Suffix
To remove a suffix, capture the prefix before the suffix:
type RemovePostSuffix<T> = T extends `${infer Prefix}:post` ? Prefix : T; type Test = RemovePostSuffix<"attribute:post">; // "attribute"Split on Delimiter
You can recursively split a string into a tuple based on a delimiter:
type Split<S extends string, D extends string> = S extends `${infer Head}${D}${infer Tail}` ? [Head, ...Split<Tail, D>] : S extends "" ? [] : [S]; type Parts = Split<"a-b-c", "-">; // ["a", "b", "c"]Understand the skill benchmarking workflow and failure modes
mainThe benchmarking workflow is designed to catch two specific failure modes before shipping skill changes:
- Non-activation: This occurs when criteria stay at 0% even when the skill is enabled.
- Regression: This occurs when the score decreases when the skill is enabled compared to the baseline (without the skill).
When to run benchmarks
- When editing any
skills/*/SKILL.mdfile. - When editing any
skills/*/rules/*.mdfile. - When preparing a release of updated skills.
Prefer named exports over default exports
mainUse named exports instead of
export default. Named exports provide better support for refactoring, improved IDE autocompletion, and more efficient tree-shaking.// GOOD - named exports export function createServer(config: Config): Server { // ... } export function createClient(config: Config): Client { // ... } // AVOID - default exports export default function createServer(config: Config): Server { // ... }How the Writable Stream Write Flow works
mainThe lifecycle of a write operation follows this path:
- JS:
stream.write(chunk)is called. - JS:
stream._write(chunk, encoding, callback)is invoked (user implementation). - C++:
WriteString()orWriteBuffer()is called. - C++:
DoWrite()triggersuv_write(). - libuv: The write request is queued.
- libuv:
write_cbis triggered upon completion. - C++:
WriteWrap::OnDone()is called. - JS:
afterWrite()triggers the user'scallback(). - JS: The
'drain'event is emitted if the buffer was previously full.
1. JavaScript: stream.write(chunk) ↓ 2. JavaScript: _write(chunk, encoding, callback) [user implements] ↓ 3. C++: WriteString() or WriteBuffer() ↓ 4. C++: DoWrite() → uv_write() ↓ 5. libuv: Queue write request ↓ 6. libuv: write_cb when complete ↓ 7. C++: WriteWrap::OnDone() ↓ 8. JavaScript: afterWrite() → callback() ↓ 9. JavaScript: 'drain' event if buffer was full- JS:
TypeScript requirements for type stripping
mainType stripping works by removing type annotations without transforming code. To ensure compatibility, your TypeScript code must follow these constraints:
1. Use Type-Only Imports
Always use the
typekeyword for imports that only involve types. This prevents runtime errors during stripping.2. No Enums
Enums require code transformation. Instead, use a
constobject with a corresponding type.3. No Namespaces
Namespaces require transformation. Use standard ES modules (
export) instead.4. No Constructor Parameter Properties
Avoid using access modifiers (e.g.,
public,private) directly in constructor parameters. Use explicit property declarations and assignments instead.5. No Legacy Decorators
Use the TC39 (stage 3) decorator syntax. Compatibility for decorators depends on your specific Node.js version.
// GOOD - type-only import import type { User, Config } from './types.ts'; import { createUser, type User } from './user.ts'; // GOOD - const object instead of enum const Status = { Active: 'active', Inactive: 'inactive', } as const; type Status = (typeof Status)[keyof typeof Status]; // GOOD - explicit property declaration instead of parameter properties class User { name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } }