@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 +25 -2
- package/catalog.json +266 -1
- package/jobs/devops/db-backup-verify.yaml +37 -0
- package/jobs/devops/license-compliance-check.yaml +31 -0
- package/jobs/devops/orphaned-branch-cleanup.yaml +28 -0
- package/jobs/finance/saas-seat-audit.yaml +35 -0
- package/jobs/learning/course-progress-checkin.yaml +37 -0
- package/jobs/learning/daily-flashcard-review.yaml +29 -0
- package/jobs/learning/reading-list-nudge.yaml +25 -0
- package/jobs/productivity/focus-block-reminder.yaml +25 -0
- package/jobs/security/failed-login-watch.yaml +37 -0
- package/jobs/security/open-port-check.yaml +36 -0
- package/jobs/security/secrets-scan.yaml +28 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,7 +60,9 @@ crondex/
|
|
|
60
60
|
│ ├── productivity/
|
|
61
61
|
│ ├── personal/
|
|
62
62
|
│ ├── content/
|
|
63
|
-
│
|
|
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
|
-
|
|
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":
|
|
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.
|