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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/intent-auto-install.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@tanstack/cli': minor
'@tanstack/create': minor
---

feat(cli): auto-install TanStack Intent during scaffolding

`tanstack create` and `tanstack add` now run `npx @tanstack/intent install`
after dependency installation, wiring up skill mappings for coding agents.
The behavior is controlled by a new `--intent` / `--no-intent` flag (default
on) and persists to `.cta.json` so subsequent `add` invocations honor the
original choice. Failures are surfaced as warnings instead of aborting the
scaffold.
22 changes: 21 additions & 1 deletion packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ function getCreateTelemetryProperties(projectName: string, options: CliOptions)
framework: options.framework ? sanitizeId(options.framework) : undefined,
git: options.git,
install: options.install !== false,
intent: options.intent !== false,
interactive: !!options.interactive,
json: !!options.json,
non_interactive: !!options.nonInteractive || !!options.yes,
Expand Down Expand Up @@ -221,6 +222,7 @@ function getResolvedCreateTelemetryProperties(
framework: sanitizeId(finalOptions.framework.id),
git: finalOptions.git,
install: finalOptions.install !== false,
intent: finalOptions.intent,
package_manager: finalOptions.packageManager,
router_only: !!cliOptions.routerOnly,
toolchain: toolchain ? sanitizeId(toolchain.id) : undefined,
Expand Down Expand Up @@ -875,6 +877,14 @@ export function cli({
.option('--json', 'output JSON for automation', false)
.option('--git', 'create a git repository')
.option('--no-git', 'do not create a git repository')
.option(
'--intent',
'set up TanStack Intent skill mappings for coding agents',
)
.option(
'--no-intent',
'skip TanStack Intent setup',
)
.option(
'--target-dir <path>',
'the target directory for the application root',
Expand Down Expand Up @@ -1441,7 +1451,15 @@ Remove your node_modules directory and package lock file and re-install.`,
'Name of the add-ons (or add-ons separated by spaces or commas)',
)
.option('--forced', 'Force the add-on to be added', false)
.action(async (addOns: Array<string>, options: { forced: boolean }) => {
.option(
'--intent',
'set up TanStack Intent skill mappings for coding agents',
)
.option(
'--no-intent',
'skip TanStack Intent setup',
)
.action(async (addOns: Array<string>, options: { forced: boolean; intent?: boolean }) => {
try {
await runWithTelemetry(
'add',
Expand Down Expand Up @@ -1472,6 +1490,7 @@ Remove your node_modules directory and package lock file and re-install.`,
if (selectedAddOns.length) {
await addToApp(environment, selectedAddOns, resolve(process.cwd()), {
forced: options.forced,
intent: options.intent,
})
}
return
Expand All @@ -1484,6 +1503,7 @@ Remove your node_modules directory and package lock file and re-install.`,
})
await addToApp(environment, parsedAddOns, resolve(process.cwd()), {
forced: options.forced,
intent: options.intent,
})
},
)
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/command-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ export async function normalizeOptions(
DEFAULT_PACKAGE_MANAGER,
git: cliOptions.git ?? true,
install: cliOptions.install,
intent: cliOptions.intent ?? true,
chosenAddOns,
addOnOptions: {
...populateAddOnOptionsDefaults(chosenAddOns),
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/dev-watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ export class DevWatchManager {
targetDir: this.tempDir,
git: false,
install: packageMetadataChanged,
intent: false,
}

// Show package installation indicator if needed
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface CliOptions {
devWatch?: string
runDev?: boolean
install?: boolean
intent?: boolean
addOnConfig?: string
force?: boolean
routerOnly?: boolean
Expand Down
7 changes: 7 additions & 0 deletions packages/create/src/add-to-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { mergePackageJSON } from './package-json.js'
import { runSpecialSteps } from './special-steps/index.js'
import { loadStarter } from './custom-add-ons/starter.js'
import { setupIntent } from './integrations/intent.js'

import type { Environment, Options } from './types.js'
import type { PersistedOptions } from './config-file.js'
Expand Down Expand Up @@ -54,6 +55,7 @@ async function createOptions(
]),
targetDir,
starter,
intent: json.intent ?? false,
} as Options
}

Expand Down Expand Up @@ -230,6 +232,7 @@ export async function addToApp(
cwd: string,
options?: {
forced?: boolean
intent?: boolean
},
) {
const persistedOptions = await getCurrentConfiguration(environment, cwd)
Expand Down Expand Up @@ -318,6 +321,10 @@ export async function addToApp(

await runNewCommands(environment, persistedOptions, cwd, output)

const intent = options?.intent ?? persistedOptions.intent ?? true
newOptions.intent = intent
await setupIntent(environment, cwd, newOptions)

environment.startStep({
id: 'write-config-file',
type: 'file',
Expand Down
3 changes: 3 additions & 0 deletions packages/create/src/create-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { resolvePackageJSONLatest } from './npm-resolver.js'
import { createTemplateFile } from './template-file.js'
import { installShadcnComponents } from './integrations/shadcn.js'
import { setupGit } from './integrations/git.js'
import { setupIntent } from './integrations/intent.js'
import { runSpecialSteps } from './special-steps/index.js'

import type { Environment, FileBundleHandler, Options } from './types.js'
Expand Down Expand Up @@ -294,6 +295,8 @@ async function runCommandsAndInstallDependencies(
}

await installShadcnComponents(environment, options.targetDir, options)

await setupIntent(environment, options.targetDir, options)
}

async function seedEnvValues(environment: Environment, options: Options) {
Expand Down
2 changes: 2 additions & 0 deletions packages/create/src/custom-add-ons/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export async function createAppOptionsFromPersisted(
starter: json.starter ? await loadStarter(json.starter) : undefined,
chosenAddOns,
addOnOptions: populateAddOnOptionsDefaults(chosenAddOns),
intent: json.intent ?? false,
}
}

Expand All @@ -103,6 +104,7 @@ export function createSerializedOptionsFromPersisted(
framework: json.framework,
starter: json.starter,
addOnOptions: {},
intent: json.intent ?? false,
}
}

Expand Down
47 changes: 47 additions & 0 deletions packages/create/src/integrations/intent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { resolve } from 'node:path'

import { packageManagerExecute } from '../package-manager.js'

import type { Environment, Options } from '../types.js'

export async function setupIntent(
environment: Environment,
targetDir: string,
options: Options,
) {
if (!options.intent) {
return
}

const s = environment.spinner()
s.start('Setting up TanStack Intent skill mappings...')
environment.startStep({
id: 'setup-intent',
type: 'command',
message: 'Setting up TanStack Intent skill mappings...',
})

try {
await packageManagerExecute(
environment,
resolve(targetDir),
options.packageManager,
'@tanstack/intent',
['install'],
)
environment.finishStep('setup-intent', 'TanStack Intent configured')
s.stop('TanStack Intent configured')
} catch (error) {
const message =
error instanceof Error ? error.message : 'Unknown error'
environment.finishStep(
'setup-intent',
`TanStack Intent setup skipped: ${message}`,
)
s.stop('TanStack Intent setup skipped')
environment.warn(
'TanStack Intent setup failed',
`Continuing without it. You can run it later with: npx @tanstack/intent install\n\n${message}`,
)
}
}
1 change: 1 addition & 0 deletions packages/create/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ export interface Options {
packageManager: PackageManager
git: boolean
install?: boolean
intent: boolean

chosenAddOns: Array<AddOn>
addOnOptions: Record<string, Record<string, any>>
Expand Down
Loading