@neurowire/cli 0.1.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.
Files changed (2) hide show
  1. package/dist/index.js +92 -2
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -7,12 +7,17 @@ import {
7
7
  FORMATS,
8
8
  MeshSchema,
9
9
  isFormat,
10
+ parseDuration,
11
+ resolveWindow,
12
+ selectEntries,
10
13
  serialize,
11
14
  validateNwf
12
15
  } from "@neurowire/core";
13
16
  import { FeedTemplateSchema, fetchFeed, fetchMesh } from "@neurowire/ingest";
14
17
  import { registerAllTaps } from "@neurowire/taps";
15
- var VERSION = "0.1.0";
18
+ var VERSION = "0.3.0";
19
+ var SORT_KEYS = ["date", "title", "source"];
20
+ var SORT_ORDERS = ["asc", "desc"];
16
21
  var HELP = `Neurowire ${VERSION} - turn any blog or feed into Atom and friends.
17
22
 
18
23
  Usage:
@@ -29,6 +34,16 @@ Options:
29
34
  -h, --help Show this help.
30
35
  -v, --version Show the version.
31
36
 
37
+ Shape the output (applied before --format):
38
+ --sort <key> Sort by date, title, or source.
39
+ --order <dir> asc or desc (default: newest-first for date, A-Z otherwise).
40
+ -n, --limit <n> Keep at most n entries. Handy for integrations: --limit 10.
41
+ --since <age> Keep entries within this window, e.g. 24h, 90m, 7d.
42
+ --max-age <age> Drop entries older than this (same window as --since).
43
+ --today Keep entries since midnight UTC today.
44
+ --this-week Keep entries since Monday midnight UTC.
45
+ --between <a>..<b> Keep entries between two dates, e.g. 2026-01-01..2026-02-01.
46
+
32
47
  Commands:
33
48
  validate <file-or-url> Check that an nwf document is well-formed (exits non-zero if not).
34
49
 
@@ -42,7 +57,8 @@ Taps teach Neurowire to read sites with no RSS/Atom feed. Add your own with
42
57
  Examples:
43
58
  neurowire https://example.com/blog
44
59
  neurowire https://example.com/feed.xml --format atom > feed.xml
45
- neurowire --mesh ai-news.json --format atom
60
+ neurowire --mesh ai-news.json --format json --limit 10
61
+ neurowire --mesh ai-news.json --since 24h --sort date --format atom
46
62
  neurowire validate feed.nwf
47
63
  `;
48
64
  var useColor = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
@@ -99,6 +115,69 @@ async function runValidate(input) {
99
115
  process.exitCode = 1;
100
116
  }
101
117
  }
118
+ function refineFeed(feed, values) {
119
+ const fail = (message) => {
120
+ process.stderr.write(`error: ${message}
121
+ `);
122
+ process.exitCode = 1;
123
+ return void 0;
124
+ };
125
+ const sort = values.sort;
126
+ if (sort !== void 0 && !SORT_KEYS.includes(sort)) {
127
+ return fail(`unknown --sort "${sort}". Use one of: ${SORT_KEYS.join(", ")}`);
128
+ }
129
+ const order = values.order;
130
+ if (order !== void 0 && !SORT_ORDERS.includes(order)) {
131
+ return fail(`unknown --order "${order}". Use asc or desc`);
132
+ }
133
+ let limit;
134
+ if (values.limit !== void 0) {
135
+ limit = Number(values.limit);
136
+ if (!Number.isInteger(limit) || limit < 0) {
137
+ return fail(`--limit must be a non-negative integer (got "${values.limit}")`);
138
+ }
139
+ }
140
+ const spec = {};
141
+ if (typeof values.since === "string") spec.since = values.since;
142
+ if (typeof values["max-age"] === "string") spec.maxAge = values["max-age"];
143
+ if (values.today) spec.today = true;
144
+ if (values["this-week"]) spec.thisWeek = true;
145
+ if (typeof values.between === "string") {
146
+ const parts = values.between.split("..");
147
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
148
+ return fail("--between expects <start>..<end>, e.g. 2026-01-01..2026-02-01");
149
+ }
150
+ spec.between = [parts[0], parts[1]];
151
+ }
152
+ for (const [flag, value] of [
153
+ ["--since", spec.since],
154
+ ["--max-age", spec.maxAge]
155
+ ]) {
156
+ if (value !== void 0 && parseDuration(value) === void 0) {
157
+ return fail(`invalid ${flag} "${value}" (use e.g. 24h, 90m, 7d)`);
158
+ }
159
+ }
160
+ if (spec.between) {
161
+ const [a, b] = spec.between;
162
+ if (Number.isNaN(Date.parse(a)) || Number.isNaN(Date.parse(b))) {
163
+ return fail("--between needs two parseable dates, e.g. 2026-01-01..2026-02-01");
164
+ }
165
+ }
166
+ const window = resolveWindow(spec, Date.now());
167
+ const opts = {
168
+ ...window,
169
+ sort,
170
+ order,
171
+ limit
172
+ };
173
+ const before = feed.entries.length;
174
+ const refined = selectEntries(feed, opts);
175
+ if (refined.entries.length !== before) {
176
+ process.stderr.write(`Refined to ${refined.entries.length} of ${before} entries
177
+ `);
178
+ }
179
+ return refined;
180
+ }
102
181
  function renderTerminal(feed) {
103
182
  const out = [bold(cyan(feed.title))];
104
183
  const sub = [feed.home, `${feed.entries.length} entries`, `updated ${day(feed.updated)}`].filter(
@@ -133,6 +212,14 @@ async function main() {
133
212
  template: { type: "string", short: "t" },
134
213
  mesh: { type: "string", short: "m" },
135
214
  taps: { type: "string", multiple: true },
215
+ sort: { type: "string" },
216
+ order: { type: "string" },
217
+ limit: { type: "string", short: "n" },
218
+ since: { type: "string" },
219
+ "max-age": { type: "string" },
220
+ today: { type: "boolean" },
221
+ "this-week": { type: "boolean" },
222
+ between: { type: "string" },
136
223
  help: { type: "boolean", short: "h" },
137
224
  version: { type: "boolean", short: "v" }
138
225
  }
@@ -171,6 +258,9 @@ async function main() {
171
258
  }
172
259
  feed = await fetchFeed(url, { template });
173
260
  }
261
+ const refined = refineFeed(feed, values);
262
+ if (!refined) return;
263
+ feed = refined;
174
264
  if (values.format) {
175
265
  if (!isFormat(values.format)) {
176
266
  process.stderr.write(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neurowire/cli",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Neurowire command-line tool: turn any blog, feed, or mesh into Atom, JSON Feed, Markdown, or nwf.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,15 +33,15 @@
33
33
  "url": "https://github.com/starside-io/neurowire/issues"
34
34
  },
35
35
  "engines": {
36
- "node": ">=20"
36
+ "node": ">=24"
37
37
  },
38
38
  "publishConfig": {
39
39
  "access": "public"
40
40
  },
41
41
  "dependencies": {
42
- "@neurowire/ingest": "0.1.0",
43
- "@neurowire/core": "0.1.0",
44
- "@neurowire/taps": "0.1.0"
42
+ "@neurowire/ingest": "0.2.0",
43
+ "@neurowire/core": "0.3.0",
44
+ "@neurowire/taps": "0.2.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/node": "^22.10.5",