Skip to content
dsh.fish
Bundle

dsh-agent-preset-recommender

Privacy-safe local activity scanner and agent preset recommender for DeepSeek Harness

Source
LeemanCheung
stars
3 stars
License
MIT
Updated
Updated 4 days ago

Readme

# dsh-agent-preset-recommender

[![Awesome](https://awesome.re/badge.svg)](https://awesome.re) [![awesome · DSH plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com) [![CI](https://github.com/LeemanCheung/dsh-agent-preset-recommender/actions/workflows/ci.yml/badge.svg)](https://github.com/LeemanCheung/dsh-agent-preset-recommender/actions/workflows/ci.yml)

English | [中文](README.zh.md)

A persistent, host-side [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) bundle that privately summarizes local Codex, Claude Code, and WorkBuddy/CodeBuddy activity and recommends a built-in DSH agent preset. It is advisory only: there is no LLM call, installation, preset mutation, or network request.

Compatibility baseline: DeepSeek Harness `0.1.2-rc.1`, Cordis `4.0.2`, and Schemastery `3.18.2`.
The installed Host result is recorded in [Windows DSH 0.1.2 acceptance](docs/WINDOWS_DSH_0.1.2_ACCEPTANCE.md).

## Preview

![Synthetic aggregate recommendation overview in DeepSeek Harness](docs/screenshot.png)

![Synthetic aggregate project detail in DeepSeek Harness](docs/project-detail.png)

> These previews use synthetic aggregate fixture data. They illustrate the bounded tool results only; no user sessions, paths, prompts, commands, or secrets appear.

## Capability map

| Area | What the plugin does | Deliberate boundary |
| --- | --- | --- |
| Local inventory | Boundedly scans supported session, project, and workflow metadata from Codex, Claude Code, CodeBuddy, and WorkBuddy. | Skips caches, builds, `.git`, symlinks, inaccessible roots, and unknown content. |
| Privacy-preserving evidence | Aggregates tool, session, workflow, and day-level activity under installation-local HMAC project IDs. | Never persists prompts, replies, commands, arguments, raw events, paths, usernames, secrets, or file bodies. |
| Deterministic advice | Maps observed evidence to `minimal`/`standard` capability presets plus optional delegation, workflow, web, MCP, and LSP capabilities. | Knows the shipped `minimal`, `standard`, `ptc`, and `cordis` roster, but never changes the user's current selection. |
| Agent-facing access | Provides `scan_agent_projects` for a fresh bounded scan and `get_agent_preset_recommendations` for the saved report. | Both tools return bounded readable text; neither installs, enables, or authenticates anything. |
| Durable local operation | Atomically persists a private report, supports startup and scheduled scans, and serializes all scan triggers. | No LLM call, network request, discovered-command execution, or background work survives plugin disposal. |

## Install

```sh
dsh plugin --profile web add github:LeemanCheung/dsh-agent-preset-recommender
```

Restart the selected DSH profile after installation. The package declares `dsh.bundle.patch` and mounts one host plugin.

## What it recommends

Observed aggregate behavior is mapped to:

- capability presets: `minimal` or `standard`.

DSH `0.1.2-rc.1` ships `minimal`, `standard`, `ptc`, and `cordis`. The scanner lists that roster for accuracy, but automatic advice remains limited to the general-purpose `minimal` and `standard` choices. It never writes a default or current preset, so an explicit user selection remains unchanged.
- optional capabilities: Codex delegation, Claude Code delegation, workflows, web, MCP, and LSP.

Every recommendation includes confidence and numerical evidence. Thresholds are deterministic and local; results never automatically change DSH.

### Exact recommendation and evidence rules

- `standard` is recommended when **at least one** recognized session, workflow, or categorized tool call exists. Metadata files alone—and an empty observation set—yield `minimal`.
- Codex/Claude Code delegation needs a recognized session from that source. Workflows need a workflow count or `workflow`-category tool call; `web`, `MCP`, and `LSP` need their corresponding category count.
- Confidence is `0` with no observations; otherwise it is `min(0.95, 0.35 + 0.2 × log10(observations + 1))`, rounded to two decimal places, where observations are sessions + workflows + categorized tool calls.
- Tool names first use exact delegation aliases, then the fixed heuristic order **delegation → workflow → MCP → LSP → web → shell → search → files**; the first match wins and unmatched names are `other`. These are trend signals, not an audit of every product tool.

## Architecture

```text
cordis.patch.yml → src/index.js (Cordis lifecycle + raw model tools)
                    ├─ scanner.js (bounded traversal and aggregation)
                    ├─ extractors.js (selected JSON/JSONL metadata fields)
                    ├─ recommender.js (deterministic rules)
                    ├─ store.js (atomic private report file)
                    └─ render.js (bounded readable tool output)
```

Runtime code is plain ESM JavaScript for Node.js 20+. It uses Node built-ins plus `@deepseek-ai/schemastery` for plugin configuration validation. Tool definitions are registered directly through `ctx.tools.register` and do not import unpublished DSH tool runtime helpers.

## Privacy

The scanner persists aggregate metadata only:

- source and installation-keyed project identifier;
- categorized tool counts;
- session, workflow, and project-metadata counts;
- first/last observation dates (day-level);
- recommendation, confidence, and evidence counts;
- an explicit machine-readable privacy declaration.

It **never persists** prompts, responses, commands, tool arguments, raw events, absolute paths, usernames, secrets, or file contents. Project identifiers are derived with a random installation-local HMAC key, so report IDs cannot be dictionary-matched without the private key. WorkBuddy/CodeBuddy memory metadata is counted from file presence and modification time only; workflow/plan files are likewise never opened. The scanner makes no network requests and runs no discovered command.

Cache, dependency, build, output, coverage, virtual-environment, and `.git` directories are skipped. Symbolic links are not followed.

The default report is:

```text
$DSH_HOME/state/agent-preset-recommender/report.json
```

If `DSH_HOME` is unset, `~/.dsh` is used. The directory also holds a private random `identity.key` used only to derive project IDs. Report writes use a same-directory temporary file and atomic rename; restrictive permissions are requested where the platform supports them.

## Supported locations and formats

| Source | Defaults | Read behavior |
| --- | --- | --- |
| Codex | `$CODEX_HOME/sessions`, `$CODEX_HOME/archived_sessions` or `~/.codex/*` | Bounded `.jsonl`/`.json`; selected session/project and tool-name fields |
| Claude Code | `$CLAUDE_CONFIG_DIR/projects` or `~/.claude/projects` | Bounded `.jsonl`/`.json`; selected project and `tool_use` name fields; workflow sidecars, journals, task/session/plan stores, and global history are excluded |
| Claude personal workflows | `$CLAUDE_CONFIG_DIR/workflows` or `~/.claude/workflows` | `.js` file presence and day only; scripts are never opened. Add project-local `<repo>/.claude/workflows` explicitly to `claudeWorkflowRoots` if desired |
| Claude transcripts | Disabled | Scanned only when `claudeTranscriptRoots` is explicitly configured |
| CodeBuddy CLI | `$CODEBUDDY_CONFIG_DIR/projects` or `~/.codebuddy/projects` | Bounded canonical project `.jsonl` records; `~/.codebuddy/workflows/*.js` and project-local workflow scripts are inventoried by presence only. Process maps, tool-result/blob directories, and workflow runtime sidecars are excluded |
| WorkBuddy | `$WORKBUDDY_CONFIG_DIR/projects` or `~/.workbuddy/projects`, `~/.workbuddy-ai/projects` | Heuristic, version-sensitive project `.jsonl` inventory; native session layout is not vendor-contracted, so it is never treated as proof of CodeBuddy session equivalence |
| Project-local CodeBuddy/WorkBuddy metadata | `<project>/.codebuddy` or `<project>/.workbuddy` `memory`, `workflows`, `plans`, or `automations` | Count and day only, including workflow `.js`; content is not read; memory never becomes workflow evidence |

Formats vary between product releases. CodeBuddy paths/workflow scripts are documented; WorkBuddy session-file detection is an observed heuristic. Unknown fields are ignored, malformed records are skipped, and malformed files are counted as errors without stopping the scan.

## Configuration

Configure the inserted `agent-preset-recommender` row in a DSH patch:

```yaml
- id: agent-preset-recommender
  config:
    scanOnStart: true
    intervalMinutes: 360      # 0 disables scheduled scans
    maxFilesPerSource: 500
    maxBytesPerFile: 1048576
    recentDays: 90
    stateDirectory: ''        # empty = $DSH_HOME/state/agent-preset-recommender
    codexRoots:
      - ~/.codex/sessions
      - ~/.codex/archived_sessions
    claudeRoots:
      - ~/.claude/projects
    claudeTranscriptRoots: [] # opt in explicitly
    claudeWorkflowRoots:
      - ~/.claude/workflows   # inventory only; script content is never read
    workbuddyRoots:
      - ~/.codebuddy
      - ~/.workbuddy
      - ~/.workbuddy-ai
      - ~/WorkBuddy
      - ~/CodeBuddy
```

Defaults honor `CODEX_HOME`, `CLAUDE_CONFIG_DIR`, `CODEBUDDY_CONFIG_DIR`, and `WORKBUDDY_CONFIG_DIR` when DSH starts. Supplying an explicit root list in the plugin configuration takes precedence over those defaults.

Bounds are validated: `intervalMinutes` is 0–35,791, `maxFilesPerSource` 1–100,000, `maxBytesPerFile` 1 KiB–64 MiB, and `recentDays` 1–3,650. Missing/inaccessible roots are skipped. Startup, scheduled, and tool-triggered scans share one serialized queue and are aborted on plugin disposal.

Set both `scanOnStart: false` and `intervalMinutes: 0` to disable **automatic** scans; the model tool can still scan on demand. Before deleting the state directory to reset the report, stop the plugin: this also removes `identity.key`, intentionally rotating every keyed project ID.

## Model tools

### `scan_agent_projects`

Runs and persists a fresh scan. Optionally refresh only selected sources:

```json
{ "sources": ["codex", "claude"] }
```

Unselected source aggregates from the previous report remain intact.

### `get_agent_preset_recommendations`

Reads the persisted report without scanning:

```json
{}
```

Or retrieve one keyed project:

```json
{ "project_id": "codex-0123456789abcdef" }
```

Both tools return bounded readable text strings. Omitting `sources` or passing an empty list scans every source; a selected-source scan preserves the prior aggregate for unselected sources.

Each persisted source report exposes `filesConsidered`, `truncatedFiles`, `skippedOld`, `skippedOversize`, `skippedLimit`, and `parseOrAccessErrors` alongside counts. Summary output lists at most 50 projects and is capped at 12,000 characters; query a `project_id` for its bounded detail.

## Limitations

- Metadata schemas are intentionally conservative; unrecognized tool events may be undercounted.
- Keyed IDs are stable only while the private state directory remains available; deleting `identity.key` intentionally creates a new identifier set.
- A recommendation reflects observed local frequency, not task quality or organizational policy.
- The plugin does not verify that optional products or capabilities are installed or authenticated.
- JSONL files above the byte cap are prefix-sampled within the byte/record bounds; their remaining data, oversized JSON files, old files, and older files beyond a source limit are intentionally omitted. Compressed Codex `.jsonl.zst` rollouts are not read in 0.1.7. Claude workflow scripts and dynamic workflow sidecars are deliberately not parsed.

## Development

```sh
npm install
npm test
```

Tests use synthetic temporary fixtures and Node's built-in `node:test`; no local product data is read. See [SECURITY.md](SECURITY.md) for private vulnerability reporting guidance.

## License

[MIT](LICENSE)

Install

dsh plugin --profile web add github:LeemanCheung/dsh-agent-preset-recommender

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source