Skip to content

Getting started with Pinion

Pinion is a handy tool that helps you generate code and automate tasks. You can create and modify files directly from the command line based on descriptions or examples. Pinion also allows to create custom code generators that bring together the benefits of static templates, a task runner and AI-generated code.

CLI

With NodeJS available, Pinion can be installed globally like this:

sh
npm i @feathershq/pinion@pre -g
npm i @feathershq/pinion@pre -g

Now the pinion command should be available everywhere. It takes a description with %s as placeholders for filenames, followed by a list of those filenames:

sh
pinion "Write about code generators in %s" generators.md
pinion "Write about code generators in %s" generators.md

You can also use it with existing files:

sh
pinion "Translate %s to German as %s" generators.md generators.de.md
pinion "Translate %s to German as %s" generators.md generators.de.md

Note

The first time you use the command with a description you will be asked to sign into feathers.cloud using GitHub. You can try command line descriptions and AI tasks for free with no time limit. For more information see our pricing.

SDK

The Pinion SDK allows to create flexible code generators using TypeScript. Install it as a development dependency into your project like this:

npm install @feathershq/pinion@pre --save-dev
npm install @feathershq/pinion@pre --save-dev

Note

While generators are written in TypeScript your project can use any programming language. For projects without a package.json run npm init --yes first.

Generator files

A Pinion generator is any TypeScript file that exports a generate function. It can live anywhere in your project. For example, the following generators/readme.ts file generates a basic readme.md file from a template string:

ts
import type { PinionContext } from '@feathershq/pinion'
import { generator, file, renderTemplate } from '@feathershq/pinion'

interface Context extends PinionContext {
}

// A template for a markdown Readme file
const readme = () => `
# Hello world

This is a readme generated by Pinion

Copyright (c) ${new Date().getFullYear()}
`

export const generate = (context: Context) => generator(context)
  // Render the readme template
  .then(renderTemplate(readme, file('readme.md')))
import type { PinionContext } from '@feathershq/pinion'
import { generator, file, renderTemplate } from '@feathershq/pinion'

interface Context extends PinionContext {
}

// A template for a markdown Readme file
const readme = () => `
# Hello world

This is a readme generated by Pinion

Copyright (c) ${new Date().getFullYear()}
`

export const generate = (context: Context) => generator(context)
  // Render the readme template
  .then(renderTemplate(readme, file('readme.md')))

Then run

sh
npx pinion run generators/readme.ts
npx pinion run generators/readme.ts

Asking questions

You can ask questions from the command line using Inquirer with the prompt operation:

ts
import type { PinionContext } from '@feathershq/pinion'
import { generator, renderTemplate, file, prompt } from '@feathershq/pinion'

interface Context extends PinionContext {
  name: string
  description: string
}

// A template for a markdown Readme file
const readme = ({ name, description }: Context) => `
# ${name}

> ${description}

This is a readme generated by Pinion

Copyright (c) ${new Date().getFullYear()}
`

export const generate = (context: Context) => generator(context)
  // Ask prompts (using Inquirer)
  .then(prompt<Context>([
    {
      type: 'input',
      name: 'name',
      message: 'What is the name of your app?'
    },
    {
      type: 'input',
      name: 'description',
      description: 'Write a short description'
    }
  ]))
  // Render the readme template
  .then(renderTemplate(readme, file('readme.md')))
import type { PinionContext } from '@feathershq/pinion'
import { generator, renderTemplate, file, prompt } from '@feathershq/pinion'

interface Context extends PinionContext {
  name: string
  description: string
}

// A template for a markdown Readme file
const readme = ({ name, description }: Context) => `
# ${name}

> ${description}

This is a readme generated by Pinion

Copyright (c) ${new Date().getFullYear()}
`

export const generate = (context: Context) => generator(context)
  // Ask prompts (using Inquirer)
  .then(prompt<Context>([
    {
      type: 'input',
      name: 'name',
      message: 'What is the name of your app?'
    },
    {
      type: 'input',
      name: 'description',
      description: 'Write a short description'
    }
  ]))
  // Render the readme template
  .then(renderTemplate(readme, file('readme.md')))

AI tasks

Just like the descriptions in the pinion command, AI tasks can also be included in generators. The following file translates the generated readme to German and French:

ts
import type { PinionContext } from '@feathershq/pinion'
import { generator, renderTemplate, file, prompt, gpt } from '@feathershq/pinion'

interface Context extends PinionContext {
  name: string
  description: string
}

// A template for a markdown Readme file
const readme = ({ name, description }: Context) => `
# ${name}

> ${description}

This is a readme generated by Pinion

Copyright (c) ${new Date().getFullYear()}
`

export const generate = (context: Context) => generator(context)
  // Ask prompts (using Inquirer)
  .then(prompt<Context>([
    {
      type: 'input',
      name: 'name',
      message: 'What is the name of your app?'
    },
    {
      type: 'input',
      name: 'description',
      description: 'Write a short description'
    }
  ]))
  // Render the readme template
  .then(renderTemplate(readme, file('readme.md')))
  // Translate the generated file
  .then(gpt`Translate ${file('readme.md')} to German as ${file('readme.de.md')} and to French as ${file('readme.fr.md')}`)
import type { PinionContext } from '@feathershq/pinion'
import { generator, renderTemplate, file, prompt, gpt } from '@feathershq/pinion'

interface Context extends PinionContext {
  name: string
  description: string
}

// A template for a markdown Readme file
const readme = ({ name, description }: Context) => `
# ${name}

> ${description}

This is a readme generated by Pinion

Copyright (c) ${new Date().getFullYear()}
`

export const generate = (context: Context) => generator(context)
  // Ask prompts (using Inquirer)
  .then(prompt<Context>([
    {
      type: 'input',
      name: 'name',
      message: 'What is the name of your app?'
    },
    {
      type: 'input',
      name: 'description',
      description: 'Write a short description'
    }
  ]))
  // Render the readme template
  .then(renderTemplate(readme, file('readme.md')))
  // Translate the generated file
  .then(gpt`Translate ${file('readme.md')} to German as ${file('readme.de.md')} and to French as ${file('readme.fr.md')}`)

Pricing

When using the pinion command or AI tasks, requests are sent to feathers.cloud and measured by total incoming and outgoing data transfer in kilobytes (Kb). You can try Pinion for free for an unlimited time for your first 100Kb. You can see your current data transfer in the dashboard.

The Pinion GPT plan starts at

  • 5$ per month for the first 250Kb
  • Then $0.01 per additional Kb

You will get

  • Unlimited transfer
  • API key support

Note

The Pinion codebase and generators are open source under the MIT license. When not using AI tasks, no data will be sent and you do not require an account.