@wonsukchoi/crondex 0.2.0 → 0.3.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/README.md CHANGED
@@ -60,7 +60,9 @@ crondex/
60
60
  │ ├── productivity/
61
61
  │ ├── personal/
62
62
  │ ├── content/
63
- └── finance/
63
+ ├── finance/
64
+ │ ├── security/
65
+ │ └── learning/
64
66
  └── scripts/
65
67
  ├── build-catalog.js regenerates catalog.json from jobs/**/*.yaml
66
68
  └── validate-jobs.js validates every job against the schema
@@ -68,7 +70,7 @@ crondex/
68
70
 
69
71
  ## Available jobs
70
72
 
71
- 24 jobs across 5 categories. Full details (description, tags, variables)
73
+ 35 jobs across 7 categories. Full details (description, tags, variables)
72
74
  live in `catalog.json` and each job's YAML file — run `crondex list` or
73
75
  browse `jobs/<category>/` for the plain-language rundown of each.
74
76
 
@@ -87,6 +89,9 @@ browse `jobs/<category>/` for the plain-language rundown of each.
87
89
  | `docker-image-prune` | `0 4 * * *` | script |
88
90
  | `env-drift-check` | `0 8 * * *` | script |
89
91
  | `stale-dependency-pr-nudge` | `0 9 * * 1-5` | script |
92
+ | `db-backup-verify` | `0 5 * * *` | script |
93
+ | `license-compliance-check` | `0 8 * * 1` | script |
94
+ | `orphaned-branch-cleanup` | `0 9 * * 1` | script |
90
95
 
91
96
  **productivity**
92
97
 
@@ -95,6 +100,7 @@ browse `jobs/<category>/` for the plain-language rundown of each.
95
100
  | `daily-standup-summary` | `0 8 * * 1-5` | script + agent-prompt |
96
101
  | `inbox-triage` | `0 7,13 * * 1-5` | agent-prompt only |
97
102
  | `weekly-report` | `0 16 * * 5` | script + agent-prompt |
103
+ | `focus-block-reminder` | `0 9,14 * * 1-5` | script |
98
104
 
99
105
  **personal**
100
106
 
@@ -120,6 +126,23 @@ browse `jobs/<category>/` for the plain-language rundown of each.
120
126
  |---|---|---|
121
127
  | `subscription-audit` | `0 9 1 * *` | script |
122
128
  | `net-worth-snapshot` | `0 9 1 * *` | script |
129
+ | `saas-seat-audit` | `0 9 1 * *` | script |
130
+
131
+ **security**
132
+
133
+ | id | schedule | modes |
134
+ |---|---|---|
135
+ | `secrets-scan` | `0 3 * * *` | script |
136
+ | `open-port-check` | `0 */4 * * *` | script |
137
+ | `failed-login-watch` | `*/15 * * * *` | script |
138
+
139
+ **learning**
140
+
141
+ | id | schedule | modes |
142
+ |---|---|---|
143
+ | `daily-flashcard-review` | `0 8 * * *` | script |
144
+ | `reading-list-nudge` | `0 9 * * 6` | script |
145
+ | `course-progress-checkin` | `0 9 * * 1` | script |
123
146
 
124
147
  ## How a job works
125
148
 
package/catalog.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "generated_by": "scripts/build-catalog.js",
3
3
  "schema": "schema/job.schema.json",
4
- "count": 24,
4
+ "count": 35,
5
5
  "jobs": [
6
6
  {
7
7
  "id": "backup-reminder",
@@ -129,6 +129,54 @@
129
129
  ],
130
130
  "path": "jobs/devops/cost-alert.yaml"
131
131
  },
132
+ {
133
+ "id": "course-progress-checkin",
134
+ "version": 1,
135
+ "name": "Course Progress Check-in",
136
+ "description": "Compares your current chapter/lesson against a target date and tells you if you're on track or falling behind. Use this if you've ever signed up for a course and quietly stopped halfway through.",
137
+ "category": "learning",
138
+ "tags": [
139
+ "learning",
140
+ "goals",
141
+ "progress"
142
+ ],
143
+ "schedule": "0 9 * * 1",
144
+ "timezone": "America/Los_Angeles",
145
+ "runner": "shell",
146
+ "modes": [
147
+ "script"
148
+ ],
149
+ "compatible_agents": [
150
+ "generic",
151
+ "claude",
152
+ "codex"
153
+ ],
154
+ "path": "jobs/learning/course-progress-checkin.yaml"
155
+ },
156
+ {
157
+ "id": "daily-flashcard-review",
158
+ "version": 1,
159
+ "name": "Daily Flashcard Review",
160
+ "description": "Lists which flashcards are due for review today from a simple deck file. Use this if you're learning something with spaced repetition but don't want a whole separate app for it.",
161
+ "category": "learning",
162
+ "tags": [
163
+ "learning",
164
+ "flashcards",
165
+ "spaced-repetition"
166
+ ],
167
+ "schedule": "0 8 * * *",
168
+ "timezone": "America/Los_Angeles",
169
+ "runner": "shell",
170
+ "modes": [
171
+ "script"
172
+ ],
173
+ "compatible_agents": [
174
+ "generic",
175
+ "claude",
176
+ "codex"
177
+ ],
178
+ "path": "jobs/learning/daily-flashcard-review.yaml"
179
+ },
132
180
  {
133
181
  "id": "daily-standup-summary",
134
182
  "version": 1,
@@ -156,6 +204,30 @@
156
204
  ],
157
205
  "path": "jobs/productivity/daily-standup-summary.yaml"
158
206
  },
207
+ {
208
+ "id": "db-backup-verify",
209
+ "version": 1,
210
+ "name": "Database Backup Restore Verification",
211
+ "description": "Actually restores your latest database backup to a throwaway database and checks it's queryable. Use this if `backup-reminder` isn't enough — a backup file existing doesn't mean it's actually restorable.",
212
+ "category": "devops",
213
+ "tags": [
214
+ "database",
215
+ "backup",
216
+ "postgres"
217
+ ],
218
+ "schedule": "0 5 * * *",
219
+ "timezone": "UTC",
220
+ "runner": "shell",
221
+ "modes": [
222
+ "script"
223
+ ],
224
+ "compatible_agents": [
225
+ "generic",
226
+ "claude",
227
+ "codex"
228
+ ],
229
+ "path": "jobs/devops/db-backup-verify.yaml"
230
+ },
159
231
  {
160
232
  "id": "dependency-audit",
161
233
  "version": 1,
@@ -255,6 +327,54 @@
255
327
  ],
256
328
  "path": "jobs/devops/env-drift-check.yaml"
257
329
  },
330
+ {
331
+ "id": "failed-login-watch",
332
+ "version": 1,
333
+ "name": "Failed Login Watch",
334
+ "description": "Watches an auth log for a burst of failed login attempts and alerts if it crosses a threshold. Use this if you run a server exposed to SSH and want an early warning for brute-force attempts.",
335
+ "category": "security",
336
+ "tags": [
337
+ "security",
338
+ "auth",
339
+ "monitoring"
340
+ ],
341
+ "schedule": "*/15 * * * *",
342
+ "timezone": "UTC",
343
+ "runner": "shell",
344
+ "modes": [
345
+ "script"
346
+ ],
347
+ "compatible_agents": [
348
+ "generic",
349
+ "claude",
350
+ "codex"
351
+ ],
352
+ "path": "jobs/security/failed-login-watch.yaml"
353
+ },
354
+ {
355
+ "id": "focus-block-reminder",
356
+ "version": 1,
357
+ "name": "Focus Block Reminder",
358
+ "description": "Nudges you to start a deep-work block at set times. Use this if your day fills up with reactive work and real focus time only happens by accident.",
359
+ "category": "productivity",
360
+ "tags": [
361
+ "focus",
362
+ "productivity",
363
+ "deep-work"
364
+ ],
365
+ "schedule": "0 9,14 * * 1-5",
366
+ "timezone": "America/Los_Angeles",
367
+ "runner": "shell",
368
+ "modes": [
369
+ "script"
370
+ ],
371
+ "compatible_agents": [
372
+ "generic",
373
+ "claude",
374
+ "codex"
375
+ ],
376
+ "path": "jobs/productivity/focus-block-reminder.yaml"
377
+ },
258
378
  {
259
379
  "id": "habit-checkin",
260
380
  "version": 1,
@@ -305,6 +425,30 @@
305
425
  ],
306
426
  "path": "jobs/productivity/inbox-triage.yaml"
307
427
  },
428
+ {
429
+ "id": "license-compliance-check",
430
+ "version": 1,
431
+ "name": "Dependency License Compliance Check",
432
+ "description": "Scans your dependencies for licenses you've flagged as risky (like GPL or AGPL). Use this if you've ever shipped something and only later found out a dependency's license doesn't play well with your product.",
433
+ "category": "devops",
434
+ "tags": [
435
+ "license",
436
+ "compliance",
437
+ "dependencies"
438
+ ],
439
+ "schedule": "0 8 * * 1",
440
+ "timezone": "UTC",
441
+ "runner": "shell",
442
+ "modes": [
443
+ "script"
444
+ ],
445
+ "compatible_agents": [
446
+ "generic",
447
+ "claude",
448
+ "codex"
449
+ ],
450
+ "path": "jobs/devops/license-compliance-check.yaml"
451
+ },
308
452
  {
309
453
  "id": "log-cleanup",
310
454
  "version": 1,
@@ -379,6 +523,78 @@
379
523
  ],
380
524
  "path": "jobs/finance/net-worth-snapshot.yaml"
381
525
  },
526
+ {
527
+ "id": "open-port-check",
528
+ "version": 1,
529
+ "name": "Expected Open Ports Check",
530
+ "description": "Checks that the ports you expect to be open on a host actually are, and flags any that aren't. Use this if a service has ever silently stopped listening and you found out from a user instead of a monitor.",
531
+ "category": "security",
532
+ "tags": [
533
+ "security",
534
+ "network",
535
+ "ports"
536
+ ],
537
+ "schedule": "0 */4 * * *",
538
+ "timezone": "UTC",
539
+ "runner": "shell",
540
+ "modes": [
541
+ "script"
542
+ ],
543
+ "compatible_agents": [
544
+ "generic",
545
+ "claude",
546
+ "codex"
547
+ ],
548
+ "path": "jobs/security/open-port-check.yaml"
549
+ },
550
+ {
551
+ "id": "orphaned-branch-cleanup",
552
+ "version": 1,
553
+ "name": "Orphaned Branch Cleanup",
554
+ "description": "Lists remote branches already merged into main that nobody's deleted yet. Use this if your branch list has become a graveyard nobody wants to clean up.",
555
+ "category": "devops",
556
+ "tags": [
557
+ "git",
558
+ "cleanup",
559
+ "branches"
560
+ ],
561
+ "schedule": "0 9 * * 1",
562
+ "timezone": "UTC",
563
+ "runner": "shell",
564
+ "modes": [
565
+ "script"
566
+ ],
567
+ "compatible_agents": [
568
+ "generic",
569
+ "claude",
570
+ "codex"
571
+ ],
572
+ "path": "jobs/devops/orphaned-branch-cleanup.yaml"
573
+ },
574
+ {
575
+ "id": "reading-list-nudge",
576
+ "version": 1,
577
+ "name": "Reading List Nudge",
578
+ "description": "Reminds you what's next on your reading list. Use this if you save articles/books to \"read later\" and later never comes.",
579
+ "category": "learning",
580
+ "tags": [
581
+ "learning",
582
+ "reading",
583
+ "reminder"
584
+ ],
585
+ "schedule": "0 9 * * 6",
586
+ "timezone": "America/Los_Angeles",
587
+ "runner": "shell",
588
+ "modes": [
589
+ "script"
590
+ ],
591
+ "compatible_agents": [
592
+ "generic",
593
+ "claude",
594
+ "codex"
595
+ ],
596
+ "path": "jobs/learning/reading-list-nudge.yaml"
597
+ },
382
598
  {
383
599
  "id": "repo-health-check",
384
600
  "version": 1,
@@ -406,6 +622,31 @@
406
622
  ],
407
623
  "path": "jobs/devops/repo-health-check.yaml"
408
624
  },
625
+ {
626
+ "id": "saas-seat-audit",
627
+ "version": 1,
628
+ "name": "Team SaaS Seat Audit",
629
+ "description": "Flags team SaaS seats (Slack, Notion, GitHub, etc.) nobody's actually used in a while. Use this if you suspect you're paying for seats of people who left or tools nobody opens anymore.",
630
+ "category": "finance",
631
+ "tags": [
632
+ "saas",
633
+ "seats",
634
+ "team",
635
+ "budget"
636
+ ],
637
+ "schedule": "0 9 1 * *",
638
+ "timezone": "UTC",
639
+ "runner": "shell",
640
+ "modes": [
641
+ "script"
642
+ ],
643
+ "compatible_agents": [
644
+ "generic",
645
+ "claude",
646
+ "codex"
647
+ ],
648
+ "path": "jobs/finance/saas-seat-audit.yaml"
649
+ },
409
650
  {
410
651
  "id": "screen-time-check",
411
652
  "version": 1,
@@ -432,6 +673,30 @@
432
673
  ],
433
674
  "path": "jobs/personal/screen-time-check.yaml"
434
675
  },
676
+ {
677
+ "id": "secrets-scan",
678
+ "version": 1,
679
+ "name": "Committed Secrets Scan",
680
+ "description": "Scans a repo for accidentally committed API keys, tokens, and private keys. Use this if you want a safety net for the \"oops, committed a key\" mistake everyone makes eventually.",
681
+ "category": "security",
682
+ "tags": [
683
+ "security",
684
+ "secrets",
685
+ "scanning"
686
+ ],
687
+ "schedule": "0 3 * * *",
688
+ "timezone": "UTC",
689
+ "runner": "shell",
690
+ "modes": [
691
+ "script"
692
+ ],
693
+ "compatible_agents": [
694
+ "generic",
695
+ "claude",
696
+ "codex"
697
+ ],
698
+ "path": "jobs/security/secrets-scan.yaml"
699
+ },
435
700
  {
436
701
  "id": "social-mentions-watch",
437
702
  "version": 1,
@@ -0,0 +1,37 @@
1
+ id: db-backup-verify
2
+ version: 1
3
+ name: Database Backup Restore Verification
4
+ description: >
5
+ Actually restores your latest database backup to a throwaway database
6
+ and checks it's queryable. Use this if `backup-reminder` isn't enough —
7
+ a backup file existing doesn't mean it's actually restorable.
8
+ category: devops
9
+ tags: [database, backup, postgres]
10
+ schedule: "0 5 * * *"
11
+ timezone: "UTC"
12
+ runner: shell
13
+ command: >
14
+ dump={{dump_path}}; scratch_db={{scratch_db_name}};
15
+ [ -f "$dump" ] || { echo "ERROR: $dump not found"; exit 1; };
16
+ command -v pg_restore >/dev/null 2>&1 || { echo "ERROR: pg_restore not installed"; exit 1; };
17
+ dropdb --if-exists "$scratch_db" 2>/dev/null;
18
+ createdb "$scratch_db" || { echo "ERROR: could not create scratch db $scratch_db"; exit 1; };
19
+ pg_restore -d "$scratch_db" "$dump" >/dev/null 2>&1;
20
+ count=$(psql -d "$scratch_db" -tAc "SELECT count(*) FROM {{checkpoint_table}}" 2>/dev/null);
21
+ dropdb --if-exists "$scratch_db" 2>/dev/null;
22
+ if [ -z "$count" ]; then echo "FAILED: restore or query failed on $dump"; exit 1;
23
+ else echo "OK: restored $dump, $checkpoint_table has $count rows"; fi
24
+ variables:
25
+ dump_path:
26
+ default: "./backups/latest.dump"
27
+ description: Path to the pg_dump/pg_restore-format backup file.
28
+ scratch_db_name:
29
+ default: "backup_verify_scratch"
30
+ description: Throwaway database name used for the test restore (created and dropped each run).
31
+ checkpoint_table:
32
+ default: "users"
33
+ description: A table expected to have rows — used to confirm the restore actually worked.
34
+ compatible_agents: [generic, claude, codex]
35
+ notes: >
36
+ Postgres-specific (pg_restore/psql/createdb/dropdb) — adapt the command
37
+ for other database engines. Needs CREATE/DROP DATABASE privileges.
@@ -0,0 +1,31 @@
1
+ id: license-compliance-check
2
+ version: 1
3
+ name: Dependency License Compliance Check
4
+ description: >
5
+ Scans your dependencies for licenses you've flagged as risky (like GPL
6
+ or AGPL). Use this if you've ever shipped something and only later
7
+ found out a dependency's license doesn't play well with your product.
8
+ category: devops
9
+ tags: [license, compliance, dependencies]
10
+ schedule: "0 8 * * 1"
11
+ timezone: "UTC"
12
+ runner: shell
13
+ command: >
14
+ cd {{repo_path}} &&
15
+ if [ -f package.json ]; then
16
+ npx --yes license-checker --summary --excludePrivatePackages 2>/dev/null | grep -iE "{{flagged_licenses}}" && echo "^ flagged licenses found above" || echo "OK: no flagged licenses found";
17
+ else
18
+ echo "no package.json found — this job only supports npm/Node projects currently";
19
+ fi
20
+ variables:
21
+ repo_path:
22
+ default: "."
23
+ description: Path to the repo to check.
24
+ flagged_licenses:
25
+ default: "GPL|AGPL"
26
+ description: Regex alternation of license names to flag.
27
+ compatible_agents: [generic, claude, codex]
28
+ notes: >
29
+ npm/Node projects only (uses `npx license-checker`, downloads on first
30
+ run). For other ecosystems, swap in the equivalent tool
31
+ (pip-licenses, cargo-license, etc.) and adjust the command.
@@ -0,0 +1,28 @@
1
+ id: orphaned-branch-cleanup
2
+ version: 1
3
+ name: Orphaned Branch Cleanup
4
+ description: >
5
+ Lists remote branches already merged into main that nobody's deleted
6
+ yet. Use this if your branch list has become a graveyard nobody wants
7
+ to clean up.
8
+ category: devops
9
+ tags: [git, cleanup, branches]
10
+ schedule: "0 9 * * 1"
11
+ timezone: "UTC"
12
+ runner: shell
13
+ command: >
14
+ cd {{repo_path}} && git fetch --prune >/dev/null 2>&1;
15
+ merged=$(git branch -r --merged {{base_branch}} | grep -v -E "(\*|{{base_branch}}$)" | sed 's#origin/##');
16
+ if [ -z "$merged" ]; then echo "OK: no merged branches to clean up";
17
+ else echo "Merged into {{base_branch}}, safe to delete:"; echo "$merged"; fi
18
+ variables:
19
+ repo_path:
20
+ default: "."
21
+ description: Path to the repo to check.
22
+ base_branch:
23
+ default: "main"
24
+ description: Branch to check merges against.
25
+ compatible_agents: [generic, claude, codex]
26
+ notes: >
27
+ List-only by design — never deletes anything. Review the list, then
28
+ `git push origin --delete <branch>` yourself for ones you actually want gone.
@@ -0,0 +1,35 @@
1
+ id: saas-seat-audit
2
+ version: 1
3
+ name: Team SaaS Seat Audit
4
+ description: >
5
+ Flags team SaaS seats (Slack, Notion, GitHub, etc.) nobody's actually
6
+ used in a while. Use this if you suspect you're paying for seats of
7
+ people who left or tools nobody opens anymore.
8
+ category: finance
9
+ tags: [saas, seats, team, budget]
10
+ schedule: "0 9 1 * *"
11
+ timezone: "UTC"
12
+ runner: shell
13
+ command: >
14
+ file={{seats_file}}; unused_days={{unused_days}};
15
+ [ -f "$file" ] || { echo "ERROR: $file not found"; exit 1; };
16
+ today=$(date +%s);
17
+ tail -n +2 "$file" | while IFS=, read -r tool owner cost last_active; do
18
+ last_epoch=$(date -j -f %Y-%m-%d "$last_active" +%s 2>/dev/null || date -d "$last_active" +%s);
19
+ days=$(( (today-last_epoch)/86400 ));
20
+ if [ "$days" -gt "$unused_days" ]; then echo "UNUSED SEAT: $tool - $owner (\$$cost/mo) - inactive $days days"; fi
21
+ done
22
+ variables:
23
+ seats_file:
24
+ default: "./saas-seats.csv"
25
+ description: >
26
+ CSV with header "tool,seat_owner,monthly_cost,last_active_date"
27
+ (YYYY-MM-DD), one seat per row.
28
+ unused_days:
29
+ default: 30
30
+ description: Flag a seat if it hasn't been active in this many days.
31
+ compatible_agents: [generic, claude, codex]
32
+ notes: >
33
+ Zero-token, deterministic. You maintain saas-seats.csv yourself (or
34
+ export last-active data from your SSO/IdP if it tracks that) — this job
35
+ only reads what's in the file, doesn't pull from any SaaS API.
@@ -0,0 +1,37 @@
1
+ id: course-progress-checkin
2
+ version: 1
3
+ name: Course Progress Check-in
4
+ description: >
5
+ Compares your current chapter/lesson against a target date and tells
6
+ you if you're on track or falling behind. Use this if you've ever
7
+ signed up for a course and quietly stopped halfway through.
8
+ category: learning
9
+ tags: [learning, goals, progress]
10
+ schedule: "0 9 * * 1"
11
+ timezone: "America/Los_Angeles"
12
+ runner: shell
13
+ command: >
14
+ current={{current_chapter}}; target={{target_chapter}}; target_date={{target_date}};
15
+ today_epoch=$(date +%s);
16
+ target_epoch=$(date -j -f %Y-%m-%d "$target_date" +%s 2>/dev/null || date -d "$target_date" +%s);
17
+ days_left=$(( (target_epoch-today_epoch)/86400 ));
18
+ chapters_left=$(( target - current ));
19
+ if [ "$chapters_left" -le 0 ]; then echo "OK: at or past target chapter $target already.";
20
+ elif [ "$days_left" -le 0 ]; then echo "OVERDUE: target date was $target_date, still $chapters_left chapter(s) to go.";
21
+ elif [ "$chapters_left" -gt "$days_left" ]; then echo "BEHIND: $chapters_left chapter(s) left, only $days_left day(s) until $target_date.";
22
+ else echo "ON TRACK: $chapters_left chapter(s) left, $days_left day(s) until $target_date."; fi
23
+ variables:
24
+ current_chapter:
25
+ default: 1
26
+ description: Chapter/lesson number you're currently on — update this yourself as you progress.
27
+ target_chapter:
28
+ default: 10
29
+ description: Chapter/lesson number you want to finish by target_date.
30
+ target_date:
31
+ default: "2026-12-31"
32
+ description: Date (YYYY-MM-DD) you want to hit target_chapter by.
33
+ compatible_agents: [generic, claude, codex]
34
+ notes: >
35
+ Zero-token. "Behind/on track" is a rough linear heuristic (chapters-left
36
+ vs. days-left), not a real study-pace model. You update current_chapter
37
+ yourself as you go.
@@ -0,0 +1,29 @@
1
+ id: daily-flashcard-review
2
+ version: 1
3
+ name: Daily Flashcard Review
4
+ description: >
5
+ Lists which flashcards are due for review today from a simple deck
6
+ file. Use this if you're learning something with spaced repetition but
7
+ don't want a whole separate app for it.
8
+ category: learning
9
+ tags: [learning, flashcards, spaced-repetition]
10
+ schedule: "0 8 * * *"
11
+ timezone: "America/Los_Angeles"
12
+ runner: shell
13
+ command: >
14
+ deck={{deck_path}};
15
+ [ -f "$deck" ] || { echo "ERROR: $deck not found"; exit 1; };
16
+ today=$(date +%F);
17
+ due=$(awk -F, -v today="$today" 'NR>1 && $3<=today {print $1" -> "$2" (due "$3")"}' "$deck");
18
+ if [ -z "$due" ]; then echo "No cards due today.";
19
+ else echo "Cards due today:"; echo "$due"; fi
20
+ variables:
21
+ deck_path:
22
+ default: "./flashcards.csv"
23
+ description: CSV with header "front,back,next_review_date" (YYYY-MM-DD).
24
+ compatible_agents: [generic, claude, codex]
25
+ notes: >
26
+ Zero-token. You maintain flashcards.csv and update each card's
27
+ next_review_date yourself after reviewing — this is simple date-based
28
+ scheduling, not full SM-2 spaced repetition. This job only tells you
29
+ what's due, it doesn't reschedule cards for you.
@@ -0,0 +1,25 @@
1
+ id: reading-list-nudge
2
+ version: 1
3
+ name: Reading List Nudge
4
+ description: >
5
+ Reminds you what's next on your reading list. Use this if you save
6
+ articles/books to "read later" and later never comes.
7
+ category: learning
8
+ tags: [learning, reading, reminder]
9
+ schedule: "0 9 * * 6"
10
+ timezone: "America/Los_Angeles"
11
+ runner: shell
12
+ command: >
13
+ file={{reading_list_path}};
14
+ [ -f "$file" ] || { echo "ERROR: $file not found"; exit 1; };
15
+ next=$(head -n 1 "$file");
16
+ if [ -z "$next" ]; then echo "Reading list is empty — nice work.";
17
+ else echo "Next up: $next"; fi
18
+ variables:
19
+ reading_list_path:
20
+ default: "./reading-list.txt"
21
+ description: Plain text file, one item per line — the top line is treated as "next."
22
+ compatible_agents: [generic, claude, codex]
23
+ notes: >
24
+ Zero-token. Suggests the top line only — remove an item from
25
+ reading-list.txt yourself once you've read it, or reorder to reprioritize.
@@ -0,0 +1,25 @@
1
+ id: focus-block-reminder
2
+ version: 1
3
+ name: Focus Block Reminder
4
+ description: >
5
+ Nudges you to start a deep-work block at set times. Use this if your
6
+ day fills up with reactive work and real focus time only happens by
7
+ accident.
8
+ category: productivity
9
+ tags: [focus, productivity, deep-work]
10
+ schedule: "0 9,14 * * 1-5"
11
+ timezone: "America/Los_Angeles"
12
+ runner: shell
13
+ command: >
14
+ echo "Focus block: {{block_minutes}} minutes on \"{{focus_task}}\" starting now. Silence notifications."
15
+ variables:
16
+ focus_task:
17
+ default: "deep work"
18
+ description: What to focus on for this block.
19
+ block_minutes:
20
+ default: 50
21
+ description: Length of the focus block in minutes.
22
+ compatible_agents: [generic, claude, codex]
23
+ notes: >
24
+ Zero-token. Purely a nudge — doesn't block notifications/apps itself;
25
+ pair with your OS's focus/DND mode if you want it enforced.
@@ -0,0 +1,37 @@
1
+ id: failed-login-watch
2
+ version: 1
3
+ name: Failed Login Watch
4
+ description: >
5
+ Watches an auth log for a burst of failed login attempts and alerts if
6
+ it crosses a threshold. Use this if you run a server exposed to SSH and
7
+ want an early warning for brute-force attempts.
8
+ category: security
9
+ tags: [security, auth, monitoring]
10
+ schedule: "*/15 * * * *"
11
+ timezone: "UTC"
12
+ runner: shell
13
+ command: >
14
+ log={{auth_log_path}}; state={{state_file}}; threshold={{fail_threshold}};
15
+ [ -f "$log" ] || { echo "ERROR: $log not found"; exit 1; };
16
+ total=$(grep -c "Failed password" "$log");
17
+ last=$(cat "$state" 2>/dev/null || echo 0);
18
+ new=$(( total - last ));
19
+ [ "$new" -lt 0 ] && new=0;
20
+ echo "$total" > "$state";
21
+ if [ "$new" -ge "$threshold" ]; then echo "WARNING: $new new failed login attempt(s) since last check (threshold $threshold)"; exit 1;
22
+ else echo "OK: $new new failed login attempt(s) since last check"; fi
23
+ variables:
24
+ auth_log_path:
25
+ default: "/var/log/auth.log"
26
+ description: Path to the auth log to watch (Linux-style).
27
+ state_file:
28
+ default: "./.failed-login-watch-state"
29
+ description: File this job uses to remember the last count seen, so it only alerts on new failures.
30
+ fail_threshold:
31
+ default: 5
32
+ description: Alert if this many new failed attempts appear since the last check.
33
+ compatible_agents: [generic, claude, codex]
34
+ notes: >
35
+ Linux-oriented (/var/log/auth.log). On macOS, use
36
+ `log show --predicate 'eventMessage contains "authentication failure"'`
37
+ instead and adapt the command — this one won't work there as-is.
@@ -0,0 +1,36 @@
1
+ id: open-port-check
2
+ version: 1
3
+ name: Expected Open Ports Check
4
+ description: >
5
+ Checks that the ports you expect to be open on a host actually are, and
6
+ flags any that aren't. Use this if a service has ever silently stopped
7
+ listening and you found out from a user instead of a monitor.
8
+ category: security
9
+ tags: [security, network, ports]
10
+ schedule: "0 */4 * * *"
11
+ timezone: "UTC"
12
+ runner: shell
13
+ command: >
14
+ host={{host}}; fail=0;
15
+ for p in $(echo "{{expected_open_ports}}" | tr ',' ' '); do
16
+ if command -v nc >/dev/null 2>&1; then
17
+ nc -z -w2 "$host" "$p" 2>/dev/null && echo "OK: $host:$p open" || { echo "MISSING: $host:$p expected open but isn't"; fail=1; };
18
+ else
19
+ (echo > /dev/tcp/$host/$p) 2>/dev/null && echo "OK: $host:$p open" || { echo "MISSING: $host:$p expected open but isn't"; fail=1; };
20
+ fi
21
+ done;
22
+ exit $fail
23
+ variables:
24
+ host:
25
+ default: "localhost"
26
+ description: Host to check.
27
+ expected_open_ports:
28
+ default: "22,80,443"
29
+ description: Comma-separated list of ports that should be open.
30
+ compatible_agents: [generic, claude, codex]
31
+ notes: >
32
+ Zero-token. Checks only the ports you list — not a full port scan. Use
33
+ nmap for broader unexpected-open-port discovery. The nc branch (used on
34
+ virtually every system) is the tested path; the /dev/tcp fallback only
35
+ matters if nc is missing and requires bash specifically — it won't work
36
+ under dash/POSIX sh.
@@ -0,0 +1,28 @@
1
+ id: secrets-scan
2
+ version: 1
3
+ name: Committed Secrets Scan
4
+ description: >
5
+ Scans a repo for accidentally committed API keys, tokens, and private
6
+ keys. Use this if you want a safety net for the "oops, committed a key"
7
+ mistake everyone makes eventually.
8
+ category: security
9
+ tags: [security, secrets, scanning]
10
+ schedule: "0 3 * * *"
11
+ timezone: "UTC"
12
+ runner: shell
13
+ command: >
14
+ cd {{repo_path}} &&
15
+ if command -v gitleaks >/dev/null 2>&1; then
16
+ gitleaks detect --source . --no-git -v;
17
+ else
18
+ echo "gitleaks not installed, falling back to basic pattern matching";
19
+ grep -rnE "(AKIA[0-9A-Z]{16}|-----BEGIN (RSA|EC|DSA|OPENSSH) PRIVATE KEY-----|api[_-]?key['\"]?[[:space:]]*[:=][[:space:]]*['\"][A-Za-z0-9_-]{16,})" --exclude-dir=.git --exclude-dir=node_modules . || echo "no matches found";
20
+ fi
21
+ variables:
22
+ repo_path:
23
+ default: "."
24
+ description: Path to the repo to scan.
25
+ compatible_agents: [generic, claude, codex]
26
+ notes: >
27
+ Zero-token. Prefers `gitleaks` if installed (far more accurate); the
28
+ fallback regex is best-effort and will miss many patterns or false-positive.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wonsukchoi/crondex",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A public directory of pre-made, agent-editable cron jobs.",
5
5
  "type": "module",
6
6
  "license": "MIT",