Skip to content

chant init

llms.txt lists every page for an agent

Scaffold a new chant project with the standard directory structure and configuration files.

chant init <path> --lexicon <name> [flags]
chant init <path> --from <repo>@<ref>[#<member>] [--param <name>=<value>]... [--force]
chant init <path> --from <dir>[#<member>] [--param <name>=<value>]... [--force]
FlagDescription
-d, --lexicon <name>Select lexicon (required)
-t, --template <name>Init template (e.g. node-pipeline, docker-build). Also writes the lineage lock
--from <repo>@<ref>[#<member>]Copy a template repository instead of a lexicon’s templates (see below)
--from <dir>[#<member>]Copy a template directory on disk (see From a directory)
--param <name>=<value>With --from, set a parameter the template declares. Repeatable (see Template parameters)
--forceOverwrite existing files

chant init creates a new project at the specified path. It writes the directory layout, the TypeScript and lint configuration, and a sample resource file.

FilePurpose
src/ sample resourcesStarter resource definitions
chant.config.tsProject configuration (lexicons, strict lint preset)
tsconfig.jsonTypeScript compiler options
package.jsonDependencies
.gitignoreIgnores generated files

With --force into a directory that already has a chant.config.ts or chant.config.json, init reads that config’s lexicons to check whether the lexicon is declared by path. If it is, package.json gets no dependency for it and the summary says where it loads from. The config is read without running it; when its lexicons cannot be read that way, init prints a note and treats the lexicon as a package. See Commands that read chant.config.ts without running it.

Terminal window
chant init my-infra --lexicon aws
cd my-infra
chant build
# Overwrite existing files
chant init my-infra --lexicon aws --force

With --template, init also writes .chant/workspace.lock.json. The lock records the template (lexicon:<lexicon>/<template>), the lexicon package and chant versions that rendered it, and the hash of every file init wrote, so a later upgrade can tell which files you have edited. The Lineage Lock page describes the format.

Without --template, chant init writes no lock.

--from copies a template repository at a ref into the target directory and records where it came from:

Terminal window
chant init my-service --from acme/starter@v1.4.0#service
chant init my-service --from https://git.example.com/acme/starter.git@main
chant init my-service --from ../starter@4624a0b6ca83bf3a2776b2d29d269e989d8b560f
PartMeaning
<repo>A git URL, git@host:path, a local repository path, or owner/name for a GitHub repository
<ref>A tag, branch or commit. Required
#<member>Copy only this directory of the repository. Its contents land at the project root

chant fetches the one commit with git fetch --depth 1, the only network step in chant init, listed on the network egress page. It copies every tracked file under the chosen directory, keeping the executable bit, and skips submodules, symbolic links and the template’s own lineage lock with a warning. It then writes .chant/workspace.lock.json with the commit and tree as the content address.

The template brings its own chant.config.ts and lexicons, so --from cannot be combined with --lexicon or --template. A non-empty directory needs --force, and files that already exist are kept and left out of the lineage. A directory that already has a lock is refused. --from does not install dependencies.

When the template is already on disk and its repository cannot be reached, --from takes the directory instead. A host that bundles a template as plain files, without .git, instantiates it this way:

Terminal window
chant init my-app --from /opt/studio/tool#template --param name=billing
chant init my-app --from ../starter#service

chant reads --from as a directory when it has no @ and names a directory on disk. A directory whose name contains @ is still a directory when it exists, since what is on disk wins. Anything else is read as <repo>@<ref>, and a value with no @ that names no directory is refused. #<member> picks a directory inside it, as in the git form.

Inside a git checkout, chant copies the files git would commit and leaves out what .gitignore ignores. Untracked files are copied too. Outside one, or when git is not installed, it copies every file. Either way it skips symbolic links, node_modules, .git and the template’s own lineage lock, and keeps the executable bit. Parameters work the same way, so the same directory gives the same files as the git form of the same tree. This form reaches no network.

The lock’s source has type dir, with the directory and member, and its address is the digest of the copied files alone, with no commit or tree. An absolute path is recorded as given, and a relative one is recorded relative to the project. A directory has no history, so chant workspace upgrade can only rebuild the merge base while that directory still holds the files the project was made from.

A template can declare parameters in a chant.template.json at its root, with the files each one is substituted into (see Template manifest for the format). --param sets one and can be repeated. This names the reference workspace’s app:

Terminal window
chant init ws --from INTENTIUS/chant@<tag>#reference-workspace --param name="Untitled app"

In each listed file, {{chant:name}} becomes Untitled app. Parameters that --param does not set take their defaults. The text up to the first = is the name, and giving one name twice is refused. chant also refuses a name the template does not declare, and its message lists the names the template does declare. A declared parameter with no value and no default is refused, and so is a value that does not match the parameter’s pattern. Nothing is written until every value checks.

The values used, defaults included, are recorded under parameters in .chant/workspace.lock.json. chant workspace upgrade substitutes the same values into the template’s newer version and into the merge base, so a file that carries a value upgrades like any other. A template without a manifest takes no --param.

A decision record in the template may pin one of the substituted files by hash (see asset pins). Init re-pins it to the file as the copy has it, so the pin holds in the copy, and lists the record under repinned in the lock. A pin that did not hold in the template is left as it was.

The manifest sits at the root of the directory a project is made from, which is the repository root or the #<member> directory. chant reads it as JSON and runs no template code to find it. Like .chant/migrations/, the manifest is template metadata. It is not copied into the project, and the lock does not list it. The reference workspace declares one parameter in reference-workspace/chant.template.json.

{
"parameters": {
"name": {
"type": "string",
"default": "Reference app",
"pattern": "^[A-Za-z0-9][A-Za-z0-9 ._-]*$",
"description": "The app's display name: the home page's title and heading, and the screen spec's"
}
},
"files": ["app/src/server.mjs", "design/screens/home.json", "design/screens/home.svg"]
}
FieldMeaning
parametersThe declared parameters, keyed by name. A name is a letter or _ followed by letters, digits or _.
parameters.<name>.type"string", the only type so far.
parameters.<name>.defaultOptional. The value used when --param does not set one. A parameter with no default must be given at init.
parameters.<name>.patternOptional. A regular expression every value must match, the default included. Values are inserted as written, so a pattern is how a template keeps a value from breaking the file around it, for example a quote inside a JSON string.
parameters.<name>.hostBoundOptional. Marks a value that differs per host, such as the domain a hosted service serves the app on. The value goes into parameters like any other, and the lock’s hostBound lists the files that carry it, so chant workspace export can switch it and import can switch it back (#2552).
parameters.<name>.descriptionOptional. For people reading the manifest.
filesThe paths, relative to the manifest’s directory, whose placeholders are substituted. Each must be a file in the template. Files not listed are copied byte for byte.
$commentOptional. Ignored.

Any other key is refused.

A listed file marks each place a value goes with {{chant:<name>}}. The chant: prefix keeps the placeholder apart from the syntax a template file may already hold. TypeScript template literals (${x}) and workflow expressions (${{ x }}) are left alone, and so is Handlebars or Mustache ({{x}}). In the reference workspace, app/src/server.mjs holds export const APP_NAME = "{{chant:name}}";.

There is no escape syntax. A file that needs the literal text {{chant: can be left out of files. A placeholder naming an undeclared parameter is an error in the template, reported at init. Substitution is one pass, so a value that looks like a placeholder is inserted as written.

When the template’s newer version declares a parameter the lock does not have, chant workspace upgrade uses its default and records it. If that parameter has no default, the upgrade is refused. A recorded parameter the newer version no longer declares is dropped from the lock.

To scaffold a new lexicon plugin project (for lexicon authors), use:

chant init lexicon <name> [path] [--force]

This creates a lexicon plugin project with a stub for every required lifecycle method. See init lexicon.

Terminal window
# Scaffold a new lexicon
chant init lexicon k8s
# Scaffold at a custom path
chant init lexicon gcp ./my-gcp-lexicon
CodeMeaning
0Project created successfully
1Error (target path is not empty or could not be created)

chant