or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-options.mdfile-watching.mdindex.mdinstallation.mdqueries.mdsubscriptions.mdtriggers.md

subscriptions.mddocs/

0

# Real-time Subscriptions

1

2

Subscribe to file change notifications that are delivered automatically as changes occur. Subscriptions provide continuous monitoring of file system changes without polling.

3

4

## Capabilities

5

6

### Create Subscription

7

8

Subscribe to file changes with optional filtering expressions that automatically sends notifications as changes occur.

9

10

```bash { .api }

11

# Use JSON input for subscription commands

12

watchman -j <<-EOT

13

["subscribe", "/path", "subscription-name", {

14

"expression": [...],

15

"fields": [...]

16

}]

17

EOT

18

```

19

20

**JSON Protocol:**

21

```json

22

["subscribe", "/absolute/path", "subscription-name", {

23

"expression": ["allof", ["type", "f"], ["suffix", "js"]],

24

"fields": ["name", "exists", "mtime"],

25

"since": "c:123:456"

26

}]

27

```

28

29

**Parameters:**

30

- `path`: Absolute path to watched directory (required)

31

- `subscription-name`: Unique name for this subscription (required)

32

- `expression`: Query expression for filtering files (optional)

33

- `fields`: Array of file metadata fields to return (optional)

34

- `since`: Optional clockspec to get changes since specific time

35

36

**Initial Response:**

37

```json

38

{

39

"version": "2.0",

40

"subscribe": "subscription-name"

41

}

42

```

43

44

**Notification Format:**

45

When files change, unilateral notifications are sent:

46

```json

47

{

48

"version": "2.0",

49

"clock": "c:1234:568",

50

"files": [

51

{

52

"name": "src/main.js",

53

"exists": true,

54

"mtime": 1234567890

55

}

56

],

57

"root": "/absolute/path",

58

"subscription": "subscription-name"

59

}

60

```

61

62

**Example:**

63

```bash

64

# Subscribe to JavaScript file changes

65

watchman -j <<-EOT

66

["subscribe", "/home/user/project", "jsfiles", {

67

"expression": ["allof", ["type", "f"], ["suffix", "js"]],

68

"fields": ["name", "exists", "size"]

69

}]

70

EOT

71

72

# Subscribe with since parameter for incremental updates

73

watchman -j <<-EOT

74

["subscribe", "/home/user/project", "incremental", {

75

"since": "n:myapp_state",

76

"expression": ["not", "empty"],

77

"fields": ["name"]

78

}]

79

EOT

80

```

81

82

### Cancel Subscription

83

84

Remove a named subscription to stop receiving notifications.

85

86

```bash { .api }

87

watchman unsubscribe <path> <subscription-name>

88

```

89

90

**JSON Protocol:**

91

```json

92

["unsubscribe", "/absolute/path", "subscription-name"]

93

```

94

95

**Parameters:**

96

- `path`: Absolute path to watched directory (required)

97

- `subscription-name`: Name of subscription to cancel (required)

98

99

**Response:**

100

```json

101

{

102

"version": "2.0",

103

"unsubscribe": "subscription-name",

104

"deleted": true

105

}

106

```

107

108

**Example:**

109

```bash

110

# Cancel a specific subscription

111

watchman unsubscribe /home/user/project jsfiles

112

113

# Using JSON protocol

114

watchman -j <<-EOT

115

["unsubscribe", "/home/user/project", "jsfiles"]

116

EOT

117

```

118

119

## Subscription Behavior

120

121

### Connection-Based Lifecycle

122

- Subscriptions are tied to the client connection

123

- Automatically removed when connection closes

124

- Not persisted across Watchman restarts

125

- Require persistent connection to receive notifications

126

127

### Initial File Set

128

- When first created, subscription evaluates expression against all files

129

- Sends initial notification if any files match

130

- Subsequent notifications sent only for changes

131

132

### File System Settling

133

- Respects Watchman's settle period before sending notifications

134

- Batches multiple changes into single notification

135

- Reduces notification volume during bulk operations

136

137

### Notification Delivery

138

- Sent unilaterally by server as changes occur

139

- Client must be prepared to receive at any time

140

- No acknowledgment required from client

141

- Notifications are JSON objects followed by newline

142

143

## Expression Filtering

144

145

Subscriptions support the same expression syntax as queries for flexible file filtering.

146

147

### Basic Expressions

148

149

```bash

150

# All files

151

"expression": ["true"]

152

153

# Regular files only

154

"expression": ["type", "f"]

155

156

# Specific file extension

157

"expression": ["suffix", "js"]

158

159

# Multiple extensions

160

"expression": ["anyof", ["suffix", "js"], ["suffix", "ts"]]

161

162

# Non-empty files

163

"expression": ["not", "empty"]

164

```

165

166

### Complex Filtering

167

168

```bash

169

# JavaScript test files only

170

watchman -j <<-EOT

171

["subscribe", "/project", "tests", {

172

"expression": ["allof",

173

["type", "f"],

174

["suffix", "js"],

175

["match", "*test*", "basename"]

176

],

177

"fields": ["name"]

178

}]

179

EOT

180

181

# Source files excluding node_modules

182

watchman -j <<-EOT

183

["subscribe", "/project", "sources", {

184

"expression": ["allof",

185

["type", "f"],

186

["anyof", ["suffix", "js"], ["suffix", "ts"], ["suffix", "json"]],

187

["not", ["match", "node_modules/**", "wholename"]]

188

],

189

"fields": ["name", "size"]

190

}]

191

EOT

192

```

193

194

## Field Selection

195

196

Control which file metadata is included in notifications:

197

198

### Common Fields

199

- `name`: Relative file path (always useful)

200

- `exists`: File existence flag

201

- `size`: File size in bytes

202

- `mode`: Unix file permissions

203

- `mtime`: Modification timestamp

204

205

### Extended Fields

206

- `uid`, `gid`: User and group IDs

207

- `ctime`, `atime`: Creation and access times

208

- `ino`: Inode number

209

- `dev`: Device ID

210

- `nlink`: Hard link count

211

- `new`: True if newly created (since queries only)

212

213

**Example with custom fields:**

214

```bash

215

watchman -j <<-EOT

216

["subscribe", "/project", "detailed", {

217

"expression": ["suffix", "py"],

218

"fields": ["name", "exists", "size", "mtime", "mode"]

219

}]

220

EOT

221

```

222

223

## Usage Examples

224

225

### Development File Watcher

226

227

```bash

228

#!/bin/bash

229

# dev-watcher.sh - Monitor development files

230

231

# Connect to watchman and maintain persistent connection

232

exec 3< <(watchman -j -p <<-'EOT'

233

["subscribe", "/home/user/project", "dev-files", {

234

"expression": ["allof",

235

["type", "f"],

236

["anyof",

237

["suffix", "js"], ["suffix", "ts"], ["suffix", "jsx"],

238

["suffix", "css"], ["suffix", "html"], ["suffix", "json"]

239

],

240

["not", ["match", "node_modules/**", "wholename"]]

241

],

242

"fields": ["name", "exists", "size"]

243

}]

244

EOT

245

)

246

247

# Read notifications

248

while read -r -u 3 notification; do

249

echo "Received: $notification"

250

251

# Parse and process notification

252

if echo "$notification" | jq -e '.subscription == "dev-files"' > /dev/null; then

253

files=$(echo "$notification" | jq -r '.files[].name')

254

echo "Changed files: $files"

255

256

# Trigger build or other actions

257

echo "Rebuilding..."

258

npm run build

259

fi

260

done

261

```

262

263

### Live Reload Implementation

264

265

```bash

266

#!/bin/bash

267

# live-reload.sh - Browser live reload on file changes

268

269

# WebSocket server for browser communication (conceptual)

270

start_websocket_server() {

271

# Start WebSocket server on port 8080

272

websocketd --port=8080 cat &

273

WS_PID=$!

274

}

275

276

# Subscribe to web asset changes

277

monitor_assets() {

278

watchman -j -p <<-'EOT' | while read -r notification; do

279

["subscribe", "/var/www/html", "assets", {

280

"expression": ["allof",

281

["type", "f"],

282

["anyof", ["suffix", "html"], ["suffix", "css"], ["suffix", "js"]]

283

],

284

"fields": ["name"]

285

}]

286

EOT

287

if echo "$notification" | jq -e '.subscription == "assets"' > /dev/null; then

288

# Send reload signal to browser

289

echo '{"type": "reload"}' > /dev/tcp/localhost/8080

290

fi

291

done

292

}

293

294

start_websocket_server

295

monitor_assets

296

```

297

298

### Test Runner Integration

299

300

```bash

301

#!/bin/bash

302

# auto-test.sh - Run tests when source files change

303

304

watchman -j -p <<-'EOT' | while read -r notification; do

305

["subscribe", "/project", "test-runner", {

306

"expression": ["allof",

307

["type", "f"],

308

["anyof",

309

["match", "src/**/*.py", "wholename"],

310

["match", "tests/**/*.py", "wholename"]

311

]

312

],

313

"fields": ["name"]

314

}]

315

EOT

316

if echo "$notification" | jq -e '.subscription == "test-runner"' > /dev/null; then

317

changed_files=$(echo "$notification" | jq -r '.files[].name | select(test("test_.*\\.py$"))')

318

319

if [ -n "$changed_files" ]; then

320

echo "Running tests for: $changed_files"

321

python -m pytest $changed_files

322

else

323

echo "Source files changed, running all tests"

324

python -m pytest tests/

325

fi

326

fi

327

done

328

```

329

330

## Protocol Considerations

331

332

### Persistent Connections

333

- Use `-p` flag for persistent mode

334

- Maintain connection to receive notifications

335

- Handle connection failures and reconnection

336

337

### JSON Parsing

338

- Each notification is a complete JSON object

339

- Followed by newline character

340

- Parse incrementally as messages arrive

341

342

### Error Handling

343

- Watch for error responses in JSON stream

344

- Handle subscription failures gracefully

345

- Implement reconnection logic for production use

346

347

### Performance

348

- Use specific expressions to reduce notification volume

349

- Select only needed fields to minimize data transfer

350

- Consider batching file operations based on notifications