psy.config.ts
Every field, its type, and what it does.
import { defineConfig } from "@psy/config";
export default defineConfig({
source: ["./src"],
});psy.config.ts controls sources, outputs, frameworks, adapters, formatting,
linting, and diagnostic policy.
It deliberately cannot inject symbols, activate declaration keywords, modify
language semantics, or change the grammar. Those come from Psy source alone, which
is what lets you read a .psy file without auditing a TypeScript file first.
Fields
| Field | Type | Meaning |
|---|---|---|
source | string[] | files or directories of .psy sources, relative to the root |
frameworks | Framework[] | domain validators, run after resolution |
targets | Target[] | generators, run by psy build |
format | FormatOptions | indent, printWidth, trailingCommas |
lint | LintOptions | per-rule settings |
diagnostics | DiagnosticsOptions | warningsAsErrors, maxWarnings |
artifacts | ArtifactOptions | what a build stamps into its own output |
format
format: {
indent: 4, // spaces per level
printWidth: 100, // width at which import lists break
trailingCommas: true,
}lint
lint: {
noUnusedImports: true,
noUnusedSpecs: "error",
noUnusedConsts: true,
noUnusedUse: true,
specNaming: true,
propertyNaming: "off",
keywordNaming: false,
}Accepted settings: true, false, "off", "info", "warning", "error".
Rules and their codes are in Diagnostic Codes.
diagnostics
diagnostics: {
warningsAsErrors: true,
maxWarnings: 0,
}artifacts
All three are off by default. Generated files belong to your project, not to Psy, so nothing announces itself unless you ask.
artifacts: {
header: true,
manifest: true,
index: true,
}| Field | Default | Effect |
|---|---|---|
header | false | a comment naming Psy and the source module, in formats that have comments |
manifest | false | .psy-manifest, listing every path a target wrote |
index | false | aggregate index documents, such as the Claude adapter's psy-generated.md |
<!-- Generated by Psy from src/skills/ReviewDiff.psy. Do not edit by hand; edit the .psy source and run `psy build`. -->JSON never carries a header, because JSON has no comment syntax. A target may
override index for itself — claude({ index: false }) opts out even when the
project opts in.
Pruning depends on these
psy build removes artifacts it previously wrote but no longer generates,
identifying them by a path in the .psy-manifest or a file still carrying the
header.
With neither enabled it can prove nothing, so it does not prune, and says so:
note: stale artifacts were not removed; a build can only remove what it can
prove it wrote. Set `artifacts.manifest` or `artifacts.header` in psy.config.ts
to enable pruning.Target options
Adapter targets take their own options. The shipped ones:
claude(...)— Claude Code agents, skills, commands and plugins.json(...),yaml(...),toml(...)— data output.
Discovery
Psy looks for, in order:
psy.config.ts
psy.config.mts
psy.config.mjs
psy.config.jsin the project root, or at the path given by --config. TypeScript config files
are imported directly; modern Node strips the types.
With no config file, Psy compiles . with no frameworks and no targets — enough
to check and format.
A config file that cannot be loaded is PSY9001.
Overriding sources for one command
psy check spec
psy format --check buildA positional path overrides source for that command only. A positional argument
that does not name an existing file or directory is treated as a command target
instead — which is how psy inspect BuildPsy works.
Psy's own config
For reference, the real thing:
import { defineConfig } from "@psy/config";
import { morphic } from "@psy/morphic";
import { claude } from "@psy/adapter-claude";
import { json, yaml } from "@psy/adapter-data";
export default defineConfig({
source: ["./psy-spec", "./psy-build", "./examples", "./fixtures/valid"],
frameworks: [morphic()],
targets: [
// The Morphic definitions become real Claude Code agents and skills.
claude({
output: ".claude",
}),
// The language specification, resolved, as one machine-readable document.
json({
output: "generated",
mode: "single",
file: "psy-spec.json",
modules: ["spec/"],
metadata: true,
}),
// The same definitions the Claude adapter consumes, as plain data.
yaml({
output: "generated/morphic",
modules: ["build/"],
}),
],
format: {
indent: 4,
printWidth: 100,
trailingCommas: true,
},
lint: {
noUnusedImports: true,
noUnusedSpecs: true,
noUnusedConsts: true,
noUnusedUse: true,
specNaming: true,
propertyNaming: true,
keywordNaming: true,
},
diagnostics: {
warningsAsErrors: true,
},
});