inboxd 1.0.13 → 1.2.0
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/.claude/skills/inbox-assistant/SKILL.md +89 -78
- package/CLAUDE.md +47 -32
- package/README.md +32 -32
- package/package.json +3 -3
- package/scripts/postinstall.js +5 -5
- package/src/archive-log.js +104 -0
- package/src/cli.js +496 -41
- package/src/deletion-log.js +101 -0
- package/src/gmail-auth.js +1 -1
- package/src/gmail-monitor.js +29 -0
- package/src/sent-log.js +35 -0
- package/tests/archive-log.test.js +196 -0
- package/tests/cleanup-suggest.test.js +239 -0
- package/tests/filter.test.js +1 -1
- package/tests/install-service.test.js +210 -0
- package/tests/interactive-confirm.test.js +175 -0
- package/tests/json-output.test.js +189 -0
- package/tests/older-than.test.js +1 -1
- package/tests/setup.test.js +1 -1
- package/tests/stats.test.js +218 -0
- package/tests/unarchive.test.js +228 -0
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ npm install -g inboxd
|
|
|
30
30
|
Run the interactive setup wizard:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
|
|
33
|
+
inboxd setup
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
The wizard will guide you through:
|
|
@@ -60,33 +60,33 @@ mv ~/Downloads/client_secret_*.json ~/.config/inboxd/credentials.json
|
|
|
60
60
|
### 2. Authenticate
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
|
|
63
|
+
inboxd auth --account personal
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
### 3. Check Your Inbox
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
|
-
|
|
69
|
+
inboxd summary
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
## Commands
|
|
73
73
|
|
|
74
74
|
| Command | Description |
|
|
75
75
|
|---------|-------------|
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
76
|
+
| `inboxd setup` | Interactive setup wizard |
|
|
77
|
+
| `inboxd auth -a <name>` | Authenticate a Gmail account |
|
|
78
|
+
| `inboxd accounts` | List configured accounts |
|
|
79
|
+
| `inboxd summary` | Show inboxd summary for all accounts |
|
|
80
|
+
| `inboxd check` | Check for new emails + send notifications |
|
|
81
|
+
| `inboxd check -q` | Silent check (for background use) |
|
|
82
|
+
| `inboxd delete --ids <ids>` | Move emails to trash |
|
|
83
|
+
| `inboxd delete --sender <pattern>` | Delete by sender (with confirmation) |
|
|
84
|
+
| `inboxd delete --match <pattern>` | Delete by subject (with confirmation) |
|
|
85
|
+
| `inboxd restore --last 1` | Restore last deleted email |
|
|
86
|
+
| `inboxd deletion-log` | View deletion history |
|
|
87
|
+
| `inboxd logout --all` | Remove all accounts |
|
|
88
|
+
| `inboxd install-service` | Install background monitoring (macOS) |
|
|
89
|
+
| `inboxd install-skill` | Install Claude Code skill for AI agents |
|
|
90
90
|
|
|
91
91
|
## Configuration
|
|
92
92
|
|
|
@@ -106,10 +106,10 @@ Install as a macOS launchd service to check for new emails periodically:
|
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
108
|
# Install and start with default 5-minute interval
|
|
109
|
-
|
|
109
|
+
inboxd install-service
|
|
110
110
|
|
|
111
111
|
# Or customize the interval
|
|
112
|
-
|
|
112
|
+
inboxd install-service --interval 10
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
The service starts automatically after installation. Manage it with:
|
|
@@ -127,7 +127,7 @@ launchctl unload ~/Library/LaunchAgents/com.danielparedes.inboxd.plist
|
|
|
127
127
|
|
|
128
128
|
**Note:** `install-service` is macOS-only. For Linux, use cron:
|
|
129
129
|
```bash
|
|
130
|
-
*/5 * * * * /path/to/node /path/to/
|
|
130
|
+
*/5 * * * * /path/to/node /path/to/inboxd check --quiet
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
## JSON Output
|
|
@@ -135,13 +135,13 @@ launchctl unload ~/Library/LaunchAgents/com.danielparedes.inboxd.plist
|
|
|
135
135
|
For AI agent integration, use the `--json` flag:
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
|
-
|
|
138
|
+
inboxd summary --json
|
|
139
139
|
```
|
|
140
140
|
|
|
141
141
|
Or use the `analyze` command for structured output:
|
|
142
142
|
|
|
143
143
|
```bash
|
|
144
|
-
|
|
144
|
+
inboxd analyze --count 20
|
|
145
145
|
```
|
|
146
146
|
|
|
147
147
|
## AI Agent Integration
|
|
@@ -163,12 +163,12 @@ This package includes a **skill** that can be installed globally, enabling any C
|
|
|
163
163
|
After installing inboxd, run:
|
|
164
164
|
|
|
165
165
|
```bash
|
|
166
|
-
|
|
166
|
+
inboxd install-skill
|
|
167
167
|
```
|
|
168
168
|
|
|
169
169
|
This copies the inbox-assistant skill to `~/.claude/skills/`, making it available in all your Claude Code sessions.
|
|
170
170
|
|
|
171
|
-
The setup wizard (`
|
|
171
|
+
The setup wizard (`inboxd setup`) also offers to install the skill automatically.
|
|
172
172
|
|
|
173
173
|
### What the Skill Enables
|
|
174
174
|
|
|
@@ -194,13 +194,13 @@ The skill auto-updates when you update inboxd (if already installed). You can al
|
|
|
194
194
|
|
|
195
195
|
```bash
|
|
196
196
|
npm update -g inboxd
|
|
197
|
-
|
|
197
|
+
inboxd install-skill
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
Safety features:
|
|
201
201
|
- Won't overwrite skills not created by inboxd (uses `source: inboxd` marker)
|
|
202
202
|
- Creates `SKILL.md.backup` before replacing if you've modified the skill
|
|
203
|
-
- Use `
|
|
203
|
+
- Use `inboxd install-skill --force` to override ownership check
|
|
204
204
|
|
|
205
205
|
### CLI vs MCP
|
|
206
206
|
|
|
@@ -208,9 +208,9 @@ Unlike an MCP server that exposes raw Gmail API primitives, `inboxd` provides **
|
|
|
208
208
|
|
|
209
209
|
| inboxd CLI | Raw Gmail MCP |
|
|
210
210
|
|------------|---------------|
|
|
211
|
-
| `
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
211
|
+
| `inboxd delete` logs before trashing | Just trashes |
|
|
212
|
+
| `inboxd restore` removes from log | Just untrashes |
|
|
213
|
+
| `inboxd analyze` formats for AI consumption | Raw API response |
|
|
214
214
|
|
|
215
215
|
The skill layer adds expert workflow guidance on top of these commands.
|
|
216
216
|
|
|
@@ -225,7 +225,7 @@ npm uninstall -g inboxd
|
|
|
225
225
|
To also remove all account data and tokens:
|
|
226
226
|
|
|
227
227
|
```bash
|
|
228
|
-
|
|
228
|
+
inboxd logout --all
|
|
229
229
|
```
|
|
230
230
|
|
|
231
231
|
To completely remove all data including credentials:
|
|
@@ -239,13 +239,13 @@ rm -rf ~/.config/inboxd
|
|
|
239
239
|
## Troubleshooting
|
|
240
240
|
|
|
241
241
|
**"credentials.json not found"**
|
|
242
|
-
Run `
|
|
242
|
+
Run `inboxd setup` or manually download OAuth credentials from Google Cloud Console and save to `~/.config/inboxd/credentials.json`.
|
|
243
243
|
|
|
244
244
|
**"Token expired"**
|
|
245
245
|
Delete the token file and re-authenticate:
|
|
246
246
|
```bash
|
|
247
247
|
rm ~/.config/inboxd/token-<account>.json
|
|
248
|
-
|
|
248
|
+
inboxd auth -a <account>
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
**No notifications appearing**
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "inboxd",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "CLI assistant for Gmail monitoring with multi-account support and AI-ready JSON output",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"
|
|
7
|
+
"inboxd": "src/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"test": "vitest run",
|
|
22
22
|
"test:watch": "vitest",
|
|
23
23
|
"lint": "eslint src tests",
|
|
24
|
-
"
|
|
24
|
+
"inboxd": "node src/cli.js",
|
|
25
25
|
"postinstall": "node scripts/postinstall.js || true"
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
package/scripts/postinstall.js
CHANGED
|
@@ -21,10 +21,10 @@ function showInstallHint() {
|
|
|
21
21
|
│ inboxd installed successfully! │
|
|
22
22
|
│ │
|
|
23
23
|
│ Quick start: │
|
|
24
|
-
│
|
|
24
|
+
│ inboxd setup # First-time configuration │
|
|
25
25
|
│ │
|
|
26
26
|
│ AI Agent Integration: │
|
|
27
|
-
│
|
|
27
|
+
│ inboxd install-skill # Enable Claude Code skill │
|
|
28
28
|
│ │
|
|
29
29
|
│ This lets AI agents manage your inbox with expert triage. │
|
|
30
30
|
│ │
|
|
@@ -50,7 +50,7 @@ function handleSkillUpdate() {
|
|
|
50
50
|
// Someone else's skill with same name → don't touch, warn
|
|
51
51
|
console.log(`\n⚠️ ~/.claude/skills/inbox-assistant exists but isn't from inboxd`);
|
|
52
52
|
console.log(` The existing skill has source: "${status.source || 'none'}"`);
|
|
53
|
-
console.log(` Run '
|
|
53
|
+
console.log(` Run 'inboxd install-skill --force' to replace it\n`);
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -69,13 +69,13 @@ function handleSkillUpdate() {
|
|
|
69
69
|
}
|
|
70
70
|
} else if (result.reason === 'backup_failed') {
|
|
71
71
|
console.log(`\n⚠️ Could not backup existing skill - update skipped`);
|
|
72
|
-
console.log(` Run '
|
|
72
|
+
console.log(` Run 'inboxd install-skill' manually to update\n`);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
// Up-to-date → silent (no message)
|
|
76
76
|
} catch (err) {
|
|
77
77
|
// Fail silently - postinstall should not break npm install
|
|
78
|
-
// User can always run '
|
|
78
|
+
// User can always run 'inboxd install-skill' manually
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { TOKEN_DIR } = require('./gmail-auth');
|
|
4
|
+
const { atomicWriteJsonSync } = require('./utils');
|
|
5
|
+
|
|
6
|
+
const LOG_DIR = TOKEN_DIR;
|
|
7
|
+
const LOG_FILE = path.join(LOG_DIR, 'archive-log.json');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Ensures the log directory exists
|
|
11
|
+
*/
|
|
12
|
+
function ensureLogDir() {
|
|
13
|
+
if (!fs.existsSync(LOG_DIR)) {
|
|
14
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Reads the current archive log
|
|
20
|
+
* @returns {Array} Array of archive entries
|
|
21
|
+
*/
|
|
22
|
+
function readArchiveLog() {
|
|
23
|
+
ensureLogDir();
|
|
24
|
+
if (!fs.existsSync(LOG_FILE)) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const content = fs.readFileSync(LOG_FILE, 'utf8');
|
|
29
|
+
return JSON.parse(content);
|
|
30
|
+
} catch (_err) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Logs archived emails to the archive log
|
|
37
|
+
* @param {Array} emails - Array of email objects with id, threadId, account, from, subject, snippet
|
|
38
|
+
*/
|
|
39
|
+
function logArchives(emails) {
|
|
40
|
+
ensureLogDir();
|
|
41
|
+
const log = readArchiveLog();
|
|
42
|
+
const timestamp = new Date().toISOString();
|
|
43
|
+
|
|
44
|
+
for (const email of emails) {
|
|
45
|
+
log.push({
|
|
46
|
+
archivedAt: timestamp,
|
|
47
|
+
account: email.account,
|
|
48
|
+
id: email.id,
|
|
49
|
+
threadId: email.threadId,
|
|
50
|
+
from: email.from,
|
|
51
|
+
subject: email.subject,
|
|
52
|
+
snippet: email.snippet,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
atomicWriteJsonSync(LOG_FILE, log);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Gets recent archives from the log
|
|
61
|
+
* @param {number} days - Number of days to look back (default: 30)
|
|
62
|
+
* @returns {Array} Array of archive entries within the time range
|
|
63
|
+
*/
|
|
64
|
+
function getRecentArchives(days = 30) {
|
|
65
|
+
const log = readArchiveLog();
|
|
66
|
+
const cutoff = new Date();
|
|
67
|
+
cutoff.setDate(cutoff.getDate() - days);
|
|
68
|
+
|
|
69
|
+
return log.filter((entry) => {
|
|
70
|
+
const archivedAt = new Date(entry.archivedAt);
|
|
71
|
+
return archivedAt >= cutoff;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Gets the path to the log file (for display purposes)
|
|
77
|
+
* @returns {string} The log file path
|
|
78
|
+
*/
|
|
79
|
+
function getArchiveLogPath() {
|
|
80
|
+
return LOG_FILE;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Removes entries from the archive log (e.g., after unarchiving)
|
|
85
|
+
* @param {Array<string>} ids - Array of message IDs to remove
|
|
86
|
+
*/
|
|
87
|
+
function removeArchiveLogEntries(ids) {
|
|
88
|
+
ensureLogDir();
|
|
89
|
+
const log = readArchiveLog();
|
|
90
|
+
|
|
91
|
+
const newLog = log.filter(entry => !ids.includes(entry.id));
|
|
92
|
+
|
|
93
|
+
if (log.length !== newLog.length) {
|
|
94
|
+
atomicWriteJsonSync(LOG_FILE, newLog);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
logArchives,
|
|
100
|
+
getRecentArchives,
|
|
101
|
+
getArchiveLogPath,
|
|
102
|
+
readArchiveLog,
|
|
103
|
+
removeArchiveLogEntries,
|
|
104
|
+
};
|