Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Getting Started

Flo script tools run inside the Flo runtime and import their helpers from flo:runtime:

import * as flo from "flo:runtime";

The checked-in type declarations live in flo.d.ts. They are the source of truth for the public TypeScript surface documented in this book.

Local Authoring Loop

  1. Write or update a tool manifest such as my_tool.tool.yaml.
  2. Write the script inline with execution.script or in a file with execution.script_file.
  3. If you need bundle-wide execution guidance, add a single *.prompts.yaml file such as runtime.prompts.yaml.
  4. Use flo.d.ts for editor autocomplete and type checking.
  5. Use the repo-root preload shim for local Node-based smoke tests:
node --import=./flo_hooks.mts path/to/script.mts

The preload shim can invoke an exported __flo_main__() for local testing. It is a development aid, not a full runtime replica.

Bundle-level prompt manifest example:

execution:
  preamble: |
    You are a slot-scoped runtime.
    Keep responses concrete and concise.

For a full local testing workflow, see Testing With Node.js.

Excluding Files From Skill Bundles

Create .floignore in the skill repository root or directly inside the directory being packed (SKILLS_DIR). The push script combines both files when present. Patterns refer to paths inside SKILLS_DIR, so omit the skills/ prefix even when .floignore is at the repository root.

# Local dependencies and test fixtures
node_modules/
*.tmp
example/tests/
private notes/

Each nonblank line is passed as a tar --exclude glob. Leading and trailing whitespace is trimmed; lines whose first non-whitespace character is # are comments. CRLF files and a final line without a newline are supported.

Use *, ?, and character classes such as [ab] for glob matching. These are tar exclusion patterns, not full Git ignore rules: patterns without slashes match names at any depth, and * can match across directory separators. A trailing / is optional for directory exclusions; excluding a directory excludes its entire subtree. Negation (!) and inline comments are not supported. Do not quote patterns; spaces inside a pattern are literal.

The push script omits .floignore itself and continues to omit ._*, .DS_Store, and __MACOSX. Without .floignore, packing otherwise works as before. Exclusions apply only during packing; local runtime discovery is unchanged. Keep referenced manifests, instructions, scripts, and assets in the bundle, or backend validation may reject it.

Runtime Constraints You Should Know Early

  • Runtime script source can be JavaScript or TypeScript.
  • TypeScript is transpiled at runtime, but it is not type checked there.
  • Static relative imports are supported.
  • Bare imports, package-style imports, and dynamic import() are rejected.
  • Some APIs are still runtime-bound and intentionally fail in the local shim, including flo.callTool(...) and child-task orchestration. The shim does support flo.state.* when FLO_MOCKS_FILE includes matching state_bindings.
  • Browser helpers require FLO_LOCAL_BROWSER=1 in the local shim.

Simple Script Example

import * as flo from "flo:runtime";

export async function run(input: { name?: string }) {
  await flo.task.emitEvent({
    event_type: "hello.started",
    title: "Greeting tool",
    message: "Preparing response",
    level: "info",
  });

  return {
    ok: true,
    greeting: `hello ${input.name ?? "world"}`,
  };
}

Next: Testing With Node.js