# https://github.com/lvxiaoxin/copilot-sync Project Manual

Generated at: 2026-06-21 18:22:10 UTC

## Table of Contents

- [Project Overview](#page-overview)
- [Installation and Onboarding](#page-onboarding)
- [Configuration Push and Pull](#page-config-sync)
- [Session History Sync](#page-history-sync)

<a id='page-overview'></a>

## Project Overview

### Related Pages

Related topics: [Installation and Onboarding](#page-onboarding), [Configuration Push and Pull](#page-config-sync)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [package.json](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json)
- [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md)
- [src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js)
- [src/commands/onboard.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js)
- [src/commands/push.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/push.js)
- [src/history-store.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/history-store.js)
- [src/secrets.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/secrets.js)
- [src/prompt.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/prompt.js)
</details>

# Project Overview

`copilot-sync` is a cross-platform Node.js CLI that synchronizes a user's GitHub Copilot CLI configuration and (optionally) session history across multiple developer machines. It is published as the npm package [`@lvxiaoxin/copilot-sync`](https://www.npmjs.com/package/@lvxiaoxin/copilot-sync) and is distributed as a single binary, `copilot-sync`, declared in [`package.json`](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json). The tool's core idea is to let a developer configure Copilot CLI once and then reuse that setup on every devbox, using a GitHub repository they own as the transport layer.

## Purpose and Scope

The project's scope is deliberately narrow: it does not modify the Copilot CLI itself, does not manage authentication, and does not talk to GitHub's API directly. Instead, it treats a user-owned Git repository as a generic object store, performing a sequence of `git add`, `git commit`, and `git push` / `git pull` operations against a locally managed clone. According to [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md), the tool "Syncs shareable parts of `~/.copilot` across machines using a GitHub repo you own" while "Keeping config sync and session history sync separate."

The tool runs on macOS, Linux, and Windows, requires Node.js `>=18.17`, and depends only on `git` being available on `PATH`. Its runtime dependencies are intentionally minimal: [`commander`](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json) for command parsing and [`better-sqlite3`](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json) for the local session history cache. The package is licensed MIT and authored by `lvxiaoxin`.

## Architecture and Core Components

The CLI is structured around a small set of commands (`onboard`, `push`, `pull`, `status`, `history …`) implemented in `src/commands/`. A high-level flow looks like this:

```mermaid
flowchart LR
  A[~/.copilot] -->|collect via manifest| B(copilot-sync push)
  B -->|secret scan| B
  B -->|commit + push| C[(Remote GitHub repo)]
  C -->|clone + checkout| D(copilot-sync pull)
  D -->|atomic write + backup| A
  E[session-state + session-store.db] -->|import| F[SQLite history store]
  F -->|export| C
```

The collection of files is governed by `src/manifest.js`. The `DEFAULT_MANIFEST` defines a `copilot` agent whose `base` is `~/.copilot` and whose default `include` list is `['mcp-config.json', 'settings.json', 'skills', 'agents', 'prompts']` ([src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js)). The `resolveManifest()` helper merges user-supplied overrides on top of these defaults, while the `AGENTS` constant is the canonical list of supported agents.

Session history is handled separately by `src/history-store.js`, which opens a local SQLite database via `openDb()`, applies a versioned `CORE_SCHEMA` containing tables such as `sessions`, `turns`, `checkpoints`, `session_files`, `session_refs`, and an FTS5 virtual table `search_index` ([src/history-store.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/history-store.js)). The `importSessionIndexes()` function wraps the import in a single transaction, validating that every payload carries the expected `SESSION_INDEX_VERSION` before upserting rows and rebuilding search entries.

User interaction during onboarding is driven by `src/prompt.js`, which exports a `createPrompter()` factory returning `ask` and `confirm` helpers built on Node's `readline`. The onboarding flow in `src/commands/onboard.js` verifies that `git` is available, asks for the remote URL, branch, and history mode, and then writes a managed `.gitattributes` and a `README.md` to the local clone to keep line endings stable across operating systems ([src/commands/onboard.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js)).

## Safety Model

Safety is the central design concern of `copilot-sync`. The README summarizes it as: "Allow-list collection for config files. Hard deny-list for auth/runtime/sensitive paths. Secret scanning before config push. Atomic writes and timestamped backups before overwrite." Each of these is implemented concretely:

| Layer | Mechanism | Source |
| --- | --- | --- |
| Allow-list | `DEFAULT_MANIFEST[agent].include` paths only | [src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js) |
| Hard deny-list | `HARD_DENY` globs (sessions, telemetry, logs, caches, `.sqlite`, etc.) | [src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js) |
| Secret scan | `TOKEN_PATTERNS` (GitHub PAT, OpenAI/Anthropic, AWS, Google, Slack, JWT) | [src/secrets.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/secrets.js) |
| Atomic writes | `writeFileAtomic` before any overwrite | [src/commands/push.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/push.js) |

The `HARD_DENY` list explicitly protects paths such as `**/config.json`, `**/settings.local.json`, `**/sessions/**`, `**/session-state/**`, `**/session-store.db*`, `**/history.jsonl`, `**/telemetry/**`, `**/cache/**`, and database files. These denials cannot be bypassed by user `include` overrides; only the `--unsafe-allow` flag disables them, and the code marks that path as "strongly discouraged" ([src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js)). Secret detection in [src/secrets.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/secrets.js) combines high-confidence token regexes with a heuristic assignment matcher for keys like `api_key`, `secret`, `token`, and `password`, and skips binary files via `looksBinary()`.

## Configuration and State

All persistent state lives under `~/.copilot-sync/` (overridable via the `COPILOT_SYNC_HOME` environment variable), with the following layout documented in the README: `config.json` for user settings, `repo/` for the managed clone, and `backups/<timestamp>/` for files replaced during `pull`. A typical `config.json` looks like:

```json
{
  "remote": "git@github.com:you/copilot-store.git",
  "branch": "main",
  "history": { "mode": "sync" },
  "manifest": {
    "copilot": { "include": ["mcp-config.json", "settings.json", "skills"] }
  }
}
```

The `history.mode` field is consumed during `onboard` and supports two values: `override` (the default, which archives local sessions additively to the remote and restores them locally with backup) and `sync` (a per-session-id reconciliation where the newer `updated_at` timestamp wins). During push, [src/commands/push.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/push.js) ensures a git identity (falling back to a synthetic `copilot-sync@localhost` identity with a warning), generates a subject line that includes the hostname and platform, and pushes to the configured branch. If nothing has changed, it short-circuits with `"Already up to date — nothing to push."`

In summary, `copilot-sync` is a thin but deliberately safety-conscious wrapper around `git` that turns a private GitHub repository into a portable, inspectable backup of a Copilot CLI workspace.

## See Also

- [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md) — user-facing documentation
- [package.json](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json) — package metadata and dependencies
- [src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js) — allow-list, deny-list, and agent configuration
- [src/secrets.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/secrets.js) — secret scanning rules
- [src/history-store.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/history-store.js) — local SQLite session index
- [src/commands/onboard.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js) — first-run setup
- [src/commands/push.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/push.js) — collect, scan, commit, push

---

<a id='page-onboarding'></a>

## Installation and Onboarding

### Related Pages

Related topics: [Project Overview](#page-overview), [Configuration Push and Pull](#page-config-sync)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [package.json](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json)
- [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md)
- [src/commands/onboard.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js)
- [src/commands/status.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/status.js)
- [src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js)
- [src/prompt.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/prompt.js)
- [src/config.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/config.js)
</details>

# Installation and Onboarding

## Purpose and Scope

`copilot-sync` is a cross-platform Node.js CLI that synchronizes a GitHub Copilot CLI setup — MCP server definitions, skills, agents, prompts, settings, and optional session history — between developer machines through a GitHub repository the user owns. The **Installation** stage makes the `copilot-sync` binary available on the host, while **Onboarding** captures the remote repository, branch, and session-history mode into `~/.copilot-sync/config.json` so subsequent `push` and `pull` operations have a destination.

The combined flow is intentionally lightweight: a single `npm install -g` followed by a single `copilot-sync onboard` interactive wizard. The wizard writes a deterministic JSON configuration that all later commands read, keeping the configuration model explicit and auditable. Source: [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md).

## Prerequisites and Installation

`copilot-sync` is published as the npm package `@lvxiaoxin/copilot-sync` and exposes a single executable, `copilot-sync`, declared in the `bin` field of the package manifest. Source: [package.json:9-12](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json).

The package declares the following runtime requirements:

| Requirement | Value | Source |
| --- | --- | --- |
| Node.js | `>=18.17` | `engines.node` in `package.json` |
| `git` binary | Must be on `PATH` (used by the managed clone) | Required by `git.js` for repo operations |
| npm | Any version that supports the `-g` global install flag | Used to install the package |

The only runtime dependencies are `commander` (CLI argument parsing) and `better-sqlite3` (session-index persistence). Neither is required for the onboarding path itself. Source: [package.json:35-38](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json).

A typical global install is:

```bash
npm install -g @lvxiaoxin/copilot-sync
```

After installation, the `copilot-sync` command is resolved through the package's `bin` entry and is invokable from any shell. The `prepublishOnly` script performs a syntax check of the entry point to keep published packages runnable. Source: [package.json:17-22](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json).

## Onboarding Workflow

`copilot-sync onboard` is an interactive command that produces the user's persistent configuration file. It is the only step that must run on a new machine before any `push`, `pull`, or `history` command will work. Source: [src/commands/onboard.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js).

### Flow

```mermaid
flowchart TD
    A[Run copilot-sync onboard] --> B{Existing config?}
    B -- "Yes, user declines reconfigure" --> Z[Exit, keep existing]
    B -- "No / user accepts reconfigure" --> C[Prompt: remote repo URL or owner/repo]
    C --> D[Normalize remote URL]
    D --> E[Prompt: branch name]
    E --> F[Prompt: history.mode override or sync]
    F --> G[Write ~/.copilot-sync/config.json]
    G --> H[status and push optional]
```

The command first inspects `loadConfig()` to see whether the user has already onboarded. If a remote is present, the wizard warns and offers a reconfigure prompt instead of silently overwriting. Source: [src/commands/onboard.js:9-21](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js).

### Captured Settings

The wizard collects three primary inputs:

1. **Remote repository** — accepted as either a full URL or an `owner/repo` shorthand and normalized via `normalizeRemote`. The loop retries until a non-empty remote is supplied. Source: [src/commands/onboard.js:23-32](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js).
2. **Branch** — defaults to `main` (or the existing branch on reconfigure). Source: [src/commands/onboard.js:34-34](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js).
3. **Session history mode** — must be either `override` or `sync`, parsed through `parseHistoryModeInput` and normalized via `normalizeHistoryMode`. Source: [src/commands/onboard.js:36-44](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js).

The interactive prompts are produced by `createPrompter()`, which abstracts over stdin/TTY and exposes `prompt.ask` for free-form input and `prompt.confirm` for yes/no questions. The `prompt.close()` call is wrapped in a `finally` block to guarantee terminal cleanup. Source: [src/commands/onboard.js:8-49](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js).

### Resulting Configuration

The wizard writes a `version: 1` JSON document to `~/.copilot-sync/config.json`. The shape written by the onboard command is:

```json
{
  "version": 1,
  "remote": "git@github.com:you/copilot-store.git",
  "branch": "main",
  "history": { "mode": "sync" }
}
```

Source: [src/commands/onboard.js:51-56](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js). This same file is the source of truth for every other command; the optional `manifest` block (used to override the default include list) can be added by hand or via future config-edit commands. Source: [src/manifest.js:30-50](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js).

### State Directories

Onboarding is the event that establishes the on-disk state layout, all rooted at `~/.copilot-sync` (overridable through `COPILOT_SYNC_HOME`):

| Path | Purpose |
| --- | --- |
| `config.json` | User configuration produced by `onboard` |
| `repo/` | Local managed clone of the remote store |
| `backups/<timestamp>/` | Timestamped snapshots before pull/restore overwrites |

Source: [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md), [src/config.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/config.js).

## Verifying with `status`

Once onboarded, `copilot-sync status` provides a quick confirmation that the wizard produced a valid configuration. The status command prints the current host, remote, branch, normalized history mode, and whether the managed clone directory exists and is a git repository. Source: [src/commands/status.js:6-19](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/status.js).

It then resolves the manifest — applying any user overrides on top of the defaults defined in `src/manifest.js` — and reports which paths would be collected for each agent. The default `copilot` agent pulls `mcp-config.json`, `settings.json`, `skills/`, `agents/`, and `prompts/` from `~/.copilot`. Source: [src/manifest.js:34-42](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js), [src/commands/status.js:21-30](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/status.js).

A healthy post-onboard status line shows a green `present` marker next to the clone path; a `not cloned yet` warning is expected on a fresh install and is cleared on the first `push` or `pull`. Source: [src/commands/status.js:18-19](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/status.js).

## Common Failure Modes

- **Missing `git` on `PATH`** — `git.js` will fail early when the managed clone is first needed, so confirm `git --version` succeeds before running `onboard` on a minimal container.
- **Insufficient Node.js version** — versions below `18.17` are rejected by npm at install time due to the `engines` declaration. Source: [package.json:14-16](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json).
- **Re-running `onboard` accidentally** — the wizard does not overwrite without confirmation; the previous `remote`, `branch`, and `history.mode` are offered as defaults. Source: [src/commands/onboard.js:13-21](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js).
- **Public remote repository** — the README explicitly recommends a **private** repository because config and session history may contain personal workflow data. Source: [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md).

## See Also

- Configuration and State Layout
- `push` and `pull` Workflow
- History Sync Modes
- Safety Model (deny-list and secret scan)

---

<a id='page-config-sync'></a>

## Configuration Push and Pull

### Related Pages

Related topics: [Installation and Onboarding](#page-onboarding)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/commands/push.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/push.js)
- [src/commands/pull.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/pull.js)
- [src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js)
- [src/collect.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/collect.js)
- [src/repo.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/repo.js)
- [src/git.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/git.js)
- [src/secrets.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/secrets.js)
- [src/commands/onboard.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js)
- [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md)
- [package.json](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json)
</details>

# Configuration Push and Pull

## Purpose and Scope

`copilot-sync` provides a Git-backed transport layer that keeps the shareable parts of the GitHub Copilot CLI configuration synchronized between developer machines. The two core commands — `copilot-sync push` and `copilot-sync pull` — move artifacts such as `mcp-config.json`, `settings.json`, and the `skills/`, `agents/`, and `prompts/` directories between a local `~/.copilot` directory and a user-owned GitHub repository. Source: [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md).

The mechanism is intentionally narrow: it only handles the files enumerated in the manifest, never arbitrary `~/.copilot` content. Session history, credentials, caches, and databases are routed through a separate command family (`copilot-sync history ...`) so that configuration sync remains safe to run on autopilot. Source: [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md).

The project ships as a single ESM binary declared in `package.json` (`"bin": { "copilot-sync": "bin/copilot-sync.js" }`) and depends on `better-sqlite3` and `commander`, with `engines.node >= 18.17`. Source: [package.json](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json).

## End-to-End Workflow

A typical push or pull cycle passes through five layers: configuration loading, manifest resolution, file collection, secret scanning / safety checks, and git transport. The flow below shows the path for both directions.

```mermaid
flowchart LR
    A[~/.copilot-sync/config.json] --> B[Resolve manifest]
    B --> C{Collect files}
    C -- push --> D[Secret scan]
    D --> E[Atomic write to managed repo]
    E --> F[git commit + push]
    C -- pull --> G[git fetch + checkout]
    G --> H[Timestamped backup of overwritten files]
    H --> I[Atomic write to ~/.copilot]
```

### Manifest resolution

The default manifest defines what is collected:

```
copilot:
  base: '~/.copilot'
  include: ['mcp-config.json', 'settings.json', 'skills', 'agents', 'prompts']
```

User overrides from `config.json` are merged on top of `DEFAULT_MANIFEST` through `resolveManifest`, which preserves a per-agent `base` and an `include` array. If a user does not provide an `include` list, the default is used verbatim. Source: [src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js).

### Collection and deny-list

Collection walks the agent base directory and selects only entries that appear in `manifest.<agent>.include`. Before any file is staged, each candidate path is matched against `HARD_DENY` — a non-overridable glob list that blocks auth files (`**/config.json`, `**/settings.local.json`), session state, telemetry, caches, logs, databases, and shell snapshots. User include entries cannot bypass the deny-list; the only way to disable it is the `--unsafe-allow` flag, which is explicitly discouraged. Source: [src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js).

### Push path

The push command assembles a per-agent directory tree inside the managed clone, writes a `.agent-sync-meta.json` metadata sidecar containing `{ version: 1, modes }` when there are modes to record, then commits and pushes. The commit subject follows the pattern `sync: <hostname> (<platform>) <ISO timestamp>`, with a body listing the number of files per agent. Source: [src/commands/push.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/push.js).

Identity handling is defensive: if the local clone lacks a `user.name` or `user.email`, `ensureIdentity` installs `copilot-sync` / `copilot-sync@localhost` as a fallback and reports `usedFallback: true` so the user is warned. Source: [src/git.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/git.js).

The push pipeline aborts cleanly when there are no staged changes — `git status --porcelain` returns empty, and the command reports `Already up to date — nothing to push.` Source: [src/commands/push.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/push.js).

### Pull path

Pull is the mirror image. After fetching the configured branch, the command writes remote files into `~/.copilot`. Crucially, it never deletes local files that are absent from the remote — only files that exist remotely are written. Every overwritten file is first copied into a timestamped directory under `~/.copilot-sync/backups/<timestamp>/` so that recovery is possible even after a successful pull. Source: [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md).

## Safety Mechanisms

The safety model is layered so that each defense compensates for gaps in the others:

| Layer | Mechanism | Source |
| --- | --- | --- |
| Allow-list | `DEFAULT_MANIFEST.include` enumerates exactly which files are candidates for collection. | [src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js) |
| Hard deny-list | `HARD_DENY` blocks sensitive paths and cannot be overridden by user `include`. | [src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js) |
| Secret scanning | High-confidence regexes match GitHub tokens (`gh[pousr]_…`, `github_pat_…`), OpenAI/Anthropic keys (`sk-…`, `sk-ant-…`), AWS access keys (`AKIA…`), Google API keys (`AIza…`), Slack tokens (`xox[abprs]-…`), and JWTs. | [src/secrets.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/secrets.js) |
| Atomic writes | Files are staged via `writeFileAtomic` so partial writes never appear in the managed repo or in `~/.copilot`. | [src/commands/push.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/push.js) |
| Backups | `~/.copilot-sync/backups/<timestamp>/` receives every file before it is overwritten by a pull. | [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md) |

The secret scanner combines whole-line token shapes with an `ASSIGN_RE` for `key: value` / `key = "value"` patterns targeting names like `api_key`, `secret`, `token`, `password`, `client_secret`, and `private_key`. Source: [src/secrets.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/secrets.js). Together these checks mean that even if a user widens their include list, an accidental `settings.local.json` or stray credential will be rejected.

The onboarding flow additionally seeds the managed repository with a `.gitattributes` that pins line endings per file type and a README explaining the contract: artifacts only, no secrets. Source: [src/commands/onboard.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js). On Windows, `configureManagedRepo` forces `core.autocrlf=false` and `core.safecrlf=false` so that the sync store does not depend on the host user's global CRLF settings. Source: [src/git.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/git.js).

## Configuration and Command Reference

### Configuration file

`~/.copilot-sync/config.json` holds the runtime settings:

```json
{
  "remote": "git@github.com:you/copilot-store.git",
  "branch": "main",
  "history": { "mode": "sync" },
  "manifest": {
    "copilot": { "include": ["mcp-config.json", "settings.json", "skills"] }
  }
}
```

The state root can be relocated by exporting `COPILOT_SYNC_HOME`. Source: [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md).

### Command matrix

| Command | Purpose | Notable flags |
| --- | --- | --- |
| `copilot-sync onboard` | Configure remote, branch, and history mode; seed `.gitattributes` and README. | — |
| `copilot-sync status` | Report remote, branch, history mode, and sync summary. | — |
| `copilot-sync push` | Collect, secret-scan, commit, and push. | `--dry-run`, `--unsafe-allow` |
| `copilot-sync pull` | Fetch, back up, and atomically write into `~/.copilot`. | `--dry-run` |

Source: [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md).

### Common failure modes

- **Push fails with auth error.** `push` wraps the underlying git error in a `UserError` that prints `Push failed. Check your access to <remote>` plus the captured stderr. Verify SSH key or credential helper setup. Source: [src/commands/push.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/push.js).
- **Secret scan rejects a push.** The collected file matches one of the token patterns; remove the credential and re-run, or isolate it under a denied path.
- **No git on PATH.** `ensureGitAvailable` throws a `UserError` instructing the user to install git. Source: [src/git.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/git.js).
- **Line-ending churn on Windows.** Already mitigated by the local `core.autocrlf=false` config; if you see diffs, ensure the cloned repo still has the seeded `.gitattributes`. Source: [src/commands/onboard.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js).

## See Also

- Session history synchronization (`copilot-sync history ...`) — covered by a separate wiki page, backed by `src/history-store.js`.
- Onboarding and repository seeding — see `src/commands/onboard.js`.
- Recommended companion tool for searching and resuming sessions: [copilot-starter](https://github.com/lvxiaoxin/copilot-starter). Source: [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md).

---

<a id='page-history-sync'></a>

## Session History Sync

### Related Pages

Related topics: [Configuration Push and Pull](#page-config-sync)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/commands/history.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/history.js)
- [src/commands/onboard.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/commands/onboard.js)
- [src/history-store.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/history-store.js)
- [src/manifest.js](https://github.com/lvxiaoxin/copilot-sync/blob/main/src/manifest.js)
- [README.md](https://github.com/lvxiaoxin/copilot-sync/blob/main/README.md)
- [package.json](https://github.com/lvxiaoxin/copilot-sync/blob/main/package.json)
</details>

# Session History Sync

## Overview and Scope

Session History Sync is an **opt-in** companion feature of `copilot-sync` that carries GitHub Copilot CLI session artifacts between machines, on top of the regular config sync (`mcp-config.json`, `skills/`, `agents/`, `prompts/`, `settings.json`). It is intentionally kept separate from the default `push` / `pull` commands so users must explicitly choose to share session data. Source: [README.md]()

The subsystem reads and writes from a local SQLite index maintained by Copilot itself, then mirrors the relevant on-disk artifacts (`~/.copilot/session-state/`, `~/.copilot/session-store.db` related metadata) into the user-owned Git remote. Source: [README.md]() Source: [src/commands/history.js]()

The runtime is a single ESM CLI (`@lvxiaoxin/copilot-sync`) built on `commander ^12.1.0` and `better-sqlite3 ^11.10.0`, requiring Node.js `>=18.17`. Source: [package.json]()

## History Modes

`copilot-sync onboard` writes a `history.mode` field into `~/.copilot-sync/config.json`. Two values are supported and parsed via `parseHistoryModeInput()` / `normalizeHistoryMode()`:

| Mode | `history push` behavior | `history pull` behavior |
| --- | --- | --- |
| `override` (default) | Archive local sessions additively to remote | Restore remote sessions locally; backup before overwrite |
| `sync` | Only push `local-only` or `local-newer` sessions | Only pull `remote-only` or `remote-newer` sessions |

Source: [README.md]() Source: [src/commands/onboard.js]()

In `sync` mode, the **conflict unit is a single session id**, and the side with the newer `mtimeMs`/`updated_at` wins in the direction of the current command. Active local sessions are skipped unless `--force` is supplied, and any local files replaced by a pull are still mirrored to `~/.copilot-sync/backups/<timestamp>/`. Source: [README.md]() Source: [src/commands/history.js]()

## Data Model

The history subsystem reads and writes a SQLite database using `better-sqlite3`. The schema is created lazily in `ensureSchema()` and versioned through `schema_version` (`SESSION_INDEX_VERSION`). Source: [src/history-store.js]()

Core tables:

- `sessions` — one row per session, carrying `id`, `cwd`, `repository`, `host_type`, `branch`, `summary`, `created_at`, `updated_at`. Source: [src/history-store.js]()
- `turns` — ordered conversation turns per session. Source: [src/history-store.js]()
- `checkpoints` — versioned snapshots (`title`, `overview`, `history`, `work_done`, `technical_details`, `important_files`, `next_steps`), keyed by `(session_id, checkpoint_number)`. Source: [src/history-store.js]()
- `session_files` — files touched during a session, keyed by `(session_id, file_path)`. Source: [src/history-store.js]()
- `session_refs` — references such as URLs, branches, or commits per session. Source: [src/history-store.js]()
- `search_index` — an FTS5 virtual table providing full-text search across `content`, scoped by `session_id`, `source_type`, `source_id`. Source: [src/history-store.js]()

`importSessionIndexes()` performs an upsert transaction: for each payload versioned with `SESSION_INDEX_VERSION`, it inserts the session row, deletes prior `turns`/`checkpoints`/`files`/`refs`, and re-inserts the new rows in a single SQLite transaction. Payloads with mismatched versions are rejected with `Unsupported session index version for <id>: <version>`. Source: [src/history-store.js]()

## Command Flow

`history push` and `history pull` are dispatched from `src/commands/history.js`. The flow is illustrated below.

```mermaid
flowchart TD
    A[copilot-sync history push|or pull] --> B{history.mode}
    B -->|sync| C[historySyncMode direction=push or pull]
    B -->|override| D[Standard flow]
    D --> E[ensureGitAvailable + resolveAgent]
    E --> F[listLocalSessions]
    F --> G[Apply --session selector]
    G --> H{--force?}
    H -->|no| I[Skip active sessions]
    H -->|yes| J[Include all]
    I --> K{--since window?}
    J --> K
    K -->|yes| L[Filter by mtimeMs]
    K -->|no| M[Keep all]
    L --> N[collectSession]
    M --> N
    N --> O[privacyReminder]
    O --> P[Copy files + write session index to remote store]
    P --> Q[git commit + push]
```

Source: [src/commands/history.js]()

Key behaviors implemented in `src/commands/history.js`:

- Selector resolution supports a full session id **or a unique prefix**, with `resolveSelector()` returning matches or an empty set when ambiguous. Source: [src/commands/history.js]()
- Active sessions are filtered via `selected.filter((s) => s.active)` before collection; `--force` keeps them in the set so live writes can still be captured. Source: [src/commands/history.js]()
- The `--since` window is parsed by `parseSince()` and compared against `session.mtimeMs`; sessions older than the window are dropped on push only. Source: [src/commands/history.js]()
- Before any transfer, `privacyReminder()` prints a one-time prompt; the user can pre-empt it with `--yes` on subsequent invocations. Source: [src/commands/history.js]()
- `collectSession()` enumerates the session's on-disk files so they can be staged and shipped next to the index payload. Source: [src/commands/history.js]()

## Safety and Privacy

Session history can include secrets, file contents, and command output, so the feature layers multiple guards:

1. **Explicit opt-in** — history is excluded from normal `push`; only `copilot-sync history ...` transfers it. Source: [README.md]()
2. **Deny-list isolation** — the manifest in `src/manifest.js` excludes `**/*.db*`, `**/*.sqlite*`, `**/*.log`, `**/backups/**`, `**/memories/**`, `**/ide/**`, `**/installation_id`, `**/version.json`, and similar runtime/credential paths from config sync, reducing accidental leakage. Source: [src/manifest.js]()
3. **Privacy confirmation** — `privacyReminder()` is invoked before any history push. Source: [src/commands/history.js]()
4. **Backup before overwrite** — pull operations preserve overwritten local files under `~/.copilot-sync/backups/<timestamp>/`. Source: [README.md]()
5. **Large-file guard** — `--force-large` is required to push files near GitHub's 100 MB limit, preventing accidental oversized commits. Source: [README.md]()

## Configuration

Session sync is configured in `~/.copilot-sync/config.json`, written by `onboard()`:

```json
{
  "remote": "git@github.com:you/copilot-store.git",
  "branch": "main",
  "history": { "mode": "sync" },
  "manifest": {
    "copilot": { "include": ["mcp-config.json", "settings.json", "skills"] }
  }
}
```

The state root can be relocated by setting the `COPILOT_SYNC_HOME` environment variable, which shifts `config.json`, the managed clone (`repo/`), and `backups/` together. Source: [README.md]() Source: [src/commands/onboard.js]()

## Flags Reference

| Flag | Scope | Effect |
| --- | --- | --- |
| `--session <id>` | history | Target one session by full id or unique prefix. Source: [src/commands/history.js]() |
| `--since <window>` | history push | Filter local sessions by `mtimeMs` (`7d`, `2w`, `1m`, `1y`). Source: [src/commands/history.js]() |
| `--dry-run` | history | Preview history changes only. Source: [README.md]() |
| `--yes` | history push | Skip the one-time privacy confirmation. Source: [README.md]() |
| `--force` | history | Include/overwrite sessions that look active. Source: [src/commands/history.js]() |
| `--force-large` | history | Allow files near GitHub's 100 MB limit. Source: [README.md]() |

## Common Failure Modes

- **Branch does not exist on first run** — `onboard.js` falls back to `git clone <remote> <dir>` (no `--branch`) and then checks out the requested branch, so a fresh empty store is not a blocker. Source: [src/commands/onboard.js]()
- **Missing local sessions** — `history push` logs `No <agent> sessions found at <path>` and exits cleanly when `listLocalSessions()` returns `[]`. Source: [src/commands/history.js]()
- **Version mismatched payload** — `importSessionIndexes()` rejects the whole transaction if a payload's `version` differs from `SESSION_INDEX_VERSION`, protecting against schema drift. Source: [src/history-store.js]()
- **Active live session** — without `--force`, in-progress sessions are skipped to avoid torn DB copies; the user must opt in knowingly. Source: [src/commands/history.js]()

## See Also

- [README — Session History Usage](https://github.com/lvxiaoxin/copilot-sync#session-history-usage)
- [copilot-sync on npm](https://www.npmjs.com/package/@lvxiaoxin/copilot-sync)
- [copilot-starter (companion session search/preview tool)](https://github.com/lvxiaoxin/copilot-starter)

---

<!-- evidence_pipeline_checked: true -->

---

## Pitfall Log

Project: lvxiaoxin/copilot-sync

Summary: Found 6 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Capability evidence risk - Capability evidence risk requires verification.

## 1. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/lvxiaoxin/copilot-sync

## 2. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/lvxiaoxin/copilot-sync

## 3. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/lvxiaoxin/copilot-sync

## 4. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/lvxiaoxin/copilot-sync

## 5. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/lvxiaoxin/copilot-sync

## 6. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/lvxiaoxin/copilot-sync

<!-- canonical_name: lvxiaoxin/copilot-sync; human_manual_source: deepwiki_human_wiki -->
