Skip to content
Open
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
3 changes: 2 additions & 1 deletion docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ Common options for the nightly actions include:
|---|---|
| `--project PATH` | Project used for transcript scope, targets, state, and staging (default: current directory) |
| `--scope invoked\|all` | Harvest this project or all projects |
| `--source claude\|codex\|copilot\|cursor\|pi\|opencode\|auto` | Transcript source; `auto` keeps Codex-then-Claude precedence and does not select Copilot, Cursor, Pi, or OpenCode |
| `--source claude\|codex\|copilot\|cursor\|pi\|opencode\|dsh\|auto` | Transcript source; `auto` keeps Codex-then-Claude precedence and does not select Copilot, Cursor, Pi, OpenCode, or DSH |
| `--backend mock\|claude\|codex\|copilot\|cursor\|pi\|opencode\|handoff\|azure_openai` | Replay/optimizer backend |
| `--model NAME` | Backend-specific model override |
| `--cursor-home PATH` | Override `~/.cursor` for Cursor transcript harvesting |
Expand All @@ -144,6 +144,7 @@ Common options for the nightly actions include:
| `--pi-path PATH` | Path to the installed Pi coding-agent CLI |
| `--opencode-path PATH` | Path to the installed OpenCode CLI |
| `--opencode-db PATH` | Path to the OpenCode SQLite history database |
| `--dsh-session-root PATH` | Override the DSH JSONL session root for `--source dsh` (default: `$DSH_HOME/sessions`, or `~/.dsh/sessions`) |
| `--opencode-tool-replay` | Enable OpenCode tool-aware replay for `tool_called` checks in rule judges |
| `--preferences TEXT` | House rules supplied to reflection |
| `--lookback-hours N` | Initial transcript lookback; `0` scans all history |
Expand Down
42 changes: 42 additions & 0 deletions docs/sleep/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,48 @@ The managed scheduler records the backend but does not preserve `--source`,
`~/.skillopt-sleep/config.json`. Use an absolute `pi_path` and verify the
scheduled account's Pi authentication.

### DeepSeek Harness (DSH)

Use `--source dsh` to read local DSH JSONL sessions. By default, SkillOpt uses
DSH's standard `$DSH_HOME/sessions` directory, or `~/.dsh/sessions` when
`DSH_HOME` is unset. Install the optional Zstandard reader first:

```bash
python -m pip install -e ".[dsh]"
skillopt-sleep harvest --project "$(pwd)" --source dsh --progress
```

`--dsh-session-root PATH` remains available only to override that default.

The source reads `session.jsonl` and the default compressed
`session.jsonl.zstd` files below DSH's project/session directory layout. It
does not start DSH, require DSH login, connect to a model provider, or modify
the stored logs. `--source auto` retains Codex-then-Claude precedence and does
not select DSH.

DSH harvesting keeps human user text, visible assistant text, short tool names,
timestamps, and positive/negative feedback signals derived from the immutable
`feedback/record` event. It excludes reasoning, tool arguments and results,
request/provider metadata, attachments, feedback remarks themselves, and
injected user-role context. Malformed sessions are silently skipped as a whole;
other sessions continue. Ordinary fork sessions are retained, while sessions
explicitly marked as subagents and SkillOpt replay sessions are excluded.

The source follows the current observed DSH session format rather than a fixed
application-version matrix. A session whose format or event structure cannot be
safely understood is skipped. Plugin integration and DSH execution are outside
this source's scope.

The managed scheduler does not preserve `--source` or `--dsh-session-root`.
Before scheduling, set the source in `~/.skillopt-sleep/config.json`; add
`dsh_session_root` only when overriding DSH's normal session directory:

```json
{
"transcript_source": "dsh"
}
```

### OpenCode

Use `--source opencode` to read local OpenCode SQLite history without launching
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ docs = ["mkdocs-material>=9.5.0", "mkdocstrings[python]>=0.24.0"]
webui = ["gradio>=5.50.0,<7"]
# Development tools
dev = ["ruff>=0.4.0", "pytest>=8.0.0"]
# DeepSeek Harness JSONL transcript source
dsh = ["zstandard>=0.22.0"]
# All optional dependencies (except docs/dev/webui)
all = [
"alfworld>=0.4.0",
Expand Down
9 changes: 7 additions & 2 deletions skillopt_sleep/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
--target-skill-path PATH explicit live SKILL.md to stage/adopt
--tasks-file PATH reviewed TaskRecord JSON file to replay instead of harvesting
--backend mock|claude|codex|copilot|cursor|pi|opencode|handoff|azure_openai
--source claude|codex|copilot|copilot_cli|cursor|pi|opencode|auto
--source claude|codex|copilot|copilot_cli|cursor|pi|opencode|dsh|auto
--vscode-workspace-storage PATH
--copilot-cli-session-store PATH
--opencode-db PATH
--dsh-session-root PATH
--model NAME
--lookback-hours N
--auto-adopt
Expand Down Expand Up @@ -115,14 +116,16 @@ def _add_common(p: argparse.ArgumentParser) -> None:
p.add_argument("--cursor-home", default="", help="override ~/.cursor for Cursor session harvest")
p.add_argument("--pi-home", default="", help="override ~/.pi for Pi session harvest")
p.add_argument("--source", default="",
choices=["", "claude", "codex", "copilot", "copilot_cli", "cursor", "pi", "opencode", "auto"],
choices=["", "claude", "codex", "copilot", "copilot_cli", "cursor", "pi", "opencode", "dsh", "auto"],
help="session transcript source")
p.add_argument("--vscode-workspace-storage", default="",
help="override VS Code User/workspaceStorage root for copilot source")
p.add_argument("--copilot-cli-session-store", default="",
help="override ~/.copilot/session-store.db for copilot_cli source")
p.add_argument("--opencode-db", default="",
help="override the local OpenCode transcript database")
p.add_argument("--dsh-session-root", default="",
help="override DSH session root (default: $DSH_HOME/sessions or ~/.dsh/sessions)")
p.add_argument("--lookback-hours", type=int, default=None,
help="harvest window in hours; 0 = scan full history")
p.add_argument("--edit-budget", type=int, default=0)
Expand Down Expand Up @@ -194,6 +197,8 @@ def _cfg_from_args(args, task_meta: Dict[str, Any] | None = None) -> Any:
if args.opencode_db == ":memory:"
else os.path.abspath(os.path.expanduser(args.opencode_db))
)
if getattr(args, "dsh_session_root", ""):
overrides["dsh_session_root"] = os.path.abspath(os.path.expanduser(args.dsh_session_root))
lh = getattr(args, "lookback_hours", None)
if lh is not None: # --lookback-hours was explicitly passed (0 = full history)
overrides["lookback_hours"] = lh
Expand Down
15 changes: 14 additions & 1 deletion skillopt_sleep/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"vscode_workspace_storage": "", # "" => auto-detect platform defaults
"copilot_cli_session_store": "", # "" => ~/.copilot/session-store.db
"opencode_db": "", # "" => OPENCODE_DB or the OpenCode XDG data path
# Explicit sources also include copilot, copilot_cli, cursor, pi, and opencode.
"dsh_session_root": "", # "" => $DSH_HOME/sessions, or ~/.dsh/sessions
# Explicit sources also include copilot, copilot_cli, cursor, pi, opencode, and dsh.
# ``auto`` keeps the established Codex-then-Claude precedence.
"transcript_source": "claude",
"projects": "invoked", # "invoked" | "all" | [list of abs paths]
Expand Down Expand Up @@ -164,6 +165,18 @@ def opencode_db_path(self) -> str:
return ":memory:"
return os.path.abspath(os.path.expanduser(str(value)))

@property
def dsh_session_root(self) -> str:
value = self.data.get("dsh_session_root", "") or ""
if value:
return os.path.abspath(os.path.expanduser(str(value)))
# Match the DSH base bundle: its JSONL persistence root is
# dshHomePath("sessions"), where DSH_HOME defaults to ~/.dsh.
dsh_home = str(os.environ.get("DSH_HOME", ""))
if not dsh_home.strip():
dsh_home = os.path.join(os.path.expanduser("~"), ".dsh")
return os.path.abspath(os.path.expanduser(os.path.join(dsh_home, "sessions")))

@property
def vscode_workspace_storage(self) -> str:
value = self.data.get("vscode_workspace_storage", "") or ""
Expand Down
Loading