@moshi-labs/snitch 1.0.2 → 1.0.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.
package/README.md CHANGED
@@ -1,53 +1,104 @@
1
- # @moshi-labs/snitch
1
+ # 🐀 Snitch
2
2
 
3
- CLI daemon that reports Claude Code activity to a team server.
3
+ See what your teammates' Claude Code sessions are working on in real-time.
4
4
 
5
- ## Install
5
+ **Dashboard:** https://snitch.moshi.ai
6
+
7
+ ---
8
+
9
+ ## Quick Start (5 minutes)
10
+
11
+ ### Step 1: Install
6
12
 
7
13
  ```bash
8
14
  npm install -g @moshi-labs/snitch
9
15
  ```
10
16
 
11
- ## Setup
17
+ ### Step 2: Configure
12
18
 
13
19
  ```bash
14
20
  snitch init
15
21
  ```
16
22
 
17
- You'll be prompted for:
18
- - **Server URL** - Your team's snitch server
19
- - **API Key** - Team API key for authentication
20
- - **Your name** - How you appear on the dashboard
23
+ You'll be asked for three things:
21
24
 
22
- ## Usage
25
+ | Prompt | What to enter |
26
+ |--------|---------------|
27
+ | **Server URL** | `https://snitch.moshi.ai` |
28
+ | **API Key** | Ask James for the team key |
29
+ | **Your name** | Your name (this shows on the dashboard) |
30
+
31
+ ### Step 3: Start the daemon
23
32
 
24
33
  ```bash
25
- snitch start # Start the background daemon
26
- snitch stop # Stop the daemon
27
- snitch status # Check if daemon is running
28
- snitch logs # View recent logs
29
- snitch logs -f # Follow logs in real-time
34
+ snitch start
30
35
  ```
31
36
 
37
+ That's it! Your Claude Code activity will now appear on the team dashboard.
38
+
39
+ ---
40
+
41
+ ## Commands
42
+
43
+ | Command | What it does |
44
+ |---------|--------------|
45
+ | `snitch start` | Start tracking (runs in background) |
46
+ | `snitch stop` | Stop tracking |
47
+ | `snitch status` | Check if it's running |
48
+ | `snitch logs` | View recent logs |
49
+ | `snitch logs -f` | Follow logs live |
50
+
51
+ ---
52
+
53
+ ## Troubleshooting
54
+
55
+ ### "Is it working?"
56
+
57
+ ```bash
58
+ snitch status
59
+ ```
60
+
61
+ Should say "Daemon is running". If not, run `snitch start`.
62
+
63
+ ### "I don't see my activity on the dashboard"
64
+
65
+ 1. Make sure you're working in a moshi repo (moshi-api, moshi-frontend, etc.)
66
+ 2. Check logs for errors: `snitch logs`
67
+ 3. Verify your config: `cat ~/.snitch/config.json`
68
+
69
+ ### "I want to reconfigure"
70
+
71
+ Just run `snitch init` again. Press Enter to keep existing values.
72
+
73
+ ---
74
+
32
75
  ## How It Works
33
76
 
34
- 1. Daemon watches `~/.claude/projects/` for Claude Code session activity
35
- 2. Extracts tool calls and messages from moshi-labs repos
36
- 3. POSTs activity to your team server
37
- 4. Server categorizes work and displays on dashboard
77
+ 1. Snitch watches `~/.claude/projects/` for Claude Code session files
78
+ 2. When you use Claude Code in a moshi repo, it detects tool calls and messages
79
+ 3. Activity is sent to the team server
80
+ 4. Server uses Claude to categorize your work into tickets
81
+ 5. Dashboard shows what everyone's working on
82
+
83
+ ---
38
84
 
39
- ## Config
85
+ ## Config File
40
86
 
41
- Stored in `~/.snitch/config.json`:
87
+ Stored at `~/.snitch/config.json`:
42
88
 
43
89
  ```json
44
90
  {
45
- "serverUrl": "http://your-server:3333",
91
+ "serverUrl": "https://snitch.moshi.ai",
46
92
  "apiKey": "your-team-key",
47
- "userName": "Your Name"
93
+ "userName": "Your Name",
94
+ "allowedRepos": ["moshi-api", "moshi-frontend", "moshi-e2e", "terraform", "snitch"]
48
95
  }
49
96
  ```
50
97
 
98
+ You can edit `allowedRepos` to track additional repositories.
99
+
100
+ ---
101
+
51
102
  ## License
52
103
 
53
104
  MIT
package/cli/collector.js CHANGED
@@ -3,6 +3,7 @@ import { readFileSync, statSync, existsSync } from 'fs';
3
3
  import { join, basename, dirname } from 'path';
4
4
  import { execSync } from 'child_process';
5
5
  import { homedir } from 'os';
6
+ import { load } from './config.js';
6
7
 
7
8
  const CLAUDE_PROJECTS_DIR = join(homedir(), '.claude', 'projects');
8
9
 
@@ -16,8 +17,13 @@ export class ActivityCollector {
16
17
  this.watcher = null;
17
18
  this.filePositions = new Map(); // Track read position per file
18
19
  this.activeThresholdMs = 5 * 60 * 1000; // 5 minutes = active
19
- // Only track these repos
20
- this.allowedRepos = ['moshi-api', 'moshi-frontend', 'moshi-e2e', 'terraform', 'browser-use', 'claude-team-viewer', 'snitch'];
20
+ // Load allowed repos from config
21
+ const config = load();
22
+ this.allowedRepos = config.allowedRepos || [];
23
+ // Build regex pattern from allowed repos
24
+ this.repoPattern = this.allowedRepos.length > 0
25
+ ? new RegExp(this.allowedRepos.map(r => r.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'))
26
+ : null;
21
27
  }
22
28
 
23
29
  start() {
@@ -102,6 +108,7 @@ export class ActivityCollector {
102
108
  events: [],
103
109
  repo: null,
104
110
  branch: null,
111
+ prUrl: null,
105
112
  };
106
113
 
107
114
  for (const event of events) {
@@ -117,6 +124,19 @@ export class ActivityCollector {
117
124
  });
118
125
  }
119
126
 
127
+ // Extract PR URLs from tool results
128
+ if (event.type === 'user' && Array.isArray(event.message?.content)) {
129
+ for (const block of event.message.content) {
130
+ if (block.type === 'tool_result' && typeof block.content === 'string') {
131
+ // Look for GitHub PR URLs in tool output
132
+ const prMatch = block.content.match(/https:\/\/github\.com\/[^\/]+\/[^\/]+\/pull\/\d+/);
133
+ if (prMatch) {
134
+ activity.prUrl = prMatch[0];
135
+ }
136
+ }
137
+ }
138
+ }
139
+
120
140
  // Extract assistant tool calls
121
141
  if (event.type === 'assistant' && event.message?.content) {
122
142
  for (const block of event.message.content) {
@@ -137,9 +157,9 @@ export class ActivityCollector {
137
157
  }
138
158
  // Detect cd into repos
139
159
  const cdMatch = cmd.match(/cd\s+['"]*([^'"&|;\s]+)/);
140
- if (cdMatch) {
160
+ if (cdMatch && this.repoPattern) {
141
161
  const path = cdMatch[1];
142
- const repoMatch = path.match(/moshi-(api|frontend|e2e)|terraform|browser-use|claude-team-viewer|snitch/);
162
+ const repoMatch = path.match(this.repoPattern);
143
163
  if (repoMatch) {
144
164
  activity.repo = repoMatch[0];
145
165
  }
@@ -150,9 +170,11 @@ export class ActivityCollector {
150
170
  // Extract repo hints from file operations
151
171
  if (['Read', 'Edit', 'Write'].includes(block.name) && block.input?.file_path) {
152
172
  const path = block.input.file_path;
153
- const repoMatch = path.match(/moshi-(api|frontend|e2e)|terraform|browser-use|claude-team-viewer|snitch/);
154
- if (repoMatch) {
155
- activity.repo = repoMatch[0];
173
+ if (this.repoPattern) {
174
+ const repoMatch = path.match(this.repoPattern);
175
+ if (repoMatch) {
176
+ activity.repo = repoMatch[0];
177
+ }
156
178
  }
157
179
  toolEvent.hint = path;
158
180
  }
@@ -166,8 +188,8 @@ export class ActivityCollector {
166
188
  if (event.gitBranch) {
167
189
  activity.branch = event.gitBranch;
168
190
  }
169
- if (event.cwd) {
170
- const repoMatch = event.cwd.match(/moshi-(api|frontend|e2e)|terraform|browser-use|claude-team-viewer|snitch/);
191
+ if (event.cwd && this.repoPattern) {
192
+ const repoMatch = event.cwd.match(this.repoPattern);
171
193
  if (repoMatch) {
172
194
  activity.repo = repoMatch[0];
173
195
  }
package/cli/config.js CHANGED
@@ -14,6 +14,7 @@ const DEFAULT_CONFIG = {
14
14
  serverUrl: '',
15
15
  apiKey: '',
16
16
  userName: '',
17
+ allowedRepos: ['moshi-api', 'moshi-frontend', 'moshi-e2e', 'terraform', 'claude-team-viewer', 'snitch'],
17
18
  };
18
19
 
19
20
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moshi-labs/snitch",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "CLI daemon that reports Claude Code activity to a team server",
5
5
  "type": "module",
6
6
  "bin": {