Agent Skills: New Script

Create or update TypeScript scripts for Script Kit. Use when the user wants to write a new script, edit an existing script, or understand Script Kit's SDK and metadata system.

UncategorizedID: johnlindquist/script-kit-next/new-script

Install this agent skill to your local

pnpm dlx add-skill https://github.com/johnlindquist/script-kit-next/tree/HEAD/kit-init/skills/new-script

Skill Files

Browse the full folder contents for new-script.

Download Skill

Loading file tree…

kit-init/skills/new-script/SKILL.md

Skill Metadata

Name
new-script
Description
Create or update TypeScript scripts for Script Kit. Use when the user wants to write a new script, edit an existing script, or understand Script Kit's SDK and metadata system.

New Script

Create and manage TypeScript scripts for Script Kit.

Where Scripts Live

~/.scriptkit/plugins/main/scripts/*.ts

Scripts are automatically discovered by Script Kit when saved to this directory.

Creating a New Script

  1. Create a .ts file in ~/.scriptkit/plugins/main/scripts/
  2. Add the SDK import and metadata export
  3. Save — Script Kit detects it immediately

Minimal Template

import "@scriptkit/sdk";

export const metadata = {
  name: "My Script",
  description: "What this script does",
};

// Your code here

With Global Hotkey

import "@scriptkit/sdk";

export const metadata = {
  name: "Quick Capture",
  description: "Capture a quick note",
  shortcut: "cmd shift n",
};

const note = await arg("Quick note:");
const filePath = home("notes", `${Date.now()}.txt`);
await Bun.write(filePath, note);
hud("Note saved!");

With Search Alias

import "@scriptkit/sdk";

export const metadata = {
  name: "Open Project",
  description: "Open a project in VS Code",
  alias: "op",
};

const projects = await $`ls ~/projects`.text();
const project = await arg(
  "Which project?",
  projects.trim().split("\n"),
);
await $`code ~/projects/${project}`;

Script Patterns

Prompt → Transform → Output

import "@scriptkit/sdk";

export const metadata = {
  name: "JSON Formatter",
  description: "Format JSON from clipboard",
};

const raw = await paste();
try {
  const formatted = JSON.stringify(JSON.parse(raw), null, 2);
  await copy(formatted);
  hud("JSON formatted and copied!");
} catch {
  await div(`<div class="p-4 text-red-400">Invalid JSON in clipboard</div>`);
}

Dynamic Choices with Preview

import "@scriptkit/sdk";

export const metadata = {
  name: "File Browser",
  description: "Browse and open files",
};

const documentsDir = home("Documents");
const files = await $`find ${documentsDir} -maxdepth 2 -type f -name "*.md"`.text();

const file = await arg(
  "Open file",
  files.trim().split("\n").map((f) => ({
    name: f.split("/").pop() || f,
    description: f,
    value: f,
    preview: `<pre class="p-4 text-sm">${f}</pre>`,
  })),
);

await open(file);

Multi-Step Workflow

import "@scriptkit/sdk";

export const metadata = {
  name: "New Blog Post",
  description: "Scaffold a new blog post",
};

const [title, category] = await fields([
  { name: "title", label: "Post Title" },
  { name: "category", label: "Category" },
]);

const slug = title.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
const date = new Date().toISOString().split("T")[0];
const content = `---
title: ${title}
date: ${date}
category: ${category}
---

# ${title}

Write your post here.
`;

const filePath = home("blog", "posts", `${slug}.md`);
await Bun.write(filePath, content);
hud(`Created: ${filePath}`);

Verification

When you create or edit a script from the Tab AI harness, writing the file is not enough. You must verify the actual script inside the current Claude Code terminal session before you report success.

Required loop for every script:

  1. Save the script to ~/.scriptkit/plugins/main/scripts/<name>.ts
  2. If the script normally uses UI or typed input (arg, div, editor, fields, etc.), add a non-interactive smoke path behind process.env.SK_VERIFY === "1"
  3. Syntax-check / transpile it with Bun:
    bun build ~/.scriptkit/plugins/main/scripts/<name>.ts --target=bun --outfile ~/.scriptkit/tmp/test-scripts/<name>.verify.mjs
    
  4. Execute it with Bun:
    SK_VERIFY=1 bun ~/.scriptkit/plugins/main/scripts/<name>.ts
    
  5. Confirm the stdout, written file, or other observable result matches the request
  6. If either command fails, fix the script and rerun both commands
  7. Never report success until both commands pass and the observed behavior is correct
  8. Only after both commands pass, end your final response with exactly:
    SCRIPT_READY path=~/.scriptkit/plugins/main/scripts/<name>.ts validated=true
    
    Do not emit SCRIPT_READY before both commands pass.

Verification-Friendly Pattern

Use this when the real script flow is interactive but the harness still needs a terminal-only execution path:

import "@scriptkit/sdk";

export const metadata = {
  name: "My Script",
  description: "What it does",
};

const isVerify = process.env.SK_VERIFY === "1";

const value = isVerify
  ? "verification input"
  : await arg("What should this script do?");

const output = `Result: ${value}`;

if (isVerify) {
  console.log(JSON.stringify({ ok: true, output }));
} else {
  await div(`<div class="p-8 text-2xl">${output}</div>`);
}

For UI-heavy requests, the Bun gate is still mandatory. If you also want to manually open Script Kit afterward, do that after the Bun gate — not instead of it.

Prompt Sequencing

Script Kit prompt APIs are stateful interactive surfaces. Never call them concurrently.

  • Do not use Promise.all, Promise.race, Promise.any, or Promise.allSettled with arg, fields, editor, div, form, drop, find, path, textarea, select, or grid
  • Multi-step prompt flows must be sequential

Wrong:

const [url1, url2, url3] = await Promise.all([
  arg("URL 1"),
  arg("URL 2"),
  arg("URL 3"),
]);

Right:

const url1 = await arg("URL 1");
const url2 = await arg("URL 2");
const url3 = await arg("URL 3");

Path Safety

  • Prefer home(...) for user-relative paths such as Documents, Downloads, and .scriptkit
  • Use home(".scriptkit", "kit", "main", ...) when you need the Script Kit workspace explicitly
  • Do not build user paths from env.HOME; it may be unset in generated scripts and can produce broken paths like undefined/...

Sample Input and Expected Output

  • Sample file: ~/.scriptkit/plugins/main/scripts/hello-world.ts
  • Sample command 1:
    bun build ~/.scriptkit/plugins/main/scripts/hello-world.ts --target=bun --outfile ~/.scriptkit/tmp/test-scripts/hello-world.verify.mjs
    
    Expected result: exit code 0; file ~/.scriptkit/tmp/test-scripts/hello-world.verify.mjs exists.
  • Sample command 2:
    SK_VERIFY=1 bun ~/.scriptkit/plugins/main/scripts/hello-world.ts
    
    Expected stdout: {"ok":true,"greeting":"Hello, verification!"}

Common Mistakes

  • Missing SDK import: Always start with import "@scriptkit/sdk";
  • CommonJS imports: Use ES import syntax, never CommonJS
  • Comment metadata: Use export const metadata = {...}, not comment-based metadata
  • Node.js APIs: Use Bun.file() / Bun.write() / $`cmd` instead of fs / child_process
  • Wrong directory: Scripts must be in plugins/main/scripts/, not scripts/ or elsewhere
  • Unsafe home paths: Prefer home(...) over env.HOME for user-relative locations

Related Skills

  • new-scriptlet — package scripts as scriptlet bundles
  • start-chat — programmatic Agent Chat workflows using the chat SDK
  • add-actions — expose script helpers through the Actions Menu
  • manage-notes — automate the Notes window from scripts
  • new-agent — mdflow-backed agent files (compatibility path)