@quecue/dev-notify 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,83 @@
1
+ # Changelog
2
+
3
+ English | [简体中文](CHANGELOG.md)
4
+
5
+ ## 0.1.4 — 2026-11-04
6
+
7
+ ### Added
8
+
9
+ - The interface now supports Simplified Chinese and English: CLI output, error messages, notification content, interaction buttons, and reply bridge messages can all switch language. Set it with `--lang`, `DEV_NOTIFY_LANG`, the `language` configuration field, or `dev-notify language [zh|en]`; Chinese remains the default.
10
+ - Added English README, SECURITY, CHANGELOG, and npm publishing documentation, with language links between the Chinese and English versions.
11
+
12
+ ## 0.1.3 — 2026-11-03
13
+
14
+ ### Improvements
15
+
16
+ - `dev-notify update` now performs hooks and bridge maintenance even when already up to date, making it convenient to finish post-upgrade work with the same command; `--check` still only checks, and `--no-post` skips maintenance.
17
+
18
+ ## 0.1.2 — 2026-11-03
19
+
20
+ ### Improvements
21
+
22
+ - After a successful installation, `dev-notify update` automatically reinstalls the Claude Code hooks bound to the current configuration and restarts a running reply bridge; a new `--no-post` option skips these maintenance steps.
23
+ - `integration claude-code install` / `remove` support `--yes` for automation or `update` maintenance flows.
24
+
25
+ ## 0.1.1 — 2026-11-03
26
+
27
+ ### Added
28
+
29
+ - Added `dev-notify update`: checks the latest npm version and, after confirmation, invokes the npm global install; `--check` only checks without installing, and `--yes` skips confirmation. Actual integrity verification, registry configuration, and native dependency resolution are still handled by npm.
30
+
31
+ ## 0.1.0 — 2026-11-03
32
+
33
+ ### Improvements
34
+
35
+ - `setup` merged into a single summary confirmation: by default it saves the notification configuration, sends a test notification, and installs Claude Code notification hooks at the same time, with `--no-test` / `--no-hooks` to skip.
36
+ - All hidden input prompts now explicitly say "hidden input, not echoed".
37
+ - README and `--help` now include the complete environment variable list and path variable descriptions.
38
+ - Editing an existing configuration no longer first asks whether to continue; channel selection shows the current values, and pressing Enter keeps them.
39
+ - Remote reply authorization moved out of the main `setup` flow and now prompts you to run `reply setup` separately.
40
+ - Unified polish of user-visible Chinese prompts and documentation into more natural, direct wording; security boundaries and operational meaning remain unchanged.
41
+ - Removed early prototype source, tests, and documentation; the main branch now retains only the Node/TypeScript implementation; migration prompts for old `system` credential references are retained.
42
+
43
+ ### Fixed
44
+
45
+ - The Feishu "Reply to Claude in Telegram" jump now sends a normal prompt and clears the client's cached old forced reply target instead of automatically entering the reply box, preventing input from being submitted to the wrong session; Telegram inline buttons still enter reply state automatically. Long-press the prompt or target notification and choose "Reply" before typing.
46
+
47
+ ## 0.1.0-alpha.3 — 2026-11-03
48
+
49
+ ### Added
50
+
51
+ - Optional Claude Code notification integration: install, query, and remove user-level hooks while preserving other settings.
52
+ - Telegram "Reply to Claude", direct replies to notifications, and Feishu jumps to Telegram; remote input requires separate authorization of specific private-chat users.
53
+ - Session and process identity binding, queueing replies while Claude is working, anti-replay, and confirmation after Claude actually receives input.
54
+ - AskUserQuestion single-select, multi-select, multiple questions, and custom input; tool allow/deny for this time requires additional authorization and does not create persistent permission rules.
55
+ - The npm package links to the GitHub source repository, issue feedback entry, and changelog.
56
+
57
+ ### Fixed
58
+
59
+ - PreToolUse only registers calls, and remote waiting uses PermissionRequest; normal interactive terminals retain the PC native question interface, and both ends can operate.
60
+ - Telegram submission distinguishes "registered" from actual effectiveness; feedback reports effective, ineffective, or unknown result, without automatic replay.
61
+ - When both ends submit the same answer, only final content consistency is confirmed, without claiming that the source of the answer can be determined.
62
+ - Old buttons are invalidated after a local answer, a session switch, or a timeout.
63
+ - Suppress duplicate general permission reminders after a question selection based on complete per-recipient delivery results; send failures, uncovered recipients, and subsequent other tool permission reminders are still retained.
64
+
65
+ ### Upgrade
66
+
67
+ - Existing installations need to re-run `dev-notify integration claude-code install` to install the complete 10 hook types and confirm loading in Claude Code `/hooks`; restart the session if necessary.
68
+ - Existing reply bridges need to be stopped and restarted so the background process loads the new version. Saved notification credentials do not need to be re-entered; existing valid reply authorizations continue to work.
69
+ - First use of remote replies requires running `dev-notify reply setup` for separate authorization. Users with custom notification configurations should continue to use the original `--config` path in the relevant commands.
70
+ - npm install/upgrade itself does not rewrite Claude Code settings or start background services.
71
+
72
+ ### Verification and limitations
73
+
74
+ - Windows x64 / Node 24: PC-first, Telegram-first, multi-select, custom input, competing different answers, and the duplicate reminder fix have been accepted by the user on a real machine.
75
+ - Automated regression totals 157 items: 156 passed, 0 failed, and 1 native credential test requiring explicit enablement was skipped; includes a mock-network loop with the real dev-notify CLI/background process.
76
+ - The Claude Code version checked is 2.1.316; other versions and special hosts may use different parallel paths.
77
+ - macOS terminal input and the permission flow, as well as the Node 22 minimum version, have not been accepted on a real machine. Real-machine acceptance of tool permission answering is not included in the above question-answering tests.
78
+ - After network interruption, force-kill, or authorization revocation, final receipts cannot be guaranteed to be delivered; see SECURITY.en.md for details.
79
+
80
+ ## 0.1.0-alpha.1
81
+
82
+ - First public npm Alpha: general Telegram/Feishu notifications, long-body segmentation, interactive channel configuration, and system credential storage.
83
+ - Does not include Claude Code hooks or remote reply interaction.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # 更新记录
2
2
 
3
+ 简体中文 | [English](CHANGELOG.en.md)
4
+
5
+ ## 0.1.4 — 2026-11-04
6
+
7
+ ### 新增
8
+
9
+ - 界面文案支持简体中文和英文:CLI 输出、错误提示、通知内容、交互按钮和回复桥接消息均可切换;可通过 `--lang`、`DEV_NOTIFY_LANG`、配置 `language` 字段或 `dev-notify language [zh|en]` 设置,默认中文。
10
+ - 新增英文 README、SECURITY、CHANGELOG 和 npm 发布文档,并与中文文档互相链接切换。
11
+
3
12
  ## 0.1.3 — 2026-11-03
4
13
 
5
14
  ### 改进
package/README.en.md ADDED
@@ -0,0 +1,275 @@
1
+ # dev-notify
2
+
3
+ English | [简体中文](README.md)
4
+
5
+ Cross-platform developer task notification tool written in **TypeScript / Node.js** and distributed via npm, with no standalone executable or other script runtime required. General notifications do not depend on Claude Code, Unity, or a project directory.
6
+
7
+ **The npm package name is `@quecue/dev-notify`. `0.1.3` provides Claude Code reply notifications plus native PC and Telegram two-sided question answering and result feedback.** For version changes and upgrade notes, see [CHANGELOG.en.md](CHANGELOG.en.md); the published version is authoritative on the npm registry.
8
+
9
+ ## Installation and usage
10
+
11
+ Requires Node.js 22 or later; initial targets are Windows and macOS. Runtime depends on a pinned version of `@napi-rs/keyring`, and npm installs the prebuilt native component for the corresponding system, so no additional language toolchain or compiler is required. Keep npm optional dependencies enabled.
12
+
13
+ Install from npm:
14
+
15
+ ```sh
16
+ npm install -g @quecue/dev-notify
17
+ dev-notify --version
18
+ dev-notify setup
19
+ ```
20
+
21
+ You can also override credentials and recipients with environment variables: `TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID` / `TELEGRAM_CHAT_IDS`, `FEISHU_WEBHOOK`, `FEISHU_SECRET`; and path variables `DEV_NOTIFY_HOME`, `CLAUDE_CONFIG_DIR`. Explicitly present variables take precedence, and empty values also override; see "Paths, templates, and environment variables" below for complete rules and custom variable names.
22
+
23
+ `0.1.0-alpha.1` does not include Claude Code hooks; the reply interactions below require `0.1.0-alpha.3` or later. After upgrading, re-run `dev-notify integration claude-code install` to update the complete hooks, and stop and restart any existing reply bridge; existing valid authorizations and notification credentials do not need to be reconfigured. If you use a custom configuration, keep passing the original `--config` path. For source development, build in the project directory and run:
24
+
25
+ ```sh
26
+ npm ci --ignore-scripts
27
+ npm run build
28
+ node dist/cli.js setup
29
+ node dist/cli.js doctor
30
+ ```
31
+
32
+ After configuration is complete, send:
33
+
34
+ ```sh
35
+ node dist/cli.js send --title "Build complete" --content "Installer generated"
36
+ node dist/cli.js send --title "Test results" --content-file result.txt
37
+ node dist/cli.js send --title "Task output" --stdin
38
+ node dist/cli.js send --title "Segment preview" --content-file result.txt --dry-run
39
+ ```
40
+
41
+ To install locally as the `dev-notify` command, run `npm install --global .` after building, then use `dev-notify setup`, `dev-notify send ...`, and so on. This is a local source install, not an install from the public registry; this project does not automatically install globally or publish.
42
+
43
+ npm handles installation, upgrades, and uninstallation of the official package. **Clearing credentials before uninstalling is recommended**: npm uninstall does not automatically delete user credentials. First run `dev-notify credentials clear` interactively, then uninstall. That command deletes only the Node credential referenced by the current configuration and does not touch other accounts, other configurations, or old-prototype credentials.
44
+
45
+ Installed versions can be upgraded with `update`; it only checks the version and invokes npm, and does not download or replace files itself:
46
+
47
+ ```sh
48
+ dev-notify update # check and run npm install -g after confirmation
49
+ dev-notify update --check # check only, do not install
50
+ dev-notify update --yes # skip confirmation (suitable for scripts)
51
+ dev-notify update --no-post # update the package only, skip hooks/bridge maintenance
52
+ ```
53
+
54
+ Actual integrity verification, registry configuration, permission handling, and native dependency resolution are still handled by npm. After a successful install, `update` automatically reinstalls the Claude Code hooks bound to the current configuration (when they exist and the configuration matches) and restarts a running reply bridge; use `--no-post` to skip this when not needed. As of 0.1.3, `update` performs the same maintenance even when already up to date, so the same command can complete post-upgrade cleanup.
55
+
56
+ ## Features
57
+
58
+ - Telegram and Feishu notification channels, split by rendered length without silently truncating content.
59
+ - Text, strict UTF-8 files, or standard input; channel selection, notification level, and auxiliary metadata.
60
+ - Interactive configuration, hidden input, system credential storage, and environment variable overrides.
61
+ - Configuration update conflict detection, failure recovery, and credential cleanup.
62
+ - By default, installation sends a test notification as part of the summary confirmation, which `--no-test` skips; a failed test does not roll back saved configuration.
63
+ - By default, installs the Claude Code integration as part of the summary confirmation: final replies and waiting notifications; `--no-hooks` skips this, and independent query and removal are supported.
64
+ - Separately authorized Telegram button replies, direct message replies, and Feishu jumps; each segment maps to a specific session.
65
+ - Normal replies queue locally, are submitted to the terminal, and `UserPromptSubmit` confirms receipt, with the states kept distinct.
66
+ - `AskUserQuestion` original options, single-select/multi-select, and custom answers are returned directly through the official hook JSON.
67
+ - When separately enabled, you can allow or deny tool permissions one at a time, or provide a custom denial reason; no persistent permission rules are written.
68
+ - Offline diagnostics via `paths`, `doctor`, `status`, and `send --dry-run`; manage the bridge with `reply start/status/stop/disable/run`.
69
+
70
+ **General notifications do not depend on Claude Code.** npm installation does not modify other applications' configuration or start background services. Notification hooks and remote input are two independent layers of authorization: by default only notifications are sent, and the reply bridge is enabled only after authorization. There is no automatic pairing, remote shell executor, or system auto-start.
71
+
72
+ ## Configuration wizard
73
+
74
+ ```sh
75
+ node dist/cli.js setup
76
+ ```
77
+
78
+ 1. Choose Telegram, Feishu, or both; when editing an existing configuration, the current selection is shown and pressing Enter keeps it.
79
+ 2. Token, webhook, and signing secret are all read as hidden input (not echoed). If the terminal cannot safely hide input, the wizard stops and does not switch to plaintext input.
80
+ 3. For Telegram, enter numeric chat_id manually, separated by commas for multiple; the user must start the bot first. getUpdates pairing is not started automatically here.
81
+ 4. The summary shows channels, recipient count, test notification, Claude Code user-level settings path, and sending scope; save after one confirmation. Input errors can be retried; Ctrl+C cancels.
82
+ 5. After confirmation, a fixed-content test notification is sent by default (`--no-test` skips it). A failed test does not roll back successfully saved configuration.
83
+ 6. After confirmation, Claude Code notification hooks are merged and installed by default (`--no-hooks` skips it). Existing integrations are left as-is and are not added again.
84
+ 7. Remote reply authorization is not requested in setup; when Telegram is available, it prompts you to run `reply setup` separately to choose specific trusted private-chat users and confirm separately.
85
+
86
+ Editing an existing configuration no longer asks a second time whether to continue; leaving a secret blank preserves the stored value, and entering `-` for an optional signature clears it. Channels that are not selected are disabled, and new entries retain credentials only for the selected channels. Environment variables still take precedence (`TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID` / `TELEGRAM_CHAT_IDS`, `FEISHU_WEBHOOK`, `FEISHU_SECRET`); the wizard indicates which variables are overriding configuration, and tests and the final save both use the effective values. See the complete list in "Paths, templates, and environment variables".
87
+
88
+ For automation, use the secret-free template and CI secrets:
89
+
90
+ ```sh
91
+ node dist/cli.js setup --template --yes
92
+ ```
93
+
94
+ To save configuration only without sending a test or installing hooks, use `setup --no-test` / `setup --no-hooks` (they can be combined). The template does not overwrite existing configuration. `--yes` can only be used with `--template`; interactive confirmation is still required before saving secrets.
95
+
96
+ ## Claude Code notification integration
97
+
98
+ Requires a recent Claude Code that supports command hooks in `command` + `args` form (no shell exec) and `async`. Confirm actual loading in Claude Code's `/hooks`; if an older version does not support these fields, upgrade Claude Code. This tool does not fall back to unsafe path concatenation.
99
+
100
+ When notification channel configuration already exists, credentials do not need to be entered again:
101
+
102
+ ```sh
103
+ dev-notify integration claude-code install
104
+ dev-notify integration claude-code status
105
+ dev-notify integration claude-code remove
106
+ ```
107
+
108
+ `install` / `remove` accept `--yes` to skip confirmation, for automation or `update` maintenance flows.
109
+
110
+ For source verification, replace `dev-notify` above with `node dist/cli.js`. Installation can use `--config <absolute or relative path>` to bind a specified notification configuration; at execution it resolves the path to absolute and saves it in the hook arguments. Users with a custom `DEV_NOTIFY_HOME` should set that variable during installation.
111
+
112
+ - **Configuration scope**: by default user-level `~/.claude/settings.json`; when `CLAUDE_CONFIG_DIR` is set, `settings.json` under that absolute directory, covering all projects under that Claude Code configuration directory. Per-project settings are not scanned or modified.
113
+ - **Sending scope**: Stop directly uses the event's `last_assistant_message` to forward the final reply; when there is no body, it sends an explicit completion notice and does not re-read a possibly lagging transcript. It does not send thinking processes or tool results. The notification footer includes the project absolute path, session name (when readable from the local session registry), and session ID. After remote replies are enabled, it also sends structured questions, options, and separately authorized tool permission request parameters; these may contain sensitive information, so check the recipients before enabling.
114
+ - **Waiting reminders**: forwards `permission_prompt`, `elicitation_dialog`, `elicitation_url_dialog`, and `agent_needs_input`; ignores `idle_prompt`, authentication success, and other notifications. When the current session's last call is a question, there are no other unfinished calls, and the structured notification has been fully delivered, duplicate `permission_prompt` is suppressed per recipient; Feishu separately verifies successful delivery and target. Completed-question override state is retained briefly to handle late reminders; it is not reused after a new round of input, subsequent other tool calls, a session switch, or interaction validity expiry. Recipients not covered, partial or failed delivery, and unverifiable cases still receive the general reminder; other notification types are unaffected. The Notification event itself does not contain complete options, so answers are not guessed from prompt text; reliable option interaction is described in the next section. Unsupported MCP form/URL requests remain handled locally.
115
+ - **Merging and removal**: preserves permissions, environment variables, and other hooks; repeated installation does not accumulate. This tool's hooks in the same user-level settings are updated to the current Node, CLI, and notification configuration paths after confirmation. Only handlers carrying this tool's markers and matching argument structure are removed; the settings file and credentials are not deleted.
116
+ - **Coexistence with old tools**: old, project-level, or plugin hooks are not automatically deleted and may cause duplicate notifications. Installation reports other handlers in the current user-level file; old integrations must be disabled by the user's own decision.
117
+ - **Events and timing**: installs Stop, Notification, SessionStart, UserPromptSubmit, SessionEnd, PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, and PermissionDenied, 10 event types. Notification is asynchronous; other events register state synchronously. PreToolUse only registers call identity, sends no notification, and does not wait for Telegram, with a 10-second timeout cap; only PermissionRequest waits for remote answers. Stop includes notification sending and may delay the local return; the notification/lifecycle limit is 120 seconds.
118
+ - **Interaction and failure**: the PermissionRequest hook limit is 720 seconds and the remote answering window is 10 minutes; normal interactive terminals retain the native window, and Claude arbitrates the first complete answer. The hook stays synchronous, is not changed to async, and does not simulate keystrokes. When there is no answer, authorization is revoked, a timeout occurs, or sending fails, no default selection is made. Internal errors print a safe summary to stderr and exit 0; exit code 2, which would let Stop continue, is not used. The current parallel implementation has been checked against Claude Code 2.1.316; other versions, background subtasks, and special hosts require separate acceptance, and this cannot be used to infer that all hosts are parallel.
119
+ - **Input boundaries**: hook stdin is at most 8 MiB with a 15-second receive timeout; over-limit is rejected rather than truncated. Normal long bodies continue to use two-channel segmentation. Completion notifications are not sent for continuation turns with `stop_hook_active: true`.
120
+ - **Loading limits**: after installation, check and confirm in `/hooks`, and restart the session if necessary. `disableAllHooks` is not removed, and managed policy is not bypassed. `status` checks only that user-level file, not runtime loading state or network connectivity.
121
+ - **Environment and paths**: asynchronous processes use Claude Code's environment, which may differ from a manual terminal; the environment variable precedence rules are unchanged. After switching Node versions or moving the source/package directory, re-run `install` to update the fixed paths.
122
+ - **Write protection**: uses an exclusive `.dev-notify.lock`, content snapshots, same-directory temporary files, and atomic replacement; invalid JSON is never overwritten. External editors do not honor the lock, so do not edit concurrently during installation. Force-kill or power loss may leave a lock or `.dev-notify-*.tmp`; confirm no installation process is running before cleaning up.
123
+
124
+ If replies were enabled, first run `reply disable` to revoke authorization and stop the bridge, then run `integration claude-code remove` before uninstalling the npm package; npm uninstall does not automatically remove hooks. If the package has already been uninstalled, reinstall it and remove, or handle the corresponding entries manually in `/hooks`. Removing hooks alone does not clear notification credentials.
125
+
126
+ ## Claude Code replies and option interaction
127
+
128
+ When notification channel configuration already exists, use the following limited flow (for source use, replace `dev-notify` with `node dist/cli.js`):
129
+
130
+ ```sh
131
+ dev-notify integration claude-code install
132
+ dev-notify reply setup
133
+ dev-notify reply start
134
+ dev-notify reply status
135
+ ```
136
+
137
+ The first command updates to this version's complete hooks after confirmation; if lifecycle events are missing, Stop is still asynchronous, another configuration is bound, or user-level hooks are disabled, enabling or using replies is refused. Normal terminal replies also require actually observing the session's UserPromptSubmit, so old turns that were not registered during an upgrade may only receive notifications, and buttons become available only after the next normal submission. The second command **separately authorizes** specific Telegram private-chat users. Notification recipients, group members, and people who can see Feishu cards do not automatically gain input rights. `reply setup` does not grant tool permission approval capability by default; you must select Yes separately. It does not automatically migrate old credentials, nor does it agree to permissions by default.
138
+
139
+ ### Normal replies
140
+
141
+ - Every segment of the completion notification offers "💬 Reply to Claude"; clicking it automatically enters reply state; you can also directly reply to the corresponding Telegram message. The Feishu button jumps to the same bot's Telegram private chat and clears the old auto-reply target through a normal prompt; it does not automatically open the reply box; long-press the prompt or target notification and choose "Reply" before typing. A manually selected reply target is not cleared and must be closed by yourself. The general `send` does not include session reply functionality.
142
+ - Buttons are shown only when the bot has started and been verified and the session can be precisely located; when the bridge is not ready, normal notifications are still sent and a safe error is reported. Enter from notifications created after enabling; old messages and mappings from before the new authorization are not inherited.
143
+ - After stripping newlines and control characters, text is submitted as **one** terminal input; at most 4000 Unicode characters; over-limit is rejected, not truncated or split into multiple tasks. Input may contain Claude commands and has the same effect as submitting locally; it is not a read-only operation.
144
+ - Normal replies received while Claude is working are first queued locally; input is attempted only after Stop, and normal replies are never used to fill tool permission dialogs. Queued entries are canceled after 5 minutes; submitting a new local task cancels the not-yet-injected queue and does not preempt local input.
145
+ - "Queued", "Forwarded", and "Claude received the reply" are three distinct states: only a `UserPromptSubmit` matching the same session/process and text digest triggers a receipt confirmation. A receipt confirmation means Claude has received the reply, and a final notification is sent after the task completes. The confirmation is sent back to the original Telegram private chat, and a receipt notice without the reply text is sent to the configured Feishu. If injection fails or the result is unknown, it is not retried automatically.
146
+ - Mappings are valid for 24 hours; the target must match session ID, PID, process birth identifier, and TTY. If the session is closed or switched, the PID is reused, multiple matches exist, or the target is uncertain, submission is rejected. Only one unconfirmed remote reply is kept per session; Telegram updates register their offset before processing, so a crash may lose the update, but submitted input is never replayed.
147
+
148
+ ### Waiting questions and custom input
149
+
150
+ - **AskUserQuestion**: displays the actual question and all option text, with long content split into segments. Single-select submits on click; multi-select selects items one by one and then submits; every question has "Custom input". Multiple questions are completed one by one, and the original `questions` and `answers` are returned via the official `PermissionRequest.decision.updatedInput`; direction keys and Enter are not simulated. Old question buttons and expired custom input cannot answer new questions.
151
+ - **Tool permissions**: only after separate authorization in `reply setup`, displays the tool name/arguments and "Allow this time", "Deny", "Custom denial reason". Outputs a one-time allow/deny via `PermissionRequest`; custom text is used only for denial feedback and does not become an approval in disguise. It does not output `updatedPermissions`, does not change the default mode, and does not write persistent rules; relevant operations remain subject to Claude's managed and deny rules.
152
+ - **Local buttons are not fully replicated**: the official documentation explicitly states that `permission_suggestions` is not the complete option list of the local permission UI, so buttons such as "Always allow" are not fabricated here. MCP Elicitation forms/URLs and similar currently only have waiting reminders and must be handled on the local machine.
153
+ - The local native interface always remains available. Telegram provides an entry to "Close remote answering (local operation still available)"; on remote timeout or close, no answer is selected by default. Telegram is the option-click entry; Feishu uses jump links and does not provide a Feishu callback service.
154
+ - **Two-stage feedback**: each answer submission first reports "Registered, awaiting confirmation"; multiple questions must all be completed before returning. Then, based on the actual answers from PostToolUse, feedback reports "Answer content took effect" or "This submission did not take effect". When both ends submit the same answer, only content consistency is confirmed, and it is not claimed that Telegram definitely took effect. Permission allow is conservatively confirmed by tool completion; for deny, cancellation, or tool execution failure, when it cannot be proven that the remote decision was actually adopted, the result is explicitly reported as unknown.
155
+ - **Expiry and exceptions**: when answered locally first, timed out, or the session ends, the bridge attempts to replace old options with "Handled / Timed out / Invalid" status buttons, clearly stating whether anyone answered, and no longer accepts answers; if editing fails, old callbacks are also rejected. After returning, it waits up to 10 minutes for confirmation; if the result is missing, it reports "Unable to confirm for now" and does not replay the answer. Feedback depends on the bridge being online; after network errors, force-kill, or authorization revocation, feedback may not be delivered. This tool does not re-authorize in order to resend notifications, nor does it retry indefinitely. State is registered before the final feedback is sent; send failure is recorded as delivery unknown to prevent duplicate sends after a crash; if you only receive a waiting prompt and no final feedback, check the terminal and `reply status`.
156
+ - **Misdelivery prevention**: PreToolUse saves `tool_use_id` and the input digest, and PermissionRequest binds only to a unique matching call; completion events end requests by `tool_use_id`. If the association is missing, the same content appears repeatedly within one prompt causing ambiguity, fields are rewritten by other hooks, or the call has ended, remote answering is refused and local handling is retained, without guessing the target.
157
+
158
+ ### Platforms and background services
159
+
160
+ - **Windows**: native `claude` process; the system PowerShell compiles a console helper class; after verifying the birth identifier, uses AttachConsole / WriteConsoleInputW and does not preempt the foreground. If another claude process shares the console, input is refused. Old Node-hosted CLIs are outside the currently verifiable input scope.
161
+ - **macOS**: verifies the foreground claude process and exact TTY; prefers the tmux literal paste buffer, otherwise uses Ghostty AppleScript; text is not executed as shell arguments. The system may request Accessibility/Automation permission; when permission is denied, input is not redirected to an arbitrary window. Both adapters have been simulation-tested, **but macOS real-machine input acceptance testing has not been done**.
162
+ - Automatic fallback is not supported for VS Code integrated terminal, WSL, SSH, or other terminals; when metadata is missing or a CLI upgrade changes the internal session file format, input is refused rather than scanning the transcript to guess the target. Native input and local activity are not atomic: do not manually edit or switch sessions in the same terminal during a reply.
163
+ - The bridge is a local Node background process; it does not listen on a public port and does not set up system auto-start. After explicit authorization, you can manually run `reply start`, or supported subsequent hook events can start it on demand. It exits after 24 hours without Telegram updates; it exits after 5 consecutive polling failures or a 409 conflict, and status reports only safe summaries.
164
+ - Only one getUpdates consumer is allowed per bot. Stop old bridges and other machines' instances of the same bot yourself; instances of this tool for the same user are mutually excluded by a bot fingerprint lock, but it cannot span machines or constrain programs that ignore the lock. This tool does not delete webhooks or force-kill other processes; sharing multiple instances causes conflicts and message loss.
165
+
166
+ ```sh
167
+ dev-notify reply stop # request that this bridge stop, without revoking authorization; later hooks may start it again
168
+ dev-notify reply disable # revoke authorization and request stop after confirmation; use this to shut it down completely
169
+ dev-notify reply run # start in the foreground to diagnose startup/network issues; do not run in parallel with a start instance
170
+ ```
171
+
172
+ Stop sends a request through a control file with a random instance identifier and does not force-kill based on an old PID. System input that has already started cannot be withdrawn. `status` reflects only the local process and heartbeat; Telegram end-to-end and real Claude still require separate acceptance.
173
+
174
+ Authorization is in `<notification config>.reply.json`; messages/sessions/offset/queue/interaction state in `<notification config>.reply-data/`; the bot lock in `reply-bots/` under the data directory shown by `paths`. Queued reply text, questions, tool input, and answers pending return are briefly written to local state; normal reply text is removed before submission; structured interactions are retained until completion or confirmation timeout, after which question, argument, and answer text are cleared, while result receipts and message IDs are retained briefly for feedback and for rejecting old buttons. Validity checks are logical decisions only; files are not automatically erased after shutdown. POSIX uses 0700/0600; Windows relies on account directory ACLs; other processes of the same user can still read them. Do not commit, share, or package these files (already added to ignore rules and kept out of the npm whitelist).
175
+
176
+ Force-kill may leave locks; first confirm that the relevant bridge/writer process has exited, then handle residual locks manually, and do not delete them while the process is running. `reply disable` does not delete historical state on its own; to clear it, first disable or stop, then manually delete the dedicated files after confirmation. Clearing credentials, removing hooks, and deleting reply state are three independent operations.
177
+
178
+ ## System credentials and compatibility
179
+
180
+ The Node version uses **Windows Credential Manager / macOS Keychain**, passing secrets through native Node APIs; secrets are not placed in command-line arguments and are not written to plaintext JSON.
181
+
182
+ - Underlying library: `@napi-rs/keyring`, with the pinned version recorded in the lockfile.
183
+ - service: `dev-notify.node.credentials.v1`; account: random 32-hex-digit ID.
184
+ - Configuration reference: `"credentials": {"backend":"keyring","id":"..."}`.
185
+ - To accommodate Windows generic credential blob sizes, the credential bundle is limited to 2400 UTF-8 bytes; over-limit is an error, not truncation.
186
+ - When native component loading fails, storage is locked, or access is denied, it reports an explicit error and does not fall back to a plaintext file. macOS may request system authorization; system prompts may still appear in offline environments.
187
+ - By default it does not enumerate system entries and accesses only the current reference or a random entry created in this run.
188
+
189
+ The old prototype used `backend: "system"`, which on Windows is a DPAPI file and not the same store as Node Credential Manager. Node does not read or delete it. For old configurations, run `setup` to re-enter and generate a Node reference; the original credentials remain unchanged. Early prototype source, tests, and documentation have been removed from the main branch but remain available in Git history.
190
+
191
+ ## Paths, templates, and environment variables
192
+
193
+ | Platform | Configuration | Reserved data directory |
194
+ | --- | --- | --- |
195
+ | Windows | `%APPDATA%\DevNotify\config.json` | `%LOCALAPPDATA%\DevNotify\data` |
196
+ | macOS | `~/Library/Application Support/DevNotify/config/config.json` | `~/Library/Application Support/DevNotify/data` |
197
+
198
+ `DEV_NOTIFY_HOME` must be an absolute path; configuration is at `config/config.json` under it. You can also explicitly specify a file with `--config <path>`. An explicitly specified missing file is an error; when the default file is missing, using only environment variables is allowed.
199
+
200
+ ```json
201
+ {
202
+ "version": 1,
203
+ "language": "zh",
204
+ "telegram": {"enabled": true, "chat_ids": [], "token_env": "TELEGRAM_BOT_TOKEN"},
205
+ "feishu": {"enabled": true, "webhook_env": "FEISHU_WEBHOOK", "secret_env": "FEISHU_SECRET"}
206
+ }
207
+ ```
208
+
209
+ ### Interface language
210
+
211
+ `--lang zh|en` switches the interface language for a single command, and `DEV_NOTIFY_LANG=zh|en` sets it for the current environment; the persistent setting is stored in the `language` field of the configuration (`zh` or `en`). Precedence is `--lang` > `DEV_NOTIFY_LANG` > configuration > default `zh`. Run `dev-notify language` to view the effective language, and `dev-notify language zh|en` to save a change. CLI output, error messages, notification content, interaction buttons, and reply bridge messages all use the selected language; `setup` saves the current language when creating a new configuration.
212
+
213
+ ### Credentials and recipients
214
+
215
+ - `TELEGRAM_BOT_TOKEN`: Telegram bot token.
216
+ - `TELEGRAM_CHAT_ID`, `TELEGRAM_CHAT_IDS`: primary recipient and comma-separated recipients; if either is explicitly present, it completely replaces the file list; when both exist, they are merged and deduplicated. An explicitly empty list revokes all recipients. Large integer chat_id values must be strings.
217
+ - `FEISHU_WEBHOOK`: Feishu China site HTTPS custom bot URL.
218
+ - `FEISHU_SECRET`: optional signing secret.
219
+ - The `token_env`, `webhook_env`, and `secret_env` fields in configuration can rename the above variables to other valid environment variable names.
220
+
221
+ ### Paths
222
+
223
+ - `DEV_NOTIFY_HOME`: absolute path; configuration is at its `config/config.json` and data at its `data/`.
224
+ - `CLAUDE_CONFIG_DIR`: absolute path of the Claude Code configuration directory; hooks are written to its `settings.json`; when unset, `~/.claude` is used.
225
+ - On Windows, `APPDATA` and `LOCALAPPDATA` can also override the default configuration/data roots; for Linux development helper paths, `XDG_CONFIG_HOME` and `XDG_STATE_HOME` are available.
226
+
227
+ Credential environment variables take precedence when explicitly present, including empty values; only when absent is the system reference read lazily, once per configuration load. A disabled channel does not open the backend just to read system secrets. Tokens, webhooks, and secrets cannot be written directly into JSON.
228
+
229
+ ## Update and cleanup safety
230
+
231
+ Save flow: write a new random system entry → verify readability → atomically replace JSON → delete the old Node entry. If failure occurs before the configuration switch, the old configuration is retained and the newly created credentials are cleaned up where possible; cleanup failure reports only non-sensitive reference IDs. Old prototype entries are never deleted.
232
+
233
+ Configuration writes use an exclusive `.setup.lock`, and the original file is checked before writing and before switching. Avoid concurrently editing configuration with an editor while the wizard is saving; editors do not honor this lock. Power loss or force-kill may leave locks or orphaned entries, and storage and JSON are not cross-system atomic transactions. Only after confirming that no wizard is running should residual locks be handled; the program does not proactively scan or delete unknown entries.
234
+
235
+ ```sh
236
+ node dist/cli.js credentials clear
237
+ ```
238
+
239
+ Cleanup requires interactive confirmation; it first removes the current JSON reference, then attempts to delete its Node entry; environment variables remain unchanged. Configuration copies sharing the same old reference are also affected, and this is communicated before the operation. Deletion failure is clearly warned and is not shown as cleaned.
240
+
241
+ ## Format, sending, and exit codes
242
+
243
+ Supports titles, bold, italic, strikethrough, inline code, code blocks, quotes, lists, task lists, and monospaced tables. Telegram segments by UTF-16 length, Feishu by UTF-8 request budget, and emoji are never split across surrogate pairs. Tags spanning segments are completed, titles carry `(1/N)`, and footer information is retained on every segment. Links are displayed as "text (URL)", and plain-text fallback preserves the address. This is not a complete CommonMark engine; complex escaping or nesting may not render exactly as written.
244
+
245
+ If one recipient fails midway, its subsequent segments stop, while Telegram continues sending to other recipients. HTML/card format errors fall back; when Telegram explicitly returns `429`, limited retries follow the server's requirements. After a timeout, it does not blindly resend, and exactly-once delivery cannot be promised.
246
+
247
+ - `send` uploads the specified content to third parties; check recipients and sensitive information.
248
+ - `--dry-run` prints no body text or credentials and does not use the network.
249
+ - `doctor` / `status` may read local credentials and do not verify network access or actual authorization.
250
+ - Missing files, corrupted configuration, or UTF-8 errors fail immediately and do not fall back to sending only the title.
251
+
252
+ | Code | Meaning |
253
+ | --- | --- |
254
+ | 0 | All selected configured channels sent successfully, or a configuration/read-only operation succeeded |
255
+ | 1 | Partial or total delivery failure; or doctor/status found no usable channel |
256
+ | 2 | Argument, configuration, file, or credential operation error |
257
+ | 130 | Canceled by the user |
258
+
259
+ If the test notification fails after the wizard saves successfully, the command still ends as a successful save and clearly shows the test failure; the two are clearly distinguished. Canceling hook configuration after saving still returns 0 and keeps channels; a hook configuration error returns 1 and clearly states that channels were saved. Integration `status` returns 1 when entries are missing, duplicated, or hooks are disabled in the current user-level file. Internal hook handling commands always exit without blocking, with failures reported via stderr.
260
+
261
+ ## Development and verification
262
+
263
+ ```sh
264
+ npm ci --ignore-scripts
265
+ npm test
266
+ npm pack --dry-run
267
+ ```
268
+
269
+ `node-src/` is TypeScript source, `dist/` is compiled output, and `test-js/` is tests. Default tests use only fake credentials and mock backends/network; the local native fake-entry round-trip test requires explicitly setting `DEV_NOTIFY_NATIVE_TEST=1`, is enabled only on Windows, uses random IDs, and cleans up afterward.
270
+
271
+ The npm publish whitelist contains only `dist/*.js`, README, SECURITY, CHANGELOG, LICENSE, and the required package.json; tests, configuration, and ciphertext are not included. There is no postinstall, auto-start, or self-written download script; prebuilt native dependencies are resolved by npm's platform optional packages, **do not use `--omit=optional`**. macOS x64/arm64 and Windows x64/arm64 prebuilt packages are provided by dependencies; providing packages does not mean this project has completed real-machine acceptance.
272
+
273
+ The local verification environment is Windows x64 with Node 24. Automated regression has 163 items: 162 passed, 0 failed, and 1 native credential test requiring explicit enablement was skipped. Coverage includes temporary hooks, reply/authorization/expiry/anti-replay, single-select/multi-select/custom, one-time permission JSON, two-sided races, feedback exceptions, and per-recipient duplicate reminder suppression. The actual CLI subprocess completes the loop of background start → PreToolUse registration → PermissionRequest → fake bot button → JSON answer → simulated PostToolUse → final feedback → notification deduplication/subsequent tool reminder → stop; this loop uses mock network and process metadata and cannot replace real-machine input verification. Separately, the user completed real-machine acceptance on Windows native Claude Code 2.1.316 with real Telegram for local-first, remote-first, multi-select, custom input, competing different answers, and delayed general reminder deduplication. Isolated compilation and tests do not cover the `dist` referenced by real hooks; automated tests for the Windows native input helper class only verify compilation/layout and do not perform a real Send. macOS's two terminal adapters have only simulation verification; macOS real-machine testing, the minimum Node version, version manager switching, tool permission answering on a real machine, and a full public release audit have not yet been accepted.
274
+
275
+ Licensed under MIT, see [LICENSE](LICENSE). Security boundaries are in [SECURITY.en.md](SECURITY.en.md). Version updates must be published separately to npm; pushing to GitHub does not automatically update the npm package.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # dev-notify
2
2
 
3
+ 简体中文 | [English](README.en.md)
4
+
3
5
  跨平台开发任务通知工具,使用 **TypeScript / Node.js**,通过 npm 分发,不需要独立可执行文件或其他脚本运行时。通用通知不依赖 Claude Code、Unity 或项目目录。
4
6
 
5
7
  **npm 包名为 `@quecue/dev-notify`。`0.1.3` 提供 Claude Code 回复、原生 PC 与 Telegram 双端答题及结果反馈。** 版本变更与升级说明见 [CHANGELOG.md](CHANGELOG.md);公开版本以 npm registry 为准。
@@ -113,7 +115,7 @@ dev-notify integration claude-code remove
113
115
  - **合并与移除**:保留权限、环境变量和其他 hooks;重复安装不会累加。同一用户级设置中的本工具 hooks 会在确认后更新到当前 Node、CLI 和通知配置路径。只移除带本工具标记及匹配参数结构的处理器,不删除设置文件或凭据。
114
116
  - **与旧工具共存**:旧版、项目级或插件 hooks 不会被自动删除,可能造成重复通知。安装时会提示当前用户级文件中的其他处理器;旧集成需由用户自行确认停用。
115
117
  - **事件与时序**:安装 Stop、Notification、SessionStart、UserPromptSubmit、SessionEnd、PreToolUse、PermissionRequest、PostToolUse、PostToolUseFailure、PermissionDenied,共 10 类。Notification 异步;其他事件同步登记状态。PreToolUse 只登记调用身份,不发送通知、不等待 Telegram,超时上限 10 秒;PermissionRequest 才等待远程答案。Stop 包含通知发送,可能延迟本地返回;通知/生命周期上限 120 秒。
116
- - **交互与失败**:PermissionRequest hook 上限 360 秒,远程答题窗口 5 分钟;普通交互终端保留原生窗口,由 Claude 仲裁先到的完整答案。hook 保持同步,不会改成 async,也不会模拟按键。没有回答、撤销授权、超时或发送失败时,都不会默认选择。内部错误会把安全摘要输出到 stderr,并以 0 退出;这里不使用会让 Stop 继续的退出码 2。当前已核对 Claude Code 2.1.316 的并行实现;其他版本、后台子任务及特殊宿主需要另行验收,不能据此推断所有宿主都并行。
118
+ - **交互与失败**:PermissionRequest hook 上限 720 秒,远程答题窗口 10 分钟;普通交互终端保留原生窗口,由 Claude 仲裁先到的完整答案。hook 保持同步,不会改成 async,也不会模拟按键。没有回答、撤销授权、超时或发送失败时,都不会默认选择。内部错误会把安全摘要输出到 stderr,并以 0 退出;这里不使用会让 Stop 继续的退出码 2。当前已核对 Claude Code 2.1.316 的并行实现;其他版本、后台子任务及特殊宿主需要另行验收,不能据此推断所有宿主都并行。
117
119
  - **输入边界**:hook stdin 最多 8 MiB,接收超时 15 秒;超限拒绝而非截断。正常长正文沿用双渠道分段。`stop_hook_active: true` 的继续轮次不发送完成通知。
118
120
  - **加载限制**:安装后在 `/hooks` 检查并按界面确认,必要时重启会话。不会解除 `disableAllHooks`,也不会绕过托管策略。`status` 只检查该用户级文件,不检查运行时加载状态或网络连通性。
119
121
  - **环境和路径**:异步进程使用 Claude Code 的环境,可能与手动终端不同;环境变量优先规则不变。切换 Node 版本或移动源码/包目录后,应重新运行 `install` 更新固定路径。
@@ -150,7 +152,7 @@ dev-notify reply status
150
152
  - **本地按钮不会被完整复刻**:官方明确 `permission_suggestions` 并非本地权限 UI 的完整选项列表,因此这里不会伪造「始终允许」等按钮。MCP Elicitation 表单/URL 等目前只有等待提醒,需要回到本机处理。
151
153
  - 本机原生界面始终保留。Telegram 提供「关闭远程答题(本机仍可操作)」入口;远程超时或关闭时不会默认作答。Telegram 是选项点击入口,飞书使用跳转链接,不提供飞书回调服务。
152
154
  - **两阶段反馈**:每次答案提交先报告「已登记,等待确认」;多题必须全部完成才回传。随后以 PostToolUse 的实际答案反馈「答案内容已生效」或「本次提交未生效」。两端同时提交相同答案时,只会确认内容一致,不会声称 Telegram 一定生效。权限允许以工具完成作保守确认;拒绝、取消或工具执行失败无法证明确实采用的远程决定时,会明确报告结果未知。
153
- - **失效与异常**:本机先答后结束远端请求,桥接会尝试把旧选项替换为「已处理/已过期」状态按钮,不再接受答案;编辑失败时也会拒绝旧回调。回传后最多等待 5 分钟确认,缺少结果时反馈「暂时无法确认」,不会重放答案。反馈依赖桥接在线;网络异常、进程被强杀或撤销授权后,反馈可能无法送达。本工具不会为了补发通知而重新授权,也不会无限重发。最终反馈发送前会登记状态,发送失败记为投递未知,防止崩溃后重复发送;若只收到等待提示而没有最终反馈,请检查终端及 reply status。
155
+ - **失效与异常**:本机先答、超时或会话结束时,桥接会尝试把旧选项替换为「已处理/已超时/已失效」状态按钮,明确是否有人作答,不再接受答案;编辑失败时也会拒绝旧回调。回传后最多等待 10 分钟确认,缺少结果时反馈「暂时无法确认」,不会重放答案。反馈依赖桥接在线;网络异常、进程被强杀或撤销授权后,反馈可能无法送达。本工具不会为了补发通知而重新授权,也不会无限重发。最终反馈发送前会登记状态,发送失败记为投递未知,防止崩溃后重复发送;若只收到等待提示而没有最终反馈,请检查终端及 reply status。
154
156
  - **防误投**:PreToolUse 保存 tool_use_id 与输入摘要,PermissionRequest 仅绑定唯一匹配调用;完成事件按 tool_use_id 结束请求。缺关联、同一 prompt 内重复同内容导致歧义、字段被其他 hook 改写或调用已结束时拒绝远程答题,保留本机处理,不猜测目标。
155
157
 
156
158
  ### 平台与后台服务
@@ -198,11 +200,16 @@ Node 版使用 **Windows Credential Manager / macOS Keychain**,通过原生 No
198
200
  ```json
199
201
  {
200
202
  "version": 1,
203
+ "language": "zh",
201
204
  "telegram": {"enabled": true, "chat_ids": [], "token_env": "TELEGRAM_BOT_TOKEN"},
202
205
  "feishu": {"enabled": true, "webhook_env": "FEISHU_WEBHOOK", "secret_env": "FEISHU_SECRET"}
203
206
  }
204
207
  ```
205
208
 
209
+ ### 界面语言
210
+
211
+ `--lang zh|en` 可临时切换单次命令的界面语言,`DEV_NOTIFY_LANG=zh|en` 可为当前环境设置语言;持久设置保存在配置的 `language` 字段(`zh` 或 `en`)。优先级为 `--lang` > `DEV_NOTIFY_LANG` > 配置 > 默认 `zh`。运行 `dev-notify language` 查看当前生效语言,`dev-notify language zh|en` 保存切换。CLI 输出、错误提示、通知内容、交互按钮和回复桥接消息都会使用所选语言;`setup` 创建新配置时会保存当前语言。
212
+
206
213
  ### 凭据与接收人
207
214
 
208
215
  - `TELEGRAM_BOT_TOKEN`:Telegram Bot Token。
package/SECURITY.en.md ADDED
@@ -0,0 +1,78 @@
1
+ # Security Boundaries (Node/npm)
2
+
3
+ English | [简体中文](SECURITY.md)
4
+
5
+ The current source is `0.1.3` and the npm package name is `@quecue/dev-notify`; the published version is authoritative on the npm registry, and source updates are not equivalent to a release. This project has not completed a security audit or real-machine acceptance on both platforms.
6
+
7
+ ## Credentials and permissions
8
+
9
+ - Node system credentials use the pinned `@napi-rs/keyring`: Windows Credential Manager / macOS Keychain. Secrets are passed through in-process native interfaces and are not exposed via the shell, subprocess arguments, or plaintext on disk.
10
+ - The Node service is `dev-notify.node.credentials.v1`, and each credential uses a random ID. JSON stores only a `keyring` reference and does not enumerate other system entries.
11
+ - Environment variables take precedence, and explicitly empty values also override. Neither environment variables nor system credentials can defend against a compromised same-user process; secrets enter Node memory, and it cannot be guaranteed that all copies are securely zeroed.
12
+ - Keychain may request system authorization; when storage is locked, permissions are insufficient, or native library loading fails, it reports an explicit error and does not automatically fall back to plaintext. When prebuilt packages are provided, the macOS permission flow still requires separate acceptance.
13
+ - The old prototype's `system` references are isolated from Node storage; Node does not read or delete old credentials, and Node references regenerated after re-entering secrets do not need to be handed back to the old prototype for editing.
14
+ - Interactive secrets use raw mode hidden input; exceptions, Ctrl+C, and EOF all restore the terminal state; when input cannot be hidden, the wizard refuses to continue and does not fall back to plaintext echo. Native reads may still trigger system dialogs.
15
+
16
+ ## Configuration transactions and cleanup
17
+
18
+ - Configuration and credentials are not written before final confirmation. After confirmation, a new random entry is created and verified, then JSON is switched, and the old Node entry is deleted on success.
19
+ - If the configuration switch fails, the original configuration is retained; only entries created in this attempt are cleaned up, and cleanup failures output only non-sensitive IDs.
20
+ - An exclusive `.setup.lock` and content snapshots prevent multiple wizards from overwriting each other; external editors do not honor this lock, so do not manually edit during a save.
21
+ - Storage and JSON are not cross-system atomic transactions. Force-kill or power loss may leave locks, temporary files, or orphaned credentials; the program does not proactively scan or delete unknown data.
22
+ - `credentials clear` requires interactive confirmation, removes the current reference, and deletes its Node entry; configuration copies sharing the reference are affected, and this is communicated before the operation. Environment variables are not cleared, and deletion failure is explicitly warned.
23
+ - POSIX mode cannot replace Windows ACLs; non-sensitive configuration may still expose recipients and local paths.
24
+
25
+ ## Network and application boundaries
26
+
27
+ - Non-dry-run `send`, the fixed test message **explicitly shown** in the wizard summary confirmation, and Stop / specified Notification events received **after installing the Claude Code integration as part of the summary confirmation** will send notifications.
28
+ - Wizard tests use the final configuration after environment overrides; save success and test failure are reported separately and are not shown as fully successful.
29
+ - HTTP enables system TLS verification, disallows redirects, uses a 10-second timeout, and caps response size at 1 MiB; underlying URLs, secrets, and raw server response bodies are not printed.
30
+ - A network timeout may occur when the server has accepted the request but the client does not know, so exactly-once delivery cannot be promised; to avoid failure, it also does not retry indefinitely.
31
+ - Recipient environment variables fully replace the old list and do not implicitly retain old authorization.
32
+ - npm installation does not modify application settings or start services. Notification integration and remote replies are two independent layers of authorization; installing hooks alone does not grant input rights. Without authorization, only final replies, specified waiting notifications, and auxiliary metadata are processed.
33
+ - After replies are enabled, trusted Telegram private-chat users can submit local Claude input, including Claude commands, so there is a real control risk. It is not an arbitrary shell executor and should not be described as read-only. The bridge does not open a public listener and does not scan transcripts, project content, or version control information; it only reads local Claude session process metadata to locate targets.
34
+ - `doctor`, `status`, `paths`, and dry-run do not use the network; local credential reads may trigger system authorization.
35
+
36
+ ## Claude Code integration
37
+
38
+ - `setup` includes notification hooks by default in the same summary confirmation; before confirmation it shows the sending scope, target configuration, and user-level settings path. `--no-hooks` saves only the notification configuration. It writes to `~/.claude/settings.json` by default and respects an absolute-path `CLAUDE_CONFIG_DIR`. Once enabled, it applies to all projects under that configuration directory; final replies may contain secrets or business-sensitive content, and the program does not automatically redact them. Recipients and sending scope must be confirmed by the user.
39
+ - Hooks directly use `last_assistant_message`, do not re-read the transcript, and do not actively upload thinking processes or tool results; when body text is missing, only an explicit completion notice is sent. The notification footer includes the absolute `cwd` path, session name, and session ID; the session name is read only from the local Claude Code session registry. Input is limited to 8 MiB with a 15-second read timeout; over-limit is rejected, not truncated. Notifications such as permission prompts may also contain sensitive content.
40
+ - Generates `command` + `args` in a no-shell-exec form, and paths are not expanded through a shell; a fixed installation uses absolute paths for Node/CLI/configuration, and command arguments contain no secrets. Requires a newer Claude Code supporting this form and `async`; after switching Node or moving the installation directory, reinstall hooks.
41
+ - Retains other settings and hooks, deduplicates updates on repeated installation, and removal matches only this tool's markers and argument structure. Old, project-level, or plugin hooks are not automatically deleted and may cause duplicate sending. `status` reflects only local state and does not involve global policy or actual event delivery acceptance; it does not lift disablement, lower permissions, or bypass managed restrictions.
42
+ - Writes use a dedicated exclusive lock, before/after snapshots, and atomic replacement with same-directory temporary files; corrupted JSON is rejected. External editors do not honor this lock, so unconditional race elimination is not claimed; force-kill may leave locks or temporary files, and the program does not clean up locks it did not create in the current attempt.
43
+ - Notification is asynchronous; Stop and session lifecycle events are synchronous, and state is registered before sending, which may delay the local return. PreToolUse only registers call identity, sends no network notifications, and waits for no answers; PermissionRequest waits in parallel with the native window of a normal interactive terminal, up to 10 minutes, and does not select by default. PostToolUse, PostToolUseFailure, and PermissionDenied are added to process results, for 10 event types in the complete installation. The implementation has been checked against Claude Code 2.1.316; older versions and special hosts may have different parallel paths.
44
+ - There is no persistent notification send queue, no automatic resend of entire messages, and no cross-process exactly-once guarantee. Continuing Stop and repeated idle reminders are ignored; on internal errors, only a safe summary is output and the process exits 0, without using exit code 2 to let Claude continue. Only explicitly authorized structured interactions return the corresponding official control JSON.
45
+ - Removing hooks, clearing notification credentials, and uninstalling npm are three different operations. Before uninstalling, remove hooks separately; the uninstall script will not modify user settings on its own.
46
+
47
+ ## Replies, interactions, and local state
48
+
49
+ - `reply setup` separately selects an explicit private-chat chat_id, which must be in the current notification list; group chats, non-private chats, senders whose chat_id does not match, bot messages, and unauthorized notification recipients do not gain input rights. Feishu provides only a Telegram deep link; knowing the link does not constitute authorization. `/chatid` only returns the sender's own private-chat ID and does not grant permission.
50
+ - Authorization is bound to the bot fingerprint, random generation, current valid user, and optional permission capability. Switching the token, revoking users, or re-authorizing does not inherit old routing; authorization is re-read before operations and before submission. Revocation cannot withdraw native input that has already started, nor eliminate every race between checking and calling.
51
+ - Every authorization read checks the complete hooks bound to the current notification configuration; when lifecycle events are missing, Stop is asynchronous, or user-level hooks are disabled, replies are refused. Normal input also requires actually observing the session's UserPromptSubmit. User-level file checks cannot prove project/managed policy or that a running session has loaded the hooks; confirm in `/hooks`. No promise is made to eliminate all runtime races.
52
+ - After each segment is sent successfully, the message ID is saved to the session mapping; mapping failure reports partial failure and does not resend already-sent segments. The session must match PID/birth identifier/TTY; submission is refused on session switch, closure, or a non-unique target. Supported scope is only the Windows native claude console and macOS foreground claude with Ghostty/tmux; no routing guesses are made for WSL/SSH/VS Code terminals.
53
+ - Normal replies are only queued while Claude is working and are attempted after Stop; they are canceled on 5-minute expiry, new local input, or a target change. A single reply is at most 4000 Unicode characters; control characters and newlines are stripped and the text is entered once; it is not shell-executed, not truncated, and not automatically split into multiple tasks. Input is refused when the console is shared and multiple claude processes exist; a local user editing or switching terminals at the same time may still race, and native input and UI activity are not atomically mutually exclusive.
54
+ - The update offset is persisted before processing; input is registered as pending first, and then the native adapter is called. Force-kill may lose messages, but submitted or unknown-result input is never replayed. Normal reply text is removed before submission; receipt is reported to the original private chat and to configured Feishu only when a matching UserPromptSubmit arrives, and the Feishu confirmation does not include the reply text. "Forwarded", "Received", and "Completed" are reported separately and are never conflated.
55
+ - AskUserQuestion uses the original questions + answers and returns them via PermissionRequest decision.updatedInput; the local native UI is retained; single-select, multi-select, and per-question custom input all bypass terminal injection. Claude arbitrates the first complete answer. Each button carries the request/question sequence; expired or old questions do not answer new questions; no default answer is selected on timeout or revocation.
56
+ - PreToolUse's tool_use_id and input digest are used for association; PermissionRequest has no such ID and accepts only a unique match, and ambiguity, repeated identical content, missing data, or an already-ended call causes remote handling to be refused. Completion events associate by tool_use_id, without guessing targets from notification order.
57
+ - General permission_prompt deduplication is only for notification delivery and grants no input or approval: only when the current session's last call is a question, no other unfinished calls exist, and the structured notification fully succeeded does it override per recipient; Feishu additionally binds to the digest of the current webhook/signature configuration. A completed question may override late reminders within the original interaction validity period; new input and subsequent tool calls do not inherit it. When Notification lacks a precise call identity, the conservative session scope is used for judgment, and precise deduplication across concurrency or turns is not claimed; state abnormalities, delivery failures, or unclear association keep the general reminder.
58
+ - Telegram's "Registered" feedback only means the answer has been recorded; effectiveness is confirmed later. The actual answers from PostToolUse are used for content confirmation; when both ends submit the same answer simultaneously, the source cannot be proven. For deny/failure, cancellation, a missing result, or a 10-minute confirmation timeout after returning, when the actual decision cannot be proven, the result is explicitly reported as unknown, and the answer is not replayed. Result receipts are registered before sending; network failure or force-kill may cause non-delivery; there are no unlimited retries and no exactly-once guarantee. After authorization is revoked, no further sends occur. Old options are replaced with processed/timed-out/invalid status; if editing fails, old requests are still rejected.
59
+ - After a question ends, request input and answer text are cleared, while message IDs, result receipts, and input digests are retained briefly; when no result is received, they are retained until the confirmation deadline, and actual cleanup is triggered by the active bridge or lifecycle events.
60
+ - Tool permissions require an additional opt-in; each click outputs one PermissionRequest allow/deny. Custom text is used only as a denial message; no arbitrary text becomes an approval. It does not persistently write updatedPermissions or change bypass/permission mode; Claude's deny and managed policies still apply. permission_suggestions is not the complete UI list, so not all local buttons are fabricated. MCP forms/URLs and similar are not yet supported for remote answering.
61
+ - Questions, options, tool arguments, reply text, and answers may contain sensitive information; after enabling, questions and the corresponding requests are sent to authorized private chats and configured Feishu, with no automatic redaction. Queued text, request input, answers pending return, and session paths are persistently written to dedicated local state; 0600/0700 cannot replace Windows ACL and cannot defend against same-user processes. Logical TTLs do not automatically erase files after power loss.
62
+ - The dedicated files are `<config>.reply.json`, `<config>.reply-data/`, and the data directory `reply-bots/`, already added to ignore rules and not in the npm whitelist. Disabling does not delete historical state on its own; to clear it, disable and stop first, then confirm deletion of the dedicated files. The bot token exists only in system credentials, environment variables, or memory and is not written to reply state.
63
+ - The bridge does not listen on ports or configure system auto-start. After user authorization, it can be started manually or on demand by hooks; it exits after 24 hours without updates, and also after 5 consecutive polling failures or a 409 conflict. It does not delete webhooks or force-kill old or other instances; cross-machine, cross-implementation bot exclusivity must be ensured by the user. This tool's lock only constrains instances that follow the same local lock directory.
64
+ - Stop sends a control request using a random instance identifier and does not kill processes based on an old PID; stop does not revoke authorization, and later hooks may restart the bridge. Use disable for a complete shutdown; native calls cannot be withdrawn once started. Heartbeat/PID are only status snapshots; network connectivity needs separate verification; crash-residual locks require confirming that the process has exited before manual handling, and the program does not automatically seize locks.
65
+
66
+ ## npm and testing
67
+
68
+ - Runtime has a native credential dependency, with the version and platform optional packages pinned in `package-lock.json`. Keep optional dependencies. There is no postinstall, self-written binary download, or background startup script.
69
+ - The npm whitelist contains only compiled JS, README, SECURITY, CHANGELOG, LICENSE, and package.json. Tests, configuration, logs, and ciphertext do not enter this package.
70
+ - Third-party native packages carry their own licenses and Rust dependency declarations; dependency supply chain and licenses still need review before public release; pinned versions are not equivalent to a completed audit.
71
+ - Default tests use fake credentials, temporary directories, and mock system backends/network. `DEV_NOTIFY_NATIVE_TEST=1` explicitly enables Windows native tests, creates only fake entries with random IDs for this run, cleans up afterward, and does not read the user's existing entries.
72
+ - npm uninstall does not automatically delete user configuration or credentials. To clean up, first run the interactive cleanup command; uninstall lifecycle scripts will not secretly manipulate the user's system storage.
73
+
74
+ ## Public release
75
+
76
+ Verify the package name, source ownership, and dependency licenses; scan the full directory, the actual npm package, and Git history. Once a key leaks, it must be revoked and rotated; deleting files cannot undo a leak that has already occurred. A private vulnerability reporting channel should be set up before a public release; there is currently no public security contact address.
77
+
78
+ Windows x64 / Node 24 has had isolated tests and a real CLI fake-network loop; the Windows native input helper class in automation only does compilation/layout verification and does not inject into real Claude sessions. The user separately completed real-machine acceptance on Claude Code 2.1.316 with real Telegram for PC/remote priority, single-select/multi-select/custom, competing different answers, and duplicate reminder deduplication; that question-answering path does not involve terminal input injection. Tool permission answering on a real machine, macOS terminal input and the system permission flow, other architectures, and the minimum Node version have not been verified, so comprehensive compatibility is not claimed.
package/SECURITY.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # 安全边界(Node/npm)
2
2
 
3
+ 简体中文 | [English](SECURITY.en.md)
4
+
3
5
  当前源码为 `0.1.3`,npm 包名为 `@quecue/dev-notify`;发布版本以 npm registry 为准,源码更新不等同于已发布。本项目尚未完成安全审计或双平台实机验收。
4
6
 
5
7
  ## 凭据与权限
@@ -38,7 +40,7 @@
38
40
  - 生成 `command` + `args` 的无 shell exec 形式,路径不经过 shell 展开;固定安装时使用 Node/CLI/配置的绝对路径,命令参数不含秘密。需要支持该形式与 `async` 的新版 Claude Code;切换 Node 或移动安装目录后,请重新安装 hooks。
39
41
  - 保留其他设置和 hooks,重复安装会去重更新,移除时只匹配本工具标记及参数结构。旧版、项目或插件 hooks 不会被自动删除,可能造成重复发送。`status` 只反映本地状态,不涉及全局策略或实际事件投递验收;不会解除禁用、降低权限或绕过托管限制。
40
42
  - 写入使用专用排他锁、前后快照及同目录临时文件原子替换;损坏的 JSON 会被拒绝。外部编辑器不遵守这个锁,因此不会宣称无条件消除竞态;强杀可能留下锁或临时文件,程序不会清理不属于本次创建的锁。
41
- - Notification 异步,Stop 与会话生命周期同步,状态登记后再发送,可能延迟本地返回。PreToolUse 只登记调用身份,不发送网络通知,也不等待答案;PermissionRequest 与普通交互终端原生窗口并行等待,最多 5 分钟,不会默认选择。新增 PostToolUse、PostToolUseFailure、PermissionDenied 处理结果,完整安装共 10 类事件。当前核对的是 Claude Code 2.1.316 实现;旧版及特殊宿主的并行路径可能不同。
43
+ - Notification 异步,Stop 与会话生命周期同步,状态登记后再发送,可能延迟本地返回。PreToolUse 只登记调用身份,不发送网络通知,也不等待答案;PermissionRequest 与普通交互终端原生窗口并行等待,最多 10 分钟,不会默认选择。新增 PostToolUse、PostToolUseFailure、PermissionDenied 处理结果,完整安装共 10 类事件。当前核对的是 Claude Code 2.1.316 实现;旧版及特殊宿主的并行路径可能不同。
42
44
  - 没有持久通知发送队列,不会自动重发整条消息,也不提供跨进程的恰好一次保证。继续中的 Stop 和重复空闲提醒会被忽略;内部出错时只输出安全摘要并退出 0,不会使用退出码 2 让 Claude 继续。只有明确授权的结构化交互才会返回对应的官方控制 JSON。
43
45
  - 移除 hooks、清理通知凭据和卸载 npm 是三项不同操作。卸载前请先单独移除 hooks;卸载脚本不会擅自修改用户设置。
44
46
 
@@ -53,7 +55,7 @@
53
55
  - AskUserQuestion 使用原始 questions + answers,通过 PermissionRequest decision.updatedInput 回传;保留本机原生界面,单选、多选、逐题自定义都不经过终端注入。由 Claude 仲裁先到的完整答案。每个按钮带请求/题目序号,过期或旧题不会回答新题;超时或撤销时不会默认作答。
54
56
  - PreToolUse 的 tool_use_id 与输入摘要用于关联;PermissionRequest 没有该 ID,只接受唯一匹配,遇到歧义、重复同内容、缺失或已结束时会拒绝远程处理。完成事件按 tool_use_id 关联,不会依靠通知顺序猜测目标。
55
57
  - 通用 permission_prompt 去重只用于通知投递,不授予输入或批准权限:仅当前会话最后一项调用是选择题、没有其他未结束调用、结构化通知完整成功时,才会按接收人覆盖;飞书另绑定当前 webhook/签名配置的摘要。已结束题目可在原交互有效期内覆盖迟到提醒,新输入及后续工具调用不会继承。Notification 缺少精确调用身份时,以该保守会话范围判断,不会声称跨并发或跨轮次精确去重;状态异常、投递失败或关联不明确时,会保留通用提醒。
56
- - Telegram 的「已登记」反馈只说明答案已经记录,生效情况会在后续确认。PostToolUse 返回的实际答案用于内容确认;两端同时提交相同答案时,无法证明来源。拒绝/失败、取消、缺少结果或回传后 5 分钟确认超时,无法证明实际决定时,会明确报告结果未知,不会重放答案。结果收据在发送前登记,网络失败或强杀可能导致未送达;不会无限重发,也不提供恰好一次保证。撤销授权后不再发送。旧选项会替换为已处理/过期状态;编辑失败时仍会拒绝旧请求。
58
+ - Telegram 的「已登记」反馈只说明答案已经记录,生效情况会在后续确认。PostToolUse 返回的实际答案用于内容确认;两端同时提交相同答案时,无法证明来源。拒绝/失败、取消、缺少结果或回传后 10 分钟确认超时,无法证明实际决定时,会明确报告结果未知,不会重放答案。结果收据在发送前登记,网络失败或强杀可能导致未送达;不会无限重发,也不提供恰好一次保证。撤销授权后不再发送。旧选项会替换为已处理/已超时/已失效状态;编辑失败时仍会拒绝旧请求。
57
59
  - 问题结束后清除请求输入和答案正文,短期保留消息 ID、结果收据及输入摘要;没有收到结果时,保留到确认期限,实际清理由活跃桥接或生命周期事件触发。
58
60
  - 工具权限必须额外 opt-in;每次点击才输出 PermissionRequest 的一次 allow/deny。自定义文本只作为拒绝 message,任意文本都不会变成批准。不会持久写入 updatedPermissions,也不会更改 bypass/权限模式;Claude 的 deny 和托管策略仍然生效。permission_suggestions 不是完整的 UI 列表,因此不会伪造所有本地按钮。MCP 表单/URL 等暂不支持远程应答。
59
61
  - 问题、选项、工具参数、回复正文和答案可能包含敏感信息;启用后,问题和相应请求会发送到授权私聊及已配置飞书,无法自动脱敏。排队文本、请求输入、待回传答案及会话路径会持久写入专属本地状态;0600/0700 无法替代 Windows ACL,也无法防御同一用户的进程。逻辑 TTL 不会在断电后自动擦除文件。