API
Introβ
Welcome to Script Kit! πβ
Script Kit provides an opinionated set of global APIs meant to streamline the process of writing scripts. Many of them (such as prompts) are meant to interact with the app, but there are also many common APIs for working with files, etc, that are simply built-in node or third-party libraries exposed as globals.
You do not need to use any of these APIs. You are free to write your scripts and add whatever npm packages you like.
If you have questions, please reach out on our Script Kit GitHub Discussions
Happy Scripting! β€οΈ - John Lindquist
Play with Examples in the Appβ
With Script Kit open, type docs
and hit enter.
With any example open, press cmd+p
to generate a script where you can experiment with examples contained in that section.
Basicsβ
Script Importsβ
Script Kit Scripts start with importing the SDK:
import "@johnlindquist/kit"
You can also import any other library from npm and Script Kit will prompt you to install it (if you haven't already installed it in the current kenv).
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai'
Top-Level awaitβ
All scripts are top-level await and essentially run top to bottom. Expect to see a lot of await
in the scripts.
const downloadMarkdownPattern = home("Downloads", "*.md");
const files = await globby(downloadMarkdownPattern);
let totalContent = "";
for await (const file of files) {
const content = await readFile(file, "utf8");
totalContent += content;
}
await editor(totalContent);
Global Helpersβ
All scripts are standard node.js scripts with added helpers from the SDK. Many of the most common helpers are provided in the global scope to save you from having to import them. For example, the follow script requires no imports:
const url =
"https://raw.githubusercontent.com/johnlindquist/kit-docs/refs/heads/main/API.md";
const response = await get(url);
const content = response.data;
const apiPath = home("Downloads", "API.md");
await writeFile(apiPath, content);
Script Metadataβ
All metadata is optional. There are two metadata modes based on your preferences:
Metadata Commentsβ
These are the defaults and have been around since Script Kit v1.
// Name: My Amazing Script
Metadata Typed Globalβ
Introduced in Script Kit v3, these allow a fully-typed, autocomplete experience:β«
metadata = {
name: "My Amazing Script
}
metadataβ
The metadata
object can include:
name
: Display name in Script Kit UI (defaults to filename)author
: Creator's namedescription
: Brief script summaryenter
: Text shown on Enter buttonalias
: Alternative search termimage
: Path to script iconshortcut
: Global keyboard shortcut, e.g, cmd+opt+4shortcode
: Execute when typed + space in menutrigger
: Execute when typed in menuexpand
: Text expansion trigger (replaces deprecatedsnippet
)keyword
: Search keyword for menupass
: Pass menu input as arg (true/string/RegExp)group
: Menu organization categoryexclude
: Hide from menuwatch
: File/dir to watch for changeslog
: Disable logging if falsebackground
: Run as background processsystem
: Trigger on system events (sleep/wake/etc)schedule
: Cron expression for timingaccess
: REST API access level (public/key/private)response
: Allow REST API responseindex
: Order within group### Metadata
metadata exampleβ
name: "Metadata Example",
description: "This is an example of how to use metadata in a script",
author: "John Lindquist",
};
AIβ
Script Kit provides several AI-related global helpers to make it easy to integrate AI and LLM-powered features into your scripts. These helpers allow you to generate text, interact with assistants, and work with structured outputs using schemas.
Setupβ
Script Kit automatically loads AI-related environment variables from your ~/.kenv/.env
file. To configure your default AI provider, model, and other options, add the following variables to your ~/.kenv/.env
:
AI_DEFAULT_PROVIDER
β Default AI provider (e.g.openai
,anthropic
,google
,xai
,openrouter
)AI_DEFAULT_MODEL
β Default model for the provider (e.g.gpt-4o
,claude-3-opus-20240229
)AI_DEFAULT_TEMPERATURE
β Default temperature for completions (e.g.0.7
)AI_DEFAULT_MAX_TOKENS
β Default max tokens for completions (e.g.1000
)
Example ~/.kenv/.env
:
AI_DEFAULT_PROVIDER=anthropic
AI_DEFAULT_MODEL=claude-3-opus-20240229
AI_DEFAULT_TEMPERATURE=0.7
AI_DEFAULT_MAX_TOKENS=1000
These values are used as defaults for all AI helpers (ai
, assistant
, generate
). You can override them per-call by passing options to the helpers.
aiβ
The ai
helper creates a text generation function using a prompt. Use it to generate text completions based on user input or other data.
ai exampleβ
const emojiStoryGenerator = ai('Generate a story using only emoji, no text.')
const story = await emojiStoryGenerator('Epic Fantasy')
await editor(story)
ai convert selected textβ
const text = await getSelectedText()
const rewriter = ai(`
You are an expert at cleaning up text for clarity and readability.
- Only improve the text.
- Do not include any other text in your response.
- Avoid using markdown formatting.
`)
const cleanedText = await rewriter(text)
await setSelectedText(cleanedText)
ai prompt improverβ
const prompt = `
# Improve the User's Prompt Following the Patterns Below
> Practical prompt patterns to help anyone get clearer, more reliable answers from an AI agent.
| # | Pattern | Why It Matters | Template | Example Prompt |
|---|-------------------------------|-------------------------------------------------------------------------|--------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|
| 1 | Lead with the ask | The model reads top-down; putting the goal first stops it wandering. | \`Do X.[context]\` | "Summarize this PDF in 5 bullet points. The text is below: ..." |
| 2 | Repeat the key ask at the end | Long contexts sometimes truncate; an end-cap protects you. | \`...[detail]...REMEMBER: Do X.\` | "List pros & cons, keep it balanced. REMEMBER: 5 pros, 5 cons." |
| 3 | Specify output shape | Dictating format cuts revision loops. | \`Return as: 1) short title, 2) table(CSV).\` | "Give 3 holiday ideas. Format: destination, flight-time-hrs, avg cost." |
| 4 | Use clear delimiters | Backticks/headings/XML keep sections from blending. | \`TEXT TO ANALYSE\` | "Rate the style of the text between the fences." |
| 5 | Induce step-by-step thinking | Planning first boosts multi-step accuracy. | \`Think step - by - step then answer.\` | "Solve this puzzle. Think step-by-step before giving the final move." |
| 6 | Ask it to plan its workflow | For big jobs, AI outlines tasks before doing them. | \`First draft a plan, wait, then execute.\` | "We're writing an e-book. βΆ Outline chapters. β· Wait. βΈ When I say 'go', draft chapter 1." |
| 7 | Limit or widen knowledge sources | Controls hallucination. | \`Use only the info below. / Combine basic knowledge + this context.\` | "Using only the product sheet below, write FAQs." |
| 8 | Guide information retrieval | Helps AI pick the right docs before answering. | \`List which docs look relevant, then answer.\` | "30 sales memos attached. 1) Name the 5 most relevant. 2) Summarise their common points." |
| 9 | Show a style/example | Anchors tone, length, vocabulary. | \`Match the style of: <example>\` | "Review this gadget in the style of the sample below: 'Short, witty, 3 key facts...'" |
| 10| Set correction handles | One-line fixes let you steer quickly. | \`If length > 150 words, shorten.\` | "Describe blockchain to a child. If your answer is >150 words, cut it in half." |
| 11| Tell it when to stop or loop | Prevents half-finished lists or runaway essays. | \`Keep going until you list 20 ideas, then stop.\` | "Brainstorm webinar titles. Give exactly 12, then finish." |
| 12| Request the hidden reasoning | Good for audits; otherwise omit to keep it short. | \`After the answer, include a brief reasoning section.\` | "Which of these stocks looks over-valued? Answer first; add a 2-sentence rationale below a divider." |
`
const text = await getSelectedText()
const promptImprover = ai(prompt)
menu('Improving Prompt') // Changes the script kit icon in the system tray
const improvedPrompt = await promptImprover(text)
menu('') // Resets the script kit icon in the system tray
await setSelectedText(improvedPrompt)
ai summarizeβ
const summarize = ai(
"Summarize the following text in one sentence"
)
const summary = await summarize(
"The quick brown fox jumps over the lazy dog. The lazy dog was not impressed. The fox, feeling dejected, went home."
)
await div(md(`
## AI Text Summarization
**Input:** The quick brown fox jumps over the lazy dog. The lazy dog was not impressed. The fox, feeling dejected, went home.
**Summary:** ${summary}
`))
ai translateβ
const translateToFrench = ai("Translate to French")
const frenchText = await translateToFrench("Hello, how are you today?")
await div(md(`
## AI Translation
**English:** Hello, how are you today?
**French:** ${frenchText}
`))
assistantβ
The assistant
helper creates an AI assistant that can maintain context and handle multi-turn conversations. You can add user and system messages, and stream responses to the UI.
assistant exampleβ
const downloadsPath = home("Downloads")
const files = await readdir(downloadsPath)
const jester = assistant("Tell a joke about these file names.")
jester.addUserMessage(files.join("\n"))
await editor({
onInit: async () => {
for await (const chunk of jester.textStream) {
editor.append(chunk)
}
}
})
assistant chatβ
const chatbot = assistant('You are a helpful assistant')
let currentMessage = ""
await chat({
onSubmit: async (input) => {
currentMessage = ""
chatbot.stop()
chat.addMessage({
text: "...",
position: "left"
})
chatbot.addUserMessage(input)
for await (const chunk of chatbot.textStream) {
currentMessage += chunk
const markdownMessage = md(currentMessage)
const messageIndex = (await chat.getMessages()).length - 1
chat.setMessage(messageIndex, {
text: markdownMessage,
})
}
}
})
assistant streamingβ
const ideaGenerator = assistant("You're an idea generatior. Generate 3 extremely terse ideas based on my query")
const idea = await arg("Enter your idea")
ideaGenerator.addUserMessage(idea)
const remainingIterations = 3
const editorStreamer = async () => {
for await (const chunk of ideaGenerator.textStream) {
editor.append(chunk)
}
editor.append("\n\n")
}
await editor({
onInit: async () => {
for (let i = 0; i < remainingIterations; i++) {
await editorStreamer()
ideaGenerator.addSystemMessage("Pick your favorite idea and generate 3 more based on it")
}
}
})
assistant with toolsβ
// Assistant with tools
const fakeWeatherAPI = ({ location }: { location: string }) => {
if (location === "moon") return "It's kinda dusty"
return `The weather in ${location} is 70 and sunny.`
}
const weatherTool = {
getWeather: {
description: "Call this tool for any weather location in the Universe",
parameters: z.object({
location: z.string().describe("The location to get the weather for")
}),
execute: async ({ location }: { location: string }) => {
console.log("Executing weather tool")
const weather = fakeWeatherAPI({ location })
return { weather }
}
}
}
console.log("Creating weather assistant")
const weatherAssistant = assistant(
"You are a helpful weather assistant. Use the provided tools to answer the user's questions.",
{ tools: weatherTool }
)
weatherAssistant.addUserMessage("What's the weather on the moon?")
const result = await weatherAssistant.generate()
console.log("Generating weather assistant")
let response = ""
if ("text" in result) {
response = result.text
} else {
toast("No text response")
await editor(JSON.stringify(result, null, 2))
exit()
}
await div(md(`
## Assistant with Tools
**User:** What's the weather on the moon?
**Assistant:** ${response}
`))
generateβ
The generate
helper allows you to generate structured data from text using a schema (such as a Zod schema). This is useful for extracting information, performing analysis, or ensuring the output matches a specific format.
generate exampleβ
const result = await generate(
'Generate 10 random users as an array in a property called "users"',
z.object({
users: z.array(
z.object({
firstName: z.string(),
lastName: z.string(),
age: z.number(),
email: z.string().email(),
})
)
})
)
const users = result.users
await editor(JSON.stringify(users, null, 2))
generate extract data from textβ
const userSchema = z.object({
name: z.string(),
age: z.number().optional(),
email: z.string().email().optional(),
})
const extractUserInfo = async (text: string) => {
return generate(
`Extract user information from the text: ${text}`,
userSchema
)
}
const userInfo = await extractUserInfo(
"My name is John Doe. I am 30 years old. You can reach me at john.doe@example.com"
)
await div(md(`
## User Info Extraction
**Text:** My name is John Doe. I am 30 years old. You can reach me at john.doe@example.com
**Result:**
\`\`\`json
${JSON.stringify(userInfo, null, 2)}
\`\`\`
`))
generate extract sentimentβ
const sentimentSchema = z.object({
sentiment: z.enum(["positive", "negative", "neutral"]),
confidence: z.number().min(0).max(1),
keywords: z.array(z.string()).describe("Keywords that contributed to the sentiment")
})
const extractSentiment = async (text: string) => {
return generate(
`Extract sentiment from the following text: ${text}`,
sentimentSchema
)
}
const sentimentResult = await extractSentiment("I love Script Kit! It's so easy to use.")
await div(md(`
## Sentiment Analysis
**Text:** I love Script Kit! It's so easy to use.
**Result:**
\`\`\`json
${JSON.stringify(sentimentResult, null, 2)}
\`\`\`
`))
generate structured dataβ
// Global Generate Function Example
// This script demonstrates structured object generation using the global generate() function
// Define a schema for extracting contact information
const contactSchema = z.object({
name: z.string().describe("Person's full name"),
email: z.string().email().describe("Email address"),
phone: z.string().optional().describe("Phone number if mentioned"),
company: z.string().optional().describe("Company or organization"),
role: z.string().optional().describe("Job title or role"),
interests: z.array(z.string()).describe("Professional interests or expertise areas"),
priority: z.enum(['low', 'medium', 'high']).describe("Priority level for follow-up")
})
const contact = await generate(
'The coolest person in the world',
contactSchema
)
// Display the structured result
await div(md(`
# Contact Information Extracted
**Name:** ${contact.name}
**Email:** ${contact.email}
**Phone:** ${contact.phone || 'Not provided'}
**Company:** ${contact.company || 'Not provided'}
**Role:** ${contact.role || 'Not provided'}
**Priority:** ${contact.priority}
**Interests:**
${contact.interests.length > 0
? contact.interests.map(interest => `- ${interest}`).join('\n')
: '- None specified'
}
---
* Generated using the global \`generate()\` function with Zod schema validation*
`))
Switching AI Providersβ
Script Kit supports multiple AI providers out of the box: openai
, anthropic
, google
, xai
, and openrouter
.
You can switch between providers by passing a model string in the format provider:model-id
to the model
option.
const result = await ai("Say hello", { model: "anthropic:claude-3-opus-20240229" })
This will use Anthropic's Claude 3 Opus model. Supported providers are:
openai
anthropic
google
xai
openrouter
ai provider switcherβ
const providers = [
{ name: "OpenAI", value: "openai", models: ["gpt-4o", "gpt-4-turbo", "gpt-3.5-turbo"] },
{ name: "Anthropic", value: "anthropic", models: ["claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307"] },
{ name: "Google", value: "google", models: ["gemini-1.5-pro-latest", "gemini-1.0-pro"] },
{ name: "xAI (Grok)", value: "xai", models: ["grok-1"] },
{ name: "OpenRouter", value: "openrouter", models: ["openrouter/cognitive-compute", "openrouter/other-model"] },
]
const provider = await arg("Select AI Provider", providers.map(p => ({ name: p.name, value: p.value })))
const selectedProvider = providers.find(p => p.value === provider)!
const model = await arg("Select Model", selectedProvider.models)
const prompt = await arg("Enter your prompt")
const modelString = `${provider}:${model}`
const result = await ai(prompt, { model: modelString })
await div(md(`
### Provider: ${selectedProvider.name}
### Model: ${model}
**Prompt:**
> ${prompt}
**Result:**
> ${result}
`))
Promptsβ
argβ
- Accept text input from the user.
- Optionally provide a list of choices filtered by the text input.
- Optionally provide a list of actions to trigger when the user presses a shortcut.
- The first argument is a string or a prompt configuration object.
- The second argument is a list of choices, a string to render, or a function that returns choices or a string to render.
arg exampleβ
let value = await arg()
arg basic string inputβ
let name = await arg("Enter your name")
arg onblurβ
await arg({
// Prevent the "arg" prompt from closing when blurred
// (or implement a custom behavior)
onBlur: () => { }
})
arg with async choices objectβ
let person = await arg("Select a person", async () => {
let response = await get("https://swapi.dev/api/people/");
// return an array of objects with "name", "value", and "description" properties
return response?.data?.results.map((person) => {
return {
name: person.name,
description: person.url,
value: person
}
});
})
arg with async choicesβ
let name = await arg("Select a name", async () => {
let response = await get("https://swapi.dev/api/people/");
return response?.data?.results.map((p) => p.name);
})
arg with choices arrayβ
let name = await arg("Select a name", [
"John",
"Mindy",
"Joy",
])
arg with generated choicesβ
let char = await arg("Type then pick a char", (input) => {
// return an array of strings
return input.split("")
})
arg with shortcutsβ
let url = "https://swapi.dev/api/people"
let name = await arg({
placeholder: "Select a name",
shortcuts: [
{
name: "Explore API",
key: "cmd+e",
onPress: async () => {
open(url)
},
bar: "right"
}
]
}, async () => {
let response = await get(url);
return response?.data?.results.map((p) => p.name);
})
miniβ
Same API as arg
, but with a compact format.
mini exampleβ
let name = await mini("Enter your name")
microβ
Same API as arg
, but with a tiny, adorable UI.
micro exampleβ
let name = await micro("Enter your name")
envβ
Load an env var if it exists, prompt to set the env var if not:
You can also prompt the user to set the env var using a prompt by nesting it in an async function:
env exampleβ
// Write write "MY_ENV_VAR" to ~/.kenv/.env
let MY_ENV_VAR = await env("MY_ENV_VAR")
env example with promptβ
// Prompt the user to select from a path
let OUTPUT_DIR = await env("OUTPUT_DIR", async () => {
return await path({
hint: `Select the output directory`,
})
})
editorβ
The editor
function opens a text editor with the given text. The editor is a full-featured "Monaco" editor with syntax highlighting, find/replace, and more. The editor is a great way to edit or update text to write a file. The default language is markdown.
editor exampleβ
let content = await editor()
editor load remote text contentβ
let response = await get(`https://raw.githubusercontent.com/johnlindquist/kit/main/API.md`)
let content = await editor(response.data)
editor with initial contentβ
let content = await editor("Hello world!")
divβ
div
displays HTML. Pass a string of HTML to div
to render it. div
is commonly used in conjunction with md
to render markdown.
- Just like arg, the first argument is a string or a prompt configuration object.
- Optional:The second argument is a string of tailwind class to apply to the container, e.g.,
bg-white p-4
.
div exampleβ
await div(`Hello world!`)
div with markdownβ
await div(md(`
# example!
### Thanks for coming to my demo
* This is a list
* This is another item
* This is the last item
`))
div with submit linksβ
let name = await div(md(`# Pick a Name
* [John](submit:John)
* [Mindy](submit:Mindy)
* [Joy](submit:Joy)
`))
await div(md(`# You selected ${name}`))
div with tailwind classesβ
await div(`Hello world!`, `bg-white text-black text-4xl p-4`)
termβ
The term
function opens a terminal window. The terminal is a full-featured terminal, but only intended for running commands and CLI tools that require user input. term
is not suitable for long-running processes (try exec
instead).
- Optional: the first argument is a command to run with the terminal
term exampleβ
await term(`cd ~/.kenv/scripts && ls`)
term with commandβ
await term(`cd ~/.kenv/scripts && ls`)
templateβ
The template
prompt will present the editor populated by your template. You can then tab through each variable in your template and edit it.
- The first argument is a string template. Add variables using $1, $2, etc. You can also use
template exampleβ
let text = await template(`Hello $1!`)
template standard usageβ
let text = await template(`
Dear \${1:name},
Please meet me at \${2:address}
Sincerely, John`)
hotkeyβ
The hotkey
prompt allows you to press modifier keys, then submits once you've pressed a non-monodifier key. For example, press command
then e
to submit key info about the command
and e
keys:
{
"key": "e",
"command": true,
"shift": false,
"option": false,
"control": false,
"fn": false,
"hyper": false,
"os": false,
"super": false,
"win": false,
"shortcut": "command e",
"keyCode": "KeyE"
}
This can be useful when you want to use a palette of commands and trigger each of them by switching on a hotkey.
- Optional: The first argument is a string to display in the prompt.
hotkey exampleβ
let keyInfo = await hotkey()
await editor(JSON.stringify(keyInfo, null, 2))
dropβ
Use await drop()
to prompt the user to drop a file or folder.
drop exampleβ
// Dropping text or an image from the browser returns a string
let fileInfos = await drop()
let filePaths = fileInfos.map(f => f.path).join(",")
await div(md(filePaths))
fieldsβ
The fields
prompt allows you to rapidly create a form with fields.
- An array of labels or objects with label and field properties.
fields exampleβ
let [first, last] = await fields(["First name", "Last name"])
fields edit the keys and values of an objectβ
let data = {
name: "John",
age: 42,
location: "USA",
};
let result = await fields(
Object.entries(data).map(([key, value]) => ({
name: key,
label: key,
value: String(value),
}))
);
let newData = Object.entries(data).map(([key], i) => ({
[key]: result[i],
}));
inspect(newData);
fields with field propertiesβ
let [name, age] = await fields([
{
name: "name",
label: "Name",
type: "text",
placeholder: "John"
},
{
name: "age",
label: "Age",
type: "number",
placeholder: "40"
}
])
formβ
Use an HTML form which returns an Object based on the names of the form fields.
form exampleβ
let result = await form(`
<div class="p-4">
<input type="text" name="textInput" placeholder="Text Input" />
<input type="password" name="passwordInput" placeholder="Password" />
<input type="email" name="emailInput" placeholder="Email" />
<input type="number" name="numberInput" placeholder="Number" />
<input type="date" name="dateInput" placeholder="Date" />
<input type="time" name="timeInput" placeholder="Time" />
<input type="datetime-local" name="dateTimeInput" placeholder="Date and Time" />
<input type="month" name="monthInput" placeholder="Month" />
<input type="week" name="weekInput" placeholder="Week" />
<input type="url" name="urlInput" placeholder="URL" />
<input type="search" name="searchInput" placeholder="Search" />
<input type="tel" name="telInput" placeholder="Telephone" />
<input type="color" name="colorInput" placeholder="Color" />
<textarea name="textareaInput" placeholder="Textarea"></textarea>
</div>
`)
inspect(result)
chatβ
A chat prompt. Use chat.addMessage()
to insert messages into the chat.
Note: Manually invoke
submit
inside of a shortcut/action/etc to end the chat.
Also see the included "chatgpt" example for a much more advanced scenario.
chat exampleβ
await chat({
onInit: async () => {
chat.addMessage({
// Note: text and position are implemented, there are other properties that are a WIP
text: "You like Script Kit",
position: "left",
})
await wait(1000)
chat.addMessage({
text: "Yeah! It's awesome!",
position: "right",
})
await wait(1000)
chat.addMessage({
text: "I know, right?!?",
position: "left",
})
await wait(1000)
chat.addMessage({
text: `<img src="https://media0.giphy.com/media/yeE6B8nEKcTMWWvBzD/giphy.gif?cid=0b9ef2f49arnbs4aajuycirjsclpbtimvib6a76g7afizgr5&ep=v1_gifs_search&rid=giphy.gif" width="200px" />`,
position: "right",
})
},
})
selectFileβ
Prompt the user to select a file using the Finder dialog:
selectFile exampleβ
let filePromptMessage = "Select a file to upload"
let filePath = await selectFile(filePromptMessage)
let text = await readFile(filePath, "utf8")
let gist = await createGist(text)
selectFolderβ
Prompt the user to select a folder using the Finder dialog:
selectFolder exampleβ
let promptMessage = "Select a folder for your project"
let folderPath = await selectFolder(promptMessage)
let files = await readdir(folderPath)
await editor(files.join("\n"))
pathβ
The path
prompt allows you to select a file or folder from the file system. You navigate with tab/shift+tab (or right/left arrows) and enter to select.
- Optional: The first argument is the initial directory to open with. Defaults to the home directory.
path exampleβ
let selectedFile = await path()
path example startpathβ
const projectPath = await path({
startPath: home("dev"),
hint: "Select a project from your dev folder",
});
await editor(projectPath);
selectβ
select
lets you choose from a list of options.
- The first argument is a array or a prompt configuration object.
- The second argument is a list of choices, a array to render, or a function that returns choices or a string to render.
select exampleβ
// Return an array of selected items
const multipleChoice = await select("Select one or more developer", [
"John",
"Nghia",
"Mindy",
"Joy",
]);
await editor(JSON.stringify(multipleChoice, null, 2));
select a choice with a single keystrokeβ
let choice = await arg({
placeholder: "Choose a color",
choices: [
{ name: "[R]ed", value: "red" },
{ name: "[G]reen", value: "green" },
{ name: "[B]lue", value: "blue" },
],
})
await div(md(`You chose ${choice}`))
select array objectβ
const people = [
{
name: "John",
description: "Full-stack Dev",
value: "John",
},
{
name: "Nghia",
description: "Full-stackoverflow dev",
value: "Nghia",
},
{
name: "Mindy",
description: "Business Analyst",
value: "Mindy",
},
{
name: "Joy",
description: "Leader",
value: "Joy",
},
]
let multipleChoice = await select(
"Select one or more developer",
people
)
select async choices array objectβ
let name = await select(
"GET: NAME (please wait)",
async () => {
let response = await get(
"https://swapi.dev/api/people/"
)
return response?.data?.results.map(person => {
return {
name: person.name,
description: `height: ${person.height}, mass: ${person.mass}`,
value: person,
preview: () => JSON.stringify(person),
}
})
}
)
select basic array inputβ
let multipleChoice = await select(
"Select one or more developer",
["John", "Nghia", "Mindy", "Joy"]
)
select generated input choicesβ
let word = await select("Type then pick a words", input => {
return input.trim().split(new RegExp("[.,;/-_\n]", "g"))
})
inspectβ
inspect
takes an object and writes out a text file you can use to read/copy/paste the values from:
Note: It will automatically convert objects to JSON to display them in the file
inspect exampleβ
let response = await get("https://swapi.dev/api/people/1/")
await inspect(response.data)
devβ
dev
Opens a standalone instance of Chrome Dev Tools so you can play with JavaScript in the console. Passing in an object will set the variable x
to your object in the console making it easy to inspect.
- Optional: the first argument is an object to set to the variable
x
to in the console.
dev exampleβ
dev()
dev with objectβ
dev({
name: "John",
age: 40
})
findβ
A file search prompt
find exampleβ
let filePath = await find("Search in the Downloads directory", {
onlyin: home("Downloads"),
})
await revealFile(filePath)
webcamβ
Prompt for webcam access. Press enter to capture an image buffer:
webcam exampleβ
let buffer = await webcam()
let imagePath = tmpPath("image.jpg")
await writeFile(imagePath, buffer)
await revealFile(imagePath)
micβ
Record from the mic, get a buffer back
mic exampleβ
const tmpMicPath = tmpPath("mic.webm");
const buffer = await mic();
await writeFile(tmpMicPath, buffer);
await playAudioFile(tmpMicPath);
eyeDropperβ
Grab a color from your desktop
Note: Behaves best on Mac. Windows might be locked to only the Script Kit app prompt.
{
"sRGBHex": "#e092d9",
"rgb": "rgb(224, 146, 217)",
"rgba": "rgba(224, 146, 217, 1)",
"hsl": "hsl(305, 56%, 73%)",
"hsla": "hsla(305, 56%, 73%, 1)",
"cmyk": "cmyk(0%, 35%, 3%, 12%)"
}
eyeDropper exampleβ
const result = await eyeDropper();
await editor(JSON.stringify(result, null, 2));
Choicesβ
formatChoicesβ
Formats an array of choices.
- If a choice is not an object, it is converted to a basic choice object.
- If a choice has a nested
choices
array (i.e. represents a group), then:- The group header is formatted (its
group
property is preserved if already set, or defaulted to its name). - Its sub-choices are formatted in their original order.
- After processing the subβchoices, any items with an
index
property are reβinserted at the appropriate positions.
- The group header is formatted (its
- For topβlevel non-group items, if every item is nonβgroup, then we reβinsert the indexed items in the final array.
Parameters:
choices
: An array of choices or simple valuesclassName
: An optional default className
Returns the formatted array of choices.
Advancedβ
Actions (cmd+k)β
Actions are available on all prompts. Actions allow you to insert custom behaviors outside of the normal flow of the script:
arg actions exampleβ
const result = await arg(
"What is your name?",
["John", "Mindy", "Ben"],
// Define an Array of Actions
[
{
name: "Submit Joy",
shortcut: `${cmd}+j`,
onAction: () => {
submit("Joy");
},
},
]
);
await editor(JSON.stringify(result, null, 2));
div actions exampleβ
const html = md(`# Hello World`);
await div(html, [
{
name: "Goodbye",
onAction: () => {
setDiv("Goodbye");
},
},
]);
editor actions exampleβ
await editor("Hello World", [
{
name: "Exclaim",
shortcut: `${cmd}+2`,
visible: true, // show the action in the shortcuts bar
onAction: () => {
editor.append("!");
},
},
{
name: "Clear",
shortcut: `${cmd}+`,
visible: true, // show the action in the shortcuts bar
onAction: () => {
editor.setText("");
},
},
]);
flagβ
A flag is almost exclusively used for the CLI, rarely with a prompt. When using a CLI script:
my-script --debug --exclude "*.md"
The flags in your script will be set as:
flag.debug = true
flag.exclude = "*.md"
flag exampleβ
// This concept is replaced by "Actions", but you will see it in older/legacy scripts
const result = await arg({
placeholder: "What is your name?",
flags: {
post: {
// This will submit the prompt with the "post" flag
shortcut: `${cmd}+p`,
},
put: {
// This will submit the prompt with the "put" flag
shortcut: `${cmd}+u`,
},
delete: {
// This will submit the prompt with the "delete" flag
shortcut: `${cmd}+d`,
},
},
});
await editor(
JSON.stringify(
{
result,
flag: global.flag, // Inspect which flag was used when submitting
},
null,
2
)
);
cssβ
You can inject css into any prompt to override styles
div css exampleβ
await div({
html: md(`# Hello World
<p style="color: red;">This is a note</p>
`),
css: `
body{
background-color: white !important;
}
h1{
color: blue !important;
}
`,
});
onTabβ
onTab allows you to build a menu where prompts are organized under a tab. Press Tab/Shift+Tab to navigate between prompts.
onTab exampleβ
onTab("People", async (event) => {
await arg("Select a person", ["John", "Mindy", "Ben"]);
});
onTab("Animals", async (event) => {
await arg("Select an animal", ["Dog", "Cat", "Bird"]);
});
openActionsβ
Manually open the actions menu
Alertsβ
beepβ
Beep the system speaker:
beep exampleβ
await beep()
sayβ
Say something using the built-in text-to-speech:
say exampleβ
await say("Done!")
setStatusβ
Set the system menu bar icon and message.
Each status message will be appended to a list.
Clicking on the menu will display the list of messages.
The status and messages will be dismissed once the tray closes, so use log
if you want to persist messages.
setStatus exampleβ
await setStatus({
message: "Working on it...",
status: "busy",
})
menuβ
Set the system menu to a custom message/emoji with a list of scripts to run.
menu exampleβ
// Set the menu to a custom message/emoji with a list of scripts to run
await menu(`π`, ["my-script", "another-script"])
menu reset exampleβ
// Reset the menu to the default icon and scripts by passing an empty string
await menu(``)
notifyβ
Send a system notification
Note: osx notifications require permissions for "Terminal Notifier" in the system preferences. Due to the complicated nature of configuring notifications, please use a search engine to find the latest instructions for your osx version. In the Script Kit menu bar icon: "Permissions -> Request Notification Permissions" might help.
notify exampleβ
await notify("Attention!")
notify example bodyβ
await notify({
title: "Title text goes here",
body: "Body text goes here",
});
Systemβ
setSelectedTextβ
Paste text into the focused app. Literally triggers a "cmd/ctrl+v", so expect a similar behavior.
setSelectedText exampleβ
await setSelectedText("Hello from Script Kit!");
getSelectedTextβ
Grab text from the focused app. Literally triggers a "cmd?ctrl+c", so expect a similar behavior.
clipboardβ
Read and write to the system clipboard
clipboard exampleβ
// Write and read text to the clipboard
await clipboard.writeText("Hello from Script Kit!");
const result = await clipboard.readText();
await editor(result);
clipboard example imageβ
const iconPath = kitPath("images", "icon.png");
const imageBuffer = await readFile(iconPath);
// Write and read image buffers to the clipboard
await clipboard.writeImage(imageBuffer);
const resultBuffer = await clipboard.readImage();
const outputPath = home("Downloads", "icon-copy.png");
await writeFile(outputPath, resultBuffer);
await revealFile(outputPath);
copyβ
Copy a string to the clipboard. A simple alias for "clipboard.writeText()"
pasteβ
Grab a string from the clipboard into the script. A simple alias for "clipboard.readText()"
Note: This is often confused with
setSelectedText
which pastes a string where your text cursor is.
mouseβ
Note: Please use with caution
move and click the system mouse
mouse exampleβ
await mouse.move([
{ x: 100, y: 100 },
{ x: 200, y: 200 },
]);
await mouse.leftClick();
await wait(100);
await mouse.rightClick();
await wait(100);
await mouse.setPosition({ x: 1000, y: 1000 });
keyboardβ
Note: Please use with caution
Type and/or tap keys on your keyboard
keyboard exampleβ
prompt: false, // 99% of the time you'll want to hide the prompt
};
await keyboard.type("Hello, world!");
keyboard example keysβ
prompt: false,
};
await keyboard.tap(Key.LeftSuper, Key.A);
await wait(100);
await keyboard.tap(Key.LeftSuper, Key.C);
await wait(100);
await keyboard.tap(Key.LeftSuper, Key.N);
await wait(100);
await keyboard.tap(Key.LeftSuper, Key.V);
Widgetβ
widgetβ
A widget
creates a new window using HTML. The HTML can be styled via Tailwind CSS class names.
Templating and interactivity can be added via petite-vue.
- The first argument is a string of HTML to render in the window.
- Optional: the second argument is "Browser Window Options"
widget exampleβ
await widget(`<h1 class="p-4 text-4xl">Hello World!</h1>`)
widget clockβ
let clock = await widget(`<h1 class="text-7xl p-5 whitespace-nowrap">{{date}}</h1>`, {
transparent: true,
draggable: true,
hasShadow: false,
alwaysOnTop: true,
})
setInterval(()=> {
clock.setState({
date: new Date().toLocaleTimeString()
})
}, 1000)
widget eventsβ
let text = ""
let count = 0
let w = await widget(`
<div class="p-5">
<h1>Widget Events</h1>
<input autofocus type="text" class="border dark:bg-black"/>
<button id="myButton" class="border px-2 py-1">+</button>
<span>{{count}}</span>
</div>
`)
w.onClick((event) => {
if (event.targetId === "myButton") {
w.setState({count: count++})
}
})
w.onClose(async () => {
await widget(`
<div class="p-5">
<h1>You closed the other widget</h1>
<p>${text}</p>
</div>
`)
})
w.onInput((event) => {
text = event.value
})
w.onMoved(({ x, y}) => {
// e.g., save position
})
w.onResized(({ width, height }) => {
// e.g., save size
})
Viteβ
viteβ
A vite
generates a vite project and opens it in its own window.
- The first argument is the name of the folder you want generated in ~/.kenv/vite/your-folder
- Optional: the second argument is "Browser Window Options"
vite exampleβ
const { workArea } = await getActiveScreen();
// Generates/opens a vite project in ~/.kenv/vite/project-path
const viteWidget = await vite("project-path", {
x: workArea.x + 100,
y: workArea.y + 100,
width: 640,
height: 480,
});
// In your ~/.kenv/vite/project-path/src/App.tsx (if you picked React)
// use the "send" api to send messages. "send" is injected on the window object
// <input type="text" onInput={(e) => send("input", e.target.value)} />
const filePath = home("vite-example.txt");
viteWidget.on(
"input",
debounce(async (input) => {
await writeFile(filePath, input);
}, 1000)
);
Commandsβ
execβ
exec
uses allows you to run shell commands within your script:
Note: Execa is an alias for
execaCommand
from theexeca
npm package with "shell" and "all" true by default.
exec exampleβ
let result = await exec(`ls -la`, {
cwd: home(), // where to run the command
shell: "/bin/zsh", // if you're expecting to use specific shell features/configs
all: true, // pipe both stdout and stderr to "all"
})
inspect(result.all)
exec with prompt infoβ
// It's extremely common to show the user what's happening while your command is running. This is often done by using `div` with `onInit` + `sumbit`:
let result = await div({
html: md(`# Loading your home directory`),
onInit: async () => {
let result = await exec(`sleep 2 && ls -la`, {
cwd: home(), // where to run the command
shell: "/bin/zsh", // use if you're expecting the command to load in your .zshrc
all: true, // pipe both stdout and stderr to "all"
})
submit(result.all)
},
})
Pro APIsβ
termβ
Opens a built-in Terminal window.
- Can run interactive commands
- Supports custom working directory and shell
term exampleβ
await term(`cd ~/.kenv/scripts && ls`)
term with commandβ
await term(`cd ~/.kenv/scripts && ls`)
showLogWindowβ
Opens a logs window to display script output.
- Displays output from all scripts run in the current session
showLogWindow exampleβ
await showLogWindow()
Platform APIsβ
scatterWindowsβ
Evenly spaces out all open windows across the screen in a neat grid.
- Only tested on macOS.
- May require accessibility permissions if it's moving windows across multiple monitors.
scatterWindows exampleβ
await scatterWindows()
focusKitWindowβ
Brings the Script Kit window into focus.
- Only tested on macOS.
- May require accessibility permissions.
focusKitWindow exampleβ
await focusKitWindow()
attemptScriptFocusβ
Attempts to bring the Script Kit window into focus.
- Only tested on macOS.
- May require accessibility permissions.
attemptScriptFocus exampleβ
await attemptScriptFocus()
getKitWindowsβ
Retrieves the Script Kit window objects.
- Only tested on macOS.
- May require accessibility permissions.
getKitWindows exampleβ
let windows = await getKitWindows()
focusWindowβ
Brings a specific window into focus.
- Only tested on macOS.
- May require accessibility permissions.
focusWindow exampleβ
await focusWindow(12345)
focusAppWindowβ
Brings a specific application window into focus.
- Only tested on macOS.
- May require accessibility permissions.
focusAppWindow exampleβ
await focusAppWindow("Google Chrome", "Script Kit - Google Chrome")
setWindowPositionβ
Sets the position of a specific window.
- Only tested on macOS.
- May require accessibility permissions.
setWindowPosition exampleβ
await setWindowPosition(12345, 100, 200)
setWindowPositionByIndexβ
Sets the position of a window based on its index.
- Only tested on macOS.
- May require accessibility permissions.
setWindowPositionByIndex exampleβ
await setWindowPositionByIndex(0, 100, 200)
scatterWindowsβ
Evenly spaces out all open windows across the screen in a neat grid.
- Only tested on macOS.
- May require accessibility permissions if it's moving windows across multiple monitors.
scatterWindows exampleβ
await scatterWindows()
organizeWindowsβ
Organizes windows in a specific way.
- Only tested on macOS.
- May require accessibility permissions.
organizeWindows exampleβ
await organizeWindows({
direction?: "horizontal" | "vertical",
padding?: number,
...
}): Promise<string>
tileWindowβ
Tiles a specific window.
- Only tested on macOS.
- May require accessibility permissions.
tileWindow exampleβ
await tileWindow(12345, {
direction: "horizontal",
padding: 10
})
scrapeSelectorβ
Scrapes a webpage using a CSS selector.
scrapeSelector exampleβ
let text = await scrapeSelector("https://example.com", "#main-content")
scrapeAttributeβ
Scrapes a webpage and extracts an attribute value.
scrapeAttribute exampleβ
let src = await scrapeAttribute("https://example.com", "img", "src")
getScreenshotFromWebpageβ
Captures a screenshot of a webpage.
getScreenshotFromWebpage exampleβ
let buffer = await getScreenshotFromWebpage("https://example.com", {
width?: number,
height?: number,
...
}): Promise<Buffer>
getWebpageAsPdfβ
Converts a webpage to a PDF.
getWebpageAsPdf exampleβ
let buffer = await getWebpageAsPdf("https://example.com", {
width: 800,
height: 600
})
applescriptβ
Executes an applescript string
- Only tested on macOS
- May require additional permissions or configurations
applescript exampleβ
let result = await applescript(`
tell application "Finder"
return name of every disk
end tell
`)
lockβ
Locks the screen.
- Only tested on macOS
- May require additional permissions or configurations
lock exampleβ
await lock()
logoutβ
Logs out the current user.
- Only tested on macOS
- May require additional permissions or configurations
logout exampleβ
await logout()
shutdownβ
Shuts down the computer.
- Only tested on macOS
- May require additional permissions or configurations
shutdown exampleβ
await shutdown()
shutdownβ
Shuts down the computer.
- Only tested on macOS
- May require additional permissions or configurations
shutdown exampleβ
await shutdown()
sleepβ
Puts the computer to sleep.
- Only tested on macOS
- May require additional permissions or configurations
sleep exampleβ
await sleep()
sleepβ
Puts the computer to sleep.
- Only tested on macOS
- May require additional permissions or configurations
sleep exampleβ
await sleep()
sleepβ
Puts the computer to sleep.
- Only tested on macOS
- May require additional permissions or configurations
sleep exampleβ
await sleep()
fileSearchβ
Searches for files on the filesystem.
- Only tested on macOS
- May require additional permissions or configurations
fileSearch exampleβ
async function fileSearch(query: string, options?: {
onlyin?: string,
...
}): Promise<string[]>
copyPathAsImageβ
Copies a file path as an image to the clipboard.
- Only tested on macOS
- May require additional permissions or configurations
copyPathAsImage exampleβ
await copyPathAsImage("/path/to/file.txt")
copyPathAsImageβ
Copies a file path as an image to the clipboard.
- Only tested on macOS
- May require additional permissions or configurations
copyPathAsImage exampleβ
await copyPathAsImage("/path/to/file.txt")
copyPathAsImageβ
Copies a file path as an image to the clipboard.
- Only tested on macOS
- May require additional permissions or configurations
copyPathAsImage exampleβ
await copyPathAsImage("/path/to/file.txt")
getWindowsβ
Retrieves information about open windows.
- Only tested on macOS
- May require additional permissions or configurations
getWindows exampleβ
let windows = await getWindows()
getWindowsBoundsβ
Retrieves the bounds of open windows.
- Only tested on macOS
- May require additional permissions or configurations
getWindowsBounds exampleβ
let bounds = await getWindowsBounds()
Utilsβ
editβ
Open a file using the KIT_EDITOR env variable
(For example, set KIT_EDITOR=/usr/local/bin/cursor)
edit exampleβ
const zshrcPath = home(".zshrc");
await edit(zshrcPath);
runβ
Run another script from the same kenv
run exampleβ
// Assuming you have a "hello-world.ts" script next to this file
await run("hello-world");
run example argβ
// Assuming the hello-world script has an: await arg("Enter your name")
await run("hello-world", "John");
homeβ
Create a path relative to the user's home directory
home exampleβ
const downloadsPath = home("Downloads");
const downloadedFileNames = await readdir(downloadsPath);
await editor(JSON.stringify(downloadedFileNames, null, 2));
getβ
An alias for axios.get
get exampleβ
const result = await get("https://jsonplaceholder.typicode.com/todos/1");
await editor(JSON.stringify(result.data));
get active app on macβ
// MAC ONLY!
// Always hide immmediately if you're not going to show a prompt
await hide()
// but you can import that package directly (or another similar package) if you prefer
let info = await getActiveAppInfo()
if (info.bundleIdentifier === "com.google.Chrome") {
await keyboard.pressKey(Key.LeftSuper, Key.T)
await keyboard.releaseKey(Key.LeftSuper, Key.T)
}
postβ
An alias for axios.post
post exampleβ
const result = await post("https://jsonplaceholder.typicode.com/posts", {
title: "foo",
body: "bar",
userId: 1,
});
await editor(JSON.stringify(result.data));
putβ
An alias for axios.put
put exampleβ
const result = await put("https://jsonplaceholder.typicode.com/posts/1", {
title: "foo",
});
await editor(JSON.stringify(result.data));
patchβ
An alias for axios.patch
patch exampleβ
const result = await patch("https://jsonplaceholder.typicode.com/posts/1", {
title: "foo",
});
await editor(JSON.stringify(result.data));
delβ
An alias for axios.delete
del exampleβ
const result = await del("https://jsonplaceholder.typicode.com/posts/1");
await editor(JSON.stringify(result.data));
downloadβ
Download a file from a URL
download exampleβ
const url = "https://github.com/johnlindquist/kit/archive/refs/heads/main.zip";
const destination = home("Downloads");
await download(url, destination);
replaceβ
Replace a string or regex in one or more files
replace exampleβ
const mdPath = kenvPath("sticky.md");
await replace({
files: [mdPath],
from: /nice/g, // replace all instances of "nice"
to: "great",
});
mdβ
Convert markdown to HTML for rendering in prompts
md exampleβ
const html = md(`# You're the Best
* Thanks for using Script Kit!
`);
await div(html);
compileβ
Create a handlebars template compiler
compile exampleβ
const compiler = compile(`
Hello {{name}}
Have a {{mood}} day!
{{#if from}}
From {{author}}
{{/if}}
`);
const result = compiler({
name: "John",
mood: "great",
author: "Script Kit",
from: true,
});
await div(result);
uuidβ
Generate a UUID
uuid exampleβ
const id = uuid();
await editor(id);
globbyβ
Glob a list of files
globby exampleβ
const kenvScripts = kenvPath("scripts", "*.ts");
const kenvScriptlets = kenvPath("scriptlets", "*.md");
const pathsForScriptsAndScriptlets = await globby([
kenvScripts,
kenvScriptlets,
]);
await editor(JSON.stringify(pathsForScriptsAndScriptlets, null, 2));
isFileβ
Check if a path is a file
isFile exampleβ
const testingIsFileTxtPath = home("testing-isFile.txt");
const isTestingFile = await isFile(testingIsFileTxtPath);
if (!isTestingFile) {
await writeFile(testingIsFileTxtPath, "Hello World");
}
const content = await readFile(testingIsFileTxtPath, "utf8");
await editor(content);
isDirβ
Check if a path is a directory
isBinβ
Check if a path can be executed
browseβ
Open a URL in the default browser.
browse exampleβ
// When executing a command without UI, "hide" allows you to instantly hide the UI rather than waiting for the command to finish
await hide();
await browse("https://scriptkit.com");
formatDateβ
Formats a date
trashβ
Moves files or directories to the trash.
- Only tested on macOS
- May require additional permissions or configurations
trash exampleβ
await trash("/path/to/file.txt")
gitβ
Git utility functions.
- Only tested on macOS
- May require additional permissions or configurations
git exampleβ
await git.clone("https://github.com/user/repo.git", "/path/to/repo")
degitβ
Clones a GitHub repository using degit.
- Only tested on macOS
- May require additional permissions or configurations
degit exampleβ
await degit("https://github.com/user/repo.git", "/path/to/repo")
openAppβ
Opens an application.
- Only tested on macOS
- May require additional permissions or configurations
openApp exampleβ
await openApp("Google Chrome")
createGistβ
Creates a GitHub gist.
- Only tested on macOS
- May require additional permissions or configurations
createGist exampleβ
let gistUrl = await createGist({
description: "My awesome gist",
public: true,
files: {
"hello.txt": {
content: "Hello, world!"
}
}
})
npmβ
Deprecated: Use standard
import
instead.
Installs an npm package.
- Only tested on macOS
- May require additional permissions or configurations
npm exampleβ
await npm("lodash")
attemptImportβ
Attempts to import a module.
attemptImport exampleβ
let module = await attemptImport("lodash")
silentAttemptImportβ
Attempts to import a module silently.
- Only tested on macOS
- May require additional permissions or configurations
silentAttemptImport exampleβ
let module = await silentAttemptImport("lodash")
storeβ
Stores data in a persistent key-value store.
- Only tested on macOS
- May require additional permissions or configurations
store exampleβ
await store.set("myKey", "myValue")
let value = await store.get("myKey")
dbβ
An extremely simple database that persists to a file.
db hello worldβ
// Pre-populate the database with some items
const peopleDb = await db({
people: [
{
name: "John",
age: 30,
city: "San Francisco",
},
{
name: "Jane",
age: 25,
city: "New York",
},
] as Person[],
});
const person = await arg<Person>("Select a person", peopleDb.people);
// Do something with the person...
const [name, age, city] = await fields({
fields: ["name", "age", "city"],
enter: "Add",
description: "Add a new person to the database",
});
peopleDb.people.push({ name, age: parseInt(age), city });
await peopleDb.write();
await editor(JSON.stringify(peopleDb.people, null, 2));
type Person = {
name: string;
age: number;
city: string;
};
db populateβ
// Pass in a function to generate data for the db
// Because this script is named "db-basic.js"
// The database is found at "~/.kenv/db/_db-basic.json"
let reposDb = await db(async () => {
let response = await get("https://api.github.com/users/johnlindquist/repos");
return response.data.map(({ name, description, html_url }) => {
return {
name,
description,
value: html_url,
};
});
});
let repoUrl = await arg("Select repo to open:", reposDb.items);
exec(`open "${repoUrl}"`);
db storeβ
let fruitDb = await db(["apple", "banana", "orange"])
while (true) {
let fruitToAdd = await arg("Add a fruit", md(fruitDb.items.map(fruit => `* ${fruit}`).join("\n")))
fruitDb.items.push(fruitToAdd)
await fruitDb.write()
let fruitToDelete = await arg("Delete a fruit", fruitDb.items)
fruitDb.items = fruitDb.items.filter(fruit => fruit !== fruitToDelete)
await fruitDb.write()
}
memoryMapβ
Manages a memory map of objects.
memoryMap exampleβ
memoryMap.set("myKey", { myObject: true })
let value = memoryMap.get("myKey")
showβ
Shows the main prompt.
show exampleβ
await show()
hideβ
Hides the main prompt.
hide exampleβ
await hide()
blurβ
Returns focus to the previous app.
blur exampleβ
import { URL, fileURLToPath } from "node:url";
await editor({
onInit: async () => {
const { workArea } = await getActiveScreen();
const topLeft = { x: workArea.x, y: workArea.y };
const size = { height: 900, width: 200 };
await setBounds({
...topLeft,
...size,
});
await blur();
// get path to current file
const currentScript = fileURLToPath(new URL(import.meta.url));
const content = await readFile(currentScript, "utf8");
const lines = content.split("\n");
for await (const line of lines) {
editor.append(`${line}\n`);
await wait(100);
}
},
});
setPanelβ
Sets the panel content.
setPanel exampleβ
await setPanel("<h1>Hello, world!</h1>")
setPromptβ
Sets the prompt content.
setPrompt exampleβ
await setPrompt("<h1>Enter your name:</h1>")
setPreviewβ
Sets the preview content.
setPreview exampleβ
await setPreview("<h1>Preview</h1>")
setIgnoreBlurβ
Sets whether to ignore blur events.
setIgnoreBlur exampleβ
await setIgnoreBlur(true)
getClipboardHistoryβ
Gets the clipboard history from the in-memory clipboard
getClipboardHistory exampleβ
const history = await getClipboardHistory();
const text = await arg("Select from clipboard history", history);
await editor(text);
removeClipboardItemβ
Removes an item from the clipboard.
removeClipboardItem exampleβ
await removeClipboardItem(item)
clearClipboardHistoryβ
Clears the clipboard history.
clearClipboardHistory exampleβ
await clearClipboardHistory()
setScoredChoicesβ
Sets scored choices for a prompt.
setScoredChoices exampleβ
await setScoredChoices([
{ name: "John", score: 0.9 },
{ name: "Mindy", score: 0.8 },
{ name: "Joy", score: 0.7 }
])
setSelectedChoicesβ
Sets selected choices for a prompt.
setSelectedChoices exampleβ
await setSelectedChoices(["John", "Mindy"])
groupChoicesβ
Groups choices for a prompt.
groupChoices exampleβ
await groupChoices([
{ name: "Group 1", choices: ["John", "Mindy"] },
{ name: "Group 2", choices: ["Joy"] }
])
preloadβ
Preloads data for a prompt.
preload exampleβ
await preload({
name: "John",
age: 40
})
selectβ
Prompts the user to select one or more options.
select exampleβ
// Return an array of selected items
const multipleChoice = await select("Select one or more developer", [
"John",
"Nghia",
"Mindy",
"Joy",
]);
await editor(JSON.stringify(multipleChoice, null, 2));
select a choice with a single keystrokeβ
let choice = await arg({
placeholder: "Choose a color",
choices: [
{ name: "[R]ed", value: "red" },
{ name: "[G]reen", value: "green" },
{ name: "[B]lue", value: "blue" },
],
})
await div(md(`You chose ${choice}`))
select array objectβ
const people = [
{
name: "John",
description: "Full-stack Dev",
value: "John",
},
{
name: "Nghia",
description: "Full-stackoverflow dev",
value: "Nghia",
},
{
name: "Mindy",
description: "Business Analyst",
value: "Mindy",
},
{
name: "Joy",
description: "Leader",
value: "Joy",
},
]
let multipleChoice = await select(
"Select one or more developer",
people
)
select async choices array objectβ
let name = await select(
"GET: NAME (please wait)",
async () => {
let response = await get(
"https://swapi.dev/api/people/"
)
return response?.data?.results.map(person => {
return {
name: person.name,
description: `height: ${person.height}, mass: ${person.mass}`,
value: person,
preview: () => JSON.stringify(person),
}
})
}
)
select basic array inputβ
let multipleChoice = await select(
"Select one or more developer",
["John", "Nghia", "Mindy", "Joy"]
)
select generated input choicesβ
let word = await select("Type then pick a words", input => {
return input.trim().split(new RegExp("[.,;/-_\n]", "g"))
})
gridβ
Prompts the user to select one or more options in a grid layout.
grid exampleβ
let multipleChoice = await grid(
"Select one or more developer",
["John", "Nghia", "Mindy", "Joy"]
)
getMediaDevicesβ
Retrieves available media devices.
getMediaDevices exampleβ
let devices = await getMediaDevices()
getTypedTextβ
Retrieves typed text from the user.
getTypedText exampleβ
let text = await getTypedText()
toastβ
Displays a small pop-up notification inside the Script Kit window.
toast exampleβ
await toast("Hello from Script Kit!", {
autoClose: 3000, // close after 3 seconds
pauseOnFocusLoss: false
})
submitβ
Forcefully submit a value from an open prompt
submit exampleβ
const result = await arg(
{
placeholder: "Pick one in under 3 seconds or I'll pick one for you",
onInit: async () => {
await wait(3000);
submit("broccoli"); //forces a submission
},
},
["cookie", "donut"]
);
// Wait for 1 second
await editor(result);
preventSubmitβ
A symbol used to block submitting a prompt
preventSubmit exampleβ
await arg({
placeholder: "Try to submit text less than 10 characters",
onSubmit: async (input) => {
if (input.length < 10) {
setHint(
"Text must be at least 10 characters. You entered " + input.length
);
setEnter("Try Again");
return preventSubmit;
}
},
});
waitβ
Wait for a number of milliseconds
wait exampleβ
div(md(`Enjoying your wait?`));
await wait(1000);
div(md(`I waited 1 second. Let's wait some more!`));
await wait(1000);
await div(md(`All done!`));
exitβ
Exit the script completely
exit exampleβ
// Prevent the script from running for more than 1 second
setTimeout(() => {
exit();
}, 1000);
await arg("I will exit in 1 second");
SDK Utilsβ
kitPathβ
Create a path relative to the kit directory.
kenvPathβ
Create a path relative to the "kenv" (kit environment) directory
kenvPath exampleβ
const scriptsPath = kenvPath("scripts");
const scripts = await readdir(scriptsPath);
await editor(JSON.stringify(scripts, null, 2));
tmpPathβ
Create a path relative to a "kit" directory in the system temp directory
Note: The tmp directory is symlinked to the ~/.kenv/tmp directory for easy access
tmpPath exampleβ
const tmpTestTxtPath = tmpPath("test.txt");
const content = await ensureReadFile(tmpTestTxtPath, "Hello World");
await editor(
JSON.stringify(
{
tmpTestTxtPath,
content,
},
null,
2
)
);
getScriptsβ
Get all scripts
getScripts exampleβ
// Get all scripts from ~/.kit/db/scripts.json
const scripts = await getScripts();
const script = await arg("Select a script", scripts);
await editor(JSON.stringify(script, null, 2));
selectScriptβ
Allows you to build a custom script selection menu
selectScript exampleβ
import type { Script } from "@johnlindquist/kit";
const script = await selectScript(
"Select a Shortcut Script to Edit",
true, // "true" will load from ~/.kit/db/scripts.json cache
(scripts: Script[]) => scripts.filter((script) => script.shortcut)
);
await edit(script.filePath);
Closing Thoughtsβ
Alternate Importingβ
Also, you can import kit
and access the APIs like so:
import kit from "@johnlindquist/kit"
await kit.arg("Enter your name")
Advanced Usage:
For more advanced scenarios, you can import the ai-sdk directly. Using Script Kit's helpers is not requiredβif you need features not provided by these helpers, feel free to use the ai-sdk, any other SDK, or any npm library directly in your scripts.