Scripts

@agent-native/core provides a script dispatcher and utilities for building agent-callable scripts.

Script Dispatcher

The script system lets you create scripts that agents can invoke via pnpm script <name>. Each script is a TypeScript file that exports a default async function.

// scripts/run.ts — dispatcher (one-time setup)
import { runScript } from "@agent-native/core";
runScript();
// scripts/hello.ts — example script
import { parseArgs } from "@agent-native/core";

export default async function hello(args: string[]) {
  const { name } = parseArgs(args);
  console.log(`Hello, ${name ?? "world"}!`);
}
# Run it
pnpm script hello --name Steve

parseArgs(args)

Parse CLI arguments in --key value or --key=value format:

import { parseArgs } from "@agent-native/core";

const args = parseArgs(["--name", "Steve", "--verbose", "--count=3"]);
// { name: "Steve", verbose: "true", count: "3" }

Shared Agent Chat

@agent-native/core provides an isomorphic chat bridge that works in both browser and Node.js:

import { agentChat } from "@agent-native/core";

// Auto-submit a message
agentChat.submit("Generate a report for Q4");

// Prefill without submitting
agentChat.prefill("Draft an email to...", contextData);

// Full control
agentChat.send({
  message: "Process this data",
  context: JSON.stringify(data),
  submit: true,
});

In the browser, messages are sent via window.postMessage(). In Node.js (scripts), they use the BUILDER_PARENT_MESSAGE: stdout format that the Electron host translates to postMessage.

Utility Functions

FunctionReturnsDescription
loadEnv(path?)voidLoad .env from project root (or custom path)
camelCaseArgs(args)RecordConvert kebab-case keys to camelCase
isValidPath(p)booleanValidate relative path (no traversal, no absolute)
isValidProjectPath(p)booleanValidate project slug (e.g. "my-project")
ensureDir(dir)voidmkdir -p helper
fail(message)neverPrint error to stderr and exit(1)

Database Sync Adapters

For apps that need bidirectional file sync across instances, agent-native provides adapters for Google Cloud Firestore, Supabase, and Neon (Postgres). All adapters implement the same FileSyncAdapter interface and plug into FileSync:

// Google Cloud Firestore
import { FileSync, FirestoreFileSyncAdapter } from "@agent-native/core/adapters/firestore";

const adapter = new FirestoreFileSyncAdapter(() => db.collection("files"));
const sync = new FileSync({
  appId: "my-app",
  ownerId: "owner-123",
  contentRoot: "./content",
  adapter,
});
await sync.initFileSync();
// Supabase
import { FileSync, SupabaseFileSyncAdapter } from "@agent-native/core/adapters/supabase";
import { createClient } from "@supabase/supabase-js";

const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
const adapter = new SupabaseFileSyncAdapter(supabase);
const sync = new FileSync({
  appId: "my-app",
  ownerId: "owner-123",
  contentRoot: "./content",
  adapter,
});
await sync.initFileSync();
// Neon (Postgres)
import { FileSync, NeonFileSyncAdapter } from "@agent-native/core/adapters/neon";
import { neon } from "@neondatabase/serverless";

const sql = neon(DATABASE_URL);
const adapter = new NeonFileSyncAdapter(sql, { pollIntervalMs: 2000 });
const sync = new FileSync({
  appId: "my-app",
  ownerId: "owner-123",
  contentRoot: "./content",
  adapter,
});
await sync.initFileSync();

All adapters support: startup sync, remote change listeners, chokidar file watchers, three-way merge with LCS-based conflict resolution, and .conflict sidecar files for unresolvable conflicts.

Supabase and Neon require a files table. Run this migration:

CREATE TABLE files (
  id TEXT PRIMARY KEY,
  path TEXT NOT NULL,
  content TEXT NOT NULL DEFAULT '',
  app TEXT NOT NULL,
  owner_id TEXT NOT NULL,
  last_updated BIGINT NOT NULL DEFAULT 0,
  created_at BIGINT
);
CREATE INDEX idx_files_app_owner ON files(app, owner_id);

The adapter interface (@agent-native/core/adapters/sync) is also available for building custom adapters for other databases.