Conformance Task Architecture
xtarterize applies conformance configuration through discrete, independently applicable tasks. This page covers the architecture and how to contribute new tasks.
Task Interface
Section titled “Task Interface”All tasks implement the Task interface from @xtarterize/core:
interface Task { id: string label: string group: string applicable: (profile: ProjectProfile) => boolean check: (cwd: string, profile: ProjectProfile) => Promise<TaskStatus> dryRun: (cwd: string, profile: ProjectProfile) => Promise<FileDiff[]> apply: (cwd: string, profile: ProjectProfile) => Promise<void>}Task Architecture
Section titled “Task Architecture”classDiagram
class Task {
+string id
+string label
+string group
+applicable(profile) boolean
+check(cwd, profile) TaskStatus
+dryRun(cwd, profile) FileDiff[]
+apply(cwd, profile) void
}
class TaskStatus {
<<enumeration>>
new
patch
skip
conflict
}
class FileDiff {
+string filepath
+string before
+string after
}
class ProjectProfile {
+Framework framework
+Bundler bundler
+PackageManager packageManager
+boolean typescript
+boolean monorepo
+boolean hasGitHub
}
Task --> TaskStatus
Task --> FileDiff
Task --> ProjectProfile
Task Directory Structure
Section titled “Task Directory Structure”Directorypackages/tasks/src/
Directoryagent/
- agents-md.ts
- module.ts
- skills.ts
Directoryci/
- auto-update.ts
- ci.ts
- release.ts
Directorycodegen/
- plop.ts
Directorydeps/
- renovate.ts
Directoryeditor/
- vscode.ts
- factory-config.ts
- factory-ops.ts
- factory.ts
Directorylint/
- biome.ts
Directorymonorepo/
- turbo.ts
Directoryquality/
- knip.ts
Directoryrelease/
- cat-version.ts
- commitlint.ts
- czg.ts
Directoryscripts/
- package-scripts.ts
Directoryts/
- strict.ts
- paths.ts
- incremental.ts
- gitignore-tsbuildinfo.ts
Directoryvite/
- checker.ts
- visualizer.ts
Task Factory
Section titled “Task Factory”Most tasks are created through factory functions in factory.ts that eliminate boilerplate:
createSimpleFileTask— For tasks that write a single new file (CI workflows, renovate, commitlint, knip, AI skills)createMultiFileTask— For tasks that write multiple text files with optional dependency install (e.g. plop writes bothplopfile.tsandplop/*.hbstemplate files)createFileTask— For text files with custom check/merge logic (AGENTS.md,.gitignoreentries)createJsonMergeTask— For tasks that deep-merge JSON configs (tsconfig.json,biome.json,turbo.json). Uses shared JSON mutation helpers infactory-config.tscreateMultiFileJsonMergeTask— For tasks that merge multiple JSON files (.vscode/settings.json+.vscode/extensions.json). Supports custom per-file merge functions and reusesfactory-config.tscreateVitePluginTask— For injecting plugins intovite.config.ts(vite-plugin-checker,rollup-plugin-visualizer)createPackageJsonTask— For tasks that add scripts, deps, and extra files viapackage.json(czg,commit-and-tag-version, package scripts)
This reduces most tasks from 40-80 lines to 10-15 lines of declarative configuration.
Shared implementation seams used by the factory layer:
factory-config.ts— centralized JSON configcheck/dryRunlogicfactory-ops.ts— shared dependency install and write operationsagent/module.ts— shared agent task creation patterns
Helper Functions
Section titled “Helper Functions”The factory provides several utilities for robust equivalence detection:
| Helper | Purpose | Used By |
|---|---|---|
deepEqual | Compare objects regardless of key ordering | createJsonMergeTask, createMultiFileJsonMergeTask |
normalizeExtends | Normalize "extends": "str" to "extends": ["str"] | biomeTask, renovateTask |
normalizeLineEndings | Convert \r\n to \n before comparison | createSimpleFileTask |
hasScriptValue | Check if a command value already exists under any script name | createPackageJsonTask |
normalizeExtends
Section titled “normalizeExtends”Config tools like Renovate and Biome accept extends as either a string or array. normalizeExtends ensures both forms are treated as equivalent during comparison:
normalizeExtends({ extends: "config:base" })// → { extends: ["config:base"] }hasScriptValue
Section titled “hasScriptValue”Package script tasks skip adding a script not only when the key exists, but also when the exact same command already exists under a different name. This handles real-world aliases like type:check vs typecheck.
Adding New Tasks
Section titled “Adding New Tasks”- Implement the
Taskinterface from@xtarterize/core - Create your task file in
packages/tasks/src/<category>/<task>.ts - Export it from
packages/tasks/src/index.ts - Add tests in
test/tasks/
Each task must implement:
applicable(profile)— Should this task run for this project?check(cwd, profile)— What’s the current status?dryRun(cwd, profile)— What would change?apply(cwd, profile)— Make the changes
Conflict Detection Pattern
Section titled “Conflict Detection Pattern”For tasks that patch JSON config files, use a tristate detection pattern to avoid permanent patch loops:
| State | Condition | Status | Behavior |
|---|---|---|---|
| Missing | Key does not exist | patch | Add the key |
| Match | Key exists with expected value | skip | No changes |
| Mismatch | Key exists with different value | conflict | Alert user, don’t overwrite |
This prevents infinite loops when a user explicitly sets a value that differs from xtarterize’s recommendation (e.g., strict: false in tsconfig).
References
Section titled “References”- defu — Deep merge utility used by JSON merge tasks
- magicast — AST manipulation for Vite plugin injection
- TypeScript tsconfig — Compiler configuration reference
- Biome Configuration —
biome.jsonschema - Turborepo Configuration —
turbo.jsonschema - commitlint Rules — Commit message validation rules
- czg Documentation — Interactive commit helper
- commit-and-tag-version — Automated versioning
- Knip Documentation — Unused code detection
- Plop Documentation — Code scaffolding
- VS Code Settings — Editor configuration
- VS Code Extensions — Workspace recommendations
- Renovate Configuration — Dependency automation