@wonsukchoi/crondex 0.6.0 → 0.8.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.
Files changed (75) hide show
  1. package/README.md +52 -128
  2. package/bin/crondex.js +85 -0
  3. package/catalog.json +1891 -153
  4. package/jobs/construction/change-order-approval-reminder.yaml +31 -0
  5. package/jobs/construction/inspection-deadline-reminder.yaml +31 -0
  6. package/jobs/construction/permit-expiry-check.yaml +31 -0
  7. package/jobs/content/hreflang-audit.yaml +25 -0
  8. package/jobs/content/image-alt-text-audit.yaml +21 -0
  9. package/jobs/content/page-speed-regression-watch.yaml +26 -0
  10. package/jobs/creator/content-calendar-gap-check.yaml +31 -0
  11. package/jobs/creator/crosspost-lag-check.yaml +35 -0
  12. package/jobs/creator/engagement-drop-watch.yaml +36 -0
  13. package/jobs/devops/cdn-cache-purge-reminder.yaml +27 -0
  14. package/jobs/devops/ci-build-time-regression-watch.yaml +35 -0
  15. package/jobs/devops/k8s-pod-restart-watch.yaml +26 -0
  16. package/jobs/devops/log-forwarding-health-check.yaml +32 -0
  17. package/jobs/devops/staging-prod-config-diff.yaml +32 -0
  18. package/jobs/education/assignment-grading-backlog-check.yaml +23 -0
  19. package/jobs/education/attendance-anomaly-check.yaml +33 -0
  20. package/jobs/education/report-card-deadline-reminder.yaml +27 -0
  21. package/jobs/events/event-budget-overrun-check.yaml +31 -0
  22. package/jobs/events/rsvp-followup-reminder.yaml +30 -0
  23. package/jobs/events/vendor-payment-deadline-reminder.yaml +31 -0
  24. package/jobs/finance/payroll-run-reminder.yaml +27 -0
  25. package/jobs/finance/recurring-payment-failure-check.yaml +23 -0
  26. package/jobs/fleet/fuel-card-anomaly-check.yaml +37 -0
  27. package/jobs/fleet/vehicle-maintenance-due-check.yaml +36 -0
  28. package/jobs/fleet/vehicle-registration-expiry-check.yaml +31 -0
  29. package/jobs/hiring/candidate-followup-nudge.yaml +23 -0
  30. package/jobs/hiring/offer-expiry-reminder.yaml +31 -0
  31. package/jobs/hiring/open-req-aging-check.yaml +31 -0
  32. package/jobs/home/warranty-expiry-reminder.yaml +31 -0
  33. package/jobs/hospitality/food-safety-temp-log-check.yaml +36 -0
  34. package/jobs/hospitality/health-inspection-due-reminder.yaml +32 -0
  35. package/jobs/hospitality/reservation-noshow-check.yaml +20 -0
  36. package/jobs/inventory/expiring-perishable-stock-check.yaml +24 -0
  37. package/jobs/inventory/inventory-count-discrepancy-check.yaml +38 -0
  38. package/jobs/inventory/low-stock-alert.yaml +20 -0
  39. package/jobs/investing/crypto-price-alert.yaml +33 -0
  40. package/jobs/investing/dividend-payment-tracker.yaml +34 -0
  41. package/jobs/investing/portfolio-rebalance-check.yaml +32 -0
  42. package/jobs/legal/contract-renewal-reminder.yaml +31 -0
  43. package/jobs/legal/nda-expiry-check.yaml +31 -0
  44. package/jobs/legal/trademark-renewal-reminder.yaml +30 -0
  45. package/jobs/logistics/customs-clearance-check.yaml +34 -0
  46. package/jobs/logistics/freight-rate-spike-watch.yaml +38 -0
  47. package/jobs/logistics/shipment-delay-watch.yaml +38 -0
  48. package/jobs/manufacturing/preventive-maintenance-due-check.yaml +31 -0
  49. package/jobs/manufacturing/production-line-downtime-watch.yaml +34 -0
  50. package/jobs/manufacturing/quality-defect-rate-check.yaml +20 -0
  51. package/jobs/marketing/ad-spend-pacing-check.yaml +37 -0
  52. package/jobs/marketing/email-deliverability-check.yaml +37 -0
  53. package/jobs/marketing/seo-rank-drop-watch.yaml +38 -0
  54. package/jobs/nonprofit/grant-deadline-reminder.yaml +44 -0
  55. package/jobs/nonprofit/lapsed-donor-check.yaml +26 -0
  56. package/jobs/nonprofit/volunteer-hours-shortfall-check.yaml +30 -0
  57. package/jobs/podcast/episode-publish-gap-check.yaml +29 -0
  58. package/jobs/podcast/guest-followup-reminder.yaml +31 -0
  59. package/jobs/podcast/sponsor-deliverable-tracker.yaml +31 -0
  60. package/jobs/productivity/timezone-meeting-conflict-check.yaml +31 -0
  61. package/jobs/realestate/lease-expiry-reminder.yaml +30 -0
  62. package/jobs/realestate/rent-payment-late-check.yaml +23 -0
  63. package/jobs/realestate/rental-vacancy-check.yaml +30 -0
  64. package/jobs/sales/lead-response-time-check.yaml +23 -0
  65. package/jobs/sales/quota-attainment-check.yaml +33 -0
  66. package/jobs/sales/stale-deal-nudge.yaml +35 -0
  67. package/jobs/security/2fa-enrollment-audit.yaml +24 -0
  68. package/jobs/security/dependabot-alert-digest.yaml +30 -0
  69. package/jobs/security/expiring-api-key-rotation.yaml +31 -0
  70. package/jobs/security/tls-cipher-weak-check.yaml +31 -0
  71. package/jobs/security/waf-rule-change-watch.yaml +38 -0
  72. package/jobs/support/csat-score-watch.yaml +32 -0
  73. package/jobs/support/sla-breach-warning.yaml +31 -0
  74. package/jobs/support/ticket-backlog-aging-check.yaml +30 -0
  75. package/package.json +1 -1
package/README.md CHANGED
@@ -27,8 +27,16 @@ npx @wonsukchoi/crondex list # browse everything
27
27
  npx @wonsukchoi/crondex list --category devops # filter by category or --tag
28
28
  npx @wonsukchoi/crondex show backup-reminder # print one job's YAML
29
29
  npx @wonsukchoi/crondex add backup-reminder --dest ./cron/backup-reminder.yaml
30
+ npx @wonsukchoi/crondex recommend "warn me before my SSL cert expires"
30
31
  ```
31
32
 
33
+ `recommend` takes a free-text description of what you want and ranks the
34
+ catalog against it (weighted keyword match over tags/name/id/category/
35
+ description, zero tokens, no network call) — handy for an agent that just
36
+ got asked "can you set up something for X" and needs to check whether a
37
+ ready-made job already covers it before writing one from scratch. Add
38
+ `--limit <n>` to control how many results come back (default 5).
39
+
32
40
  Or install once and drop the scope prefix on every call:
33
41
 
34
42
  ```bash
@@ -52,142 +60,58 @@ node bin/crondex.js list
52
60
 
53
61
  ```
54
62
  crondex/
55
- ├── bin/crondex.js CLI: list / show / add
63
+ ├── bin/crondex.js CLI: list / show / add / recommend
56
64
  ├── catalog.json generated index of every job — read this first
57
65
  ├── schema/job.schema.json spec every job file follows
58
- ├── jobs/
59
- │ ├── devops/
60
- │ ├── productivity/
61
- │ ├── personal/
62
- │ ├── content/
63
- │ ├── finance/
64
- │ ├── security/
65
- │ ├── learning/
66
- │ ├── team/
67
- │ ├── home/
68
- │ ├── travel/
69
- │ └── growth/
66
+ ├── jobs/ one YAML per job, grouped by category subdirectory
70
67
  └── scripts/
71
- ├── build-catalog.js regenerates catalog.json from jobs/**/*.yaml
68
+ ├── build-catalog.js regenerates catalog.json + the summary below from jobs/**/*.yaml
72
69
  └── validate-jobs.js validates every job against the schema
73
70
  ```
74
71
 
75
72
  ## Available jobs
76
73
 
77
- 56 jobs across 11 categories. Full details (description, tags, variables)
78
- live in `catalog.json` and each job's YAML file — run `crondex list` or
79
- browse `jobs/<category>/` for the plain-language rundown of each.
80
-
81
- **devops**
82
-
83
- | id | schedule | modes |
84
- |---|---|---|
85
- | `dependency-audit` | `0 8 * * 1` | script + agent-prompt |
86
- | `log-cleanup` | `30 3 * * *` | script |
87
- | `repo-health-check` | `0 9 * * 1-5` | script + agent-prompt |
88
- | `backup-reminder` | `0 9 * * *` | script |
89
- | `ssl-cert-expiry-check` | `0 6 * * *` | script |
90
- | `uptime-ping-check` | `*/15 * * * *` | script |
91
- | `cost-alert` | `0 7 * * *` | script + agent-prompt |
92
- | `disk-space-check` | `0 */6 * * *` | script |
93
- | `docker-image-prune` | `0 4 * * *` | script |
94
- | `env-drift-check` | `0 8 * * *` | script |
95
- | `stale-dependency-pr-nudge` | `0 9 * * 1-5` | script |
96
- | `db-backup-verify` | `0 5 * * *` | script |
97
- | `license-compliance-check` | `0 8 * * 1` | script |
98
- | `orphaned-branch-cleanup` | `0 9 * * 1` | script |
99
- | `dns-record-check` | `0 */6 * * *` | script |
100
- | `queue-depth-check` | `*/10 * * * *` | script |
101
- | `api-rate-limit-check` | `*/15 * * * *` | script |
102
-
103
- **productivity**
104
-
105
- | id | schedule | modes |
106
- |---|---|---|
107
- | `daily-standup-summary` | `0 8 * * 1-5` | script + agent-prompt |
108
- | `inbox-triage` | `0 7,13 * * 1-5` | agent-prompt only |
109
- | `weekly-report` | `0 16 * * 5` | script + agent-prompt |
110
- | `focus-block-reminder` | `0 9,14 * * 1-5` | script |
111
-
112
- **personal**
113
-
114
- | id | schedule | modes |
115
- |---|---|---|
116
- | `bill-due-reminder` | `0 9 * * *` | script |
117
- | `habit-checkin` | `0 20 * * *` | script |
118
- | `meal-plan-reminder` | `0 9 * * 0` | script + agent-prompt |
119
- | `water-intake-reminder` | `0 9,12,15,18 * * *` | script |
120
- | `screen-time-check` | `0 20 * * *` | agent-prompt only |
121
-
122
- **content**
123
-
124
- | id | schedule | modes |
125
- |---|---|---|
126
- | `changelog-digest` | `0 10 * * 5` | script + agent-prompt |
127
- | `broken-link-check` | `0 7 * * 1` | script |
128
- | `social-mentions-watch` | `0 9 * * *` | agent-prompt only |
129
- | `seo-meta-check` | `0 7 * * 1` | script |
130
- | `rss-feed-validate` | `0 8 * * *` | script |
131
- | `robots-txt-check` | `0 8 * * *` | script |
132
-
133
- **finance**
134
-
135
- | id | schedule | modes |
136
- |---|---|---|
137
- | `subscription-audit` | `0 9 1 * *` | script |
138
- | `net-worth-snapshot` | `0 9 1 * *` | script |
139
- | `saas-seat-audit` | `0 9 1 * *` | script |
140
- | `invoice-overdue-check` | `0 9 * * *` | script |
141
- | `tax-deadline-reminder` | `0 9 * * *` | script |
142
-
143
- **security**
144
-
145
- | id | schedule | modes |
146
- |---|---|---|
147
- | `secrets-scan` | `0 3 * * *` | script |
148
- | `open-port-check` | `0 */4 * * *` | script |
149
- | `failed-login-watch` | `*/15 * * * *` | script |
150
- | `sudo-usage-audit` | `0 * * * *` | script |
151
- | `firewall-rule-diff` | `0 6 * * *` | script |
152
- | `certificate-transparency-watch` | `0 */12 * * *` | script |
153
-
154
- **learning**
155
-
156
- | id | schedule | modes |
157
- |---|---|---|
158
- | `daily-flashcard-review` | `0 8 * * *` | script |
159
- | `reading-list-nudge` | `0 9 * * 6` | script |
160
- | `course-progress-checkin` | `0 9 * * 1` | script |
161
-
162
- **team**
163
-
164
- | id | schedule | modes |
165
- |---|---|---|
166
- | `1on1-prep-reminder` | `0 9 * * 3` | script |
167
- | `pto-balance-check` | `0 9 1 * *` | script |
168
-
169
- **home**
170
-
171
- | id | schedule | modes |
172
- |---|---|---|
173
- | `hvac-filter-reminder` | `0 9 1 * *` | script |
174
- | `smoke-detector-battery-check` | `0 9 1 * *` | script |
175
- | `plant-watering-reminder` | `0 9 * * *` | script |
176
-
177
- **travel**
178
-
179
- | id | schedule | modes |
180
- |---|---|---|
181
- | `passport-expiry-check` | `0 9 1 * *` | script |
182
- | `visa-expiry-check` | `0 9 1 * *` | script |
183
- | `flight-checkin-reminder` | `0 * * * *` | script |
184
-
185
- **growth**
186
-
187
- | id | schedule | modes |
188
- |---|---|---|
189
- | `review-request-nudge` | `0 9 * * *` | script |
190
- | `cart-abandonment-followup` | `0 10 * * *` | script + agent-prompt |
74
+ Full details (description, tags, variables) live in `catalog.json` and each
75
+ job's YAML file — run `crondex list`, `crondex recommend "<what you want>"`,
76
+ or browse `jobs/<category>/` for the plain-language rundown of each. The
77
+ counts below are regenerated by `npm run build-catalog`, so they never drift
78
+ from what's actually in `jobs/`.
79
+
80
+ <!-- BEGIN JOB SUMMARY -->
81
+ 150 jobs across 29 categories:
82
+
83
+ | category | jobs |
84
+ |---|---|
85
+ | `construction` | 3 |
86
+ | `content` | 12 |
87
+ | `creator` | 3 |
88
+ | `devops` | 25 |
89
+ | `education` | 3 |
90
+ | `events` | 3 |
91
+ | `finance` | 9 |
92
+ | `fleet` | 3 |
93
+ | `growth` | 4 |
94
+ | `hiring` | 3 |
95
+ | `home` | 5 |
96
+ | `hospitality` | 3 |
97
+ | `inventory` | 3 |
98
+ | `investing` | 3 |
99
+ | `learning` | 5 |
100
+ | `legal` | 3 |
101
+ | `logistics` | 3 |
102
+ | `manufacturing` | 3 |
103
+ | `marketing` | 3 |
104
+ | `nonprofit` | 3 |
105
+ | `personal` | 6 |
106
+ | `podcast` | 3 |
107
+ | `productivity` | 7 |
108
+ | `realestate` | 3 |
109
+ | `sales` | 3 |
110
+ | `security` | 14 |
111
+ | `support` | 3 |
112
+ | `team` | 4 |
113
+ | `travel` | 5 |
114
+ <!-- END JOB SUMMARY -->
191
115
 
192
116
  ## How a job works
193
117
 
package/bin/crondex.js CHANGED
@@ -30,14 +30,92 @@ Usage:
30
30
  crondex list [--category <name>] [--tag <name>]
31
31
  crondex show <id>
32
32
  crondex add <id> [--dest <path>]
33
+ crondex recommend "<what you want done>" [--limit <n>]
33
34
 
34
35
  Examples:
35
36
  crondex list --category devops
36
37
  crondex show dependency-audit
37
38
  crondex add backup-reminder --dest ./cron/backup-reminder.yaml
39
+ crondex recommend "warn me before my SSL cert expires"
38
40
  `);
39
41
  }
40
42
 
43
+ const STOPWORDS = new Set([
44
+ "a", "an", "the", "to", "for", "of", "in", "on", "at", "by", "and", "or", "my", "me", "i",
45
+ "can", "you", "do", "does", "this", "that", "these", "those", "please", "want", "wants",
46
+ "wanted", "need", "needs", "help", "with", "is", "are", "be", "it", "so", "when", "should",
47
+ "could", "would", "how", "what", "which", "up", "down", "get", "make", "set", "just", "really",
48
+ "some", "something", "any", "want", "us", "our", "your", "yours", "if", "then", "than",
49
+ ]);
50
+
51
+ function stem(word) {
52
+ if (word.length > 4 && word.endsWith("ies")) return word.slice(0, -3) + "y";
53
+ if (word.length > 4 && word.endsWith("es") && !word.endsWith("ses")) return word.slice(0, -2);
54
+ if (word.length > 3 && word.endsWith("s") && !word.endsWith("ss")) return word.slice(0, -1);
55
+ return word;
56
+ }
57
+
58
+ function tokenize(text) {
59
+ return text
60
+ .toLowerCase()
61
+ .replace(/[^a-z0-9\s-]/g, " ")
62
+ .split(/[\s-]+/)
63
+ .filter((w) => w.length > 1 && !STOPWORDS.has(w))
64
+ .map(stem);
65
+ }
66
+
67
+ // id and name are near-duplicates of each other (id is just the slugified
68
+ // name), so they're merged into one "title" field — otherwise a match on a
69
+ // shared word like "water" gets weighted 3+3 instead of 3, which can outrank
70
+ // a more specific job that only matches once via tags.
71
+ const RECOMMEND_WEIGHTS = { tags: 4, title: 3, category: 2, description: 1 };
72
+
73
+ function scoreJob(queryTokens, job) {
74
+ const fields = {
75
+ tags: job.tags.map((t) => stem(t.toLowerCase())),
76
+ title: [...new Set([...tokenize(job.name), ...tokenize(job.id.replace(/-/g, " "))])],
77
+ category: tokenize(job.category ?? ""),
78
+ description: tokenize(job.description ?? ""),
79
+ };
80
+ let score = 0;
81
+ const matched = new Set();
82
+ for (const qt of queryTokens) {
83
+ for (const [field, weight] of Object.entries(RECOMMEND_WEIGHTS)) {
84
+ if (fields[field].includes(qt)) {
85
+ score += weight;
86
+ matched.add(qt);
87
+ }
88
+ }
89
+ }
90
+ return { score, matchedTerms: [...matched] };
91
+ }
92
+
93
+ function recommend(queryText) {
94
+ const limit = Number(flag("limit")) || 5;
95
+ const queryTokens = tokenize(queryText);
96
+ if (!queryTokens.length) {
97
+ console.log("query too vague to match on — describe what you want the job to check or remind you about.");
98
+ return;
99
+ }
100
+ const ranked = CATALOG.jobs
101
+ .map((job) => ({ job, ...scoreJob(queryTokens, job) }))
102
+ .filter((r) => r.score > 0)
103
+ .sort((a, b) => b.score - a.score)
104
+ .slice(0, limit);
105
+ if (!ranked.length) {
106
+ console.log(`no confident match for "${queryText}". Run "crondex list" to browse everything.`);
107
+ return;
108
+ }
109
+ console.log(`top match${ranked.length > 1 ? "es" : ""} for "${queryText}":`);
110
+ console.log();
111
+ for (const r of ranked) {
112
+ console.log(`${r.job.id} [${r.job.category}] score ${r.score} (${r.job.modes.join(", ")})`);
113
+ console.log(` ${r.job.description}`);
114
+ console.log(` matched: ${r.matchedTerms.join(", ")}`);
115
+ console.log();
116
+ }
117
+ }
118
+
41
119
  function catalogInfoLine() {
42
120
  const viaNpx = process.env.npm_command === "exec";
43
121
  const freshness = viaNpx
@@ -98,6 +176,13 @@ switch (cmd) {
98
176
  }
99
177
  add(args[0]);
100
178
  break;
179
+ case "recommend":
180
+ if (!args[0]) {
181
+ console.error('usage: crondex recommend "<what you want done>" [--limit <n>]');
182
+ process.exit(1);
183
+ }
184
+ recommend(args[0]);
185
+ break;
101
186
  default:
102
187
  printHelp();
103
188
  }