Module Interface
A technical reference for the DevFlow module interface: how modules detect existing configuration, build action plans and join the wizard.
Interface Definition
Every module in DevFlow implements the DevFlowModule interface:
interface DevFlowModule {
/** Unique identifier, e.g. "husky", "eslint" */
id: string;
/** Human-readable name */
name: string;
/** Short description shown in wizard checkboxes */
description: string;
/** Category for grouping */
category: Category;
/** Stacks for which this module is pre-selected */
recommendedFor?: Stack[];
/** Module IDs that conflict (mutually exclusive) */
conflicts?: string[];
/** Module IDs that must be installed first */
dependsOn?: string[];
/** Check if the module is applicable and/or already configured */
detect(ctx: ProjectContext): Promise<ModuleDetection>;
/** Generate planned actions without side effects */
plan(ctx: ProjectContext, options: ModuleOptions): Promise<ModuleAction[]>;
}Categories
type Category =
| "git" // Git & Commits
| "quality" // Code Quality
| "testing" // Testing
| "ci" // CI/CD
| "release" // Releases
| "security" // Security
| "dx" // Dev Environment
| "docker" // Docker
| "docs" // Documentation
| "monorepo"; // MonorepoDetection Result
interface ModuleDetection {
/** Is this module applicable for the current project? */
applicable: boolean;
/** Is it already configured? */
alreadyConfigured: boolean;
/** Human-readable reason */
reason?: string;
}Action Types
Modules return an array of ModuleAction — a discriminated union:
CreateFileAction
{
type: "create-file",
path: "eslint.config.mjs",
content: "export default [...];",
skipIfExists: true
}InstallAction
{
type: "install",
dev: true,
packages: ["eslint", "@eslint/js"]
}AddScriptAction
{
type: "add-script",
name: "lint",
command: "eslint .",
append: false
}MergePackageJsonAction
{
type: "merge-package-json",
merge: {
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"]
}
}
}UpdateFileAction
{
type: "update-file",
path: ".gitignore",
description: "Add coverage/ to .gitignore",
updater: (existing) => existing + "\ncoverage/\n"
}RunCommandAction
{
type: "run-command",
command: "npx playwright install",
description: "Install Playwright browsers"
}Project Context
Every module receives a ProjectContext:
interface ProjectContext {
root: string; // absolute path
pm: PackageManager; // "npm" | "yarn" | "pnpm" | "bun"
hasPackageJson: boolean;
hasTypeScript: boolean;
repoType: RepoType; // "single" | "monorepo"
monorepoTool?: MonorepoTool;
workspacePatterns: string[];
stack?: Stack;
existingFiles: Set<string>;
packageJson?: Record<string, unknown>;
}Modules use this context to make stack-aware decisions — for example, ESLint adds React plugins when ctx.stack === 'react'.