Skip to content

CLI commands

Find each command’s syntax and options below. Every command also accepts --json and -h / --help; shared behavior and output formats are documented at the end.

syllabus project manages rules and skills for a project, the codebase whose rules and skills Syllabus manages. Initialize from the project root. In Git repositories, other project commands also work from subdirectories.

Configuration lives in .syllabus/config.yaml at the project root. See Working directories for repository discovery and input-file paths.

Terminal window
syllabus project init [options]

Create .syllabus/config.yaml, the authored rules/ and skills/ directories, and the managed Syllabus guide, and add the Syllabus section to the repository’s AGENTS.md, creating the file if needed. Rerunning init preserves valid configuration and authored content and refreshes an unmodified guide and section.

OptionMeaning
--skill-targets TARGETWhere to install skills: claude for .claude/skills/, agents for .agents/skills/, or none. Repeat for both targets. none can’t be combined with a target.
--non-interactiveAccepted; init runs without prompts even when this flag is omitted.

A new configuration gets schemaVersion: 2 and skillTargets: [claude, agents], or skillTargets: [] with --skill-targets none. Init creates no target folders: sync writes them when the repository first installs a skill.

Rerunning init on an existing configuration changes skillTargets only when you pass --skill-targets. A version 1 configuration fails with a message that shows the version 2 shape; see No backward compatibility.

Init does not fetch sources or generate rule guidance. It refuses to overwrite a manually edited Syllabus guide or AGENTS.md section, and leaves an AGENTS.md that already points to generated/RULES.md outside the section alone. For file locations and ownership, see Project and rule group guides.

Terminal window
syllabus project add source NAME [options]

Record a source in configuration without fetching it. NAME is the source name, such as team. Run syllabus project sync afterward to take the included rules and install the included skills. The same command adds a library or any other Git repository with skill folders: you don’t declare which it is, and sync detects whether the repository has a manifest.

OptionMeaning
--repository URLRequired. An accepted HTTPS or SSH Git repository address.
--ref REFThe branch, tag, commit, or library release to take the source from, recorded as the source’s ref. Required for a source without release/<number> tags, such as a non-library source; see Refs.
--rules SELECTORA rule selector for rules.include: a rule ID, such as practices/testing/verify-retry-limits, a prefix, such as practices/testing/*, or *. Repeat for several.
--skills SELECTORA skill selector for skills.include: recommended, *, or a skill ID, such as skills/build-feature. Repeat for several.
--non-interactiveNever prompt. Supply all required inputs as flags.

Quote * and selectors that end in /* so your shell does not expand them. Pass each selector as a separate option, rather than a comma-separated flag value:

Terminal window
syllabus project add source team \
--repository https://github.com/example/rules.git \
--rules 'practices/testing/*' \
--rules 'techs/typescript/*' \
--skills recommended \
--non-interactive

The repository address and selectors are illustrative; replace them with a source you can access.

--rules and --skills are both optional: a source added with neither still installs the skills its library requires. Without --ref, the source follows the library’s newest library release when the project updates. To hold individual rules, add pins to the source in configuration, or use syllabus project update --keep; see Choose versions.

Sync checks the source when it first reads it. A source without release/<number> tags and without ref fails with ref-required. rules on a non-library source fails, because it has no rules. A --ref that names no branch, tag, or commit fails with version-not-found, and a skill from a non-library source with no license terms fails with license-not-found; see License rules. An existing source name or repository fails before writing anything; declare each repository once.

Terminal window
syllabus project add group ID [options]

Create an authored rule group in .syllabus/rules/ID/, with _group.yaml metadata and an authoring README. ID is a rule group path such as practices/testing or techs/typescript.

OptionMeaning
--name TEXTRequired. Rule group display name.
--description TEXTRequired. What the rule group covers.
--when-to-read TEXTRequired. When an agent should read the rule group.
--non-interactiveNever prompt. Supply all required inputs as flags.

A rule group can exist before it has rules. Authored rule groups need no source declaration. Existing rule group files are preserved.

Terminal window
syllabus project add rule ID [options]

Create a rule in an existing authored rule group, at .syllabus/rules/ID.md. ID includes the rule group path and rule name, such as practices/testing/check-retries, without .md.

OptionMeaning
--title TEXTRequired. Action-oriented rule title.
--when-to-read TEXTRequired. When an agent should read the rule.
--impact LEVELRequired. One of CRITICAL, HIGH, MEDIUM-HIGH, MEDIUM, LOW-MEDIUM, or LOW.
--impact-description TEXTRequired. The consequence the rule helps prevent.
--body-file PATHOptional UTF-8 Markdown body, without frontmatter. Relative paths start at your working directory. Omit to create an unfinished draft.
--non-interactiveNever prompt. Supply all required inputs as flags.

Create the rule group first with syllabus project add group. A missing rule group fails before metadata prompts. Without --body-file, complete the template and remove its syllabus:draft marker before building. Existing rules are not overwritten.

Terminal window
syllabus project add rule ID --from LIBRARY@VERSION [options]

Copy one version of a library rule into .syllabus/rules/ so the project controls its text. Use a fork to change what a rule says. To take one rule without the rest of its rule group, include it by ID instead, and to keep a rule at an older version, pin it; both keep the rule’s identity and update reports. ID is the library rule ID, such as practices/testing/verify-retry-limits, and the fork keeps it: .syllabus/rules/practices/testing/verify-retry-limits.md.

OptionMeaning
--from LIBRARY@VERSIONRequired for a fork. LIBRARY is a configured source name, such as team, or a repository address. VERSION is one of the rule’s versions, such as 1.3.0.
--reason TEXTWhy the project uses the fork instead of the library’s rule. Required when the fork adds an exclusion, as in step 4 below, and refused otherwise.
--non-interactiveNever prompt. Supply all required inputs as flags.

Metadata options and --body-file don’t apply to a fork. The command:

  1. Finds the library release that published <VERSION> from the release records in the library’s release/<number> tags, and reads the rule and its asset directory at that tag’s commit.
  2. Writes them under .syllabus/rules/. When neither authored rule group metadata nor a source the project includes supplies the rule’s rule group, it also creates the authored rule group from the library’s rule group metadata.
  3. Adds an attribution entry that links to the rule at the tag’s commit. Libraries hosted outside GitHub.com and GitLab.com get an attribution entry only when the repository address is an HTTPS URL, and the entry links to that address.
  4. When the LIBRARY source includes the rule and its last sync took it, adds an exclude entry for it with your reason, the fork’s ID as replacedBy, and the forked version as basedOn, so agents read only the fork, and updates list the library’s later changes to it. If the source pins the rule, the same edit removes the pin and warns that it did: the fork, not a pinned copy, now decides what agents read. If you changed that source’s configuration since its last sync, run syllabus project sync first.

If the rule links to files in the library’s shared .syllabus/assets/ directory, the fork copies them into its own asset directory and updates the links, because authored rules can’t depend on a source’s files. For the same reason, the command refuses a fork whose links would point outside the copied files, such as links to declared license or notice files, or raw HTML links. Existing authored rules and exclusions are never overwritten. A forked rule has no version in your project; it changes only when you edit it. Run syllabus project build afterward. The library’s license still applies to the copied text; see License rules.

Terminal window
syllabus project add skill NAME [options]

Create an authored skill in .syllabus/skills/NAME/. NAME follows the skill name grammar, such as release-checklist; the skill’s ID is skills/NAME.

OptionMeaning
--description TEXTRequired. What the skill does and when to use it, at most 1,024 characters.
--type TYPEOptional. playbook or review, recorded as syllabus-skill-type. See Skill types.
--non-interactiveNever prompt. Supply all required inputs as flags.

The command writes SKILL.md with the name, description, and type in its frontmatter, and a body with the syllabus:draft marker. Complete it and remove the marker before building; an authored skill that still has the marker fails sync and build. An authored skill cites your own rules by their IDs and rules from sources by project ID, such as fabrica:practices/testing/verify-retry-limits; see Skills in a project. In a library, use library add skill instead, whose skills cite only the library’s own rules. Existing skills are not overwritten. When a skill the repository installs from a source already has the name, the command warns that sync will fail with skill-name-clash until you rename or exclude one of them.

Terminal window
syllabus project add skill --from SOURCE:SKILL [options]

Copy a skill the project installs into .syllabus/skills/ so the project controls its steps. SOURCE:SKILL names a configured source and the skill’s ID in it, such as fabrica:skills/fix-bug. The fork keeps the skill’s name, so leave out NAME.

OptionMeaning
--from SOURCE:SKILLRequired for a fork. A skill that the source’s last sync installed, whether the project included it or another skill uses it. A required skill is refused, naming the library that requires it.
--reason TEXTRequired. Why the project uses the fork instead of the source’s skill, recorded with the exclusion.
--non-interactiveNever prompt. Supply all required inputs as flags.

--description and --type don’t apply to a fork. The command:

  1. Copies the skill’s folder from vendor/, exactly as the project installs it, with every file’s executable bit, to .syllabus/skills/<name>/: the version recorded in vendor/<source-name>/_source.json, or, for a non-library source, the recorded commit. A renamed skill’s fork takes its install name, so the folder and name match what agents already see.
  2. Rewrites the fork’s syllabus-rules-used and syllabus-skills-used to project IDs, such as fabrica:practices/testing/verify-retry-limits, because an authored skill cites rules and skills from sources by project ID. For a non-library source, it writes the skills that skills.uses lists for the skill into syllabus-skills-used.
  3. Adds an exclude entry for the skill with your reason, skills/<name> as replacedBy, and the forked version as basedOn, or, for a non-library source, the full commit SHA. If you changed that source’s configuration since its last sync, run syllabus project sync first.

Links in a skill stay inside its folder, so the fork needs no link changes. A SKILL.md without frontmatter, which a repository that isn’t a library can supply, gets frontmatter with the skill’s name, and each list the fork changes becomes a block string. The command refuses a skill whose frontmatter it can’t rewrite, such as one that writes metadata as a flow mapping, or that would fail the checks build applies to a skill you author; fork such a skill by hand. It also refuses a fork whose folder exists or that another exclusion already names as its replacedBy. When an update moves the source while the command waits for your reason, it writes nothing; run it again. Existing authored skills and exclusions are never overwritten. A forked skill has no version; it changes only when you edit it. Run syllabus project build afterward. The source’s license still applies to the copied files; see License rules.

Terminal window
syllabus project sync [options]

Take the rule and skill versions recorded for each source, validate their files, and replace vendor/ and generated/ in the Syllabus directory with the complete result. Sync also writes each installed skill’s folder into the repository’s skill targets, removes the folders of skills it no longer installs, refreshes an older, unedited managed Syllabus guide, and adds or refreshes the Syllabus section of AGENTS.md, as build does.

Accepts the shared options only.

Sync needs access to every configured repository and uses your existing Git credentials. It never moves a source: it restores the library release or commit recorded in vendor/<source-name>/_source.json, and the rule versions recorded there. Sync chooses a version only when configuration asks for something the record doesn’t have:

SituationWhat sync takes
A new source, or a changed repositoryThe source as syllabus project update would: the newest library release, or the commit the source’s ref names now.
A newly included rule or skillThe source’s recorded library release or commit. Each new rule gets the newest version that library release or an earlier one published. A rule or skill that isn’t in the recorded revision, such as one the library published later, fails with skill-not-in-revision, naming syllabus project update SOURCE.
A pin added or changedThe pinned version, up or down, when the recorded library release or an earlier one published it. A later version fails, naming syllabus project update SOURCE.
A pin removedThe recorded version, unchanged. The next update offers newer versions.
ref added, changed, or removedNothing new: the record is out of date, and sync fails, naming syllabus project update SOURCE, which takes the revision the configuration now names. A ref changed to another spelling of the recorded revision, such as refs/tags/release/2 for release/2, is current.

Sync trusts only a record it wrote: a record whose checksum doesn’t match, or that has none, such as after a hand edit or a merge resolution, fails, and sync writes nothing and never records it again. To recover, restore a record sync wrote with git checkout -- .syllabus/vendor/<source-name>/_source.json, or, during a merge conflict, take one side with git checkout --ours or git checkout --theirs for that file, then run syllabus project sync, which applies any configuration changes; or delete .syllabus/vendor/<source-name>/ and run syllabus project sync, which takes the source again as a new source. Before taking anything from a record it wrote, sync also checks it against the library’s release records: each recorded rule version must be the version its recorded library release publishes, and each recorded library release must exist. A source using ref keeps the commit it recorded, even after a tag moves or is gone, so sync checks it against the release records only when choosing versions reads them anyway.

Sync installs the skills each source’s levels and skills.include bring in, every skill they use, transitively, and every rule an installed library skill cites; see Skill selectors. A skill borrowed by a library comes from the commit the library’s record holds for it. Sync writes a skill folder in a target only when generated/installed-skills.json records that Syllabus installed it there, or when no folder with that name exists. It fails, writing nothing, with skill-folder-not-managed when a folder with an installed skill’s install name exists in a target that Syllabus didn’t install, and with skill-name-clash when two skills would install under the same name, from any sources, authored skills, or both; rename or exclude one of them. See Who owns the target folders.

These changes come from edits you made to configuration, so sync applies them without a preview. To move sources to newer revisions, use project update. If a source fails, the previous complete output is preserved. For recovery behavior, see Sync and recovery.

Sync warns:

  • about a source that takes unreleased changes through ref, naming the source and its unreleased rules and skills;
  • when an exclusion, a rule included by ID, or a skill included by ID names a rule or skill the library retired, or a skill folder a non-library source no longer has, since the entry no longer does anything;
  • about a rule included by ID that a prefix in the same include already reaches, since the entry changes nothing;
  • for each place a skill from a non-library source breaks the skill format, and installs the skill as it is;
  • for each rename it applies, with the fixed text in Rename a skill;
  • for each personal skill with the same name as an installed skill; see Personal skills with the same name;
  • when it installed no skill copies because skillTargets is empty, though the repository has skills to install.
Terminal window
syllabus project update [SOURCE | SOURCE:RULE ...] [options]

Preview each source’s move along its ref, with newer rule and skill versions, new rules and skills, level changes, and retirements, then apply it after you confirm, and regenerate guidance and installed skills like sync. With no arguments, every source is updated. SOURCE, such as team, limits the update to one source. SOURCE:RULE, such as team:techs/react/prefer-server-components, moves only that rule; nothing else changes, including new rules.

A source’s skills all come from one revision: the library release that supplies its library-wide files, or a non-library source’s commit. They move together, with those library-wide files or that commit, and syllabus project update SOURCE:SKILL, such as fabrica:skills/build-feature, is refused with a message asking you to update the whole source. --keep is for rules only. --exclude and --update-fork take a skill as SOURCE:SKILL the same way they take a rule.

OptionMeaning
--yesApply the previewed changes without asking. Required to apply changes without a terminal, or with --json.
--keep SOURCE:RULEPin this rule at its current version before applying the update, so it stays where it is. Repeat for several. Requires --reason. Refused for a skill, which can’t be pinned.
--exclude SOURCE:RULEExclude this new rule or skill before applying the update, so it doesn’t join. Repeat for several. Requires --reason. Refused for a skill that becomes required.
--update-fork SOURCE:RULEReplace your authored rule or skill for this replaced row with a fork of the source’s newest version, overwriting your edits, and set its exclusion’s basedOn to that version, or, for a non-library source, the newest commit. Repeat for several. Takes no --reason. See Replace a fork with the newest version.
--reason TEXTThe reason recorded with each pin that --keep writes and each exclusion that --exclude writes.

The preview lists, for each source:

ChangeMeaning
major, minor, or patchThe rule’s old and new versions, and the summary of every version in between.
newA rule the library added that your selectors reach, at its newest version.
retiredA rule the library retired, with its last version, its summary, and its replacement when there is one. When the library later retired the replacement too, the preview says so, and names the rule its replacements lead to, if any, instead of recommending the retired one. When your project already replaces the replacement with an authored rule, the preview names your rule instead of recommending the library’s. Applying the update drops it.
replacedA rule you replaced with an authored rule, with your rule’s path. When the exclusion records basedOn, the row appears whenever the library’s newest version is newer than basedOn, and lists every change after it, labeled with its version when there are several, also when a pin keeps the vendored copy where it is. Without basedOn, it compares with the version the project’s lock records instead: it appears whenever the library’s newest version is newer, also when a pin keeps the vendored copy where it is, and lists the changes since, which your rule may already have. Your rule doesn’t change unless you pass --update-fork; compare the two to decide whether it needs the same change.
pinnedA pinned rule that has a newer version. It doesn’t move; the preview shows the pin’s reason.

Skills from a library appear in the same list, by their skill IDs, with the same changes except pinned. When the source’s library-wide files move to a newer library release, every installed skill moves with them, and the preview lists each skill whose version changed. A skill that a moving skill newly uses appears as new, naming the skill that uses it, and a rule it newly cites appears as new, at its newest version, marked reference-only unless your selectors also reach it. A skill or cited rule that no installed skill needs any longer is dropped.

A skill whose level changes shows the move on its row, such as level: recommended -> required or level: offered -> recommended, with the library that set it. A move that installs a skill, such as offered -> required, or offered -> recommended when you include recommended, appears as new; a move that stops installing one, such as recommended -> offered when you don’t name it, appears as dropped. A skill whose version doesn’t change and that the move neither installs nor drops appears as level, so you can decide whether to include it. A skill that becomes required while your configuration excludes or forks it fails the update, naming the library and the exclusion to remove: required skills can’t be excluded.

Under each skill’s row, from a library or a non-library source, the preview flags every executable file that’s new or changed, on its own line, such as script added: scripts/setup.sh or script changed: scripts/verify.sh: a file whose executable bit is set at the new revision and that the update adds or changes, or a file whose executable bit changed. When it flags any, it prints one warning that Syllabus never runs these scripts but agents may, so read them before confirming. The warning doesn’t block the update.

A source whose ref names a branch or a tag lists its commit move, such as main: 3f9c2e1 -> 8a41d07, and warns when a tag now names a different commit than at the last update. A borrowed source lists its move the same way, under the library that borrows it, such as fabrica borrows mattpocock: main: 3f9c2e1 -> 8a41d07, since each project resolves a borrowed ref when it updates. For a non-library source, and for borrowed skills, the preview lists one row for each installed skill folder that differs between the recorded and new commits:

ChangeMeaning
changedA skill folder whose files differ, with the paths of the files that changed, including a file whose executable bit changed.
addedA new skill folder that the source’s selectors include, such as with "*".
removedAn installed skill whose folder no longer exists. Applying the update stops installing it.

See Update rules and skills for examples.

In a terminal, syllabus project update shows the preview and asks for confirmation. For each major change and retirement of a rule, you can choose to keep the rule at its current version instead; the command then asks for a reason and writes a pin. It asks you to confirm each major change, retirement, and level change of a skill too, but can’t keep a skill back on its own: to hold one, decline, then exclude or fork the skill, or set the source’s ref. For each new rule or skill that isn’t required, you can choose to add it or exclude it; excluding asks for a reason and writes an exclusion. For each replaced rule, including one whose vendored copy a pin keeps, it names the authored files that replacing your rule would replace or remove, and you can review the changes later, or replace your rule with a fork of the newest version, as --update-fork does. When your answers keep, exclude, or replace rules, it lists them before you confirm, with the files each replaced fork replaces and those it removes. The terminal shows the preview once; the result after you confirm lists the files the update changed. Before each wait on the sources, such as reading their releases or fetching the update after you confirm, a terminal also shows a short status line, such as Fetching the update..., on standard error; JSON output and output that isn’t a terminal never include it. Without a terminal, or with --json, it shows the preview and writes nothing unless you pass --yes. The update applies exactly the versions the preview showed, and writes its pins, exclusions, basedOn versions, and replaced forks in the same step that replaces vendor/ and generated/. When the preview has no rule or skill to move, add, or drop, and no shared files or commits to move, a terminal update applies without asking, as sync would, and when that changes no files, says, as a preview would, that no updates are available and no files were written.

A preview that writes nothing exits 0, because available updates aren’t an error; so does declining the confirmation. In JSON output, value.applied is false for a preview and true once the update is applied, and value.sources lists each source’s rules, each with its id, change, versions (from, to, and, where they apply, newest or lastVersion), summaries, summaryVersions (the version each summary belongs to, in the same order), and, where they apply, replacedBy (with replacementRetired: true and any currentReplacement when the library retired the replacement too, and replacementAuthoredRule when your project already replaces that replacement with an authored rule), authoredRule, basedOn, pin, and the decision (keep, exclude, or update-fork) that --keep, --exclude, --update-fork, or your answers recorded, with the reason of a pin or exclusion. A row’s change is one of the preview’s changes: major, minor, patch, new, retired, replaced, or pinned. For an update-fork decision, a row’s overwrites lists the authored files that the new fork replaces with its own copies, and removes those it deletes because the new fork doesn’t have them, relative to the Syllabus directory; both are empty for other rows. A replaced row’s from is the version the project’s lock records and to the newest version, or newest instead when a pin keeps the vendored copy; with basedOn, its summaries are the library’s changes after basedOn, and otherwise the changes since from, not the changes its authoredRule lacks. The added, changed, and removed lists are empty for a preview; once applied, they include config.yaml when the update wrote pins, exclusions, or basedOn versions, and the files of each fork it replaced.

In JSON output, each source in value.sources also has a skills list, whose rows have the same fields as its rules, plus level, with the levels from and to when the level changed, absent otherwise; and scripts, a list of the flagged scripts, each with its path, relative to the skill’s folder, and its change, added or changed, empty when the row flags none. A skill row’s change can also be dropped or level. A source whose ref names a branch or tag, and each entry in a source’s borrowed list, has commit, with the commits from and to, absent when it doesn’t move, and tagMoved: true when a tag now names a different commit. Rows for skills from a non-library source or a borrowed source have a change of added, changed, or removed, and files, the paths of the changed files relative to the skill’s folder, empty for other rows.

The preview doesn’t list changes to rules or skills you excluded without a replacement. --keep and pins in configuration never change which rules are included. When a rule you exclude or include by ID is retired, the entry no longer does anything; update warns about it so you can delete it, and applies the rest of the update. When the update, or a sync, would leave your authored rules in a rule group with no metadata, such as a fork of a retired rule, it writes the rule group’s metadata to rules/<group-id>/_group.yaml in the same step, lists the file as added, and warns that it did; see Keep the rule group of an authored rule.

An update of a whole source also moves its library-wide files, such as the manifest, rule group metadata, shared assets, and the library’s license files, to the newest library release, or the commit its ref names now, even when no rule moves; the preview shows it as Shared files: release 3 -> 4, and JSON output as the source’s sharedFiles, with the library releases from and to, which is absent when they don’t move. A source whose ref names a library release or a commit doesn’t move; change ref and run syllabus project update SOURCE to take the revision it names. A SOURCE:RULE update leaves the library-wide files where they are, unless a rule it moves comes from a newer library release: then they move to that library release, since a rule version can rely on the shared files its library release published. The source’s skills and skill levels always move with its library-wide files.

A source’s skills come from the revision that supplies its library-wide files, so they move with them. For each source the update names, the preview compares the skills the source installs once updated with the copies its source record holds, and lists, after the rules, one row for each skill that differs:

ChangeMeaning
changedA skill whose files differ, with the paths of the files that changed, including a file whose executable bit changed.
addedA skill the update starts installing, such as one that skills.include newly names.
removedAn installed skill the update stops installing, because skills.include no longer names it, or, with *, the new revision no longer has it.

Under each skill’s row, the preview flags every executable script the update adds or changes, on its own line, such as script added: scripts/setup.sh or script changed: scripts/run.sh: a file whose executable bit is set at the new revision and that the update adds, changes, or makes executable. Every executable file of an added skill is flagged as added. A script the update leaves as it is isn’t flagged, and neither is a file that isn’t executable at the new revision, even when it changed. A script is any file with its executable bit set, which Git records as mode 100755, wherever it is in the skill’s folder; a file in scripts/ without the bit isn’t one.

When the preview flags any script, it ends with one warning:

Warning: some skills in this update add or change scripts. Syllabus never runs them, but your agents may; read them before you confirm.

In a terminal, the warning follows the preview, before any question. It doesn’t block the update. A skill row is a change to confirm, so a terminal asks before applying it. An applied update, such as one with --yes, lists the same rows and warns some skills in this update added or changed scripts. Syllabus never runs them, but your agents may; read them before you commit the update.

In JSON output, each source in value.sources has a skills list, empty when no skill differs. Each row has its id, change, files, the changed paths relative to the skill’s folder for a changed row and empty for other rows, and scripts, a list of the flagged scripts, each with its path, relative to the skill’s folder, and its change, added or changed, empty when the row flags none. The warning is in value.warnings.

When your decisions keep a SOURCE:RULE update’s library-wide files at an older library release, its skills stay there too, and the preview shows the skills at that release. A skill that skills.include names and the update’s revision lacks fails the update, as installing it would. A source that uses ref doesn’t move, but its rows still list a skill that the update would add or remove because skills.include changed. Sync doesn’t flag scripts, because it never moves a source.

For a skill, --update-fork SOURCE:SKILL replaces the whole authored skill folder that the exclusion’s replacedBy names with what syllabus project add skill --from SOURCE:SKILL writes for the source’s newest version, or a non-library source’s newest commit, and removes files you added to the folder. The rest of this section describes rules; it applies to skills the same way.

--update-fork SOURCE:RULE replaces the authored rule that a replaced row names, the exclusion’s replacedBy, with a fork of the version the row lists changes up to: the library’s newest version, also when a pin keeps the vendored copy at an older one. The new fork is exactly what syllabus project add rule RULE --from SOURCE@VERSION writes, with its attribution, its assets, and the shared assets its links reach, placed at the path of the replacedBy rule, with links rewritten for that path. The update sets the exclusion’s basedOn to that version, adding it to a replacement you wrote yourself, and installs the fork, config.yaml, vendor/, and generated/ in one step, so an interrupted update restores or finishes them together.

Passing --update-fork is your consent to overwrite the authored rule: your edits to it are lost, so commit them first if you want to compare. The preview, human and JSON, lists every file it replaces and, separately, every file it removes, reading the new fork from the library to tell them apart. The authored rule’s asset directory is replaced as a whole, including files you added to it, and the shared assets the new version links to are copied into it, as a fork copies them. Nothing outside the rule and its asset directory changes, because each rule owns its asset directory and no other rule may link into it. A pin stays: it governs only the vendored copy.

--update-fork refuses, as an invalid-arguments usage error that exits 2, a rule the source doesn’t exclude with replacedBy, an unknown source, a --reason, and a rule that --keep or --exclude also names, before reading any source. It also refuses a rule the preview doesn’t list as replaced, because the library has nothing newer than the version your rule is based on or the update doesn’t include the rule, and a rule the library retired, which has no newest version to fork. Like the rest of the update, it fails with invalid-release-tag, writing nothing, when the library release tag that published the fork’s version names another commit than when the update previewed it, also when a pin or a SOURCE:RULE update keeps the vendored copy at an older version. Without --update-fork, --yes leaves every authored rule as it is.

If you merged the library’s changes into your authored rule by hand, record the version you merged by editing basedOn in .syllabus/config.yaml, so later updates list only newer changes.

See Update rules and skills for the workflow.

Terminal window
syllabus project build [options]

Regenerate generated/ from configuration, verified vendor snapshots, and authored rules and skills, and rewrite the installed skill folders in the repository’s skill targets. Build works offline and does not change recorded revisions. It also creates a missing managed Syllabus guide or refreshes an older, unedited guide alongside generated output, and adds or refreshes the Syllabus section of the repository’s AGENTS.md. A manually edited guide or section stops the build before it changes output. See Where agents start.

Accepts the shared options only.

Run sync first if the recorded repository, selectors, pins, or ref no longer match configuration, or if the vendor snapshots need repair. Build validates authored skills, and fails on an unfinished draft, as sync does.

Terminal window
syllabus project check [options]

Check generated guidance, installed skills, the managed Syllabus guide, and the Syllabus section of AGENTS.md without writing files or contacting repositories. Reports stale, missing, or unexpected output and invalid inputs, including an installed skill folder that differs from what sync would write, a folder that generated/installed-skills.json records but that’s missing, such as when .gitignore excludes the target folders so they were never committed, a stale installed-skills.json, an authored skill that cites a rule its library retired, a missing or outdated AGENTS.md section, and a source record in vendor/ that was changed outside syllabus project sync, which no command trusts until you restore it or take the source again; see the source record’s checksum. It warns about personal skills with the same name as installed skills, as sync does, without failing.

Accepts the shared options only.

Use check in CI to detect files that need regeneration. It uses recorded commits and rule and skill versions, and does not check whether remote tags or branches moved, or newer library releases exist. Success means the managed files agree with their inputs; it does not establish that application code follows the rules.

Terminal window
syllabus project list skills [options]

List the skills the repository’s sources offer, with their levels, so you can choose which to install. Lists never write files. The command reads each library source’s newest library release, or the revision its ref names now, and each non-library and borrowed source at the newest commit its ref names, and needs access to every configured and borrowed repository.

OptionMeaning
--type TYPEOnly skills of this type: playbook or review.
--applies-rules-from GROUPOnly skills whose syllabus-rules-used names a rule in this rule group, such as practices/architecture.
$ syllabus project list skills --applies-rules-from practices/architecture
NAME ID LEVEL SET BY VERSION STATUS
build-feature fabrica:skills/build-feature recommended fabrica release 7 1.3.0 installed
review-architecture fabrica:skills/review-architecture offered fabrica release 7 2.0.1 available
write-design-doc fabrica:skills/write-design-doc offered fabrica release 7 1.1.0 available
3 skills cite rules in practices/architecture. Skills that cite no rules, including those from non-library sources, show up only without --applies-rules-from.

Each row shows:

  • NAME: the install name, which is the skill’s own name unless the repository renames it.
  • ID: the skill’s project ID. A borrowed skill’s ID names the library and the borrowed source, such as fabrica:mattpocock:skills/engineering/to-spec.
  • LEVEL: required, recommended, or offered. Every skill from a non-library source is offered.
  • SET BY: the library and library release whose manifest set the level, or - for a non-library source.
  • VERSION: the skill’s newest version, or, for a skill without versions, the commit it would come from.
  • STATUS: installed for a skill the repository installs, whether included, required, or used by another installed skill, and available for one a source offers that the repository doesn’t install.

Authored skills aren’t listed. Skills are sorted by project ID. To install one, add it to that source’s skills.include and run syllabus project sync, or syllabus project update SOURCE when it isn’t in the recorded revision yet.

Skills don’t belong to rule groups, so --applies-rules-from finds skills by the rules they cite: a skill that cites rules in several rule groups appears under each of them, and a skill that cites no rules, including every skill from a non-library source, appears only without the option. Both options can be combined.

JSON output lists the skills in value.skills, each with its name, id, level, status (installed or available), and, where they apply, setBy, with the library and release, its type, and its version or commit.

syllabus library manages a library, a repository whose manifest publishes its authored rule groups and skills for projects to take. Library commands use the same .syllabus/ directory and config.yaml as project commands, so a repository can be both. Initialize from the repository root. Other library commands find that root from subdirectories. Use --directory PATH to target another library; see Working directories.

Terminal window
syllabus library init [options]

Add a library section, the manifest, to .syllabus/config.yaml, creating the configuration with schemaVersion: 2 and sources: {} when it doesn’t exist, and create the authored rules/ and skills/ directories, an authoring README, and a GitHub Actions workflow, without overwriting existing authored files. Optionally copy explicitly supplied license terms into the library and declare them in library.license. A configuration that already has a library section keeps it.

The workflow, .github/workflows/syllabus.yml, runs syllabus library check on every pull request, using the Syllabus version that created it. A development build, which no release published, writes a workflow that finds the latest release’s version when it runs and uses that instead. Either way, the workflow installs only that version’s archive, after verifying it against the release’s attested SHA256SUMS, and prints the version it installed in the job’s log and summary. Rerunning syllabus library init never changes an existing workflow; to move it to the Syllabus version you run, delete the workflow and run syllabus library init again. See Check changes in CI.

OptionMeaning
--directory PATHDirectory to operate in. Defaults to your working directory; repository discovery applies. Init must target the repository root.
--spdx EXPRESSIONLibrary SPDX expression. Must be supplied together with --license-file.
--license-file PATHUTF-8 license text to copy to LICENSE.md. Relative to your working directory, even with --directory.
--notice-file PATHOptional UTF-8 notice text to copy to NOTICE.md. Requires both license options. Relative to your working directory.
--non-interactiveAccepted; library init runs without prompts even when this flag is omitted.

If you omit the license options, the manifest leaves the license undeclared. Init does not infer license terms, create a Git repository, commit, or publish the library. A repository with a rule-library.yaml fails with a message that shows the manifest’s shape; see No backward compatibility.

Terminal window
syllabus library add group ID [options]

Create a rule group in .syllabus/rules/ID/, with _group.yaml metadata and an authoring README. ID is a rule group path such as practices/testing or techs/typescript. It writes the same files as syllabus project add group.

OptionMeaning
--directory PATHDirectory to operate in. Defaults to your working directory; repository discovery applies.
--name TEXTRequired. Rule group display name.
--description TEXTRequired. What the rule group covers.
--when-to-read TEXTRequired. When an agent should read the rule group.
--non-interactiveNever prompt. Supply all required inputs as flags.

Empty rule groups are valid. Existing rule group files are preserved.

Terminal window
syllabus library add rule ID [options]

Create a rule in an existing rule group, at .syllabus/rules/ID.md. ID includes the rule group path and rule name, such as techs/javascript/prefer-for-of, without .md.

OptionMeaning
--directory PATHDirectory to operate in. Defaults to your working directory; repository discovery applies.
--title TEXTRequired. Action-oriented rule title.
--when-to-read TEXTRequired. When an agent should read the rule.
--impact LEVELRequired. One of CRITICAL, HIGH, MEDIUM-HIGH, MEDIUM, LOW-MEDIUM, or LOW.
--impact-description TEXTRequired. The consequence the rule helps prevent.
--body-file PATHOptional UTF-8 Markdown body, without frontmatter. Relative paths start at your working directory. Omit to create an unfinished draft.
--non-interactiveNever prompt. Supply all required inputs as flags.

Create the rule group first with syllabus library add group; rule creation does not create missing rule groups. Without --body-file, complete the draft and remove its syllabus:draft marker before validation. Existing rules are not overwritten. After the first library release, the next steps include adding the new rule’s change note with syllabus library change. Like syllabus library check, it fails with instructions in a shallow clone, or a clone with change notes but no release tags, where it can’t tell whether the library has a library release.

Terminal window
syllabus library add skill NAME [options]

Create a skill in .syllabus/skills/NAME/. NAME follows the skill name grammar, such as fix-bug; the skill’s ID is skills/NAME. A new skill that the manifest’s levels don’t list is offered; to require or recommend it, add it to library.skills. See Skill levels.

OptionMeaning
--directory PATHDirectory to operate in. Defaults to your working directory; repository discovery applies.
--description TEXTRequired. What the skill does and when to use it, at most 1,024 characters.
--type TYPEOptional. playbook or review, recorded as syllabus-skill-type. See Skill types.
--non-interactiveNever prompt. Supply all required inputs as flags.

The command writes .syllabus/skills/NAME/SKILL.md with the name, description, and type in its frontmatter, and a body with the syllabus:draft marker. Complete the draft and remove the marker before validation; see Write a skill. An invalid name, or a description that’s blank or too long, fails before prompts with exit code 2. Existing skills are not overwritten, and the ID of a skill a library release retired can’t be reused. After the first library release, the next steps include adding the new skill’s change note with syllabus library change. Like syllabus library add rule, it fails with instructions in a shallow clone, or a clone with change notes but no release tags.

Terminal window
syllabus library change ID... [options]

Write a new change note for one or more rules and skills. ID is a rule ID, such as practices/testing/verify-retry-limits, or a skill ID, such as skills/build-feature; name several to cover related changes in one note. Before the first library release, rules and skills need no notes, so the command fails and says so.

OptionMeaning
--bump LEVELmajor, minor, or patch. Required for rules and skills that have a version. Not accepted for new or retired ones.
--summary TEXTRequired. One line describing the change for project maintainers.
--retireRecord that the rules or skills are retired; see Retired rules and Retired skills. A rule’s Markdown file and asset directory, or a skill’s folder, must already be gone.
--replaced-by IDOptional with --retire and a single ID: the rule that replaces a retired rule, or the skill that replaces a retired skill. If it isn’t a rule or skill in the library yet, the command warns; add it before the next library release.
--non-interactiveNever prompt. Supply all required inputs as flags.

The command writes a new file in .syllabus/changes/ with a unique name, such as .syllabus/changes/2026-09-29-verify-retry-limits-7f3a9c.yaml, and never edits or deletes existing notes. Rule IDs go in the note’s rules map and skill IDs in its skills map. A rule or skill without any version is recorded as new. It rejects an ID that isn’t a rule or skill in the library, unless --retire is supplied for one that has a version. It also rejects a rule that has a version but whose Markdown file and asset directory are unchanged since the latest library release, and a skill whose folder’s tracked files and their modes are unchanged: edit it first, then record the change. A rule or skill that was never published can’t be retired: delete it, and remove it from any pending note.

--replaced-by must be the same kind as the retired ID: a rule for a rule, a skill for a skill. Naming a rule to replace a skill, or the reverse, fails as a usage error with exit code 2, such as --replaced-by practices/testing/verify-retries is a rule, but skills/triage-bug is a skill. A skill can only be replaced by a skill.

See Choose a version change for picking LEVEL for a rule, and Choose a skill’s version change for a skill.

Terminal window
syllabus library check [options]

Validate the manifest, all rule groups and rules, all skills and their levels, change notes, supporting assets, and declared license and notice files. After the first library release, also check that every changed rule and skill has a change note. Does not change files.

OptionMeaning
--directory PATHDirectory to operate in. Defaults to your working directory; repository discovery applies.
--non-interactiveAccepted; library check does not prompt.

Reports rule group, rule, and skill counts, value.groupCount, value.ruleCount, and value.skillCount in JSON, such as Library is valid: 3 group(s), 6 rule(s), 3 skill(s)., and file-specific errors. The human output leaves out the skill count when the library has no skills. Empty rule groups are valid. Unfinished marked drafts fail. Undeclared licenses produce warnings; invalid declarations, missing declared files, and declared files inside a rule’s version (a rule’s Markdown file or its asset directory) fail validation. Library check validates the format, not writing quality or legal permissions. Use the authoring rubric to review guidance quality.

Check validates every skill folder and its SKILL.md: the folder’s name and contents, the frontmatter fields, the syllabus-skill-type, syllabus-rules-used, and syllabus-skills-used metadata keys, and links. It lists every skill problem it finds and fails with the code invalid-skill. It warns when a skill’s license differs from the library’s SPDX expression, and about a file in a skill folder that Git neither tracks nor ignores. Validation lists each check and its message.

Check also validates the manifest’s skill levels. It fails when a level lists a skill the library doesn’t have, a skill appears in two levels, or two listed skills, authored or borrowed, have the same name. To check borrowed skills, it reads each borrowed source at the ref the library’s configuration names, so it needs access to those repositories; see Name clashes. Levels are library-wide settings, so changing one needs no change note.

After the first library release, check compares each rule’s versioned content, meaning its Markdown file and its asset directory, and each skill’s, meaning the tracked files in its folder and their modes, with the latest release/<number> tag in the current branch’s history. Notes added since that tag are pending. Check fails when any of the following is true of a rule, and the same for a skill, with the message naming the skill:

  • A rule changed and no pending note names it. The error names the rule and its latest version, and gives the syllabus library change command to run.
  • A new rule has no pending note.
  • A published rule was deleted without a pending note that retires it.
  • A retired rule’s replacedBy isn’t a rule in the library, or a retired skill’s replacedBy isn’t a skill in it.
  • A rule reuses the ID of a retired rule.
  • A pending note names a rule that’s unchanged since the latest library release, so the note is stale.
  • A pending note names a rule that doesn’t exist and isn’t being retired.
  • A note is invalid, such as an unknown change, a blank summary, a skill ID under rules, or neither rules nor skills.

Check warns when a note that a library release already published was edited, because the edit has no effect. It also warns when such a note was deleted, because notes are never deleted; restore it. When a pending note retires a rule that an earlier library release named as a retired rule’s replacement, check warns about it: when the note names a replacement, that updates will point projects that still include the earlier rule on to it; otherwise, that they would be pointed at a retired rule, suggesting a replacement as replacedBy. A pending retirement whose replacedBy isn’t a rule in the library fails.

A skill is compared with the newest library release whose release record has a skills field; when a later library release lacks it, because an older Syllabus published it, check warns, as Skills in the release record describes.

This comparison needs the repository’s history and tags. Check fails with instructions in a shallow clone, with shallow-clone, and in a clone whose commit has change notes but no release/<number> tags, such as one made with git clone --no-tags, with missing-release-tags; fetch the tags with git fetch --tags. Check never fetches, so it refuses such a clone; syllabus library release fetches the remote’s release tags itself. In CI, check out with full history, such as fetch-depth: 0. Before the first library release, rules need no notes, and check validates everything else. A library outside a Git repository has no library releases.

When checks pass, the result previews the pending library release: each rule and skill, its change, and its current and next version, in one list sorted by ID. When no rule or skill change is pending and no library-wide file changed, it says there is nothing to publish, as syllabus library release does, except before the first library release, which always publishes the library-wide files, even without rules, so check says that instead. When library-wide files, such as the manifest, rule group metadata, shared assets, or license files, changed since the latest library release, the preview lists them too, so you know a library release would publish them even when no rule changed. JSON output includes this preview in value.pendingRelease, with its release number, a rules list, and a libraryFiles list of the changed library-wide files, which is empty before the first library release. Each rule has its id and change; from and to, its version before and after the library release (from is absent for a new rule); for a retired rule, lastVersion and any replacedBy instead; and summaries, one per change note that names it, in note order, as the release record and syllabus project update name them. value.pendingRelease.skills lists pending skill changes in the same form. syllabus library release reports the rules and skills it publishes in value.rules and value.skills the same way.

Terminal window
syllabus library release [options]

Publish the pending change notes for rules and skills, and any changes to library-wide files, as a library release. A library release that changes only skills publishes like any other. See Version your rules for the workflow.

OptionMeaning
--directory PATHDirectory to operate in. Defaults to your working directory; repository discovery applies.
--dry-runShow exactly what would be published, including the release notes, without creating a tag or a GitHub Release page.
--no-github-releaseSkip creating the GitHub Release page.
--non-interactiveAccepted; library release does not prompt.

Before publishing, the command fetches from the branch’s upstream remote. It refuses unless:

  • you’re on the remote’s default branch,
  • the branch matches the remote exactly, so the library release contains no unpushed commits and misses none, and
  • syllabus library check passes.

It then computes each changed rule’s and skill’s next version from the pending notes, using the largest change when several notes name the same rule or skill. The first library release gives every rule and skill version 1.0.0. The release record lists skills in its skills field, and the release notes list skill changes in a section of their own. It creates the annotated release/<number> tag on the current commit, with the release notes and release record as its message, and pushes it. No files change and nothing is committed. Finally, it creates the GitHub Release page.

--dry-run shows the repository, branch, commit, release number, each rule’s and skill’s change and versions, and the complete release notes.

If a run stops after pushing the tag, such as when the GitHub Release page can’t be created, run it again: it finds the tag on the current commit and creates what’s missing. A run that stopped before pushing leaves its tag only in your clone; the next run checks it against the library release the commit publishes after the remote’s latest one, pushes it only when its release notes and record match exactly, and otherwise refuses with release-tag-mismatch and asks you to delete it. A tag that a later Syllabus created in a newer release record format instead fails with unsupported-release-record: upgrade Syllabus and run it again rather than deleting the tag. The result reports separately whether the tag and the GitHub Release page were created. JSON output gives them as value.tagCreated and value.githubRelease.created, and describes the library release in value.release, value.rules, value.skills, value.libraryFiles, and value.notes. Like other optional values that aren’t lists, value.githubRelease is left out when the library release has no GitHub Release page: on a dry run, with syllabus library release --no-github-release, or for a repository that isn’t on GitHub.com. When the tag is published but the page can’t be created, the command fails with github-release-failed, and the JSON response still includes value, with tagCreated and without value.githubRelease, so a script can tell the tag is published and only the page is missing. With no pending notes and no library-wide changes, the command reports that there is nothing to publish, with value.release set to 0, value.rules, value.skills, and value.libraryFiles empty, and value.tag and value.notes left out, as optional values that don’t apply always are, and succeeds. A tool such as jq reads a left-out value as null.

The tag uses Git’s configured identity as its tagger, including the GIT_COMMITTER_* environment variables.

When the push fails, the command fails with push-failed and deletes the tag it created, so a rerun starts over. Syllabus doesn’t show what Git or the server printed, as Text from Git, servers, and the GitHub CLI explains; its message names the cause it recognizes in that text instead:

  • a repository rule or tag protection, such as a GitHub ruleset or a GitLab protected tag, with GitHub’s error code when the server gave GH006 or GH013,
  • the HTTPS server’s certificate or the SSH host key couldn’t be verified,
  • Git couldn’t connect to the server,
  • the server denied access, or authentication failed,
  • a hook on the server, such as a pre-receive or update hook, declined the tag,
  • the server refused the tag for a reason Syllabus doesn’t recognize, with GitHub’s error code when the server gave another one, such as GH001 for large files, or
  • the push failed before the server answered, such as when your pre-push hook stopped it.

To read the server’s own message, push a test tag that your tag rules cover yourself, such as release/0, which isn’t a library release tag:

Terminal window
git tag release/0
git push origin refs/tags/release/0
git tag --delete release/0

If that push succeeds, delete the test tag from the remote with git push origin --delete refs/tags/release/0.

syllabus library release refuses before creating anything, with exit code 1 and error.code in JSON output, when:

CodeRefusal
not-a-repositoryThe library isn’t the root of a Git repository.
shallow-cloneThe clone has partial history. Fetch it with git fetch --unshallow --tags, or check out with fetch-depth: 0 in CI.
missing-release-tagsThe commit has change notes, but neither the clone nor the remote has release/<number> tags. The command fetches the remote’s release tags before checking, so it doesn’t refuse a clone made with git clone --no-tags; that fetch adds them to the clone, also on a dry run.
detached-headHEAD isn’t on a branch.
no-upstreamThe branch has no upstream branch on a remote.
push-destinationThe remote pushes to several URLs, or to another repository than it fetches from.
fetch-failedGit couldn’t read or fetch from the remote. The message says whether the HTTPS server’s certificate or the SSH host key couldn’t be verified, Git couldn’t connect to the server, or the server denied access or has no such repository, when it recognizes one of them; run git fetch with the remote’s name to read Git’s message.
not-default-branchThe branch doesn’t track the remote’s default branch, or the remote reports none.
no-commitsThe library has no commits yet.
branch-differsThe branch has commits the remote lacks, the remote has commits the branch lacks, or the remote branch doesn’t exist.
remote-changedSomeone pushed to the branch, or changed a release tag, while the command ran.
release-tag-mismatchA release tag in the clone differs from the remote’s, exists only in the clone without being the next one on this commit, or doesn’t publish what this commit would.
invalid-release-tagA release tag reachable from the commit isn’t a valid annotated tag with a release record.
unsupported-release-recordA release tag uses a newer release record format. Upgrade Syllabus.
uncommitted-changesLibrary files differ from the checked-out commit.
change-notesChange notes don’t match the rule or skill changes, as syllabus library check reports; other check failures keep their own codes, such as invalid-skill.
version-limitA change would take a rule’s or skill’s version past the largest version number.
git-identityGit doesn’t know your name and email for the tag’s tagger.
github-cli-missingA GitHub Release page applies, but gh isn’t installed.
github-cli-signed-outA GitHub Release page applies, but gh isn’t signed in to GitHub.com.
release-too-largeThe release tag would exceed 8 MiB.
limit-exceededThe clone has more than 20,000 release tags, or gh printed more than its output limit.
changed-inputLibrary files changed while the command read them.

After it creates the tag, it can fail with:

CodeFailure
tag-failedGit couldn’t create the tag, such as when tag signing fails.
push-failedThe push failed, including when the remote refused it, with the cause described above. The command deletes the tag it created.
release-conflictSomeone else published the same release/<number> first.
github-cli-failedgh couldn’t run.
github-release-failedgh couldn’t look up or create the GitHub Release page. The message names the cause it recognizes: GitHub’s rate limit, gh signed out or its credentials refused, permission denied, or a repository GitHub can’t find. Otherwise, when the lookup failed, it says to run gh release view to read gh’s message; when creating the page failed, it suggests checks that change nothing, gh auth status and gh repo view, and running the command again, which creates the page with its generated notes only if it’s still missing. The tag is published; run the command again to create the page.

GitHub Release pages are created with the GitHub CLI, gh, for repositories on GitHub.com. Each library release gets one GitHub Release page on its release/<number> tag, with the release notes as its body. Before changing anything, syllabus library release checks that gh is installed and signed in, and refuses if it isn’t, unless you pass --no-github-release. syllabus library release --dry-run checks it too, and refuses the same way. Repositories hosted elsewhere get tags only. The page’s URL in the result is built from the repository and the tag, never taken from what gh printed.

Terminal window
syllabus --help
syllabus COMMAND --help
syllabus --version
syllabus --license

Use -h or --help to inspect command syntax and options. For example, run syllabus project --help to find project commands or syllabus library add rule --help for rule authoring options.

OptionMeaning
-h, --helpShow help for the selected command.
-v, --versionAt the root, print the executable’s version.
--licenseAt the root, print the full embedded MIT license and copyright notice.
--jsonReturn help, version, or license text in value.text inside a JSON response.

Running syllabus or a command group such as syllabus library without a subcommand displays help. The syllabus project, syllabus project add, syllabus project list, syllabus library, and syllabus library add groups organize commands; they do not perform operations themselves. Help never prompts or writes files.

The root --version flag prints the tool version. To choose which versions of a library’s rules and skills to take, see Choose versions.

In Git repositories, run syllabus project init or syllabus library init from the repository root. Initialization from a subdirectory fails without writing files. This also applies to the target of syllabus library init --directory.

Other commands find the nearest ancestor containing a .git directory or file, including worktrees and submodules. Project and library commands both use that root’s .syllabus/config.yaml; library commands also need its library section. They stop at that repository boundary, even if its configuration is missing. A nested .syllabus/ does not override the root’s configuration.

Outside Git, commands use the current directory, or the directory selected by --directory for library commands. They do not search parent directories for configuration. Custom project configuration locations are not supported.

Relative input paths such as --body-file, --license-file, and --notice-file resolve from the directory where you run the command. Paths inside project configuration remain relative to .syllabus/.

Every command accepts --json, which prints one JSON response and disables prompts. Every command also accepts -h / --help.

Authoring commands accept --non-interactive. Without it, commands can prompt for missing required metadata or source selections when running in a terminal. With --non-interactive, --json, or no terminal, missing required inputs cause an error. Positional arguments such as ID and ALIAS must always be supplied.

Invalid interactive answers repeat the same question while retaining earlier answers; for a choice, such as [yes/no], the error lists the valid answers. Without a terminal, or with --json or --non-interactive, a missing required input fails with exit code 2 and names the flag to pass, such as --summary. Explicit flags are validated without prompting for replacement values. Existing rule groups, rules, skills, and source names fail before prompts.

Both init commands, both check commands, syllabus project list skills, and syllabus library release run without prompts. Of these, syllabus library check and syllabus library release accept --non-interactive; syllabus project check, syllabus project sync, syllabus project update, syllabus project build, and syllabus project list skills do not need or accept it.

String options accept one value and cannot be repeated, except --rules, --skills, --skill-targets, --keep, --exclude, and --update-fork, which accept repeated values. For a value beginning with -, use the equals form, such as --description='-prefixed text'.

Scaffolding commands validate paths and detect collisions before writing. They preserve existing authored files and roll back failed creation attempts. They do not publish content or convert arbitrary third-party material. For the authoring workflow, see Set up your first project or Create your first library.

Human-readable output is the default. Add --json for scripts:

Terminal window
syllabus project check --json

JSON mode writes one response to stdout:

FieldMeaning
oktrue for success; false for failure or an out-of-date project check.
valueThe command’s result, when available. An out-of-date check still includes its report here.
errorOn failure, an object with kind (usage, validation, operation, cancelled, or out-of-date for a stale project check) and message, plus location when available. Domain failures include a stable code, such as needs-init, missing-group, guide-edited, or agents-section-edited, and every usage error has the code invalid-arguments.
warningsPresent only when there is a warning about how the command found the project, such as a project that still keeps its files in .code-rules/; see Upgrade from Code Rules. It accompanies success and failure alike. Human output prints these warnings to stderr.

Every list in a value is always present, empty when there’s nothing to report, including warnings. Enumerated values, such as statuses, kinds, changes, and codes, are kebab-case. Only optional values that aren’t lists, such as a ref or a githubRelease object, are left out when they don’t apply.

Project check returns value.status as up-to-date or out-of-date, and a value.problems list. Each problem has a kind (missing-file, stale-contents, unexpected-file, outdated-readme, or outdated-agents-section), path, message, and nextSteps, a list of repair steps in the same form as authoring results use. Paths are relative to the Syllabus directory, except paths in a skill target, which are relative to the project root, so the AGENTS.md problem’s path is ../AGENTS.md. Generated guidance, installed skills, the managed Syllabus guide, and the AGENTS.md section must all be current for success.

Authoring results include value.nextSteps, an ordered list of steps, each an instruction and its copyable commands. Human output shows those steps after initialization and rule or rule group creation.

Sync, update, and build report added, changed, and removed file lists, with paths relative to the Syllabus directory, or to generated/ for build, except paths in a skill target, such as .claude/skills/build-feature/SKILL.md, which are relative to the project root. Authoring commands, such as init, add source, add group, add rule, add skill, and library change, report the absolute paths of the files they created in added and of those they modified, such as config.yaml, in changed. Their human output lists the same files under Added: and Changed:, relative to the working directory when they’re inside it. Update also reports each source’s rule changes in value.sources. Sync and update list their warnings, such as for an unreleased ref, an entry naming a retired rule, a rename, a personal skill with the same name, or authored rule group metadata they wrote, in value.warnings, which is empty when there are none. When they refresh the managed Syllabus guide, value.guide reports its path relative to the Syllabus directory and whether it was created. When they change the Syllabus section of AGENTS.md, value.agents reports the file’s path relative to the repository root and an action: created for a new file, added for a section added to an existing file, or updated. Init lists AGENTS.md among its added or changed files instead. Help, version, and license return their text in value.text.

In human mode, operational errors go to stderr. An out-of-date check prints its status, problems, and next steps on stdout. Human output, prompts, and errors show control characters, which could make a terminal clear or rewrite what it displays, as visible escapes: ESC as \x1b, other controls from \x00 to \x1f and \x7f the same way, C1 controls as \u0080 to \u009f, and bytes that aren’t valid UTF-8 as \xNN. Libraries supply much of that text, such as change summaries and rule IDs. JSON output encodes text as JSON strings instead. In JSON mode, errors go in the response; stderr is reserved for failures writing that response. Unreleased preview builds also print a non-production warning with their source commit to stderr before every command, including help, version, and JSON commands. JSON output on stdout is unchanged. See Test a PR build in the contributing guide.

Text from Git, servers, and the GitHub CLI

Section titled “Text from Git, servers, and the GitHub CLI”

Syllabus never shows text that Git, a Git server, or the GitHub CLI, gh, produced: not in human output, JSON output, or errors. That includes Git’s error messages, the lines a server or its hooks print, the reason in Git’s ! [remote rejected] line, the default branch a server advertises, and everything gh prints. Such text can hold anything, including credentials Syllabus can’t know about, such as one a Git credential helper, GIT_ASKPASS, or http.extraHeader supplies, so Syllabus doesn’t try to find and remove them. It reads the text privately to choose one of its own fixed messages and error codes, which name the likely cause and what to check. A message may name a GitHub error code it recognizes, such as GH013, because those are a fixed set. To read the original text, run the Git or gh command yourself; the message or this reference says which.

Syllabus also never shows credentials in a URL it displays. A repository address it shows, such as a library’s remote in syllabus library release, has any password, query, and fragment removed, and its user name too unless it’s an SSH user name, which names an account. An address it can’t parse is replaced by a note, including one with an authority but no scheme, such as //host/rules, and a user@host:path address with a colon before its @, which could start a password. The GitHub Release page URL is built from the repository and the tag, not taken from gh.

Syllabus shows some values that Git computes, after checking their form: commit and object IDs, which must be hexadecimal object IDs; commit counts, which must be nonnegative integers; and object types, which must be one of Git’s four. A value in any other form fails with code git-failed and a fixed message instead.

Syllabus shows as they are:

  • names you chose locally, such as branch and remote names,
  • the repository addresses in project configuration, which can’t contain credentials, and
  • library content, such as rule files, change notes, rule group descriptions, and release records.

Human output still shows control characters in that text as visible escapes, as Command output describes.

CodeMeaning
0Success.
1An operation failed, inputs are invalid, or a project check found stale output.
2Invalid command usage, such as an unknown command, unsupported option, missing required CLI input, or options the command can’t combine, such as syllabus library change --bump for a new rule, or a syllabus project update decision the preview doesn’t offer, such as --keep for a rule the update doesn’t move.
130The command was interrupted, such as with Ctrl-C, including at a prompt. It says it was cancelled; a command interrupted before writing files writes none, and one interrupted while replacing files is recovered by the next command, as Sync and recovery describes. JSON output reports error.kind as cancelled.

Usage errors point to the relevant help page. Operational errors identify the affected file or rule when available and describe the problem. A failure of syllabus project sync or syllabus project update that wrote nothing, including an update’s refusal of its arguments after the command line is parsed, ends with No files were written., or, when the command first recovered an interrupted earlier command, which restores or finishes that command’s files, says so instead, as an update’s preview and a declined or interrupted update do too, and names the source, as in source team:, when the problem doesn’t already. An interrupted one says cancelled; no files were written, or, after such a recovery, that it was cancelled and what the recovery did. For repair commands and interrupted updates, see Sync and recovery.

Only the project and library command trees are supported. Earlier command paths now fail with an unknown-command error. Update existing scripts to use the scoped commands below. Use -h or --help for help; there is no help subcommand.

Earlier commandCanonical command
syllabus initsyllabus project init
syllabus add sourcesyllabus project add source
syllabus local add groupsyllabus project add group
syllabus local add rulesyllabus project add rule
syllabus syncsyllabus project sync
syllabus buildsyllabus project build
syllabus checksyllabus project check

syllabus project add library and syllabus project add repository are replaced by syllabus project add source, which adds either kind of source. Library command paths are unchanged. For configuration fields that changed, see No backward compatibility.

Fabrica SyllabusMIT licensedPrivacy

Built with Fabrica.