@forwardimpact/basecamp 2.9.2 → 2.9.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/basecamp",
3
- "version": "2.9.2",
3
+ "version": "2.9.3",
4
4
  "description": "Claude Code-native personal knowledge system with autonomous agents",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -30,7 +30,7 @@
30
30
  "node": ">=18.0.0"
31
31
  },
32
32
  "dependencies": {
33
- "xlsx": "^0.18.5"
33
+ "read-excel-file": "^7.0.3"
34
34
  },
35
35
  "publishConfig": {
36
36
  "access": "public"
@@ -28,9 +28,9 @@ Run this skill:
28
28
 
29
29
  - A Workday requisition export file (`.xlsx`) accessible on the filesystem
30
30
  (typically in `~/Downloads/`)
31
- - The `xlsx` npm package installed in the KB root:
31
+ - The `read-excel-file` npm package installed in the KB root:
32
32
  ```bash
33
- npm install xlsx
33
+ npm install read-excel-file
34
34
  ```
35
35
  - User identity configured in `USER.md`
36
36
 
@@ -135,9 +135,9 @@ Names may include parenthetical annotations:
135
135
 
136
136
  1. Read `USER.md` to get the user's name, email, and domain.
137
137
  2. Confirm the XLSX file path with the user (or use the provided path).
138
- 3. Ensure the `xlsx` package is installed:
138
+ 3. Ensure the `read-excel-file` package is installed:
139
139
  ```bash
140
- npm list xlsx 2>/dev/null || npm install xlsx
140
+ npm list read-excel-file 2>/dev/null || npm install read-excel-file
141
141
  ```
142
142
 
143
143
  ## Step 1: Parse the Export
@@ -19,11 +19,9 @@
19
19
  * node scripts/parse-workday.mjs <path-to-xlsx> --summary
20
20
  * node scripts/parse-workday.mjs -h|--help
21
21
  *
22
- * Requires: npm install xlsx
22
+ * Requires: npm install read-excel-file
23
23
  */
24
24
 
25
- import { readFileSync } from "node:fs";
26
-
27
25
  if (
28
26
  process.argv.includes("-h") ||
29
27
  process.argv.includes("--help") ||
@@ -39,16 +37,16 @@ Usage:
39
37
  Output (JSON):
40
38
  { requisition: { id, title, ... }, candidates: [ { name, ... }, ... ] }
41
39
 
42
- Requires: npm install xlsx`);
40
+ Requires: npm install read-excel-file`);
43
41
  process.exit(process.argv.length < 3 ? 1 : 0);
44
42
  }
45
43
 
46
- let XLSX;
44
+ let readXlsxFile;
47
45
  try {
48
- XLSX = await import("xlsx");
46
+ readXlsxFile = (await import("read-excel-file/node")).default;
49
47
  } catch {
50
48
  console.error(
51
- "Error: xlsx package not found. Install it first:\n npm install xlsx",
49
+ "Error: read-excel-file package not found. Install it first:\n npm install read-excel-file",
52
50
  );
53
51
  process.exit(1);
54
52
  }
@@ -56,13 +54,20 @@ try {
56
54
  const filePath = process.argv[2];
57
55
  const summaryMode = process.argv.includes("--summary");
58
56
 
59
- const data = readFileSync(filePath);
60
- const wb = XLSX.read(data, { type: "buffer", cellDates: true });
57
+ /** Read a sheet by number (1-indexed) or name, returning rows as arrays of strings. */
58
+ async function readSheet(file, sheet) {
59
+ const rows = await readXlsxFile(file, { sheet });
60
+ // Normalise null cells to empty strings to match previous behaviour
61
+ return rows.map((row) => row.map((cell) => (cell == null ? "" : cell)));
62
+ }
63
+
64
+ // Get sheet names to find the candidates sheet
65
+ const sheets = await readXlsxFile(filePath, { getSheets: true });
66
+ const sheetNames = sheets.map((s) => s.name);
61
67
 
62
68
  // --- Sheet 1: Requisition metadata ---
63
69
 
64
- const ws1 = wb.Sheets[wb.SheetNames[0]];
65
- const sheet1Rows = XLSX.utils.sheet_to_json(ws1, { header: 1, defval: "" });
70
+ const sheet1Rows = await readSheet(filePath, 1);
66
71
 
67
72
  /** Extract the requisition ID and title from the header row. */
68
73
  function parseReqHeader(headerText) {
@@ -113,10 +118,9 @@ const requisition = {
113
118
  // - Old format: 3+ sheets, candidates on a sheet named "Candidates" or Sheet3
114
119
  // - New format: 2 sheets, candidates on Sheet2
115
120
  const candSheetName =
116
- wb.SheetNames.find((n) => n.toLowerCase() === "candidates") ||
117
- wb.SheetNames[Math.min(2, wb.SheetNames.length - 1)];
118
- const ws3 = wb.Sheets[candSheetName];
119
- const candRows = XLSX.utils.sheet_to_json(ws3, { header: 1, defval: "" });
121
+ sheetNames.find((n) => n.toLowerCase() === "candidates") ||
122
+ sheetNames[Math.min(2, sheetNames.length - 1)];
123
+ const candRows = await readSheet(filePath, candSheetName);
120
124
 
121
125
  // Find the header row dynamically — look for a row containing "Stage"
122
126
  // Old format: row 3 (index 2). New format: row 8 (index 7).
@@ -199,7 +203,7 @@ function parseName(raw) {
199
203
  const match = name.match(/^(.+?)\s*\(([^)]+)\)\s*$/);
200
204
  if (match) {
201
205
  const annotation = match[2].trim();
202
- let ie = "";
206
+ let ie;
203
207
  if (/prior\s*worker/i.test(annotation)) ie = "External (Prior Worker)";
204
208
  else if (/internal/i.test(annotation)) ie = "Internal";
205
209
  else ie = annotation;