Metadata-Version: 2.3
Name: tlc-shared-docs
Version: 1.1.10
Summary: Share documentation files between Git repositories
License: MIT
Requires-Python: >=3.9,<4.0
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Dist: gitpython (>=3.1,<4.0)
Description-Content-Type: text/markdown

# tlc-shared-docs

Share documentation files between Git repositories. Pull files from a remote repo into your local docs tree, or push local files back — all configured through a single `shared.json`.

## Installation

```bash
pip install tlc-shared-docs
```

## Quick Start

### 1. Create the config

Create `docs/source/shared/shared.json` in your project:

```json
{
  "source_repo": {
    "url": "https://github.com/your-org/shared-docs.git",
    "branch": "main"
  },
  "shared_files": [
    {
      "remote_path": "guides/getting-started.md",
      "local_path": "getting-started.md",
      "action": "get"
    },
    {
      "remote_path": "guides/api-reference.md",
      "local_path": "api-reference.md",
      "action": "push"
    }
  ]
}
```

### 2. Pull shared files

```bash
tlc-shared-docs get
```

This fetches every file with `"action": "get"` from the remote repo and saves it locally.

### 3. Push local files

```bash
tlc-shared-docs push
```

This pushes every file with `"action": "push"` to the remote repo. If a remote file has changed since you last pulled, the command aborts with a conflict warning. Use `--force` to overwrite:

```bash
tlc-shared-docs push --force
```

### 4. Preview changes

Both commands support `--dry-run`:

```bash
tlc-shared-docs get --dry-run
tlc-shared-docs push --dry-run
```

## Configuration Reference

### `shared.json`

| Field | Description |
|---|---|
| `source_repo.url` | Git clone URL for the shared repo |
| `source_repo.branch` | Branch to pull from / push to (default: `main`) |
| `shared_files[].remote_path` | Path to the file in the remote repo (supports glob patterns for `get`) |
| `shared_files[].local_path` | Local destination path (relative to `docs/source/shared/`) |
| `shared_files[].action` | `get` (pull from remote) or `push` (push to remote). Default: `get` |

### Wildcard / glob patterns

The `remote_path` field supports glob patterns for `get` actions, allowing you to fetch multiple files with a single entry:

```json
{
  "remote_path": "stories/**/*",
  "local_path": "stories",
  "action": "get"
}
```

Supported patterns:
- `*` — matches any file in a single directory (e.g., `docs/*.md`)
- `**/*` — matches files recursively across directories (e.g., `stories/**/*`)
- `?` — matches a single character (e.g., `chapter?.md`)
- `[seq]` — matches any character in the set (e.g., `file[0-9].txt`)

When using globs, `local_path` acts as the **destination directory**. Matched files preserve their directory structure relative to the non-glob prefix of the pattern. For example:

| Pattern | Matched remote file | `local_path` | Written to |
|---|---|---|---|
| `stories/**/*` | `stories/ch1/intro.md` | `mystories` | `mystories/ch1/intro.md` |
| `Global/*.gitignore` | `Global/Vim.gitignore` | `ignores` | `ignores/Vim.gitignore` |
| `*.md` | `README.md` | `docs` | `docs/README.md` |

### Local path resolution

- **Relative paths** (e.g., `guide.md`) resolve relative to `docs/source/shared/`.
- **Absolute paths** (starting with `/`, e.g., `/src/docs/guide.md`) resolve relative to the project root.

### Git ignore

On first run, `tlc-shared-docs get` creates `docs/source/shared/` with a `.gitignore` that tracks only `shared.json` — all fetched files are ignored so they don't bloat your repo.

The auto-generated `docs/source/shared/.gitignore` contains:

```gitignore
# Auto-generated by tlc-shared-docs
# Ignore all fetched shared files; only track the config
*
!.gitignore
!shared.json
```

This means:
- `shared.json` is committed to your repo (so teammates share the same config)
- All fetched/pushed doc files are ignored locally
- The `.gitignore` itself is also tracked

#### Versioning specific shared files in your own repo

The `.gitignore` is written once and never overwritten by `tlc-shared-docs`. You can edit it freely to commit specific shared files to your own repo.

For example, to version-control `getting-started.md` and everything under `guides/` while still ignoring everything else:

```gitignore
# Auto-generated by tlc-shared-docs
# Ignore all fetched shared files; only track the config
*
!.gitignore
!shared.json

# Files I want to version in THIS repo:
!getting-started.md
!guides/
!guides/**
```

This is useful when your team wants certain shared docs to appear in your repo's git history — for example, files that are reviewed during PRs or that tools like GitHub Pages need to find in the repository.

### Auto-uploading new files (central mode)

When running in **central mode**, consumer repos can automatically upload new files to the shared repo. The central config controls which paths are allowed:

```json
{
  "shared_files": [
    { "remote_path": "guides/intro.md", "local_path": "intro.md", "action": "get" }
  ],
  "uploads": {
    "allowed": true,
    "paths": [
      "contributions/**/*.md",
      "images/*.png"
    ]
  }
}
```

| Field | Description |
|---|---|
| `uploads.allowed` | Set to `true` to enable auto-upload for this repo |
| `uploads.paths` | Glob patterns for permitted remote paths (supports `*`, `**`, `?`) |

#### How it works

1. Place new files anywhere under `docs/source/shared/` in your local repo.
2. Run `tlc-shared-docs push`. The tool automatically scans for files that are **not** already listed in `shared_files`.
3. Each new file's path (relative to `docs/source/shared/`) is checked against the `uploads.paths` patterns.
   - **Permitted** files are included in the push commit.
   - **Denied** files produce a `DENIED:` warning and are skipped.
4. Use `--dry-run` to preview which files would be uploaded or denied.

```bash
# Preview what would be uploaded
tlc-shared-docs push --dry-run

# Push configured files + upload new ones
tlc-shared-docs push

# Force-push (skip conflict check)
tlc-shared-docs push --force
```

> **Note:** Auto-upload only works in central mode. In local mode, add new entries directly to your `shared.json` `shared_files` list.

### Multi-project configs

If your repo participates in multiple architecture projects (e.g., auth, events, agent-coder), you can define them all in a single `shared.json` and select which one to use at runtime:

```json
{
  "projects": {
    "auth": {
      "source_repo": { "url": "https://github.com/your-org/tlc-auth-arch.git" },
      "mode": "central"
    },
    "events": {
      "source_repo": { "url": "https://github.com/your-org/tlc-events-arch.git", "branch": "dev" },
      "mode": "central"
    },
    "agent-coder": {
      "source_repo": { "url": "https://github.com/your-org/agent-coder.git" },
      "mode": "central"
    }
  },
  "default_project": "agent-coder"
}
```

Each project entry supports the same fields as the legacy single-source format (`source_repo`, `mode`, `shared_files`, `uploads`).

#### Usage

```bash
# Pull docs for a specific project
tlc-shared-docs get --project agent-coder
tlc-shared-docs get -p auth

# Uses default_project when --project is omitted
tlc-shared-docs get

# Push works the same way
tlc-shared-docs push -p events --dry-run
```

| Field | Description |
|---|---|
| `projects.<name>` | A named project entry (same schema as legacy root config) |
| `default_project` | Project to use when `--project` is not specified |

If no `--project` flag is given and no `default_project` is set, the command exits with an error listing available projects.

#### Automatic file isolation

In multi-project mode, each project's files are automatically placed into a subdirectory named after the project. You don't need to manually prefix `local_path` — it's handled for you:

```
docs/source/shared/
├── shared.json
├── auth/              ← files from the "auth" project
│   └── guide.md
├── events/            ← files from the "events" project
│   └── architecture.md
└── agent-coder/       ← files from the "agent-coder" project
    └── spec.md
```

For example, if the central config for the `agent-coder` project defines `"local_path": "spec.md"`, the file is written to `docs/source/shared/agent-coder/spec.md`. Absolute paths (starting with `/`) bypass this prefix and resolve from the project root as usual.

#### Project name rules

Project names must be valid folder names: alphanumerics, hyphens, underscores, and dots, starting with a letter or digit. Invalid names (e.g., `../escape`, names with spaces or special characters) are rejected at config load time.

> **Backward compatibility:** The legacy single-source format (with `source_repo` at the root) still works exactly as before — no prefixing, no migration needed.

## Requirements

- Python 3.9+
- Git installed and on `PATH`
- Valid Git credentials for the source repo

