Skip to content

ankhorage/devtools

Repository files navigation

devtools

Shared development tools and repository standards for Ankhorage TypeScript projects.

What it owns

@ankhorage/devtools is the single source of truth for these separate concerns:

src/
├── cli/
└── tools/
    ├── eslint/
    ├── prettier/
    ├── knip/
    ├── package/
    ├── workflows/
    └── vscode/
  • eslint: shared flat ESLint configuration, automatic project profiles, and the bundled ESLint runner
  • prettier: shared Prettier configuration and the bundled Prettier runner
  • knip: shared Knip configuration helpers and the bundled Knip runner
  • package: merge-aware synchronization of the shared package.json tooling contract
  • workflows: canonical .github/workflows/ci.yml and release.yml
  • vscode: canonical .vscode/settings.json and extensions.json

The package owns the supported ESLint, TypeScript ESLint, Prettier, Knip, security, React, React Hooks, React Native, import/sort, unused-import, and formatting-plugin versions used by consuming repositories.

Bootstrap

For a repository that does not yet depend on the shared toolchain:

bun add -D @ankhorage/devtools
bunx @ankhorage/ankh devtools sync .
bun install

After the first install, the normal workflow is:

ankh devtools sync

The target path is optional and defaults to the current working directory.

Synchronization ensures @ankhorage/devtools is declared using the version of the provider performing the sync, installs the standard package scripts, and removes direct devDependencies for tools/plugins owned by devtools. Unrelated package metadata, dependencies, and scripts are preserved.

Ankh provider

The package is discovered under the devtools category and exposes these capabilities:

  • devtools.lint
  • devtools.format
  • devtools.knip
  • devtools.sync
  • devtools.status
  • devtools.eslint.sync
  • devtools.eslint.status
  • devtools.prettier.sync
  • devtools.prettier.status
  • devtools.knip.sync
  • devtools.knip.status
  • devtools.package.sync
  • devtools.package.status
  • devtools.workflows.sync
  • devtools.workflows.status
  • devtools.vscode.sync
  • devtools.vscode.status

The canonical command prefix is always:

ankh devtools ...

Tool commands

ankh devtools lint -- --max-warnings=0 .
ankh devtools format -- --check .
ankh devtools knip -- --production

These delegate to the same bundled tools as the package binaries:

  • ankh devtools lintankhorage-eslint
  • ankh devtools formatankhorage-prettier
  • ankh devtools knipankhorage-knip

The synchronized package scripts are:

{
  "scripts": {
    "lint": "ankhorage-eslint . --max-warnings=0",
    "lint:fix": "ankhorage-eslint . --fix --max-warnings=0",
    "format": "ankhorage-prettier --write .",
    "format:check": "ankhorage-prettier --check .",
    "knip": "ankhorage-knip"
  }
}

Repository synchronization

Synchronize or inspect every managed concern:

ankh devtools sync .
ankh devtools status .

Synchronize one concern:

ankh devtools eslint sync .
ankh devtools prettier sync .
ankh devtools knip sync .
ankh devtools package sync .
ankh devtools workflows sync .
ankh devtools vscode sync .

Report one concern:

ankh devtools eslint status .
ankh devtools prettier status .
ankh devtools knip status .
ankh devtools package status .
ankh devtools workflows status .
ankh devtools vscode status .

Preview synchronization without writing:

ankh devtools sync . --dry-run
ankh devtools eslint sync . --dry-run
ankh devtools package sync . --dry-run

A dry run reports would create and would update actions without mutating files. status exits with code 1 when managed state has drifted and 0 when it is current.

Synchronization guarantees

Synchronization is deterministic and idempotent:

  • missing managed artifacts are created
  • outdated centrally owned artifacts are updated
  • current artifacts are left untouched
  • unrelated files and package fields are preserved
  • repeated sync produces only unchanged results
  • invalid target paths and write failures return a non-zero exit code
  • create-only repository extension files are never overwritten after creation

The canonical workflow and VS Code files are packaged with @ankhorage/devtools; synchronization does not fetch mutable files from GitHub at runtime.

ESLint profiles

createConfig() defaults to profile: 'auto'.

Automatic detection reads the consuming repository's package.json and delegates project trait detection to @ankhorage/utility/project. Dependency signals are considered across dependencies, devDependencies, and peerDependencies.

Profile precedence is:

React Native / Expo
        ↓
react-native
        ↓ includes
react
        ↓ includes
base

A React or Next.js project selects react. A React Native or Expo project selects react-native. Everything else selects base.

An unusual repository can opt out of automatic selection:

import { createConfig } from '@ankhorage/devtools/eslint';

export default createConfig({
  files: ['src/**/*.ts'],
  profile: 'base',
  project: ['./tsconfig.json'],
  tsconfigRootDir: import.meta.dirname,
});

The base profile enforces the shared TypeScript policy plus:

  • maximum 50 effective lines per function
  • maximum 300 effective lines per file
  • modified cyclomatic complexity maximum 15
  • security review for dynamic object access
  • rejection of non-literal require() calls

The React profile adds React and React Hooks correctness rules. The React Native profile composes the React profile and adds the selected React Native style rules.

Managed ESLint setup and local overrides

ankh devtools eslint sync centrally owns eslint.config.mjs and creates eslint.local.config.mjs once.

The canonical wrapper uses automatic profile detection and appends repository-owned flat-config entries:

import { createConfig } from '@ankhorage/devtools/eslint';
import localConfig from './eslint.local.config.mjs';

const localEntries = Array.isArray(localConfig) ? localConfig : [localConfig];

export default [
  ...createConfig({
    files: ['src/**/*.{ts,tsx}'],
    project: ['./tsconfig.json'],
    tsconfigRootDir: import.meta.dirname,
  }),
  ...localEntries,
];

Use eslint.local.config.mjs for narrow repository-specific flat-config overrides, including temporary file-specific migration overrides. On first synchronization, an existing non-canonical eslint.config.mjs is preserved as the initial local config before the canonical wrapper is installed. Synchronization never overwrites that local file afterward.

Prettier

ankh devtools prettier sync owns .prettierrc.js and emits the correct ESM or CommonJS delegate based on the repository's package.json module type.

The consumer delegates formatting policy to:

@ankhorage/devtools/prettier

Knip

ankh devtools knip sync bootstraps knip.config.ts with:

import { createKnipConfig } from '@ankhorage/devtools/knip';

export default createKnipConfig();

knip.config.ts is create-only after bootstrap so repositories can retain narrow local entries, projects, ignores, binaries, dependencies, or switch to createKnipMonorepoConfig() without synchronization overwriting those extensions.

Managed package contract

ankh devtools package sync merge-updates package.json rather than replacing it.

It owns:

  • the @ankhorage/devtools devDependency version range
  • lint
  • lint:fix
  • format
  • format:check
  • knip

It also removes direct devDependencies for tools and ESLint plugins already provided by @ankhorage/devtools. Unrelated scripts, dependencies, metadata, and repository-specific configuration remain unchanged.

Managed GitHub Actions workflows

workflows owns exactly:

.github/workflows/ci.yml
.github/workflows/release.yml

The CI workflow installs the pinned Bun version with the frozen lockfile, builds before repository-provider validation, runs bunx @ankhorage/ankh doctor validate ., and conditionally runs lint, formatting, Knip, tests, typecheck, and Changesets checks.

Managed VS Code configuration

vscode owns exactly:

.vscode/settings.json
.vscode/extensions.json

Unknown workflow and VS Code files are never deleted.

Adding another managed concern

A new concern should:

  1. live in its own sibling directory under src/tools
  2. define only the files and behavior it owns
  3. expose deterministic status and synchronization
  4. add provider commands under ankh devtools
  5. include dry-run, status, and idempotence coverage
  6. document its central ownership and repository-owned extension points

About

ESLint, Prettier, Knip

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors