<p align="center">
<br>
<picture>
  <source media="(prefers-color-scheme: dark)" srcset=".github/assets/logo-dark.svg">
  <source media="(prefers-color-scheme: light)" srcset=".github/assets/logo-light.svg">
  <img src=".github/assets/logo-light.svg" alt="ziku" height="120">
</picture>
<br>
<br>
<em>A bi-directional dev environment template that evolves with you.</em>
<br>
<br>
<a href="https://www.npmjs.com/package/ziku"><img src="https://img.shields.io/npm/v/ziku?color=a1b858&label=" alt="npm version"></a>
<a href="https://www.npmjs.com/package/ziku"><img src="https://img.shields.io/npm/dm/ziku?color=50a36f&label=" alt="npm downloads"></a>
<a href="https://github.com/tktcorporation/.github/blob/main/LICENSE"><img src="https://img.shields.io/github/license/tktcorporation/.github?color=blue&label=" alt="license"></a>
<br>
<br>
</p>

> _ziku_ (軸) — the axis around which things revolve. Your template is the axis; every project builds on it, and improvements flow back to keep it turning.

## Why

Templates go stale the moment you scaffold them. Each project improves upon the original — better configs, new workflows, refined settings — but those improvements never flow back.

**ziku** solves this with bi-directional sync:

- **`init`** — Pull the latest template into your project
- **`push`** — Push your improvements back to the template
- **`pull`** — Sync latest template updates with 3-way merge
- **`status`** — See pending pull/push counts and what to do next
- **`diff`** — See what's changed
- **`track`** — Add file patterns to the sync whitelist
- **`aggregate`** — Inventory unsynced diffs across every repository using your template (read-only; run from the template repo)

Your template stays alive, fed by every project that uses it.

<!-- GETTING_STARTED:START -->

## Getting Started

ziku has two roles: **template author** (setup) and **template user** (init). If someone else has already set up the template, skip to Step 2.

### Step 1: Create the template (`setup`) — template author

`ziku setup` initializes a template repository by creating `.ziku/ziku.jsonc`. This file defines which file patterns ziku manages.

```bash
# In your template repository
npx ziku setup
```

This creates `.ziku/ziku.jsonc` with default patterns. Edit it to match your needs:

```jsonc
{
  "$schema": "https://raw.githubusercontent.com/tktcorporation/ziku/main/schema/ziku.json",
  "include": [
    ".claude/settings.json",
    ".claude/rules/*.md",
    ".claude/skills/**",
    ".mcp.json",
    ".devcontainer/**"
  ]
}
```

You can also set up a remote template repository:

```bash
# Create a PR to add .ziku/ziku.jsonc to a remote repo
npx ziku setup --remote --from my-org/my-templates
```

By default, ziku looks for `{your-org}/.ziku`, then `{your-org}/.github` based on your git remote. If the repository doesn't exist, `npx ziku` will offer to create it interactively.

### Step 2: Apply the template (`init`) — template user

`ziku init` (or just `npx ziku`) downloads the template and lets you select which directories to sync.

```bash
# Auto-detect template from git remote
npx ziku

# Use a specific template
npx ziku --from my-org/my-templates

# Use a local directory as template (no GitHub needed)
npx ziku --from-dir ../my-template
```

ziku copies the matching files into your project and creates:

- `.ziku/ziku.jsonc` — selected sync patterns (same format as the template)
- `.ziku/lock.json` — template source + sync state (hashes, refs)

### Step 3: Keep it in sync

```bash
# Push local improvements back to the template
npx ziku push -m "Add new workflow"

# Pull latest template updates (includes new patterns)
npx ziku pull

# Check what's different
npx ziku diff

# Add file patterns to the sync whitelist
npx ziku track '.eslintrc.*'
```

`push` works with both GitHub (creates a PR) and local templates (copies files directly). `pull` also syncs new patterns added to the template's `ziku.jsonc`.

<!-- GETTING_STARTED:END -->

<!-- FEATURES:START -->

## How it Works

```mermaid
graph TB

  subgraph Template["Template Repository"]
    T_ZIKU_ZIKU_JSONC[".ziku/ziku.jsonc"]
    T_SYNCED_FILES["synced files"]
    T_README_MD["README.md"]
  end

  subgraph User["User Project"]
    U_ZIKU_ZIKU_JSONC[".ziku/ziku.jsonc"]
    U_ZIKU_LOCK_JSON[".ziku/lock.json"]
    U_SYNCED_FILES["synced files"]
  end

  subgraph Consumers["Consumer Repositories"]
    R_ZIKU_LOCK_JSON[".ziku/lock.json"]
    R_ZIKU_ZIKU_JSONC[".ziku/ziku.jsonc"]
    R_SYNCED_FILES["synced files"]
  end

  setup([setup]) -->|create| T_ZIKU_ZIKU_JSONC
  init([init]) -.->|read| T_ZIKU_ZIKU_JSONC
  init -->|create| U_ZIKU_ZIKU_JSONC & U_ZIKU_LOCK_JSON & U_SYNCED_FILES
  pull([pull]) -.->|read| U_ZIKU_ZIKU_JSONC & U_ZIKU_LOCK_JSON & T_SYNCED_FILES
  pull -->|update| U_SYNCED_FILES & U_ZIKU_ZIKU_JSONC & U_ZIKU_LOCK_JSON
  push([push]) -.->|read| U_ZIKU_ZIKU_JSONC & U_ZIKU_LOCK_JSON & U_SYNCED_FILES & T_SYNCED_FILES
  push -->|update| U_ZIKU_ZIKU_JSONC & T_SYNCED_FILES & T_ZIKU_ZIKU_JSONC & T_README_MD & U_ZIKU_LOCK_JSON
  diff([diff]) -.->|read| U_ZIKU_ZIKU_JSONC & U_ZIKU_LOCK_JSON & U_SYNCED_FILES & T_SYNCED_FILES
  status([status]) -.->|read| U_ZIKU_ZIKU_JSONC & U_ZIKU_LOCK_JSON & U_SYNCED_FILES & T_SYNCED_FILES
  track([track]) -.->|read| U_ZIKU_ZIKU_JSONC
  track -->|update| U_ZIKU_ZIKU_JSONC
  aggregate([aggregate]) -.->|read| T_ZIKU_ZIKU_JSONC & R_ZIKU_LOCK_JSON & R_ZIKU_ZIKU_JSONC & T_SYNCED_FILES & R_SYNCED_FILES

```

> For detailed file operations per command, see [File Lifecycle](docs/architecture/file-lifecycle.md).

### The config file

Both the template and user project share the same `.ziku/ziku.jsonc` format — just `include` and `exclude` patterns:

```jsonc
{
  "include": [
    ".claude/rules/*.md",
    ".mcp.json",
    ".github/workflows/**"
  ]
}
```

### Command overview

| Command | Who runs it | What it does |
|---|---|---|
| **`setup`** | Template author | Initialize a template repository |
| **`init (user project)`** | Template user | Initialize user project from template |
| **`pull`** | Template user | Pull latest template updates to local project |
| **`push`** | Template user | Push local changes to template (GitHub: PR / local: direct copy) |
| **`diff`** | Template user | Show differences between local and template |
| **`status`** | Template user | Show pending pull/push counts and recommend next action |
| **`track`** | Template user | Add file patterns to the sync whitelist |
| **`aggregate`** | Template author | Inventory unsynced diffs across repositories using this template (read-only) |

Template source info (owner/repo or local path) is stored in `.ziku/lock.json`, separate from patterns. When you `pull`, new patterns added to the template's `.ziku/ziku.jsonc` are automatically merged into yours.

> For detailed file operations per command, see [File Lifecycle](docs/architecture/file-lifecycle.md).

<!-- FEATURES:END -->

<!-- COMMANDS:START -->

## Commands

### `setup`

Initialize a template repository with .ziku/ziku.jsonc

```
Initialize a template repository with .ziku/ziku.jsonc (setup vdev)

USAGE setup [OPTIONS] [DIR]

ARGUMENTS

  DIR    Template repository directory (Default: .)

OPTIONS

       --remote    Create a PR to set up a remote template repository instead of local (Default: false)
  --from=<from>    Remote template repository as owner/repo (used with --remote)
   -n, --dryRun    Preview what would be created, without writing files or opening a PR (Default: false)
      -y, --yes    Skip prompts (with --remote, the PR is opened without asking to confirm) (Default: false)
```

### `init`

Apply dev environment template to your project

```
Apply dev environment template to your project (ziku vdev)

USAGE ziku [OPTIONS] [DIR]

ARGUMENTS

  DIR    Target directory (Default: .)

OPTIONS

                                        --force    Approve overwriting existing files with the template version (Default: false)
                                      -y, --yes    Skip prompts (apply every template directory; existing files are kept, not overwritten) (Default: false)
                              -d, --dirs=<dirs>    Comma-separated directory names to apply (non-interactive)
  -s, --overwrite-strategy=<overwrite_strategy>    Overwrite strategy: overwrite, skip, or prompt
                                  --from=<from>    Template source as owner/repo (e.g., my-org/my-templates)
                          --from-dir=<from_dir>    Local directory to use as template source (skips GitHub download)
                                   -n, --dryRun    Preview which files would be created/overwritten, without writing them (Default: false)
```

### `push`

Push local changes to the template (PR for GitHub, direct copy for local)

```
Push local changes to the template (PR for GitHub, direct copy for local) (push)

USAGE push [OPTIONS] [DIR]

ARGUMENTS

  DIR    Project directory (Default: .)

OPTIONS

             -n, --dryRun    Preview only, don't push (Default: false)
  -m, --message=<message>    PR title (GitHub only)
                -y, --yes    Skip prompts (untracked files are reported and left out instead of being selected for tracking) (Default: false)
                   --edit    Edit PR title and description before creating (GitHub only) (Default: false)
          --files=<files>    Comma-separated file paths to include (skips file selection prompt)
       --includeDeletions    Include locally deleted files (default: unselected in interactive mode) (Default: false)
```

### `pull`

Pull latest template updates

```
Pull latest template updates (pull)

USAGE pull [OPTIONS] [DIR]

ARGUMENTS

  DIR    Project directory (Default: .)

OPTIONS

   -f, --force    Approve deleting local files that were removed from the template (Default: false)
     -y, --yes    Skip prompts (files removed from the template are kept, not deleted) (Default: false)
    --continue    Continue a paused merge (asks which version to keep for files that could not be auto-merged) (Default: false)
  -n, --dryRun    Preview changes without applying them (Default: false)
```

### `diff`

Show differences between local and template

```
Show differences between local and template (diff)

USAGE diff [OPTIONS] [DIR]

ARGUMENTS

  DIR    Project directory (Default: .)

OPTIONS

  -v, --verbose    Show detailed diff (Default: false)
```

### `status`

Show pending pull/push counts and recommend next action

```
Show pending pull/push counts and recommend next action (status)

USAGE status [OPTIONS] [DIR]

ARGUMENTS

  DIR    Project directory (Default: .)
```

### `track`

Add file patterns to the tracking whitelist in ziku.jsonc

```
Add file patterns to the tracking whitelist in ziku.jsonc (track)

USAGE track [OPTIONS] [PATTERNS]

ARGUMENTS

  PATTERNS    File paths or glob patterns to track (e.g., .cloud/rules/*.md)

OPTIONS

  -d, --dir=<dir>    Project directory (default: current directory) (Default: .)
       -l, --list    List all currently tracked patterns (Default: false)
     -n, --dryRun    Preview patterns that would be tracked, without writing (Default: false)
```

### `aggregate`

Inventory unsynced diffs across all repositories using this template (read-only; does not push or consolidate changes)

```
Inventory unsynced diffs across all repositories using this template (read-only; does not push or consolidate changes) (aggregate)

USAGE aggregate [OPTIONS] [DIR]

ARGUMENTS

  DIR    Template repository directory (Default: .)

OPTIONS

              --owner=<owner>    GitHub owner to search for template usage (default: origin owner)
              --since=<since>    Only include repositories with pending-push/conflict changes on or after this date/time (ISO 8601; interpreted as UTC unless an explicit offset is given)
                       --json    Print the JSON report to stdout (no decoration; safe to pipe) (Default: false)
                  --out=<out>    Write the JSON report to this file path
           --include-archived    Include archived repositories (Default: false)
  --concurrency=<concurrency>    Number of repositories to process concurrently (default: 4)
```

<!-- COMMANDS:END -->

`aggregate` runs in the template repository and reads GitHub (via the API) to find repositories using it, comparing each against the template. It only inventories differences into a JSON report (`--json` / `--out`) — it never writes to any repository. Consolidating the reported diffs back into the template (e.g. via `push`) is a separate, deliberate step for a human or an agent reading the report.

<!-- FILES:START -->

## What You Get

The files you get depend on the patterns configured in your template's `.ziku/ziku.jsonc`. After running `ziku init`, your selected patterns are saved in your own `.ziku/ziku.jsonc` — you can customize them anytime with `ziku track`.

ziku also creates:

- `.ziku/ziku.jsonc` — Your sync patterns (which files to include/exclude)
- `.ziku/lock.json` — Sync state + template source (hashes, base refs, source info)

<!-- FILES:END -->

## Contributing

See [CONTRIBUTING.md](./CONTRIBUTING.md).

## License

[MIT](./LICENSE) License &copy; [tktcorporation](https://github.com/tktcorporation)
