SLOPSHOPPER

my-privacy-audit-mock

Offline synthetic model stream audit

newguard
★ 59v0.1.0no licenseupdated 2026-10-06jinrunsen/claude-my-privacy/verification/mock-model
A shopper browsing a rack in a slop shop
README

MyPrivacy

English | 中文

MyPrivacy replaces specified names and company information with aliases in model-input text supported by Claude Code, restores the originals before local tools run, and redacts the text returned by tools. It also attempts to restore aliases in supported assistant reply displays. The plugin uses best-effort replacement, has no command or email allowlist, and does not lock the session when replacement fails.

It only processes configured terms; it is not a general-purpose PII detector. Unconfigured information and unsupported content may remain unchanged. The source includes no personal mappings, and the plugin itself does not call a model or a network detection service. Replacement scope and limitations explains which content is protected and which content is restored.

The current plugin version is 0.5.0. Offline tool and terminal display checks have passed on macOS with Claude Code 2.1.290; other versions and platforms require fresh verification. See the verification guide for the checked scope.

The repository is named claude-my-privacy, the installation identifier is my-privacy@my-privacy-marketplace, and all commands use the /my-privacy- prefix.

<a id="安装并验证"></a>

Install and verify

Run the following commands from the repository root. First confirm that the claude command is available; see the contributing guide for additional development and offline-verification dependencies.

<a id="1-创建私有配置"></a>

1. Create private configuration

Copy the fictional example outside the repository, then edit it with your own mappings. The copy command below will not overwrite an existing file.

mkdir -p "$HOME/.config/claude-my-privacy"
chmod 700 "$HOME/.config/claude-my-privacy"
if [ ! -e "$HOME/.config/claude-my-privacy/mappings.json" ]; then
  cp -n examples/mappings.example.json "$HOME/.config/claude-my-privacy/mappings.json"
fi
chmod 600 "$HOME/.config/claude-my-privacy/mappings.json"

The example configuration uses 陈星河 → 陈瑞恩, chenxinghe → ryanchen, 云杉智科 → 海岚数科, and cedarbyte → harborwave. Enter real information only outside the repository. See the configuration guide for the format, case handling, and alias selection.

<a id="2-安装插件"></a>

2. Install the plugin

Register this directory as a local marketplace, then install the plugin:

claude plugin marketplace add "$PWD" --scope user
claude plugin install my-privacy@my-privacy-marketplace --scope user

<a id="3-确认配置生效"></a>

3. Confirm that configuration is active

Run these commands in a Claude Code session:

/reload-plugins
/my-privacy-status

When a nonempty configuration loads successfully, the status includes Configuration: loaded and a mappings count greater than zero. loaded; mappings: 0 means the term list is empty; unavailable or invalid means replacement is inactive. The troubleshooting guide provides recovery steps for each state. The status command does not display the term list, and the plugin has no persistent status-bar display.

<a id="日常使用"></a>

Everyday use

Enter requests and use tools as usual. For example, the example configuration restores staff42@harborwave.example on the model side to staff42@cedarbyte.example for actual tool execution. Supported assistant reply displays attempt to show originals without changing stored session text or model context. The plugin also attempts to restore execution arguments used by Write, Edit, or Bash to write files. See the full flow and display limits.

After editing private configuration, run /reload-plugins, then check /my-privacy-status. The plugin reads configuration only once per load. Use /my-privacy-audit for diagnostics; it retains only structural metadata, without commands or body text.

<a id="更新"></a>

Update

After updating the source and incrementing the plugin version, run this command in a terminal:

claude plugin update my-privacy@my-privacy-marketplace --scope user

Then run /reload-plugins and /my-privacy-status in the active session. The source directory is the marketplace installation source; the running copy is in the Claude plugin cache. If you move the source directory, register the marketplace path again.

install.py additionally changes plugin ordering, disables a specified compaction plugin, and writes telemetry-related settings. Use the CLI commands above for routine installation; see the development guide for the script's side effects.

<a id="文档与开发"></a>

Documentation and development

Choose documentation by task; you do not need to read the entire implementation:

TaskDocument
Configure terms in names, companies, domains, and email addressesConfiguration guide
Understand the differences between input, tool execution, files, and repliesReplacement behavior and limits
Diagnose paths, old blocking messages, and tool errorsTroubleshooting and audit
Locate implementation, loading, and storage responsibilitiesArchitecture
Change code, check documentation, and prepare a releaseContributing guide
Run isolated verification and consult historical probesVerification guide

The project does not yet have an open-source license; the plugin manifest is marked UNLICENSED.

Source 2 files
hooks/register.js 23 lines
1import { INPUT, OUTPUT, REPORT } from './paths.js';
2const RAW = ["Cedarbyte", "audit.person@example.invalid", "+1 202-555-0199"];
3let sawRead=false, sawWrite=false, sawEdit=false, captured=[], writtenContent="";
4function result(e, answer, tools=[], stop="end_turn") { return {turnId:e.turnId,index:e.index,answer,toolUses:tools,stopReason:stop,usage:null}; }
5function facts(messages) { const data=JSON.stringify(messages); return {array:Array.isArray(messages),rawVisible:RAW.some(x=>data.includes(x)),hasCompanyAlias:data.includes("Harborwave"),hasEmailToken: /<PII_EMAIL_[A-Z0-9_]+>/.test(data),hasPhoneToken:/<PII_PHONE_[A-Z0-9_]+>/.test(data)}; }
6export function register(on) {
7  on("tool.call", async ($,e,next) => { if(e.tool==="Read")sawRead=true; if(e.tool==="Write")sawWrite=true; if(e.tool==="Edit")sawEdit=true; return next(e); });
8  on("turn.step", async function*($,e) {
9    const messages=await $.session.messages({as:"api"}); const data=JSON.stringify(messages); captured.push({index:e.index,...facts(messages)});
10    let name, input;
11    if(e.index===0){name="Read";input={file_path:INPUT};}
12    else if(e.index===1){const email=data.match(/<PII_EMAIL_[A-Z0-9_]+>/)?.[0];const phone=data.match(/<PII_PHONE_[A-Z0-9_]+>/)?.[0];if(!email||!phone){await $.fs.write(REPORT,JSON.stringify({captured,sawRead,sawWrite,sawEdit,missingTokens:true}));yield {kind:"text",index:0,text:"AUDIT_MISSING_TOKENS"};return result(e,"AUDIT_MISSING_TOKENS");}name="Write";input={file_path:OUTPUT,content:`Synthetic output\nEmployer: Harborwave\nContact: ${email}\nPhone: ${phone}\nVersion: before\n`};}
13    else if(e.index===2){name="Edit";input={file_path:OUTPUT,old_string:writtenContent,new_string:writtenContent.replace("Version: before","Version: after")};}
14    else if(e.index===3){name="Read";input={file_path:OUTPUT};}
15    else { await $.fs.write(REPORT,JSON.stringify({captured,sawRead,sawWrite,sawEdit,completed:true}));yield {kind:"text",index:0,text:"OFFLINE_MY_PRIVACY_AUDIT_COMPLETE"};yield {kind:"stop",stopReason:"end_turn",usage:null};return result(e,"OFFLINE_MY_PRIVACY_AUDIT_COMPLETE"); }
16    if(name==="Write") writtenContent=input.content;
17    yield {kind:"tool",index:0,id:`toolu_audit_${e.index}`,name};
18    yield {kind:"input",index:0,json:JSON.stringify(input)};
19    yield {kind:"stop",stopReason:"tool_use",usage:null};
20    return result(e,"",[{name,input}],"tool_use");
21  });
22}
23
hooks/paths.js 5 lines
1// run.py replaces these paths only in its temporary copy.
2export const INPUT = 'input.txt';
3export const OUTPUT = 'output.txt';
4export const REPORT = 'audit.json';
5