npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@exellix/ai-skills

v7.2.2

Published

Foundational skill execution layer for exellix ecosystem using @x12i/ai-gateway with FlexMD 2.0 support and Catalox as the catalog store

Readme

@exellix/ai-skills

Foundational skill execution layer for the exellix ecosystem: templates live in the Catalox native catalog ai-skills, execution uses @x12i/ai-gateway, and responses support FlexMD 2.0 structured text.

🚀 Env-Ready: Provider keys can be loaded from .env automatically. catalox is required at construction time (see Quick Start).

Catalox 6: Skill catalogs bootstrap via Mongo (ensureCataloxFromEnv / createCataloxFromEnvAsync from @exellix/catalox-ai-skills). Env: MONGO_URI or MONGODB_URI. See documenations/catalox-6-rollout.md.

Documentation

| Topic | Doc | |--------|-----| | Flex-MD vs gateway parsing, integration tests | docs/FLEX_MD_AND_TESTING.md | | Catalox integration & env notes | docs/CATALOX_PEER_GUIDE.md · Environment (Mongo & Catalox 6) | | Activix integration best practices | .docs/activix-integration-best-practices-checklist.md | | Per-package log level (AI_SKILLS_LOGS_LEVEL) | Logging below · Logxer integration checklist · @x12i/logxer on npm | | Gateway templates (v4), invoke() vs invokeChat(), templateRendering | docs/GATEWAY_TEMPLATE_PROTOCOL_V4.md | | This package: workingMemory, templateRenderOptions, client templateRendering | docs/AI_SKILLS_GATEWAY_TEMPLATES.md | | Upgrading to @x12i/ai-gateway 10.x (billing, required fields, removed behaviors) | docs/UPGRADING_AI_GATEWAY_10.md | | Invoke execution metadata (provider, modelUsed, effectiveModelConfig, …) | docs/AI_GATEWAY_INVOKE_EXECUTION_METADATA.md | | Wire model on runSkill() (required; no alias / no gateway default) | docs/MODEL_INVOKE_CONTRACT.md | | Host invoke contract (reasoningEffort, no caller maxTokens) | Host responsibilities · MODEL_INVOKE_CONTRACT.md | | OpenRouter vs direct routing (PREFER_OPENROUTER) — not implemented here | docs/PREFER_OPENROUTER_ROUTING.md | | Graph execution context (graphId, nodeId, identity mapping) | docs/GRAPH_EXECUTION_SUPPORT.md | | Skills manager: preflight, FuncX, authoring (not in this package) | docs/SKILLS_MANAGER_CLIENT_GUIDE.md | | External follow-ups (Activix persistence vs gateway envelope) | docs/AI_GATEWAY_FEATURE_REQUESTS.md | | Gateway server tools (OpenRouter pass-through) | docs/GATEWAY_SERVER_TOOLS.md · OPENROUTER_SERVER_TOOLS_WITH_AI_SKILLS.md · upstream CR for gateway |

Features

  • Unified Skill Runner: Execute skills via metadata, not hardcoded handlers
  • FlexMD 2.0 Output: Always returns structured-text format with parsed payloads
  • Catalox-native templates: Instruction and prompt bodies are read from the Firestore native catalog ai-skills via @x12i/catalox; the gateway receives inline template text (not nx-content registry keys for skills)
  • Catalox provisioning: npm run catalox:provision-ai-skills upserts catalog metadata + Optimixer fields from AI_SKILLS_CATALOG_ITEMS (preserves template bodies already stored in Catalox)
  • Presentation API: Load pretty markdown for editors, save with lossless storage normalization (getSkillTemplatesForPresentation, updateSkillTemplatesFromPresentation, getSkillTemplateInputs); see Skill templates (Catalox)
  • Catalog audit fields (storage only): Catalox rows may store optional audit template text for editors; this package only executes runSkill (no built-in audit pass).
  • Gateway integration: @x12i/ai-gateway for LLM calls only; skill templates are never loaded from nx-content / GitHub — Catalox inline strings on invoke() only
  • Activity tracking: With enableActivityTracking, the gateway persists activities via @x12i/activix; this SDK enriches rows with cost / costStatus and contract-shaped outer.output.parsed when applicable (see Cost and output contract)
  • Model Configuration: Per-request model selection and sampling parameters (temperature, topP, reasoningEffort); completion token budget is Optimixer-owned (see Host responsibilities)
  • Full template pipeline (default): Uses gateway.invoke() with inline bodies; the gateway message builder renders tokens (downstream stack; e.g. Rendrix). runSkill populates workingMemory.input and can pass templateRenderOptions, templateTokens, or client-level templateRendering (see docs/AI_SKILLS_GATEWAY_TEMPLATES.md).
  • Execution engine catalog: Optional aiEngineId echoes the Catalox ai-engines row (ai-gateway only; provision with npm run catalox:provision-ai-engines) for discovery and identity — see Execution engines (aiEngineId).
  • Execution-only scope: Preflight / authoring / FuncX invoke review live in skills manager clients — see docs/SKILLS_MANAGER_CLIENT_GUIDE.md.

Installation

npm install @exellix/ai-skills

This package depends on @x12i/ai-gateway, @x12i/catalox, @x12i/logxer, @x12i/env, @x12i/rendrix, and @exellix/catalox-ai-skills (Mongo Catalox bootstrap). @x12i/catalox ≥ 6.0 expects Node 20+.

@x12i/ai-gateway ≥ 11.0.0 (openrouter-runtime 1.0.4, @x12i/ai-tools 3.3.5) (see package.json) with @x12i/activix ^9.0 and @x12i/ai-profiles ^3.4 is required: every invoke needs an explicit wire model and maxTokens (this package supplies maxTokens via @x12i/optimixer before gateway.invoke()). Invoke metadata includes routing fields (≥9.1.1), rejection metadata on failures (≥9.1.2), normalized billing (costUsd, cost, costStatus: priced | unpriced, optional costBreakdown) on success (≥9.3; ai-tools catalog pricing in gateway ≥10.1), and optional OpenRouter server-tool metadata (≥10.4.1). This package does not use @x12i/ai-tools directly — cost is resolved by the gateway. See docs/UPGRADING_AI_GATEWAY_10.md and docs/AI_GATEWAY_INVOKE_EXECUTION_METADATA.md. Every runSkill call must include agentId, jobTypeId, and taskTypeId (Activix linkage — no package defaults).

CI: npm run test:ci runs npm run build and npm run test:unit (no live Firestore or real LLM). Full npm test still runs test:integration, which may append live catalog tests when env enables them.

Custom admin flows that call Catalox with createCatalog, bindCatalogToApp, or similar before an app binding exists should set superAdmin: true on CataloxContext (see defaultAiSkillsCataloxContext overrides and docs/CATALOX_PEER_GUIDE.md).

Publishing: the npm tarball is limited to dist/, README.md, and erc-manifest.json via the files field in package.json (avoids shipping tests, logs, or local scratch files).

Environment variables (Mongo & Catalox 6)

createCataloxFromEnvAsync() / ensureCataloxFromEnv() from @exellix/catalox-ai-skills delegate to @x12i/catalox/mongo.

| Variable | Notes | |----------|--------| | MONGO_URI or MONGODB_URI | Required — Mongo connection string for Catalox catalogs | | MONGO_DB_NAME / MONGODB_DB | Optional database name (default catalox) |

Provider keys for the default gateway (OPENAI_API_KEY, GROK_API_KEY, OPEN_ROUTER_KEY / OPENROUTER_API_KEY) are unchanged — see Troubleshooting below.

Quick Start

0) Prerequisites

  1. Node.js 20+ — matches @x12i/catalox engine requirements.
  2. Mongo Catalox — set MONGO_URI or MONGODB_URI (see above). Use await createCataloxFromEnvAsync() or pass your own Catalox from createCataloxFromMongoEnv.
  3. Provision the catalog (creates/updates Catalox app bindings, descriptors, and skill rows):
npm run catalox:provision-ai-skills
npm run catalox:provision-ai-engines
npm run catalox:verify
  1. At least one LLM provider key (OPENAI_API_KEY, GROK_API_KEY, or OPEN_ROUTER_KEY / OPENROUTER_API_KEY) for @x12i/ai-gateway.

1) Create the client

import { ExellixSkillsClient, RunSkillRequest, RunSkillResponse, createCataloxFromEnv } from "@exellix/ai-skills";

const catalox = createCataloxFromEnv();

const skills = new ExellixSkillsClient({
  catalox,
  enableActivityTracking: true,
});

You can also construct Catalox yourself (createCatalox from @x12i/catalox or @x12i/catalox/embedder with your firestore / firebaseApp) and pass options.catalox. An external gateway is optional; catalox is always required.

2) Run a skill

// Using the RunSkillRequest interface
const runSkillRequest: RunSkillRequest = {
  skillKey: "skills/professional-answer",
  input: "Question: What is the best way to migrate from X to Y?",
  variables: { orgName: "ACME", audience: "security team" },
  jobId: "job-123",
  taskId: "task-456",
  agentId: "agent-abc",
  jobTypeId: "your-job-type-id",
  taskTypeId: "your-task-type-id",
};

const res: RunSkillResponse = await skills.runSkill(runSkillRequest);

// Access FlexMD payloads
console.log(res.flexMd.payloads.shortAnswer);
console.log(res.flexMd.payloads.fullAnswer);

2b) Execution engines (aiEngineId)

runSkill always invokes @x12i/ai-gateway with inline Catalox template text; Rendrix runs inside the gateway. Optional aiEngineId must be omitted, blank, or ai-gateway — it is merged into gateway.invoke identity and matches the single row in the Catalox ai-engines catalog (provisioned for listing / UIs). Unknown values throw at resolve time.

2a) Run a skill with model configuration

// Override model and generation parameters per-request
const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Question: What is the best way to migrate from X to Y?",
  variables: { orgName: "ACME" },
  jobId: "job-123",
  taskId: "task-456",
  agentId: "agent-abc",
  jobTypeId: "your-job-type-id",
  taskTypeId: "your-task-type-id",
  modelConfig: {
    model: "gpt-4-turbo",
    temperature: 0.7,
    topP: 0.9,
    reasoningEffort: "medium",
  }
});

Host responsibilities (orchestrator ↔ MAIN)

Applies to @exellix/ai-tasks 8.4+ and other graph orchestrators calling runSkill() for the MAIN skill phase.

| Send | Do not send | |------|-------------| | Wire model / modelId (required) | maxTokens, max_completion_tokens, max_output_tokens on modelConfig | | Optional temperature, topP, reasoningEffort, frequencyPenalty, presencePenalty, retry, timeoutMs | Profile / plan model names — resolve in @exellix/ai-tasks only | | Optional serverTools, openrouter.apiMode (top-level or on modelConfig) | Raw OpenRouter tool payloads, provider secrets | | Optional provider | Expectation that ai-skills picks a default model |

Token budget: owned by @x12i/optimixer from Catalox catalog + prompts. After invoke, read metadata.usage.maxTokensRequested (and trace usage.maxTokensRequested when trace mode is on).

reasoningEffort: optional on modelConfig. Request value overrides Catalox optimixerReasoningEffort for that skill key; omit to keep catalog-only behavior. Resolution order: request → catalog → "not-applicable".

Full rules: docs/MODEL_INVOKE_CONTRACT.md · observability follow-ups: archived ai-skills-llm-observability.md.

2b) Optional: parser / template overrides

Per skill run you can pass templateRenderOptions (merged on gateway defaults) and templateTokens (highest-priority overlay). On the client, templateRendering sets defaults for every invoke. Details and examples: docs/AI_SKILLS_GATEWAY_TEMPLATES.md.

2c) Optional: identity propagation

identity is per-request runtime context from your application (trace ids, tenant, graph/node ids, etc.). It is not the SDK client’s constructor options, not env/config for this package, and not “package identity” from package.json—unless you explicitly copy such values into this object when you build the request.

identity vs runContext: The gateway invoke() API uses the property name identity (see @x12i/ai-gateway). Activix stores that same envelope on activity documents as BSON runContext. This SDK passes identity through to the gateway; you do not set runContext on runSkill inputs here. Responses expose the envelope as identity when the gateway returns it (and resolution may also consider metadata shapes that use runContext).

You can pass an identity object to runSkill() to propagate caller identity context downstream.

  • If you provide identity, the client forwards it as-is to the gateway.
  • The client will add identity.skillId from the executing request only if it is missing.
  • The RunSkillResponse includes identity only if downstream returns one. If downstream does not provide identity, it will be omitted (no fallback).
const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Question: ...",
  jobId: "job-123",
  taskId: "task-456",
  agentId: "agent-abc",
  jobTypeId: "your-job-type-id",
  taskTypeId: "your-task-type-id",
  skillId: "node-q0",
  identity: {
    traceId: "trace-123",
    userId: "user-456"
    // skillId is optional here; if omitted, it will be set from `skillId` above
  }
});

// Only present when downstream returned it:
console.log(res.identity);

// Token usage + cost (from gateway metadata); always on metadata.usage for every run.
// costUsd when priced; costStatus when usage exists but no price ("unpriced") or async pricing ("deferred").
console.log(res.metadata.usage, res.metadata.costUsd, res.metadata.costStatus, res.metadata.modelUsed);

// When `identity` is returned, the same snapshot is nested under `identity.aiSkillsLlm` for Activix-style
// chaining—forward the whole `identity` to the next `runSkill` so pipelines retain billing context.
console.log(res.identity?.aiSkillsLlm);

3) Model wire id, Optimixer budget, and trace visibility

Wire model (mandatory): Every runSkill() must include modelConfig.model or modelConfig.modelId as the concrete provider wire id resolved upstream (@exellix/ai-tasks / resolveInvocationPlan). Missing model is rejected before invoke; non-wire profile input is handled by @x12i/ai-gateway / @x12i/ai-tools, not duplicated here. No gateway default model in this package. See docs/MODEL_INVOKE_CONTRACT.md.

Completion token budget (Optimixer-owned): Caller maxTokens / max_completion_tokens / max_output_tokens are stripped (anomaly log). Budget is predicted by @x12i/optimixer from Catalox catalog fields and resolved reasoningEffort, then merged into modelConfig before gateway.invoke().

Max-token retry (MAIN hop): When the provider returns a limit finishReason (e.g. length) and Optimixer completeAiMaxTokensPrediction returns retryPrediction, this package re-invokes the gateway once with Optimixer’s bumped recommendedMaxTokens (caller ceilings still apply via existing merge). Up to one retry; observability includes metadata.optimixerRetryCount and log code OPTIMIXER_MAX_TOKENS_RETRY. No local token multipliers or re-predict bypass.

Other modelConfig fields (temperature, topP, reasoningEffort, provider, serverTools, openrouter, …) are forwarded when set.

3b) Gateway server tools (OpenRouter pass-through)

@exellix/ai-skills forwards OpenRouter server-tool intent to @x12i/ai-gateway (≥ 10.4.4). This package does not execute OpenRouter tools locally and does not depend on @x12i/openrouter-runtime.

  • Set serverTools on the request, on modelConfig, in Catalox data.runtime.serverTools, or via client defaultServerTools (merge order: client → catalog → request → modelConfig).
  • Tool usage, citations, generated images, and patch proposals appear on RunSkillResponse.metadata (not in FlexMD unless the skill template asks for them).
  • No tools are enabled by default; existing skills behave unchanged unless you opt in.

See docs/GATEWAY_SERVER_TOOLS.md · docs/OPENROUTER_SERVER_TOOLS_WITH_AI_SKILLS.md.

const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "What changed in the vendor docs?",
  jobId: "job-1",
  taskId: "task-1",
  agentId: "agent-1",
  jobTypeId: "research",
  taskTypeId: "answer",
  modelConfig: {
    provider: "openrouter",
    model: "openai/gpt-5.2",
    serverTools: {
      webSearch: { mode: "required", engine: "auto", maxResults: 5 },
      webFetch: { mode: "allowed", maxContentTokens: 50000 },
    },
  },
});

console.log(res.flexMd.payloads);
console.log(res.metadata.citations);
console.log(res.metadata.serverTools);

| What you need | Where it appears | |---------------|------------------| | What was sent toward invoke (merged modelConfig, timeoutMs) | Trace-only: debugTrace.invokeRequest when executionMode: "trace" or diagnostics.includeDebugTrace | | Effective completion cap | metadata.usage.maxTokensRequested · trace debugTrace.usage.maxTokensRequested | | Effective reasoningEffort | Trace debugTrace.invokeRequest.modelConfig.reasoningEffort (request override or catalog default) | | What ran (model id, token counts) | debugTrace.modelUsed, debugTrace.usage, routing ids |

const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Question: ...",
  jobId: "job-1",
  taskId: "task-1",
  agentId: "agent-abc",
  jobTypeId: "your-job-type-id",
  taskTypeId: "your-task-type-id",
  modelConfig: {
    model: "gpt-4-turbo",
    temperature: 0.5,
    reasoningEffort: "high",
  },
  executionMode: "trace",
});

console.log(res.debugTrace?.invokeRequest?.modelConfig?.reasoningEffort);
console.log(res.metadata.usage?.maxTokensRequested);
console.log(res.debugTrace?.usage.maxTokensRequested);

On gateway failures with trace mode enabled, SkillExecutionTraceError carries diagnostics.trace with the same shapes where available; metadata attached on the thrown error is merged when present (see metadataFromInvokeError export).

Cost and output contract (Run Analysis)

Studio / graph Run Analysis expects activities and responses to explain billing and structured output. This package normalizes both on every successful runSkill and best-effort patches the persisted Activix row when metadata.activityId is present.

Cost reporting (metadata.costUsd / metadata.costStatus)

Gateway invoke() metadata (≥ 9.3, ai-tools catalog in gateway ≥ 10.1) uses costUsd / cost when priced and costStatus: "priced" | "unpriced". Optional costBreakdown (prompt/completion USD) is forwarded when the gateway catalog pricing includes it. This SDK never calculates cost locally — it normalizes gateway metadata only. Priced runs expose costUsd only (gateway "priced" status is not echoed). When usage exists but no USD total is known, costStatus: "unpriced" is set (gateway explicit flag or SDK fallback).

| Gateway / router situation | Gateway metadata | On RunSkillResponse.metadata | Activix record (gateway logSuccess + SDK patch) | |----------------------------|-------------------|-------------------------------|------------------------------------------------------| | Priced (router or ai-tools catalog) | costUsd, cost, costStatus: "priced", optional costBreakdown | costUsd, optional costBreakdown (no costStatus) | Root cost / costUsd / costStatus: "priced", outer.cost.usd; prefer these over outer.metadata for billing | | Usage, no price | costStatus: "unpriced" | costStatus: "unpriced" | Root costStatus: "unpriced" + token usage on root metadata / response.metadata | | Legacy async pricing | costStatus: "deferred" (older gateways) | costStatus: "deferred" | same |

Gateway logSuccess writes billing on the activity row during invoke() when enableActivityTracking is on (default). patchActivixActivityAfterSkill then merges the same billing slice plus outputContract parsed fields so Run Analysis and Activix queries stay aligned with RunSkillResponse.metadata.

metadata.usage is always populated (zeros when the gateway omits token fields). The same billing slice is mirrored on identity.aiSkillsLlm when the gateway returns identity, and on debugTrace in trace mode.

Output contract (outputContract)

outputContract is per invoke, not a global list for all skills. Pass the field names your graph node or skill expects on parsed / outer.output.parsed — typically from graph inputs.outputContract (via @exellix/ai-tasks / graph-engine).

Supported shapes: string[], { fields: string[] }, { keys: string[] }, or { required: string[] }.

After FlexMD / gateway parsing (and local parseFlexMd fallback), the SDK fills missing contract keys from:

  1. flexMd.payloads
  2. Markdown section headings (### Short AnswershortAnswer)

Examples (illustrative — use the contract for that skill/node):

// professional-answer–style skill
await skills.runSkill({
  skillKey: "skills/professional-answer",
  outputContract: ["shortAnswer", "fullAnswer", "assumptions", "unknowns", "evidence"],
  // ...jobId, taskId, agentId, jobTypeId, taskTypeId, input, ...
});

// professional-decision–style skill
await skills.runSkill({
  skillKey: "skills/professional-decision",
  outputContract: ["decision", "score", "rationale", "risks"],
  // ...
});

This package does not auto-map skillKey to a catalog contract; upstream must pass outputContract when structured fields are required. Catalog payload names are documented in docs/metadata.md (requiredPayloads per skill).

Exports (for orchestrators and tests): normalizeSkillBillingFromGatewayMetadata, enrichParsedForOutputContract, patchActivixActivityAfterSkill, types SkillCostStatus, OutputContract.

Direct functions (re-consider)

Typed wrappers on ExellixSkillsClient that call revision skills with nested templateTokens. Shared runtime: ReConsiderationRuntime (jobId, taskId, agentId, jobTypeId, taskTypeId, modelConfig). Object fields (context, providedContext, etc.) are stringified via stringifyForPrompt so they never become [object Object]. Callers own merge/apply of returned fragments.

| Method | Skill | Result | |--------|-------|--------| | fillReConsiderationGap | re-consider.fill-gap | { gapOutput } | | reviewReConsiderationResponse | re-consider.review | { reviewStatus, reviewOutput } | | rewriteReConsiderationScope | re-consider.rewrite-scope | { correctedScope } | | rewriteReConsiderationFull | re-consider.rewrite-full | { correctedAnswer } | | calibrateReConsiderationDecision | re-consider.calibrate-decision | { calibratedOutput } |

const result = await skills.fillReConsiderationGap(
  {
    input: "What are the risks?",
    originalInstructions: "Answer with evidence.",
    originalResponse: priorAnswer,
    gapExplained: "Missing evidence bullets for Section B.",
    fragmentName: "Section B",
    context: { assetId: "db-1" },
  },
  {
    jobId: "job-1",
    taskId: "task-1",
    agentId: "agent-1",
    jobTypeId: "job-type",
    taskTypeId: "task-type",
    modelConfig: { model: "gpt-4o-mini" },
  }
);
// result.gapOutput — insert or replace; do not expect a merged full response

Token roots (examples): taskVariables.gap.* / jobMemory.gap.*, taskVariables.review.* / jobMemory.review.*, taskVariables.scope.* / jobMemory.scope.*, taskVariables.full.* / jobMemory.full.*, taskVariables.calibration.* / jobMemory.calibration.*, plus jobMemory.providedContext and {{input}}. Chain prior outputs via optional gapResults / reviewResults / rewriteResults / calibrationResults.

Also exported as standalone functions (and request builders) from @exellix/ai-skills.

Built-in skills (catalog)

Authoritative rows are defined in code as AI_SKILLS_CATALOG_ITEMS and written to Catalox by npm run catalox:provision-ai-skills. Typical runnable keys:

  • skills/professional-answer — structured professional answer (FlexMD payloads)
  • skills/re-consider.fill-gap — fill a known gap (usageHints.skillMode: "revision")
  • skills/re-consider.review — review response → reviewStatus + reviewOutput
  • skills/re-consider.rewrite-scope — rewrite one selected scope → correctedScope
  • skills/re-consider.rewrite-full — full rewrite → correctedAnswer
  • skills/re-consider.calibrate-decision — calibrate decision/priority/ranking → calibratedOutput
  • Additional catalog rows / forks: use listCatalogSkills(), listPublishedSkills(), or Catalox listCatalogItems for the live list.

runSkill takes skillKey like skills/professional-answer. Bodies are not resolved as nx-content registry keys for execution; they are loaded from Catalox and sent to the gateway as inline template strings.

Skill templates (Catalox)

Provisioning (metadata + Optimixer → Catalox)

npm run catalox:provision-ai-skills upserts rows from AI_SKILLS_CATALOG_ITEMS (title, description, Optimixer fields, status). It preserves existing instructionsText / promptText already in Catalox — template bodies are authored in Catalox only (via updateSkillTemplatesFromPresentation, upsertSkillCatalogItem, or your admin tooling).

npm run catalox:provision-ai-skills
npm run catalox:verify

Runnable skills need both bodies in Catalox and status draft or published (computed from body completeness + catalogReleaseStatus on provision).

Catalog manager API (skills studio / admin)

For apps that manage the catalog without running LLMs, use createAiSkillsCatalogApi({ catalox }) — full CRUD + fork. See docs/AI_SKILLS_CATALOG_MANAGER.md.

| Function | Purpose | |----------|---------| | listSkills / listSubSkills | List base and sub-skill rows | | getSkill / getSkillContent | Read metadata and template markdown | | createSkill / updateSkill | Create and patch rows | | forkSkill | Sub-skill fork with optional overrides | | deleteSkill | Hard-delete sub-skills; soft-delete base skills | | publishSkill / unpublishSkill | Release status shortcuts |

Lower-level ExellixSkillsClient methods below remain available when you already use the execution client.

Presentation layer (read / edit)

Low-level helpers live under @exellix/ai-skills exports from ./catalox (e.g. normalizeForStorage, toPresentationMarkdown, extractTemplateTokensFromTexts).

On ExellixSkillsClient:

| Method | Purpose | |--------|---------| | getSkillTemplatesForPresentation(skillKey, { includeAudit? }) | Catalox read → markdown formatted for editors | | updateSkillTemplatesFromPresentation(skillKey, patch, options?) | Editor markdown → lossless storage normalization → Catalox upsert (requires write Catalox context) | | getSkillTemplateInputs(skillKey) | Union of {{token}} placeholders from raw stored bodies; treats input as the primary payload token | | resolveRawTemplate(skillKey, section, { role, strategyKey? }) | Raw Catalox bodies for PRE synthesis / orchestration / audit roles (used by @exellix/ai-tasks) | | resolveSynthesisContextStrategyKey(skillKey) | Default Catalox synthesisContext template key from supportedCapabilities.synthesisContext (graphenix 2.1.0 — plan wire must not carry unitParams.synthesisInputStrategy) | | listCatalogSkills(options?) | Lists every catalog row (all status values); forwards Catalox query options (e.g. limit) | | upsertSkillCatalogItem(input, options?) | Create/update full row (metadata + optional markdown); optional ifNotExists for strict create | | softDeleteSkillCatalogItem(skillKey) | Clears bodies and audit fields and sets planned (no hard delete) |

Write access: updates use batchUpsertNativeCatalogItems; use a Catalox context with permission to write the catalog (e.g. provision-style god mode or an app binding with write). This package does not create credentials.

Skill key usage

await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Your question here...",
});

Catalox item ids are the short key (e.g. professional-answer); the client accepts either skills/professional-answer or the short id where helpers normalize.

Troubleshooting

options.catalox is required

Pass catalox into new ExellixSkillsClient({ catalox, ... }). Use createCataloxFromEnv() from this package, createCataloxFromEnv() from @x12i/catalox/firebase, or createCatalox({ firestore, firebaseApp }) from @x12i/catalox / @x12i/catalox/embedder.

❌ Skill is planned or “incomplete templates”

Run npm run catalox:provision-ai-skills (or write bodies via updateSkillTemplatesFromPresentation) so instructionsText and promptText are non-empty and status is draft or published.

❌ Provider / env validation

At least one of OPENAI_API_KEY, GROK_API_KEY, OPEN_ROUTER_KEY, OPENROUTER_API_KEY is required when the client builds the default gateway. Firebase / Catalox variables are summarized above.

Logging (AiSkills / @x12i/logxer)

This package uses @x12i/logxer with three stable identifiers:

| Field | Value | Purpose | |-------|--------|---------| | packageName | AiSkills | package column in logs, shadow, Mongo | | envPrefix | AI_SKILLS | Env vars and stack keys | | debugNamespace | ai-skills | DEBUG= filter slug |

Exported constants: AI_SKILLS_LOGXER_PACKAGE, AI_SKILLS_LOGXER_ENV_PREFIX, AI_SKILLS_LOGXER_DEBUG_NAMESPACE, AI_SKILLS_LOGXER_PACKAGE_CONFIG, createAiSkillsLogger (alias createAiSkillsLogxer).

| Item | Detail | |------|--------| | Canonical env var | AI_SKILLS_LOGS_LEVEL | | Fallback | AI_SKILLS_LOG_LEVEL is used only if AI_SKILLS_LOGS_LEVEL is unset | | Default (both unset) | warn — not silent; you will see warnings and errors | | Silence this package | Set AI_SKILLS_LOGS_LEVEL=off (or none / silent) | | More detail | info, debug, or verbose (case-insensitive; see @x12i/logxer docs) | | Full payloads | Set AI_SKILLS_VERBOSE=true and AI_SKILLS_LOGS_LEVEL=verbose — attaches sanitized gateway request/response on runSkill() | | Bulk stack config | LOGXER_PACKAGE_LEVELS=AI_SKILLS:debug,... (host calls applyPackageLogLevelsFromEnv() at startup) |

Cross-cutting sinks (console, file, JSON, unified app config) are configured by the host app, not per this prefix — see the @x12i/logxer README.

Console output includes app: (host app from cwd package.json) and package: (AiSkills for lines from this library). Optional packageName on the client sets RuntimeIdentity.service only — it does not change the Logxer package field.

Per-path tiers:

| Path | info | debug | verbose + AI_SKILLS_VERBOSE | |------|--------|---------|-----------------------------------| | runSkill() | One completion summary (duration, provider, model, tokens) | Entry, Catalox fetch, invoke routing, FlexMD steps | Sanitized gateway request/response | | runSkill() | One completion summary per invoke | Gateway invoke, Optimixer merge | Response metadata / trace when enabled | | Catalog / registry | discoverStructure, diagnose, runDiagnostics completion only | List/probe/connection tests, client init/shutdown | N/A |

Failures on invoke and config validation emit at error / warnCode with correlation ids.

.env: with default autoLoadDotenv, the client loads .env from the current working directory using loadDotenv from @x12i/env.

# Threshold for this package
export AI_SKILLS_LOGS_LEVEL=info

# Optional: full payloads (still requires _LOGS_LEVEL=verbose to print)
export AI_SKILLS_VERBOSE=true

# Bulk stack config (loaded at startup)
export LOGXER_PACKAGE_LEVELS=AI_SKILLS:debug,AI_GATEWAY:warn

Programmatic stack (logxer ≥ 4.5):

import { type StackLoggingOptions } from "@x12i/logxer";
import { ExellixSkillsClient, AI_SKILLS_LOGXER_ENV_PREFIX, createCataloxFromEnv } from "@exellix/ai-skills";

const logging: StackLoggingOptions = {
  packageLevels: { [AI_SKILLS_LOGXER_ENV_PREFIX]: "debug" },
};

const skills = new ExellixSkillsClient({
  catalox: createCataloxFromEnv(),
  logging,
  verbose: true,
});

Filter Mongo / shadow logs with getJobLogs({ package: "AiSkills", ... }).

Testing (integration + templates)

This repo is ESM ("type": "module"). Use the npm scripts below (they run via tsx for .ts entrypoints).

Default test gate (npm test): npm run build then npm run test:integration (may append live catalog tests when env enables them). npm run test:ci runs npm run build and npm run test:unit (mocked gateway / no Firestore / no LLM).

npm test                       # build + integration harness (see run.ts)
npm run test:ci                # build + deterministic unit slice (CI-safe)
npm run test:unit              # same unit slice as test:ci (without build)
npm run test:integration       # full integration harness (run.ts)
npm run catalox:provision-ai-skills   # upsert catalog metadata + Optimixer fields (before live tests)
npm run catalox:provision-ai-engines  # upsert in-repo engine rows → native `ai-engines`
npm run catalox:verify                # validate live Catalox rows for runtime requirements
npm run live:professional-answer      # smoke: professional-answer via Catalox + default gateway LLM
npm run live:re-consider              # smoke: all five re-consider direct functions via Catalox + LLM
npm run test:real              # extra real LLM scenarios; needs .env + valid Firebase credentials

Live tests (opt-in, test/run.ts)

Some cases use real Catalox (Mongo-backed catalog) and the same integration slice can invoke real LLM calls when provider keys are set. They are off by default so CI does not require a provisioned project.

Set AI_SKILLS_LIVE_TESTS=1 (or true / yes / on) before npm run test:integration to append the live cases registered in test/run.ts:

  • Catalox ai-skills list — read-only catalog list sanity check.
  • Catalox ai-engines list — verifies npm run catalox:provision-ai-engines wrote the ai-gateway row (read-only).
  • Re-consider direct functions — real LLM calls for fillReConsiderationGap, reviewReConsiderationResponse, rewriteReConsiderationScope, rewriteReConsiderationFull, calibrateReConsiderationDecision (provision + sync templates first).

Unit mocks for re-consider live only under test/ (CI wiring via fake gateway). Production code and npm run live:re-consider use real Catalox + real LLM only — no mock fallbacks.

Why not in @x12i/catalox: only this package’s test harness reads AI_SKILLS_LIVE_TESTS; Catalox does not gate downstream CI.

Optionally run npm run test:real before a major release (broader LLM + client scenarios; needs Mongo + provider keys in .env).

Flex-MD log noise (extractJsonFromFlexMd / require(...) is not a function): that path lives in @x12i/ai-gateway, not in this package’s parseFlexMd. See docs/FLEX_MD_AND_TESTING.md for the split of responsibilities and acceptance notes.

Model Configuration

You can control which model is used and how it generates responses on a per-request basis using the modelConfig field.

Model Configuration Options

interface ModelConfig {
  /** Model identifier (e.g., "gpt-4-turbo", "claude-3-opus", "gpt-3.5-turbo") */
  model?: string;
  
  /** Model ID (alternative to model name, for provider-specific model IDs) */
  modelId?: string;
  
  /** Provider name (e.g., "openai", "anthropic") */
  provider?: string;
  
  /** Temperature for generation (0.0 to 2.0) - controls randomness */
  temperature?: number;
  
  /** @deprecated Caller maxTokens is stripped — Optimixer sets completion budget from Catalox catalog. */
  maxTokens?: never;
  
  /** Top-p (nucleus) sampling parameter (0.0 to 1.0) */
  topP?: number;

  /** Optimixer reasoning depth; overrides Catalox catalog default when set. */
  reasoningEffort?: "not-applicable" | "none" | "low" | "medium" | "high";
  
  /** Frequency penalty (-2.0 to 2.0) */
  frequencyPenalty?: number;
  
  /** Presence penalty (-2.0 to 2.0) */
  presencePenalty?: number;
  
  /** Stop sequences (array of strings) */
  stop?: string[];
  
  /** Additional provider-specific parameters */
  [key: string]: any;
}

Usage Examples

Basic Model Selection

const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Your question here",
  modelConfig: {
    model: "gpt-4-turbo"
  }
});

Full Model Configuration

const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Your question here",
  modelConfig: {
    model: "gpt-4-turbo",
    temperature: 0.7,
    topP: 0.9,
    reasoningEffort: "medium",
    frequencyPenalty: 0.5
  }
});

Per-node reasoning depth

const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Your question here",
  jobId: "job-1",
  taskId: "task-1",
  agentId: "agent-abc",
  jobTypeId: "your-job-type-id",
  taskTypeId: "your-task-type-id",
  modelConfig: {
    model: "gpt-4-turbo",
    reasoningEffort: "high", // overrides Catalox optimixerReasoningEffort for this invoke
  },
});

Provider Override

const res = await skills.runSkill({
  skillKey: "skills/professional-answer",
  input: "Your question here",
  modelConfig: {
    provider: "anthropic",
    model: "claude-3-opus",
    temperature: 0.3
  }
});

Notes

  • modelConfig.model / modelId are required (concrete wire id). Missing or alias-shaped values throw before gateway.invoke(). See docs/MODEL_INVOKE_CONTRACT.md.
  • maxTokens on the request are stripped; Optimixer sets the cap from Catalox catalog fields and resolved reasoningEffort.
  • reasoningEffort on the request overrides the Catalox catalog default for that skill key.
  • This package does not merge a global default modelConfig at construction time; only templateRendering has client-wide defaults for template rendering.
  • The gateway and provider enforce valid ranges (e.g. temperature).
  • OpenRouter routing policy (PREFER_OPENROUTER) is not applied in this package. Orchestrators (@exellix/ai-tasks, graph-engine) resolve the plan and set the wire model on modelConfig before runSkill(). See docs/PREFER_OPENROUTER_ROUTING.md.

API Reference

ExellixSkillsClient

Constructor options (essential)

import type { Catalox } from "@x12i/catalox";
import type { TemplateRenderOptions } from "@x12i/ai-gateway";

interface ExellixSkillsClientOptions {
  /** Required. Skill bodies are read from the native `ai-skills` catalog. */
  catalox: Catalox;
  /** Optional; defaults to catalog app id `ai-skills`. */
  cataloxAppId?: string;

  packageName?: string;
  enableActivityTracking?: boolean;
  /** Optional external gateway; skill templates still load from `catalox`. */
  gateway?: AIGateway;

  templateRendering?: TemplateRenderOptions;
  autoLoadDotenv?: boolean;
  testMode?: boolean;
  disableActivityTrackingInTests?: boolean;
}

The built-in AIGateway has no nx-content / GitHub skill registry; GITHUB_* is not required for ExellixSkillsClient initialization.

Methods

  • runSkill<T>(input: RunSkillRequest): Promise<RunSkillResponse<T>> — loads templates from Catalox, invokes gateway with inline bodies. Optional outputContract enriches parsed and the Activix activity row (see Cost and output contract).
  • getSkillTemplatesForPresentation(skillKey, opts?) — editor-oriented markdown + metadata.
  • updateSkillTemplatesFromPresentation(skillKey, patch, opts?) — save edited markdown (lossless storage normalization); requires Catalox write context.
  • getSkillTemplateInputs(skillKey){{token}} discovery from stored bodies.
  • listPublishedSkills(options?) — Catalox rows with status === "published".
  • listCatalogSkills(options?) — all catalog rows (any status); optional Catalox list query options.
  • upsertSkillCatalogItem(input, options?) — create/update catalog metadata and template bodies; options.ifNotExists enforces create-only.
  • softDeleteSkillCatalogItem(skillKey) — clears template and audit text and sets planned.

Template bodies are loaded only through runSkill (and the same Catalox fetch used there): pass skillKey; there is no separate “peek at one section string” API.

Diagnostic methods (Catalox catalog)

  • testCatalogConnection(), discoverCatalogStructure(), diagnoseSkillInCatalog(), listAvailableSkills(), runCatalogDiagnostics() — Catalox ai-skills catalog reachability and discovery.

For {{token}} discovery on stored bodies without invoking the LLM, use getSkillTemplateInputs(skillKey) (Catalox read + parse).

FlexMD 2.0 Format

Primary skills are designed around structured markdown (headings and/or Flex-MD style markers). A common explicit shape is:

[[professional-answer]]

@payload:shortAnswer
Brief answer here...

@payload:fullAnswer
Detailed answer here...

@payload:assumptions
List of assumptions...

The gateway normalizes responses with its own Flex-MD extraction. When it does not return parsed fields, this SDK uses parseFlexMd (src/utils/flex-md-parser.ts), which prefers the flex-md package via ESM import() and falls back to parsing [[frame:...]] + @payload: blocks.

When you pass outputContract, the SDK also maps markdown ### Section headings to camelCase keys (e.g. ### Full AnswerfullAnswer) so professional-answer templates that use headings still populate parsed even without @payload: markers. See Cost and output contract.

Details and caveats: docs/FLEX_MD_AND_TESTING.md.

Requirements

  • Node.js 20+ (see package.json engines; aligns with @x12i/catalox)
  • @x12i/catalox ≥ 4 and Firebase access for the ai-skills native catalog (see createCataloxFromEnv and Environment (Firebase & Catalox v4))
  • @x12i/ai-gateway ^10.4.4 (see package.json; shares @x12i/activix ^9.0 with this package)
  • @x12i/ai-profiles ^3.0.0 (profile/choice → wire model resolution)
  • @x12i/activix ^8.6.0 (direct dependency for activity-row enrichment types/patches)
  • At least one LLM provider key when using the default constructed gateway

Downstream orchestrators: @exellix/ai-tasks ≥ 8.7.0 depends on this package (≥ 6.4.0) and @exellix/xynthesis ≥ 4.5.0 for the full PRE/MAIN/POST stack — see docs/UPGRADING_AI_GATEWAY_10.md.

Related runtime packages

Task and graph orchestration live in sibling packages, not in @exellix/ai-skills:

  • @exellix/ai-tasks — task orchestration and task-level runtime wiring (≥ 8.7.0 pins @exellix/ai-skills ≥ 6.4.0 + @exellix/xynthesis ≥ 4.5.0; gateway 10.x billing and invoke rules apply to MAIN via this package)
  • @exellix/graph-engine — graph execution and graph-level runtime wiring; forward each node’s inputs.outputContract on runSkill when Run Analysis or downstream validation require structured parsed fields
  • @exellix/exellix-runtime — root runtime composition (for example loading composed runtimeObjects for debug tooling)

Older scopes such as @woroces/*, worox, worex, and graph packages named worox-graph / worex-graphs are not used here; there are no intentional aliases or compatibility shims for those names in this repository.

License

ISC