@opsrev/acculynx-cli 1.14.0 → 1.15.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 +45 -5
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -102,9 +102,16 @@ async function scanJobs(client, filters) {
102
102
  });
103
103
  return { jobs, scanned: fetched.length, serverCount, complete, ...pageError ? { pageError } : {} };
104
104
  }
105
- var ENRICHERS = ["financials", "reps", "dates"];
105
+ var ENRICHERS = ["financials", "reps", "dates", "messages"];
106
106
  var asRecord = (v) => v !== null && typeof v === "object" ? v : {};
107
- async function enrichJobs(client, jobs, enrich, concurrency = 5) {
107
+ async function defaultLoadMessagesTool() {
108
+ const { createRequire: createRequire2 } = await import("module");
109
+ const require3 = createRequire2(import.meta.url);
110
+ const resolved = require3.resolve("@opsrev/acculynx-cli-unofficial");
111
+ const mod = await import(resolved);
112
+ return mod.handleExtendedToolCall;
113
+ }
114
+ async function enrichJobs(client, jobs, enrich, concurrency = 5, deps = {}) {
108
115
  let userNames = /* @__PURE__ */ new Map();
109
116
  if (enrich.includes("reps")) {
110
117
  try {
@@ -114,6 +121,8 @@ async function enrichJobs(client, jobs, enrich, concurrency = 5) {
114
121
  }
115
122
  }
116
123
  const nameOf = (id) => userNames.get(String(id)) || String(id ?? "").slice(0, 8);
124
+ let messagesToolPromise = null;
125
+ const getMessagesTool = () => messagesToolPromise ??= (deps.loadMessagesTool ?? defaultLoadMessagesTool)();
117
126
  const out = jobs.map((job) => ({ job, errors: [] }));
118
127
  let next = 0;
119
128
  async function enrichOne(entry) {
@@ -143,6 +152,17 @@ async function enrichJobs(client, jobs, enrich, concurrency = 5) {
143
152
  name: String(i.name ?? ""),
144
153
  date: String(i.date ?? "")
145
154
  }));
155
+ } else if (source === "messages") {
156
+ const tool = await getMessagesTool();
157
+ const res = await tool({ name: "acculynx_jobs_messages", args: { jobId } });
158
+ const text = String(res.content?.[0]?.text ?? "");
159
+ if (res.isError) throw new Error(text.slice(0, 200) || "messages tool error");
160
+ const data = asRecord(JSON.parse(text || "{}"));
161
+ entry.messages = (data.messages ?? []).slice(0, 3).map((msg) => ({
162
+ date: String(msg.createdDate ?? ""),
163
+ by: String(msg.createdBy ?? ""),
164
+ text: String(msg.message ?? "").replace(/\s+/g, " ").trim().slice(0, 200)
165
+ }));
146
166
  }
147
167
  } catch (error) {
148
168
  entry.errors.push({ jobId, source, message: error instanceof Error ? error.message : String(error) });
@@ -211,6 +231,14 @@ function headerLine2(report) {
211
231
  }
212
232
  return line;
213
233
  }
234
+ function messagesLine(entry, enrich) {
235
+ if (!enrich.includes("messages")) return null;
236
+ if (entry.errors.some((e) => e.source === "messages")) return " note ERR:messages";
237
+ const m = entry.messages?.[0];
238
+ if (!m) return " note: -";
239
+ const text = m.text.slice(0, 110) || "-";
240
+ return ` note ${mmdd(m.date)} ${m.by || "-"}: ${text}`;
241
+ }
214
242
  function jobLine(entry, enrich) {
215
243
  const job = entry.job;
216
244
  const id8 = String(job.id ?? "").slice(0, 8);
@@ -245,11 +273,23 @@ function jobLine(entry, enrich) {
245
273
  }
246
274
  function formatScanDigest(report) {
247
275
  const lines = [headerLine1(report), headerLine2(report)];
248
- for (const entry of report.jobs) lines.push(jobLine(entry, report.enrich));
276
+ for (const entry of report.jobs) {
277
+ lines.push(jobLine(entry, report.enrich));
278
+ const note = messagesLine(entry, report.enrich);
279
+ if (note !== null) lines.push(note);
280
+ }
249
281
  const allErrors = report.jobs.flatMap((j) => j.errors);
250
282
  if (allErrors.length > 0) {
251
283
  lines.push("errors:");
252
- for (const e of allErrors) lines.push(` ${e.jobId.slice(0, 8)} ${e.source}: ${e.message}`);
284
+ const groups = /* @__PURE__ */ new Map();
285
+ for (const e of allErrors) {
286
+ const key = `${e.source}: ${e.message}`;
287
+ const ids = groups.get(key) ?? [];
288
+ ids.push(e.jobId.slice(0, 8));
289
+ groups.set(key, ids);
290
+ }
291
+ for (const [key, ids] of groups)
292
+ lines.push(ids.length > 3 ? ` ${key} (${ids.length} jobs)` : ` ${ids.join(",")} ${key}`);
253
293
  }
254
294
  return lines.join("\n");
255
295
  }
@@ -278,7 +318,7 @@ function registerJobsCommands(parentCmd, getClient2) {
278
318
  });
279
319
  console.log(JSON.stringify(result));
280
320
  });
281
- jobs.command("scan").description("Fetch ALL matching jobs and print a digest with a coverage receipt").option("--start-date <date>", "Start date (YYYY-MM-DD)").option("--end-date <date>", "End date (YYYY-MM-DD)").option("--date-filter-type <type>", "Date field to filter on").option("--milestones <milestones>", "Filter by milestones (comma-separated)").option("--assignment <type>", "Filter by assignment: assigned, unassigned").option("--trade-type <name>", "Client-side trade-type filter (repeatable)", collect, []).option("--enrich <list>", "Comma-separated: financials,reps,dates").option("--format <fmt>", "digest or jsonl", "digest").option("--out <path>", "Also write full jsonl to this file").action(async (opts) => {
321
+ jobs.command("scan").description("Fetch ALL matching jobs and print a digest with a coverage receipt").option("--start-date <date>", "Start date (YYYY-MM-DD)").option("--end-date <date>", "End date (YYYY-MM-DD)").option("--date-filter-type <type>", "Date field to filter on").option("--milestones <milestones>", "Filter by milestones (comma-separated)").option("--assignment <type>", "Filter by assignment: assigned, unassigned").option("--trade-type <name>", "Client-side trade-type filter (repeatable)", collect, []).option("--enrich <list>", "Comma-separated: financials,reps,dates,messages").option("--format <fmt>", "digest or jsonl", "digest").option("--out <path>", "Also write full jsonl to this file").action(async (opts) => {
282
322
  const enrich = String(opts.enrich ?? "").split(",").map((s) => s.trim()).filter(Boolean);
283
323
  const badEnricher = enrich.find((e) => !ENRICHERS.includes(e));
284
324
  if (badEnricher) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opsrev/acculynx-cli",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "AccuLynx MCP server & CLI — use the AccuLynx roofing CRM in Claude Desktop, Cursor, and any MCP client. Also a JSON CLI for scripts and AI agents.",
5
5
  "repository": {
6
6
  "type": "git",