agentsclimarketplace

Writing scripts

Skill celigo/ai/skills/writing-scripts

Domain knowledge and tools for building Celigo integrations with AI coding assistants.

Install
npx -y skills add celigo/ai --skill writing-scripts

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Write Celigo JavaScript hook scripts -- preSavePage, preMap, postMap, postSubmit, postResponseMap, filter, transform, branching, handleRequest. Use when creating or editing scripts, choosing the right hook point, understanding input/output data shapes, or debugging script behavior.

SKILL.md

22.4 KB, as published. Nobody here has run it

<!-- TIER:1 -->

Writing Scripts

A script is a JavaScript function that runs at a specific hook point in the Celigo data pipeline. Scripts handle logic that expressions, filters, and visual mappings cannot -- complex conditionals, cross-record calculations, API calls within the pipeline, and custom routing.

Concerns when writing a script:

  • Choosing the right hook point -- which function type matches what you're trying to accomplish
  • Input/output contracts -- what options contains and what the function must return (array length rules are strict)
  • Expression alternative -- filter, transform, and output filter have expression-based alternatives that don't require a script; prefer expressions when possible
  • Available modules -- scripts can import three built-in modules: integrator-api (call Celigo APIs), dayjs (date/time manipulation), and sjcl (Stanford JavaScript Crypto Library for hashing/encryption)
  • One script, many functions -- a single script resource can contain multiple exported functions, each wired independently to different hook points

Used across flows, APIs, and tools.

Hook Points

Every script function runs at a specific point in the pipeline. Choose based on when you need to act and what data you need access to.

Data Pipeline Hooks

HookRuns onWhenInputMust return
preSavePageExportAfter retrieval, before pipelineoptions.data[], errors[], files[], retryData{}{ data[], errors[], abort, newErrorsAndRetryData[] }
preMapImportBefore field mappingoptions.data[] (unmapped records)Array matching data.length: { data }, { errors }, or {} to skip
postMapImportAfter field mapping, before submitoptions.preMapData[], postMapData[]Array matching postMapData.length: { data }, { errors }, or {} to skip
postSubmitImportAfter destination submissionoptions.preMapData[], postMapData[], responseData[]responseData[] (same length, modified)
postAggregateImportAfter file aggregation uploadoptions.postAggregateData: { success, _json, code, message }void

Record-Level Processors (on export or import)

HookWhenInputMust return
filterPer-record, before processingoptions.recordboolean (true = process)
input_filterPer-record on lookup exportsoptions.recordboolean (true = include)
transformPer-record, reshaping before mappingoptions.recordTransformed record

filter and transform have expression-based alternatives. Only use a script when the logic is too complex for an expression (multi-field conditionals, date math, external lookups).

Flow-Level Hook

HookRuns onWhenInputMust return
postResponseMapPage processor (flow/API/tool)After response mapping merges resultsoptions.postResponseMapData[], responseData[]postResponseMapData[] (same length)

Configured on the flow's pageProcessors[] entry, not on the export/import. Plan this hook when building the resource, but wire it at the flow level.

Routing and Handlers

HookRuns onWhenInputMust return
branchingRouterPer-record routing decisionoptions.record, settingsnumber[] (branch indices, e.g., [0, 2])
handleRequestAPI resourceIncoming HTTP request (script-mode API)options.method, headers, queryString, body, rawBody{ statusCode, headers?, body }
contentBasedFlowRouterAS2 connectionEDI message routingoptions.httpHeaders, mimeHeaders, rawMessageBody{ _flowId, _exportId }

Quick Reference

Hook Point Decision Matrix

When you need to...Use hookConfigured onInput / Output
Transform or filter a batch after retrievalpreSavePageExportReceives pages of records, returns pages (with optional errors)
Filter individual records before processingfilterExport or importReceives single record, returns boolean (true = keep)
Filter records entering a lookup exportinput_filterExport (lookup)Receives single record, returns boolean (true = include)
Reshape records before mappingtransformExport or importReceives single record, returns transformed record
Transform records before field mappingpreMapImportReceives unmapped records array, returns array (same length)
Transform records after field mappingpostMapImportReceives pre-map + post-map arrays, returns array (same length)
Process API responses after submissionpostSubmitImportReceives pre-map, post-map, and response arrays, returns response array
Handle results after file aggregationpostAggregateImport (file)Receives aggregation result, returns void
Post-response processing (merge lookup/import results)postResponseMapFlow pageProcessors[] entryReceives merged records + response data, returns merged records (same length)
Route records to branchesbranchingRouter in flow/toolReceives single record + settings, returns branch indices array
Handle incoming HTTP requests (script-mode API)handleRequestAPI resourceReceives method, headers, query, body; returns { statusCode, headers?, body }
Route EDI messages to flowscontentBasedFlowRouterAS2 connectionReceives HTTP/MIME headers + raw body, returns { _flowId, _exportId }

Minimum Required Fields

A script resource needs only two fields:

  • name -- descriptive name (convention: <System> - <step> - <hookType>, e.g., "Salesforce - getBatchRecords - postResponseMap")
  • content -- the JavaScript source code as a string

See references/schemas/request.yml for the full create/update schema.

Related Skills

<!-- TIER:2 -->

Common Options Available to All Hooks

Most hooks receive these context fields in options:

  • _flowId, _integrationId, _apiId, _parentIntegrationId -- execution context IDs
  • _exportId or _importId -- the step's resource ID
  • _connectionId -- the connection in use
  • settings -- custom settings in scope for the resource
  • testMode -- boolean, whether running in test/preview mode
  • job -- the current job object

Function Point Categories

Scripts run at twelve function points, grouped into four categories. The Hook Points tables above give each one's input/output contract; this is the mental model for which kind of point you're wiring and whether a non-script alternative exists.

  • Step-level pipeline hooks (on the export or import) -- preSavePage, preMap, postMap, postSubmit, postAggregate
  • Parent-level response hook (on the flow/API/tool pageProcessors[] entry, not the step) -- postResponseMap
  • Script-mode replacements for declarative slots -- filter, input_filter, transform, branching
  • Resource-specific function points -- contentBasedFlowRouter (on an AS2 connection) and handleRequest (on a script-mode API)

Script-only points have no declarative equivalent: postSubmit, postResponseMap, postAggregate, contentBasedFlowRouter, and handleRequest. On those slots a script is the only option. The four script-mode slots (filter, input_filter, transform, branching) each hold either a declarative rule tree or a script -- never both -- so prefer the declarative path there unless the logic genuinely can't be expressed as rules (see Declarative vs Script Mode).

Declarative vs Script Mode

The four mode-switchable slots -- filter, input_filter, transform, and branching -- hold a declarative rule tree or a script reference at any one moment, not both. Because the slot's contents change, switching modes is a two-part operation.

From script mode to declarative mode (the common direction -- prototype with a script, then clean up):

  1. Clear the script from the slot. The slot reverts to declarative mode by default.
  2. Author the declarative rule for that slot (rules-engine filter, Mapper 2.0 transform, or router input-filter rule).

From declarative mode to script mode (rarer -- the rules engine couldn't express what you need):

  1. Wire a script into the slot. The declarative rules already there are replaced by the script reference automatically.

Wiring a script and clearing it are mirror operations on the same slot. Recognize the mode-swap in phrasing like "switch the filter to rules", "convert this transform back to expressions", or "use a script for this filter instead of rules".

How to Write a Script

1. Determine what you need to accomplish

Map your goal to the right hook point using the Hook Point Decision Matrix above.

2. Check if an expression can handle it

Filter, transform, and output filter all have expression-based alternatives. Expressions are simpler to maintain and don't require a script resource. Use a script only when you need:

  • Multi-step logic or loops
  • Cross-record calculations (totals, deduplication)
  • External API calls via integrator-api
  • Error handling with retry data
  • Access to preMapData alongside postMapData

3. Check for existing scripts in the account

celigo scripts list
celigo scripts get <id>   # content is only returned on individual GET

4. Create the script resource

Build the script with the correct function name matching the hook point. A single script can contain multiple functions.

See references/schemas/request.yml for the create/update schema and references/schemas/response.yml for the response shape.

Key fields:

  • name -- descriptive name (convention: <System> - <step> - <hookType>, e.g., "Salesforce - getBatchRecords - postResponseMap")
  • content -- the JavaScript source code

5. Wire the script to the resource

Wiring depends on the hook type:

HookWiring patternWhere
preSavePage, preMap, postMap, postSubmit, postAggregatehooks.{hookType}: { _scriptId, function }Export or import resource
filter, input_filter, transform{field}: { type: "script", script: { _scriptId, function } }Export or import resource
postResponseMaphooks.postResponseMap: { _scriptId, function }Flow pageProcessors[] entry
branchingrouteRecordsUsing: "script" + script referenceRouter in flow
handleRequestscript: { _scriptId, function } + type: "script"API resource
contentBasedFlowRouteras2.contentBasedFlowRouter: { _scriptId, function }AS2 connection

Hook-based attachment (preSavePage, preMap, etc.) is additive -- adding a hook doesn't remove existing config. Replace-based attachment (filter, transform) replaces the existing filter/transform expression.

6. Test and iterate

# Enable debug logging on the script
celigo scripts enable-debug <script-id>

# Run the flow or API that triggers the script
celigo flows run <flow-id> -y

# Check debug logs
celigo scripts debug-logs <script-id> --since 30

# Check execution logs
celigo scripts debug-logs <script-id> --level error --limit 20

# Disable debug when done
celigo scripts disable-debug <script-id>

Available Modules

Scripts can import three built-in modules:

integrator-api

Call Celigo APIs from within the script -- run exports, read connections, trigger imports.

import { exports, imports, connections } from 'integrator-api'

const result = exports.run({ _id: 'exportId' })
const conn = connections.get({ _id: 'connectionId' })

Useful in preSavePage for enrichment, handleRequest for orchestration, and postSubmit for triggering downstream processes.

dayjs

Date and time manipulation. Handles parsing, formatting, diffing, and timezone conversions without manual date math.

import dayjs from 'dayjs'

const formatted = dayjs(record.createdAt).format('YYYY-MM-DD')
const isRecent = dayjs().diff(dayjs(record.updatedAt), 'day') < 7

sjcl

Stanford JavaScript Crypto Library for hashing, encryption, and HMAC generation.

import sjcl from 'sjcl'

const hash = sjcl.hash.sha256.hash(payload)
const hexDigest = sjcl.codec.hex.fromBits(hash)

CLI Commands

CRUD

celigo scripts list
celigo scripts get <id>
celigo scripts create < script.json
celigo scripts update <id> < script.json
celigo scripts set <id> name="New Name"
celigo scripts delete <id>

Logs and Debugging

celigo scripts debug-logs <id> [--limit N] [--offset N] [--level error|warn|info|debug] [--start-date ISO] [--end-date ISO]
celigo scripts enable-debug <id> [--duration <minutes>]
celigo scripts disable-debug <id>
celigo scripts debug-logs <id> [--since <minutes>] [--flow-id <id>]

Authoring Against Sample Data

Script logic is runtime-dependent -- it only works against the specific shape of data it handles -- so a script is written and validated against a sample input. The sample comes from the step's recent runs, a test/run capture on the parent flow, or a JSON example you supply. A script written without sample data is written blind.

Treat authoring as a loop, not a one-shot:

  1. Generate or edit the function against the sample input.
  2. Run it against that sample.
  3. Check the output for errors or obviously-wrong results.
  4. Iterate -- refine and re-run until it passes.

A script that fails on the first pass isn't a failure; it's the first turn of the loop -- the runtime error and the code are both visible, so the next pass is informed by what went wrong. When no sample is available (the step has never run and no parent provided records), supply a JSON example before writing the script; a user-supplied sample plays the same validation role as captured runtime data.

Execution Logs and the Debug Window

Scripts write to a per-script execution log using standard console methods. What gets captured depends on the level:

  • console.error(), console.warn(), console.info(), and console.log() are always captured -- no setup, no toggle (info and log are equivalent).
  • console.debug() is gated: its output is persisted only while a time-bounded debug window is open on the script. When the window is closed, console.debug() still runs but its output is dropped.

"Debugging a script" here means exactly this -- turning on console.debug() capture for a window. It is not breakpoint-style debugging; there is no pausing or stepping through code. Open a window only when you need console.debug() output; for "why did this fail?" / "what errors happened?", the always-captured error/warn/info/log entries are usually enough.

The debug window is time-bounded and expires automatically -- it defaults to a short window (15 minutes) and is opened with celigo scripts enable-debug <id> [--duration <minutes>]. There's no need to close it manually, though celigo scripts disable-debug <id> ends it early.

Each log entry records its time, level (INFO / WARN / ERROR / DEBUG), the message, and two locating fields:

  • functionType -- which hook produced the entry (preMap, postSubmit, etc.)
  • _resourceId -- the export or import that ran the hook

Because one script can carry many functions across many hook sites, an unfiltered log stream interleaves entries from every consumer. Filter aggressively when reading -- by flow (--flow-id), by time (--since / --start-date / --end-date), and by level (--level). The practical query is "logs for this script, in this flow, on this step, during this window."

<!-- TIER:3 -->

Pre-Submit Checklist

Before creating or updating a script, verify:

  • Hook point is correct -- the function name matches the hook type being wired (e.g., preSavePage function for a hooks.preSavePage reference)
  • Return value matches contract -- batch hooks (preMap, postMap, postSubmit, postResponseMap) return arrays that match the input array length exactly
  • Error handling uses return pattern, not throw -- per-record errors use { errors: [...] } return values, not thrown exceptions (which fail the entire page)
  • Expression alternative considered -- filter, transform, and output filter can use expressions; only use a script when expressions cannot handle the logic
  • content field is included on PUT -- omitting content on update erases the code; always GET first, modify, then PUT
  • Debug mode is disabled after testing -- celigo scripts disable-debug <id> to avoid log noise in production

Gotchas

  1. Array length contracts are strict. preMap, postMap, and postResponseMap return arrays MUST match the input array length. Returning fewer or more elements fails the entire page silently or with cryptic errors.
  2. abort: true stops pagination, not the flow. In preSavePage, setting abort: true tells the export to stop generating new pages. It does NOT stop the flow or cancel processing of the current page's records.
  3. Script content is not returned in list responses. celigo scripts list shows metadata only. You must celigo scripts get <id> to see the actual JavaScript code.
  4. PUT erases content if omitted. Always GET the script first, modify, then PUT the complete object. The set command handles this automatically.
  5. One script resource can contain multiple functions. A single script with both preSavePage and preMap functions can be wired to different resources by specifying the function name in each hook reference.
  6. Throwing an exception fails the entire page. In batch hooks (preSavePage, preMap, postMap, postSubmit), an unhandled exception fails ALL records on that page, not just one. Use the error return pattern ({ errors: [...] }) for per-record errors.
  7. postResponseMap lives on the flow, not the resource. The hook is configured on the pageProcessors[] entry in the flow/API/tool, even though it processes export or import response data.
  8. filter/transform scripts replace expression-based alternatives. Wiring a script filter replaces any existing expression filter. They cannot coexist on the same resource.
  9. console.log() output goes to script logs, not stdout. Use celigo scripts debug-logs to see output. Logs require debug mode to be enabled for debug-level messages.
  10. Only console.debug() needs the debug window. error / warn / info / log are always captured; debug output is persisted only while a time-bounded debug window is open (celigo scripts enable-debug). A closed window silently drops console.debug() output.
  11. Shared-script logs interleave across hook sites. One script can hold many functions used by many exports/imports, so its log stream mixes entries from every consumer. Filter by flow, level, and time when reading; each entry's functionType and _resourceId identify where it came from.
  12. Clearing a script-mode filter/transform reverts the slot to declarative mode. The four mode-switchable slots (filter, input_filter, transform, branching) hold a rule tree or a script, never both -- removing the script drops the slot back to rules, and wiring a script replaces the rules.

Common Errors

Error / SymptomCauseFix
"The number of elements in the return value must match the input"Batch hook return array length differs from inputEnsure return array has exactly data.length (preMap) or postMapData.length (postMap) elements; use {} for skipped records
All records on a page fail with no per-record detailUnhandled exception thrown in batch hookWrap logic in try/catch; return { errors: [...] } per record instead of throwing
Script content is empty after updatePUT omitted the content fieldAlways GET first, modify, then PUT the complete object (or use celigo scripts set)
abort: true set but flow keeps runningabort only stops pagination; current page still processesThis is expected behavior; use error returns or filter to skip individual records
Script not executing / no logsScript not wired to any resource, or debug mode not enabledVerify _scriptId + function reference on the export/import/flow; enable debug with celigo scripts enable-debug
"Function not found" or similarfunction name in hook reference doesn't match an exported function in the scriptCheck the function name matches exactly (case-sensitive) between the hook config and the script's export
Filter always returns all/no recordsFilter function returns truthy/falsy value instead of strict booleanReturn explicit true or false; avoid returning objects or undefined
postResponseMap not firingHook wired on the import/export instead of the flow's pageProcessors[] entryMove the hook config to the pageProcessors[] entry in the flow, not the resource
console.debug() lines missing from logsNo debug window was open while the script ranOpen a window first (celigo scripts enable-debug <id>), then reproduce; error/warn/info/log don't require it
Log stream is a confusing mix of unrelated entriesScript is shared across many hooks/flows and the query is unfilteredFilter by --flow-id, --level, and date range; use each entry's functionType / _resourceId to identify the origin

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.