Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "agent-environment-backup",
"version": "0.2.0",
"description": "Agent Skills and Python CLI for offline Codex and Claude Code environment backup, restore, and health checks.",
"author": {
"name": "gaoguobin",
"url": "https://github.com/gaoguobin"
},
"homepage": "https://github.com/gaoguobin/codex-environment-backup#readme",
"repository": "https://github.com/gaoguobin/codex-environment-backup",
"license": "MIT",
"keywords": [
"agent-skills",
"codex",
"openai-codex",
"codex-skill",
"claude-code",
"ai-agents",
"environment-backup",
"backup-restore",
"sqlite-backup",
"local-first"
],
"skills": "./skills/",
"interface": {
"displayName": "Agent Environment Backup",
"shortDescription": "Back up and restore local agent environments.",
"longDescription": "Bundle Agent Skills and a Python CLI for local, offline Codex and Claude Code environment backup, restore, listing, and health checks.",
"developerName": "gaoguobin",
"category": "Developer Tools",
"capabilities": [
"Local workflow",
"Backup",
"Restore",
"Health checks"
],
"websiteURL": "https://github.com/gaoguobin/codex-environment-backup",
"privacyPolicyURL": "https://github.com/gaoguobin/codex-environment-backup#safety-model",
"termsOfServiceURL": "https://github.com/gaoguobin/codex-environment-backup/blob/main/LICENSE",
"defaultPrompt": [
"Back up current Codex environment.",
"List Codex environment backups.",
"Back up current Claude Code environment."
],
"brandColor": "#10A37F"
}
}
84 changes: 83 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# codex-environment-backup

[![CI](https://github.com/gaoguobin/codex-environment-backup/actions/workflows/ci.yml/badge.svg)](https://github.com/gaoguobin/codex-environment-backup/actions/workflows/ci.yml)

Offline backup, restore, and health checks for local Codex and Claude Code
environments. The `--profile codex|claude-code` flag selects which environment
to operate on; Codex is the default.
Expand All @@ -9,7 +11,7 @@ language. The Python CLI is the deterministic implementation layer and remains
available for advanced users, automation, smoke tests, and recovery when the
agent itself is unavailable.

[中文说明](#chinese) · [Install](#install) · [Daily Use](#daily-use) · [Restore](#restore) · [Update](#update) · [Uninstall](#uninstall) · [Safety](#safety-model) · [Advanced CLI](#advanced-cli)
[中文说明](#chinese) · [Agent Skills](#agent-skills-and-discovery) · [Plugin Readiness](#plugin-readiness) · [Install](#install) · [Daily Use](#daily-use) · [Restore](#restore) · [Update](#update) · [Uninstall](#uninstall) · [Safety](#safety-model) · [Advanced CLI](#advanced-cli)

## Why

Expand All @@ -30,6 +32,68 @@ workflow instead of relying on memory or manual copy steps.
| No-command handoff | Each backup includes `RESTORE.md`, plain-text instructions, platform helpers, and a standalone restore script. |
| Sandbox conservative | `.sandbox-secrets`, sandbox caches, temp folders, and live SQLite WAL/SHM files are excluded. |

## Agent Skills and Discovery

This repository includes Agent Skills for local environment backup workflows:

| Skill | Path | Primary use case |
| --- | --- | --- |
| `codex-environment-backup` | `skills/codex-environment-backup/SKILL.md` | Let Codex back up, restore, inspect, list, install, update, and uninstall local Codex environment backups. |
| `claude-code-environment-backup` | `skills/claude-code-environment-backup/SKILL.md` | Let Claude Code back up, restore, inspect, list, install, update, and uninstall local Claude Code environment backups. |

Tools that index public GitHub repositories for Agent Skills can discover the
skills at the paths above. This project does not claim to be listed on SkillsMP
or any other marketplace, and it is not an official OpenAI plugin or official
marketplace project.

Typical natural-language triggers:

```text
Back up current Codex environment
List Codex environment backups
Check Codex environment backup health
Restore this Codex backup
Back up current Claude Code environment
List Claude Code environment backups
Check Claude Code environment backup health
Restore this Claude Code backup
```

Safety boundaries for both skills:

- Backups are local and sensitive; the tool does not upload archives.
- Restore is dry-run by default and requires explicit apply confirmation.
- Apply restore creates a pre-restore backup first.
- API keys, auth payloads, and conversation contents are not printed.
- `.sandbox-secrets`, sandbox/temp directories, and live SQLite WAL/SHM files are excluded.
- ACLs, hooks, providers, and install state are not changed unless the user asks for the corresponding install/update/uninstall/restore workflow.

Doctor and smoke checks:

```powershell
python -m agent_environment_backup doctor
python -m agent_environment_backup --profile claude-code doctor
python -m agent_environment_backup list-backups
python -m unittest discover -s tests
```

There is no benchmark command; this repository focuses on backup, restore,
listing, and health checks.

## Plugin Readiness

Codex plugin documentation defines a plugin as a package with a required
`.codex-plugin/plugin.json` manifest and optional bundled `skills/`, apps, MCP
servers, hooks, and assets. This repository includes plugin metadata that points
to `./skills/` so future Codex plugin tooling can identify the bundled Agent
Skills.

Current supported installation is still the Codex-managed or Claude
Code-managed flow in [Install](#install). The plugin metadata is preparatory
discovery and packaging metadata only; it does not install hooks, change
provider config, start background services, restore files, or imply an official
marketplace listing.

## Install

### Codex
Expand Down Expand Up @@ -486,6 +550,24 @@ Fetch and follow instructions from https://raw.githubusercontent.com/gaoguobin/c
- `codex-fast-proxy` 只是可选集成;检测到可用时记录安全摘要,不打印 provider URL、本机状态路径或完整 stdout;
不可用时记为 skipped,不会失败。

### Agent Skill 和可发现性

这个仓库包含两个 Agent Skill:

| Skill | 路径 | 用途 |
| --- | --- | --- |
| `codex-environment-backup` | `skills/codex-environment-backup/SKILL.md` | 让 Codex 管理本地 Codex 环境备份、恢复、体检、列表、安装、更新和卸载。 |
| `claude-code-environment-backup` | `skills/claude-code-environment-backup/SKILL.md` | 让 Claude Code 管理本地 Claude Code 环境备份、恢复、体检、列表、安装、更新和卸载。 |

会索引公开 GitHub 仓库中 Agent Skills 的工具,可以通过上面的路径发现这些 skill。本项目不声称已经被
SkillsMP 或其它 marketplace 收录,也不声称是 OpenAI 官方 plugin 或官方 marketplace 项目。

### Plugin readiness

当前仓库包含 `.codex-plugin/plugin.json`,并指向根目录下的 `./skills/`。这只是为 Codex plugin
分发格式做准备的发现/打包元数据,不会改变安装流程、不会安装 hook、不会改 provider 配置、不会启动
后台服务,也不代表已经进入官方 marketplace。

### 高级 CLI

CLI 留给高级用户、CI、smoke test、自动化和 Codex 自身不可用时的恢复兜底。普通备份、检查和列出备份
Expand Down
18 changes: 18 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,31 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = []
license = { text = "MIT" }
keywords = [
"agent-skills",
"codex",
"openai-codex",
"codex-skill",
"claude-code",
"ai-agents",
"environment-backup",
"backup-restore",
"sqlite-backup",
"local-first",
]
classifiers = [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Operating System :: OS Independent",
]

[project.urls]
Homepage = "https://github.com/gaoguobin/codex-environment-backup#readme"
Repository = "https://github.com/gaoguobin/codex-environment-backup"
Issues = "https://github.com/gaoguobin/codex-environment-backup/issues"
Documentation = "https://github.com/gaoguobin/codex-environment-backup#readme"

[project.scripts]
agent-environment-backup = "agent_environment_backup.cli:main"
codex-environment-backup = "codex_environment_backup.cli:main"
Expand Down
2 changes: 1 addition & 1 deletion skills/claude-code-environment-backup/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: claude-code-environment-backup
description: Back up, restore, inspect, list, install, update, or uninstall local Claude Code environment backup tooling. Use when the user asks to back up the current Claude Code environment, restore Claude Code from a backup, check whether a backup is valid, list Claude Code backups, install/update/uninstall the Claude Code backup skill, or set up manual/periodic Claude Code environment backup workflows with phrases such as "备份 Claude Code 环境", "恢复 Claude Code 备份", "检查 Claude Code 备份", "列出 Claude Code 备份", "安装 Claude Code 环境备份工具", "更新 Claude Code 环境备份工具", "卸载 Claude Code 环境备份工具", or "back up Claude Code environment", "restore Claude Code backup", "check Claude Code backup health", "list Claude Code backups".
description: Claude Code environment backup, restore, doctor, and list workflows for local ~/.claude state. Use for requests like back up Claude Code, restore Claude Code backup, list/check Claude Code backups, or install/update/uninstall the backup skill.
---

Use this skill when the user wants Claude Code to manage local Claude Code environment backups. The user-facing interface is natural language; the Python CLI is the source of truth that Claude Code runs behind the scenes.
Expand Down
4 changes: 4 additions & 0 deletions skills/claude-code-environment-backup/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
interface:
display_name: "Claude Code Environment Backup"
short_description: "Back up and restore Claude Code state."
default_prompt: "Use $claude-code-environment-backup to back up the current Claude Code environment and report the local archive path, SHA256, and safety note."
2 changes: 1 addition & 1 deletion skills/codex-environment-backup/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: codex-environment-backup
description: Back up, restore, inspect, list, install, update, or uninstall local Codex environment backup tooling. Use when the user asks to back up the current Codex environment, restore Codex from a backup, check whether a backup is valid, list Codex backups, install/update/uninstall the Codex backup skill, or set up manual/periodic Codex environment backup workflows with phrases such as "备份当前 Codex 环境", "恢复 Codex 环境", "检查 Codex 备份", "列出 Codex 备份", "安装 Codex 环境备份工具", "更新 Codex 环境备份工具", "卸载 Codex 环境备份工具", or "定期备份 Codex".
description: Codex environment backup, restore, doctor, and list workflows for local CODEX_HOME state. Use for requests like back up Codex, restore Codex backup, list/check Codex backups, or install/update/uninstall the backup skill.
---

Use this skill when the user wants Codex to manage local Codex environment backups. The user-facing interface is natural language; the Python CLI is the source of truth that Codex runs behind the scenes.
Expand Down
7 changes: 4 additions & 3 deletions skills/codex-environment-backup/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
display_name: Codex Environment Backup
short_description: Back up, restore, and inspect local Codex environment state.
default_prompt: Back up the current Codex environment and report the local archive path, SHA256, and safety note.
interface:
display_name: "Codex Environment Backup"
short_description: "Back up and restore Codex state."
default_prompt: "Use $codex-environment-backup to back up the current Codex environment and report the local archive path, SHA256, and safety note."
71 changes: 71 additions & 0 deletions tests/test_docs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import json
import tomllib
import unittest
from pathlib import Path

Expand All @@ -15,6 +17,8 @@ def test_readme_keeps_bilingual_natural_language_entry(self) -> None:
readme = self.read("README.md")
self.assertIn("[中文说明](#chinese)", readme)
self.assertIn("## 中文说明", readme)
self.assertIn("[Agent Skills](#agent-skills-and-discovery)", readme)
self.assertIn("[Plugin Readiness](#plugin-readiness)", readme)
self.assertIn("Fetch and follow instructions from", readme)
self.assertIn("备份当前 Codex 环境", readme)
self.assertIn(".claude/INSTALL.md", readme)
Expand All @@ -25,6 +29,28 @@ def test_readme_keeps_bilingual_natural_language_entry(self) -> None:
self.assertLess(readme.index("## Daily Use"), readme.index("## Advanced CLI"))
self.assertLess(readme.index("## 中文说明"), readme.index("### 高级 CLI"))

def test_readme_declares_agent_skills_and_discovery_boundaries(self) -> None:
readme = self.read("README.md")
self.assertIn("## Agent Skills and Discovery", readme)
self.assertIn("`codex-environment-backup`", readme)
self.assertIn("`skills/codex-environment-backup/SKILL.md`", readme)
self.assertIn("`claude-code-environment-backup`", readme)
self.assertIn("`skills/claude-code-environment-backup/SKILL.md`", readme)
self.assertIn("Tools that index public GitHub repositories for Agent Skills", readme)
self.assertIn("does not claim to be listed on SkillsMP", readme)
self.assertIn("not an official OpenAI plugin", readme)
self.assertIn("There is no benchmark command", readme)

def test_readme_declares_plugin_readiness_without_marketplace_claims(self) -> None:
readme = self.read("README.md")
normalized = " ".join(readme.split())
self.assertIn("## Plugin Readiness", readme)
self.assertIn("`.codex-plugin/plugin.json`", readme)
self.assertIn("`./skills/`", readme)
self.assertIn("preparatory discovery and packaging metadata only", normalized)
self.assertIn("does not install hooks", readme)
self.assertIn("imply an official marketplace listing", normalized)

def test_codex_lifecycle_docs_keep_chinese_handoff(self) -> None:
install = self.read(".codex/INSTALL.md")
update = self.read(".codex/UPDATE.md")
Expand Down Expand Up @@ -98,6 +124,51 @@ def test_codex_skill_uses_new_module_name(self) -> None:
self.assertNotIn("restore-codex-environment", skill)
self.assertIn("restore-environment.cmd", skill)

def test_skill_ui_metadata_exists_for_both_skills(self) -> None:
codex = self.read("skills/codex-environment-backup/agents/openai.yaml")
claude = self.read("skills/claude-code-environment-backup/agents/openai.yaml")
self.assertIn("interface:", codex)
self.assertIn('display_name: "Codex Environment Backup"', codex)
self.assertIn("$codex-environment-backup", codex)
self.assertIn("interface:", claude)
self.assertIn('display_name: "Claude Code Environment Backup"', claude)
self.assertIn("$claude-code-environment-backup", claude)

def test_pyproject_has_discovery_metadata(self) -> None:
data = tomllib.loads(self.read("pyproject.toml"))
project = data["project"]
self.assertEqual(project["name"], "agent-environment-backup")
for keyword in (
"agent-skills",
"codex",
"openai-codex",
"codex-skill",
"claude-code",
"environment-backup",
"backup-restore",
"sqlite-backup",
):
self.assertIn(keyword, project["keywords"])
urls = project["urls"]
self.assertEqual(urls["Repository"], "https://github.com/gaoguobin/codex-environment-backup")
self.assertEqual(urls["Issues"], "https://github.com/gaoguobin/codex-environment-backup/issues")
self.assertIn("#readme", urls["Documentation"])

def test_codex_plugin_manifest_is_valid_and_points_to_skills(self) -> None:
manifest = json.loads(self.read(".codex-plugin/plugin.json"))
self.assertEqual(manifest["name"], "agent-environment-backup")
self.assertEqual(manifest["skills"], "./skills/")
self.assertEqual(manifest["repository"], "https://github.com/gaoguobin/codex-environment-backup")
self.assertIn("agent-skills", manifest["keywords"])
self.assertIn("codex", manifest["keywords"])
self.assertIn("claude-code", manifest["keywords"])
self.assertIn("interface", manifest)
self.assertEqual(manifest["interface"]["category"], "Developer Tools")
self.assertLessEqual(len(manifest["interface"]["defaultPrompt"]), 3)
self.assertNotIn("hooks", manifest)
self.assertNotIn("mcpServers", manifest)
self.assertNotIn("apps", manifest)


if __name__ == "__main__":
unittest.main()
Loading