claude-notification-plugin 1.1.13 → 1.1.15

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-notification-plugin",
3
- "version": "1.0.122",
3
+ "version": "1.1.15",
4
4
  "description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
5
5
  "author": {
6
6
  "name": "Viacheslav Makarov",
package/README.md CHANGED
@@ -195,7 +195,7 @@ Alternatively, add a `listener` section to config manually:
195
195
  }
196
196
  ```
197
197
 
198
- The `"default"` alias receives messages without a `@project` prefix.
198
+ The `"default"` alias receives messages without a `/project` prefix.
199
199
  `api` and `web` are project aliases for easy reference from Telegram.
200
200
 
201
201
  ### 2. Start the listener
@@ -208,16 +208,16 @@ claude-notify listener start
208
208
 
209
209
  ```
210
210
  fix the login bug → runs in "default" project
211
- @api add pagination to GET /users → runs in "api" project
212
- @api/feature/auth implement OAuth2 → runs in a worktree (auto-created)
211
+ /api add pagination to GET /users → runs in "api" project
212
+ /api/feature/auth implement OAuth2 → runs in a worktree (auto-created)
213
213
  ```
214
214
 
215
215
  The bot replies with status and results:
216
216
 
217
217
  ```
218
- ⏳ [@api] Running: add pagination to GET /users
218
+ ⏳ [/api] Running: add pagination to GET /users
219
219
  ...
220
- ✅ [@api] Done: add pagination to GET /users
220
+ ✅ [/api] Done: add pagination to GET /users
221
221
  <claude's output>
222
222
  ```
223
223
 
@@ -238,15 +238,15 @@ All commands start with `/` and execute instantly (not queued).
238
238
  | Command | Description |
239
239
  |-------------------------------|--------------------------------------|
240
240
  | `/status` | Status of all projects and worktrees |
241
- | `/status @project` | Status of a specific project |
241
+ | `/status /project` | Status of a specific project |
242
242
  | `/queue` | Show all queues |
243
- | `/cancel @project[/branch]` | Cancel the active task |
244
- | `/drop @project N` | Remove task N from queue |
245
- | `/clear @project[/branch]` | Clear queue |
243
+ | `/cancel /project[/branch]` | Cancel the active task |
244
+ | `/drop /project N` | Remove task N from queue |
245
+ | `/clear /project[/branch]` | Clear queue |
246
246
  | `/projects` | List projects and paths |
247
- | `/worktrees @project` | List worktrees |
248
- | `/worktree @project branch` | Create a worktree |
249
- | `/rmworktree @project branch` | Remove a worktree |
247
+ | `/worktrees /project` | List worktrees |
248
+ | `/worktree /project/branch` | Create a worktree |
249
+ | `/rmworktree /project/branch` | Remove a worktree |
250
250
  | `/history` | Recent task history |
251
251
  | `/stop` | Stop the listener |
252
252
  | `/help` | Show help |
@@ -267,15 +267,15 @@ All commands start with `/` and execute instantly (not queued).
267
267
  ### Projects and worktrees
268
268
 
269
269
  **The queue is tied to the working directory, not the project name:**
270
- - `@api task` and `@api/feature/auth task` → **different queues** (parallel)
271
- - `@api task1` and `@api task2` → **same queue** (sequential)
270
+ - `/api task` and `/api/feature/auth task` → **different queues** (parallel)
271
+ - `/api task1` and `/api task2` → **same queue** (sequential)
272
272
 
273
- Worktrees are auto-created when you use `@project/branch` syntax (controlled by `autoCreateWorktree`).
273
+ Worktrees are auto-created when you use `/project/branch` syntax (controlled by `autoCreateWorktree`).
274
274
 
275
275
  ```
276
- /worktree @api feature/payments ← create
277
- /worktrees @api ← list
278
- /rmworktree @api feature/payments ← remove
276
+ /worktree /api/feature/payments ← create
277
+ /worktrees /api ← list
278
+ /rmworktree /api/feature/payments ← remove
279
279
  ```
280
280
 
281
281
 
package/commit-sha CHANGED
@@ -1 +1 @@
1
- 03f882a91dd04b8467a10dc0c80af8040eaa54cd
1
+ b092cf465b465f338c3e5ab0e073a8c91aac4d51
@@ -60,7 +60,7 @@ GET /getUpdates?timeout=30 ────► "Let's wait up to 30 seconds,
60
60
  connection is open)
61
61
 
62
62
  After 15 sec a user
63
- wrote "@proj1 fix bug"
63
+ wrote "/proj1 fix bug"
64
64
 
65
65
  ◄──── {"result": [{"message":...}]} "There's a message, here it is!"
66
66
 
@@ -180,7 +180,7 @@ Running two listeners is impossible — the PID file prevents it. And this is im
180
180
  │ ┌───────┴────────┐ ┌───────┴───────┐ │
181
181
  │ │ MessageParser │ │ TaskRunner │ │
182
182
  │ │ │ │ │ │
183
- │ │ @proj/branch │ │ spawn claude │ │
183
+ │ │ /proj/branch │ │ spawn claude │ │
184
184
  │ │ /commands │ │ timeouts │ │
185
185
  │ └────────────────┘ │ kill │ │
186
186
  │ └───────────────┘ │
@@ -197,10 +197,10 @@ Running two listeners is impossible — the PID file prevents it. And this is im
197
197
  | Module | File | Description |
198
198
  |---|---|---|
199
199
  | **TelegramPoller** | `telegram-poller.js` | Long polling to the Telegram API. Receives messages, sends replies. Splits long messages into chunks |
200
- | **MessageParser** | `message-parser.js` | Parses message text: is it a command (`/status`) or a task (`@proj1 fix bug`)? Extracts project, branch, task text |
200
+ | **MessageParser** | `message-parser.js` | Parses message text: is it a command (`/status`) or a task (`/proj1 fix bug`)? Extracts project, branch, task text |
201
201
  | **WorkQueue** | `work-queue.js` | Manages task queues. Each working directory has a separate FIFO queue. Guarantees: one `claude` process per directory. Persists state to disk |
202
202
  | **TaskRunner** | `task-runner.js` | Runs `claude -p "task"` as a child process. Monitors timeouts. Can kill the process on cancellation. Emits events: complete, error, timeout |
203
- | **WorktreeManager** | `worktree-manager.js` | Creates and removes git worktrees. Auto-discovery via `git worktree list`. Maps `@project/branch` to a path on disk |
203
+ | **WorktreeManager** | `worktree-manager.js` | Creates and removes git worktrees. Auto-discovery via `git worktree list`. Maps `/project/branch` to a path on disk |
204
204
  | **Logger** | `logger.js` | Writes operational log to `~/.claude/.cc-n-listener.log`. Rotation when exceeding 5 MB (old file → `.log.old`) |
205
205
  | **TaskLogger** | `task-logger.js` | Writes task Q&A logs (questions to Claude and answers). Separate file per project/branch. Rotation at 5 MB |
206
206
 
@@ -227,12 +227,12 @@ MessageParser.parse(text)
227
227
 
228
228
  └─ Otherwise ──► Task
229
229
 
230
- ├─ "@proj1/feature/auth fix bug"
230
+ ├─ "/proj1/feature/auth fix bug"
231
231
  │ → project = "proj1"
232
232
  │ → branch = "feature/auth"
233
233
  │ → text = "fix bug"
234
234
 
235
- ├─ "@proj1 fix bug"
235
+ ├─ "/proj1 fix bug"
236
236
  │ → project = "proj1"
237
237
  │ → branch = null (main)
238
238
  │ → text = "fix bug"
@@ -357,9 +357,9 @@ Each project is an alias (short name) + path to a directory on disk:
357
357
  }
358
358
  ```
359
359
 
360
- Now in Telegram you can write `@api refactor the code`, and Claude will run in the `/home/user/projects/api-server` directory.
360
+ Now in Telegram you can write `/api refactor the code`, and Claude will run in the `/home/user/projects/api-server` directory.
361
361
 
362
- The **`default`** alias is special. Messages without `@project` go to it:
362
+ The **`default`** alias is special. Messages without `/project` prefix go to it:
363
363
 
364
364
  ```json
365
365
  {
@@ -378,9 +378,9 @@ The **`default`** alias is special. Messages without `@project` go to it:
378
378
  In the Telegram chat with the bot:
379
379
 
380
380
  ```
381
- @project task ← task in the main worktree of the project
382
- @project/branch task ← task in the worktree of a specific branch
383
- task without @ ← task in the "default" project
381
+ /project task ← task in the main worktree of the project
382
+ /project/branch task ← task in the worktree of a specific branch
383
+ task without /project prefix ← task in the "default" project
384
384
  ```
385
385
 
386
386
  ### Examples
@@ -391,25 +391,25 @@ add a README to the project
391
391
  → runs in the `default` project (if configured)
392
392
 
393
393
  ```
394
- @api fix the authentication bug
394
+ /api fix the authentication bug
395
395
  ```
396
396
  → runs in `/home/user/projects/api-server`
397
397
 
398
398
  ```
399
- @api/feature/payments add Stripe integration
399
+ /api/feature/payments add Stripe integration
400
400
  ```
401
401
  → runs in the `feature/payments` worktree of the `api` project.
402
402
  If the worktree doesn't exist, it will be created automatically.
403
403
 
404
404
  ```
405
- @web update dependencies
405
+ /web update dependencies
406
406
  ```
407
407
  → runs in `/home/user/projects/web-app`
408
408
 
409
409
  ### What happens when a task is sent
410
410
 
411
411
  1. The Listener receives the message from Telegram
412
- 2. Parses `@project/branch` from the beginning of the message
412
+ 2. Parses `/project/branch` from the beginning of the message
413
413
  3. Determines the working directory (workDir)
414
414
  4. Checks: is this workDir busy with another task?
415
415
  - **No** → runs `claude -p "task"` immediately, replies with `⏳ Running...`
@@ -445,8 +445,8 @@ api-server/ ← main worktree, branch main
445
445
  **The queue is tied to the working directory, not to the project name.**
446
446
 
447
447
  This means:
448
- - `@api task` and `@api/feature/auth task` are **different queues**, because they're different directories. They run **in parallel**.
449
- - `@api task1` and `@api task2` are **the same queue** (both go to the main worktree). `task2` will wait for `task1` to complete.
448
+ - `/api task` and `/api/feature/auth task` are **different queues**, because they're different directories. They run **in parallel**.
449
+ - `/api task1` and `/api task2` are **the same queue** (both go to the main worktree). `task2` will wait for `task1` to complete.
450
450
 
451
451
  ```
452
452
  Project "api"
@@ -466,7 +466,7 @@ Within each — strictly one task at a time.
466
466
 
467
467
  ### Auto-creation of worktrees
468
468
 
469
- When you write `@api/feature/new task`, and a worktree for the `feature/new` branch doesn't exist:
469
+ When you write `/api/feature/new task`, and a worktree for the `feature/new` branch doesn't exist:
470
470
 
471
471
  1. The Listener checks: does the `feature/new` branch exist in git?
472
472
  - Yes → `git worktree add ~/.claude/worktrees/api/feature-new feature/new`
@@ -484,9 +484,9 @@ On startup, the listener scans each project with `git worktree list` and picks u
484
484
  ### Manual worktree management from Telegram
485
485
 
486
486
  ```
487
- /worktree @api feature/payments ← create a worktree
488
- /worktrees @api ← list all worktrees for a project
489
- /rmworktree @api feature/payments ← remove a worktree
487
+ /worktree /api/feature/payments ← create a worktree
488
+ /worktrees /api ← list all worktrees for a project
489
+ /rmworktree /api/feature/payments ← remove a worktree
490
490
  ```
491
491
 
492
492
  ---
@@ -504,41 +504,41 @@ While `active !== null`, all new tasks for this workDir go into the `queue`.
504
504
  ### Example: 4 tasks, 2 projects
505
505
 
506
506
  ```
507
- 10:00 You: @api fix the router bug
508
- Bot: ⏳ [@api] Running: fix the router bug
507
+ 10:00 You: /api fix the router bug
508
+ Bot: ⏳ [/api] Running: fix the router bug
509
509
  (api/main: active = "fix the router bug", queue = [])
510
510
 
511
- 10:01 You: @web update dependencies
512
- Bot: ⏳ [@web] Running: update dependencies
511
+ 10:01 You: /web update dependencies
512
+ Bot: ⏳ [/web] Running: update dependencies
513
513
  (web/main: active = "update dependencies", queue = [])
514
514
  (api and web are running in parallel!)
515
515
 
516
- 10:02 You: @api add tests
517
- Bot: 📋 [@api] Queued (position 1).
516
+ 10:02 You: /api add tests
517
+ Bot: 📋 [/api] Queued (position 1).
518
518
  Currently running: fix the router bug
519
519
  (api/main: active = "fix the router bug", queue = ["add tests"])
520
520
 
521
- 10:03 You: @api refactor the code
522
- Bot: 📋 [@api] Queued (position 2).
521
+ 10:03 You: /api refactor the code
522
+ Bot: 📋 [/api] Queued (position 2).
523
523
  Currently running: fix the router bug
524
524
  (api/main: active = "fix the router bug", queue = ["add tests", "refactor"])
525
525
 
526
- 10:05 Bot: ✅ [@web] Done: update dependencies
526
+ 10:05 Bot: ✅ [/web] Done: update dependencies
527
527
  <result>
528
528
  (web/main: active = null, queue = [])
529
529
 
530
- 10:08 Bot: ✅ [@api] Done: fix the router bug
530
+ 10:08 Bot: ✅ [/api] Done: fix the router bug
531
531
  <result>
532
- Bot: ⏳ [@api] Running: add tests
532
+ Bot: ⏳ [/api] Running: add tests
533
533
  (api/main: active = "add tests", queue = ["refactor"])
534
534
  (next task started automatically!)
535
535
 
536
- 10:15 Bot: ✅ [@api] Done: add tests
536
+ 10:15 Bot: ✅ [/api] Done: add tests
537
537
  <result>
538
- Bot: ⏳ [@api] Running: refactor the code
538
+ Bot: ⏳ [/api] Running: refactor the code
539
539
  (api/main: active = "refactor", queue = [])
540
540
 
541
- 10:25 Bot: ✅ [@api] Done: refactor the code
541
+ 10:25 Bot: ✅ [/api] Done: refactor the code
542
542
  <result>
543
543
  (api/main: active = null, queue = [])
544
544
  (all tasks completed)
@@ -555,7 +555,7 @@ While `active !== null`, all new tasks for this workDir go into the `queue`.
555
555
  If a task runs longer than 30 minutes (configurable: `taskTimeoutMinutes`), it is forcefully stopped:
556
556
 
557
557
  ```
558
- Bot: ⏰ [@api] Task forcefully stopped — timeout exceeded (30 min): refactor the code
558
+ Bot: ⏰ [/api] Task forcefully stopped — timeout exceeded (30 min): refactor the code
559
559
  ```
560
560
 
561
561
  After a timeout, the next task from the queue starts automatically.
@@ -581,7 +581,7 @@ Bot: 📊 Status:
581
581
  ```
582
582
 
583
583
  ```
584
- You: /status @api
584
+ You: /status /api
585
585
  Bot: 📊 Project "api":
586
586
 
587
587
  main:
@@ -598,7 +598,7 @@ Bot: 📊 Project "api":
598
598
  You: /queue
599
599
  Bot: 📋 Queues:
600
600
 
601
- @api:
601
+ /api:
602
602
  ▶ fix the router bug
603
603
  1. add tests
604
604
  2. refactor the code
@@ -607,16 +607,16 @@ Bot: 📋 Queues:
607
607
  ### /cancel — stop a running task
608
608
 
609
609
  ```
610
- You: /cancel @api
611
- Bot: 🛑 [@api] Task cancelled. Starting next.
612
- ⏳ [@api] Running: add tests
610
+ You: /cancel /api
611
+ Bot: 🛑 [/api] Task cancelled. Starting next.
612
+ ⏳ [/api] Running: add tests
613
613
  ```
614
614
 
615
615
  Cancelling a task in a worktree:
616
616
 
617
617
  ```
618
- You: /cancel @api/feature/auth
619
- Bot: 🛑 [@api/feature/auth] Task cancelled
618
+ You: /cancel /api/feature/auth
619
+ Bot: 🛑 [/api/feature/auth] Task cancelled
620
620
  ```
621
621
 
622
622
  ### /drop — remove from queue
@@ -624,7 +624,7 @@ Bot: 🛑 [@api/feature/auth] Task cancelled
624
624
  Removes a task that **hasn't started executing yet** (waiting in the queue):
625
625
 
626
626
  ```
627
- You: /drop @api 2
627
+ You: /drop /api 2
628
628
  Bot: 🗑 Removed from queue: refactor the code
629
629
  ```
630
630
 
@@ -635,8 +635,8 @@ The task number is the position in the queue (starting from 1). You can check nu
635
635
  Removes all tasks from the queue (the active task continues running):
636
636
 
637
637
  ```
638
- You: /clear @api
639
- Bot: 🧹 [@api] Queue cleared (3 tasks)
638
+ You: /clear /api
639
+ Bot: 🧹 [/api] Queue cleared (3 tasks)
640
640
  ```
641
641
 
642
642
  ### /projects — list projects
@@ -646,15 +646,15 @@ You: /projects
646
646
  Bot: 📂 Projects:
647
647
 
648
648
  @default → /home/user/main-project
649
- @api → /home/user/projects/api-server
649
+ /api → /home/user/projects/api-server
650
650
  /feature/auth → ~/.claude/worktrees/api/feature-auth
651
- @web → /home/user/projects/web-app
651
+ /web → /home/user/projects/web-app
652
652
  ```
653
653
 
654
654
  ### /worktrees — project worktrees
655
655
 
656
656
  ```
657
- You: /worktrees @api
657
+ You: /worktrees /api
658
658
  Bot: 🌳 Worktrees for project "api":
659
659
  • main → /home/user/projects/api-server
660
660
  • feature/auth → ~/.claude/worktrees/api/feature-auth
@@ -664,7 +664,7 @@ Bot: 🌳 Worktrees for project "api":
664
664
  ### /worktree — create a worktree
665
665
 
666
666
  ```
667
- You: /worktree @api feature/payments
667
+ You: /worktree /api/feature/payments
668
668
  Bot: 🌿 Created worktree for project "api":
669
669
  Branch: feature/payments
670
670
  Path: ~/.claude/worktrees/api/feature-payments
@@ -673,7 +673,7 @@ Bot: 🌿 Created worktree for project "api":
673
673
  ### /rmworktree — remove a worktree
674
674
 
675
675
  ```
676
- You: /rmworktree @api feature/payments
676
+ You: /rmworktree /api/feature/payments
677
677
  Bot: 🗑 Worktree feature/payments removed from project "api"
678
678
  ```
679
679
 
@@ -681,7 +681,7 @@ If a task is running in the worktree, removal will be rejected:
681
681
 
682
682
  ```
683
683
  Bot: ❌ Cannot remove worktree: a task is running in it.
684
- First /cancel @api/feature/payments
684
+ First /cancel /api/feature/payments
685
685
  ```
686
686
 
687
687
  ### /history — history
@@ -690,10 +690,10 @@ Bot: ❌ Cannot remove worktree: a task is running in it.
690
690
  You: /history
691
691
  Bot: 📜 Recent tasks:
692
692
 
693
- ✅ [@api] fix the router bug
694
- ✅ [@web] update dependencies
695
- 🛑 [@api/feature/auth] implement OAuth2
696
- ✅ [@api] add tests
693
+ ✅ [/api] fix the router bug
694
+ ✅ [/web] update dependencies
695
+ 🛑 [/api/feature/auth] implement OAuth2
696
+ ✅ [/api] add tests
697
697
  ```
698
698
 
699
699
  ### /stop — stop the listener
@@ -720,7 +720,7 @@ Shows a brief reference for all commands.
720
720
  Telegram message → getUpdates() → parsing
721
721
 
722
722
  2. ROUTING
723
- "@api/feature/auth task"
723
+ "/api/feature/auth task"
724
724
  → project = "api"
725
725
  → branch = "feature/auth"
726
726
  → workDir = ~/.claude/worktrees/api/feature-auth
@@ -896,7 +896,7 @@ Check:
896
896
  ### Task is stuck
897
897
 
898
898
  ```
899
- /cancel @project
899
+ /cancel /project
900
900
  ```
901
901
 
902
902
  Or restart the listener:
@@ -950,8 +950,8 @@ You (terminal): claude-notify listener start
950
950
 
951
951
  === 10:01 — First task ===
952
952
 
953
- You: @api add endpoint GET /users with pagination
954
- Bot: ⏳ [@api] Running: add endpoint GET /users with pagination
953
+ You: /api add endpoint GET /users with pagination
954
+ Bot: ⏳ [/api] Running: add endpoint GET /users with pagination
955
955
 
956
956
  Behind the scenes: process started
957
957
  claude -p "add endpoint GET /users with pagination" --output-format text
@@ -959,23 +959,23 @@ Bot: ⏳ [@api] Running: add endpoint GET /users with pagination
959
959
 
960
960
  === 10:02 — Task to another project (in parallel!) ===
961
961
 
962
- You: @web add a /users page that calls GET /users
963
- Bot: ⏳ [@web] Running: add a /users page that calls GET /users
962
+ You: /web add a /users page that calls GET /users
963
+ Bot: ⏳ [/web] Running: add a /users page that calls GET /users
964
964
 
965
965
  Now two claude processes are running in parallel:
966
966
  one in /home/user/projects/api, another in /home/user/projects/web
967
967
 
968
968
  === 10:03 — Another task for api (queued) ===
969
969
 
970
- You: @api add tests for /users
971
- Bot: 📋 [@api] Queued (position 1).
970
+ You: /api add tests for /users
971
+ Bot: 📋 [/api] Queued (position 1).
972
972
  Currently running: add endpoint GET /users with pagination
973
973
 
974
974
  === 10:04 — Task in a worktree (in parallel with api/main!) ===
975
975
 
976
- You: @api/feature/auth add JWT authorization middleware
976
+ You: /api/feature/auth add JWT authorization middleware
977
977
  Bot: 🌿 Created worktree feature/auth for project "api"
978
- ⏳ [@api/feature/auth] Running: add JWT authorization middleware
978
+ ⏳ [/api/feature/auth] Running: add JWT authorization middleware
979
979
 
980
980
  Three claude processes running in parallel:
981
981
  1. api/main → GET /users
@@ -996,7 +996,7 @@ Bot: 📊 Status:
996
996
 
997
997
  === 10:07 — web finished ===
998
998
 
999
- Bot: ✅ [@web] Done: add a /users page that calls GET /users
999
+ Bot: ✅ [/web] Done: add a /users page that calls GET /users
1000
1000
 
1001
1001
  Created file src/pages/Users.vue with a user table.
1002
1002
  Added route in src/router.js.
@@ -1004,24 +1004,24 @@ Bot: ✅ [@web] Done: add a /users page that calls GET /users
1004
1004
 
1005
1005
  === 10:09 — api/main finished, automatically starts the next task ===
1006
1006
 
1007
- Bot: ✅ [@api] Done: add endpoint GET /users with pagination
1007
+ Bot: ✅ [/api] Done: add endpoint GET /users with pagination
1008
1008
 
1009
1009
  Created controller src/controllers/users.js.
1010
1010
  Added route GET /users in src/routes.js.
1011
1011
  Supports query parameters: page, limit, sort.
1012
1012
 
1013
- Bot: ⏳ [@api] Running: add tests for /users
1013
+ Bot: ⏳ [/api] Running: add tests for /users
1014
1014
 
1015
1015
  Next task from the queue started automatically!
1016
1016
 
1017
1017
  === 10:12 — Cancel a worktree task ===
1018
1018
 
1019
- You: /cancel @api/feature/auth
1020
- Bot: 🛑 [@api/feature/auth] Task cancelled
1019
+ You: /cancel /api/feature/auth
1020
+ Bot: 🛑 [/api/feature/auth] Task cancelled
1021
1021
 
1022
1022
  === 10:15 — api/main (tests) finished ===
1023
1023
 
1024
- Bot: ✅ [@api] Done: add tests for /users
1024
+ Bot: ✅ [/api] Done: add tests for /users
1025
1025
 
1026
1026
  Created tests/users.test.js.
1027
1027
  Covered cases: pagination, sorting, empty result, errors.
@@ -1031,14 +1031,14 @@ Bot: ✅ [@api] Done: add tests for /users
1031
1031
  You: /history
1032
1032
  Bot: 📜 Recent tasks:
1033
1033
 
1034
- ✅ [@api] add tests for /users
1035
- 🛑 [@api/feature/auth] add JWT authorization middleware
1036
- ✅ [@api] add endpoint GET /users with pagination
1037
- ✅ [@web] add a /users page...
1034
+ ✅ [/api] add tests for /users
1035
+ 🛑 [/api/feature/auth] add JWT authorization middleware
1036
+ ✅ [/api] add endpoint GET /users with pagination
1037
+ ✅ [/web] add a /users page...
1038
1038
 
1039
1039
  === 10:17 — Remove unneeded worktree ===
1040
1040
 
1041
- You: /rmworktree @api feature/auth
1041
+ You: /rmworktree /api/feature/auth
1042
1042
  Bot: 🗑 Worktree feature/auth removed from project "api"
1043
1043
 
1044
1044
  === Evening — Shut down ===
@@ -216,9 +216,9 @@ function formatLabel (entry) {
216
216
  return 'unknown';
217
217
  }
218
218
  if (entry.branch && entry.branch !== 'main' && entry.branch !== 'master') {
219
- return `@${entry.project}/${entry.branch}`;
219
+ return `/${entry.project}/${entry.branch}`;
220
220
  }
221
- return `@${entry.project}`;
221
+ return `/${entry.project}`;
222
222
  }
223
223
 
224
224
  async function startTask (workDir, task) {
@@ -358,8 +358,8 @@ function handleQueue () {
358
358
  for (const [project, statuses] of Object.entries(all)) {
359
359
  for (const s of statuses) {
360
360
  const label = s.branch && s.branch !== 'main' && s.branch !== 'master'
361
- ? `@${project}/${s.branch}`
362
- : `@${project}`;
361
+ ? `/${project}/${s.branch}`
362
+ : `/${project}`;
363
363
  if (s.active || s.queueLength > 0) {
364
364
  text += `\n<b>${escapeHtml(label)}</b>:`;
365
365
  if (s.active) {
@@ -392,12 +392,12 @@ async function handleCancel (args) {
392
392
  }
393
393
 
394
394
  if (!runner.isRunning(workDir)) {
395
- return `❌ No active task in @${escapeHtml(projectAlias)}${branch ? '/' + escapeHtml(branch) : ''}`;
395
+ return `❌ No active task in /${escapeHtml(projectAlias)}${branch ? '/' + escapeHtml(branch) : ''}`;
396
396
  }
397
397
 
398
398
  runner.cancel(workDir);
399
399
  const next = queue.cancelActive(workDir);
400
- const label = branch ? `@${projectAlias}/${branch}` : `@${projectAlias}`;
400
+ const label = branch ? `/${projectAlias}/${branch}` : `/${projectAlias}`;
401
401
 
402
402
  if (next) {
403
403
  startTask(workDir, next);
@@ -409,7 +409,7 @@ async function handleCancel (args) {
409
409
  function handleDrop (args) {
410
410
  const target = parseTarget(args);
411
411
  if (!target) {
412
- return '❌ Usage: /drop @project N';
412
+ return '❌ Usage: /drop /project N';
413
413
  }
414
414
  const index = parseInt(target.rest, 10);
415
415
  if (!index || index < 1) {
@@ -443,7 +443,7 @@ function handleClear (args) {
443
443
  }
444
444
 
445
445
  const count = queue.clearQueue(workDir);
446
- const label = branch ? `@${projectAlias}/${branch}` : `@${projectAlias}`;
446
+ const label = branch ? `/${projectAlias}/${branch}` : `/${projectAlias}`;
447
447
  return `🧹 [${escapeHtml(label)}] Queue cleared (${count} tasks)`;
448
448
  }
449
449
 
@@ -452,7 +452,7 @@ function handleProjects () {
452
452
  let text = '📂 <b>Projects:</b>\n';
453
453
  for (const [alias, proj] of Object.entries(projects)) {
454
454
  const projPath = typeof proj === 'string' ? proj : proj.path;
455
- text += `\n<b>@${escapeHtml(alias)}</b> → <code>${escapeHtml(projPath)}</code>`;
455
+ text += `\n<b>/${escapeHtml(alias)}</b> → <code>${escapeHtml(projPath)}</code>`;
456
456
  const worktrees = typeof proj === 'object' ? proj.worktrees : null;
457
457
  if (worktrees && Object.keys(worktrees).length > 0) {
458
458
  for (const [branch, wtPath] of Object.entries(worktrees)) {
@@ -466,7 +466,7 @@ function handleProjects () {
466
466
  function handleWorktrees (args) {
467
467
  const target = parseTarget(args);
468
468
  if (!target) {
469
- return '❌ Usage: /worktrees @project';
469
+ return '❌ Usage: /worktrees /project';
470
470
  }
471
471
 
472
472
  const result = worktreeManager.listWorktrees(target.project);
@@ -484,11 +484,11 @@ function handleWorktrees (args) {
484
484
 
485
485
  function handleCreateWorktree (args) {
486
486
  const target = parseTarget(args);
487
- if (!target || !target.rest) {
488
- return '❌ Usage: /worktree @project branch-name';
487
+ if (!target || !target.branch) {
488
+ return '❌ Usage: /worktree /project/branch';
489
489
  }
490
490
 
491
- const branch = target.rest;
491
+ const branch = target.branch;
492
492
  try {
493
493
  const wtDir = worktreeManager.createWorktree(target.project, branch);
494
494
  return `🌿 Created worktree for "<b>${escapeHtml(target.project)}</b>":\n`
@@ -501,11 +501,11 @@ function handleCreateWorktree (args) {
501
501
 
502
502
  function handleRemoveWorktree (args) {
503
503
  const target = parseTarget(args);
504
- if (!target || !target.rest) {
505
- return '❌ Usage: /rmworktree @project branch-name';
504
+ if (!target || !target.branch) {
505
+ return '❌ Usage: /rmworktree /project/branch';
506
506
  }
507
507
 
508
- const branch = target.rest;
508
+ const branch = target.branch;
509
509
 
510
510
  // Check if there's an active task in this worktree
511
511
  let workDir;
@@ -517,7 +517,7 @@ function handleRemoveWorktree (args) {
517
517
  }
518
518
 
519
519
  if (workDir && runner.isRunning(workDir)) {
520
- return `❌ Cannot remove worktree: task is running. First /cancel @${escapeHtml(target.project)}/${escapeHtml(branch)}`;
520
+ return `❌ Cannot remove worktree: task is running. First /cancel /${escapeHtml(target.project)}/${escapeHtml(branch)}`;
521
521
  }
522
522
 
523
523
  try {
@@ -536,8 +536,8 @@ function handleHistory () {
536
536
  let text = '📜 <b>Recent tasks:</b>\n';
537
537
  for (const h of history.reverse()) {
538
538
  const label = h.branch && h.branch !== 'main' && h.branch !== 'master'
539
- ? `@${h.project}/${h.branch}`
540
- : `@${h.project}`;
539
+ ? `/${h.project}/${h.branch}`
540
+ : `/${h.project}`;
541
541
  const status = h.result === 'CANCELLED' ? '🛑' : h.result?.startsWith('ERROR') ? '❌' : '✅';
542
542
  text += `\n${status} [${escapeHtml(label)}] ${escapeHtml(h.text)}`;
543
543
  }
@@ -556,22 +556,22 @@ function handleHelp () {
556
556
  return `<b>📖 Commands:</b>
557
557
 
558
558
  /status — status of all projects
559
- /status @project — project status
559
+ /status /project — project status
560
560
  /queue — all queues
561
- /cancel [@project[/branch]] — cancel task
562
- /drop @project N — remove task from queue
563
- /clear @project[/branch] — clear queue
561
+ /cancel [/project[/branch]] — cancel task
562
+ /drop /project N — remove task from queue
563
+ /clear /project[/branch] — clear queue
564
564
  /projects — list projects
565
- /worktrees @project — project worktrees
566
- /worktree @project branch — create worktree
567
- /rmworktree @project branch — remove worktree
565
+ /worktrees /project — project worktrees
566
+ /worktree /project/branch — create worktree
567
+ /rmworktree /project/branch — remove worktree
568
568
  /history — task history
569
569
  /stop — stop listener
570
570
  /help — this help
571
571
 
572
572
  <b>Tasks:</b>
573
- <code>@project task</code> — main worktree
574
- <code>@project/branch task</code> — worktree
573
+ <code>/project task</code> — main worktree
574
+ <code>/project/branch task</code> — worktree
575
575
  <code>task</code> — default project`;
576
576
  }
577
577
 
@@ -606,7 +606,7 @@ async function handleTask (parsed, telegramMessageId) {
606
606
 
607
607
  if (autoCreated) {
608
608
  await poller.sendMessage(`🌿 Created worktree <b>${escapeHtml(parsed.branch)}</b> for "<b>${escapeHtml(parsed.project)}</b>"`);
609
- logger.info(`Auto-created worktree for task: @${parsed.project}/${parsed.branch} → ${workDir}`);
609
+ logger.info(`Auto-created worktree for task: /${parsed.project}/${parsed.branch} → ${workDir}`);
610
610
  }
611
611
 
612
612
  const result = queue.enqueue(
@@ -672,7 +672,7 @@ async function mainLoop () {
672
672
  await poller.sendMessage(response, msg.messageId);
673
673
  }
674
674
  } else if (parsed.type === 'task') {
675
- logger.info(`Task for @${parsed.project}${parsed.branch ? '/' + parsed.branch : ''}: ${parsed.text}`);
675
+ logger.info(`Task for /${parsed.project}${parsed.branch ? '/' + parsed.branch : ''}: ${parsed.text}`);
676
676
  await handleTask(parsed, msg.messageId);
677
677
  }
678
678
  }
@@ -11,9 +11,12 @@ const COMMANDS = [
11
11
  *
12
12
  * Formats:
13
13
  * /command args → { type: 'command', cmd, args }
14
- * @project/branch text → { type: 'task', project, branch, text }
15
- * @project text → { type: 'task', project, branch: null, text }
14
+ * /project/branch text → { type: 'task', project, branch, text }
15
+ * /project text → { type: 'task', project, branch: null, text }
16
16
  * text → { type: 'task', project: 'default', branch: null, text }
17
+ *
18
+ * If /word is a known command, it's treated as a command.
19
+ * Otherwise /word is treated as a project alias.
17
20
  */
18
21
  export function parseMessage (text) {
19
22
  if (!text || typeof text !== 'string') {
@@ -25,10 +28,11 @@ export function parseMessage (text) {
25
28
  return null;
26
29
  }
27
30
 
28
- // Check for commands
29
31
  if (trimmed.startsWith('/')) {
30
32
  const parts = trimmed.split(/\s+/);
31
33
  const cmd = parts[0].toLowerCase().replace(/@\w+$/, ''); // strip @botname
34
+
35
+ // Known command
32
36
  if (COMMANDS.includes(cmd)) {
33
37
  return {
34
38
  type: 'command',
@@ -36,30 +40,29 @@ export function parseMessage (text) {
36
40
  args: parts.slice(1).join(' '),
37
41
  };
38
42
  }
39
- }
40
43
 
41
- // Check for @project/branch or @project prefix
42
- const projectMatch = trimmed.match(/^@(\S+)\s+([\s\S]+)$/);
43
- if (projectMatch) {
44
- const target = projectMatch[1];
45
- const taskText = projectMatch[2].trim();
44
+ // Not a known command → treat as /project[/branch] task
45
+ const projectMatch = trimmed.match(/^\/(\S+)\s+([\s\S]+)$/);
46
+ if (projectMatch) {
47
+ const target = projectMatch[1];
48
+ const taskText = projectMatch[2].trim();
46
49
 
47
- // Split target into project and optional branch
48
- const slashIndex = target.indexOf('/');
49
- if (slashIndex > 0) {
50
+ const slashIndex = target.indexOf('/');
51
+ if (slashIndex > 0) {
52
+ return {
53
+ type: 'task',
54
+ project: target.substring(0, slashIndex),
55
+ branch: target.substring(slashIndex + 1),
56
+ text: taskText,
57
+ };
58
+ }
50
59
  return {
51
60
  type: 'task',
52
- project: target.substring(0, slashIndex),
53
- branch: target.substring(slashIndex + 1),
61
+ project: target,
62
+ branch: null,
54
63
  text: taskText,
55
64
  };
56
65
  }
57
- return {
58
- type: 'task',
59
- project: target,
60
- branch: null,
61
- text: taskText,
62
- };
63
66
  }
64
67
 
65
68
  // Plain text → default project
@@ -72,14 +75,14 @@ export function parseMessage (text) {
72
75
  }
73
76
 
74
77
  /**
75
- * Parse @project or @project/branch from command args.
76
- * Returns { project, branch } or null.
78
+ * Parse /project or /project/branch from command args.
79
+ * Returns { project, branch, rest } or null.
77
80
  */
78
81
  export function parseTarget (args) {
79
82
  if (!args) {
80
83
  return null;
81
84
  }
82
- const match = args.trim().match(/^@(\S+)/);
85
+ const match = args.trim().match(/^\/(\S+)/);
83
86
  if (!match) {
84
87
  return null;
85
88
  }
@@ -49,7 +49,7 @@ export function createTaskLogger (logDir) {
49
49
  const ts = new Date().toISOString();
50
50
  const entry = `\n${'='.repeat(80)}\n`
51
51
  + `[${ts}] QUESTION\n`
52
- + `Project: @${project}${branch && branch !== 'main' && branch !== 'master' ? '/' + branch : ''}\n`
52
+ + `Project: /${project}${branch && branch !== 'main' && branch !== 'master' ? '/' + branch : ''}\n`
53
53
  + `WorkDir: ${workDir}\n`
54
54
  + `Task: ${taskText}\n`;
55
55
  fs.appendFileSync(logPath, entry);
@@ -269,7 +269,7 @@ export class WorktreeManager {
269
269
 
270
270
  throw new Error(
271
271
  `Worktree "${branch}" not found for project "${projectAlias}". `
272
- + `Create it: /worktree @${projectAlias} ${branch}`
272
+ + `Create it: /worktree /${projectAlias}/${branch}`
273
273
  );
274
274
  }
275
275
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "claude-notification-plugin",
3
3
  "productName": "claude-notification-plugin",
4
- "version": "1.1.13",
4
+ "version": "1.1.15",
5
5
  "description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
6
6
  "type": "module",
7
7
  "engines": {