@ariestools/toolchain
v10.0.5
Published
Unified TypeScript toolchain for Aries Tools — build, lint, test, and publish with the xy (stable) and xyex (experimental) CLIs
Readme
@ariestools/toolchain
Unified TypeScript toolchain for Aries Tools — build, lint, test, and publish with auto-detected package manager and React support
Install
pnpm add -D @ariestools/toolchain
# or: npm install -D @ariestools/toolchain
# or: yarn add -D @ariestools/toolchainRequires Node.js ≥ 22. pnpm is fully supported; npm / yarn / bun support is experimental.
Hello world
{
"scripts": {
"build": "xy build",
"compile": "xy compile",
"lint": "xy lint",
"test": "xy test",
"check": "xy check"
}
}// xy.config.ts
import type { XyConfig } from '@ariestools/toolchain'
const config: XyConfig = {}
export default config// eslint.config.ts
import { recommendedConfig } from '@ariestools/eslint-config-flat'
export default [...recommendedConfig({ tier: 3, isTypeChecked: true })]// tsconfig.json
{ "extends": "@ariestools/tsconfig", "include": ["src"] }Run xy from the repository root. Prefer xy in CI; xyex is labs. Catalog: pnpm xy --stability. Profiles: docs/profiles.md. Config fields: docs/xy-config.md.
xy vs xyex
| Binary | Channel |
|--------|---------|
| xy | Stable public CLI |
| xyex | Experimental supersets (may change without a major bump) |
Both are installed with this package. Prefer xy in CI. See the repo stability policy.
Agent skills
Long-form agent docs: ariestools/ariestools-skills. Install with pnpm xy skills lint --fix or npx skills add ariestools/ariestools-skills --all.
The package also ships the versioned XY plan-manifest schema for .xy/plan.json. It indexes white/yellow/green papers, other governing Markdown, strategic roadmap phases, and foreign keys into xy work without copying tracker state. See the repository plan-manifest convention.
Plan manifests (xy plan, experimental)
pnpm xy plan init --dry-run
pnpm xy plan init
pnpm xy plan init --project-id owner/repository --summary "One-sentence orientation."
pnpm xy plan init --json
pnpm xy plan lint
pnpm xy plan lint --fix
pnpm xy plan lint --rules
pnpm xy plan lint --jsonThe initializer discovers conventional tracked Markdown from the Git root and exclusively creates .xy/plan.json with status: bootstrap. Suggestions remain non-governing until reviewed: it does not infer authority, lifecycle, roadmap phases or gates, work IDs, or green-paper non-applicability. It never initializes xy work and refuses to overwrite an existing manifest. xyex plan init avoids the experimental warning until promotion.
The linter applies a deliberately narrow, case-sensitive repository layout. The root must contain AGENTS.md, CLAUDE.md, CHANGELOG.md, CONTRIBUTING.md, and README.md. papers/README.md, papers/WHITE-PAPER.md, and papers/YELLOW-PAPER.md are required, papers/GREEN-PAPER.md is optional, and every other leaf entry beneath papers/ is unexpected. Unexpected-paper detection traverses nested directories recursively. docs/README.md and docs/ROADMAP.md are required, but all other docs/ content is permitted for now. notes/README.md is required; beyond that index, notes/ is an unlinted freeform destination for irregular files at any depth.
With --fix, a missing required file is created from its built-in template only when the destination does not already exist. Existing files, directories, symlinks, and other entries are never overwritten. Every unexpected regular file under papers/, including a nested file, is flattened directly into notes/ using its basename: papers/archive/OLD.md targets notes/OLD.md, discarding its source-directory provenance. The move copies and byte-verifies the file before removing the source. If the base destination exists, the fixer tries OLD Copy.md, then OLD Copy 2.md, and so on; it never overwrites. Unexpected paper symlinks and special files remain findings for manual resolution. Root documentation rules require CLAUDE.md to contain @AGENTS.md, forbid Claude @path imports in AGENTS.md, and require root README.md and AGENTS.md to Markdown-link to papers/ and docs/. --fix prepends the Claude import and appends missing folder links; it does not rewrite AGENTS.md @imports. The command does not require, read, or validate .xy/plan.json. Use --rules to inspect the catalog and --json for the standard machine-readable result envelope. Prefer xyex plan lint while the parent command is experimental. Package consumer READMEs are enforced by xy repo lint.
Create a new repo (xy repo init)
Scaffold a toolchain-ready repo. Omit the template to answer setup questions (monorepo, React, package manager, skills tier).
# Interactive (recommended for new projects)
npx --package=@ariestools/toolchain xy repo init
# Explicit template + name (defaults: pnpm, @ariestools, MIT, xy skills)
pnpm xy repo init cli my-cli --pm pnpm --yes
# Single-package React tooling
pnpm xy repo init cli my-app --no-monorepo --react --skills-tier xl1Useful flags:
| Flag | Meaning |
|---|---|
| --pm | pnpm (default) / yarn / npm / bun (non-pnpm is experimental) |
| --scope | npm scope (default @ariestools; none for unscoped) |
| --license | SPDX id (default MIT, not the toolchain LGPL) |
| --monorepo / --no-monorepo | Workspace layout vs single package |
| --react / --no-react | eslint-config-react-flat + tsconfig-react vs flat |
| --skills-tier | none / xy / xyo / xl1 (default xy) |
| --skills-optional | With xl1, also install xl1-scaffold / xl1-build |
| --skip-install / --skip-git | Skip post-scaffold steps |
| -y / --yes | Accept wizard defaults (project name still required) |
Security (xy secure)
xy secure is an overview: it runs both audits, prints one summary line each, and points at the subcommands for detail.
pnpm xy secure # summary of both
pnpm xy secure deps # direct dependency age + weekly downloads
pnpm xy secure dependabot # GitHub Dependabot security alertsxy secure deps is the dependency-hygiene audit (age, download volume) — it does not look at CVEs.
xy secure dependabot reads GitHub's Dependabot alerts through the gh CLI, so it needs gh installed and authenticated; the standard repo scope is enough. Alerts are enabled per repository, and a repo with the feature switched off is reported as such rather than treated as an error.
| Flag | Behavior |
|---|---|
| --org <name> | Report every repository in a GitHub org instead of the current repo |
| --scope | all (default) / runtime / development |
| --relationship | all (default) / direct / transitive |
| --state | open (default) / fixed / dismissed / all |
| --summary | Summary line and counts only, no per-alert table |
| --rules | List the dependabot rules and their effective levels |
Findings map onto the rule system, one rule per severity plus a check that alerts are switched on at all. Because alerts are overwhelmingly transitive lockfile findings, nothing fails a build by default — critical and high warn, medium and low are off. Raise them to gate:
const config: XyConfig = {
commands: {
dependabot: {
rules: {
'dependabot.critical': 'error',
'dependabot.medium': 'warn',
},
},
},
}Console output caps at 50 alerts with a count of what was held back; --json always carries every finding.
Enforcing that alerts are on
Alerts are only useful where the feature is switched on, so xy repo lint carries a companion policy rule:
pnpm xy repo lint # warns: Dependabot alerts are disabled for this repository
pnpm xy repo lint --fix # turns them onrepo.dependabot-enabled warns by default and is fixable. The fix needs admin on the repository; without it the rule reports the finding as still open rather than claiming a fix it did not make. The check costs one gh subprocess and stays silent when gh is missing, unauthenticated, or the remote is not GitHub — an environment it cannot ask about is not a repository finding.
Rules (xy --rules)
Every rule the toolchain enforces has a namespaced id, a level (error / warn / off), and a config entry at commands.<command>.rules["<id>"] in xy.config.ts. To list them:
# every xy-native rule, grouped by command
pnpm xy --rules
# machine-readable
pnpm xy --rules --jsonAdd --rules to any individual command to see just that command's rules and the levels currently in effect for this repo:
pnpm xy deplint --rules
pnpm xy publint --rules
pnpm xy repo lint --rules
pnpm xy clean --rulesThe Level column shows the default; when a repo overrides it, the effective level follows in parentheses (warn (error)). Fix marks rules that --fix can resolve.
pnpm xy --rules deliberately omits the ~600 upstream ESLint rules — list those separately with pnpm xy lint --rules. It also omits the aggregate xy check and xy fix views, which re-list rules already shown under their owning command; pnpm xy check --rules and pnpm xy fix --rules show what those commands run.
Rule levels accept several config forms:
const config: XyConfig = {
commands: {
repoLint: {
rules: {
'repo.spec-layout': 'off',
'repo.pnpm-no-overrides': 'error',
'repo.engines-lts': { enabled: false },
},
},
},
}An unknown rule id is a hard error rather than a silent no-op, so a typo surfaces immediately.
Extending package-compile / package-build / package-recompile
To add steps to a per-package build phase, override the matching script in your package's package.json and chain to the toolchain default via the matching -only bin:
{
"scripts": {
"package-compile": "package-compile-only && tsx scripts/generate-types.ts"
}
}Do not call pnpm package-compile / yarn package-compile / pnpm run package-compile from the override — those re-enter the same npm script and recurse infinitely. The -only bin variants exist precisely so your override can invoke the toolchain default once and add work around it.
Available -only variants: package-compile-only, package-build-only, package-recompile-only.
Lint execution modes
xy lint names its execution implementations by mechanism. Select one with
--mode:
| Mode | Behavior |
|---|---|
| package-workers | Default. Persistent worker processes lint packages in parallel. Each worker owns its own TypeScript Project Service when type-aware rules are enabled. |
| shared-typecheck | Opt-in. Parallel workers run structural rules without a Project Service, while one semantic owner builds package-specific TypeScript Programs and reuses parsed SourceFiles across packages. |
| workspace-eslint | Runs the direct workspace ESLint CLI path that predates package workers. A package target delegates to that package's lint script. |
pnpm xy lint # package-workers (default)
pnpm xy lint --mode shared-typecheck # shared TypeScript ingestion
pnpm xy lint --mode workspace-eslint # direct workspace ESLintshared-typecheck preserves each package's compiler options and TypeChecker;
only immutable parsed SourceFiles are shared. Its structural phase keeps the
normal ESLint content cache. The semantic phase intentionally runs without the
ESLint cache because supplied TypeScript Program objects are not serializable.
Repository-level incremental linting still limits semantic work to affected
packages.
The historical --next and --prev selectors remain hidden transition aliases
for package-workers and workspace-eslint, respectively, and print a
deprecation warning. New automation must use --mode.
Copying opaque monolith entries
Monolith packages can copy already-built runtime files into exact public output
paths without a package-local post-processing script. Configure
compile.monolith.copyEntries as platform → output path → source:
import type { XyConfig } from '@ariestools/toolchain'
const config: XyConfig = {
compile: {
mode: 'monolith',
monolith: {
copyEntries: {
neutral: {
'hash/worker/subtleHash-bundle.mjs':
'@xyo-network/sdk-protocol-core/hash/worker/subtleHash-bundle.mjs',
},
},
modules: [
{
name: 'hash',
export: true,
reexport: '@xyo-network/sdk-protocol-core/hash',
},
],
platforms: ['neutral'],
},
},
}
export default configThe example copies the dependency export byte-for-byte to
dist/neutral/hash/worker/subtleHash-bundle.mjs. The output key is also the
subpath suggested by xy publint --fix, so the corresponding package export is
./hash/worker/subtleHash-bundle.mjs.
Sources may be package export specifiers, paths relative to the compiling
package, or absolute paths. Copying runs only during emit, after normal monolith
compilation; --validate-only does not resolve or copy the configured sources.
Destinations must remain inside their configured dist/<platform> directory.
License
LGPL-3.0-only. See LICENSE (LGPLv3) and COPYING (GPLv3, incorporated by LGPL-3).
