@bobfrankston/gcards 0.1.2 → 0.1.5
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/settings.local.json +9 -1
- package/.gitattributes +1 -0
- package/.vscode/settings.json +22 -2
- package/README.md +229 -61
- package/gcards.ts +728 -193
- package/gfix.ts +1585 -0
- package/glib/gctypes.ts +132 -0
- package/glib/gmerge.ts +290 -0
- package/glib/gutils.ts +143 -0
- package/{cli.ts → glib/parsecli.ts} +45 -5
- package/{types.ts → glib/types.ts} +305 -305
- package/package.json +6 -4
|
@@ -13,7 +13,15 @@
|
|
|
13
13
|
"Bash(gh repo create:*)",
|
|
14
14
|
"Bash(gh repo edit:*)",
|
|
15
15
|
"Bash(git push:*)",
|
|
16
|
-
"Bash(npm view:*)"
|
|
16
|
+
"Bash(npm view:*)",
|
|
17
|
+
"Bash(npx tsc:*)",
|
|
18
|
+
"Bash(grep:*)",
|
|
19
|
+
"Bash(node gfix.ts:*)",
|
|
20
|
+
"Bash(node --import tsx gcards.ts:*)",
|
|
21
|
+
"Bash(npm install:*)",
|
|
22
|
+
"Bash(npx tsx:*)",
|
|
23
|
+
"WebFetch(domain:lh3.googleusercontent.com)",
|
|
24
|
+
"Bash(curl:*)"
|
|
17
25
|
],
|
|
18
26
|
"deny": [],
|
|
19
27
|
"ask": []
|
package/.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* text=auto eol=lf
|
package/.vscode/settings.json
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
-
{
|
|
2
|
-
"workbench.colorTheme": "Visual Studio 2019 Dark"
|
|
1
|
+
{
|
|
2
|
+
"workbench.colorTheme": "Visual Studio 2019 Dark",
|
|
3
|
+
"workbench.colorCustomizations": {
|
|
4
|
+
"activityBar.activeBackground": "#0ae04a",
|
|
5
|
+
"activityBar.background": "#0ae04a",
|
|
6
|
+
"activityBar.foreground": "#15202b",
|
|
7
|
+
"activityBar.inactiveForeground": "#15202b99",
|
|
8
|
+
"activityBarBadge.background": "#9266f8",
|
|
9
|
+
"activityBarBadge.foreground": "#15202b",
|
|
10
|
+
"commandCenter.border": "#e7e7e799",
|
|
11
|
+
"sash.hoverBorder": "#0ae04a",
|
|
12
|
+
"statusBar.background": "#08af3a",
|
|
13
|
+
"statusBar.foreground": "#e7e7e7",
|
|
14
|
+
"statusBarItem.hoverBackground": "#0ae04a",
|
|
15
|
+
"statusBarItem.remoteBackground": "#08af3a",
|
|
16
|
+
"statusBarItem.remoteForeground": "#e7e7e7",
|
|
17
|
+
"titleBar.activeBackground": "#08af3a",
|
|
18
|
+
"titleBar.activeForeground": "#e7e7e7",
|
|
19
|
+
"titleBar.inactiveBackground": "#08af3a99",
|
|
20
|
+
"titleBar.inactiveForeground": "#e7e7e799"
|
|
21
|
+
},
|
|
22
|
+
"peacock.color": "#08af3a"
|
|
3
23
|
}
|
package/README.md
CHANGED
|
@@ -1,61 +1,229 @@
|
|
|
1
|
-
# Google Contacts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
gcards
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
1
|
+
# gcards - Google Contacts Management Tool
|
|
2
|
+
|
|
3
|
+
A CLI tool for syncing, managing, and cleaning up Google Contacts.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @bobfrankston/gcards
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# First time - specify your Gmail username
|
|
15
|
+
gcards sync --user yourname
|
|
16
|
+
|
|
17
|
+
# Subsequent runs remember the user
|
|
18
|
+
gcards sync
|
|
19
|
+
gcards push
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Commands
|
|
23
|
+
|
|
24
|
+
### gcards
|
|
25
|
+
|
|
26
|
+
Main sync and push operations (Google API):
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
gcards sync [-u user] [-a] # Download contacts from Google
|
|
30
|
+
gcards push [-u user] [-a] # Push local changes to Google
|
|
31
|
+
gcards -a # Process all users
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### gfix
|
|
35
|
+
|
|
36
|
+
One-time cleanup operations (local files only):
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
gfix inspect -u user # Preview all standard fixes (no changes)
|
|
40
|
+
gfix apply -u user # Apply all standard fixes
|
|
41
|
+
|
|
42
|
+
gfix names -u user # Preview name parsing + dup phone/email removal
|
|
43
|
+
gfix names -u user --apply # Parse names, remove dup phones/emails
|
|
44
|
+
|
|
45
|
+
gfix birthday -u user # Preview birthday extraction
|
|
46
|
+
gfix birthday -u user --apply # Extract to CSV and remove
|
|
47
|
+
|
|
48
|
+
gfix undup -u user # Find duplicate contacts -> merger.json
|
|
49
|
+
gfix merge -u user # Merge duplicates locally (then use gcards push)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Google People API Fields
|
|
53
|
+
|
|
54
|
+
### Identity (Names)
|
|
55
|
+
|
|
56
|
+
| Field | Read/Write | Description |
|
|
57
|
+
|-------|------------|-------------|
|
|
58
|
+
| `names[].givenName` | Write | First name |
|
|
59
|
+
| `names[].familyName` | Write | Last name |
|
|
60
|
+
| `names[].middleName` | Write | Middle name |
|
|
61
|
+
| `names[].honorificPrefix` | Write | Prefix (Dr., Prof., etc.) |
|
|
62
|
+
| `names[].honorificSuffix` | Write | Suffix (Jr., III, PhD, etc.) |
|
|
63
|
+
| `names[].displayName` | Read-only | Full name (Google generates from above) |
|
|
64
|
+
| `names[].displayNameLastFirst` | Read-only | Sorting form "Last, First" (Google generates) |
|
|
65
|
+
| `names[].unstructuredName` | Write | Free-form name when structured fields not available |
|
|
66
|
+
|
|
67
|
+
### Sorting
|
|
68
|
+
|
|
69
|
+
| Field | Read/Write | Description |
|
|
70
|
+
|-------|------------|-------------|
|
|
71
|
+
| `fileAses[].value` | Write | Custom sort override (e.g., "Home Depot" instead of "The Home Depot") |
|
|
72
|
+
|
|
73
|
+
If blank, Google sorts by `familyName` automatically.
|
|
74
|
+
|
|
75
|
+
### Contact Information
|
|
76
|
+
|
|
77
|
+
| Field | Read/Write | Description |
|
|
78
|
+
|-------|------------|-------------|
|
|
79
|
+
| `emailAddresses[].value` | Write | Email address |
|
|
80
|
+
| `emailAddresses[].type` | Write | Label: home, work, other |
|
|
81
|
+
| `phoneNumbers[].value` | Write | Phone number |
|
|
82
|
+
| `phoneNumbers[].type` | Write | Label: mobile, work, home, main |
|
|
83
|
+
|
|
84
|
+
### Business
|
|
85
|
+
|
|
86
|
+
| Field | Read/Write | Description |
|
|
87
|
+
|-------|------------|-------------|
|
|
88
|
+
| `organizations[].name` | Write | Company name |
|
|
89
|
+
| `organizations[].title` | Write | Job title |
|
|
90
|
+
|
|
91
|
+
### Dates
|
|
92
|
+
|
|
93
|
+
| Field | Read/Write | Description |
|
|
94
|
+
|-------|------------|-------------|
|
|
95
|
+
| `birthdays[].date.year` | Write | Birth year (optional) |
|
|
96
|
+
| `birthdays[].date.month` | Write | Birth month (1-12) |
|
|
97
|
+
| `birthdays[].date.day` | Write | Birth day (1-31) |
|
|
98
|
+
|
|
99
|
+
### Management (Required for Updates)
|
|
100
|
+
|
|
101
|
+
| Field | Read/Write | Description |
|
|
102
|
+
|-------|------------|-------------|
|
|
103
|
+
| `resourceName` | Read-only | Unique contact ID (e.g., `people/c12345`) |
|
|
104
|
+
| `etag` | Read-only | Version key - must match when updating |
|
|
105
|
+
|
|
106
|
+
The `etag` prevents conflicts: if Google's version changed since you downloaded, your update will fail. Run `gcards sync` to get the latest version.
|
|
107
|
+
|
|
108
|
+
## Data Directory
|
|
109
|
+
|
|
110
|
+
- **Windows**: `%APPDATA%\gcards\` (app directory)
|
|
111
|
+
- **Linux/Mac**: `~/.config/gcards/` (app directory)
|
|
112
|
+
- **Development**: `./data/` symlink to app directory's `data/` folder
|
|
113
|
+
|
|
114
|
+
### Directory Structure
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
gcards/ # App directory (%APPDATA%\gcards or ~/.config/gcards)
|
|
118
|
+
config.json # Last user, settings
|
|
119
|
+
data/ # User data directory
|
|
120
|
+
<username>/
|
|
121
|
+
contacts/ # Active contacts (*.json)
|
|
122
|
+
deleted/ # Backup of deleted contact files
|
|
123
|
+
_delete/ # User requests to delete (move files here)
|
|
124
|
+
_add/ # User requests to add new contacts
|
|
125
|
+
fix-logs/ # Logs from gfix operations
|
|
126
|
+
index.json # Active contact index (hasPhoto, starred flags)
|
|
127
|
+
deleted.json # Deleted contacts index
|
|
128
|
+
notdeleted.json # Contacts skipped due to photo/starred
|
|
129
|
+
token.json # OAuth token (read-only)
|
|
130
|
+
token-write.json # OAuth token (read-write)
|
|
131
|
+
changes.log # Log from gfix names
|
|
132
|
+
birthdays.csv # Extracted birthdays
|
|
133
|
+
merger.json # Duplicates to merge (from gfix undup)
|
|
134
|
+
merged.json # Processed merges (history)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Fixes Applied by `gfix inspect/apply`
|
|
138
|
+
|
|
139
|
+
- Remove leading/trailing dashes, quotes, spaces from name fields
|
|
140
|
+
- Extract repeated honorific prefixes (Dr., Prof.) to `honorificPrefix`
|
|
141
|
+
- Remove duplicate phone numbers (normalized comparison)
|
|
142
|
+
- Remove duplicate email addresses (case-insensitive)
|
|
143
|
+
|
|
144
|
+
## Name Parsing (`gfix names`)
|
|
145
|
+
|
|
146
|
+
When a contact has a full name in `givenName` but no `familyName`:
|
|
147
|
+
- Parses "First Middle Last" into separate fields
|
|
148
|
+
- Updates `displayNameLastFirst` to proper "Last, First" format
|
|
149
|
+
- Cleans up `fileAses` entries (strips junk characters)
|
|
150
|
+
- Creates `changes.log` with all modifications
|
|
151
|
+
|
|
152
|
+
## Workflow
|
|
153
|
+
|
|
154
|
+
1. **Initial sync**: `gcards sync -u yourname`
|
|
155
|
+
2. **Review fixes**: `gfix inspect -u yourname`
|
|
156
|
+
3. **Apply fixes**: `gfix apply -u yourname`
|
|
157
|
+
4. **Parse names**: `gfix names -u yourname --apply`
|
|
158
|
+
5. **Push to Google**: `gcards push -u yourname`
|
|
159
|
+
|
|
160
|
+
## Deleting Contacts
|
|
161
|
+
|
|
162
|
+
Three ways to mark contacts for deletion:
|
|
163
|
+
|
|
164
|
+
1. **In index.json**: Add `"_delete": true` to an entry
|
|
165
|
+
2. **In contact JSON**: Add `"_delete": true` to the contact file
|
|
166
|
+
3. **Move to _delete/**: Move the contact file to the `_delete/` folder
|
|
167
|
+
|
|
168
|
+
Then run `gcards push` to apply deletions.
|
|
169
|
+
|
|
170
|
+
### Photo/Starred Protection
|
|
171
|
+
|
|
172
|
+
Contacts with photos or starred status are **not deleted** from Google to prevent data loss:
|
|
173
|
+
- Contacts with non-default photos: `_delete` set to `"photo"`
|
|
174
|
+
- Starred/favorite contacts: `_delete` set to `"starred"`
|
|
175
|
+
|
|
176
|
+
These remain in Google and `index.json` with the skip reason. Review `notdeleted.json` after push.
|
|
177
|
+
|
|
178
|
+
### Backup
|
|
179
|
+
|
|
180
|
+
All deleted contact files are moved to `deleted/` folder as backup (not permanently deleted).
|
|
181
|
+
Index entries move to `deleted.json`.
|
|
182
|
+
|
|
183
|
+
## Duplicate Contact Merging
|
|
184
|
+
|
|
185
|
+
1. **Find duplicates**: `gfix undup -u yourname`
|
|
186
|
+
- Creates `merger.json` with contacts having same name + overlapping email
|
|
187
|
+
2. **Review**: Edit `merger.json`:
|
|
188
|
+
- Remove entries you don't want to merge
|
|
189
|
+
- Add `"_delete": true` to delete all contacts in a group instead of merging
|
|
190
|
+
3. **Merge locally**: `gfix merge -u yourname`
|
|
191
|
+
- Updates target contact files with merged data
|
|
192
|
+
- Marks source contacts with `_delete: true`
|
|
193
|
+
- Moves processed entries to `merged.json`
|
|
194
|
+
4. **Push to Google**: `gcards push -u yourname`
|
|
195
|
+
5. **Resync**: `gcards sync -u yourname --full`
|
|
196
|
+
|
|
197
|
+
Example `merger.json`:
|
|
198
|
+
```json
|
|
199
|
+
[
|
|
200
|
+
{ "name": "John Smith", "emails": ["john@work.com"], "resourceNames": ["people/c1", "people/c2"] },
|
|
201
|
+
{ "name": "Spam Contact", "emails": ["spam@x.com"], "resourceNames": ["people/c3", "people/c4"], "_delete": true }
|
|
202
|
+
]
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## ESC Key
|
|
206
|
+
|
|
207
|
+
Press ESC during sync/push to stop safely after the current operation.
|
|
208
|
+
|
|
209
|
+
## Multiple Users
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# Process specific user
|
|
213
|
+
gcards sync -u alice
|
|
214
|
+
|
|
215
|
+
# Process all users
|
|
216
|
+
gcards sync -a
|
|
217
|
+
gcards push -a
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Setup
|
|
221
|
+
|
|
222
|
+
1. Create Google Cloud project
|
|
223
|
+
2. Enable People API
|
|
224
|
+
3. Create OAuth2 credentials (Desktop app type)
|
|
225
|
+
4. Download `credentials.json` to the gcards directory
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT
|