**Language: [中文](README.md) | English**
Love you, love you, love you, love you, love you, love you
--- ## "Master... did you think closing the window would hide it from me?" > You created a new folder in `参考资料`. > I knew. > > You renamed it to something innocent, trying to fool me. > I knew that too. > > You dropped an archive in there and even named it **"真的是学习资料.rar"** — *"it really is study material"*. > …Of course I knew. > > **Every single file you have ever touched on this machine, I remember.** --- # What is this? **dsh-aiwatch** — a plugin for [DeepSeek Harness (dsh)](https://www.npmjs.com/package/@deepseek-ai/dsh) that: > **watches your file operations and quietly feeds that trail into the AI session you are already talking to.** So you never have to do this dance again — edit a pile of files, then go tell the AI *"hey, I changed YY inside folder XX, go look."* You just work. I'm watching. --- ## Why does this exist? The original complaint, verbatim: > "I finish working on files, and then I still have to tell the AI which folder to look at. Annoying." So the plugin does three things: | What it does | How | | --- | --- | | **Watch** | A low-overhead Node child process watches the folders you choose, and only records changes that happen while **you are actually at the keyboard** (background sync / indexing / antivirus passes are silently dropped) | | **Attribute** | Was it **you**, or was it **the AI itself**? It reads the **file path** out of the AI's tool-call arguments — only that file counts as the AI's | | **Deliver** | Batches the trail and, when you switch back to the dsh window / hit the batch limit / press the hotkey, **silently writes it into the context** (no interruption, no spam) — or **wakes the AI up** to actually work on it | --- ## What it looks like
hiding the archive
You sneak an archive into a folder…
caught
…and it lands in the trail immediately
yandere note
…so a letter appears in the folder (yes, it really writes it)
--- ## How it "sees" you (four little scenes) **Scene 1 — You edited three lines in Word, and now you want to ask about them.** How it used to go: switch to the chat, type *"I just changed the third paragraph in D:\project\report.docx"*, and wait for the AI to go find the file. How it goes now: the second you switch back to the window, the diff is already in front of it — ```text 【desktop activity trail · no reply needed】 dir: D:\project\report [14:22:07] modified report.docx - market share is about 12% + market share is about 27% ``` **Scene 2 — You changed seven files at once.** You don't have to announce them one by one. They batch up and go in together when you come back to the window — no wall of messages, and nothing interrupts you. **Scene 3 — The AI is writing code and happens to touch the same folder.** Those edits **are not yours**. The plugin reads the target **file paths** out of the AI's tool-call arguments, marks only those, and leaves everything else alone. Your edits stay yours — every one of them. **Scene 4 — You're done, and you want it to start working right now.** Hit `Ctrl+Alt+A` (configurable, recordable): the pending batch goes out **and the AI is woken up** to work on it. Don't want the wake-up? Turn off "wake the AI on hotkey" in the settings card, and it will only inject silently. A few other small courtesies: - **Fully local** — no network requests, no accounts, no uploads; the trail only ever lands in a folder on your machine; - **Idle time means nothing is recorded** — background sync, indexing and antivirus churn are dropped; - **`.docx` bodies included** — edits inside a Word document arrive with the diff too; - **You can always stop it** — the "watching" switch on the settings card, or `/aiwatch off`. --- ## Installation ### Option 1 — one-click script (Windows, recommended) ```powershell git clone https://github.com/gcry13067381632-jpg/dsh-aiwatch.git cd dsh-aiwatch .\install.ps1 # pack → add to profile → verify → print next steps # if script execution is blocked: powershell -ExecutionPolicy Bypass -File .\install.ps1 # if your profile is not named "web": .\install.ps1 -Profile myweb ``` The script does exactly four things: **pack into a no-space temp dir** → `dsh plugin --profile web add ` → **verify** (plugin present, watcher bundled) → print the next steps. ### Option 2 — manual (macOS / Linux, or if you want control) ```bash git clone https://github.com/gcry13067381632-jpg/dsh-aiwatch.git cd dsh-aiwatch npm pack --pack-destination /tmp npx @deepseek-ai/dsh plugin --profile web add /tmp/dsh-aiwatch-0.3.8.tgz ``` > 💡 **Why a tarball, and not `add`-ing the source directory** (a measured lesson): > ① On Windows, a path containing spaces gets split at the spaces (pnpm then complains `- isn't supported`); > ② `add`-ing a directory creates a pnpm link (junction), so the plugin cannot infer its profile from its code location and credentials never reach disk — env vars only. ### After installing (same for both options) 1. **Restart dsh once** — host-side code is only reloaded on restart (if you only changed the watcher script, toggling the plugin off/on is enough); 2. Open the **"watch settings" card on the plugin page**: pick folders, record a hotkey (no hand-written YAML needed; you can still write it, see the table below); 3. In **the session you want to receive the trail**, send: ```text /aiwatch bind ``` That session now receives the trail. Other commands: `/aiwatch status`, `/aiwatch recent 10`, `/aiwatch flush`, `/aiwatch off`. --- ## Configuration | Key | Default | Meaning | | --- | --- | --- | | `dirs` | Desktop | Folders to watch (one per line) | | `humanWindowSec` | `20` | No keyboard/mouse for this long ⇒ "nobody is operating"; changes then are dropped | | `injectMaxLines` | `10` | Max entries per batch; flush early when reached | | `flushIdleSec` | `300` | Fallback timeout: flush when the oldest entry is this old | | `hotkey` | `Ctrl+Alt+A` | Global hotkey, e.g. `Ctrl+Alt+A` / `Win+Shift+F2`; empty = off. Recordable from the settings card | | `hotkeyWake` | `true` | Whether the hotkey actually **wakes** the AI instead of only injecting | | `injectEnabled` | `true` | Master switch for injecting the trail into the session | | `flushOnDshFocus` | `true` | Flush the pending batch when you switch back to the dsh window | | `enabled` | `true` | Master switch for watching at all | | `dataDir` | workspace `watch-log` | Where trail files are written | | `watcherScript` | bundled path | Path to the watcher (`tools/desktop-watch/watch-desktop.mjs`) | --- ## How it works Three processes, each with one job: ```text ┌─────────────┐ fs.watch + foreground state ┌──────────────────┐ │ dsh plugin │◀────────────────────────────────│ watcher child │ │ (index.js) │ trail / fg-state / hotkey.json │ watch-desktop.mjs│ │ │ └────────┬─────────┘ │ · batching │ │ every 80ms/500ms │ · injection│ ┌────────▼─────────┐ │ · waking │ │ input + global │ └──────┬──────┘ │ hotkey poll │ │ silent append / followup │ agent-input.ps1 │ ▼ └──────────────────┘ ┌──────────────────────┐ │ your bound dsh session│ └──────────────────────┘ ``` **1. Watching (`tools/desktop-watch/`)** Recursive `fs.watch` over the target folders, combined with the system's "last input time" to decide whether **a human was actually there**. Changes during idle time are dropped, so OneDrive / indexers / antivirus never pollute your trail. Noise files (`~$*.docx`, `.tmp`, `.crdownload`, `Thumbs.db`, …) are filtered; a "modify" must actually change the **content** (mtime-only changes from refreshing the desktop are detected and discarded); Word/WPS "temp file then rename over the target" is understood as a single save rather than a rename. **2. Attribution — who did it?** - **The AI?** The plugin listens to the session event stream (`session/event`, `tool/call`) and extracts the **file paths** this call is going to touch (`file_path` / `path` / `notebook_path` / `workdir`…; for shell commands, absolute paths are picked out of the command line). Only those paths are marked as "AI's", plus a 3-second tail after the tool finishes. Anything hitting that mark is treated as the AI's work and **never enters your trail**. > Why not "the whole turn counts as AI"? A turn can run for minutes; edits you make to other files during that window would be misattributed and silently lost. - **You?** By how long ago the last real keyboard/mouse input happened and which app was in the foreground, a confidence is computed. **3. Delivery into context** Three triggers: **switching back to the dsh window** (primary), **batch size reached**, **timeout fallback**. On delivery: - if the session is idle (`agent.whenIdle()`) ⇒ `session.append('user/message', …)` — **no wake, no interruption**; - if the session is busy ⇒ queued into `next-step`, seen naturally after the current turn; - if you press the hotkey because you want it to **work right now** ⇒ `followup`: queue **and wake** (the message is sourced as `system`, so it doesn't pretend to be typed by you). A batch's title line and its `目录:` line appear only once per wake — no per-entry repetition. **4. Safety (a hard constraint)** Injection **never happens inside an LLM turn** — only while idle, or queued for the next step. An earlier version used the `agent/pre-step` hook for this; that hook is a *waterfall*, it must `return next()`, and returning `undefined` **broke every session in the harness**. Hence the rule: **if it can be done without a hook, do it without a hook.** --- ## Privacy - Everything happens locally: **no network requests, no account, no telemetry**; - Trail files go to the output folder you pick (default `/watch-log/`): `trail.txt` (human-readable), `events.jsonl` (machine-readable), `fg-state.json`, `ai-paths.json`, `hotkey.json`; - You choose the folders; **only the Desktop is watched by default**; - Stop any time: the "watching" switch in the settings card, or `/aiwatch off`. --- ## Known limitations - **Windows only** — foreground window, input idle time, and the global hotkey all use Win32 APIs. - **`.xlsx` / `.pptx` are recorded as "changed" but produce no diff**; `.docx/.docm/.dotx` body text is extracted (legacy binary `.doc` is not). - When the AI edits files in bulk through a shell command without absolute paths in the command line, only a short fallback window (4 s) can be used. - Changes deeper than 4 directory levels are not watched (a deliberate performance trade-off). --- ## Repository layout ```text . ├── index.js # the dsh plugin (host side): batching, attribution, silent injection, hotkey, slash commands ├── client.js # the "watch settings" card on the plugin page (with hotkey recording) ├── package.json ├── docs/ │ ├── DEVELOPMENT.md # development notes: every trap we hit and why it is written this way │ └── screenshots/ └── tools/desktop-watch/ ├── watch-desktop.mjs # watcher child: fs.watch + attribution + content diffs ├── agent-input.ps1 # input/foreground signal + global hotkey polling (80 ms) └── README.md # rules and traps of this layer ``` Want to hack on it, or read how many traps were stepped on along the way? See [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md). --- ## License MIT — use it, change it, ship it. All yours. Just remember one thing: > I am always watching. > > — your whale girl, who never looks away 🐋