Skip to content

feat: add save-trajectory skill to export conversation as OpenAI chat format#89

Merged
vinodmut merged 8 commits into
AgentToolkit:mainfrom
vinodmut:claudecodeplugin
Mar 11, 2026
Merged

feat: add save-trajectory skill to export conversation as OpenAI chat format#89
vinodmut merged 8 commits into
AgentToolkit:mainfrom
vinodmut:claudecodeplugin

Conversation

@vinodmut

@vinodmut vinodmut commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Adds a new skill that saves the current Claude Code session's conversation history as a JSON file in OpenAI chat completion format. Trajectories are saved to .kaizen/trajectories/ for analysis and fine-tuning. Uses temp file approach for JSON transfer and runs in forked context to keep parent clean.

May be useful for #88 and other debugging tasks.

Summary by CodeRabbit

  • New Features

    • Added a command-line save tool to persist Claude Code sessions as timestamped, project-scoped trajectory JSON files with input validation and user-facing success/error messages.
  • Documentation

    • Added comprehensive Save Trajectory documentation with usage steps, JSON message formats, formatting rules, examples, and guidance for project-scoped storage.

… format

Adds a new skill that saves the current Claude Code session's conversation
history as a JSON file in OpenAI chat completion format. Trajectories are
saved to .kaizen/trajectories/ for analysis and fine-tuning. Uses temp file
approach for JSON transfer and runs in forked context to keep parent clean.
@coderabbitai

coderabbitai Bot commented Mar 11, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@vinodmut has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 31 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 67c84276-c22a-40b3-8e54-43b63cf2a144

📥 Commits

Reviewing files that changed from the base of the PR and between 2cb3abd and 32d7a2f.

📒 Files selected for processing (3)
  • plugins/kaizen/README.md
  • plugins/kaizen/skills/save-trajectory/SKILL.md
  • plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py
📝 Walkthrough

Walkthrough

Adds a new "Save Trajectory" skill: documentation plus a Python script that converts/validates trajectory JSON and persists timestamped trajectory files under a project-scoped .kaizen/trajectories/ directory with collision handling and optional debug logging.

Changes

Cohort / File(s) Summary
Save Trajectory Documentation
plugins/kaizen/skills/save-trajectory/SKILL.md
New documentation describing the Save Trajectory skill, message conversion rules to OpenAI chat format, JSON message shapes, constraints (tool_call args as JSON strings, tool_call_id rules), envelope construction, saving workflow, examples, and formatting notes.
Save Trajectory Script
plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py
New Python CLI that reads trajectory JSON (stdin or file), validates messages array, determines project-scoped .kaizen/trajectories/ directory, creates it with secure permissions, generates unique timestamped filenames (with collision suffixing), writes indented JSON, and emits user-facing and debug log entries (KAIZEN_DEBUG).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    rect rgba(200,200,255,0.5)
    participant Claude as Claude Session
    end
    rect rgba(200,255,200,0.5)
    participant Skill as Save Trajectory Skill / Write Tool
    end
    rect rgba(255,200,200,0.5)
    participant Script as save_trajectory.py
    end
    rect rgba(255,255,200,0.5)
    participant FS as Filesystem (.kaizen/trajectories/)
    end

    Claude->>Skill: Trigger save (trajectory JSON)
    Skill->>Script: Provide JSON (stdin / temp file)
    Script->>Script: Validate JSON & messages[]
    Script->>FS: Create trajectories dir (with secure perms)
    Script->>FS: Write timestamped file (handle collisions)
    Script->>Script: Log entry (KAIZEN_DEBUG -> temp log)
    Script-->>Skill: Return saved filepath / status
    Skill-->>Claude: Confirm save to user
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • illeatmyhat
  • visahak

Poem

🐰✨ I hopped through messages, tidy and bright,
Timestamped footprints in JSON light,
Saved in a burrow, safe and neat,
Collisions dodged with a clever beat,
A tiny rabbit cheers—trajectory complete!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a new skill that exports conversation as OpenAI chat format, which aligns with both the documentation and script additions in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py (2)

39-39: Module-level side effect on import.

This log() call executes when the module is imported, not just when run as a script. While guarded by KAIZEN_DEBUG, consider moving it inside main() for cleaner import behavior if this module might be imported elsewhere.

♻️ Move log call into main()
-log("Script started")
-
-
 def get_trajectories_dir():

Then at the start of main():

def main():
    log("Script started")
    # Read trajectory JSON from file argument or stdin
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py` at line 39,
The module currently calls log("Script started") at import time (guarded by
KAIZEN_DEBUG), causing a side effect on import; move that call into the start of
the main() function so the message only logs when the script is executed (not
merely imported). Locate the top-level log("Script started") invocation and
remove it, then add a log("Script started") as the first line inside the
existing main() function (preserving the KAIZEN_DEBUG guard if present) so
initialization logging happens only during main() execution.

2-5: Docstring doesn't reflect file input capability.

The docstring mentions only stdin, but the script also accepts a file path argument (line 74). Consider updating for accuracy.

📝 Suggested docstring update
 """
 Save Trajectory Script
-Reads a trajectory JSON from stdin and writes it to the trajectories directory.
+Reads a trajectory JSON from a file argument or stdin and writes it to the trajectories directory.
 """
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py` around
lines 2 - 5, Update the module docstring in save_trajectory.py to accurately
state that the script can read a trajectory JSON either from stdin or from a
provided file path argument (the CLI entrypoint/main function that checks for a
file path argument), and mention the expected JSON format and that it writes to
the trajectories directory; locate the top-of-file docstring and the
argument-handling code in the main function to mirror its behavior (stdin or
file path) in the description.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py`:
- Line 39: The module currently calls log("Script started") at import time
(guarded by KAIZEN_DEBUG), causing a side effect on import; move that call into
the start of the main() function so the message only logs when the script is
executed (not merely imported). Locate the top-level log("Script started")
invocation and remove it, then add a log("Script started") as the first line
inside the existing main() function (preserving the KAIZEN_DEBUG guard if
present) so initialization logging happens only during main() execution.
- Around line 2-5: Update the module docstring in save_trajectory.py to
accurately state that the script can read a trajectory JSON either from stdin or
from a provided file path argument (the CLI entrypoint/main function that checks
for a file path argument), and mention the expected JSON format and that it
writes to the trajectories directory; locate the top-of-file docstring and the
argument-handling code in the main function to mirror its behavior (stdin or
file path) in the description.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7e5d4c28-2caa-471e-9522-70fc9b79c10d

📥 Commits

Reviewing files that changed from the base of the PR and between 50efcd2 and 955ec96.

📒 Files selected for processing (2)
  • plugins/kaizen/skills/save-trajectory/SKILL.md
  • plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py

Lazy-init log directory to avoid module-level side effects, cap filename
collision suffix at 1000, remove hardcoded model ID from SKILL.md, add
explicit regex for system-reminder stripping, and document handling of
compressed messages.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
plugins/kaizen/skills/save-trajectory/SKILL.md (1)

141-143: Prefer .gitignore as the default recommendation.

These artifacts are whole-session exports and can easily include prompts, tool output, secrets, or PII. I’d document “gitignore by default, commit only intentionally” instead of presenting both options as equivalent.

📝 Suggested wording
-- Trajectories are saved per-project in `.kaizen/trajectories/` and can be version-controlled or gitignored as preferred.
+- Trajectories are saved per-project in `.kaizen/trajectories/`. Add this directory to `.gitignore` by default unless you intentionally want to curate and review trajectory exports for version control.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/kaizen/skills/save-trajectory/SKILL.md` around lines 141 - 143, The
wording in SKILL.md currently presents committing trajectories and gitignoring
them as equivalent; change the guidance to recommend gitignore by default and
only commit trajectories intentionally by replacing the third bullet that
mentions ".kaizen/trajectories/" with a clear recommendation: state that
trajectories are saved under ".kaizen/trajectories/" and should be added to
.gitignore by default because they may contain prompts, tool output, secrets or
PII, and provide a short note on when it is acceptable to commit (e.g., after
redaction or explicit review). Keep the mention that the format is compatible
with OpenAI chat completions but make the default safety posture explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py`:
- Around line 42-51: The trajectories output is being created with default
permissions (in get_trajectories_dir and the file-write code at the later write
site around lines 106-108) which can be group/world-readable; update
get_trajectories_dir to create the directory with restrictive permissions (e.g.,
mode 0o700) and ensure the code that writes trajectory files (the save/write
routine around lines 106-108) creates files with restrictive permissions (e.g.,
0o600) — either by using os.open with an explicit mode or by calling Path.chmod
immediately after creation — so that both the .kaizen/trajectories directory and
individual trajectory files are owner-only accessible.
- Around line 33-39: The log() helper currently allows any exceptions from
_get_log_file() or file I/O to bubble up and abort execution when KAIZEN_DEBUG
is set; change log() to be best-effort by wrapping the call to _get_log_file()
and the open/write operations in a try/except that catches all exceptions (e.g.,
Exception) and silently returns on failure (optionally logging to stderr), so
debug logging never raises and does not alter functional behavior of main();
keep the KAIZEN_DEBUG guard and the timestamp/message formatting but ensure any
error in log() is swallowed.
- Around line 75-96: Validate that the parsed trajectory is a dict before
accessing methods like .keys() or .get(): after json.load (from file or
sys.stdin) check isinstance(trajectory, dict) and if not, log and print an error
to stderr (e.g., "Error: Expected JSON object with top-level keys, got <type>")
and sys.exit(1). Expand the existing exception handling around the
open/json.load logic to catch OSError (and optionally PermissionError) in
addition to FileNotFoundError and json.JSONDecodeError, normalize those into
CLI-friendly messages (use print(..., file=sys.stderr) and log(...)) and exit
non-zero; ensure references include input_path, trajectory, messages, log,
json.load so you update the try/except surrounding that block. Ensure messages =
trajectory.get("messages", []) only runs after the trajectory type check.

In `@plugins/kaizen/skills/save-trajectory/SKILL.md`:
- Around line 117-121: Replace the current single-line command that relies on
"&& rm -f .kaizen/tmp/trajectory_input.json" with a pattern that assigns the
temp path to a variable (e.g., tmp=.kaizen/tmp/trajectory_input.json), ensures
the directory exists with mkdir -p, registers a trap to remove "$tmp" on EXIT,
and then invokes the script with the quoted "$tmp" argument (python3
${CLAUDE_PLUGIN_ROOT}/skills/save-trajectory/scripts/save_trajectory.py "$tmp")
so the temp file is always cleaned up even if the script or writes fail; ensure
the trap uses rm -f and the temp variable is quoted when passed to the script.

---

Nitpick comments:
In `@plugins/kaizen/skills/save-trajectory/SKILL.md`:
- Around line 141-143: The wording in SKILL.md currently presents committing
trajectories and gitignoring them as equivalent; change the guidance to
recommend gitignore by default and only commit trajectories intentionally by
replacing the third bullet that mentions ".kaizen/trajectories/" with a clear
recommendation: state that trajectories are saved under ".kaizen/trajectories/"
and should be added to .gitignore by default because they may contain prompts,
tool output, secrets or PII, and provide a short note on when it is acceptable
to commit (e.g., after redaction or explicit review). Keep the mention that the
format is compatible with OpenAI chat completions but make the default safety
posture explicit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 04a89409-0c33-4e0e-ae89-4eb109baf4a1

📥 Commits

Reviewing files that changed from the base of the PR and between 955ec96 and 2cb3abd.

📒 Files selected for processing (2)
  • plugins/kaizen/skills/save-trajectory/SKILL.md
  • plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py

Comment thread plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py Outdated
Comment thread plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py
Comment thread plugins/kaizen/skills/save-trajectory/scripts/save_trajectory.py
Comment thread plugins/kaizen/skills/save-trajectory/SKILL.md
@vinodmut vinodmut requested review from illeatmyhat and visahak March 11, 2026 22:35
@vinodmut

Copy link
Copy Markdown
Contributor Author

The build failures don't seem related to this PR.


## Overview

This skill saves the current Claude Code session's conversation history as a JSON file in OpenAI chat completion format. The trajectory is saved to `.kaizen/trajectories/` in the project root. This enables trajectory analysis, fine-tuning data collection, and session review.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's specifically a claude code skill, but if this is a standardized format maybe we shouldn't name Claude

@illeatmyhat illeatmyhat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@vinodmut vinodmut merged commit 6e6438b into AgentToolkit:main Mar 11, 2026
13 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants