harnery 0.2.1 → 0.2.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"commander.d.ts","sourceRoot":"","sources":["../src/commander.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+BpC,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAEhC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;IAEnB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,KAAK,MAAM,GAAG,SAAS,CAAC;IACzB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACvE;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACjE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;IAC5C,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3D,KAAK,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,GAAG,OAAO,GAAG,IAAI,CAAC;IACrF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACpE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,eAAO,MAAM,WAAW,EAAE,WAkCzB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CA2C3E"}
1
+ {"version":3,"file":"commander.d.ts","sourceRoot":"","sources":["../src/commander.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+BpC,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAEhC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;IAEnB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,KAAK,MAAM,GAAG,SAAS,CAAC;IACzB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACvE;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACjE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;IAC5C,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3D,KAAK,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,GAAG,OAAO,GAAG,IAAI,CAAC;IACrF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACpE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,eAAO,MAAM,WAAW,EAAE,WAkCzB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CA2C3E"}
package/dist/commander.js CHANGED
@@ -13,6 +13,9 @@
13
13
  * Each subdirectory under src/commands/ exports its `register…` function
14
14
  * (Commander pattern).
15
15
  */
16
+ import { readFileSync } from "node:fs";
17
+ import { dirname, join } from "node:path";
18
+ import { fileURLToPath } from "node:url";
16
19
  import { Command } from "commander";
17
20
  import { registerAgentsCommand } from "./commands/agents.js";
18
21
  import { registerBackupCommand } from "./commands/backup.js";
@@ -120,7 +123,29 @@ export function createHarneryProgram(opts = {}) {
120
123
  return program;
121
124
  }
122
125
  function readVersion() {
123
- // Static for the empty scaffold. Will be replaced by a build-time substitution
124
- // (or a JSON import) once we have any command shipping.
125
- return "0.1.0";
126
+ // Resolve package.json by walking up from this module. Works under Bun (this
127
+ // file runs from src/) and Node (from dist/): package.json sits at the
128
+ // package root above both. Falls back to "0.0.0" if it can't be found rather
129
+ // than crashing `--version`.
130
+ try {
131
+ let dir = dirname(fileURLToPath(import.meta.url));
132
+ for (let i = 0; i < 6; i++) {
133
+ try {
134
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
135
+ if (pkg.name === "harnery" && typeof pkg.version === "string")
136
+ return pkg.version;
137
+ }
138
+ catch {
139
+ // no package.json at this level, or not ours; keep walking up
140
+ }
141
+ const parent = dirname(dir);
142
+ if (parent === dir)
143
+ break;
144
+ dir = parent;
145
+ }
146
+ }
147
+ catch {
148
+ // import.meta.url unavailable or fs error; fall through
149
+ }
150
+ return "0.0.0";
126
151
  }
@@ -4,12 +4,14 @@
4
4
  *
5
5
  * The library backing the `read` command.
6
6
  *
7
- * jsdom, @mozilla/readability, and turndown are heavy and (jsdom's tree)
8
- * ESM-fragile, so they are loaded lazily inside `htmlToMarkdown` rather than
9
- * at module top level. A static import would pull them into CLI startup (every
10
- * command is registered eagerly), and jsdom's transitive `html-encoding-sniffer`
11
- * crashes a plain `node` process via ERR_REQUIRE_ESM. Lazy-loading keeps the
12
- * CLI booting for users who never touch `read`/`browse`.
7
+ * linkedom, @mozilla/readability, and turndown are heavy, so they are loaded
8
+ * lazily inside `htmlToMarkdown` rather than at module top level. A static
9
+ * import would pull them into CLI startup (every command is registered
10
+ * eagerly). linkedom replaced jsdom here: jsdom's transitive dependency tree
11
+ * (`html-encoding-sniffer`, `whatwg-url`) does a CommonJS `require()` of the
12
+ * ESM-only `@exodus/bytes`, which crashes a plain `node` process via
13
+ * ERR_REQUIRE_ESM. linkedom is a lighter, CJS-friendly DOM with no such chain.
14
+ * See ADR 0002.
13
15
  */
14
16
  export interface ReadabilityOptions {
15
17
  /** Base URL for resolving relative links. Default `http://local/`. */
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/readability/client.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAID;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CA8D5B"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/readability/client.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAID;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CAwE5B"}
@@ -10,14 +10,23 @@ export async function htmlToMarkdown(html, opts = {}) {
10
10
  if (!html.trim()) {
11
11
  throw new Error("Empty HTML input");
12
12
  }
13
- const [{ Readability }, { JSDOM }, { default: TurndownService }, { tables }] = await Promise.all([
13
+ const [{ Readability }, { parseHTML }, { default: TurndownService }, { tables }] = await Promise.all([
14
14
  import("@mozilla/readability"),
15
- import("jsdom"),
15
+ import("linkedom"),
16
16
  import("turndown"),
17
17
  import("turndown-plugin-gfm"),
18
18
  ]);
19
- const dom = new JSDOM(html, { url: opts.url ?? "http://local/" });
20
- const doc = dom.window.document;
19
+ // jsdom took a `url` option to set the document base so Readability resolves
20
+ // relative links to absolute. linkedom's parseHTML has no such option, so we
21
+ // inject a <base> tag, which sets doc.baseURI to the same effect. Only when
22
+ // the HTML doesn't already carry its own <base>.
23
+ const baseUrl = opts.url ?? "http://local/";
24
+ const htmlWithBase = /<base\b/i.test(html)
25
+ ? html
26
+ : html.replace(/<head\b[^>]*>/i, (m) => `${m}<base href="${baseUrl}">`);
27
+ // If there's no <head> to anchor the <base> into, fall back to the raw HTML
28
+ // (relative links stay relative, matching a document with no base).
29
+ const { document: doc } = parseHTML(htmlWithBase.includes("<base") ? htmlWithBase : html);
21
30
  preprocessDom(doc);
22
31
  let contentHtml;
23
32
  let title = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harnery",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Multi-agent coordination + harness adapters + portable CLI utilities for Claude Code / Cursor / Codex.",
5
5
  "license": "MIT",
6
6
  "author": "Ryan Kelly",
@@ -141,7 +141,7 @@
141
141
  "commander": "^15.0.0",
142
142
  "gpt-tokenizer": "^3.4.0",
143
143
  "js-yaml": "^4.1.1",
144
- "jsdom": "^29.0.2",
144
+ "linkedom": "^0.18.0",
145
145
  "mailparser": "^3.9.8",
146
146
  "pixelmatch": "^7.2.0",
147
147
  "playwright": "^1.59.1",
@@ -154,7 +154,6 @@
154
154
  "@changesets/cli": "^2.27.0",
155
155
  "@types/bun": "^1.2.0",
156
156
  "@types/js-yaml": "^4.0.9",
157
- "@types/jsdom": "^28.0.1",
158
157
  "@types/mailparser": "^3.4.6",
159
158
  "@types/node": "^26.0.0",
160
159
  "@types/pixelmatch": "^5.2.6",
package/src/commander.ts CHANGED
@@ -14,6 +14,9 @@
14
14
  * (Commander pattern).
15
15
  */
16
16
 
17
+ import { readFileSync } from "node:fs";
18
+ import { dirname, join } from "node:path";
19
+ import { fileURLToPath } from "node:url";
17
20
  import { Command } from "commander";
18
21
  import { registerAgentsCommand } from "./commands/agents.ts";
19
22
  import { registerBackupCommand } from "./commands/backup.ts";
@@ -236,7 +239,25 @@ export function createHarneryProgram(opts: HarneryContextOpts = {}): Command {
236
239
  }
237
240
 
238
241
  function readVersion(): string {
239
- // Static for the empty scaffold. Will be replaced by a build-time substitution
240
- // (or a JSON import) once we have any command shipping.
241
- return "0.1.0";
242
+ // Resolve package.json by walking up from this module. Works under Bun (this
243
+ // file runs from src/) and Node (from dist/): package.json sits at the
244
+ // package root above both. Falls back to "0.0.0" if it can't be found rather
245
+ // than crashing `--version`.
246
+ try {
247
+ let dir = dirname(fileURLToPath(import.meta.url));
248
+ for (let i = 0; i < 6; i++) {
249
+ try {
250
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
251
+ if (pkg.name === "harnery" && typeof pkg.version === "string") return pkg.version;
252
+ } catch {
253
+ // no package.json at this level, or not ours; keep walking up
254
+ }
255
+ const parent = dirname(dir);
256
+ if (parent === dir) break;
257
+ dir = parent;
258
+ }
259
+ } catch {
260
+ // import.meta.url unavailable or fs error; fall through
261
+ }
262
+ return "0.0.0";
242
263
  }
@@ -6,12 +6,14 @@
6
6
  *
7
7
  * The library backing the `read` command.
8
8
  *
9
- * jsdom, @mozilla/readability, and turndown are heavy and (jsdom's tree)
10
- * ESM-fragile, so they are loaded lazily inside `htmlToMarkdown` rather than
11
- * at module top level. A static import would pull them into CLI startup (every
12
- * command is registered eagerly), and jsdom's transitive `html-encoding-sniffer`
13
- * crashes a plain `node` process via ERR_REQUIRE_ESM. Lazy-loading keeps the
14
- * CLI booting for users who never touch `read`/`browse`.
9
+ * linkedom, @mozilla/readability, and turndown are heavy, so they are loaded
10
+ * lazily inside `htmlToMarkdown` rather than at module top level. A static
11
+ * import would pull them into CLI startup (every command is registered
12
+ * eagerly). linkedom replaced jsdom here: jsdom's transitive dependency tree
13
+ * (`html-encoding-sniffer`, `whatwg-url`) does a CommonJS `require()` of the
14
+ * ESM-only `@exodus/bytes`, which crashes a plain `node` process via
15
+ * ERR_REQUIRE_ESM. linkedom is a lighter, CJS-friendly DOM with no such chain.
16
+ * See ADR 0002.
15
17
  */
16
18
 
17
19
  export interface ReadabilityOptions {
@@ -50,15 +52,25 @@ export async function htmlToMarkdown(
50
52
  throw new Error("Empty HTML input");
51
53
  }
52
54
 
53
- const [{ Readability }, { JSDOM }, { default: TurndownService }, { tables }] = await Promise.all([
54
- import("@mozilla/readability"),
55
- import("jsdom"),
56
- import("turndown"),
57
- import("turndown-plugin-gfm"),
58
- ]);
55
+ const [{ Readability }, { parseHTML }, { default: TurndownService }, { tables }] =
56
+ await Promise.all([
57
+ import("@mozilla/readability"),
58
+ import("linkedom"),
59
+ import("turndown"),
60
+ import("turndown-plugin-gfm"),
61
+ ]);
59
62
 
60
- const dom = new JSDOM(html, { url: opts.url ?? "http://local/" });
61
- const doc = dom.window.document;
63
+ // jsdom took a `url` option to set the document base so Readability resolves
64
+ // relative links to absolute. linkedom's parseHTML has no such option, so we
65
+ // inject a <base> tag, which sets doc.baseURI to the same effect. Only when
66
+ // the HTML doesn't already carry its own <base>.
67
+ const baseUrl = opts.url ?? "http://local/";
68
+ const htmlWithBase = /<base\b/i.test(html)
69
+ ? html
70
+ : html.replace(/<head\b[^>]*>/i, (m) => `${m}<base href="${baseUrl}">`);
71
+ // If there's no <head> to anchor the <base> into, fall back to the raw HTML
72
+ // (relative links stay relative, matching a document with no base).
73
+ const { document: doc } = parseHTML(htmlWithBase.includes("<base") ? htmlWithBase : html);
62
74
  preprocessDom(doc);
63
75
 
64
76
  let contentHtml: string;