skills/setup-renovate-for-tuist/SKILL.md
Sets up Renovate automated dependency updates for Tuist iOS projects. Detects integration style (Project.swift-based or Tuist/Package.swift-based), handles registry vs URL packages, and creates renovate.json plus an optional GitHub Actions workflow. Use when you want to automate dependency bump PRs for a Tuist project.
npx skillsauth add 2sem/tuist-renovate-skill setup-renovate-for-tuistInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Renovate automates dependency update PRs for your Tuist iOS project. This skill creates the correct renovate.json based on your project's integration style and package format.
Before starting:
Tuist.swift or Tuist/ directory)Check where packages are declared:
Tuist/Package.swift-based — look for non-empty dependencies array:
// Tuist/Package.swift
let package = Package(
dependencies: [
.package(url: "https://github.com/firebase/firebase-ios-sdk", from: "11.8.1")
]
)
Project.swift-based — search for Project.swift files anywhere in the project that contain a non-empty packages: array:
grep -rl "packages:" --include="Project.swift" .
// (location varies per project)
let project = Project(
packages: [
.remote(url: "https://github.com/firebase/firebase-ios-sdk", requirement: .upToNextMajor(from: "11.8.1"))
]
)
A project uses one style only. If
Tuist/Package.swifthas non-empty dependencies → Tuist/Package.swift-based. If anyProject.swiftfile has a non-emptypackages:array → Project.swift-based.
Within the detected files, check whether packages use URL format or registry format:
| Format | Example |
|--------|---------|
| URL-based | .package(url: "https://github.com/firebase/firebase-ios-sdk", from: "11.8.1") |
| Registry-based | .package(id: "firebase.firebase-ios-sdk", from: "11.8.1") |
| URL-based (Project.swift) | .remote(url: "https://github.com/...", requirement: .upToNextMajor(from: "1.0.0")) |
Also note any packages using requirement: .branch("...") — these cannot be tracked by Renovate and should be left out (see Branch Dependencies below).
Check if mise.toml exists in the project root. If yes, Renovate can also update tool versions (tuist, swiftlint, etc.) automatically.
Ask the user: "When would you like Renovate to check for updates? (e.g. 'every Monday morning', 'weekly on Friday', 'daily')"
Use the answer to set the schedule field in renovate.json. Renovate uses natural language scheduling:
| User preference | Renovate schedule value |
|----------------|------------------------|
| Monday morning | "before 9am on monday" |
| Friday afternoon | "after 2pm on friday" |
| Weekly (any) | "once a week" |
| Daily | "every day" |
Full syntax reference: https://docs.renovatebot.com/configuration-options/#schedule
If using a self-hosted GitHub Actions workflow, also convert the chosen schedule to a matching cron expression.
Create renovate.json in the project root based on the detected configuration.
The Renovate Swift manager natively handles Package.swift files. The default pattern /(^|/)Package\.swift/ already matches Tuist/Package.swift.
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"packageRules": [
{
"matchManagers": ["swift"],
"groupName": "Swift dependencies",
"schedule": ["<schedule>"]
}
]
}
Renovate's Swift manager only understands url:-based packages. For id:-based registry packages, use customManagers with a regex that:
from: and exact: requirementsfirebase.firebase-ios-sdk → firebase/firebase-ios-sdk){{{ }}} in packageNameTemplate to prevent HTML-escaping the /Use two managers (releases + tags) as fallback since some packages only publish tags, not releases:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"enabledManagers": ["custom.regex"],
"customManagers": [
{
"customType": "regex",
"managerFilePatterns": ["/Tuist/Package\\.swift/"],
"matchStrings": [
"\\.package\\(id:\\s*\"(?<depName>[\\w\\-.]+?)\"[\\s\\S]*?(?:from|exact):\\s*\"(?<currentValue>[^\"]+)\"\\)"
],
"datasourceTemplate": "github-releases",
"packageNameTemplate": "{{{replace '\\.' '/' depName}}}"
},
{
"customType": "regex",
"managerFilePatterns": ["/Tuist/Package\\.swift/"],
"matchStrings": [
"\\.package\\(id:\\s*\"(?<depName>[\\w\\-.]+?)\"[\\s\\S]*?(?:from|exact):\\s*\"(?<currentValue>[^\"]+)\"\\)"
],
"datasourceTemplate": "github-tags",
"packageNameTemplate": "{{{replace '\\.' '/' depName}}}"
}
],
"packageRules": [
{
"matchManagers": ["custom.regex"],
"groupName": "Swift dependencies",
"schedule": ["<schedule>"]
}
]
}
Note on dotted repo names: If a repo name had dots replaced with underscores (e.g.,
groue.GRDB_swift), thepackageNameTemplatereplacementgroue/GRDB_swiftwon't match the real GitHub repogroue/GRDB.swift. For these packages, add an explicitpackageRulesoverride:{ "matchPackageNames": ["groue.GRDB_swift"], "packageName": "groue/GRDB.swift" }
Project.swift uses .remote(url:) which is a Tuist-specific syntax, not standard SPM. Renovate's Swift manager won't parse this. The regex must:
https:// prefix and .git suffix.upToNextMajor, .upToNextMinor, .exactUse two managers (releases + tags) as fallback:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"enabledManagers": ["custom.regex"],
"customManagers": [
{
"customType": "regex",
"managerFilePatterns": ["/(^|/)Project\\.swift$/"],
"matchStrings": [
"\\.remote\\(url:\\s*\"(?:https?:\\/\\/)?github\\.com\\/(?<depName>[\\w\\-_]+\\/[\\w\\-_.]+?)(?:\\.git)?\"[\\s,]*requirement:\\s*\\.(?:upToNextMajor|upToNextMinor)\\(from:\\s*\"(?<currentValue>[^\"]+)\"\\)"
],
"datasourceTemplate": "github-releases"
},
{
"customType": "regex",
"managerFilePatterns": ["/(^|/)Project\\.swift$/"],
"matchStrings": [
"\\.remote\\(url:\\s*\"(?:https?:\\/\\/)?github\\.com\\/(?<depName>[\\w\\-_]+\\/[\\w\\-_.]+?)(?:\\.git)?\"[\\s,]*requirement:\\s*\\.(?:upToNextMajor|upToNextMinor)\\(from:\\s*\"(?<currentValue>[^\"]+)\"\\)"
],
"datasourceTemplate": "github-tags"
},
{
"customType": "regex",
"managerFilePatterns": ["/(^|/)Project\\.swift$/"],
"matchStrings": [
"\\.remote\\(url:\\s*\"(?:https?:\\/\\/)?github\\.com\\/(?<depName>[\\w\\-_]+\\/[\\w\\-_.]+?)(?:\\.git)?\"[\\s,]*requirement:\\s*\\.exact\\(\"(?<currentValue>[^\"]+)\"\\)"
],
"datasourceTemplate": "github-releases"
},
{
"customType": "regex",
"managerFilePatterns": ["/(^|/)Project\\.swift$/"],
"matchStrings": [
"\\.remote\\(url:\\s*\"(?:https?:\\/\\/)?github\\.com\\/(?<depName>[\\w\\-_]+\\/[\\w\\-_.]+?)(?:\\.git)?\"[\\s,]*requirement:\\s*\\.exact\\(\"(?<currentValue>[^\"]+)\"\\)"
],
"datasourceTemplate": "github-tags"
}
],
"packageRules": [
{
"matchManagers": ["custom.regex"],
"groupName": "Swift dependencies",
"schedule": ["<schedule>"]
}
]
}
Combine the customManagers entries from Case B and Case C. Set enabledManagers to include only the managers you use:
{
"enabledManagers": ["custom.regex"]
}
Packages declared with requirement: .branch("...") use mutable Git refs — there is no semantic version for Renovate to track. Do not add regex patterns for these. If Renovate picks them up accidentally, disable them explicitly:
{
"matchDatasources": ["git-refs"],
"enabled": false
}
If mise.toml exists, add mise to enabledManagers:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"enabledManagers": ["mise", "custom.regex"],
"packageRules": [
{
"matchManagers": ["mise"],
"groupName": "Dev tools",
"schedule": ["<schedule>"]
}
]
}
The
misemanager readsmise.tomland updates tool versions liketuist,swiftlint, etc.
Consider adding these to control merge behaviour:
{
"packageRules": [
{
"matchUpdateTypes": ["patch"],
"automerge": true,
"automergeType": "branch"
},
{
"matchUpdateTypes": ["minor"],
"automerge": false
},
{
"matchUpdateTypes": ["major"],
"automerge": false
}
]
}
And these top-level options to prevent PR floods:
{
"prConcurrentLimit": 3,
"prHourlyLimit": 2
}
Run a local dry-run to confirm the renovate.json detects the expected packages before merging.
Check if Renovate is installed:
renovate --version
npm install -g renovate?" Only install if the user confirms.Run the dry-run:
RENOVATE_GITHUB_TOKEN=<github-pat> renovate --dry-run=full <org>/<repo>
A GitHub PAT with repo scope is required. Ask the user to provide it if not already available in the environment.
What to check in the output:
matched 0 files warnings for your managerFilePatternsdepName (e.g. firebase/firebase-ios-sdk, not firebase.firebase-ios-sdk)If a package is missing:
matchStrings pattern directly[\s\S]*?, unhandled requirement type (e.g. .upToNextMinor, .exact)Choose one of the two approaches:
renovate.json — Renovate will auto-create an onboarding PR, then begin raising dependency update PRs.No additional files needed.
1. Check for an existing Renovate workflow:
ls .github/workflows/
Search for any workflow file that already references Renovate:
grep -rl "renovate" .github/workflows/
renovatebot/github-action:gh release list --repo renovatebot/github-action --limit 1
Use the major version from that tag (e.g. if latest is v40.3.2, use renovatebot/[email protected]). Do not use a bare major tag like @v40 as it may not exist.
Then create .github/workflows/renovate.yml:
name: Renovate
on:
# Disabled until tested — uncomment after confirming the workflow runs correctly
# schedule:
# - cron: '<cron expression matching chosen schedule>'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
renovate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: renovatebot/github-action@<latest-version>
with:
configurationFile: renovate.json
token: ${{ secrets.RENOVATE_GITHUB_TOKEN }}
env:
LOG_LEVEL: 'debug'
RENOVATE_REPOSITORIES: ${{ github.repository }}
2. The schedule cron is commented out so the user can test manually first. After the workflow file is merged, ask the user to trigger it manually from the Actions tab (workflow_dispatch) and confirm it runs correctly.
Once the user confirms it works, uncomment the schedule block and push the change.
3. Set RENOVATE_GITHUB_TOKEN in GitHub repository secrets. Ask the user which token type they are using:
Fine-grained personal access token — requires these permissions (set token lifetime to ≤ 366 days — some organizations such as tuist enforce this limit and will reject tokens with longer lifetimes):
Classic personal access token — requires:
repo (full read/write)For additional self-hosted configuration options (autodiscover, git author, PR limits, etc.), refer to the Renovate self-hosted configuration docs. Ask the user if they need any of these before finalising the workflow.
After merging:
https://app.renovatebot.com/dashboard for run logs| Issue | Fix |
|-------|-----|
| No PRs created | Confirm renovate.json is valid JSON; check app dashboard for errors |
| customManagers not matching | Test the regex against your actual Swift file at regex101.com (use JavaScript flavor) |
| Multiline declarations not detected | Ensure [\\s\\S]*? is used instead of .+? in matchStrings |
| Wrong package gets updated (e.g. LSExtensions bumped with kakao's version) | [\\s\\S]*? is spanning across multiple .remote() blocks — use [\\s,]* between the URL and requirement: to constrain the match within a single block |
| Registry package PRs have wrong version | Override packageName in packageRules to point to the correct GitHub repo |
| Renovate opens too many PRs at once | Add "prConcurrentLimit": 3 to renovate.json |
| Package updates break build | Add "automerge": false (it's the default — confirm it's not set to true) |
| Renovate scan is very slow | Add "enabledManagers": ["custom.regex"] to skip irrelevant built-in managers |
| Datasource unknown error for tuist/tuist with fine-grained PAT | The tuist org forbids fine-grained PATs with lifetime > 366 days — shorten the token expiry or switch to a classic PAT with repo scope |
renovate.json created in project root with correct config for detected stylecustomManagers regex tested against actual Swift file content (including multiline examples)renovate --dry-run confirms all expected packages are detected.github/workflows/renovate.yml createdRENOVATE_GITHUB_TOKEN secret set (self-hosted only)tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
A CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.