feat(cli): set up AI tooling in trigger init and add getting-started skill (#3872)
## Summary `trigger init` now sets up your AI coding assistant as part of project setup. Instead of the old either/or "MCP or CLI" prompt, it offers the MCP server and agent skills together, then asks whether to scaffold with the CLI or let your assistant do it. A new `getting-started` agent skill backs that hand-off: it teaches the assistant the bootstrap recipe (install the SDK, write `trigger.config.ts`, scaffold a first task, wire tsconfig/gitignore, run `trigger dev`) and is explicit about the two steps that genuinely need a human (`trigger login` and copying the DEV secret key from the dashboard). It ships in the CLI alongside the existing skills, version-matched to your SDK. Prompt-once gating is shared, so opting in or out during `init` means `trigger dev` won't ask about skills again.
This commit is contained in:
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
|
||||
if (!continueWithCLI) {
|
||||
// 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());
|
||||
}
|
||||
|
||||
// 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(
|
||||
@@ -179,11 +206,44 @@ async function _initCommand(dir: string, options: InitCommandOptions) {
|
||||
);
|
||||
|
||||
if (installError) {
|
||||
outro(`Failed to install MCP server: ${installError.message}`);
|
||||
// Don't abort init if MCP fails: skills may already be installed and the user
|
||||
// still needs the project scaffolded. Warn and carry on.
|
||||
log.warn(`Skipped MCP server: ${installError.message}`);
|
||||
} else {
|
||||
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 && 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."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +348,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.`);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { afterAll, describe, expect, it } from "vitest";
|
||||
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { resolveBundledPackageJSON } from "./skills.js";
|
||||
|
||||
/**
|
||||
* Reproduces the published layout: tshy emits a dialect stub `package.json`
|
||||
* ({"type":"module"}) in `dist/esm`, which shadows the real package root when resolving
|
||||
* from the bundled code. The skills dir ships at the package root. Resolution must skip
|
||||
* the stub and land on the root, otherwise `trigger skills` silently finds no skills.
|
||||
*/
|
||||
async function makeBundledPackage(): Promise<{ root: string; distEsm: string }> {
|
||||
const root = await mkdtemp(join(tmpdir(), "bundled-cli-"));
|
||||
|
||||
await writeFile(
|
||||
join(root, "package.json"),
|
||||
JSON.stringify({ name: "trigger.dev", version: "9.9.9-test.1" })
|
||||
);
|
||||
|
||||
await mkdir(join(root, "skills", "authoring-tasks"), { recursive: true });
|
||||
await writeFile(join(root, "skills", "authoring-tasks", "SKILL.md"), "# Authoring");
|
||||
|
||||
const distEsm = join(root, "dist", "esm");
|
||||
await mkdir(distEsm, { recursive: true });
|
||||
// The tshy dialect stub that caused the bug.
|
||||
await writeFile(join(distEsm, "package.json"), JSON.stringify({ type: "module" }));
|
||||
|
||||
return { root, distEsm };
|
||||
}
|
||||
|
||||
describe("resolveBundledPackageJSON", () => {
|
||||
const roots: string[] = [];
|
||||
|
||||
afterAll(async () => {
|
||||
await Promise.all(roots.map((d) => rm(d, { recursive: true, force: true })));
|
||||
});
|
||||
|
||||
it("skips the tshy dist/esm dialect stub and resolves the package root", async () => {
|
||||
const { root, distEsm } = await makeBundledPackage();
|
||||
roots.push(root);
|
||||
|
||||
const resolved = await resolveBundledPackageJSON(distEsm);
|
||||
|
||||
// Must be the root package.json (which has `skills/` beside it), not the dist/esm stub.
|
||||
expect(resolved).toBe(join(root, "package.json"));
|
||||
expect(dirname(resolved!)).toBe(root);
|
||||
});
|
||||
|
||||
it("resolves directly when started from a dir under the named package root (source/tsx path)", async () => {
|
||||
const { root } = await makeBundledPackage();
|
||||
roots.push(root);
|
||||
|
||||
const srcDir = join(root, "src");
|
||||
await mkdir(srcDir, { recursive: true });
|
||||
|
||||
const resolved = await resolveBundledPackageJSON(srcDir);
|
||||
|
||||
expect(resolved).toBe(join(root, "package.json"));
|
||||
});
|
||||
});
|
||||
@@ -100,10 +100,45 @@ export async function installSkillsCommand(options: unknown) {
|
||||
* `sourceDir` (the CLI's location) rather than the user's cwd. The CLI is the only source
|
||||
* of skills (there is no remote fallback), so this only returns null in the unexpected
|
||||
* case that the CLI ships without any skills.
|
||||
*
|
||||
* tshy emits a dialect stub `package.json` ({"type":"module"}) in `dist/esm`, so the
|
||||
* package.json nearest the bundled code is NOT the package root and has no `skills/`
|
||||
* beside it. We walk up to the first package.json that has a `name` (the real root);
|
||||
* that resolves correctly both when bundled (`<root>/dist/esm`) and from source
|
||||
* (`<root>/src`, run via tsx in dev/tests).
|
||||
*/
|
||||
export async function resolveBundledPackageJSON(startDir: string = sourceDir): Promise<
|
||||
string | null
|
||||
> {
|
||||
let searchDir = startDir;
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const candidate = await resolvePackageJSON(searchDir);
|
||||
const pkg = await readPackageJSON(candidate);
|
||||
|
||||
if (pkg.name) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
// Climb above this (stub) package.json and keep looking for the real root.
|
||||
const above = dirname(dirname(candidate));
|
||||
if (above === searchDir) {
|
||||
return null;
|
||||
}
|
||||
searchDir = above;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function loadSkillsManifest(): Promise<RulesManifest | null> {
|
||||
try {
|
||||
const packageJsonPath = await resolvePackageJSON(sourceDir);
|
||||
const packageJsonPath = await resolveBundledPackageJSON();
|
||||
|
||||
if (!packageJsonPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pkg = await readPackageJSON(packageJsonPath);
|
||||
const skillsDir = join(dirname(packageJsonPath), "skills");
|
||||
const version = typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
||||
@@ -208,14 +243,55 @@ export async function initiateSkillsInstallWizard(options: SkillsWizardOptions)
|
||||
}
|
||||
}
|
||||
|
||||
async function installSkills(manifest: RulesManifest, opts: 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);
|
||||
|
||||
// Returns true only if skills were actually written (false e.g. when the only target
|
||||
// chosen is "unsupported"), so callers like `trigger init` don't claim skills are ready
|
||||
// when nothing landed.
|
||||
return await installSkills(manifest, opts);
|
||||
}
|
||||
|
||||
async function installSkills(manifest: RulesManifest, opts: SkillsWizardOptions): Promise<boolean> {
|
||||
const currentVersion = await manifest.getCurrentVersion();
|
||||
|
||||
const targetNames = await resolveTargets(opts);
|
||||
|
||||
if (targetNames.length === 1 && targetNames.includes("unsupported")) {
|
||||
handleUnsupportedTargetOnly();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
const results = [];
|
||||
@@ -228,7 +304,9 @@ async function installSkills(manifest: RulesManifest, opts: SkillsWizardOptions)
|
||||
}
|
||||
}
|
||||
|
||||
if (results.some((r) => r.installations.length > 0 || r.pointer)) {
|
||||
const installedAny = results.some((r) => r.installations.length > 0 || r.pointer);
|
||||
|
||||
if (installedAny) {
|
||||
log.step("Installed the following skills:");
|
||||
|
||||
for (const r of results) {
|
||||
@@ -244,6 +322,8 @@ async function installSkills(manifest: RulesManifest, opts: SkillsWizardOptions)
|
||||
`${cliLink("Learn how to use Trigger.dev skills", "https://trigger.dev/docs/agents/rules/overview")}`
|
||||
);
|
||||
}
|
||||
|
||||
return installedAny;
|
||||
}
|
||||
|
||||
function handleUnsupportedTargetOnly() {
|
||||
|
||||
Reference in New Issue
Block a user