Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { ComponentInfo, DataMapperMetadata, Diagnostics, DMModel, ImportStatemen
export type AIPanelPrompt =
| { type: 'command-template'; command: Command; templateId: TemplateId; text?: string; params?: Record<string, string>; metadata?: Record<string, any>; hiddenContext?: string }
| { type: 'text'; text: string; planMode: boolean; codeContext?: CodeContext; autoSubmit?: boolean; hiddenContext?: string; suggestedCommandTemplates?: AIPanelPrompt[]; inputPlaceholder?:string; }
| { type: 'skill'; skillId: string; skillName: string; args?: string; autoSubmit?: boolean; hiddenContext?: string }
| { type: 'skill'; skillId: string; skillName: string; args?: string; tagParams?: Record<string, string>; autoSubmit?: boolean; hiddenContext?: string }
| undefined;

export interface AIMachineSnapshot {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com/) All Rights Reserved.

// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

export const SKILL_USAGE_RULES = `**Rules for using skills**:
- **One at a time**: Always invoke one skill per message. Never call \`invoke_skill\` for multiple skills in the same turn — load each skill, apply it, then decide if another is needed.
- **Silent operation**: Never announce or narrate skill usage in text. Call \`invoke_skill\` directly and apply the rules silently.
- **Priority over tools**: Skills take priority over direct tool calls. Before using tools like \`LibrarySearchTool\`, \`web_search\`, \`web_fetch\`, or invoking sub-skills, always check first whether any skill's trigger condition is met and invoke that skill. Follow all tool references (web search, library selection, sub-skill invocations) mentioned in the skill content rather than the default workflow steps.`;
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

import * as path from 'path';
import { ProjectSkillMeta } from './types';
import { getDisabledBuiltIns, REGISTERED_SKILLS } from './index';
import { REGISTERED_SKILLS } from './registry';
import { getDisabledBuiltIns } from './index';
import { scanProjectSkills, scanUserSkills } from '../tools/skill-tool/skill-reader';
import { getSkillsConfig, GLOBAL_SKILLS_CONFIG_PATH } from '../tools/skill-tool/skill-writer';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,121 @@
name: data-map
description: Use this skill whenever you are generating Ballerina mapping/transformation expressions between any data types — records, JSON, XML, arrays, or primitive types (e.g. implementing a transform function body, converting JSON to a record, mapping XML elements to fields, or transforming primitive values).
---

### Mapping Request
$ARGUMENTS

Interpret the request above using whichever of these patterns fits:
- "generate mappings for the <functionname> function" → function mode; the function name is the word immediately before "function".
- "generate mappings using input as <inputs> and output as <output> using the <name> function" → records mode; create a new transform function with that signature.
- "generate mappings using record fields and external values" → inline mode; derive the output type from "Output type: <name>" in the hidden context.
- A single function name (no surrounding text) → function mode; fill out the existing function with the mapping.
- A single record/type name with no surrounding text and no function context → inline mode; produce inline field expressions, not a function body.
- Empty (literal `$ARGUMENTS` left in place) → derive everything from the current file context provided in the hidden context.

### Priority Hierarchy
When generating mapping expressions, follow this strict priority order:
1. **User-defined mappings** — ABSOLUTE HIGHEST PRIORITY. Complete precedence over everything else.
2. **Existing sub-mappings** — use the sub-mapping output name as a direct reference.
3. **Context and constraints** — apply all provided business rules and transformation logic.
4. **Ballerina programming knowledge** — use built-in functions and standard approaches.
5. **Default handling** — only for non-optional fields when no mapping is available.

### Mapping Output Modes

There are two distinct mapping modes. Choose the correct one based on the task:

**Mode 1 — Transform Function (reusable mapping)**
When implementing a named Ballerina transform function, you MUST use the expression body syntax with `=>`. This is critical: the `=>` expression body is what the Ballerina tooling uses to classify the function as a Data Mapper. A block-body function with `return` will be placed under "Functions" instead of "Data Mappers" and will not work as a data mapper.

- The syntax is: `function name(params) returns Type => { field: expression, ... };`
- NEVER write a block body with `{ ... }` braces and a `return` statement — that produces a regular function, not a data mapper.
- The `{...}` after `=>` is a record constructor. Each entry is `outputField: expression`. Do not use `return` or variable declarations inside it.
- For intermediate values, use a `let` expression before the record constructor (see the `let` section below).

**Mode 2 — Inline Field Expression**
When mapping individual output fields directly inside a service or resource function (not writing a transform function body) — for example, when constructing a record variable inline — produce a single standalone expression for each field:

- Do **NOT** use `=>`, function declarations, or `let` expressions — just the expression itself.
- Do **NOT** use `return` statements.
- If complex logic is needed, define a separate helper function and call it from the expression.

### Schema and Type Handling
- Never use generic types like `anydata` or `any`; use exact type names from the schema.
- For nullable/optional types, always use the `?` suffix (e.g. `string?`), never `string|()`.
- Only reference fields and symbols that exist in the schema.
- Use existing submappings within the data model schema when available.
- Ensure type compatibility between input and output fields.
- For imported package records, use only the package alias (the part after the colon).
- When declaring nullable types, use ONLY the `?` suffix (e.g. `string?`, `CustomType?`). Never combine `|()` with `?`.

### Field Access
- Use `?.` (safe navigation) **only** when the field is actually optional or nullable.
- Use `.` (dot notation) for non-optional, non-nullable fields.
- Same-type input/output → direct assignment, even for optional fields.
- Different types → apply appropriate transformation/conversion.
- For output field names, always use dot notation from the root level.

### Union Types and Enums
- When either input or output is a union type or enum, always create a custom helper function — never handle inline.
- For nested union types, create a separate function per nesting level — each function must handle only one level of union complexity.
- Handle type narrowing using `is` checks or `if-else` type narrowing inside helper functions.
- Use exact type names from the schema in all function signatures.

### Mapping Strategy
- Map at **field level**, not at record or array level.
- Break down complex structures and map their individual components.
- For arrays of records, analyze individual fields within those records.
- Use query expressions (`from var x in inputArray select {...}`) **only** when both input and output are arrays — otherwise do not use this pattern.
- For nested record structures, use record constructor expressions calling appropriate helpers.

### Custom Functions for Advanced Transformations
Define separate Ballerina functions for logic that cannot be expressed as a simple inline expression:
- Union type / enum handling
- Multi-step computations reused across multiple output fields
- Any transformation requiring conditionals or loops

Place all helper function definitions before the main transform function.

### `let` Expressions for Reusable Sub-Mappings
Use a `let` expression only in these two cases:
- The user explicitly requests sub-mappings in their prompt, or
- The same intermediate value is needed by multiple output fields (sub-mapping reuse)

- The syntax is: `function name(params) returns Type => let Type varName = computedExpression in { fieldA: varName, fieldB: varName, ... };`
- The `let` binding goes between `=>` and the record constructor, with `in` separating them.
- Use `let` **only** inside an expression-body function for reusable intermediate values.
- Do **not** use `let` clause inside query expressions (`from ... select`).
- For complex logic that cannot be expressed with a single `let`, define a separate helper function instead.

### Regular Expression Operations
- Use Ballerina's `lang.regexp` library: `import ballerina/lang.regexp;`
- Create RegExp values with the `re` template: `` re `[0-9]+` ``
- Common functions: `regexp:isFullMatch()`, `regexp:find()`, `regexp:findAll()`, `regexp:replace()`, `regexp:replaceAll()`, `regexp:split()`

### Reserved Keywords
Ballerina reserved keywords cannot be used as plain identifiers. Prefix them with a single quote (`'`) when used as field names, variable names, or loop element variables.
Reserved keywords: {{KEYWORDS}}

### Ballerina Syntax Rules
- Write syntactically correct Ballerina — no compilation errors.
- Use `.toString()` for type conversion to strings.
- Use `check` for error handling — never `trap` or `panic`.
- Handle union types and enums using `check` expressions or `if-else` type narrowing.
- For nested field access, use dot notation.
- Use Ballerina built-in methods for transformations.
- Prefer record constructor expressions for nested structures.
- Only provide default values for non-optional fields that have no available mapping.
- Do not provide default values for fields that have explicit mappings or for optional fields.

### Repairing Mapping Errors
After writing mapping code, check for compilation errors. If errors appear on a mapping expression, fix them focusing on:

1. **Type compatibility** — ensure the expression produces the correct type for the output field.
2. **Field access** — use correct syntax for accessing record fields; required fields use `record.field`, optional fields use `record?.field`.
3. **Null safety** — handle optional/nilable types appropriately.
4. **Function calls** — verify imported functions are called correctly.
5. **Type conversions** — add necessary type casts or conversions.
6. **Syntax errors** — fix any Ballerina syntax issues.

Repeat until there are no errors on the mapping expressions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,11 @@
// under the License.

import { Skill, ProjectSkillMeta } from './types';
import { dataMapSkill } from './data-map';
import { skillCreatorSkill } from './skill-creator';
import { REGISTERED_SKILLS } from './registry';
import { SKILL_USAGE_RULES } from './constants';

export type { Skill, ProjectSkillMeta };

export const REGISTERED_SKILLS: Skill[] = [
dataMapSkill,
skillCreatorSkill,
];
export { REGISTERED_SKILLS };

function formatSkill(skill: Skill | ProjectSkillMeta): string {
return `## Skill: ${skill.name}
Expand All @@ -33,11 +29,6 @@ function formatSkill(skill: Skill | ProjectSkillMeta): string {
**How to use**: Call the \`invoke_skill\` tool with \`skillName="${skill.name}"\` to fetch the full rules.`;
}

const SKILL_USAGE_RULES = `**Rules for using skills**:
- **One at a time**: Always invoke one skill per message. Never call \`invoke_skill\` for multiple skills in the same turn — load each skill, apply it, then decide if another is needed.
- **Silent operation**: Never announce or narrate skill usage in text. Call \`invoke_skill\` directly and apply the rules silently.
- **Priority over tools**: Skills take priority over direct tool calls. Before using tools like \`LibrarySearchTool\`, \`web_search\`, \`web_fetch\`, or invoking sub-skills, always check first whether any skill's trigger condition is met and invoke that skill. Follow all tool references (web search, library selection, sub-skill invocations) mentioned in the skill content rather than the default workflow steps.`;

export function getBuiltInSkillsSection(disabledSkills?: Set<string>): string {
const activeSkills = disabledSkills
? REGISTERED_SKILLS.filter(s => !disabledSkills.has(s.name))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,23 @@
// specific language governing permissions and limitations
// under the License.

import skillMd from './SKILL.md';
import { getDataMappingSkillContent } from '../../../data-mapper/prompts/mapping-prompt';
import { DIAGNOSTICS_TOOL_NAME } from '../../tools/diagnostics';
import { SkillCommand } from '@wso2/ballerina-core';
import { Skill } from '../types';
import { keywords, SkillCommand } from '@wso2/ballerina-core';
import { Skill } from './types';
import { parseSkillMd } from './utils';
import dataMapMd from './data-map/SKILL.md';
import skillCreatorMd from './skill-creator/SKILL.md';

function parseSkillMd(content: string): { name: string; description: string } {
const start = content.indexOf('---');
const end = content.indexOf('---', start + 3);
const frontmatter = start !== -1 && end !== -1 ? content.slice(start + 3, end) : content;
return {
name: /^name:\s*(.+)$/m.exec(frontmatter)?.[1]?.trim() ?? '',
description: /^description:\s*(.+)$/m.exec(frontmatter)?.[1]?.trim() ?? '',
};
}

const { name, description } = parseSkillMd(skillMd);

if (!name || !description) {
throw new Error(`[data-map] SKILL.md is missing required frontmatter fields (name="${name}", description="${description}")`);
// data-map skill
const dataMap = parseSkillMd(dataMapMd);
if (!dataMap.name || !dataMap.description) {
throw new Error(`[data-map] SKILL.md is missing required frontmatter fields (name="${dataMap.name}", description="${dataMap.description}")`);
}
const keywordList = keywords.map((k: string) => `\`${k}\``).join(', ');

export const dataMapSkill: Skill = {
name,
trigger: description,
content: getDataMappingSkillContent(DIAGNOSTICS_TOOL_NAME),
name: dataMap.name,
trigger: dataMap.description,
content: dataMap.body.replace('{{KEYWORDS}}', keywordList),
optional: false,
default: true,
skillCommand: SkillCommand.DataMap,
Expand Down Expand Up @@ -68,3 +59,19 @@ export const dataMapSkill: Skill = {
},
],
};

// skill-creator skill
const skillCreator = parseSkillMd(skillCreatorMd);

export const skillCreatorSkill: Skill = {
name: skillCreator.name,
trigger: skillCreator.description,
content: skillCreator.body,
optional: true,
default: false,
};

export const REGISTERED_SKILLS: Skill[] = [
dataMapSkill,
skillCreatorSkill,
];
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@
// specific language governing permissions and limitations
// under the License.

import skillMd from './SKILL.md';
import { Skill } from '../types';

function parseSkillMd(content: string): { name: string; description: string; body: string } {
export function parseSkillMd(content: string): { name: string; description: string; body: string } {
const start = content.indexOf('---');
const end = content.indexOf('---', start + 3);
const frontmatter = start !== -1 && end !== -1 ? content.slice(start + 3, end) : content;
Expand All @@ -28,13 +25,3 @@ function parseSkillMd(content: string): { name: string; description: string; bod
body,
};
}

const { name, description, body } = parseSkillMd(skillMd);

export const skillCreatorSkill: Skill = {
name,
trigger: description,
content: body,
optional: true,
default: false,
};

This file was deleted.

Loading
Loading