diff --git a/README.ko.md b/README.ko.md index 8413c03..76c4033 100644 --- a/README.ko.md +++ b/README.ko.md @@ -2,6 +2,8 @@ Savepoint는 이전 대화 컨텍스트에 의존하지 않고 새 코딩 에이전트가 현재 repo/Git 상태에서 이어갈 수 있게 `.savepoint/SAVEPOINT.md`를 생성하거나 검증하는 skill입니다. +Savepoint는 가벼운 대화 요약이 아닙니다. 복구 가능한 repo/Git checkpoint입니다. 파일 복구가 필요 없으면 `/savepoint text`나 일반 요약을 사용하세요. + ## 30초 사용법 ```text @@ -34,7 +36,7 @@ Savepoint는 이전 대화 컨텍스트에 의존하지 않고 새 코딩 에이 - file mode는 `.savepoint/SAVEPOINT.md`를 씁니다. - artifact는 repo/Git snapshot, `## Resume Prompt`, 마지막 `SAVEPOINT_V1` marker block을 포함합니다. -- `REDACTION_CHECKED: yes` 전에 생성된 artifact의 secret-like 값을 스캔합니다. +- `REDACTION_CHECKED: yes` 전에 generated artifact를 pattern-based secret-like scan으로 검사합니다. - bundled validator가 marker shape와 safe-resume 필드를 검사합니다. - load 시 현재 disk state가 savepoint text보다 우선합니다. @@ -46,17 +48,7 @@ Savepoint는 이전 대화 컨텍스트에 의존하지 않고 새 코딩 에이 - 미래 충돌 없음 - text mode만으로 repo 상태를 복구할 수 있음 -## Runtime command - -public entrypoint는 다음입니다. - -```bash -python3 scripts/savepoint.py save --input .savepoint/input.json --output .savepoint/SAVEPOINT.md --assert-no-active-commands --scan-redaction --validate -python3 scripts/savepoint.py init-input --output .savepoint/input.json -python3 scripts/savepoint.py validate .savepoint/SAVEPOINT.md -python3 scripts/savepoint.py inspect .savepoint/SAVEPOINT.md --json -python3 scripts/savepoint.py text --input .savepoint/input.json -``` +## 최소 CLI 흐름 portable skill entrypoint는 `skills/savepoint/scripts/savepoint.py`입니다. repository-local 명령은 `scripts/savepoint.py`를 사용합니다. @@ -71,42 +63,7 @@ python3 scripts/savepoint.py save --input .savepoint/input.json --output .savepo python3 scripts/savepoint.py inspect .savepoint/SAVEPOINT.md --json ``` -짧은 savepoint를 JSON 편집 없이 만들 때: - -```bash -python3 scripts/savepoint.py save \ - --output .savepoint/SAVEPOINT.md \ - --assert-no-active-commands --scan-redaction --validate \ - --goal "focused fix 마무리" \ - --current-state "구현은 끝난 상태" \ - --next-action "최종 검증 suite 실행" \ - --project-status passed \ - --validation-command "python3 scripts/check-savepoint-renderer.py" \ - --validation-result passed \ - --validation-summary "focused renderer checks passed" -``` - -`--scan-redaction`을 쓰면 입력 JSON을 렌더 전에 먼저 스캔합니다. `.savepoint/input.json`에 raw secret을 넣지 마세요. `--delete-input-on-success`를 추가하면 resume-ready save가 성공했을 때만 `.savepoint/input.json`을 삭제합니다. - -기존 자동화도 `scripts/savepoint.py`를 호출하게 바꿉니다. - -| 이전 호출 | 현재 호출 | -|---|---| -| `scripts/render_savepoint.py --input ...` | `scripts/savepoint.py save --input ...` | -| `scripts/validate_savepoint.py ...` | `scripts/savepoint.py validate ...` | - -프로젝트 검증 입력은 `validation.project`를 사용합니다. - -| 이전 key | 현재 field | -|---|---| -| `project_validation` | `validation.project.commands`와 `validation.project.status` | -| `skipped_checks_next_validation` | `validation.project.next_validation` | -| `smallest_next_step` | `next_action` | -| `blockers` | `unresolved_blockers` | - -`failed-expected` 또는 `not-run-justified`를 쓰면 사유와 다음 검증 명령을 함께 기록합니다. - -`validation.project.status`는 문서에 나열된 영어 값을 사용합니다. 검증 명령 `result`는 `passed` 또는 `failed`처럼 canonical English 값을 쓰고, summary와 reason은 한국어도 괜찮습니다. +`validation.project.status`는 `passed`, `failed-expected`, `failed-blocking`, `not-run-justified`, `not-run-unknown` 중 하나를 사용합니다. `--scan-redaction`을 쓰면 입력 JSON을 렌더 전에 스캔합니다. `.savepoint/input.json`에 raw secret을 넣지 마세요. ## 설치 @@ -142,24 +99,11 @@ examples, evals, maintainer docs, repository validation scripts는 일반 agent - `examples/text-note/`: response-only `/savepoint text` 예시 - `examples/unsafe-savepoint/`: 의도적으로 unsafe한 `RESUME_READY: no` artifact -## Maintainer validation +## Maintainer docs 생성된 artifact는 `scripts/savepoint.py validate .savepoint/SAVEPOINT.md`로 검증합니다. -`scripts/validate-repo.py`는 이 저장소를 유지보수할 때만 사용합니다. packaging, examples, trigger evals, marker/schema contracts를 검사합니다. - -커밋 전에는 다음을 실행합니다. - -```bash -python3 scripts/check-frontmatter.py -python3 scripts/check-marker-block.py -python3 scripts/check-marker-semantics.py -python3 scripts/validate-examples.py -python3 scripts/check-output-contract.py -python3 scripts/validate-repo.py -python3 scripts/check-savepoint-renderer.py -python3 scripts/check-install-helper.py -python3 scripts/savepoint.py validate --allow-example-paths examples/SAVEPOINT.filled.example.md examples/file-bugfix/SAVEPOINT.md examples/file-architecture/SAVEPOINT.md examples/unsafe-savepoint/SAVEPOINT.md -python3 -m compileall -q skills/savepoint/scripts scripts -git diff --check -``` +- repository 변경 검증: `AGENTS.md` +- marker와 safe-resume semantics: `docs/reference/savepoint-contract.md` +- compact packaging 기준: `docs/reference/context-packaging.md` +- 수동 artifact fallback: `docs/reference/savepoint-template.md` diff --git a/README.md b/README.md index 7c7037d..120806e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Savepoint creates or verifies a recoverable coding-session checkpoint so a fresh agent can continue from current repo/Git state without prior chat context. +Savepoint is not a lightweight conversation summary. It is a recoverable repo/Git checkpoint; use `/savepoint text` or an ordinary summary when file recovery is unnecessary. + ## 30-second usage ```text @@ -34,7 +36,7 @@ If a client does not pass custom slash prompts through, use the natural-language - File mode writes `.savepoint/SAVEPOINT.md`. - The artifact includes a repo/Git snapshot, `## Resume Prompt`, and one final `SAVEPOINT_V1` marker block. -- Generated artifacts are scanned for secret-like values before `REDACTION_CHECKED: yes`. +- Generated artifacts receive pattern-based secret-like scans before `REDACTION_CHECKED: yes`. - The bundled validator checks marker shape and safe-resume fields. - On load, current disk state wins over savepoint text. @@ -46,17 +48,7 @@ If a client does not pass custom slash prompts through, use the natural-language - Future conflicts are impossible. - Repo recovery from text mode. -## Runtime command - -The public entrypoint is: - -```bash -python3 scripts/savepoint.py save --input .savepoint/input.json --output .savepoint/SAVEPOINT.md --assert-no-active-commands --scan-redaction --validate -python3 scripts/savepoint.py init-input --output .savepoint/input.json -python3 scripts/savepoint.py validate .savepoint/SAVEPOINT.md -python3 scripts/savepoint.py inspect .savepoint/SAVEPOINT.md --json -python3 scripts/savepoint.py text --input .savepoint/input.json -``` +## Minimal CLI workflow The portable skill entrypoint is `skills/savepoint/scripts/savepoint.py`; repository-local commands use `scripts/savepoint.py`. @@ -71,42 +63,7 @@ python3 scripts/savepoint.py save --input .savepoint/input.json --output .savepo python3 scripts/savepoint.py inspect .savepoint/SAVEPOINT.md --json ``` -For a short savepoint without editing JSON: - -```bash -python3 scripts/savepoint.py save \ - --output .savepoint/SAVEPOINT.md \ - --assert-no-active-commands --scan-redaction --validate \ - --goal "finish the focused fix" \ - --current-state "implementation is done" \ - --next-action "run the final validation suite" \ - --project-status passed \ - --validation-command "python3 scripts/check-savepoint-renderer.py" \ - --validation-result passed \ - --validation-summary "focused renderer checks passed" -``` - -With `--scan-redaction`, the input JSON is scanned before rendering. Do not put raw secrets in `.savepoint/input.json`. Add `--delete-input-on-success` to remove `.savepoint/input.json` only after a resume-ready save succeeds. - -Existing automation should call `scripts/savepoint.py`. Update old root-wrapper calls as: - -| Old call | Current call | -|---|---| -| `scripts/render_savepoint.py --input ...` | `scripts/savepoint.py save --input ...` | -| `scripts/validate_savepoint.py ...` | `scripts/savepoint.py validate ...` | - -Use `validation.project` for project validation input. Replace old top-level input keys as: - -| Old key | Current field | -|---|---| -| `project_validation` | `validation.project.commands` plus `validation.project.status` | -| `skipped_checks_next_validation` | `validation.project.next_validation` | -| `smallest_next_step` | `next_action` | -| `blockers` | `unresolved_blockers` | - -For `failed-expected` or `not-run-justified`, include the reason and next validation command. - -Use the listed English `validation.project.status` values. Validation command `result` should use canonical English values such as `passed` or `failed`; summaries and reasons may be any language. +Use `validation.project.status` values `passed`, `failed-expected`, `failed-blocking`, `not-run-justified`, or `not-run-unknown`. With `--scan-redaction`, the input JSON is scanned before rendering; do not put raw secrets in `.savepoint/input.json`. ## Install @@ -124,13 +81,6 @@ The helper defaults to dry-run. It writes files only with `--apply`. With repo-s On Windows, prefer the install helper or a normal Git clone/worktree. Archive extraction tools can mishandle symlinks. -Typical skill locations: - -- Codex user skill: `$HOME/.agents/skills/savepoint/` -- Codex repo skill: `/.agents/skills/savepoint/` -- Claude user skill: `$HOME/.claude/skills/savepoint/` -- Claude project skill: `/.claude/skills/savepoint/` - ## Runtime boundary Normal create/load should use only: @@ -149,27 +99,14 @@ Examples, evals, maintainer docs, and repository validation scripts are not norm - `examples/text-note/`: response-only `/savepoint text` note. - `examples/unsafe-savepoint/`: intentionally unsafe `RESUME_READY: no` artifact. -## Maintainer validation +## Maintainer docs Use `scripts/savepoint.py validate .savepoint/SAVEPOINT.md` for generated artifacts. -Use `scripts/validate-repo.py` only for maintaining this repository. It checks packaging, examples, trigger evals, and marker/schema contracts. - -Before committing repository changes, run: - -```bash -python3 scripts/check-frontmatter.py -python3 scripts/check-marker-block.py -python3 scripts/check-marker-semantics.py -python3 scripts/validate-examples.py -python3 scripts/check-output-contract.py -python3 scripts/validate-repo.py -python3 scripts/check-savepoint-renderer.py -python3 scripts/check-install-helper.py -python3 scripts/savepoint.py validate --allow-example-paths examples/SAVEPOINT.filled.example.md examples/file-bugfix/SAVEPOINT.md examples/file-architecture/SAVEPOINT.md examples/unsafe-savepoint/SAVEPOINT.md -python3 -m compileall -q skills/savepoint/scripts scripts -git diff --check -``` +- Repository change validation: `AGENTS.md`. +- Marker and safe-resume semantics: `docs/reference/savepoint-contract.md`. +- Compact packaging guidance: `docs/reference/context-packaging.md`. +- Manual artifact fallback: `docs/reference/savepoint-template.md`. ## Orchestrators diff --git a/scripts/validate-repo.py b/scripts/validate-repo.py index e8c9168..546bd00 100644 --- a/scripts/validate-repo.py +++ b/scripts/validate-repo.py @@ -264,8 +264,8 @@ def validate_frontmatter(self) -> None: if phrase not in description: self.fail(f"frontmatter description must include Korean invocation phrase: {phrase}") body = "\n".join(lines[end + 1 :]) - if not body.strip().startswith("# Savepoint"): - self.fail("SKILL.md body should start with '# Savepoint'") + if not body.strip().startswith("Modes:"): + self.fail("SKILL.md body should start with mode definitions") def validate_references(self) -> None: skill_text = self.read(SKILL_DIR / "SKILL.md") @@ -284,30 +284,44 @@ def validate_references(self) -> None: self.fail(f"root wrapper should not exist: scripts/{removed_wrapper}") required_skill_phrases = [ - "Default behavior", - "/savepoint -> create or refresh `.savepoint/SAVEPOINT.md`", - "/savepoint save", - "/savepoint load", - "/savepoint text", + "Modes:", + "default or `save`: create or refresh `.savepoint/SAVEPOINT.md`", + "`load`: verify an existing savepoint and report whether continuation is safe", + "`text`: response-only copy-paste handoff; no file recovery guarantee", ".savepoint/SAVEPOINT.md", "SAVEPOINT_V1", "RESUME_READY: yes", - "Do not read references, `scripts/*.py`, or `evals/*.json` during normal use.", + "Run the bundled CLI; do not inspect implementation source during normal use.", + "Resolve `` before running commands", "python3 /scripts/savepoint.py save", + "inspect --json", "append `--force` only when", "generated, untracked, valid default artifact", "`validation.project.status`", "`not-run-justified`", "`failed-expected`", "`no-file`, `no files`, `in-response`, or `in the response`", + "Prepare `.savepoint/input.json` as in Save, then run", "## Load / Resume", "For inspect-only requests, do not clean up by default.", "Continue only when the user requested continuation and `RESUME_READY` is `yes`", - "Read `references/contract.md` only when", + "Read references only when normal CLI use is insufficient", + "`references/contract.md`", + "`references/safety.md`", + "`references/template.md`", ] for phrase in required_skill_phrases: if phrase not in skill_text: self.fail(f"SKILL.md missing required policy: {phrase}") + skill_line_count = len(skill_text.splitlines()) + if skill_line_count > 55: + self.fail(f"SKILL.md should stay concise at <=55 lines, got {skill_line_count}") + for phrase in [ + "direct flags such as", + "do not combine direct flags", + ]: + if phrase in skill_text: + self.fail(f"SKILL.md should not include CLI shortcut detail: {phrase}") contract_text = self.read(REFERENCE_DIR / "savepoint-contract.md") for phrase in [ @@ -383,9 +397,10 @@ def validate_readme_format(self) -> None: "Savepoint", ".savepoint/SAVEPOINT.md", "scripts/savepoint.py", - "scripts/check-output-contract.py", - "scripts/validate-repo.py", - "python3 -m compileall", + "AGENTS.md", + "docs/reference/savepoint-contract.md", + "Savepoint is not a lightweight conversation summary.", + "pattern-based secret-like scans", ]: if phrase not in readme_text: self.fail(f"README.md missing entry: {phrase}") @@ -399,12 +414,27 @@ def validate_readme_format(self) -> None: "Savepoint", ".savepoint/SAVEPOINT.md", "scripts/savepoint.py", - "scripts/check-output-contract.py", - "scripts/validate-repo.py", - "python3 -m compileall", + "AGENTS.md", + "docs/reference/savepoint-contract.md", + "Savepoint는 가벼운 대화 요약이 아닙니다.", + "pattern-based secret-like scan", ]: if phrase not in readme_ko_text: self.fail(f"README.ko.md missing entry: {phrase}") + for phrase in [ + "Old call", + "Old key", + "Before committing repository changes, run:", + ]: + if phrase in readme_text: + self.fail(f"README.md should not carry maintainer/migration detail: {phrase}") + for phrase in [ + "이전 호출", + "이전 key", + "커밋 전에는 다음을 실행합니다.", + ]: + if phrase in readme_ko_text: + self.fail(f"README.ko.md should not carry maintainer/migration detail: {phrase}") def validate_agent_metadata(self) -> None: path = SKILL_DIR / "agents" / "openai.yaml" diff --git a/skills/savepoint/SKILL.md b/skills/savepoint/SKILL.md index 1ab7dcd..e38a03c 100644 --- a/skills/savepoint/SKILL.md +++ b/skills/savepoint/SKILL.md @@ -1,82 +1,54 @@ --- name: savepoint -description: "Create or load a recoverable coding-session checkpoint at .savepoint/SAVEPOINT.md so a fresh agent can resume from current repo/Git state. Use for context reset, session transfer, 세이브포인트 만들어줘, 세이브포인트 로드해줘, 세이브포인트 읽어줘, 세이브포인트 이어서 해줘. Not for SQL SAVEPOINT, ordinary summaries, direct code/docs edits without checkpoint intent, /status, /new, PTY/session control, session rotation, or app features named savepoint." +description: "Recoverable coding-session checkpoint at .savepoint/SAVEPOINT.md for context reset/session transfer. Use for 세이브포인트 만들어줘, 세이브포인트 로드해줘, 세이브포인트 읽어줘, 세이브포인트 이어서 해줘. Not for SQL SAVEPOINT, ordinary summaries, direct code/docs edits without checkpoint intent, /status, /new, PTY/session control, session rotation, or app features named savepoint." argument-hint: "[save|load|text] [next-session focus]" --- -# Savepoint - -Use this skill to preserve or load coding-session state without relying on prior chat context. - -Default behavior: - -```text -/savepoint -> create or refresh `.savepoint/SAVEPOINT.md` -/savepoint save -> same as default -/savepoint load -> verify an existing savepoint and report whether continuation is safe -/savepoint text -> response-only copy-paste handoff; no file recovery guarantee -``` - -Native slash-command support depends on the client. If slash prompts are not passed through, use `$savepoint` natural language requests. +Modes: +- default or `save`: create or refresh `.savepoint/SAVEPOINT.md` +- `load`: verify an existing savepoint and report whether continuation is safe +- `text`: response-only copy-paste handoff; no file recovery guarantee ## Rules -- Stay in savepoint scope. Do not edit application code. +- During save/load verification, stay in savepoint scope; do not edit application code. +- Resolve `` before running commands; Claude Code: `${CLAUDE_SKILL_DIR}`. - Do not run `/new`, `/status`, PTY/session rotation, threshold policy, or background process control. -- Do not read references, `scripts/*.py`, or `evals/*.json` during normal use. -- Prefer current files, Git state, and durable state files over chat memory. -- Do not paste transcripts, full diffs, long logs, shell history, PRDs, ADRs, issues, or commits. -- Reference existing artifacts by path, URL, branch, or commit. -- Redact API keys, tokens, cookies, credentials, private keys, passwords, `.env` values, and PII as ``; do not place raw secrets in semantic input files. +- Run the bundled CLI; do not inspect implementation source during normal use. +- Prefer current disk/Git/durable state over chat memory. +- Do not paste transcripts, diffs, logs, shell history, PRDs, ADRs, issues, or commit text; cite paths, URLs, branches, or commits. +- Redact API keys, tokens, cookies, credentials, private keys, passwords, `.env` values, and PII as ``; never put raw secrets in input. - File savepoints must end with exactly one `SAVEPOINT_V1` marker block. -- Keep top-level `SAVEPOINT.md` compact. Use generated `details/*.md` only when needed for recovery. +- Keep top-level `SAVEPOINT.md` compact; use generated `details/*.md` only when needed for recovery. ## Create / Save -1. Treat provided focus text, if any, only as next-session focus. -2. Capture repo/Git state and write compact input JSON with `goal`, `current_state`, `next_action`, `files_to_inspect_first`, and `unresolved_blockers`; start with `python3 /scripts/savepoint.py init-input --output .savepoint/input.json` if blank. -3. Set `validation.project.status` to one of `passed`, `failed-expected`, `failed-blocking`, `not-run-justified`, or `not-run-unknown`. For `failed-expected`, include failed command/result/summary evidence, an explicit reason, and next validation command. For `not-run-justified`, include a reason and next validation command. -4. Run: - -```bash -python3 /scripts/savepoint.py save --input .savepoint/input.json --output .savepoint/SAVEPOINT.md --assert-no-active-commands --scan-redaction --validate -``` - -Inside this repository, `python3 scripts/savepoint.py save ...` also works. +1. Treat any focus text only as next-session focus. +2. Prepare compact input JSON with `goal`, `current_state`, `next_action`, `files_to_inspect_first`, and `unresolved_blockers`; if none exists, run `python3 /scripts/savepoint.py init-input --output .savepoint/input.json`. +3. Set `validation.project.status` to `passed`, `failed-expected`, `failed-blocking`, `not-run-justified`, or `not-run-unknown`. `failed-expected` needs failed command/result/summary, reason, next validation; `not-run-justified` needs reason and next validation. +4. Run `python3 /scripts/savepoint.py save --input .savepoint/input.json --output .savepoint/SAVEPOINT.md --assert-no-active-commands --scan-redaction --validate`. +5. Inspect only generated `.savepoint/SAVEPOINT.md`; report exact path, `RESUME_READY`, blockers if any, and the first next action. -For simple savepoints, direct flags such as `--goal`, `--current-state`, `--next-action`, and `--project-status` may replace `--input`; do not combine direct flags with `--input`. Add `--delete-input-on-success` only when `.savepoint/input.json` should be removed after a resume-ready save. - -5. Inspect only the generated `.savepoint/SAVEPOINT.md`. -6. Report exact path, `RESUME_READY`, blockers if any, and the first next action. - -`savepoint.py save` exit code `2` can still mean a not-ready `SAVEPOINT.md` was written. Inspect the file, report blockers, and do not continue unless `RESUME_READY: yes`. +Exit code `2` may write not-ready `SAVEPOINT.md`; inspect, report blockers, and do not continue unless `RESUME_READY: yes`. ## Load / Resume -1. Read the selected savepoint: user path first, then `.savepoint/SAVEPOINT.md`. -2. Verify cwd, Git root, branch, short HEAD, status, and diff against current disk state. +1. Select user path first, else `.savepoint/SAVEPOINT.md`; run `python3 /scripts/savepoint.py inspect --json`. +2. Verify cwd, Git root, branch, short HEAD, status, and diff against disk state. 3. Disk state wins over savepoint text. Report drift before edits. 4. Continue only when the user requested continuation and `RESUME_READY` is `yes`, with no blocking drift or missing required file. 5. For inspect-only requests, do not clean up by default. ## Text Mode -Use text mode only when the user explicitly asks for copy-paste, text-only, `no-file`, `no files`, `in-response`, or `in the response`. - -Run: +Use text mode only for explicit copy-paste, text-only, `no-file`, `no files`, `in-response`, or `in the response` requests. -```bash -python3 /scripts/savepoint.py text --input .savepoint/input.json -``` +Prepare `.savepoint/input.json` as in Save, then run `python3 /scripts/savepoint.py text --input .savepoint/input.json`. Text mode must not claim `.savepoint/SAVEPOINT.md` was written, repo recovery is guaranteed, or `RESUME_READY: yes`. ## Advanced Cases -Read `references/contract.md` only when marker semantics, cleanup, stale savepoints, detail spillover, overwrite adoption, or safe-resume edge cases are unclear. - -For refresh, append `--force` only when the existing file is the generated, untracked, valid default artifact `.savepoint/SAVEPOINT.md` and the user did not ask to preserve history; otherwise preserve or ask. - -Read `references/safety.md` only when secret redaction or secret-like paths are involved. +Read references only when normal CLI use is insufficient: `references/contract.md` for marker/resume/cleanup/stale/detail/overwrite, `references/safety.md` for secrets, `references/template.md` for renderer fallback. -Read `references/template.md` only when the renderer is unavailable and a manual artifact is unavoidable. +For refresh, append `--force` only when existing `.savepoint/SAVEPOINT.md` is the generated, untracked, valid default artifact and no history preservation was requested; otherwise preserve or ask.