Merge branch 'main' into u/h.kapoor/update_experts_rule

This commit is contained in:
h-kapoor-sfdc 2026-03-13 10:36:45 +05:30 committed by GitHub
commit 3e7b93e51d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 2998 additions and 9 deletions

43
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,43 @@
**References:** [Contributing guide](../CONTRIBUTING.md) · [Skill authoring guide](../README.md) · [Agent Skills spec](https://agentskills.io/specification)
## What changed
<!-- Briefly describe what skill(s) were added, updated, or removed. -->
## Why
<!-- What gap does this fill, or what problem does it solve? -->
## Notes
<!-- Anything reviewers should know — testing approach, follow-ups, open questions. -->
---
## Skills
### Manual checklist
**Description quality**
- [ ] Describes what the skill does and the expected output
- [ ] Includes relevant Salesforce domain keywords (Apex, LWC, SOQL, metadata types, etc.)
- [ ] Trigger phrases are specific enough for Vibes to select this skill reliably
**Instructions**
- [ ] Clear goal statement
- [ ] Step-by-step workflow
- [ ] Validation rules for generated output
- [ ] Defined output / artifact
**Context efficiency**
- [ ] Core instructions are concise — supporting material lives in `templates/`, `examples/`, or `docs/` subdirectories
- [ ] No unnecessary background explanation in the body
### Automated checks
Enforced by CI ([`npm run validate:skills`](../scripts/validate-skills.ts)) per the [Agent Skills spec](https://agentskills.io/specification):
- Directory is one level deep, named in kebab-case (max 64 chars), contains `SKILL.md`
- Frontmatter `name` matches directory name; `description` is present, ≥ 20 words, ≤ 1024 characters, and includes trigger language
- Body is non-empty and under 500 lines
- Name uses gerund form ⚠ (warning — does not block merge)

91
.github/workflows/release-skills.yml vendored Normal file
View File

@ -0,0 +1,91 @@
name: release-skills
on:
push:
branches: [main]
paths: ['skills/**']
workflow_dispatch:
inputs:
ref:
description: "Branch or tag to release from (must be main)"
required: false
default: main
permissions:
contents: write
packages: write
jobs:
release:
runs-on: ubuntu-latest
outputs:
skipped: ${{ steps.changelog.outputs.skipped }}
version: ${{ steps.changelog.outputs.version }}
steps:
- name: Require main branch
if: github.event_name == 'workflow_dispatch'
run: |
if [ "${{ github.ref_name }}" != "main" ]; then
echo "Releases must be run from the main branch (got '${{ github.ref_name }}')."
exit 1
fi
- uses: actions/checkout@v4
with:
token: ${{ secrets.IDEE_GH_TOKEN }}
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- run: npm ci
- name: Validate skills
run: npm run validate:skills
- name: Conventional Changelog Action
id: changelog
uses: TriPSs/conventional-changelog-action@v5
with:
git-user-name: svc-idee-bot
git-user-email: svc_idee_bot@salesforce.com
github-token: ${{ secrets.IDEE_GH_TOKEN }}
tag-prefix: ""
release-count: "0"
skip-on-empty: ${{ github.event_name == 'push' }}
version-file: "package.json"
output-file: "CHANGELOG.md"
- name: Create Github Release
id: release
uses: ncipollo/release-action@v1
if: ${{ steps.changelog.outputs.skipped == 'false' }}
with:
name: "${{ steps.changelog.outputs.version }}"
tag: "${{ steps.changelog.outputs.version }}"
commit: ${{ github.sha }}
body: |
## Changes in @salesforce/afv-skills
${{ steps.changelog.outputs.clean_changelog }}
token: ${{ secrets.IDEE_GH_TOKEN }}
skipIfReleaseExists: true
- name: Publish to npm
id: publish
if: ${{ steps.changelog.outputs.skipped == 'false' && steps.release.outputs.id != '' }}
run: |
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
npm publish --access public
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Notify cline-fork
if: ${{ steps.changelog.outputs.skipped == 'false' && steps.release.outputs.id != '' && steps.publish.outcome == 'success' }}
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.IDEE_GH_TOKEN }}
repository: forcedotcom/cline-fork
event-type: skills-released
client-payload: '{"version": "${{ steps.changelog.outputs.version }}"}'

10
.github/workflows/validate-pr.yml vendored Normal file
View File

@ -0,0 +1,10 @@
name: Validate PR Title
on:
pull_request:
types: [opened, reopened, edited]
branches: [main]
jobs:
pr-title:
uses: salesforcecli/github-workflows/.github/workflows/validatePR.yml@main

29
.github/workflows/validate-skills.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Validate Skills
on:
pull_request:
jobs:
validate-skills:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- run: npm ci
# Run against only changed skills when skills files are touched; fall back
# to full corpus validation when only tooling files changed, so validator
# regressions against existing skills are caught in the same PR.
- name: Validate skills
run: |
if git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -q '^skills/'; then
npm run validate:skills -- --changed --base=origin/${{ github.base_ref }}
else
npm run validate:skills
fi

2
.nvmrc
View File

@ -1 +1 @@
v20.18.1
v22.22.1

11
CHANGELOG.md Normal file
View File

@ -0,0 +1,11 @@
# 1.1.0 (2026-03-12)
### Features
* add first pass at b2e sample ([ce615bf](https://github.com/forcedotcom/afv-library/commit/ce615bf0361e4a6a8f5635726ce304e6de921413))
* add metadata type skills ([3689f14](https://github.com/forcedotcom/afv-library/commit/3689f14af7eeb1dd8ea80ae206437f3483d37938))
* add skills from 260 ([65947b1](https://github.com/forcedotcom/afv-library/commit/65947b15509fef576ac683226976ee3d60a624f3))

552
package-lock.json generated
View File

@ -1,13 +1,459 @@
{
"name": "afv-library",
"name": "@salesforce/afv-skills",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "afv-library",
"name": "@salesforce/afv-skills",
"version": "1.0.0",
"license": "CC-BY-NC-4.0",
"devDependencies": {
"@salesforce/webapp-template-app-react-sample-b2e-experimental": "*",
"@salesforce/webapp-template-app-react-sample-b2x-experimental": "*"
"@salesforce/webapp-template-app-react-sample-b2x-experimental": "*",
"tsx": "^4.21.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
"integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
"integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
"integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
"integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
"integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
"integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
"integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
"integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
"integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
"integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
"integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
"integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
"integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
"integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
"integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
"integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
"integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
"integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
"integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
"integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
"integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
"integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
"integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
"integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
"integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
"integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@salesforce/webapp-template-app-react-sample-b2e-experimental": {
@ -22,6 +468,106 @@
"integrity": "sha512-ry4U36CjJx9h7pv5R5q0tVQCHkEvEYm/4u/5Ysh3UqTz89s3B9o/GPb0JK9fZ2Mgt/DCd0zfPUm1oRlCplnhGQ==",
"dev": true,
"license": "SEE LICENSE IN LICENSE.txt"
},
"node_modules/esbuild": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.27.3",
"@esbuild/android-arm": "0.27.3",
"@esbuild/android-arm64": "0.27.3",
"@esbuild/android-x64": "0.27.3",
"@esbuild/darwin-arm64": "0.27.3",
"@esbuild/darwin-x64": "0.27.3",
"@esbuild/freebsd-arm64": "0.27.3",
"@esbuild/freebsd-x64": "0.27.3",
"@esbuild/linux-arm": "0.27.3",
"@esbuild/linux-arm64": "0.27.3",
"@esbuild/linux-ia32": "0.27.3",
"@esbuild/linux-loong64": "0.27.3",
"@esbuild/linux-mips64el": "0.27.3",
"@esbuild/linux-ppc64": "0.27.3",
"@esbuild/linux-riscv64": "0.27.3",
"@esbuild/linux-s390x": "0.27.3",
"@esbuild/linux-x64": "0.27.3",
"@esbuild/netbsd-arm64": "0.27.3",
"@esbuild/netbsd-x64": "0.27.3",
"@esbuild/openbsd-arm64": "0.27.3",
"@esbuild/openbsd-x64": "0.27.3",
"@esbuild/openharmony-arm64": "0.27.3",
"@esbuild/sunos-x64": "0.27.3",
"@esbuild/win32-arm64": "0.27.3",
"@esbuild/win32-ia32": "0.27.3",
"@esbuild/win32-x64": "0.27.3"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/get-tsconfig": {
"version": "4.13.6",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
"integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==",
"dev": true,
"license": "MIT",
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
},
"funding": {
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/tsx": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "~0.27.0",
"get-tsconfig": "^4.7.5"
},
"bin": {
"tsx": "dist/cli.mjs"
},
"engines": {
"node": ">=18.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
}
}
}
}

View File

@ -1,12 +1,22 @@
{
"name": "afv-library",
"description": "AI prompts and rules library for Agentforce Vibes development",
"private": true,
"name": "@salesforce/afv-skills",
"version": "1.1.0",
"description": "Salesforce skills for Agentforce Vibes",
"license": "CC-BY-NC-4.0",
"files": [
"skills/"
],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
},
"devDependencies": {
"tsx": "^4.21.0",
"@salesforce/webapp-template-app-react-sample-b2e-experimental": "*",
"@salesforce/webapp-template-app-react-sample-b2x-experimental": "*"
},
"scripts": {
"validate:skills": "tsx scripts/validate-skills.ts",
"sync-react-b2e-sample": "node scripts/sync-react-b2e-sample.js",
"sync-react-b2x-sample": "node scripts/sync-react-b2x-sample.js"
}

View File

@ -41,3 +41,14 @@ Source is synced from the npm package [@salesforce/webapp-template-app-react-sam
### Version tracking
The file `samples/webapp-template-app-react-sample-b2x-experimental/.version` stores the last-synced npm version. The Action compares it to the latest on npm and only creates a PR when they differ.
## native-mobile-rental-tenant-app
A sample Custom Agentic Mobile App (CAMA) for rental property tenants. This sample is maintained directly in this repository (not synced from npm). It includes:
- **digitalExperiences** metadata: CAMA app config (`experience__camaAppMetadata`), build metadata (`experience__camaBuildMetadata`), EC definition (`experience__camaECDefinition`), and screens (`experience__camaScreen`) with tabs (Home, Tenants, Properties), theme, and toolbar settings
- **Source**: Synced from [cama-mcp-server](https://git.soma.salesforce.com/khawkins/cama-mcp-server) (branch `apply_metadata_updates`)
### How it's used
The sample appears on the Agentforce Vibes welcome page under the **Mobile** app type. Users can clone it via the welcome page wizard or directly from this repo.

View File

@ -0,0 +1,15 @@
# List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status
# More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm
#
package.xml
# LWC configuration files
**/jsconfig.json
**/.eslintrc.json
# LWC Jest
**/__tests__/**
node_modules/
.DS_Store

View File

@ -0,0 +1,20 @@
# Dependencies
node_modules/
# Salesforce
.sfdx/
.localdevserver/
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Logs
*.log
npm-debug.log*

View File

@ -0,0 +1,9 @@
# Native Mobile Rental Tenant App
A sample Custom Agentic Mobile App (CAMA) for property managers and tenants. Use this as a starting point for building native mobile apps with Salesforce Mobile Publisher, MCF (Mobile Component Framework), and SharedUI.
## Overview
This sample demonstrates a rental tenant experience—browse properties, submit maintenance requests, pay rent, and communicate with property management—all from a native iOS or Android app.

View File

@ -0,0 +1,10 @@
{
"orgName": "Rental Tenant Dev",
"edition": "Developer",
"features": [],
"settings": {
"mobileSettings": {
"enableS1EncryptedStoragePref2": false
}
}
}

View File

@ -0,0 +1,5 @@
{
"apiName": "appMetadata",
"type": "experience__camaAppMetadata",
"path": "appMetadata"
}

View File

@ -0,0 +1,41 @@
{
"type": "experience__camaAppMetadata",
"title": "App Metadata",
"contentBody": {
"name": "Homestead",
"description": "Mobile application for managing property rentals, tenant information, lease agreements, and maintenance requests.",
"version": "1.0.0",
"url": "http://login.test1.pc-rnd.salesforce.com/",
"templateID": "1",
"tabs": [
{
"id": "home",
"label": "Home",
"icon": "house.fill",
"screen": "homeScreen"
},
{
"id": "tenants",
"label": "Tenants",
"icon": "person.2.fill",
"screen": "tenantsScreen"
},
{
"id": "properties",
"label": "Properties",
"icon": "building.2.fill",
"screen": "propertiesScreen"
}
],
"toolbarActions": {
"notifications": false,
"search": false,
"agentforce": false
},
"theme": {
"accentColor": "#9400D3",
"primaryColor": "#000000",
"secondaryColor": "#B3B3B3"
}
}
}

View File

@ -0,0 +1,5 @@
{
"apiName": "buildMetadata",
"type": "experience__camaBuildMetadata",
"path": "buildMetadata"
}

View File

@ -0,0 +1,8 @@
{
"type": "experience__camaBuildMetadata",
"title": "Build Metadata",
"contentBody": {
"BiometricOptIn": false,
"NotificationOptIn": false
}
}

View File

@ -0,0 +1,5 @@
{
"apiName": "rentalApp",
"type": "experience__camaECDefinition",
"path": "rentalApp"
}

View File

@ -0,0 +1,5 @@
{
"type": "experience__camaECDefinition",
"title": "CAMA EC Definition",
"contentBody": {}
}

View File

@ -0,0 +1,5 @@
{
"apiName": "homeScreen",
"type": "experience__camaScreen",
"path": "homeScreen"
}

View File

@ -0,0 +1,489 @@
{
"type": "experience__camaScreen",
"title": "Home Screen",
"contentBody": {
"view": {
"definition": "homestead/HomeScreen",
"properties": {
"pullToRefresh": true
},
"regions": {
"components": {
"components": [
{
"definition": "ui/container",
"properties": {
"padding": "$spacing.spacing4",
"backgroundColor": "$colors.surface2"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/card",
"properties": {},
"regions": {
"components": {
"components": [
{
"definition": "ui/column",
"properties": {
"width": "fill",
"height": "fill",
"alignment": "start",
"gap": "$spacing.spacing4",
"padding": "$spacing.spacing4"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/text",
"properties": {
"text": "Today",
"style": "titlesFontScale4Semibold",
"color": "$colors.onSurface1"
},
"regions": {}
},
{
"definition": "ui/text",
"properties": {
"text": "You have no event.",
"color": "$colors.onSuccess1",
"style": "bodyFontScale2Regular"
},
"regions": {}
}
]
}
}
}
]
}
}
},
{
"definition": "ui/card",
"properties": {},
"regions": {
"components": {
"components": [
{
"definition": "ui/column",
"properties": {
"width": "fill",
"height": "fill",
"alignment": "start",
"gap": "$spacing.spacing4",
"padding": "$spacing.spacing4"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/text",
"properties": {
"text": "Upcoming",
"style": "titlesFontScale4Semibold",
"color": "$colors.onSurface1"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "123 Oak Street",
"subtitle": "Inspection on 01/08/2026"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/nothing"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Scheduled",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "456 Maple Avenue",
"subtitle": "Lease Signing on 01/10/2026"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/nothing"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Scheduled",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "789 Pine Road",
"subtitle": "Maintenance on 01/15/2026"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/nothing"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Requested",
"variant": "error"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "321 Elm Street",
"subtitle": "Rent Collection on 01/20/2026"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/nothing"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Scheduled",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Community Center",
"subtitle": "HOA Meeting on 01/22/2026"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/nothing"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Proposed",
"variant": "warning"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "555 Cedar Lane",
"subtitle": "Background Check"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/nothing"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "In Progress",
"variant": "warning"
},
"regions": {}
}
]
}
}
}
]
}
}
},
{
"definition": "ui/card",
"properties": {},
"regions": {
"components": {
"components": [
{
"definition": "ui/column",
"properties": {
"width": "fill",
"height": "fill",
"alignment": "start",
"gap": "$spacing.spacing4",
"padding": "$spacing.spacing4"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/text",
"properties": {
"text": "Tenants",
"style": "titlesFontScale4Semibold",
"color": "$colors.onSurface1"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Sarah Johnson",
"subtitle": "456 Maple Avenue, Apt 5"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Paid",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Michael Chen",
"subtitle": "789 Pine Road"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Overdue",
"variant": "error"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Emily Davis",
"subtitle": "321 Elm Street, Unit 5B"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Paid",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Robert Williams",
"subtitle": "555 Cedar Lane"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Paid",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Jessica Martinez",
"subtitle": "123 Oak Street, Unit 3C"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Moved Out",
"variant": "warning"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "David Lee",
"subtitle": "456 Maple Avenue, Apt 2"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Signed",
"variant": "lightest"
},
"regions": {}
}
]
}
}
}
]
}
}
}
]
}
}
}
]
}
}
},
"target": "native__homesteadHome",
"apiName": "uem_homesteadHome"
}
}

View File

@ -0,0 +1,5 @@
{
"apiName": "propertiesScreen",
"type": "experience__camaScreen",
"path": "propertiesScreen"
}

View File

@ -0,0 +1,565 @@
{
"type": "experience__camaScreen",
"title": "Properties Screen",
"contentBody": {
"view": {
"definition": "homestead/PropertiesScreen",
"properties": {
"pullToRefresh": true
},
"regions": {
"components": {
"components": [
{
"definition": "ui/container",
"properties": {
"padding": "$spacing.spacing4",
"backgroundColor": "$colors.surface2"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/card",
"properties": {},
"regions": {
"components": {
"components": [
{
"definition": "ui/baseRow",
"properties": {
"title": "Oak Street Apartments",
"subtitle": "123 Oak Street"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Full",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Maple Avenue Complex",
"subtitle": "456 Maple Avenue"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Partial",
"variant": "warning"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Pine Road House",
"subtitle": "789 Pine Road"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Vacant",
"variant": "lightest"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Cedar Lane Condos",
"subtitle": "555 Cedar Lane"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Full",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Elm Street Tower",
"subtitle": "321 Elm Street"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Partial",
"variant": "warning"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Birch Boulevard Villa",
"subtitle": "234 Birch Boulevard"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Full",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Willow Way Estates",
"subtitle": "890 Willow Way"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Vacant",
"variant": "lightest"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Sunset Plaza",
"subtitle": "567 Sunset Drive"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Full",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Riverside Apartments",
"subtitle": "678 River Road"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Partial",
"variant": "warning"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Lakeside Manor",
"subtitle": "901 Lake Avenue"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Full",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Mountain View Heights",
"subtitle": "112 Mountain Road"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Maintenance",
"variant": "error"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Harbor Point Lofts",
"subtitle": "445 Harbor Street"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Partial",
"variant": "warning"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Garden Terrace",
"subtitle": "223 Garden Lane"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Full",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Park Place Residences",
"subtitle": "334 Park Avenue"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Vacant",
"variant": "lightest"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Hillside Court",
"subtitle": "778 Hill Street"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Full",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Downtown Tower",
"subtitle": "990 Main Street"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Partial",
"variant": "warning"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Bayside Commons",
"subtitle": "101 Bay Road"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Full",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Meadow Brook Village",
"subtitle": "212 Meadow Lane"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/home",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Maintenance",
"variant": "error"
},
"regions": {}
}
]
}
}
}
]
}
}
}
]
}
}
}
]
}
}
},
"target": "native__homesteadProperties",
"apiName": "uem_homesteadProperties"
}
}

View File

@ -0,0 +1,5 @@
{
"apiName": "tenantsScreen",
"type": "experience__camaScreen",
"path": "tenantsScreen"
}

View File

@ -0,0 +1,478 @@
{
"type": "experience__camaScreen",
"title": "Tenants Screen",
"contentBody": {
"view": {
"definition": "homestead/TenantsScreen",
"properties": {
"pullToRefresh": true
},
"regions": {
"components": {
"components": [
{
"definition": "ui/container",
"properties": {
"padding": "$spacing.spacing4",
"backgroundColor": "$colors.surface2"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/card",
"properties": {},
"regions": {
"components": {
"components": [
{
"definition": "ui/baseRow",
"properties": {
"title": "Sarah Johnson",
"subtitle": "456 Maple Avenue, Apt 5"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Paid",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Michael Chen",
"subtitle": "789 Pine Road"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Overdue",
"variant": "error"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Emily Davis",
"subtitle": "321 Elm Street, Unit 5B"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Paid",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Robert Williams",
"subtitle": "555 Cedar Lane"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Paid",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Jessica Martinez",
"subtitle": "123 Oak Street, Unit 3C"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Moved Out",
"variant": "warning"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "David Lee",
"subtitle": "456 Maple Avenue, Apt 2"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Signed",
"variant": "lightest"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Amanda Thompson",
"subtitle": "789 Pine Road, Unit 7A"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Paid",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Christopher Garcia",
"subtitle": "321 Elm Street, Apt 8"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Overdue",
"variant": "error"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Nicole Anderson",
"subtitle": "555 Cedar Lane, Unit 12B"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Paid",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "James Wilson",
"subtitle": "123 Oak Street, Apt 1A"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Pending",
"variant": "warning"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Rachel Brown",
"subtitle": "456 Maple Avenue, Unit 9C"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Paid",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Kevin Taylor",
"subtitle": "789 Pine Road, Apt 4B"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Signed",
"variant": "lightest"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Stephanie Miller",
"subtitle": "321 Elm Street, Unit 6D"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Paid",
"variant": "success"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Brian Martinez",
"subtitle": "555 Cedar Lane, Apt 10A"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Overdue",
"variant": "error"
},
"regions": {}
}
]
}
}
},
{
"definition": "ui/baseRow",
"properties": {
"title": "Laura Rodriguez",
"subtitle": "123 Oak Street, Unit 11B"
},
"regions": {
"components": {
"components": [
{
"definition": "ui/utilityIcon",
"properties": {
"iconName": "utility/user",
"size": "small"
},
"regions": {}
},
{
"definition": "ui/badge",
"properties": {
"label": "Paid",
"variant": "success"
},
"regions": {}
}
]
}
}
}
]
}
}
}
]
}
}
}
]
}
}
},
"target": "native__homesteadTenants",
"apiName": "uem_homesteadTenants"
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<DigitalExperienceBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<label>CAMA Rental App</label>
</DigitalExperienceBundle>

View File

@ -0,0 +1,13 @@
{
"name": "native-mobile-rental-tenant-app",
"version": "1.0.0",
"description": "Sample CAMA app for rental property tenants",
"private": true,
"scripts": {
"build": "echo 'No build required for base project'",
"clean": "echo 'No clean required'",
"lint": "eslint **/{aura,lwc}/**/*.js 2>/dev/null || true",
"test": "echo 'Add tests when ready'"
},
"devDependencies": {}
}

View File

@ -0,0 +1,12 @@
{
"packageDirectories": [
{
"path": "force-app",
"default": true
}
],
"name": "RentalTenantApp",
"namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "66.0"
}

368
scripts/validate-skills.ts Normal file
View File

@ -0,0 +1,368 @@
#!/usr/bin/env tsx
// Validates the skills/ directory structure and SKILL.md format.
// Exits with code 1 if any violations are found.
//
// Usage:
// npm run validate:skills # validate all skills
// npm run validate:skills -- --changed --base=origin/main # validate only skills changed vs base
import { execSync } from "child_process"
import fs from "fs"
import path from "path"
import { parseArgs } from "util"
const SKILLS_DIR = path.join(__dirname, "..", "skills")
/** Parsed context for a single skill directory, built before content checks run. */
interface SkillContext {
dirName: string
dirPath: string
content: string
frontmatter: Record<string, string> | null
body: string
}
/** Return value of every check function. */
interface CheckResult {
errors: string[]
/** When true and errors is non-empty, skip remaining checks for this entry. */
fatal?: boolean
/** Defaults to "error". Warnings are printed but do not cause a non-zero exit code. */
severity?: "error" | "warning"
}
/** Collected results for a single skill entry. */
interface SkillResult {
errors: string[]
warnings: string[]
}
/** Runs before SKILL.md is read — validates directory layout. */
interface StructureCheck {
description: string
run(dirName: string, dirPath: string): CheckResult
}
/** Runs after SKILL.md is read — validates file content. */
interface ContentCheck {
description: string
run(ctx: SkillContext): CheckResult
}
/**
* Structure checks run on every entry in `skills/` before SKILL.md is read.
* A fatal error aborts content checks for that entry.
*/
const STRUCTURE_CHECKS: StructureCheck[] = [
{
description: "Entry must be a directory (no loose files in skills/)",
run(dirName, dirPath) {
if (!fs.statSync(dirPath).isDirectory()) {
return { errors: [`Loose file in skills/: ${dirName} (expected only directories)`], fatal: true }
}
return { errors: [] }
},
},
{
description: "Skill directory must contain SKILL.md",
run(dirName, dirPath) {
if (!fs.existsSync(path.join(dirPath, "SKILL.md"))) {
return { errors: [`Missing SKILL.md in skills/${dirName}/`], fatal: true }
}
return { errors: [] }
},
},
{
description: "Name must be kebab-case (lowercase letters, digits, and hyphens only)",
run(dirName) {
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(dirName)) {
return { errors: [`skills/${dirName}: name must be kebab-case (only lowercase letters, digits, and hyphens)`] }
}
return { errors: [] }
},
},
{
description: "Name must be at most 64 characters",
run(dirName) {
if (dirName.length > 64) {
return { errors: [`skills/${dirName}: name is ${dirName.length} characters (maximum 64)`] }
}
return { errors: [] }
},
},
{
description: "Name should use gerund form — first word should end in -ing (e.g. generating-apex-tests)",
run(dirName) {
if (!dirName.split("-")[0].endsWith("ing")) {
return {
errors: [`skills/${dirName}: name should use gerund form (e.g. generating-apex-tests, refactoring-triggers)`],
severity: "warning",
}
}
return { errors: [] }
},
},
{
description: "Skills must be exactly one level deep (no nested category directories)",
run(dirName, dirPath) {
const errors: string[] = []
for (const sub of fs.readdirSync(dirPath)) {
const subPath = path.join(dirPath, sub)
if (fs.statSync(subPath).isDirectory() && fs.existsSync(path.join(subPath, "SKILL.md"))) {
errors.push(
`Nested skill detected: skills/${dirName}/${sub}/SKILL.md — skill directories must be exactly one level deep under skills/`
)
}
}
return { errors }
},
},
]
/**
* Content checks run only on entries that have a valid SKILL.md.
* A fatal error aborts remaining content checks for that entry.
*/
const CONTENT_CHECKS: ContentCheck[] = [
{
description: "SKILL.md must have a valid YAML frontmatter block (--- ... ---)",
run({ dirName, frontmatter }) {
if (!frontmatter) {
return {
errors: [`skills/${dirName}/SKILL.md: missing or malformed YAML frontmatter (expected --- ... --- block at top)`],
fatal: true,
}
}
return { errors: [] }
},
},
{
description: 'Frontmatter "name" must be present and match the directory name',
run({ dirName, frontmatter }) {
if (!frontmatter) return { errors: [] }
if (!frontmatter.name) {
return { errors: [`skills/${dirName}/SKILL.md: missing "name" field in frontmatter`] }
}
if (frontmatter.name !== dirName) {
return {
errors: [
`skills/${dirName}/SKILL.md: "name" value ("${frontmatter.name}") does not match directory name ("${dirName}")`,
],
}
}
return { errors: [] }
},
},
{
description: 'Frontmatter "description" must be present and non-empty',
run({ dirName, frontmatter }) {
if (!frontmatter) return { errors: [] }
if (!frontmatter.description?.trim()) {
return { errors: [`skills/${dirName}/SKILL.md: missing or empty "description" field in frontmatter`] }
}
return { errors: [] }
},
},
{
description: "SKILL.md must have a non-empty body (instructions after the frontmatter block)",
run({ dirName, body }) {
if (!body.trim()) {
return { errors: [`skills/${dirName}/SKILL.md: body (instructions after frontmatter) is empty`] }
}
return { errors: [] }
},
},
{
description: "Description must be at least 20 words to be information-rich",
run({ dirName, frontmatter }) {
if (!frontmatter) return { errors: [] }
const words = frontmatter.description?.trim().split(/\s+/) ?? []
if (words.length < 20) {
return {
errors: [`skills/${dirName}/SKILL.md: description too short (${words.length} word(s), minimum 20)`],
}
}
return { errors: [] }
},
},
{
description: "Description must be at most 1024 characters",
run({ dirName, frontmatter }) {
if (!frontmatter) return { errors: [] }
const len = frontmatter.description?.length ?? 0
if (len > 1024) {
return { errors: [`skills/${dirName}/SKILL.md: description is ${len} characters (maximum 1024)`] }
}
return { errors: [] }
},
},
{
description: 'Description must include trigger/activation language (contain "use")',
run({ dirName, frontmatter }) {
if (!frontmatter) return { errors: [] }
if (!frontmatter.description?.toLowerCase().includes("use")) {
return {
errors: [
`skills/${dirName}/SKILL.md: description must include trigger context (e.g. "Use this skill when...")`,
],
}
}
return { errors: [] }
},
},
{
description: "Skill body should be under 500 lines for context efficiency",
run({ dirName, body }) {
const lines = body.split("\n").length
if (lines > 500) {
return {
errors: [`skills/${dirName}/SKILL.md: body is ${lines} lines (recommended maximum is 500)`],
severity: "warning",
}
}
return { errors: [] }
},
},
]
/**
* Returns the deduplicated list of top-level skill directory names that have
* changed relative to `base` (e.g. `origin/main`) and still exist on disk.
* Deleted skill directories are intentionally excluded removing a skill is
* valid and requires no structural validation.
*/
function getChangedSkillDirs(base: string): string[] {
const output = execSync(`git diff --name-only ${base}...HEAD`, { encoding: "utf8" })
return [
...new Set(
output
.split("\n")
.filter((f) => f.startsWith("skills/"))
.map((f) => f.split("/")[1])
.filter(Boolean)
),
].filter((dir) => fs.existsSync(path.join(SKILLS_DIR, dir)))
}
/**
* Parses a SKILL.md file into its frontmatter fields and body.
* `frontmatter` is `null` if the `--- ... ---` block is missing or malformed.
* Wrapping quotes on frontmatter values are stripped.
*/
function parseSkillMd(content: string): { frontmatter: Record<string, string> | null; body: string } {
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/)
if (!match) return { frontmatter: null, body: content }
const frontmatter: Record<string, string> = {}
for (const line of match[1].split(/\r?\n/)) {
const colonIdx = line.indexOf(":")
if (colonIdx === -1) continue
const key = line.slice(0, colonIdx).trim()
const raw = line.slice(colonIdx + 1).trim()
// Strip wrapping single or double quotes to match how consumers read values
frontmatter[key] = raw.replace(/^(['"])([\s\S]*)\1$/, "$2")
}
return { frontmatter, body: content.slice(match[0].length) }
}
/**
* Runs all structure and content checks for a single skill directory.
* Returns errors (block CI) and warnings (advisory, printed but exit 0) separately.
*/
function validateSkill(dirName: string, dirPath: string): SkillResult {
if (!fs.existsSync(dirPath)) {
return { errors: [`skills/${dirName}: directory not found`], warnings: [] }
}
const errors: string[] = []
const warnings: string[] = []
const collectIssues = (result: CheckResult): boolean => {
if (result.errors.length === 0) return false
if (result.severity === "warning") {
warnings.push(...result.errors)
} else {
errors.push(...result.errors)
}
return result.fatal === true && result.severity !== "warning"
}
for (const check of STRUCTURE_CHECKS) {
if (collectIssues(check.run(dirName, dirPath))) return { errors, warnings }
}
const content = fs.readFileSync(path.join(dirPath, "SKILL.md"), "utf8")
const { frontmatter, body } = parseSkillMd(content)
const ctx: SkillContext = { dirName, dirPath, content, frontmatter, body }
for (const check of CONTENT_CHECKS) {
if (collectIssues(check.run(ctx))) return { errors, warnings }
}
return { errors, warnings }
}
/** CLI entry point. Parses flags, resolves the list of skills to check, and reports results. */
function main(): void {
const { values } = parseArgs({
args: process.argv.slice(2),
options: {
/** Validate only skill dirs touched in this branch vs the given base ref. */
changed: { type: "boolean", default: false },
/** Base ref for --changed (e.g. origin/main). Defaults to origin/HEAD. */
base: { type: "string", default: "origin/HEAD" },
},
})
let entries: string[]
if (values.changed) {
entries = getChangedSkillDirs(values.base!)
if (entries.length === 0) {
console.log("No skill directories changed — nothing to validate.")
return
}
console.log(`Validating ${entries.length} changed skill(s): ${entries.join(", ")}`)
} else {
entries = fs.readdirSync(SKILLS_DIR)
}
const allErrors: string[] = []
const allWarnings: string[] = []
let passed = 0
for (const entry of entries) {
const { errors, warnings } = validateSkill(entry, path.join(SKILLS_DIR, entry))
allErrors.push(...errors)
allWarnings.push(...warnings)
if (errors.length === 0) passed++
}
const hasIssues = allErrors.length > 0 || allWarnings.length > 0
const footer = "Spec: https://agentskills.io/specification · Authoring guide: https://github.com/forcedotcom/afv-library#readme"
if (allWarnings.length > 0) {
console.warn(`\n${allWarnings.length} warning(s):\n`)
for (const w of allWarnings) {
console.warn(`${w}`)
}
console.warn("")
}
if (allErrors.length > 0) {
console.error(`\nSkill validation failed with ${allErrors.length} error(s):\n`)
for (const err of allErrors) {
console.error(`${err}`)
}
console.error("")
console.error(footer)
console.error("")
process.exit(1)
} else {
console.log(`Skill validation passed: ${passed} of ${entries.length} skill(s) checked.`)
if (hasIssues) console.warn(footer)
}
}
main()

View File

@ -0,0 +1,171 @@
---
name: generate-permission-set
description: Generates correct, deployable Salesforce permission set metadata (PermissionSet XML) with object, field, user, and app permissions. Use when creating or editing permission set metadata, PermissionSet XML, object permissions, field-level security (FLS), tab visibility, or deploying permission sets.
compatibility: Salesforce Metadata API v60.0+
metadata:
author: afv-library
version: "1.0"
---
## When to Use This Skill
Use when generating or editing permission set metadata, or when granting object, field, user, and app permissions.
## Step 1: Define Core Properties
Start by defining the required permission set properties:
```xml
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>YourPermissionSetName</fullName>
<label>Display Name for Administrators</label>
<description>Clear description of purpose and intended audience</description>
</PermissionSet>
```
**Naming conventions:**
- Use descriptive API names (e.g., `Sales_Manager_Access`)
## Step 2: Configure Object Permissions
Add CRUD permissions for standard and custom objects:
```xml
<objectPermissions>
<allowCreate>true</allowCreate>
<allowRead>true</allowRead>
<allowEdit>true</allowEdit>
<allowDelete>false</allowDelete>
<modifyAllRecords>false</modifyAllRecords>
<viewAllRecords>false</viewAllRecords>
<viewAllFields>false</viewAllFields>
<object>Account</object>
</objectPermissions>
```
## Step 3: Set Field-Level Security
Define field permissions for sensitive or custom fields:
```xml
<fieldPermissions>
<editable>true</editable>
<readable>true</readable>
<field>Account.SSN__c</field>
</fieldPermissions>
```
**Important:**
- Required fields must NEVER appear in list of field permissions. Granting field-level security on required fields is not allowed by the platform and will cause deployment failure.
- Before adding any field, confirm from the object metadata that the field exists and is not required
- A field is required when its metadata contains `<required>true</required>`:
```xml
<fields>
<fullName>FieldName__c</fullName>
<required>true</required>
</fields>
```
- Use format `ObjectName.FieldName` for field references
- Set both readable and editable to true when the user needs edit access; editable implies readable
- If all fields should be visible, can alternatively enable the "viewAllFields" object permission
## Step 4: Grant User Permissions
Add system-level permissions for features and capabilities:
```xml
<userPermissions>
<enabled>true</enabled>
<name>ApiEnabled</name>
</userPermissions>
<userPermissions>
<enabled>true</enabled>
<name>RunReports</name>
</userPermissions>
```
**Common permissions:**
- `ApiEnabled`: API access
- `ViewSetup`: View Setup menu
- `ManageUsers`: User management
- `RunReports`: Report execution
**Security review required for:**
- `ViewAllData`: Read all records
- `ModifyAllData`: Edit all records
- `ManageUsers`: User administration
## Step 5: Configure App and Tab Visibility
Make applications and tabs visible to users:
```xml
<applicationVisibilities>
<application>Sales_Console</application>
<visible>true</visible>
</applicationVisibilities>
<tabSettings>
<tab>CustomTab__c</tab>
<visibility>Visible</visibility>
</tabSettings>
```
**Application visibility options:**
- <visible> can be true or false
**Tab visibility options:**
- `Visible`: Always shown
- `Available`: Available but not default
- `Hidden`: Not visible
**CRITICAL - Tab Naming:**
- Custom object tabs: MUST include the __c suffix (e.g., MyCustomObject__c)
- Standard object tabs: Use the object name with "standard-" prefix (e.g., standard-Account, standard-Contact)
- The tab name matches the object's API name exactly
## Step 6: Add Apex and Visualforce Access (Optional)
Grant access to custom code:
```xml
<classAccesses>
<apexClass>CustomController</apexClass>
<enabled>true</enabled>
</classAccesses>
<pageAccesses>
<apexPage>CustomPage</apexPage>
<enabled>true</enabled>
</pageAccesses>
```
## Step 7: Set License and Record Type Settings (Optional)
Specify license requirements and record type visibility:
```xml
<license>Salesforce</license>
<hasActivationRequired>false</hasActivationRequired>
<recordTypeVisibilities>
<recordType>Account.Business</recordType>
<visible>true</visible>
<default>true</default>
</recordTypeVisibilities>
```
## Validation Checklist
Before deploying, verify:
- [ ] fullName, label, description set
- [ ] Permissions follow least privilege
- [ ] No required fields in `<fieldPermissions>`
- [ ] No duplicate permissions
- [ ] no lengthy comments
## What Causes Deployment Failure
- **Field permissions on required fields:** Any required field in `<fieldPermissions>` fails deployment. Required fields cannot have FLS; omit them entirely. Always confirm from object/field metadata that a field exists and is not required—never assume.
- **Incorrect API names:** Using the wrong name or missing suffixes (e.g. missing `__c` for custom objects, fields, tabs) cause failure.
## Deployment
Deploy using Salesforce CLI

View File

@ -1,6 +1,6 @@
---
name: salesforce-flow
description: Generate Salesforce Flows using the MCP tool execute_metadata_action. Use when the user asks to create, build, or generate a flow — including Screen, Autolaunched, Record-Triggered (before/after-save), Scheduled, or Platform Event-Triggered Flows. Also trigger for flow-like requests such as "when a record is created", "trigger daily at", "send an email when", "update the field when", "automate", "workflow", or "flow XML/metadata". This is the only skill for Salesforce Flow generation.
description: Generate Salesforce Flows using the MCP tool execute_metadata_action. Use when the user asks to create, build, or generate a flow — including Screen, Autolaunched, Record-Triggered (before/after-save), Scheduled. Also trigger for flow-like requests such as "when a record is created", "trigger daily at", "send an email when", "update the field when", "automate", "workflow", or "flow XML/metadata". This is the only skill for Salesforce Flow generation.
---
## Goal
@ -21,7 +21,7 @@ Use this skill when you need to:
# Flow Metadata Specification
## Overview
Salesforce Flows are powerful automation tools that enable complex business process automation without code. Flows can collect and process data through interactive screens, execute logic and calculations, manipulate records, call external services, and trigger based on various events. Flow types include Screen Flows (user-guided), Autolaunched Flows (background processing), Record-Triggered Flows (database events), Scheduled Flows (time-based), and Platform Event-Triggered Flows (event-driven).
Salesforce Flows are powerful automation tools that enable complex business process automation without code. Flows can collect and process data through interactive screens, execute logic and calculations, manipulate records, call external services, and trigger based on various events. Flow types include Screen Flows (user-guided), Autolaunched Flows (background processing), Record-Triggered Flows (database events) and Scheduled Flows (time-based).
## Purpose
- Automate complex business processes with declarative logic and branching