Import the official Hugging Face ecosystem skills and sync the\nexisting local coverage with upstream metadata and assets.\n\nRegenerate the canonical catalog, plugin mirrors, docs, and release\nnotes after the maintainer merge batch so main stays in sync.\n\nFixes #417
273 lines
8.5 KiB
Markdown
273 lines
8.5 KiB
Markdown
---
|
||
title: Jetski/Cortex + Gemini Integration Guide
|
||
description: "Come usare antigravity-awesome-skills con Jetski/Cortex evitando l’overflow di contesto con 1.340+ skill."
|
||
---
|
||
|
||
# Jetski/Cortex + Gemini: integrazione sicura con 1.340+ skill
|
||
|
||
Questa guida mostra come integrare il repository `antigravity-awesome-skills` con un agente basato su **Jetski/Cortex + Gemini** (o framework simili) **senza superare il context window** del modello.
|
||
|
||
L’errore tipico visto in Jetski/Cortex è:
|
||
|
||
> `TrajectoryChatConverter: could not convert a single message before hitting truncation`
|
||
|
||
Il problema non è nelle skill, ma **nel modo in cui vengono caricate**.
|
||
|
||
---
|
||
|
||
## 1. Antipattern da evitare
|
||
|
||
Non bisogna mai:
|
||
|
||
- leggere **tutte** le directory `skills/*/SKILL.md` all’avvio;
|
||
- concatenare il contenuto di tutte le `SKILL.md` in un singolo system prompt;
|
||
- reiniettare l’intera libreria per **ogni** richiesta.
|
||
|
||
Con oltre 1.340 skill, questo approccio riempie il context window prima ancora di aggiungere i messaggi dell’utente, causando l’errore di truncation.
|
||
|
||
---
|
||
|
||
## 2. Pattern raccomandato
|
||
|
||
Principi chiave:
|
||
|
||
- **Manifest leggero**: usare `data/skills_index.json` per sapere *quali* skill esistono, senza caricare i testi completi.
|
||
- **Lazy loading**: leggere `SKILL.md` **solo** per le skill effettivamente invocate in una conversazione (es. quando compare `@skill-id`).
|
||
- **Limiti espliciti**: imporre un massimo di skill/tokens caricati per turno, con fallback chiari.
|
||
- **Path safety**: verificare che i path del manifest restino dentro `SKILLS_ROOT` prima di leggere `SKILL.md`.
|
||
|
||
Il flusso consigliato è:
|
||
|
||
1. **Bootstrap**: all’avvio dell’agente leggere `data/skills_index.json` e costruire una mappa `id -> meta`.
|
||
2. **Parsing dei messaggi**: prima di chiamare il modello, estrarre tutti i riferimenti `@skill-id` dai messaggi utente/sistema.
|
||
3. **Risoluzione**: mappare gli id trovati in oggetti `SkillMeta` usando la mappa di bootstrap.
|
||
4. **Lazy load**: leggere i file `SKILL.md` solo per questi id (fino a un massimo configurabile).
|
||
5. **Prompt building**: costruire i system messages del modello includendo solo le definizioni delle skill selezionate.
|
||
|
||
---
|
||
|
||
## 3. Struttura di `skills_index.json`
|
||
|
||
Il file `data/skills_index.json` è un array di oggetti, ad esempio:
|
||
|
||
```json
|
||
{
|
||
"id": "brainstorming",
|
||
"path": "skills/brainstorming",
|
||
"category": "planning",
|
||
"name": "brainstorming",
|
||
"description": "Use before any creative or constructive work.",
|
||
"risk": "safe",
|
||
"source": "official",
|
||
"date_added": "2026-02-27"
|
||
}
|
||
```
|
||
|
||
Campi chiave:
|
||
|
||
- **`id`**: identificatore usato nelle menzioni `@id` (es. `@brainstorming`).
|
||
- **`path`**: directory che contiene la `SKILL.md` (es. `skills/brainstorming/`).
|
||
|
||
Per ottenere il percorso alla definizione della skill:
|
||
|
||
- `fullPath = path.join(SKILLS_ROOT, meta.path, "SKILL.md")`.
|
||
|
||
> Nota: `SKILLS_ROOT` è la directory radice dove avete installato il repository (es. `~/.agent/skills`).
|
||
|
||
---
|
||
|
||
## 4. Pseudo‑codice di integrazione (TypeScript)
|
||
|
||
> Esempio completo in: [`docs/integrations/jetski-gemini-loader/`](../../docs/integrations/jetski-gemini-loader/).
|
||
|
||
### 4.1. Tipi di base
|
||
|
||
```ts
|
||
type SkillMeta = {
|
||
id: string;
|
||
path: string;
|
||
name: string;
|
||
description?: string;
|
||
category?: string;
|
||
risk?: string;
|
||
};
|
||
```
|
||
|
||
### 4.2. Bootstrap: caricare il manifest
|
||
|
||
```ts
|
||
function loadSkillIndex(indexPath: string): Map<string, SkillMeta> {
|
||
const raw = fs.readFileSync(indexPath, "utf8");
|
||
const arr = JSON.parse(raw) as SkillMeta[];
|
||
const map = new Map<string, SkillMeta>();
|
||
for (const meta of arr) {
|
||
map.set(meta.id, meta);
|
||
}
|
||
return map;
|
||
}
|
||
```
|
||
|
||
### 4.3. Parsing dei messaggi per trovare `@skill-id`
|
||
|
||
```ts
|
||
const SKILL_ID_REGEX = /@([a-zA-Z0-9-_./]+)/g;
|
||
|
||
function resolveSkillsFromMessages(
|
||
messages: { role: string; content: string }[],
|
||
index: Map<string, SkillMeta>,
|
||
maxSkills: number
|
||
): SkillMeta[] {
|
||
const found = new Set<string>();
|
||
|
||
for (const msg of messages) {
|
||
let match: RegExpExecArray | null;
|
||
while ((match = SKILL_ID_REGEX.exec(msg.content)) !== null) {
|
||
const id = match[1];
|
||
if (index.has(id)) {
|
||
found.add(id);
|
||
}
|
||
}
|
||
}
|
||
|
||
const metas: SkillMeta[] = [];
|
||
for (const id of found) {
|
||
const meta = index.get(id);
|
||
if (meta) metas.push(meta);
|
||
if (metas.length >= maxSkills) break;
|
||
}
|
||
|
||
return metas;
|
||
}
|
||
```
|
||
|
||
### 4.4. Lazy loading dei file `SKILL.md`
|
||
|
||
```ts
|
||
async function loadSkillBodies(
|
||
skillsRoot: string,
|
||
metas: SkillMeta[]
|
||
): Promise<string[]> {
|
||
const bodies: string[] = [];
|
||
|
||
for (const meta of metas) {
|
||
const fullPath = path.join(skillsRoot, meta.path, "SKILL.md");
|
||
const text = await fs.promises.readFile(fullPath, "utf8");
|
||
bodies.push(text);
|
||
}
|
||
|
||
return bodies;
|
||
}
|
||
```
|
||
|
||
### 4.5. Costruzione del prompt Jetski/Cortex
|
||
|
||
Pseudocodice per la fase di pre‑processing, prima del `TrajectoryChatConverter`:
|
||
|
||
```ts
|
||
async function buildModelMessages(
|
||
baseSystemMessages: { role: "system"; content: string }[],
|
||
trajectory: { role: "user" | "assistant" | "system"; content: string }[],
|
||
skillIndex: Map<string, SkillMeta>,
|
||
skillsRoot: string,
|
||
maxSkillsPerTurn: number,
|
||
overflowBehavior: "truncate" | "error" = "truncate"
|
||
): Promise<{ role: string; content: string }[]> {
|
||
const referencedSkills = resolveSkillsFromMessages(
|
||
trajectory,
|
||
skillIndex,
|
||
Number.MAX_SAFE_INTEGER
|
||
);
|
||
if (
|
||
overflowBehavior === "error" &&
|
||
referencedSkills.length > maxSkillsPerTurn
|
||
) {
|
||
throw new Error(
|
||
`Too many skills requested in a single turn. Reduce @skill-id usage to ${maxSkillsPerTurn} or fewer.`
|
||
);
|
||
}
|
||
|
||
const selectedMetas = resolveSkillsFromMessages(
|
||
trajectory,
|
||
skillIndex,
|
||
maxSkillsPerTurn
|
||
);
|
||
|
||
const skillBodies = await loadSkillBodies(skillsRoot, selectedMetas);
|
||
|
||
const skillMessages = skillBodies.map((body) => ({
|
||
role: "system" as const,
|
||
content: body,
|
||
}));
|
||
|
||
return [...baseSystemMessages, ...skillMessages, ...trajectory];
|
||
}
|
||
```
|
||
|
||
> Suggerimento: aggiungete una stima dei token per troncare o riassumere i `SKILL.md` se il context window si avvicina al limite.
|
||
> Il reference loader di questo repo supporta anche un fallback esplicito: `overflowBehavior: "error"`.
|
||
|
||
---
|
||
|
||
## 5. Gestione degli overflow di contesto
|
||
|
||
Per evitare errori difficili da capire per l’utente, impostate:
|
||
|
||
- una **soglia di sicurezza** (es. 70–80% del context window);
|
||
- un **limite massimo di skill per turno** (es. 5–10).
|
||
|
||
Strategie quando si supera la soglia:
|
||
|
||
- ridurre il numero di skill incluse (es. in base a recenza o priorità); oppure
|
||
- restituire un errore chiaro all’utente, ad esempio:
|
||
|
||
> "Sono state richieste troppe skill in un singolo turno. Riduci il numero di `@skill-id` nel messaggio o dividili in più passaggi."
|
||
|
||
---
|
||
|
||
## 6. Scenari di test raccomandati
|
||
|
||
- **Scenario 1 – Messaggio semplice ("hi")**
|
||
- Nessun `@skill-id` → nessuna `SKILL.md` caricata → il prompt rimane piccolo → nessun errore.
|
||
- **Scenario 2 – Poche skill**
|
||
- Messaggio con 1–2 `@skill-id` → solo le relative `SKILL.md` vengono caricate → nessun overflow.
|
||
- **Scenario 3 – Molte skill**
|
||
- Messaggio con molte `@skill-id` → si attiva il limite `maxSkillsPerTurn` o il controllo di token → nessun overflow silenzioso.
|
||
|
||
---
|
||
|
||
## 7. Sottoinsiemi di skill e bundle
|
||
|
||
Per ulteriore controllo:
|
||
|
||
- spostate le skill non necessarie in `skills/.disabled/` per escluderle in certi ambienti;
|
||
- usate i **bundle** descritti in [`docs/users/bundles.md`](../users/bundles.md) per caricare solo gruppi tematici.
|
||
|
||
## 8. Recovery su Windows se siete gia in crash loop
|
||
|
||
Se l’host continua a riaprire la stessa trajectory corrotta dopo un errore di truncation:
|
||
|
||
- rimuovete la skill o il pacchetto problematico;
|
||
- cancellate Local Storage / Session Storage / IndexedDB usati da Antigravity;
|
||
- svuotate `%TEMP%`;
|
||
- riavviate con un loader lazy e limiti espliciti.
|
||
|
||
Guida completa:
|
||
|
||
- [`docs/users/windows-truncation-recovery.md`](../users/windows-truncation-recovery.md)
|
||
|
||
Per evitare che il problema si ripresenti:
|
||
|
||
- mantenete `overflowBehavior: "error"` quando preferite un fallimento esplicito;
|
||
- continuate a validare che i path risolti restino dentro `skillsRoot`.
|
||
|
||
---
|
||
|
||
## 9. Riepilogo
|
||
|
||
- Non concatenate mai tutte le `SKILL.md` in un singolo prompt.
|
||
- Usate `data/skills_index.json` come manifest leggero.
|
||
- Caricate le skill **on‑demand** in base a `@skill-id`.
|
||
- Impostate limiti chiari (max skill per turno, soglia di token).
|
||
|
||
Seguendo questo pattern, Jetski/Cortex + Gemini può usare l’intera libreria di `antigravity-awesome-skills` in modo sicuro, scalabile e compatibile con il context window dei modelli moderni.
|