inboxd 1.0.9 → 1.0.10
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.md +22 -5
- package/README.md +8 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +5 -1
- package/src/skill-installer.js +36 -8
package/CLAUDE.md
CHANGED
|
@@ -106,16 +106,21 @@ inbox install-skill --uninstall # Remove
|
|
|
106
106
|
|
|
107
107
|
The `inbox setup` wizard also offers to install the skill automatically.
|
|
108
108
|
|
|
109
|
-
### Skill Location &
|
|
109
|
+
### Skill Location & Update Detection
|
|
110
110
|
|
|
111
111
|
| Location | Purpose |
|
|
112
112
|
|----------|---------|
|
|
113
113
|
| `.claude/skills/inbox-assistant/SKILL.md` | Source (bundled with package) |
|
|
114
114
|
| `~/.claude/skills/inbox-assistant/SKILL.md` | Installed (global for Claude Code) |
|
|
115
115
|
|
|
116
|
-
The skill
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
The skill uses content-hash detection (no version field). Updates are detected automatically:
|
|
117
|
+
- On `npm install`: Auto-updates if skill already installed
|
|
118
|
+
- Manual: Run `inbox install-skill` to update
|
|
119
|
+
|
|
120
|
+
Safety features:
|
|
121
|
+
- `source: inboxd` marker identifies ownership (won't overwrite user's own skills)
|
|
122
|
+
- Creates `SKILL.md.backup` before replacing modified files
|
|
123
|
+
- Use `--force` to override ownership check
|
|
119
124
|
|
|
120
125
|
### Architecture
|
|
121
126
|
|
|
@@ -135,10 +140,22 @@ scripts/postinstall.js # npm postinstall hint about install-skill
|
|
|
135
140
|
|---------|---------|
|
|
136
141
|
| `inbox summary --json` | Quick status check (unread counts) |
|
|
137
142
|
| `inbox analyze --count 50` | Get email data as JSON for classification |
|
|
138
|
-
| `inbox
|
|
143
|
+
| `inbox analyze --group-by sender` | Group emails by sender domain |
|
|
144
|
+
| `inbox delete --ids "id1,id2" --confirm` | Delete specific emails by ID |
|
|
145
|
+
| `inbox delete --sender "pattern" --dry-run` | Preview deletion by sender filter |
|
|
146
|
+
| `inbox delete --match "pattern" --dry-run` | Preview deletion by subject filter |
|
|
139
147
|
| `inbox restore --last N` | Undo last N deletions |
|
|
140
148
|
| `inbox install-skill` | Install/update the Claude Code skill |
|
|
141
149
|
|
|
150
|
+
### Smart Filtering Options
|
|
151
|
+
| Option | Description |
|
|
152
|
+
|--------|-------------|
|
|
153
|
+
| `--sender <pattern>` | Case-insensitive substring match on From field |
|
|
154
|
+
| `--match <pattern>` | Case-insensitive substring match on Subject field |
|
|
155
|
+
| `--limit <N>` | Max emails for filter operations (default: 50) |
|
|
156
|
+
| `--force` | Override safety warnings (short patterns, large batches) |
|
|
157
|
+
| `--dry-run` | Preview what would be deleted without deleting |
|
|
158
|
+
|
|
142
159
|
### Email Object Shape (from `analyze`)
|
|
143
160
|
```json
|
|
144
161
|
{
|
package/README.md
CHANGED
|
@@ -80,6 +80,8 @@ inbox summary
|
|
|
80
80
|
| `inbox check` | Check for new emails + send notifications |
|
|
81
81
|
| `inbox check -q` | Silent check (for background use) |
|
|
82
82
|
| `inbox delete --ids <ids>` | Move emails to trash |
|
|
83
|
+
| `inbox delete --sender <pattern>` | Delete by sender (with confirmation) |
|
|
84
|
+
| `inbox delete --match <pattern>` | Delete by subject (with confirmation) |
|
|
83
85
|
| `inbox restore --last 1` | Restore last deleted email |
|
|
84
86
|
| `inbox deletion-log` | View deletion history |
|
|
85
87
|
| `inbox logout --all` | Remove all accounts |
|
|
@@ -188,13 +190,18 @@ The skill provides:
|
|
|
188
190
|
|
|
189
191
|
### Updating the Skill
|
|
190
192
|
|
|
191
|
-
|
|
193
|
+
The skill auto-updates when you update inboxd (if already installed). You can also update manually:
|
|
192
194
|
|
|
193
195
|
```bash
|
|
194
196
|
npm update -g inboxd
|
|
195
197
|
inbox install-skill
|
|
196
198
|
```
|
|
197
199
|
|
|
200
|
+
Safety features:
|
|
201
|
+
- Won't overwrite skills not created by inboxd (uses `source: inboxd` marker)
|
|
202
|
+
- Creates `SKILL.md.backup` before replacing if you've modified the skill
|
|
203
|
+
- Use `inbox install-skill --force` to override ownership check
|
|
204
|
+
|
|
198
205
|
### CLI vs MCP
|
|
199
206
|
|
|
200
207
|
Unlike an MCP server that exposes raw Gmail API primitives, `inboxd` provides **opinionated commands** with built-in safety:
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -62,10 +62,14 @@ function handleSkillUpdate() {
|
|
|
62
62
|
|
|
63
63
|
if (result.success) {
|
|
64
64
|
if (result.backedUp) {
|
|
65
|
-
console.log(`\n✓ inbox-assistant skill updated
|
|
65
|
+
console.log(`\n✓ inbox-assistant skill updated`);
|
|
66
|
+
console.log(` Your previous version was saved to: SKILL.md.backup\n`);
|
|
66
67
|
} else {
|
|
67
68
|
console.log(`\n✓ inbox-assistant skill updated\n`);
|
|
68
69
|
}
|
|
70
|
+
} else if (result.reason === 'backup_failed') {
|
|
71
|
+
console.log(`\n⚠️ Could not backup existing skill - update skipped`);
|
|
72
|
+
console.log(` Run 'inbox install-skill' manually to update\n`);
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
// Up-to-date → silent (no message)
|
package/src/skill-installer.js
CHANGED
|
@@ -50,7 +50,7 @@ function getSkillSource(skillPath) {
|
|
|
50
50
|
/**
|
|
51
51
|
* Create a backup of the skill file
|
|
52
52
|
* @param {string} skillDir - Directory containing SKILL.md
|
|
53
|
-
* @returns {string|null
|
|
53
|
+
* @returns {{ success: boolean, tempPath: string|null, originalHash: string|null }} Backup result
|
|
54
54
|
*/
|
|
55
55
|
function createBackup(skillDir) {
|
|
56
56
|
const skillPath = path.join(skillDir, 'SKILL.md');
|
|
@@ -59,12 +59,21 @@ function createBackup(skillDir) {
|
|
|
59
59
|
|
|
60
60
|
try {
|
|
61
61
|
if (fs.existsSync(skillPath)) {
|
|
62
|
+
// Get hash of original before backup (for verification)
|
|
63
|
+
const originalHash = getFileHash(skillPath);
|
|
62
64
|
fs.copyFileSync(skillPath, backupPath);
|
|
63
|
-
|
|
65
|
+
|
|
66
|
+
// Verify backup was created with correct content
|
|
67
|
+
const backupHash = getFileHash(backupPath);
|
|
68
|
+
if (backupHash === originalHash) {
|
|
69
|
+
return { success: true, tempPath: backupPath, originalHash };
|
|
70
|
+
}
|
|
71
|
+
// Backup hash doesn't match - something went wrong
|
|
72
|
+
return { success: false, tempPath: null, originalHash: null };
|
|
64
73
|
}
|
|
65
|
-
return null;
|
|
74
|
+
return { success: false, tempPath: null, originalHash: null };
|
|
66
75
|
} catch {
|
|
67
|
-
return null;
|
|
76
|
+
return { success: false, tempPath: null, originalHash: null };
|
|
68
77
|
}
|
|
69
78
|
}
|
|
70
79
|
|
|
@@ -197,9 +206,19 @@ function installSkill(options = {}) {
|
|
|
197
206
|
// Backup if replacing existing (user may have modified)
|
|
198
207
|
let backedUp = false;
|
|
199
208
|
let backupPath = null;
|
|
200
|
-
let
|
|
209
|
+
let backupResult = null;
|
|
201
210
|
if (status.installed) {
|
|
202
|
-
|
|
211
|
+
backupResult = createBackup(SKILL_DEST_DIR);
|
|
212
|
+
|
|
213
|
+
if (!backupResult.success) {
|
|
214
|
+
// Backup failed - don't proceed without protecting user's data
|
|
215
|
+
return {
|
|
216
|
+
success: false,
|
|
217
|
+
action: 'skipped',
|
|
218
|
+
reason: 'backup_failed',
|
|
219
|
+
path: SKILL_DEST_DIR
|
|
220
|
+
};
|
|
221
|
+
}
|
|
203
222
|
|
|
204
223
|
// Remove existing for clean update
|
|
205
224
|
fs.rmSync(SKILL_DEST_DIR, { recursive: true, force: true });
|
|
@@ -209,9 +228,18 @@ function installSkill(options = {}) {
|
|
|
209
228
|
copyDirSync(SKILL_SOURCE_DIR, SKILL_DEST_DIR);
|
|
210
229
|
|
|
211
230
|
// Move backup into the new skill directory
|
|
212
|
-
if (
|
|
213
|
-
backupPath = moveBackupToSkillDir(
|
|
231
|
+
if (backupResult && backupResult.tempPath) {
|
|
232
|
+
backupPath = moveBackupToSkillDir(backupResult.tempPath, SKILL_DEST_DIR);
|
|
214
233
|
backedUp = !!backupPath;
|
|
234
|
+
|
|
235
|
+
// Verify the backup still has the original content after move
|
|
236
|
+
if (backedUp && backupResult.originalHash) {
|
|
237
|
+
const movedBackupHash = getFileHash(backupPath);
|
|
238
|
+
if (movedBackupHash !== backupResult.originalHash) {
|
|
239
|
+
// Something went wrong during move - backup is corrupted
|
|
240
|
+
backedUp = false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
215
243
|
}
|
|
216
244
|
|
|
217
245
|
const action = status.installed ? 'updated' : 'installed';
|