Skip to content

Repository files navigation

claude-personal-framework

A personal framework for packaging reusable Claude Code agents, skills and commands into a versioned catalog and installing them into any project with a single command.

Built around two ideas:

  • Compose, don't copy. Define agents/skills/commands once. Group them into presets. Reuse across projects.
  • Project-level overrides. A project can disable, add or patch any inherited artifact, so the framework adapts to projects that don't follow your default conventions.

The problem this solves

After a few months using Claude Code, you accumulate agents, skills, commands and architectural conventions that work for you. Every new project pays the same tax: copy a dozen markdown files, edit a couple, forget which version is the canonical one, repeat.

This framework is the answer: a single source of truth in a git repo, a CLI that materializes the right combination into .claude/ of any target project, and an override mechanism so projects can deviate without forking the catalog.

How it works

Three layers, in increasing project-specificity:

┌─────────────────────────────────────────────────────────────┐
│  Catalog              presets/   agents/   skills/   git-hooks/
│  (this repo)          *.yaml     *.md      *.md      commit-msg…
└─────────────────────────────────────────────────────────────┘
                                │
                                │  resolveExtends + applyOverrides
                                ▼
┌─────────────────────────────────────────────────────────────┐
│  Project manifest     .claude-fw.yaml in the target project │
│                       └ preset: <name>                      │
│                       └ overrides: [disable, add, patch]    │
└─────────────────────────────────────────────────────────────┘
                                │
                                │  claude-fw install
                                ▼
┌─────────────────────────────────────────────────────────────┐
│  Materialized output  .claude/    agents, skills, commands, │
│  (gitignored)                     settings.json, CLAUDE.md  │
│                       .githooks/  commit-msg, pre-commit,   │
│                                   pre-push (chmod 0755)     │
└─────────────────────────────────────────────────────────────┘

Git hooks are the one artifact type that lands outside .claude/: they go to .githooks/ and install points core.hooksPath at that folder (only when it is unset — an existing value is never overwritten).

A preset declares a list of artifact ids and can extend other presets. Resolution flattens the chain (parent ids before child ids, deduplicated). Overrides apply on top of that resolved preset:

# .claude-fw.yaml in a target project
preset: react-native
overrides:
  - disable: agent:plane-pm                # this repo doesn't use Plane
  - add: agent:hexagonal-test-reviewer     # not in any preset by design
  - patch: skill:commit-style              # different conventions here
    content: |
      ---
      name: commit-style
      ---
      Custom body…

Idempotent installs and drift

Every install writes a lockfile — .claude-fw.lock.json — recording the content hash of each materialized artifact. The next install compares the catalog against that lockfile and applies only the difference: new artifacts are written, changed ones updated, and artifacts dropped from the preset are deleted. Files you placed in .claude/ by hand are never touched — the engine only manages what the lockfile tracks. Re-running install is therefore safe and idempotent; a file deleted by accident gets restored.

claude-fw status reports that same diff (added / updated / removed / unchanged) without writing anything — useful before pulling a newer version of the catalog into a project.

Quick start

Inside this repo:

pnpm install
pnpm -r build
CFW_CATALOG_PATH=. node packages/cli/dist/index.js install --project .

The last line is the framework configuring itself: the artifacts declared in presets/base.yaml are loaded from agents/, skills/ and git-hooks/, then materialized under .claude/ and .githooks/. CFW_CATALOG_PATH is the dev-flow override that makes the working copy win over the catalog embedded in the binary.

To install into another project:

CFW=/path/to/claude-personal-framework
cd /path/to/your/project
CFW_CATALOG_PATH=$CFW node $CFW/packages/cli/dist/index.js init --preset base
CFW_CATALOG_PATH=$CFW node $CFW/packages/cli/dist/index.js install

Commands: init writes the manifest, install materializes the preset, list enumerates the catalog, status shows drift against the last install, detect reports what a path is, and detect-stack ranks presets by how well their detects: rules match a project. All of them accept --json for programmatic consumers (the desktop app consumes exactly that).

(A global bin install is on the roadmap; for now invoke via node.)

Run the desktop app

pnpm install
pnpm -r build
pnpm -C apps/desktop tauri:dev

On Linux/Wayland prepend WEBKIT_DISABLE_DMABUF_RENDERER=1 if the window fails to open with a GDK protocol error. See apps/desktop/README.md for the full setup (stack, IPC bridge, other gotchas).

Inside the desktop app

First-time users land on a two-step welcome wizard. The first step picks a project folder and detects the stack against the catalog's detects: rules to preselect the right preset:

Welcome wizard, step 1 — project + detected preset

The second step shows the setup summary and runs init + install in sequence; if the folder is missing or not a git repo, the wizard intercepts the typed error and offers the right native modal:

Welcome wizard, step 2 — setup summary and Set up button

Once a project is set up, the app switches to the free mode: project header with a Switch dropdown, plus three cards for Status / Catalog / Actions. Outcomes are ephemeral — success banners auto-dismiss after 5 s, errors stay sticky:

Free mode — project header + Status, Catalog, Actions cards

When the welcome flag is set and there's no active project, the recent projects screen replaces the wizard:

Recent projects screen with three recently used projects

Settings is a full-screen modal that manages the catalog sources — built-in toggle, user folders with per-row remove, and an Add folder button that validates against the engine before persisting. The welcome wizard can be reset from the bottom section:

Settings panel — Catalog folders and Welcome wizard sections

Architecture

Hexagonal (ports & adapters):

packages/core/src/
├── domain/              ← entities, value objects, domain services
│   ├── model/           Agent, Skill, Command, GitHook, Preset,
│   │                    Composition, Settings, Instructions,
│   │                    ContentHash, Override, DetectRule, ids,
│   │                    ProjectManifest, Lockfile, DriftReport
│   ├── errors/          DomainError + typed subclasses
│   └── services/        resolveExtends, applyOverrides, computeDrift,
│                        evaluateDetects, computeGitignoreBlock, dedupe
├── application/         ← use cases + ports + shared services
│   ├── ports/           Catalog, Writer, LockfileStore, ManifestStore,
│   │                    ProjectInspector, StackInspector, GitConfig,
│   │                    Gitignore, PathProbe
│   ├── services/        buildComposition (shared by install + status),
│   │                    AggregatedCatalog (multi-source precedence)
│   └── use-cases/
│       ├── init-project/    writes .claude-fw.yaml
│       ├── install/         drift-aware materialization
│       ├── check-status/    same diff, read-only
│       ├── list-catalog/    enumerate the catalog
│       ├── detect-path/     is this a framework root? a project?
│       └── detect-stack/    rank presets against a project
└── infrastructure/      ← adapters that implement ports
    ├── yaml/            parse/serialize preset + project manifest
    ├── json/            parseLockfile, serializeLockfile
    ├── markdown/        extractFrontmatterDescription
    ├── git/             ChildProcessGitConfig (core.hooksPath)
    └── fs/              FsCatalogReader, ClaudeWriter, LockfileStore,
                         ManifestStore, FsGitignore, FsStackInspector,
                         LocalProjectInspector, PathProbe

packages/cli/            ← CLI port over the same engine
└── src/                 6 commands, each with a --json formatter

apps/desktop/            ← Tauri desktop port over the same engine
├── src/                 React 19 + Vite + Tailwind 4
└── src-tauri/           Rust handlers that spawn the CLI as subprocess

The domain has zero filesystem or framework imports. Adapters depend inward on the domain. CLI and the desktop app are two independent ports over the same use cases — the desktop app does not reimplement the engine, it spawns the CLI in a subprocess and consumes its structured JSON output.

Project layout

.
├── agents/              Catalog: 5 agents
├── skills/              Catalog: 18 skills (cross-cutting + per stack)
├── git-hooks/           Catalog: commit-msg, pre-commit, pre-push
├── presets/             Catalog: 6 preset YAMLs
│   ├── base.yaml            everything below extends it
│   ├── nestjs.yaml          react-native.yaml    vue.yaml
│   └── laravel.yaml         tauri-rust-react.yaml
├── packages/
│   ├── core/            Engine: domain + application + infrastructure
│   └── cli/             CLI port
├── apps/
│   └── desktop/         Tauri desktop port (React + Rust)
├── docs/
│   ├── adr/             Architecture Decision Records
│   └── roadmap.md       Prioritized backlog (Now / Next / Deferred)
├── .claude/             Output of `claude-fw install` (gitignored)
├── .githooks/           Output of `claude-fw install` (gitignored)
├── .claude-fw.yaml      Project manifest (this repo configures itself)
└── .claude-fw.lock.json Lockfile: content hashes of the last install

There is no commands/ directory yet — the engine supports the artifact type end to end (CommandId, writeCommand, lockfile tracking), no preset declares one so far. Same for instructions/, which materializes as .claude/CLAUDE.md when a preset asks for it.

Status

  • ✅ Domain model + composition resolver (extends chains, diamond inheritance, cycle detection)
  • ✅ YAML + JSON + filesystem adapters
  • ✅ Lockfile-based drift: idempotent installs, status reports added / updated / removed / unchanged without writing
  • ✅ CLI with 6 commands, all with --json
  • ✅ Artifact types: agents, skills, commands, git hooks, per-preset settings.json and CLAUDE.md
  • ✅ Git hooks materialized to .githooks/ with core.hooksPath activation and take-over guards for user-authored hooks
  • ✅ Managed .gitignore block in the target project so install output never gets committed by a stray git add . (ADR 0007)
  • ✅ Multi-source catalog with precedence env > folder > builtin; the built-in catalog ships embedded in the desktop binary (ADR 0003, ADR 0004)
  • ✅ Stack detection (detects: rules) driving preset preselection
  • ✅ Six presets: base + nestjs, react-native, vue, laravel, tauri-rust-react
  • ✅ Self-hosting: this repo's own .claude/ and .githooks/ are materialized by the engine
  • ✅ Tauri desktop app: welcome wizard, free mode, recent projects, settings panel (see apps/desktop/README.md)
  • 🚧 Global bin install — today the CLI runs via node …/dist/index.js
  • 🚧 overrides: field in Preset schema (ADR 0001)
  • 🚧 Provider-agnostic pr-creator (ADR 0001)
  • 🚧 Sidecar bundling Node + CLI inside the desktop binary

The prioritized backlog, with the trigger condition for each deferred item, lives in docs/roadmap.md.

Architectural decisions

Recorded in docs/adr/. Each record captures the context, the options considered (including the rejected ones), the decision and its consequences.

Development

pnpm install              # install deps
pnpm -r test              # run tests
pnpm -r build             # type-check + emit dist/
pnpm lint                 # biome check
pnpm check                # biome check --write (fix formatting)

For the desktop app specifically, see apps/desktop/README.md — there is a Linux/Wayland environment variable that may be required.

TypeScript strict (noUncheckedIndexedAccess, exactOptionalPropertyTypes, verbatimModuleSyntax). Biome for lint

  • format. Vitest for tests on the engine.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages