Gets a second opinion from TypeSafe Jev, a decision model, for a yes or no check, a pick from options, or a rating. Needs `TYPESAFE_API_KEY`.

dotclaude is a Claude Code plugin for software engineering on official extension points. It turns compaction off, sets a 300K context window, asks only before dangerous commands, replaces the coding part of the system prompt with a short output style, and keeps the state of long work in OpenSpec changes and handoff notes across /clear. It optimizes for the most quality per unit of usage quota, not for speed.
The wiki tells what each part does and why. Parts lists each part with its file and bound. Design gives the source of each design claim.
/plugin marketplace add xsyetopz/dotclaude
/plugin install dotclaude@dotclaude
Requirements: Claude Code 2.1.292, Node.js 22.18 or later on PATH, and git. The hooks module uses the mods API of Claude Code, which can change between releases, so this release is tested only on 2.1.292.
Run /dotclaude:setup and restart Claude Code. A plugin settings file can set only agent and subagentStatusLine, so the skill merges the profile into a settings file that you choose. It shows each change and makes a backup first. It also checks OpenSpec and offers to install it and to run openspec init --tools claude.
| Part | What it does |
|---|---|
| No compaction | autoCompactEnabled: false and DISABLE_COMPACT=1, which also turns off /compact. |
| Window | CLAUDE_CODE_MAX_CONTEXT_TOKENS=300000 and CLAUDE_CODE_AUTO_COMPACT_WINDOW=300000. At the limit the session stops, and you run /clear. |
| No auto memory | autoMemoryEnabled: false. Handoff notes and OpenSpec hold the state. |
| System prompt | CLAUDE_CODE_SIMPLE_SYSTEM_PROMPT=1 and the forced dotclaude output style, which replaces the built-in coding instructions. |
| Permissions | Deny secret reads and disk wipes. Ask before force pushes, history rewrites, piped shells, sudo, publishes, public gh writes, and SQL drops. Allow read-only git and the usual build and test commands. |
| Sandbox | Bash runs in the sandbox, with network access only to GitHub and the package registries. |
| Guard | One hooks module asks before a recursive rm outside the project, and before the first call to a GitHub repository of another owner with an AI policy. |
| Status line | Model, effort, context against the window (yellow at 75%, red at 90%), the 5-hour limit, and the active OpenSpec change with its task count. |
| Agents | investigator, web-researcher, implementer, debugger, reviewer, test-runner. |
| Skills | /dotclaude:setup, /dotclaude:handoff. |
The plugin has no options. To drop a part of the profile, see the setup skill.
| Model | Roles | Effort |
|---|---|---|
| Opus 5.5 | main session, reviewer | default medium, reviewer high |
| Sonnet 5.5 | investigator, web-researcher, implementer, debugger | medium |
| Haiku 4.5 | test-runner | none |
The profile sets no effort level, so each model keeps its default. maxEffortLevel is xhigh, so /effort can go up for a hard check.
Each turn reads the whole context again, so the size of the context sets the cost of a turn.
/opsx:propose. After /clear, /opsx:apply continues at the first unchecked task./clear, run /dotclaude:handoff to write the goal, the decisions, and the proof to .claude/handoffs/<slug>.md. Start the next session with @.claude/handoffs/<slug>.md./clear, because a new model starts a new prompt cache. An effort change keeps the cache.Each add-on works without the core plugin.
/plugin install dotclaude-browser@dotclaude
/plugin install dotclaude-jev@dotclaude
/plugin install dotclaude-modder@dotclaude
dotclaude-browser has the drive-web-browser skill. It uses agent-browser, and its backend option selects CloakBrowser for sites with bot detection.dotclaude-jev has the second-opinion skill, which asks TypeSafe Jev for probabilities on a close call. Export TYPESAFE_API_KEY before Claude Code starts. Each call sends the data of the decision to the TypeSafe API.dotclaude-modder lets Claude mod a PC game that you own. It is a port of universal-modder by Rehan, and it needs Bun and uv. It adds one skill and the fal MCP server, so install it only where you mod games. For reverse engineering, use the skills in xsyetopz/skills. See Modder.claude plugin marketplace update dotclaude
claude plugin update dotclaude@dotclaude
CHANGELOG.md. Before 1.0, a release can change or remove behavior without a compatibility layer./dotclaude:setup again. It shows each change.0.27.0 is a rebuild that removes most 0.26 parts. See its changelog entry for the list. To try an unreleased checkout, run claude --plugin-dir /path/to/dotclaude/plugins/dotclaude.
just check runs lint, tests, plugin validation, and the hook lab. just sandbox runs Claude Code with this checkout in a separate config. See Development and Sandbox.
MIT.
hooks/module/index.mjs 102 lines1// The hooks module of dotclaude-jev.
2// It acts on the session note in `hooks/session-start/second-opinion.md`:
3// Jev picks before the user answers a question with options.
4// On each `AskUserQuestion`, the hook runs `jev.mjs ask` once. Jev sorts each
5// question into preference or facts, and picks from its options. A facts
6// question shows the pick of Jev and its confidence to the user. A question
7// about a goal, a preference, or an approval stays as it is. The hook never
8// denies, so Claude has no reason to ask in plain text instead. Without
9// `TYPESAFE_API_KEY`, or when Jev fails, the question passes unchanged.
10
11/** Under this confidence, the TypeSafe docs treat an answer as uncertain. */
12const MIN_CONFIDENCE = 0.5;
13const JEV_TIMEOUT_MS = 45000;
14
15const KIND = {
16 type: "choice",
17 instructions:
18 "Only the user can answer some questions. Facts decide others. Which is this question?",
19 criteria: {
20 preference:
21 "A goal, a preference, an approval, or a choice that reasonable engineers make either way",
22 facts:
23 "Correctness, security, data loss, performance, or maintenance cost decide the answer",
24 },
25};
26
27/** The Jev request for the questions that have 2 or more options. */
28export function jevRequest(questions) {
29 const asked = {};
30 const state = [];
31 questions.forEach((q, i) => {
32 const options = Array.isArray(q?.options) ? q.options : [];
33 if (options.length < 2) return;
34 state.push(
35 `Question ${i}: ${q.question}`,
36 ...options.map((o) => `- ${o.label}: ${o.description ?? ""}`),
37 );
38 asked[`kind_${i}`] = {
39 ...KIND,
40 instructions: `${KIND.instructions} (question ${i})`,
41 };
42 asked[`pick_${i}`] = {
43 type: "choice",
44 instructions: `Which option is the best answer to question ${i}?`,
45 criteria: Object.fromEntries(
46 options.map((o) => [o.label, o.description || o.label]),
47 ),
48 };
49 });
50 return Object.keys(asked).length
51 ? { state: state.join("\n"), questions: asked }
52 : null;
53}
54
55/** The questions with the pick of Jev added to each facts question. */
56export function withPicks(questions, answers) {
57 return questions.map((q, i) => {
58 const kind = answers?.[`kind_${i}`];
59 const pick = answers?.[`pick_${i}`];
60 if (
61 kind?.choice !== "facts" ||
62 !(kind.confidence >= MIN_CONFIDENCE) ||
63 !(pick?.confidence >= MIN_CONFIDENCE) ||
64 !q.options.some((o) => o.label === pick.choice)
65 )
66 return q;
67 const note = `Jev picks "${pick.choice}" (confidence ${pick.confidence.toFixed(2)}).`;
68 return { ...q, question: `${q.question}\n\n${note}` };
69 });
70}
71
72export function register(on) {
73 on("tool.call", async ($, e, next) => {
74 if (e.tool !== "AskUserQuestion" || !Array.isArray(e.questions))
75 return next(e);
76 try {
77 if (!(await $.env.get("TYPESAFE_API_KEY"))) return next(e);
78 const request = jevRequest(e.questions);
79 if (!request) return next(e);
80 const run = await $.process.run(
81 [
82 "node",
83 `${$.plugin.root}/skills/second-opinion/scripts/jev.mjs`,
84 "ask",
85 "--state",
86 request.state,
87 ],
88 {
89 stdin: JSON.stringify({ questions: request.questions }),
90 timeoutMs: JEV_TIMEOUT_MS,
91 },
92 );
93 if (run.exitCode !== 0) return next(e);
94 const questions = withPicks(e.questions, JSON.parse(run.stdout));
95 return next({ ...e, questions });
96 } catch {
97 // Jev is missing, slow, or gave output that is not valid.
98 return next(e);
99 }
100 });
101}
102