@wonsukchoi/crondex 0.10.0 → 0.11.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
@@ -36,6 +36,10 @@ npx @wonsukchoi/crondex add ssl-cert-expiry-check --dest ./cron/ssl-cert-expiry-
36
36
  - `add <id> [--dest path]` — copy it into your project to edit
37
37
  - `init <id> [--category x]` — scaffold a brand-new job from the template
38
38
 
39
+ Add `--json` to `list`/`categories`/`show`/`recommend` for machine-readable
40
+ output — useful when an agent is parsing the result programmatically
41
+ instead of a human reading it.
42
+
39
43
  No install needed — `npx` always runs against the latest catalog. Prefer
40
44
  installing once? `npm install -g @wonsukchoi/crondex`, then drop the `npx`
41
45
  prefix (run `npm update -g` later to pick up new jobs). Prefer no npm at
@@ -87,7 +91,7 @@ tags, variables) use `crondex list`, `crondex recommend`, or browse
87
91
  `jobs/<category>/` directly.
88
92
 
89
93
  <!-- BEGIN JOB SUMMARY -->
90
- 187 jobs across 37 categories:
94
+ 189 jobs across 37 categories:
91
95
 
92
96
  | category | jobs |
93
97
  |---|---|
@@ -113,7 +117,7 @@ tags, variables) use `crondex list`, `crondex recommend`, or browse
113
117
  | `inventory` | 3 |
114
118
  | `investing` | 3 |
115
119
  | `learning` | 5 |
116
- | `legal` | 3 |
120
+ | `legal` | 4 |
117
121
  | `logistics` | 3 |
118
122
  | `manufacturing` | 3 |
119
123
  | `marketing` | 4 |
@@ -121,7 +125,7 @@ tags, variables) use `crondex list`, `crondex recommend`, or browse
121
125
  | `personal` | 9 |
122
126
  | `podcast` | 3 |
123
127
  | `productivity` | 9 |
124
- | `realestate` | 3 |
128
+ | `realestate` | 4 |
125
129
  | `restaurant` | 3 |
126
130
  | `sales` | 5 |
127
131
  | `security` | 14 |
@@ -136,6 +140,7 @@ tags, variables) use `crondex list`, `crondex recommend`, or browse
136
140
 
137
141
  ```
138
142
  crondex/
143
+ ├── llms.txt agent-discovery manifest (llms.txt convention)
139
144
  ├── bin/crondex.js CLI: list / categories / show / add / recommend / init
140
145
  ├── lib/recommend.js recommend's scoring logic (unit tested in test/)
141
146
  ├── catalog.json generated index of every job — read this first
package/bin/crondex.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // CLI over catalog.json — browse jobs, read one, or pull one into your project.
3
3
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
4
4
  import { join } from "node:path";
5
+ import yaml from "js-yaml";
5
6
  import { tokenize, rankJobs } from "../lib/recommend.js";
6
7
 
7
8
  const ROOT = new URL("..", import.meta.url).pathname;
@@ -15,6 +16,14 @@ function flag(name) {
15
16
  return i === -1 ? undefined : args[i + 1];
16
17
  }
17
18
 
19
+ function hasFlag(name) {
20
+ return args.includes(`--${name}`);
21
+ }
22
+
23
+ function printJson(value) {
24
+ console.log(JSON.stringify(value, null, 2));
25
+ }
26
+
18
27
  function findJob(id) {
19
28
  const meta = CATALOG.jobs.find((j) => j.id === id);
20
29
  if (!meta) {
@@ -28,11 +37,11 @@ function printHelp() {
28
37
  console.log(`crondex — browse and pull pre-made cron jobs
29
38
 
30
39
  Usage:
31
- crondex list [--category <name>] [--tag <name>]
32
- crondex categories
33
- crondex show <id>
40
+ crondex list [--category <name>] [--tag <name>] [--json]
41
+ crondex categories [--json]
42
+ crondex show <id> [--json]
34
43
  crondex add <id> [--dest <path>]
35
- crondex recommend "<what you want done>" [--limit <n>]
44
+ crondex recommend "<what you want done>" [--limit <n>] [--json]
36
45
  crondex init <id> [--category <name>] [--dest <path>]
37
46
 
38
47
  Examples:
@@ -42,20 +51,39 @@ Examples:
42
51
  crondex add backup-reminder --dest ./cron/backup-reminder.yaml
43
52
  crondex recommend "warn me before my SSL cert expires"
44
53
  crondex init ssl-cert-expiry-check --category security
54
+
55
+ Add --json to list/categories/show/recommend for machine-readable output —
56
+ useful when an agent is parsing crondex's output programmatically instead
57
+ of a human reading it.
45
58
  `);
46
59
  }
47
60
 
48
61
  function recommend(queryText) {
49
62
  const limit = Number(flag("limit")) || 5;
63
+ const json = hasFlag("json");
50
64
  if (!tokenize(queryText).length) {
65
+ if (json) return printJson([]);
51
66
  console.log("query too vague to match on — describe what you want the job to check or remind you about.");
52
67
  return;
53
68
  }
54
69
  const ranked = rankJobs(CATALOG.jobs, queryText, limit);
55
70
  if (!ranked.length) {
71
+ if (json) return printJson([]);
56
72
  console.log(`no confident match for "${queryText}". Run "crondex list" to browse everything.`);
57
73
  return;
58
74
  }
75
+ if (json) {
76
+ return printJson(
77
+ ranked.map((r) => ({
78
+ id: r.job.id,
79
+ category: r.job.category,
80
+ score: r.score,
81
+ matched_terms: r.matchedTerms,
82
+ modes: r.job.modes,
83
+ description: r.job.description,
84
+ }))
85
+ );
86
+ }
59
87
  console.log(`top match${ranked.length > 1 ? "es" : ""} for "${queryText}":`);
60
88
  console.log();
61
89
  for (const r of ranked) {
@@ -77,9 +105,11 @@ function catalogInfoLine() {
77
105
  function list() {
78
106
  const category = flag("category");
79
107
  const tag = flag("tag");
108
+ const json = hasFlag("json");
80
109
  const jobs = CATALOG.jobs.filter(
81
110
  (j) => (!category || j.category === category) && (!tag || j.tags.includes(tag))
82
111
  );
112
+ if (json) return printJson(jobs);
83
113
  console.log(catalogInfoLine());
84
114
  console.log();
85
115
  if (!jobs.length) {
@@ -94,15 +124,20 @@ function list() {
94
124
  }
95
125
 
96
126
  function show(id) {
97
- console.log(readFileSync(join(ROOT, findJob(id).path), "utf8"));
127
+ const meta = findJob(id);
128
+ const raw = readFileSync(join(ROOT, meta.path), "utf8");
129
+ if (hasFlag("json")) return printJson(yaml.load(raw));
130
+ console.log(raw);
98
131
  }
99
132
 
100
133
  function categories() {
101
134
  const counts = {};
102
135
  for (const j of CATALOG.jobs) counts[j.category] = (counts[j.category] ?? 0) + 1;
136
+ const sorted = Object.entries(counts).sort(([a], [b]) => a.localeCompare(b));
137
+ if (hasFlag("json")) return printJson(sorted.map(([category, count]) => ({ category, count })));
103
138
  console.log(catalogInfoLine());
104
139
  console.log();
105
- for (const [cat, n] of Object.entries(counts).sort(([a], [b]) => a.localeCompare(b))) {
140
+ for (const [cat, n] of sorted) {
106
141
  console.log(`${cat} (${n})`);
107
142
  }
108
143
  }
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": 187,
4
+ "count": 189,
5
5
  "jobs": [
6
6
  {
7
7
  "id": "1on1-prep-reminder",
@@ -809,6 +809,31 @@
809
809
  ],
810
810
  "path": "jobs/learning/course-progress-checkin.yaml"
811
811
  },
812
+ {
813
+ "id": "court-deadline-reminder",
814
+ "version": 1,
815
+ "name": "Court Deadline Reminder",
816
+ "description": "Checks a tracked list of court/filing deadlines and warns before any come due. Use this if a filing deadline has ever been missed because it lived only in one person's head or a sticky note instead of a shared, checked calendar.",
817
+ "category": "legal",
818
+ "tags": [
819
+ "legal",
820
+ "court",
821
+ "deadline",
822
+ "compliance"
823
+ ],
824
+ "schedule": "0 7 * * *",
825
+ "timezone": "UTC",
826
+ "runner": "shell",
827
+ "modes": [
828
+ "script"
829
+ ],
830
+ "compatible_agents": [
831
+ "generic",
832
+ "claude",
833
+ "codex"
834
+ ],
835
+ "path": "jobs/legal/court-deadline-reminder.yaml"
836
+ },
812
837
  {
813
838
  "id": "coverage-gap-audit",
814
839
  "version": 1,
@@ -2385,12 +2410,12 @@
2385
2410
  "id": "low-stock-alert",
2386
2411
  "version": 1,
2387
2412
  "name": "Low Stock Alert",
2388
- "description": "Checks inventory levels against a reorder point and flags SKUs running low. Use this if you've ever gone out of stock on a bestseller because nobody was watching the count.",
2413
+ "description": "Checks a general-purpose inventory database (warehouse, retail backroom, restaurant supplies — anything with a quantity and a set reorder point) against that fixed threshold and flags what's running low. Use this for physical stock tracked in a database, not tied to online sales; if you specifically need SKU reorder timing driven by e-commerce sales velocity and supplier lead time, see `low-stock-reorder-alert` instead.",
2389
2414
  "category": "inventory",
2390
2415
  "tags": [
2391
2416
  "inventory",
2392
2417
  "stock",
2393
- "ecommerce"
2418
+ "warehouse"
2394
2419
  ],
2395
2420
  "schedule": "0 7 * * *",
2396
2421
  "timezone": "UTC",
@@ -3032,6 +3057,31 @@
3032
3057
  ],
3033
3058
  "path": "jobs/manufacturing/production-line-downtime-watch.yaml"
3034
3059
  },
3060
+ {
3061
+ "id": "property-maintenance-inspection-reminder",
3062
+ "version": 1,
3063
+ "name": "Property Maintenance Inspection Reminder",
3064
+ "description": "Tracks when each property was last inspected and flags any that are due for their next routine walkthrough. Use this if a property has ever gone a year or more without a proper inspection because nothing forced anyone to schedule one.",
3065
+ "category": "realestate",
3066
+ "tags": [
3067
+ "realestate",
3068
+ "maintenance",
3069
+ "inspection",
3070
+ "property"
3071
+ ],
3072
+ "schedule": "0 8 * * 1",
3073
+ "timezone": "UTC",
3074
+ "runner": "shell",
3075
+ "modes": [
3076
+ "script"
3077
+ ],
3078
+ "compatible_agents": [
3079
+ "generic",
3080
+ "claude",
3081
+ "codex"
3082
+ ],
3083
+ "path": "jobs/realestate/property-maintenance-inspection-reminder.yaml"
3084
+ },
3035
3085
  {
3036
3086
  "id": "pto-balance-check",
3037
3087
  "version": 1,
@@ -2,11 +2,14 @@ id: low-stock-alert
2
2
  version: 1
3
3
  name: Low Stock Alert
4
4
  description: >
5
- Checks inventory levels against a reorder point and flags SKUs running
6
- low. Use this if you've ever gone out of stock on a bestseller because
7
- nobody was watching the count.
5
+ Checks a general-purpose inventory database (warehouse, retail backroom,
6
+ restaurant supplies anything with a quantity and a set reorder point)
7
+ against that fixed threshold and flags what's running low. Use this for
8
+ physical stock tracked in a database, not tied to online sales; if you
9
+ specifically need SKU reorder timing driven by e-commerce sales velocity
10
+ and supplier lead time, see `low-stock-reorder-alert` instead.
8
11
  category: inventory
9
- tags: [inventory, stock, ecommerce]
12
+ tags: [inventory, stock, warehouse]
10
13
  schedule: "0 7 * * *"
11
14
  timezone: "UTC"
12
15
  runner: shell
@@ -0,0 +1,40 @@
1
+ id: court-deadline-reminder
2
+ version: 1
3
+ name: Court Deadline Reminder
4
+ description: >
5
+ Checks a tracked list of court/filing deadlines and warns before any come
6
+ due. Use this if a filing deadline has ever been missed because it lived
7
+ only in one person's head or a sticky note instead of a shared, checked
8
+ calendar.
9
+ category: legal
10
+ tags: [legal, court, deadline, compliance]
11
+ schedule: "0 7 * * *"
12
+ timezone: "UTC"
13
+ runner: shell
14
+ command: >
15
+ awk -F',' -v days="{{warn_days_before}}" '
16
+ NR==1 {next}
17
+ {
18
+ cmd = "date -d \"" $3 "\" +%s 2>/dev/null || date -j -f %Y-%m-%d \"" $3 "\" +%s 2>/dev/null";
19
+ cmd | getline due_epoch; close(cmd);
20
+ "date +%s" | getline now_epoch; close("date +%s");
21
+ diff_days = (due_epoch - now_epoch) / 86400;
22
+ if (diff_days <= days && diff_days >= 0) {
23
+ printf "DUE SOON: %s — %s due %s (%d days)\n", $1, $2, $3, diff_days;
24
+ }
25
+ }
26
+ ' "{{deadlines_csv_path}}"
27
+ variables:
28
+ deadlines_csv_path:
29
+ default: "./court-deadlines.csv"
30
+ description: "Path to a CSV with columns: matter_name,deadline_type,due_date (YYYY-MM-DD) — e.g. one row per filing/response/statute-of-limitations deadline."
31
+ warn_days_before:
32
+ default: 14
33
+ description: How many days before a deadline to start warning.
34
+ compatible_agents: [generic, claude, codex]
35
+ notes: >
36
+ This is a plain date-threshold check, not a rules-based calendaring
37
+ engine — it won't calculate deadlines from court rules the way dedicated
38
+ legal calendaring software does. Export computed due dates from that
39
+ system into the CSV, or maintain them manually, and use this as the
40
+ "did anyone actually notice" backstop.
@@ -0,0 +1,35 @@
1
+ id: property-maintenance-inspection-reminder
2
+ version: 1
3
+ name: Property Maintenance Inspection Reminder
4
+ description: >
5
+ Tracks when each property was last inspected and flags any that are due
6
+ for their next routine walkthrough. Use this if a property has ever gone
7
+ a year or more without a proper inspection because nothing forced anyone
8
+ to schedule one.
9
+ category: realestate
10
+ tags: [realestate, maintenance, inspection, property]
11
+ schedule: "0 8 * * 1"
12
+ timezone: "UTC"
13
+ runner: shell
14
+ command: >
15
+ awk -F',' -v days="{{inspection_interval_days}}" '
16
+ NR==1 {next}
17
+ {
18
+ cmd = "date -d \"" $2 "\" +%s 2>/dev/null || date -j -f %Y-%m-%d \"" $2 "\" +%s 2>/dev/null";
19
+ cmd | getline last_epoch; close(cmd);
20
+ "date +%s" | getline now_epoch; close("date +%s");
21
+ days_since = (now_epoch - last_epoch) / 86400;
22
+ if (days_since >= days) {
23
+ printf "DUE: %s — last inspected %s (%d days ago)\n", $1, $2, days_since;
24
+ }
25
+ }
26
+ ' "{{properties_csv_path}}"
27
+ variables:
28
+ properties_csv_path:
29
+ default: "./property-inspections.csv"
30
+ description: "Path to a CSV with columns: property_name,last_inspection_date (YYYY-MM-DD)."
31
+ inspection_interval_days:
32
+ default: 180
33
+ description: How often each property should be inspected, in days.
34
+ compatible_agents: [generic, claude, codex]
35
+ notes: Assumes a maintained CSV of last-inspection dates per property — export it from whatever property management system you use, or update it manually after each walkthrough.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wonsukchoi/crondex",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "A public directory of pre-made, agent-editable cron jobs.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,10 +35,13 @@
35
35
  "build-catalog": "node scripts/build-catalog.js",
36
36
  "validate": "node scripts/validate-jobs.js",
37
37
  "lint-shell": "node scripts/lint-shell.js",
38
+ "check-duplicates": "node scripts/check-duplicates.js",
38
39
  "test": "node --test"
39
40
  },
40
- "devDependencies": {
41
- "ajv": "^8.17.1",
41
+ "dependencies": {
42
42
  "js-yaml": "^4.1.0"
43
+ },
44
+ "devDependencies": {
45
+ "ajv": "^8.17.1"
43
46
  }
44
47
  }