chore: clean misplaced repo artifacts
This commit is contained in:
@@ -78,7 +78,7 @@ Per ottenere il percorso alla definizione della skill:
|
||||
|
||||
## 4. Pseudo‑codice di integrazione (TypeScript)
|
||||
|
||||
> Esempio completo in: [`examples/jetski-gemini-loader/`](../../examples/jetski-gemini-loader/).
|
||||
> Esempio completo in: [`docs/integrations/jetski-gemini-loader/`](../../docs/integrations/jetski-gemini-loader/).
|
||||
|
||||
### 4.1. Tipi di base
|
||||
|
||||
|
||||
94
docs/integrations/jetski-gemini-loader/README.md
Normal file
94
docs/integrations/jetski-gemini-loader/README.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Jetski + Gemini Lazy Skill Loader (Example)
|
||||
|
||||
This example shows one way to integrate **antigravity-awesome-skills** with a Jetski/Cortex‑style agent using **lazy loading** based on `@skill-id` mentions, instead of concatenating every `SKILL.md` into the prompt.
|
||||
|
||||
> This is **not** a production‑ready library – it is a minimal reference you can adapt to your own host/agent implementation.
|
||||
|
||||
---
|
||||
|
||||
## What this example demonstrates
|
||||
|
||||
- How to:
|
||||
- load the global manifest `data/skills_index.json` once at startup;
|
||||
- scan conversation messages for `@skill-id` patterns;
|
||||
- resolve those ids to entries in the manifest;
|
||||
- read only the corresponding `SKILL.md` files from disk (lazy loading);
|
||||
- build a prompt array with:
|
||||
- your base system messages;
|
||||
- one system message per selected skill;
|
||||
- the rest of the trajectory.
|
||||
- How to enforce a **maximum number of skills per turn** via `maxSkillsPerTurn`.
|
||||
- How to choose whether to **truncate or error** when too many skills are requested via `overflowBehavior`.
|
||||
|
||||
This pattern avoids context overflow when you have 1,200+ skills installed.
|
||||
|
||||
---
|
||||
|
||||
## Files
|
||||
|
||||
- `loader.ts`
|
||||
- Implements:
|
||||
- `loadSkillIndex(indexPath)`;
|
||||
- `resolveSkillsFromMessages(messages, index, maxSkills)`;
|
||||
- `loadSkillBodies(skillsRoot, metas)`;
|
||||
- `buildModelMessages({...})`.
|
||||
- See also the integration guide:
|
||||
- [`docs/integrations/jetski-cortex.md`](../../docs/integrations/jetski-cortex.md)
|
||||
|
||||
---
|
||||
|
||||
## Basic usage (pseudo‑code)
|
||||
|
||||
```ts
|
||||
import path from "path";
|
||||
import {
|
||||
loadSkillIndex,
|
||||
buildModelMessages,
|
||||
Message,
|
||||
} from "./loader";
|
||||
|
||||
const REPO_ROOT = "/path/to/antigravity-awesome-skills";
|
||||
const SKILLS_ROOT = REPO_ROOT;
|
||||
const INDEX_PATH = path.join(REPO_ROOT, "data", "skills_index.json");
|
||||
|
||||
// 1. Bootstrap once at agent startup
|
||||
const skillIndex = loadSkillIndex(INDEX_PATH);
|
||||
|
||||
// 2. Before calling the model, build messages with lazy‑loaded skills
|
||||
async function runTurn(trajectory: Message[]) {
|
||||
const baseSystemMessages: Message[] = [
|
||||
{
|
||||
role: "system",
|
||||
content: "You are a helpful coding agent.",
|
||||
},
|
||||
];
|
||||
|
||||
const modelMessages = await buildModelMessages({
|
||||
baseSystemMessages,
|
||||
trajectory,
|
||||
skillIndex,
|
||||
skillsRoot: SKILLS_ROOT,
|
||||
maxSkillsPerTurn: 8,
|
||||
overflowBehavior: "error",
|
||||
});
|
||||
|
||||
// 3. Pass `modelMessages` to your Jetski/Cortex + Gemini client
|
||||
// e.g. trajectoryChatConverter.convert(modelMessages)
|
||||
}
|
||||
```
|
||||
|
||||
Adapt the paths and model call to your environment.
|
||||
|
||||
---
|
||||
|
||||
## Important notes
|
||||
|
||||
- **Do not** iterate through `skills/*/SKILL.md` and load everything at once.
|
||||
- This example:
|
||||
- assumes skills live under the same repo root as `data/skills_index.json`;
|
||||
- uses Node.js `fs`/`path` APIs and TypeScript types for clarity.
|
||||
- In a real host:
|
||||
- wire `buildModelMessages` into the point where you currently assemble the prompt before `TrajectoryChatConverter`;
|
||||
- consider `overflowBehavior: "error"` if you want a clear user-facing failure instead of silently dropping extra skills;
|
||||
- keep path validation so manifest entries cannot escape your configured skills root;
|
||||
- add token‑counting / truncation logic if you want a stricter safety budget.
|
||||
148
docs/integrations/jetski-gemini-loader/loader.ts
Normal file
148
docs/integrations/jetski-gemini-loader/loader.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
export type SkillMeta = {
|
||||
id: string;
|
||||
path: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
category?: string;
|
||||
risk?: string;
|
||||
};
|
||||
|
||||
export type Message = {
|
||||
role: "system" | "user" | "assistant";
|
||||
content: string;
|
||||
};
|
||||
|
||||
const SKILL_ID_REGEX = /@([a-zA-Z0-9-_./]+)/g;
|
||||
|
||||
function collectReferencedSkillIds(
|
||||
messages: Message[],
|
||||
index: Map<string, SkillMeta>
|
||||
): string[] {
|
||||
const referencedSkillIds = new Set<string>();
|
||||
|
||||
for (const msg of messages) {
|
||||
for (const match of msg.content.matchAll(SKILL_ID_REGEX)) {
|
||||
const id = match[1];
|
||||
if (index.has(id)) {
|
||||
referencedSkillIds.add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...referencedSkillIds];
|
||||
}
|
||||
|
||||
function assertValidMaxSkills(maxSkills: number): number {
|
||||
if (!Number.isInteger(maxSkills) || maxSkills < 1) {
|
||||
throw new Error("maxSkills must be a positive integer.");
|
||||
}
|
||||
|
||||
return maxSkills;
|
||||
}
|
||||
|
||||
export 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;
|
||||
}
|
||||
|
||||
export function resolveSkillsFromMessages(
|
||||
messages: Message[],
|
||||
index: Map<string, SkillMeta>,
|
||||
maxSkills: number
|
||||
): SkillMeta[] {
|
||||
const skillLimit = assertValidMaxSkills(maxSkills);
|
||||
const referencedSkillIds = collectReferencedSkillIds(messages, index);
|
||||
|
||||
const metas: SkillMeta[] = [];
|
||||
for (const id of referencedSkillIds) {
|
||||
const meta = index.get(id);
|
||||
if (meta) {
|
||||
metas.push(meta);
|
||||
}
|
||||
if (metas.length >= skillLimit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return metas;
|
||||
}
|
||||
|
||||
export async function loadSkillBodies(
|
||||
skillsRoot: string,
|
||||
metas: SkillMeta[]
|
||||
): Promise<string[]> {
|
||||
const bodies: string[] = [];
|
||||
const rootPath = path.resolve(skillsRoot);
|
||||
|
||||
for (const meta of metas) {
|
||||
const fullPath = path.resolve(rootPath, meta.path, "SKILL.md");
|
||||
const relativePath = path.relative(rootPath, fullPath);
|
||||
|
||||
if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
|
||||
throw new Error(`Skill path escapes skills root: ${meta.id}`);
|
||||
}
|
||||
|
||||
const text = await fs.promises.readFile(fullPath, "utf8");
|
||||
bodies.push(text);
|
||||
}
|
||||
|
||||
return bodies;
|
||||
}
|
||||
|
||||
export async function buildModelMessages(options: {
|
||||
baseSystemMessages: Message[];
|
||||
trajectory: Message[];
|
||||
skillIndex: Map<string, SkillMeta>;
|
||||
skillsRoot: string;
|
||||
maxSkillsPerTurn?: number;
|
||||
overflowBehavior?: "truncate" | "error";
|
||||
}): Promise<Message[]> {
|
||||
const {
|
||||
baseSystemMessages,
|
||||
trajectory,
|
||||
skillIndex,
|
||||
skillsRoot,
|
||||
maxSkillsPerTurn = 8,
|
||||
overflowBehavior = "truncate",
|
||||
} = options;
|
||||
const skillLimit = assertValidMaxSkills(maxSkillsPerTurn);
|
||||
const referencedSkillIds = collectReferencedSkillIds(trajectory, skillIndex);
|
||||
|
||||
if (
|
||||
overflowBehavior === "error" &&
|
||||
referencedSkillIds.length > skillLimit
|
||||
) {
|
||||
throw new Error(
|
||||
`Too many skills requested in a single turn. Reduce @skill-id usage to ${skillLimit} or fewer.`
|
||||
);
|
||||
}
|
||||
|
||||
const selectedMetas = resolveSkillsFromMessages(
|
||||
trajectory,
|
||||
skillIndex,
|
||||
skillLimit
|
||||
);
|
||||
|
||||
if (selectedMetas.length === 0) {
|
||||
return [...baseSystemMessages, ...trajectory];
|
||||
}
|
||||
|
||||
const skillBodies = await loadSkillBodies(skillsRoot, selectedMetas);
|
||||
|
||||
const skillMessages: Message[] = skillBodies.map((body) => ({
|
||||
role: "system",
|
||||
content: body,
|
||||
}));
|
||||
|
||||
return [...baseSystemMessages, ...skillMessages, ...trajectory];
|
||||
}
|
||||
Reference in New Issue
Block a user