| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Built and maintained in my own time. If SyncroNow AI helps you or your team, a sponsorship keeps it moving — it directly funds new features, fixes and keeping pace with ServiceNow's REST surface.
SyncroNow AI is a tool for managing ServiceNow code in a more modern way. It allows you to:
Because your scoped-app code is downloaded as plain, editable files in a project folder, SyncroNow AI is well suited for tracking your ServiceNow source code in Git — giving you real diffs, history, branches and pull requests over code that would otherwise live only inside the instance.
Heritage: SyncroNow AI is a modern successor to Sincronia (sinc). If you know that tool, the mental model carries over — but the CLI, package scope and build pipeline here are different (see Installation).
Project documentation
Table of Contents
syncrona and all fifteen @syncrona/* packages are published on npm with build provenance, so no build step is needed to use the CLI.
npm i -g syncrona
syncrona login
syncrona initOr without installing anything: npx syncrona init.
For contributing, or to run an unreleased commit:
git clone https://github.com/IvanBBaev/syncrona
cd syncrona
npm ci
npm run build
# expose the CLI as `syncrona` on your PATH:
npm link --workspace syncrona
syncrona login
syncrona initPrefer not to link globally? Run the built CLI directly from the repo with node packages/core/dist/index.js <command>.
In order to use SyncroNow AI, you will need:
⚠️ Windows users: WSL (Windows Subsystem for Linux) is currently required — native Windows is not yet supported.
- Install WSL with an Ubuntu distribution (Windows version 1903+; earlier versions are untested/not working)
- Run all syncrona commands from inside the WSL shell
- (Optional) Windows Terminal is recommended for proper text rendering
Native Windows support (PowerShell install, Windows Credential Manager) is on the roadmap.
ServiceNow compatibility: SyncroNow AI talks to standard ServiceNow REST/Table APIs and works with or without the companion scoped app, so it is broadly release-agnostic. It is actively used against recent ServiceNow releases; a formal supported-version matrix is being established — if you hit a release-specific issue, please open an issue with your instance version.
Authentication: SyncroNow AI supports every inbound REST authentication method ServiceNow offers, in both clients (CLI axios and the MCP server's native fetch). Select the method explicitly with SN_AUTH_METHOD, or let it be inferred for backward compatibility. Every auth variable also accepts a per-profile _<PROFILE> suffix.
| Method (SN_AUTH_METHOD) | Required vars | Notes |
|---|---|---|
| Basic — basic (default) | SN_USER, SN_PASSWORD | HTTP Basic over HTTPS. |
| OAuth password — oauth-password | SN_OAUTH_CLIENT_ID, SN_OAUTH_CLIENT_SECRET, SN_USER, SN_PASSWORD | OAuth 2.0 Resource Owner Password grant; Bearer token from oauth_token.do, refreshed on expiry/401. |
| OAuth client credentials — oauth-client-credentials | SN_OAUTH_CLIENT_ID, SN_OAUTH_CLIENT_SECRET | OAuth 2.0 Client Credentials grant — service-to-service, no user password (Tokyo+). |
| OAuth JWT bearer — oauth-jwt-bearer | SN_OAUTH_CLIENT_ID, SN_OAUTH_CLIENT_SECRET, SN_JWT_KEY | OAuth 2.0 JWT Bearer grant. SN_JWT_KEY is a path to (or inline) RS256 private-key PEM; a fresh signed assertion is minted per token. Optional SN_JWT_KID / SN_JWT_ISS / SN_JWT_SUB / SN_JWT_AUD override the JWT header/claims (defaults derived from client id + user + instance). |
| API key — api-key | SN_API_KEY | Inbound REST API Key sent as a header — default x-sn-apikey, override with SN_API_KEY_HEADER (Vancouver+). |
Without SN_AUTH_METHOD the method is inferred exactly as before — OAuth password when a client id/secret pair and a password are present, otherwise Basic — so existing setups keep working unchanged. An unrecognized value is an error rather than a fallback: syncrona doctor reports Unknown SN_AUTH_METHOD "<value>" and the MCP server refuses to start, so a typo can no longer resolve silently to a different method. Use a dedicated least-privilege integration user.
Mutual TLS (client certificate) is orthogonal to the method above and combines with any of them (or works alone): point SN_CLIENT_CERT and SN_CLIENT_KEY at PEM files (plus SN_CLIENT_KEY_PASSPHRASE if the key is encrypted). It is applied at the TLS layer in both the CLI and the MCP server.
syncrona login walks you through any of these interactively (method picker), or runs non-interactively with flags — for example syncrona login --auth-method api-key --api-key XXXX or syncrona login --auth-method oauth-client-credentials --client-id … --client-secret …. Secrets are kept in the encrypted global credential store; the JWT key and TLS cert/key are referenced by path, never copied into it. See SECURITY.md and docs/MULTI_INSTANCE.md.
Corporate proxies & custom TLS (G9): the CLI honors the standard HTTPS_PROXY / HTTP_PROXY / NO_PROXY environment variables automatically, so it works behind a corporate proxy with no extra configuration. For a corporate or self-signed certificate authority, point SYNCRONA_CA_BUNDLE at a PEM CA bundle (or set Node's built-in NODE_EXTRA_CA_CERTS, which also covers the MCP server's native-fetch client). As a last resort for a throwaway test instance, SYNCRONA_TLS_REJECT_UNAUTHORIZED=0 disables certificate verification — insecure, never use it against a real instance.
Request timeout: every ServiceNow request the CLI makes (data path and OAuth token endpoint alike) is bounded at 120 seconds. Raise or lower it with SN_REQUEST_TIMEOUT in milliseconds — a large bulk download over a slow link may need more, and 0 restores the old unbounded behaviour. An unparseable or negative value falls back to the 120-second default rather than disabling the timeout.
npm i -D syncronanpx syncrona initIf your repository is a monorepo with many scoped apps under packages/, run SyncroNow AI from the specific scope directory, for example packages/cs. Each scope package should get its own .env, sync.config.js, and sync.manifest.json.
npx syncrona devSyncroNow AI takes a two-pronged approach to managing your ServiceNow scoped app. Architecture, creation of records, deletion of records, metadata and other ServiceNow objects besides your actual source code will be managed normally. Your source code itself will be managed inside of your SyncroNow AI project.
SyncroNow AI has a few basic commands to help you get the job done
| Command | Aliases | Description | Usage |
|---|---|---|---|
| refresh | r | Refreshes the sync.manifest.json file and downloads all new files created in ServiceNow since the last refresh. Does not override existing file contents. | npx syncrona refresh |
| dev | d | Starts development mode. Watches files for changes, then builds and pushes them to the corresponding record. Only works on files in the manifest file. | npx syncrona dev |
| init | none | Walks you through creating a basic SyncroNow AI project. This is the recommended way to create a SyncroNow AI project from scratch. Use --ci to provision every scope the detected .env exposes without prompting. | npx syncrona init |
| push | none | Builds and pushes all files in your local SyncroNow AI project to the ServiceNow instance in your .env file. Takes an optional path argument (push src/sys_script/MyBusinessRule, absolute or relative to the current directory) to push just that file or folder; the path must resolve inside the configured source or build directory, otherwise the push is refused with a warning. | npx syncrona push |
| download <scope> | none | Downloads the specified scoped app, overwriting all local files in the way. Only use this if you know what you are doing! | npx syncrona download my_test_app |
| build | none | Builds the local SyncroNow AI project and stores the files locally | npx syncrona build |
| deploy | none | Deploys the files in the build folder to the ServiceNow instance. Use --ci to skip the interactive prompts in noninteractive runs — it deploys the diff manifest when build --diff produced one, and the full build scope otherwise. | npx syncrona deploy |
| docs | none | Generates or logically updates Markdown documentation and Mermaid diagrams describing the downloaded scope (overview, tables, per-record files). | npx syncrona docs |
| repair | none | Reconciles the manifest with local files: reports (default) or re-downloads files the manifest expects but are missing locally, and optionally prunes orphan files — files under the source directory that carry the manifest's <table>/<record>/<field>.<ext> layout (or its flat <table>/<record>~<field>.<ext> form) but that no manifest record claims. Hand-written sources, dot-directories and anything of another shape are never orphans. Use --apply to re-download missing files, --prune (with --apply) to delete orphans, and --ci for non-interactive runs; pruning is refused outright when sourceDirectory resolves to the project root. | npx syncrona repair |
| status | none | Shows extended workspace status: instance/user/scope, config paths, env readiness, and connectivity diagnostics. | npx syncrona status |
| check-env | none | Checks machine prerequisites (Node 22+, supported platform/WSL, Git) and prints actionable fixes. | npx syncrona check-env |
| doctor | none | Runs local configuration and ServiceNow connectivity diagnostics, and reports actionable failures. | npx syncrona doctor |
| plugins | none | Shows configured plugin rules and reports plugin package availability (installed or missing) from the current workspace. | npx syncrona plugins |
| config <action> | none | Inspect or extend configuration. config show-defaults prints the built-in default includes/excludes; config add-plugin [--plugin <name>] lists the first-party build plugins (with install status) and prints a paste-ready rules snippet. | npx syncrona config add-plugin --plugin typescript |
| completion [shell] | none | Prints a shell tab-completion script covering every syncrona command. Pass bash or zsh, or omit the argument to auto-detect the shell from $SHELL; append the output to your shell rc file to install it. | npx syncrona completion |
| mcp | none | Starts standalone MCP server and can auto-configure local MCP client files (.vscode/mcp.json, .syncrona-mcp/secrets.json). | npx syncrona mcp |
| login [instance] | none | Saves ServiceNow credentials in the encrypted global CredentialStore and optionally sets active instance. | npx syncrona login dev123.service-now.com |
| logout [instance] | none | Removes stored credentials for one instance (or all with --all) from the global CredentialStore. | npx syncrona logout dev123.service-now.com |
| instances | none | Lists instances saved in the global CredentialStore and marks the active one. | npx syncrona instances |
| use <instance> | none | Sets active instance from the global CredentialStore for subsequent commands. | npx syncrona use dev123.service-now.com |
| jira [key] | --profile, --comments, --json | Fetches rich context for a Jira issue (summary, description, status, type, priority, assignee/reporter, labels, components, parent, subtasks, links, fix versions, recent comments). Resolves the key from the argument or the current git branch name. Supports Jira Cloud and Server/Data Center. | npx syncrona jira SCRUM-123 |
| jira-login | --profile | Saves Jira credentials in the encrypted global CredentialStore. Auto-detects Cloud vs Server/Data Center from the base URL and verifies the connection. | npx syncrona jira-login |
| jira-logout | --profile, --all | Removes stored Jira credentials for one profile (or all with --all) from the global CredentialStore. | npx syncrona jira-logout |
| mirror <action> | --full, --reconcile, --verify-quiescent, --deep, --json | Full-instance git mirror. mirror init provisions the current repository for a mirror-sized checkout (feature.manyFiles, core.fsmonitor, core.precomposeunicode, scheduled git maintenance, .gitattributes); mirror sync sweeps the instance into the tree (--full for a complete baseline, --reconcile to propagate instance-side deletions now instead of waiting for the every-Nth-sync cadence, --verify-quiescent to report tables that changed under the sweep) and prints a suggested commit message without ever committing; mirror status compares the tree against the live instance; mirror verify checks the tree against its own manifests with no network access; mirror report re-prints the last sweep's report. status and verify take --deep to re-hash sampled records instead of comparing aggregates alone. Exit codes: 0 clean, 1 the run could not finish, 2 completed with drift or findings. | npx syncrona mirror sync --full |
init wizard behavior notes:
The global CredentialStore writes each instance's credentials to ~/.syncrona/credentials/<instance>.enc, encrypted with AES-256-GCM. The encryption key is resolved with this precedence:
⚠️ With the machine-derived fallback key, anyone who can read the .enc file and run code as your user on the same machine (or who knows your hostname + username) can decrypt the credentials. It guards against casual inspection and accidental file sharing — not a compromised account, stolen disk, or malware running as your user. Configure an explicit key or the keychain for real at-rest protection.
Reads fall back to the machine-derived key, so credential files written before this change keep decrypting; the next syncrona login re-encrypts them with the resolved key.
Recommendations:
--diff <branch> means different things for push and build — both use git diff <branch>... against your source folder, but apply it differently:
syncrona push --diff <branch> pushes only the files that changed versus that branch. This is the "changed-only" push — use it to push just your feature's edits instead of the whole scope.
npx syncrona push --diff mainsyncrona build --diff <branch> builds all source files but also writes a sync.diff.manifest.json recording which files changed, so a later syncrona deploy can target just those (an audit/deploy-tracking trail).
npx syncrona build --diff mainWithout --diff, push and build act on the entire source folder.
push accepts a few more flags for scope handling, change tracking and automation:
npx syncrona push --update-set "PRJ-123 changes" --scope-swap
npx syncrona push --ci --concurrency 5Six commands implement a preview and accept --dry-run: push, deploy, download, build, init and repair. They report exactly what they would do and write nothing — locally or on the instance.
npx syncrona push --dry-runEvery other command refuses --dry-run rather than accepting and ignoring it, because a flag that is parsed and then does nothing turns a request for a preview into a real run that reports nothing unusual:
$ npx syncrona docs --dry-run
`syncrona docs` has no preview mode, so --dry-run would run it for real. Re-run without --dry-run.init --dry-run previews only the .env-driven path. With no .env in the directory, init would fall back to the interactive setup wizard, which has no preview mode — so it stops and tells you instead of provisioning the project.
To work with multiple ServiceNow instances from one workspace, define profile-specific env vars and select them with --instance-profile.
SN_INSTANCE_DEV=dev123.service-now.com
SN_USER_DEV=dev.user
SN_PASSWORD_DEV=dev.password
npx syncrona status --instance-profile devProfile vars (SN_INSTANCE_<PROFILE>, SN_USER_<PROFILE>, SN_PASSWORD_<PROFILE>) fall back to base vars when a specific value is missing.
In dev mode SyncroNow AI periodically re-reads the instance manifest to pick up records created in ServiceNow since you started (it does not overwrite local file contents). The interval defaults to 30 seconds (refreshInterval in sync.config.js). Overlapping refreshes are guarded — a slow refresh never stacks. On a slow network, raise the interval or disable polling:
npx syncrona dev --refresh-interval 60 # poll every 60s
npx syncrona dev --refresh-interval 0 # disable polling; refresh manually with `syncrona refresh`Run with --log-level debug to see Manifest refresh took Xms and per-file rule matches.
syncrona jira [key] pulls read-only context for a Jira issue (summary, description, status, type, priority, assignee/reporter, labels, components, parent, subtasks, links, fix versions and recent comments) — handy for pairing an issue with the ServiceNow change you are working on. If you omit the key, the CLI parses it from the current git branch name (e.g. feature/SCRUM-123-foo → SCRUM-123). It supports both Jira Cloud and Jira Server / Data Center.
Save credentials once with syncrona jira-login; they are stored in the same encrypted global CredentialStore as ServiceNow credentials. Configuration is resolved with this precedence (first match wins):
| Variable | Purpose |
|---|---|
| JIRA_BASE_URL | Jira base URL, e.g. https://acme.atlassian.net (Cloud) or https://jira.acme.com (Server / DC). |
| JIRA_TOKEN | Cloud API token, or a Server / Data Center Personal Access Token (PAT). |
| JIRA_EMAIL | Account email — required for Cloud (paired with the API token as Basic auth). Omit for Server / DC PAT (Bearer) auth. |
| JIRA_DEPLOYMENT | Force the deployment type: cloud or server. Auto-detected from the base URL when unset. |
See packages/jira/README.md for the full configuration reference (Cloud vs Server/DC auth semantics, profiles and precedence).
Development workflow — you author code on your ServiceNow dev instance, SyncroNow AI pulls a local copy, and every save is built and pushed back to the matching record automatically.
flowchart TD
subgraph SN["ServiceNow Dev Instance"]
A["Create new code file<br/>(ex. script include)"]
B["Create tables,<br/>metadata, etc"]
C["Create update set,<br/>push to team dev, etc"]
OK["Results of build are placed into<br/>the corresponding record field"]:::ok
A --> B --> C
end
subgraph SY["SyncroNow AI Project"]
REF["npx syncrona refresh"]:::cmd
LC["Local copy gets created"]
CE["Change extension if desired"]
DEV["npx syncrona dev"]:::cmd
SV["Save changes"]
BP["Build process starts automatically"]
REF --> LC --> CE --> DEV --> SV --> BP
end
A -->|new record| REF
BP -->|Successful build| OK
BP -->|Errors| ERR["Errors printed to console"]:::err
classDef cmd fill:#3f3f46,color:#ffffff,stroke:#27272a;
classDef ok fill:#d3ecd3,color:#14532d,stroke:#4caf50;
classDef err fill:#f6c9c9,color:#7f1d1d,stroke:#e57373;
Deployment — point the same project at a higher instance (e.g. prod) and push the master version of the code up.
flowchart TD
subgraph SN["ServiceNow Prod Instance"]
L["Load changes from dev instance"]
U["Code files are updated<br/>in ServiceNow"]:::ok
end
subgraph SY["SyncroNow AI Project (prod)"]
LM["Load master version of code"]
PUSH["npx syncrona push"]:::cmd
AB["All files get built"]
LM --> PUSH --> AB
end
L --> LM
AB -->|Successful build| U
AB -->|Errors| ERR2["Errors printed to console"]:::err
classDef cmd fill:#3f3f46,color:#ffffff,stroke:#27272a;
classDef ok fill:#d3ecd3,color:#14532d,stroke:#4caf50;
classDef err fill:#f6c9c9,color:#7f1d1d,stroke:#e57373;
When you download your source code using SyncroNow AI, it creates a folder structure that goes as follows:
project_folder/
src/
table_name/
record_name/
field_name.extension
Records are shown as folders because there are times where there are multiple code files per record. This makes it very important that you never have records with the exact same display value in the same table! If you do, then you will notice issues building your files to the right record in ServiceNow.
This is the configuration file for SyncroNow AI. Learn More
Keeps track of all ServiceNow files that are watched by SyncroNow AI. Do not manually modify it
Tracks changed files for build and deploy commands when using diff option.
Stores login credentials and and the instance URL. Do not commit this to git
When you download your source code using SyncroNow AI, you are effectively 'taking control' of that code. Once the code is in your project, you no longer want to edit it directly in ServiceNow! This is why putting your code into source control is highly recommended. Anything else besides code, such as tables, configuration of script records, metadata, etc. must still be tracked in ServiceNow and passed along with your preferred method of moving ServiceNow architecture
Modern javascript development workflows are asymmetric, meaning that the source code you write is usually not the code that gets executed. It is built using various tools and compiled/transpiled into some more compatible or smaller javascript code that is run by browsers or node environments.
SyncroNow AI takes advantage of this same principle by allowing you to leverage some of those same tools. This means that you will no longer be able to store your source code directly in ServiceNow, instead you will have a local version of your source code that gets built and the result of that build will be put into ServiceNow.
EXAMPLE
Let's say I want to develop using TypeScript. Once I have the right plugin configuration for my needs, this Typescript file:
// Example/script.ts
class Example {
constructor(message: string) {
gs.info(message);
}
sayHello() {
gs.info("Hello, SyncroNow AI!");
}
}becomes
// ServiceNow `Example` script include.
"use strict";
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
var Example =
/*#__PURE__*/
(function () {
function Example(message) {
_classCallCheck(this, Example);
gs.info(message);
}
_createClass(Example, [
{
key: "sayHello",
value: function sayHello() {
gs.info("Hello, SyncroNow AI!");
},
},
]);
return Example;
})();File extensions are typically only one short blurb (e.g. .js, .css, etc.). When you use SyncroNow AI, you may find that you want to treat one .js file differently than another. That's where extensions can become more powerful! You could create an extension in your project such as .server.js and .client.js which you could combine with the rules configuration of SyncroNow AI to have two different build pipelines. You could use Webpack for client scripts and Babel for server scripts! Pretty cool huh?
As long as the main filename stays the same, you can add as many extensions as you want.
EXAMPLE
script.js becomes script.servicenow.js or script.ts or script.what.ever.you.want.js
SyncroNow AI aims to be as configurable as possible. To do that, it creates a special javascript file in your project directory called sync.config.js. It's contents will look something like this:
module.exports = {
// Directory where your source files will be kept and will be watched by SyncroNow AI
// during development.
sourceDirectory: "src",
//Directory where local builds will be stored
buildDirectory: "build",
// This is where you will configure your plugins. You match based on plugins.
// Order your rules by MOST SPECIFIC extension first! The first match is the
// only one that gets executed.
rules: [],
// === INCLUDES/EXCLUDES apply on top of the default config! See more below ===
// Tables/fields to exclude (AKA not download or track) from SyncroNow AI
excludes: {},
// Tables/fields to explicitly include in your SyncroNow AI project.
// Can override excludes if necessary.
includes: {},
//How often syncrona will refresh the manifest in development mode
refreshInterval: 30,
// (Experimental, opt-in) Use a flat local layout
// <table>/<record>~<field>.<ext> instead of per-record folders. The mapping
// is lossless and reversible (see "Flat layout" below).
flat: false,
// Write a `.meta.json` sidecar next to each record's field files, holding the
// record's non-file columns (see "Record metadata" below). Set to false to
// stop writing it.
meta: true,
// Push local edits to that sidecar back to the instance. Set to false to keep
// it as read-only reference data.
metaPush: true,
};If you find that your config is getting too large, you can use typical nodejs techniques for splitting it into smaller modules and loading them into the sync.config.js.
By default each record is stored as a folder of field files (<table>/<record>/<field>.<ext>). Setting flat: true selects a flatter layout that collapses the per-record folder into one file per field, keeping table, record and field so the mapping stays lossless and reversible:
sys_script_include/MyUtil/script.js -> sys_script_include/MyUtil~script.js
The ~ separator never appears in a ServiceNow dictionary field name, so the record is everything before the last ~ and the field everything after it. The conversion is implemented as pure, fully-tested helpers (folderRelToFlat / flatRelToFolder in packages/core/src/flatLayout.ts). download/refresh/push/build honour flat: true automatically — files are written and read back in the flat shape, and the build tree mirrors it so deploy works unchanged. It remains opt-in and labelled experimental pending broad validation against a live instance. Switching flat on an existing workspace does not convert what is already on disk: nothing re-lays the tree, and refresh only fetches files the manifest does not have yet, so the old per-record folders stay where they are and new files land in the other shape. Delete the downloaded tree (or start a fresh workspace) and re-download after changing the setting.
A record is more than its code. A script include also carries api_name, access, client_callable, active and description; a business rule carries when, order, condition and collection. None of those is a file field — ServiceNow types them as string, boolean, choice or reference — so they used to be invisible: a workspace held the script and nothing else.
Each record now also gets a metadata sidecar holding its non-file columns:
sys_script_include/MyUtil/script.js sys_script_include/MyUtil/.meta.json <- and flat: MyUtil~.meta.json
{
"access": "public",
"active": "true",
"api_name": "x_demo.MyUtil",
"caller_access": "",
"client_callable": "false",
"description": "Shared helpers",
"sys_class_name": "sys_script_include",
"sys_policy": "",
"sys_scope": "5b1e53bc1b6624501aba63166e4bcb1c"
}The columns are discovered from the dictionary, walking the table's parents so an inherited column counts, and the manifest records the list it used per table in metaFields.
What is excluded, and only that: every file field (it already has its own file); password/password2 and the other credential types (a secret must never reach the working tree); journal fields and binaries (no stable string form); and the six columns that describe the last save rather than the record — sys_id, sys_created_by, sys_created_on, sys_updated_by, sys_updated_on, sys_mod_count. Everything else is kept, including the sys_* columns that do describe the artifact: sys_scope, sys_package, sys_name, sys_class_name, sys_policy, sys_overrides, sys_update_name, sys_domain, sys_customer_update, sys_replace_on_upgrade.
Keys are sorted and every tracked column is written even when it is empty on the instance — a blank caller_access you cannot see is a column you cannot set, and the point of the sidecar is that it is editable. A column the instance did not return at all (a read ACL hides it) is left out rather than claimed as ""; that distinction is what keeps a push from clearing a field it never read. Re-downloading an unchanged record still produces no diff.
The sidecar is editable. push, dev and deploy expand it back into an update of the record's real columns, in the same request that carries the field files. Three rules, each there because the alternative loses an edit silently:
A fourth rule covers the case where the metadata layer itself failed to build: if the manifest records no columns for the table, the push does not report "unknown column" for every key in a perfectly good file. It says the manifest is the thing that is missing a metadata layer and sends you to syncrona refresh. Correspondingly, refresh warns — at warn, not debug — naming each table whose dictionary it could not read, the cause, and both ways out. A dictionary that cannot be read never fails the download: the scripts still land.
Everything on the instance still applies on top: write ACLs, business rules and data policies can reject or rewrite a value the push sent.
Set metaPush: false in sync.config.js to keep the sidecar as read-only reference data — the field files still push, and each skipped record says so. Set meta: false to stop writing sidecars at all. To control which columns a given table records, list them explicitly — that replaces discovery for that table, so it can also re-add a column the default rules exclude. It skips the dictionary read entirely (which is what makes it usable when sys_dictionary is not readable), so it skips every default exclusion with it: an explicit list that names a password column will write that password into the working tree.
// sync.config.js
module.exports = {
// ...
tableOptions: {
sys_script: {
metaFields: ["when", "order", "condition", "collection", "sys_overrides"],
},
},
};The full design — the model, every discovery and serialization rule with its rationale, the push contract, how a degraded metadata layer behaves, the cost of building it, and the measurements taken against a live instance — is in docs/design/record-metadata-layer.md.
OR
When you first set up your project, you may notice you may have more files than you want to manage or some files are missing. This can be easily resolved by tweaking your includes and excludes section of your sync.config.js. SyncroNow AI attempts to establish sane defaults for these values here (and you can list them with syncrona config show-defaults).
If you think there is something wrong with the default setup, feel free to submit a pull request! 🐙👍
The excludes and includes sections in your sync.config.js act as additions to that default setting. You can override parts of it or turn parts of it off.
Once you have updated your includes and excludes, run npx syncrona refresh to load the new files and update the manifest. You will have to manually delete any newly excluded tables/fields.
// sync.config.js
module.exports = {
excludes: {
// Turns off the default exclusion of the `sys_scope_privilege` table
sys_scope_privilege: false,
// Excludes everything from the `my_cool_table` table
my_cool_table: true,
// Excludes the `cool_script` field specifically from the `new_cool_table` table.
// Other valid fields will be included.
new_cool_table: {
cool_script: true,
},
},
includes: {
// Turns off the default inclusion of the `content_css` table
content_css: false,
// Explicitly includes the `sys_report` table. Overrides any excludes on the
// same table.
sys_report: true,
// Explicitly pulls in the `neat_script_field` as a `js` file in spite of whatever
// type of field it might be in ServiceNow. Useful for text fields that
// represent code.
special_code_table: {
neat_script_field: {
type: "js",
},
},
},
};Plugins are where the true 💪 POWER 💪 of SyncroNow AI comes from! The rules section is used to configure plugins. When configuring plugins, Make sure to always put your rules in the order you want them matched! The first rule that gets matched will be the only one that runs!
// sync.config.js
module.exports = {
rules: [
{
// The match argument is a regular expression that will match on your desired files
// The order matters, so put your most specific rules first!
// If there is a file that ends in `.secret.ts` it will match here and
// NO PLUGINS WILL BE RUN
match: /\.secret\.ts$/,
plugins: [],
},
{
// If there are just generic TypeScript files that have no other extension, they will
// match on this rule instead.
match: /\.ts$/,
// List of plugins to run on the matched files. Each plugin will run in order.
// THE RESULT OF THE PREVIOUS PLUGIN WILL BE PASSED TO THE NEXT PLUGIN so make
// sure they are in the right order!
plugins: [
{
// The name of the plugin, it is the same as the name of the NPM package of
// the plugin.
name: "@syncrona/typescript-plugin",
// Options to pass to the plugin. This will be defined by the plugin itself.
// In this case, we are telling the typescript plugin to only type check and
// not transpile.
options: {
transpile: false,
},
},
],
},
],
};This is a relatively new feature and potentially subject to change
The tableOptions section allows for special setups on any table. Example:
// sync.config.js
module.exports = {
// ...
tableOptions: {
some_table: {
// sets the field used for the record folder name
displayField: "some_field",
// Allows to de-duplicate records based on certain fields
differentiatorField: "sys_id",
// can be an array, if there isn't a value in a field, it moves to the next one
differentiatorField: ["some_field", "sys_id"],
// an encoded query to filter records by
query: "some_field=test",
// the columns written into each record's .meta.json sidecar; replaces
// dictionary discovery for this table
metaFields: ["active", "order"],
},
},
};When to use each option
Note on differentiatorField
This feature will currently put a colon in the filename, which breaks the Windows filesystem (and WSL paths under /mnt). Prefer a non-sys_id differentiator where possible, and avoid it entirely if your team works on native Windows.
Deleting something in SyncroNow AI is relatively simple. Just follow these steps:
Why is this not automatic? Deleting files can be a dangerous game and it should be a deliberate action!
Use the global credential store (syncrona login / syncrona use) or instance-profile env vars and --instance-profile. syncrona status shows which instance and credential source are active. Full guide: docs/MULTI_INSTANCE.md.
Treat each scope as its own project under packages/, run commands from the scope directory, and share node_modules/plugins at the root. Full guide: docs/MONOREPO_GUIDE.md.
After downloading a scope, run npx syncrona docs to generate Markdown documentation and Mermaid diagrams for it (overview, tables, per-record files) — a quick way to explore what a real SyncroNow AI project looks like.
| Name | Description |
|---|---|
| @syncrona/babel-plugin | Runs Babel on .js/.ts files |
| @syncrona/prettier-plugin | Prettifies your output files using Prettier |
| @syncrona/sass-plugin | Runs the Sass compiler on your files |
| @syncrona/typescript-plugin | Type checks and compiles TypeScript files |
| @syncrona/webpack-plugin | Creates Webpack bundles with your files |
| @syncrona/eslint-plugin | Runs ESLint on your files on build |
This project is built and maintained in my own time. If it saves you or your team time, please consider supporting its continued development — sponsorship directly funds new features, fixes and maintenance.
ServiceNow is a registered trademark of ServiceNow, Inc. This project is an independent, third-party tool and is not affiliated with, endorsed by, or sponsored by ServiceNow, Inc. All other trademarks are the property of their respective owners.
SyncroNow AI is licensed under the GNU General Public License v3.0 (GPL-3.0-or-later). It is a derivative work of Sincronia ("sinc") by Nuvolo, which is licensed under GPL-3.0; substantial portions of the build/plugin packages, CLI core, and shared types originate from that project and remain under GPL-3.0. See NOTICE for full attribution and docs/PROVENANCE.md for the complete provenance/IP analysis. As required by the GPL, redistributions must preserve these notices and make the corresponding source available.
| Back | FazBrowse Home | New Git URL |