-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat(cli): set up AI tooling in trigger init and add getting-started skill #3872
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "trigger.dev": patch | ||
| --- | ||
|
|
||
| `trigger init` now sets up your AI coding assistant as part of project setup: pick the MCP server, the agent skills, or both, then scaffold with the CLI or hand off to your assistant. Adds a new `getting-started` agent skill that teaches assistants how to bootstrap Trigger.dev (install the SDK, write `trigger.config.ts`, create a first task, run `trigger dev`), so the AI-driven setup path works end to end. It ships in the CLI alongside the existing skills, version-matched to your SDK. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,214 @@ | ||
| --- | ||
| name: getting-started | ||
| description: > | ||
| Bootstrap Trigger.dev into an existing project from scratch: authenticate the | ||
| CLI, install @trigger.dev/sdk and @trigger.dev/build, write trigger.config.ts | ||
| with the project ref and task dirs, scaffold a /trigger directory with a first | ||
| task, wire tsconfig and .gitignore, set TRIGGER_SECRET_KEY, and run the dev | ||
| server. Load this when a project has no trigger.config.ts yet and the user | ||
| asks to "add Trigger.dev", "set up Trigger.dev", "initialize Trigger.dev", or | ||
| get a first task running, including in a monorepo. Once the project is set up | ||
| and you are writing task code, switch to the authoring-tasks skill. | ||
| type: core | ||
| library: trigger.dev | ||
| library_version: "{{TRIGGER_SDK_VERSION}}" | ||
| sources: | ||
| - docs/quick-start.mdx | ||
| - docs/manual-setup.mdx | ||
| - docs/config/config-file.mdx | ||
| - docs/triggering.mdx | ||
| --- | ||
|
|
||
| # Getting started with Trigger.dev | ||
|
|
||
| Set up Trigger.dev in an existing project. The end state is: the SDK installed, a | ||
| `trigger.config.ts` pointing at a project ref, a `/trigger` directory with at least | ||
| one exported task, and `trigger dev` running so the task shows up in the dashboard. | ||
|
|
||
| The fastest path is the CLI's own wizard, which performs every mechanical step below | ||
| and also offers to install the MCP server and these agent skills: | ||
|
|
||
| ```bash | ||
| npx trigger.dev@latest init | ||
| ``` | ||
|
|
||
| Prefer `init` when you can. Do the manual steps further down when `init` does not fit | ||
| (monorepos, an existing config to extend, or a non-interactive environment). | ||
|
|
||
| ## Two steps need the human | ||
|
|
||
| Most of setup is automatable, but two steps require a person and cannot be done | ||
| headlessly. When you reach them, stop and ask the user to do them, then continue: | ||
|
|
||
| 1. **Authenticating the CLI.** `npx trigger.dev@latest login` opens a browser for the | ||
| user to sign in. If they have no account, point them to https://cloud.trigger.dev | ||
| (or a self-hosted instance) first. You cannot complete this for them. | ||
| 2. **The secret key and project ref.** `TRIGGER_SECRET_KEY` and the project ref | ||
| (`proj_...`) come from the dashboard. Ask the user to copy the **DEV** secret key | ||
| from the project's API Keys page, and to pick or create the project so you have its | ||
| ref. `trigger init` can select the project interactively once the user is logged in. | ||
|
|
||
| Treat these as handoffs: state exactly what you need, wait for the user, then resume. | ||
|
|
||
| ## Manual setup | ||
|
|
||
| ### 1. Authenticate (human step) | ||
|
|
||
| ```bash | ||
| npx trigger.dev@latest login | ||
| # self-hosted: | ||
| npx trigger.dev@latest login --api-url https://your-trigger-instance.com | ||
| ``` | ||
|
|
||
| ### 2. Install the packages | ||
|
|
||
| `@trigger.dev/sdk` is a runtime dependency; `@trigger.dev/build` is a dev dependency. | ||
| Pin both to the same version as the `trigger.dev` CLI you run; the CLI warns on a | ||
| mismatch during `dev`/`deploy`. | ||
|
|
||
| ```bash | ||
| npm add @trigger.dev/sdk@latest | ||
| npm add --save-dev @trigger.dev/build@latest | ||
| ``` | ||
|
|
||
| ### 3. Write `trigger.config.ts` | ||
|
|
||
| Create it in the project root (or `trigger.config.mjs` for JavaScript). The `project` | ||
| ref and `dirs` are the only required fields. | ||
|
|
||
| ```ts | ||
| import { defineConfig } from "@trigger.dev/sdk"; | ||
|
|
||
| export default defineConfig({ | ||
| project: "<project ref>", // e.g. "proj_abc123", from the dashboard | ||
| dirs: ["./src/trigger"], // where your tasks live | ||
| maxDuration: 3600, | ||
| retries: { | ||
| enabledInDev: false, | ||
| default: { maxAttempts: 3, factor: 2, minTimeoutInMs: 1000, maxTimeoutInMs: 10000, randomize: true }, | ||
| }, | ||
| }); | ||
| ``` | ||
|
|
||
| Use the Bun runtime by adding `runtime: "bun"`. Build extensions (`prismaExtension`, | ||
| `puppeteer`, `additionalFiles`, etc.) come from `@trigger.dev/build` and go in | ||
| `build.extensions`. | ||
|
|
||
| ### 4. Add a first task | ||
|
|
||
| Create the directory that matches `dirs` and export a task from it. Every task must be | ||
| a named export with a project-unique `id`. | ||
|
|
||
| ```ts | ||
| // src/trigger/example.ts | ||
| import { task } from "@trigger.dev/sdk"; | ||
|
|
||
| export const helloWorld = task({ | ||
| id: "hello-world", | ||
| run: async (payload: { name: string }) => { | ||
| return { message: `Hello ${payload.name}!` }; | ||
| }, | ||
| }); | ||
| ``` | ||
|
|
||
| ### 5. Wire tsconfig and gitignore | ||
|
|
||
| Add `trigger.config.ts` to the `include` array in `tsconfig.json`, and add `.trigger` | ||
| to `.gitignore` (the CLI writes local dev state there). | ||
|
|
||
| ```jsonc | ||
| // tsconfig.json | ||
| { "include": ["trigger.config.ts" /* ...existing */] } | ||
| ``` | ||
|
|
||
| ```bash | ||
| # .gitignore | ||
| .trigger | ||
| ``` | ||
|
|
||
| ### 6. Set the secret key (human step) | ||
|
|
||
| For triggering from your own code, set `TRIGGER_SECRET_KEY` to the DEV key from the | ||
| dashboard's API Keys page. Self-hosted users also set `TRIGGER_API_URL`. | ||
|
|
||
| ```bash | ||
| # .env (or .env.local for Next.js) | ||
| TRIGGER_SECRET_KEY=tr_dev_xxxxxxxx | ||
| ``` | ||
|
|
||
| ### 7. Run the dev server | ||
|
|
||
| ```bash | ||
| npx trigger.dev@latest dev | ||
| ``` | ||
|
|
||
| Leave it running. Tasks register with the dashboard, where the user can fire a test run | ||
| from the task's test page. On first run the CLI offers to install the MCP server and | ||
| agent skills; recommend both. | ||
|
|
||
| ## Triggering from your app | ||
|
|
||
| Once a task exists, trigger it from backend code with a **type-only** import so the | ||
| task code is never bundled into your app. Trigger by id, not by calling the task object. | ||
|
|
||
| ```ts | ||
| import { tasks } from "@trigger.dev/sdk"; | ||
| import type { helloWorld } from "@/trigger/example"; // type-only | ||
|
|
||
| const handle = await tasks.trigger<typeof helloWorld>("hello-world", { name: "Ada" }); | ||
| ``` | ||
|
|
||
| `TRIGGER_SECRET_KEY` must be set wherever this runs. Framework specifics live in the | ||
| Next.js / Remix / Node.js guides. | ||
|
|
||
| ## Monorepos | ||
|
|
||
| Two layouts, both supported: put tasks in a shared package (`@repo/tasks` with its own | ||
| `trigger.config.ts`, consumed via `workspace:*`), or install Trigger.dev directly in the | ||
| app that needs it. Run `trigger dev` from the directory that holds `trigger.config.ts`. | ||
| See the manual setup docs for full Turborepo examples before scaffolding either. | ||
|
|
||
| ## Common mistakes | ||
|
|
||
| 1. **Trying to do the human-only steps headlessly.** You cannot complete `trigger login` | ||
| or read the dashboard secret key for the user. | ||
| - Wrong: spawning `trigger login` and waiting on it to finish in an agent session. | ||
| - Correct: ask the user to log in and to paste the DEV key, then continue. | ||
|
|
||
| 2. **Mismatched CLI and SDK versions.** A `trigger.dev` CLI on a different major than | ||
| `@trigger.dev/sdk` breaks dev/deploy. | ||
| - Wrong: `npx trigger.dev@latest dev` against an old pinned SDK. | ||
| - Correct: keep `trigger.dev`, `@trigger.dev/sdk`, and `@trigger.dev/build` on the same version. | ||
|
|
||
| 3. **Importing from `@trigger.dev/sdk/v3` or using `client.defineJob()`.** Both are old. | ||
| - Correct: always import from `@trigger.dev/sdk`; define work with `task()`. | ||
|
|
||
| 4. **Tasks not exported, or outside `dirs`.** A task that is not a named export inside a | ||
| configured directory will not be picked up. | ||
| - Correct: `export const ... = task({ ... })` in a file under a `dirs` path. | ||
|
|
||
| 5. **Importing the task instance into backend code.** This bundles the task. | ||
| - Wrong: `import { helloWorld } from "@/trigger/example"` in a route handler. | ||
| - Correct: `import type { helloWorld }` plus `tasks.trigger<typeof helloWorld>("hello-world", payload)`. | ||
|
|
||
| 6. **Forgetting `TRIGGER_SECRET_KEY`.** Triggering from your app fails without it; the | ||
| `dev` server itself works once the CLI is logged in. | ||
|
|
||
| ## References | ||
|
|
||
| Sibling skills: | ||
|
|
||
| - **authoring-tasks** for writing the tasks themselves once setup is done: retries, waits, | ||
| queues, scheduled tasks, triggering, and the full `trigger.config.ts`. | ||
| - **realtime-and-frontend** for showing live run status in a frontend. | ||
| - **authoring-chat-agent** and **chat-agent-advanced** for building AI chat agents. | ||
|
|
||
| Docs: | ||
|
|
||
| - [Quick start](https://trigger.dev/docs/quick-start) | ||
| - [Manual setup](https://trigger.dev/docs/manual-setup) | ||
| - [Configuration file](https://trigger.dev/docs/config/config-file) | ||
|
|
||
| ## Version | ||
|
|
||
| Generated for @trigger.dev/sdk {{TRIGGER_SDK_VERSION}}. Re-run the trigger.dev skills installer after upgrading. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,4 @@ | ||||||||||||||||||
| import { intro, isCancel, log, outro, select, text } from "@clack/prompts"; | ||||||||||||||||||
| import { intro, isCancel, log, multiselect, outro, select, text } from "@clack/prompts"; | ||||||||||||||||||
| import { context, trace } from "@opentelemetry/api"; | ||||||||||||||||||
| import { | ||||||||||||||||||
| GetProjectResponseBody, | ||||||||||||||||||
|
|
@@ -43,6 +43,7 @@ import { | |||||||||||||||||
| writeConfigHasSeenMCPInstallPrompt, | ||||||||||||||||||
| } from "../utilities/configFiles.js"; | ||||||||||||||||||
| import { installMcpServer } from "./install-mcp.js"; | ||||||||||||||||||
| import { installSkillsFromInit, markSkillsPromptSeen } from "./skills.js"; | ||||||||||||||||||
|
|
||||||||||||||||||
| const cliVersion = VERSION as string; | ||||||||||||||||||
| const cliTag = cliVersion.includes("v4-beta") ? "v4-beta" : "latest"; | ||||||||||||||||||
|
|
@@ -147,27 +148,53 @@ async function _initCommand(dir: string, options: InitCommandOptions) { | |||||||||||||||||
|
|
||||||||||||||||||
| const hasSeenMCPInstallPrompt = readConfigHasSeenMCPInstallPrompt(); | ||||||||||||||||||
|
|
||||||||||||||||||
| // Skip the MCP-vs-CLI prompt when --yes is set: the user explicitly chose CLI | ||||||||||||||||||
| // by running `trigger.dev init` non-interactively, and the prompt would | ||||||||||||||||||
| // Skip the AI-tooling prompt when --yes is set: the user explicitly chose the CLI | ||||||||||||||||||
| // scaffold by running `trigger.dev init` non-interactively, and the prompt would | ||||||||||||||||||
| // otherwise hang on a fresh machine where `hasSeenMCPInstallPrompt` is false. | ||||||||||||||||||
| if (!hasSeenMCPInstallPrompt && !options.yes) { | ||||||||||||||||||
| const installChoice = await select({ | ||||||||||||||||||
| message: "Choose how you want to initialize your project:", | ||||||||||||||||||
| const tooling = await multiselect({ | ||||||||||||||||||
| message: "Set up AI tooling for your coding assistant? (optional, space to toggle)", | ||||||||||||||||||
| options: [ | ||||||||||||||||||
| { | ||||||||||||||||||
| value: "mcp", | ||||||||||||||||||
| label: "Trigger.dev MCP", | ||||||||||||||||||
| hint: "Automatically install the Trigger.dev MCP server and then vibe your way to a new project.", | ||||||||||||||||||
| label: "MCP server", | ||||||||||||||||||
| hint: "live access to your project: trigger tasks, deploy, monitor runs", | ||||||||||||||||||
| }, | ||||||||||||||||||
| { | ||||||||||||||||||
| value: "skills", | ||||||||||||||||||
| label: "Agent skills", | ||||||||||||||||||
| hint: "teach your AI to write Trigger.dev code, version-matched to your SDK", | ||||||||||||||||||
| }, | ||||||||||||||||||
| { value: "cli", label: "CLI", hint: "Continue with the CLI" }, | ||||||||||||||||||
| ], | ||||||||||||||||||
| required: false, | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| writeConfigHasSeenMCPInstallPrompt(true); | ||||||||||||||||||
|
|
||||||||||||||||||
| const continueWithCLI = isCancel(installChoice) || installChoice === "cli"; | ||||||||||||||||||
| const selectedTooling = isCancel(tooling) ? [] : tooling; | ||||||||||||||||||
|
|
||||||||||||||||||
| // Track what actually installed (not just what was selected), so the AI hand-off is | ||||||||||||||||||
| // only offered, and only described, in terms of tooling that really landed. | ||||||||||||||||||
| let installedSkills = false; | ||||||||||||||||||
| let installedMcp = false; | ||||||||||||||||||
|
|
||||||||||||||||||
| // Skills are auth-free and bundled in the CLI. The user opted in here, so install | ||||||||||||||||||
| // straight away (no extra confirm). If they declined, still mark the prompt seen so | ||||||||||||||||||
| // `trigger dev` doesn't ask about skills a second time. | ||||||||||||||||||
| if (selectedTooling.includes("skills")) { | ||||||||||||||||||
| log.step("Installing the Trigger.dev agent skills"); | ||||||||||||||||||
| const [skillsError, installed] = await tryCatch(installSkillsFromInit()); | ||||||||||||||||||
| if (skillsError) { | ||||||||||||||||||
| log.warn(`Skipped agent skills: ${skillsError.message}`); | ||||||||||||||||||
| } else { | ||||||||||||||||||
| installedSkills = installed === true; | ||||||||||||||||||
| } | ||||||||||||||||||
| } else { | ||||||||||||||||||
| await tryCatch(markSkillsPromptSeen()); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (!continueWithCLI) { | ||||||||||||||||||
| // The MCP server is also auth-free. | ||||||||||||||||||
| if (selectedTooling.includes("mcp")) { | ||||||||||||||||||
| log.step("Welcome to the Trigger.dev MCP server install wizard 🧙"); | ||||||||||||||||||
|
|
||||||||||||||||||
| const [installError] = await tryCatch( | ||||||||||||||||||
|
|
@@ -183,7 +210,37 @@ async function _initCommand(dir: string, options: InitCommandOptions) { | |||||||||||||||||
| return; | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
210
to
211
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚩 MCP install failure aborts init even after successful skills installation When the user selects both skills and MCP, and skills install successfully but MCP fails, the (Refers to lines 208-211) Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||||||
|
|
||||||||||||||||||
| return; | ||||||||||||||||||
| installedMcp = true; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // Vibe path: once AI tooling is actually installed, the user can hand scaffolding to | ||||||||||||||||||
| // their assistant instead of the CLI. Only offered when something landed, and the | ||||||||||||||||||
| // hand-off message names only the tooling that did. | ||||||||||||||||||
| if (installedSkills || installedMcp) { | ||||||||||||||||||
| const setupChoice = await select({ | ||||||||||||||||||
| message: "How do you want to set up your project?", | ||||||||||||||||||
| options: [ | ||||||||||||||||||
| { | ||||||||||||||||||
| value: "cli", | ||||||||||||||||||
| label: "Scaffold it now with the CLI", | ||||||||||||||||||
| hint: "log in, create trigger.config.ts and an example task", | ||||||||||||||||||
| }, | ||||||||||||||||||
| { | ||||||||||||||||||
| value: "ai", | ||||||||||||||||||
| label: "Let my AI assistant set it up", | ||||||||||||||||||
| hint: "hand off and let your assistant bootstrap the project", | ||||||||||||||||||
| }, | ||||||||||||||||||
| ], | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (!isCancel(setupChoice) && setupChoice === "ai") { | ||||||||||||||||||
| outro( | ||||||||||||||||||
| installedSkills | ||||||||||||||||||
| ? "Your AI tooling is ready. Ask your assistant to set up Trigger.dev and it will use the getting-started skill to add the SDK, config, and your first task." | ||||||||||||||||||
| : "The MCP server is installed. Ask your assistant to set up Trigger.dev using the MCP server." | ||||||||||||||||||
|
Comment on lines
+238
to
+240
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Outro message should describe all installed tooling. When both 💬 Suggested improvement- installedSkills
- ? "Your AI tooling is ready. Ask your assistant to set up Trigger.dev and it will use the getting-started skill to add the SDK, config, and your first task."
- : "The MCP server is installed. Ask your assistant to set up Trigger.dev using the MCP server."
+ installedSkills && installedMcp
+ ? "Your AI tooling is ready. Ask your assistant to set up Trigger.dev—it can use the getting-started skill and the MCP server to add the SDK, config, and your first task."
+ : installedSkills
+ ? "Your AI tooling is ready. Ask your assistant to set up Trigger.dev and it will use the getting-started skill to add the SDK, config, and your first task."
+ : "The MCP server is installed. Ask your assistant to set up Trigger.dev using the MCP server."📝 Committable suggestion
Suggested change
|
||||||||||||||||||
| ); | ||||||||||||||||||
| return; | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -288,7 +345,7 @@ async function _initCommand(dir: string, options: InitCommandOptions) { | |||||||||||||||||
| log.info("Next steps:"); | ||||||||||||||||||
| log.info( | ||||||||||||||||||
| ` 1. To start developing, run ${chalk.green( | ||||||||||||||||||
| `npx trigger.dev@${cliTag} dev${options.profile ? "" : ` --profile ${options.profile}`}` | ||||||||||||||||||
| `npx trigger.dev@${cliTag} dev${options.profile ? ` --profile ${options.profile}` : ""}` | ||||||||||||||||||
| )} in your project directory` | ||||||||||||||||||
| ); | ||||||||||||||||||
| log.info(` 2. Visit your ${projectDashboard} to view your newly created tasks.`); | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -208,6 +208,46 @@ export async function initiateSkillsInstallWizard(options: SkillsWizardOptions) | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Mark the agent-skills install prompt as already seen at the current skills version. | ||
| * `trigger init` calls this after offering skills in its AI-tooling step (whether or not | ||
| * the user installs them) so `trigger dev` doesn't ask about skills again. Returns false | ||
| * if the CLI ships without bundled skills. | ||
| */ | ||
| export async function markSkillsPromptSeen(): Promise<boolean> { | ||
| const manifest = await loadSkillsManifest(); | ||
|
|
||
| if (!manifest) { | ||
| return false; | ||
| } | ||
|
|
||
| writeConfigHasSeenRulesInstallPrompt(true); | ||
| writeConfigLastRulesInstallPromptVersion(manifest.currentVersion); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * Install skills as part of `trigger init`. The user already opted in via init's AI-tooling | ||
| * prompt, so this skips the extra confirm and goes straight to target/skill selection, then | ||
| * marks the prompt seen so `trigger dev` won't re-prompt. Returns false if the CLI ships | ||
| * without bundled skills. | ||
| */ | ||
| export async function installSkillsFromInit(opts: SkillsWizardOptions = {}): Promise<boolean> { | ||
| const manifest = await loadSkillsManifest(); | ||
|
|
||
| if (!manifest) { | ||
| return false; | ||
| } | ||
|
|
||
| writeConfigHasSeenRulesInstallPrompt(true); | ||
| writeConfigLastRulesInstallPromptVersion(manifest.currentVersion); | ||
|
|
||
| await installSkills(manifest, opts); | ||
|
|
||
| return true; | ||
|
Comment on lines
+246
to
+248
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡
Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback. |
||
| } | ||
|
|
||
| async function installSkills(manifest: RulesManifest, opts: SkillsWizardOptions) { | ||
| const currentVersion = await manifest.getCurrentVersion(); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.