inboxd 1.0.9 → 1.0.11
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 +43 -10
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
|
|
|
@@ -194,13 +203,28 @@ function installSkill(options = {}) {
|
|
|
194
203
|
const skillsDir = path.dirname(SKILL_DEST_DIR);
|
|
195
204
|
fs.mkdirSync(skillsDir, { recursive: true });
|
|
196
205
|
|
|
197
|
-
// Backup if replacing existing
|
|
206
|
+
// Backup if replacing existing AND user modified it
|
|
207
|
+
// (Don't backup if it matches source - nothing worth preserving)
|
|
198
208
|
let backedUp = false;
|
|
199
209
|
let backupPath = null;
|
|
200
|
-
let
|
|
201
|
-
if (status.installed) {
|
|
202
|
-
|
|
210
|
+
let backupResult = null;
|
|
211
|
+
if (status.installed && updateInfo.hashMismatch) {
|
|
212
|
+
// Only backup if the installed version differs from our source
|
|
213
|
+
// This prevents overwriting a previous backup with an unmodified version
|
|
214
|
+
backupResult = createBackup(SKILL_DEST_DIR);
|
|
215
|
+
|
|
216
|
+
if (!backupResult.success) {
|
|
217
|
+
// Backup failed - don't proceed without protecting user's data
|
|
218
|
+
return {
|
|
219
|
+
success: false,
|
|
220
|
+
action: 'skipped',
|
|
221
|
+
reason: 'backup_failed',
|
|
222
|
+
path: SKILL_DEST_DIR
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
203
226
|
|
|
227
|
+
if (status.installed) {
|
|
204
228
|
// Remove existing for clean update
|
|
205
229
|
fs.rmSync(SKILL_DEST_DIR, { recursive: true, force: true });
|
|
206
230
|
}
|
|
@@ -209,9 +233,18 @@ function installSkill(options = {}) {
|
|
|
209
233
|
copyDirSync(SKILL_SOURCE_DIR, SKILL_DEST_DIR);
|
|
210
234
|
|
|
211
235
|
// Move backup into the new skill directory
|
|
212
|
-
if (
|
|
213
|
-
backupPath = moveBackupToSkillDir(
|
|
236
|
+
if (backupResult && backupResult.tempPath) {
|
|
237
|
+
backupPath = moveBackupToSkillDir(backupResult.tempPath, SKILL_DEST_DIR);
|
|
214
238
|
backedUp = !!backupPath;
|
|
239
|
+
|
|
240
|
+
// Verify the backup still has the original content after move
|
|
241
|
+
if (backedUp && backupResult.originalHash) {
|
|
242
|
+
const movedBackupHash = getFileHash(backupPath);
|
|
243
|
+
if (movedBackupHash !== backupResult.originalHash) {
|
|
244
|
+
// Something went wrong during move - backup is corrupted
|
|
245
|
+
backedUp = false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
215
248
|
}
|
|
216
249
|
|
|
217
250
|
const action = status.installed ? 'updated' : 'installed';
|