@skillrecordings/cli 0.5.0 → 0.7.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/dist/index.js CHANGED
@@ -91670,6 +91670,77 @@ function registerFaqCommands(program3) {
91670
91670
  // src/commands/front/index.ts
91671
91671
  init_esm_shims();
91672
91672
 
91673
+ // src/commands/front/api.ts
91674
+ init_esm_shims();
91675
+ function getFrontClient() {
91676
+ const apiToken = process.env.FRONT_API_TOKEN;
91677
+ if (!apiToken) {
91678
+ throw new Error("FRONT_API_TOKEN environment variable is required");
91679
+ }
91680
+ return createInstrumentedFrontClient({ apiToken });
91681
+ }
91682
+ async function apiPassthrough(method, endpoint, options) {
91683
+ const front = getFrontClient();
91684
+ const httpMethod = method.toUpperCase();
91685
+ let body = void 0;
91686
+ if (options.data) {
91687
+ try {
91688
+ body = JSON.parse(options.data);
91689
+ } catch {
91690
+ throw new Error("Invalid JSON in --data");
91691
+ }
91692
+ }
91693
+ const normalizedEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
91694
+ let result;
91695
+ switch (httpMethod) {
91696
+ case "GET":
91697
+ result = await front.raw.get(normalizedEndpoint);
91698
+ break;
91699
+ case "POST":
91700
+ result = await front.raw.post(normalizedEndpoint, body);
91701
+ break;
91702
+ case "PATCH":
91703
+ result = await front.raw.patch(normalizedEndpoint, body);
91704
+ break;
91705
+ case "PUT":
91706
+ result = await front.raw.put(normalizedEndpoint, body);
91707
+ break;
91708
+ case "DELETE":
91709
+ result = await front.raw.delete(normalizedEndpoint);
91710
+ break;
91711
+ default:
91712
+ throw new Error(
91713
+ `Unsupported method: ${method}. Use GET, POST, PATCH, PUT, or DELETE.`
91714
+ );
91715
+ }
91716
+ console.log(JSON.stringify(result, null, 2));
91717
+ }
91718
+ function registerApiCommand(frontCommand) {
91719
+ frontCommand.command("api").description("Raw Front API request (escape hatch)").argument("<method>", "HTTP method (GET, POST, PATCH, PUT, DELETE)").argument(
91720
+ "<endpoint>",
91721
+ "API endpoint path (e.g., /me, /conversations/cnv_xxx)"
91722
+ ).option("--data <json>", "Request body as JSON string").action(
91723
+ async (method, endpoint, options) => {
91724
+ try {
91725
+ await apiPassthrough(method, endpoint, options);
91726
+ } catch (error) {
91727
+ console.error(
91728
+ JSON.stringify(
91729
+ {
91730
+ error: error instanceof Error ? error.message : "Unknown error",
91731
+ method: method.toUpperCase(),
91732
+ endpoint
91733
+ },
91734
+ null,
91735
+ 2
91736
+ )
91737
+ );
91738
+ process.exit(1);
91739
+ }
91740
+ }
91741
+ );
91742
+ }
91743
+
91673
91744
  // src/commands/front/archive.ts
91674
91745
  init_esm_shims();
91675
91746
 
@@ -91721,6 +91792,31 @@ function conversationActions(convId) {
91721
91792
  description: "Archive this conversation",
91722
91793
  destructive: true
91723
91794
  },
91795
+ {
91796
+ action: "assign",
91797
+ command: `skill front assign ${convId} <teammate-id> --json`,
91798
+ description: "Assign to a teammate"
91799
+ },
91800
+ {
91801
+ action: "unassign",
91802
+ command: `skill front assign ${convId} --unassign --json`,
91803
+ description: "Remove assignee"
91804
+ },
91805
+ {
91806
+ action: "tag",
91807
+ command: `skill front tag ${convId} <tag-name-or-id> --json`,
91808
+ description: "Add a tag"
91809
+ },
91810
+ {
91811
+ action: "untag",
91812
+ command: `skill front untag ${convId} <tag-name-or-id> --json`,
91813
+ description: "Remove a tag"
91814
+ },
91815
+ {
91816
+ action: "reply",
91817
+ command: `skill front reply ${convId} --body "<text>" --json`,
91818
+ description: "Create a draft reply"
91819
+ },
91724
91820
  {
91725
91821
  action: "tags",
91726
91822
  command: `skill front tags list --json`,
@@ -91757,6 +91853,11 @@ function conversationListActions(inboxId) {
91757
91853
  action: "triage",
91758
91854
  command: `skill front triage --inbox ${inboxId} --json`,
91759
91855
  description: "Triage conversations"
91856
+ },
91857
+ {
91858
+ action: "report",
91859
+ command: `skill front report --inbox ${inboxId} --json`,
91860
+ description: "Generate inbox report"
91760
91861
  }
91761
91862
  );
91762
91863
  }
@@ -91861,7 +91962,7 @@ function teammateListLinks(teammates) {
91861
91962
  }
91862
91963
 
91863
91964
  // src/commands/front/archive.ts
91864
- function getFrontClient() {
91965
+ function getFrontClient2() {
91865
91966
  const apiToken = process.env.FRONT_API_TOKEN;
91866
91967
  if (!apiToken) {
91867
91968
  throw new Error("FRONT_API_TOKEN environment variable is required");
@@ -91885,7 +91986,7 @@ async function archiveConversation(front, convId) {
91885
91986
  }
91886
91987
  async function archiveConversations(convId, additionalIds, options) {
91887
91988
  try {
91888
- const front = getFrontClient();
91989
+ const front = getFrontClient2();
91889
91990
  const allIds = [convId, ...additionalIds];
91890
91991
  if (options.json) {
91891
91992
  const results2 = await Promise.all(
@@ -91960,6 +92061,75 @@ function registerArchiveCommand(frontCommand) {
91960
92061
  frontCommand.command("archive").description("Archive one or more conversations by ID").argument("<id>", "Conversation ID (e.g., cnv_xxx)").argument("[ids...]", "Additional conversation IDs to archive").option("--json", "Output as JSON").action(archiveConversations);
91961
92062
  }
91962
92063
 
92064
+ // src/commands/front/assign.ts
92065
+ init_esm_shims();
92066
+ function getFrontClient3() {
92067
+ const apiToken = process.env.FRONT_API_TOKEN;
92068
+ if (!apiToken) {
92069
+ throw new Error("FRONT_API_TOKEN environment variable is required");
92070
+ }
92071
+ return createInstrumentedFrontClient({ apiToken });
92072
+ }
92073
+ function normalizeId2(idOrUrl) {
92074
+ return idOrUrl.startsWith("http") ? idOrUrl.split("/").pop() : idOrUrl;
92075
+ }
92076
+ async function assignConversation(conversationId, teammateId, options) {
92077
+ try {
92078
+ const front = getFrontClient3();
92079
+ const convId = normalizeId2(conversationId);
92080
+ if (!teammateId && !options.unassign) {
92081
+ throw new Error(
92082
+ "Provide a teammate ID or use --unassign to remove assignment"
92083
+ );
92084
+ }
92085
+ if (teammateId && options.unassign) {
92086
+ throw new Error("Cannot provide both teammate ID and --unassign");
92087
+ }
92088
+ const assigneeId = options.unassign ? "" : normalizeId2(teammateId);
92089
+ await front.conversations.updateAssignee(convId, assigneeId);
92090
+ if (options.json) {
92091
+ console.log(
92092
+ JSON.stringify(
92093
+ hateoasWrap({
92094
+ type: "assign-result",
92095
+ command: options.unassign ? `skill front assign ${convId} --unassign --json` : `skill front assign ${convId} ${assigneeId} --json`,
92096
+ data: {
92097
+ id: convId,
92098
+ assignee: options.unassign ? null : assigneeId,
92099
+ success: true
92100
+ }
92101
+ }),
92102
+ null,
92103
+ 2
92104
+ )
92105
+ );
92106
+ } else {
92107
+ if (options.unassign) {
92108
+ console.log(`\u2705 Unassigned ${convId}`);
92109
+ } else {
92110
+ console.log(`\u2705 Assigned ${convId} to ${assigneeId}`);
92111
+ }
92112
+ }
92113
+ } catch (error) {
92114
+ if (options.json) {
92115
+ console.error(
92116
+ JSON.stringify({
92117
+ error: error instanceof Error ? error.message : "Unknown error"
92118
+ })
92119
+ );
92120
+ } else {
92121
+ console.error(
92122
+ "Error:",
92123
+ error instanceof Error ? error.message : "Unknown error"
92124
+ );
92125
+ }
92126
+ process.exit(1);
92127
+ }
92128
+ }
92129
+ function registerAssignCommand(frontCommand) {
92130
+ frontCommand.command("assign").description("Assign a conversation to a teammate").argument("<conversation-id>", "Conversation ID (cnv_xxx)").argument("[teammate-id]", "Teammate ID (tea_xxx) - omit with --unassign").option("--unassign", "Remove assignee").option("--json", "Output as JSON").action(assignConversation);
92131
+ }
92132
+
91963
92133
  // src/commands/front/bulk-archive.ts
91964
92134
  init_esm_shims();
91965
92135
  function parseDuration(duration) {
@@ -92247,9 +92417,133 @@ function registerBulkArchiveCommand(parent2) {
92247
92417
  ).option("--dry-run", "Preview without archiving").option("--json", "JSON output").action(bulkArchiveConversations);
92248
92418
  }
92249
92419
 
92420
+ // src/commands/front/conversation-tag.ts
92421
+ init_esm_shims();
92422
+ function getFrontClient4() {
92423
+ const apiToken = process.env.FRONT_API_TOKEN;
92424
+ if (!apiToken) {
92425
+ throw new Error("FRONT_API_TOKEN environment variable is required");
92426
+ }
92427
+ return createInstrumentedFrontClient({ apiToken });
92428
+ }
92429
+ function normalizeId3(idOrUrl) {
92430
+ return idOrUrl.startsWith("http") ? idOrUrl.split("/").pop() : idOrUrl;
92431
+ }
92432
+ async function resolveTag(front, tagNameOrId) {
92433
+ const normalized = normalizeId3(tagNameOrId);
92434
+ const data2 = await front.raw.get("/tags");
92435
+ const tags = data2._results ?? [];
92436
+ if (normalized.startsWith("tag_")) {
92437
+ const match2 = tags.find((t2) => t2.id === normalized);
92438
+ return { id: normalized, name: match2?.name ?? normalized };
92439
+ }
92440
+ const needle = tagNameOrId.trim().toLowerCase();
92441
+ const match = tags.find((t2) => t2.name.toLowerCase() === needle);
92442
+ if (!match) {
92443
+ throw new Error(
92444
+ `Tag not found: "${tagNameOrId}". Use \`skill front tags list\` to see available tags.`
92445
+ );
92446
+ }
92447
+ return { id: match.id, name: match.name };
92448
+ }
92449
+ async function tagConversation(convId, tagNameOrId, options) {
92450
+ try {
92451
+ const front = getFrontClient4();
92452
+ const normalizedConvId = normalizeId3(convId);
92453
+ const tag = await resolveTag(front, tagNameOrId);
92454
+ await front.conversations.addTag(normalizedConvId, tag.id);
92455
+ if (options.json) {
92456
+ console.log(
92457
+ JSON.stringify(
92458
+ hateoasWrap({
92459
+ type: "tag-result",
92460
+ command: `skill front tag ${normalizedConvId} ${tagNameOrId} --json`,
92461
+ data: {
92462
+ conversationId: normalizedConvId,
92463
+ tagId: tag.id,
92464
+ tagName: tag.name,
92465
+ action: "added"
92466
+ }
92467
+ }),
92468
+ null,
92469
+ 2
92470
+ )
92471
+ );
92472
+ return;
92473
+ }
92474
+ console.log(
92475
+ `
92476
+ \u2705 Tagged ${normalizedConvId} with "${tag.name}" (${tag.id})
92477
+ `
92478
+ );
92479
+ } catch (error) {
92480
+ if (options.json) {
92481
+ console.error(
92482
+ JSON.stringify({
92483
+ error: error instanceof Error ? error.message : "Unknown error"
92484
+ })
92485
+ );
92486
+ } else {
92487
+ console.error(
92488
+ "Error:",
92489
+ error instanceof Error ? error.message : "Unknown error"
92490
+ );
92491
+ }
92492
+ process.exit(1);
92493
+ }
92494
+ }
92495
+ async function untagConversation(convId, tagNameOrId, options) {
92496
+ try {
92497
+ const front = getFrontClient4();
92498
+ const normalizedConvId = normalizeId3(convId);
92499
+ const tag = await resolveTag(front, tagNameOrId);
92500
+ await front.conversations.removeTag(normalizedConvId, tag.id);
92501
+ if (options.json) {
92502
+ console.log(
92503
+ JSON.stringify(
92504
+ hateoasWrap({
92505
+ type: "untag-result",
92506
+ command: `skill front untag ${normalizedConvId} ${tagNameOrId} --json`,
92507
+ data: {
92508
+ conversationId: normalizedConvId,
92509
+ tagId: tag.id,
92510
+ tagName: tag.name,
92511
+ action: "removed"
92512
+ }
92513
+ }),
92514
+ null,
92515
+ 2
92516
+ )
92517
+ );
92518
+ return;
92519
+ }
92520
+ console.log(`
92521
+ \u2705 Removed tag "${tag.name}" from ${normalizedConvId}
92522
+ `);
92523
+ } catch (error) {
92524
+ if (options.json) {
92525
+ console.error(
92526
+ JSON.stringify({
92527
+ error: error instanceof Error ? error.message : "Unknown error"
92528
+ })
92529
+ );
92530
+ } else {
92531
+ console.error(
92532
+ "Error:",
92533
+ error instanceof Error ? error.message : "Unknown error"
92534
+ );
92535
+ }
92536
+ process.exit(1);
92537
+ }
92538
+ }
92539
+ function registerConversationTagCommands(frontCommand) {
92540
+ frontCommand.command("tag").description("Add a tag to a conversation").argument("<conversation-id>", "Conversation ID (cnv_xxx)").argument("<tag-name-or-id>", "Tag name or ID (tag_xxx)").option("--json", "Output as JSON").action(tagConversation);
92541
+ frontCommand.command("untag").description("Remove a tag from a conversation").argument("<conversation-id>", "Conversation ID (cnv_xxx)").argument("<tag-name-or-id>", "Tag name or ID (tag_xxx)").option("--json", "Output as JSON").action(untagConversation);
92542
+ }
92543
+
92250
92544
  // src/commands/front/inbox.ts
92251
92545
  init_esm_shims();
92252
- function getFrontClient2() {
92546
+ function getFrontClient5() {
92253
92547
  const apiToken = process.env.FRONT_API_TOKEN;
92254
92548
  if (!apiToken) {
92255
92549
  throw new Error("FRONT_API_TOKEN environment variable is required");
@@ -92264,12 +92558,12 @@ function formatTimestamp(ts) {
92264
92558
  minute: "2-digit"
92265
92559
  });
92266
92560
  }
92267
- function normalizeId2(idOrUrl) {
92561
+ function normalizeId4(idOrUrl) {
92268
92562
  return idOrUrl.startsWith("http") ? idOrUrl.split("/").pop() : idOrUrl;
92269
92563
  }
92270
92564
  async function findInbox(nameOrId) {
92271
- const front = getFrontClient2();
92272
- const normalizedId = normalizeId2(nameOrId);
92565
+ const front = getFrontClient5();
92566
+ const normalizedId = normalizeId4(nameOrId);
92273
92567
  if (normalizedId.startsWith("inb_")) {
92274
92568
  try {
92275
92569
  const inbox = await front.inboxes.get(normalizedId);
@@ -92286,7 +92580,7 @@ async function findInbox(nameOrId) {
92286
92580
  }
92287
92581
  async function listInboxes(options) {
92288
92582
  try {
92289
- const front = getFrontClient2();
92583
+ const front = getFrontClient5();
92290
92584
  const inboxList = await front.inboxes.list();
92291
92585
  const inboxes = inboxList._results ?? [];
92292
92586
  if (options.json) {
@@ -92337,35 +92631,59 @@ async function listInboxes(options) {
92337
92631
  }
92338
92632
  async function listConversations(inboxNameOrId, options) {
92339
92633
  try {
92340
- const front = getFrontClient2();
92634
+ const front = getFrontClient5();
92341
92635
  const inbox = await findInbox(inboxNameOrId);
92342
92636
  if (!inbox) {
92343
92637
  throw new Error(`Inbox not found: ${inboxNameOrId}`);
92344
92638
  }
92639
+ const totalLimit = parseInt(String(options.limit ?? "50"), 10);
92640
+ const resolvedLimit = Number.isFinite(totalLimit) && totalLimit > 0 ? totalLimit : void 0;
92345
92641
  const filters2 = [];
92346
92642
  if (options.status) {
92347
- filters2.push(`status:${options.status}`);
92643
+ filters2.push(`status: ${options.status}`);
92348
92644
  }
92349
92645
  if (options.tag) {
92350
- filters2.push(`tag:"${options.tag}"`);
92646
+ filters2.push(`tag: "${options.tag}"`);
92351
92647
  }
92352
- const queryParts = [];
92353
- if (filters2.length > 0) {
92354
- queryParts.push(`q=${encodeURIComponent(filters2.join(" "))}`);
92648
+ const queryParts = ["limit=50"];
92649
+ if (options.status) {
92650
+ queryParts.push(`q[statuses][]=${encodeURIComponent(options.status)}`);
92355
92651
  }
92356
- if (options.limit) {
92357
- queryParts.push(`limit=${options.limit}`);
92652
+ if (options.tag) {
92653
+ queryParts.push(`q=${encodeURIComponent(`tag:"${options.tag}"`)}`);
92654
+ }
92655
+ const queryString = `?${queryParts.join("&")}`;
92656
+ const conversations = [];
92657
+ let nextUrl = `/inboxes/${inbox.id}/conversations${queryString}`;
92658
+ while (nextUrl) {
92659
+ const response = await front.raw.get(nextUrl);
92660
+ conversations.push(...response._results ?? []);
92661
+ if (!options.json) {
92662
+ process.stdout.write(
92663
+ `\r Fetched ${conversations.length} conversations...`
92664
+ );
92665
+ }
92666
+ nextUrl = response._pagination?.next || null;
92667
+ if (resolvedLimit && conversations.length >= resolvedLimit) {
92668
+ conversations.splice(resolvedLimit);
92669
+ break;
92670
+ }
92671
+ }
92672
+ if (!options.json) {
92673
+ console.log(`
92674
+ Total: ${conversations.length} conversations
92675
+ `);
92358
92676
  }
92359
- const queryString = queryParts.length > 0 ? `?${queryParts.join("&")}` : "";
92360
- const response = await front.raw.get(`/inboxes/${inbox.id}/conversations${queryString}`);
92361
- const conversations = response._results ?? [];
92362
92677
  if (options.json) {
92363
92678
  console.log(
92364
92679
  JSON.stringify(
92365
92680
  hateoasWrap({
92366
92681
  type: "conversation-list",
92367
92682
  command: `skill front inbox ${inbox.id} --json`,
92368
- data: conversations,
92683
+ data: {
92684
+ total: conversations.length,
92685
+ conversations
92686
+ },
92369
92687
  links: conversationListLinks(
92370
92688
  conversations.map((c) => ({ id: c.id, subject: c.subject })),
92371
92689
  inbox.id
@@ -92405,12 +92723,6 @@ async function listConversations(inboxNameOrId, options) {
92405
92723
  console.log(` Created: ${time}`);
92406
92724
  }
92407
92725
  console.log("");
92408
- if (response._pagination?.next) {
92409
- console.log(
92410
- ` \u{1F4A1} More conversations available. Use --limit to adjust results.`
92411
- );
92412
- console.log("");
92413
- }
92414
92726
  } catch (error) {
92415
92727
  if (options.json) {
92416
92728
  console.error(
@@ -92593,6 +92905,74 @@ function registerPullCommand(parent2) {
92593
92905
  parent2.command("pull").description("Export conversations to JSON for eval datasets").option("-i, --inbox <id>", "Inbox ID to pull from").option("-l, --limit <n>", "Max conversations to pull", parseInt).option("-o, --output <file>", "Output file path").option("-f, --filter <term>", "Filter by subject/tag containing term").option("--json", "JSON output").action(pullConversations);
92594
92906
  }
92595
92907
 
92908
+ // src/commands/front/reply.ts
92909
+ init_esm_shims();
92910
+ function getFrontClient6() {
92911
+ const apiToken = process.env.FRONT_API_TOKEN;
92912
+ if (!apiToken) {
92913
+ throw new Error("FRONT_API_TOKEN environment variable is required");
92914
+ }
92915
+ return createInstrumentedFrontClient({ apiToken });
92916
+ }
92917
+ function normalizeId5(idOrUrl) {
92918
+ return idOrUrl.startsWith("http") ? idOrUrl.split("/").pop() : idOrUrl;
92919
+ }
92920
+ async function replyToConversation(conversationId, options) {
92921
+ try {
92922
+ const front = getFrontClient6();
92923
+ const normalizedId = normalizeId5(conversationId);
92924
+ const draft = await front.raw.post(
92925
+ `/conversations/${normalizedId}/drafts`,
92926
+ {
92927
+ body: options.body,
92928
+ ...options.author ? { author_id: options.author } : {}
92929
+ }
92930
+ );
92931
+ if (options.json) {
92932
+ console.log(
92933
+ JSON.stringify(
92934
+ hateoasWrap({
92935
+ type: "draft-reply",
92936
+ command: `skill front reply ${normalizedId} --body ${JSON.stringify(options.body)}${options.author ? ` --author ${options.author}` : ""} --json`,
92937
+ data: draft
92938
+ }),
92939
+ null,
92940
+ 2
92941
+ )
92942
+ );
92943
+ return;
92944
+ }
92945
+ const draftId = draft.id;
92946
+ const bodyPreview = options.body.length > 100 ? options.body.slice(0, 100) + "..." : options.body;
92947
+ console.log("");
92948
+ console.log(`\u{1F4DD} Draft reply created on ${normalizedId}`);
92949
+ if (draftId) {
92950
+ console.log(` Draft ID: ${draftId}`);
92951
+ }
92952
+ console.log(` Body preview: ${bodyPreview}`);
92953
+ console.log("");
92954
+ console.log(` \u{1F4A1} Review and send from Front.`);
92955
+ console.log("");
92956
+ } catch (error) {
92957
+ if (options.json) {
92958
+ console.error(
92959
+ JSON.stringify({
92960
+ error: error instanceof Error ? error.message : "Unknown error"
92961
+ })
92962
+ );
92963
+ } else {
92964
+ console.error(
92965
+ "Error:",
92966
+ error instanceof Error ? error.message : "Unknown error"
92967
+ );
92968
+ }
92969
+ process.exit(1);
92970
+ }
92971
+ }
92972
+ function registerReplyCommand(frontCommand) {
92973
+ frontCommand.command("reply").description("Create a draft reply on a conversation").argument("<conversation-id>", "Conversation ID (cnv_xxx)").requiredOption("--body <text>", "Reply body text").option("--author <teammate-id>", "Author teammate ID").option("--json", "Output as JSON").action(replyToConversation);
92974
+ }
92975
+
92596
92976
  // src/commands/front/report.ts
92597
92977
  init_esm_shims();
92598
92978
  function formatDate2(timestamp) {
@@ -92798,6 +93178,135 @@ function registerReportCommand(front) {
92798
93178
  ).option("--json", "Output as JSON").action(generateReport);
92799
93179
  }
92800
93180
 
93181
+ // src/commands/front/search.ts
93182
+ init_esm_shims();
93183
+ function getFrontClient7() {
93184
+ const apiToken = process.env.FRONT_API_TOKEN;
93185
+ if (!apiToken) {
93186
+ throw new Error("FRONT_API_TOKEN environment variable is required");
93187
+ }
93188
+ return createInstrumentedFrontClient({ apiToken });
93189
+ }
93190
+ function formatTimestamp2(ts) {
93191
+ return new Date(ts * 1e3).toLocaleString("en-US", {
93192
+ month: "short",
93193
+ day: "numeric",
93194
+ hour: "2-digit",
93195
+ minute: "2-digit"
93196
+ });
93197
+ }
93198
+ function truncate2(str2, len) {
93199
+ if (str2.length <= len) return str2;
93200
+ return str2.slice(0, len - 3) + "...";
93201
+ }
93202
+ function buildQuery(query, options) {
93203
+ const parts = [query];
93204
+ if (options.inbox) parts.push(`inbox:${options.inbox}`);
93205
+ if (options.tag) parts.push(`tag:${options.tag}`);
93206
+ if (options.assignee) parts.push(`assignee:${options.assignee}`);
93207
+ if (options.status) parts.push(`is:${options.status}`);
93208
+ if (options.from) parts.push(`from:${options.from}`);
93209
+ if (options.after) parts.push(`after:${options.after}`);
93210
+ if (options.before) parts.push(`before:${options.before}`);
93211
+ return parts.filter(Boolean).join(" ");
93212
+ }
93213
+ async function searchConversations(query, options) {
93214
+ try {
93215
+ const front = getFrontClient7();
93216
+ const fullQuery = buildQuery(query, options);
93217
+ const limit2 = parseInt(String(options.limit ?? "25"), 10);
93218
+ const resolvedLimit = Number.isFinite(limit2) && limit2 > 0 ? limit2 : void 0;
93219
+ const conversations = [];
93220
+ let nextUrl = `/conversations/search/${encodeURIComponent(fullQuery)}`;
93221
+ while (nextUrl) {
93222
+ const response = await front.raw.get(nextUrl);
93223
+ conversations.push(...response._results ?? []);
93224
+ if (!options.json) {
93225
+ process.stdout.write(`\r Searching... ${conversations.length} results`);
93226
+ }
93227
+ nextUrl = response._pagination?.next || null;
93228
+ if (resolvedLimit && conversations.length >= resolvedLimit) {
93229
+ conversations.splice(resolvedLimit);
93230
+ break;
93231
+ }
93232
+ }
93233
+ if (!options.json) {
93234
+ console.log("");
93235
+ }
93236
+ if (options.json) {
93237
+ console.log(
93238
+ JSON.stringify(
93239
+ hateoasWrap({
93240
+ type: "search-results",
93241
+ command: `skill front search ${JSON.stringify(fullQuery)} --json`,
93242
+ data: {
93243
+ query: fullQuery,
93244
+ total: conversations.length,
93245
+ conversations
93246
+ },
93247
+ links: conversationListLinks(
93248
+ conversations.map((c) => ({ id: c.id, subject: c.subject }))
93249
+ ),
93250
+ actions: options.inbox ? conversationListActions(options.inbox) : []
93251
+ }),
93252
+ null,
93253
+ 2
93254
+ )
93255
+ );
93256
+ return;
93257
+ }
93258
+ console.log(`
93259
+ \u{1F50D} Search: ${fullQuery}`);
93260
+ console.log(` ${conversations.length} results`);
93261
+ console.log("-".repeat(80));
93262
+ if (conversations.length === 0) {
93263
+ console.log(" (no conversations found)");
93264
+ console.log("");
93265
+ return;
93266
+ }
93267
+ for (const conv of conversations) {
93268
+ const statusIcon = conv.status === "archived" ? "\u{1F4E6}" : conv.status === "assigned" ? "\u{1F464}" : "\u2753";
93269
+ const time = formatTimestamp2(conv.created_at);
93270
+ const assignee = conv.assignee ? conv.assignee.email : "unassigned";
93271
+ const tags = conv.tags && conv.tags.length > 0 ? conv.tags.map((t2) => t2.name).join(", ") : "";
93272
+ console.log(
93273
+ `
93274
+ [${statusIcon}] ${truncate2(conv.subject || "(no subject)", 80)}`
93275
+ );
93276
+ console.log(` ${conv.id} ${assignee} ${time}`);
93277
+ if (tags) {
93278
+ console.log(` Tags: ${tags}`);
93279
+ }
93280
+ }
93281
+ console.log("");
93282
+ } catch (error) {
93283
+ if (options.json) {
93284
+ console.error(
93285
+ JSON.stringify({
93286
+ error: error instanceof Error ? error.message : "Unknown error"
93287
+ })
93288
+ );
93289
+ } else {
93290
+ console.error(
93291
+ "Error:",
93292
+ error instanceof Error ? error.message : "Unknown error"
93293
+ );
93294
+ }
93295
+ process.exit(1);
93296
+ }
93297
+ }
93298
+ function registerSearchCommand(frontCommand) {
93299
+ frontCommand.command("search").description(
93300
+ "Search conversations (text, subject, filters). See https://dev.frontapp.com/docs/search-1"
93301
+ ).argument(
93302
+ "<query>",
93303
+ 'Search query (text, "exact phrase", or filter syntax)'
93304
+ ).option("--inbox <id>", "Filter by inbox ID (inb_xxx)").option("--tag <id>", "Filter by tag ID (tag_xxx)").option("--assignee <id>", "Filter by assignee (tea_xxx)").option(
93305
+ "--status <status>",
93306
+ "Filter by status (open, archived, assigned, unassigned, unreplied, snoozed, resolved)"
93307
+ ).option("--from <email>", "Filter by sender email").option("--after <timestamp>", "Filter after Unix timestamp").option("--before <timestamp>", "Filter before Unix timestamp").option("--limit <n>", "Max results (default 25)", "25").option("--json", "Output as JSON").action(searchConversations);
93308
+ }
93309
+
92801
93310
  // src/commands/front/tags.ts
92802
93311
  init_esm_shims();
92803
93312
  import { confirm as confirm3 } from "@inquirer/prompts";
@@ -92869,7 +93378,7 @@ async function createTagRaw(params) {
92869
93378
  const front = createInstrumentedFrontClient({ apiToken });
92870
93379
  await front.raw.post("/tags", body);
92871
93380
  }
92872
- function truncate2(str2, len) {
93381
+ function truncate3(str2, len) {
92873
93382
  if (str2.length <= len) return str2;
92874
93383
  return str2.slice(0, len - 3) + "...";
92875
93384
  }
@@ -92962,7 +93471,7 @@ ${header} (${filteredTags.length}):`);
92962
93471
  const highlight = tag.highlight || "-";
92963
93472
  const countStr = tag.conversation_count === 0 ? "0 \u26A0\uFE0F" : tag.conversation_count.toString();
92964
93473
  console.log(
92965
- `${truncate2(tag.id, 20).padEnd(20)} ${truncate2(tag.name, 30).padEnd(30)} ${highlight.padEnd(10)} ${countStr.padEnd(8)}`
93474
+ `${truncate3(tag.id, 20).padEnd(20)} ${truncate3(tag.name, 30).padEnd(30)} ${highlight.padEnd(10)} ${countStr.padEnd(8)}`
92966
93475
  );
92967
93476
  }
92968
93477
  console.log("");
@@ -93355,7 +93864,7 @@ function registerTagCommands(frontCommand) {
93355
93864
 
93356
93865
  // src/commands/front/triage.ts
93357
93866
  init_esm_shims();
93358
- function getFrontClient3() {
93867
+ function getFrontClient8() {
93359
93868
  const apiToken = process.env.FRONT_API_TOKEN;
93360
93869
  if (!apiToken) {
93361
93870
  throw new Error("FRONT_API_TOKEN environment variable is required");
@@ -93394,7 +93903,7 @@ function categorizeConversation(conversation) {
93394
93903
  }
93395
93904
  return { category: "actionable", reason: "Real support issue" };
93396
93905
  }
93397
- function formatTimestamp2(ts) {
93906
+ function formatTimestamp3(ts) {
93398
93907
  return new Date(ts * 1e3).toLocaleString("en-US", {
93399
93908
  month: "short",
93400
93909
  day: "numeric",
@@ -93410,7 +93919,7 @@ async function triageConversations(options) {
93410
93919
  json = false
93411
93920
  } = options;
93412
93921
  try {
93413
- const front = getFrontClient3();
93922
+ const front = getFrontClient8();
93414
93923
  if (!json) {
93415
93924
  console.log(`
93416
93925
  Fetching ${status} conversations from inbox ${inbox}...`);
@@ -93491,7 +94000,7 @@ Fetching ${status} conversations from inbox ${inbox}...`);
93491
94000
  if (byCategory.actionable.length > 0) {
93492
94001
  console.log(`\u2705 ACTIONABLE (${byCategory.actionable.length}):`);
93493
94002
  for (const r of byCategory.actionable.sort((a, b) => b.created_at - a.created_at).slice(0, 10)) {
93494
- console.log(` ${r.id} - ${formatTimestamp2(r.created_at)}`);
94003
+ console.log(` ${r.id} - ${formatTimestamp3(r.created_at)}`);
93495
94004
  console.log(` From: ${r.senderEmail}`);
93496
94005
  console.log(` Subject: ${r.subject}`);
93497
94006
  console.log(` \u2192 ${r.reason}`);
@@ -93577,7 +94086,7 @@ function registerTriageCommand(front) {
93577
94086
  }
93578
94087
 
93579
94088
  // src/commands/front/index.ts
93580
- function getFrontClient4() {
94089
+ function getFrontClient9() {
93581
94090
  const apiToken = process.env.FRONT_API_TOKEN;
93582
94091
  if (!apiToken) {
93583
94092
  throw new Error("FRONT_API_TOKEN environment variable is required");
@@ -93591,7 +94100,7 @@ function getFrontSdkClient2() {
93591
94100
  }
93592
94101
  return createInstrumentedFrontClient({ apiToken });
93593
94102
  }
93594
- function formatTimestamp3(ts) {
94103
+ function formatTimestamp4(ts) {
93595
94104
  return new Date(ts * 1e3).toLocaleString("en-US", {
93596
94105
  month: "short",
93597
94106
  day: "numeric",
@@ -93599,23 +94108,23 @@ function formatTimestamp3(ts) {
93599
94108
  minute: "2-digit"
93600
94109
  });
93601
94110
  }
93602
- function truncate3(str2, len) {
94111
+ function truncate4(str2, len) {
93603
94112
  if (str2.length <= len) return str2;
93604
94113
  return str2.slice(0, len - 3) + "...";
93605
94114
  }
93606
- function normalizeId3(idOrUrl) {
94115
+ function normalizeId6(idOrUrl) {
93607
94116
  return idOrUrl.startsWith("http") ? idOrUrl.split("/").pop() : idOrUrl;
93608
94117
  }
93609
94118
  async function getMessage(id, options) {
93610
94119
  try {
93611
- const front = getFrontClient4();
93612
- const message = await front.messages.get(normalizeId3(id));
94120
+ const front = getFrontClient9();
94121
+ const message = await front.messages.get(normalizeId6(id));
93613
94122
  if (options.json) {
93614
94123
  console.log(
93615
94124
  JSON.stringify(
93616
94125
  hateoasWrap({
93617
94126
  type: "message",
93618
- command: `skill front message ${normalizeId3(id)} --json`,
94127
+ command: `skill front message ${normalizeId6(id)} --json`,
93619
94128
  data: message,
93620
94129
  links: messageLinks(message.id)
93621
94130
  }),
@@ -93629,7 +94138,7 @@ async function getMessage(id, options) {
93629
94138
  console.log(` ID: ${message.id}`);
93630
94139
  console.log(` Type: ${message.type}`);
93631
94140
  console.log(` Subject: ${message.subject || "(none)"}`);
93632
- console.log(` Created: ${formatTimestamp3(message.created_at)}`);
94141
+ console.log(` Created: ${formatTimestamp4(message.created_at)}`);
93633
94142
  if (message.author) {
93634
94143
  console.log(` Author: ${message.author.email || message.author.id}`);
93635
94144
  }
@@ -93642,7 +94151,7 @@ async function getMessage(id, options) {
93642
94151
  console.log(
93643
94152
  ` Length: ${message.body.length} chars (HTML), ${textBody.length} chars (text)`
93644
94153
  );
93645
- console.log(` Preview: ${truncate3(textBody, 500)}`);
94154
+ console.log(` Preview: ${truncate4(textBody, 500)}`);
93646
94155
  if (message.attachments && message.attachments.length > 0) {
93647
94156
  console.log(`
93648
94157
  \u{1F4CE} Attachments: ${message.attachments.length}`);
@@ -93669,17 +94178,17 @@ async function getMessage(id, options) {
93669
94178
  }
93670
94179
  async function getConversation2(id, options) {
93671
94180
  try {
93672
- const front = getFrontClient4();
93673
- const conversation = await front.conversations.get(normalizeId3(id));
94181
+ const front = getFrontClient9();
94182
+ const conversation = await front.conversations.get(normalizeId6(id));
93674
94183
  let messages;
93675
94184
  if (options.messages) {
93676
94185
  const messageList = await front.conversations.listMessages(
93677
- normalizeId3(id)
94186
+ normalizeId6(id)
93678
94187
  );
93679
94188
  messages = messageList._results ?? [];
93680
94189
  }
93681
94190
  if (options.json) {
93682
- const convId = normalizeId3(id);
94191
+ const convId = normalizeId6(id);
93683
94192
  console.log(
93684
94193
  JSON.stringify(
93685
94194
  hateoasWrap({
@@ -93699,7 +94208,7 @@ async function getConversation2(id, options) {
93699
94208
  console.log(` ID: ${conversation.id}`);
93700
94209
  console.log(` Subject: ${conversation.subject || "(none)"}`);
93701
94210
  console.log(` Status: ${conversation.status}`);
93702
- console.log(` Created: ${formatTimestamp3(conversation.created_at)}`);
94211
+ console.log(` Created: ${formatTimestamp4(conversation.created_at)}`);
93703
94212
  if (conversation.recipient) {
93704
94213
  console.log(` Recipient: ${conversation.recipient.handle}`);
93705
94214
  }
@@ -93718,11 +94227,11 @@ async function getConversation2(id, options) {
93718
94227
  for (const msg of messages) {
93719
94228
  const direction = msg.is_inbound ? "\u2190 IN" : "\u2192 OUT";
93720
94229
  const author = msg.author?.email || "unknown";
93721
- const time = formatTimestamp3(msg.created_at);
94230
+ const time = formatTimestamp4(msg.created_at);
93722
94231
  const textBody = msg.text || msg.body.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
93723
94232
  console.log(`
93724
94233
  [${direction}] ${time} - ${author}`);
93725
- console.log(` ${truncate3(textBody, 200)}`);
94234
+ console.log(` ${truncate4(textBody, 200)}`);
93726
94235
  }
93727
94236
  } else if (!options.messages) {
93728
94237
  console.log("\n (use --messages to see message history)");
@@ -93782,10 +94291,18 @@ async function listTeammates(options) {
93782
94291
  console.log("");
93783
94292
  }
93784
94293
  } catch (error) {
93785
- console.error(
93786
- "Error:",
93787
- error instanceof Error ? error.message : "Unknown error"
93788
- );
94294
+ if (options.json) {
94295
+ console.error(
94296
+ JSON.stringify({
94297
+ error: error instanceof Error ? error.message : "Unknown error"
94298
+ })
94299
+ );
94300
+ } else {
94301
+ console.error(
94302
+ "Error:",
94303
+ error instanceof Error ? error.message : "Unknown error"
94304
+ );
94305
+ }
93789
94306
  process.exit(1);
93790
94307
  }
93791
94308
  }
@@ -93822,10 +94339,18 @@ async function getTeammate(id, options) {
93822
94339
  console.log(` Available: ${teammate.is_available ? "Yes" : "No"}`);
93823
94340
  console.log("");
93824
94341
  } catch (error) {
93825
- console.error(
93826
- "Error:",
93827
- error instanceof Error ? error.message : "Unknown error"
93828
- );
94342
+ if (options.json) {
94343
+ console.error(
94344
+ JSON.stringify({
94345
+ error: error instanceof Error ? error.message : "Unknown error"
94346
+ })
94347
+ );
94348
+ } else {
94349
+ console.error(
94350
+ "Error:",
94351
+ error instanceof Error ? error.message : "Unknown error"
94352
+ );
94353
+ }
93829
94354
  process.exit(1);
93830
94355
  }
93831
94356
  }
@@ -93842,6 +94367,11 @@ function registerFrontCommands(program3) {
93842
94367
  registerTriageCommand(front);
93843
94368
  registerPullCommand(front);
93844
94369
  registerTagCommands(front);
94370
+ registerAssignCommand(front);
94371
+ registerConversationTagCommands(front);
94372
+ registerReplyCommand(front);
94373
+ registerSearchCommand(front);
94374
+ registerApiCommand(front);
93845
94375
  }
93846
94376
 
93847
94377
  // src/commands/health.ts
@@ -94368,7 +94898,7 @@ var InngestClient = class {
94368
94898
  };
94369
94899
 
94370
94900
  // src/commands/inngest/events.ts
94371
- function formatTimestamp4(iso) {
94901
+ function formatTimestamp5(iso) {
94372
94902
  const date = new Date(iso);
94373
94903
  return date.toLocaleString("en-US", {
94374
94904
  month: "short",
@@ -94392,7 +94922,7 @@ function printEventsTable(events) {
94392
94922
  console.log("-".repeat(86));
94393
94923
  for (const event of events) {
94394
94924
  console.log(
94395
- pad(event.internal_id, 24) + " " + pad(event.name, 40) + " " + pad(formatTimestamp4(event.received_at), 20)
94925
+ pad(event.internal_id, 24) + " " + pad(event.name, 40) + " " + pad(formatTimestamp5(event.received_at), 20)
94396
94926
  );
94397
94927
  }
94398
94928
  console.log("");
@@ -94408,7 +94938,7 @@ function printRunsTable(runs) {
94408
94938
  console.log("-".repeat(94));
94409
94939
  for (const run3 of runs) {
94410
94940
  console.log(
94411
- pad(run3.run_id, 30) + " " + pad(run3.function_id, 30) + " " + pad(run3.status, 12) + " " + pad(formatTimestamp4(run3.run_started_at), 20)
94941
+ pad(run3.run_id, 30) + " " + pad(run3.function_id, 30) + " " + pad(run3.status, 12) + " " + pad(formatTimestamp5(run3.run_started_at), 20)
94412
94942
  );
94413
94943
  }
94414
94944
  console.log("");
@@ -94471,7 +95001,7 @@ async function getEvent(id, options) {
94471
95001
  console.log("\n\u{1F4CB} Event Details:");
94472
95002
  console.log(` ID: ${event.internal_id}`);
94473
95003
  console.log(` Name: ${event.name}`);
94474
- console.log(` Received: ${formatTimestamp4(event.received_at)}`);
95004
+ console.log(` Received: ${formatTimestamp5(event.received_at)}`);
94475
95005
  console.log(
94476
95006
  ` Data: ${event.data ? JSON.stringify(event.data, null, 2) : "(null)"}`
94477
95007
  );
@@ -112023,7 +112553,7 @@ function formatDate3(date) {
112023
112553
  minute: "2-digit"
112024
112554
  });
112025
112555
  }
112026
- function truncate4(str2, len) {
112556
+ function truncate5(str2, len) {
112027
112557
  if (!str2) return "";
112028
112558
  if (str2.length <= len) return str2;
112029
112559
  return str2.slice(0, len - 3) + "...";
@@ -112233,7 +112763,7 @@ ${ratingIcon} [${formatDate3(r.createdAt)}] ${r.appSlug}`);
112233
112763
  console.log(` Customer: ${r.customerDisplay}`);
112234
112764
  console.log(` Category: ${r.category}`);
112235
112765
  console.log(
112236
- ` Response: ${truncate4(r.response.replace(/\n/g, " "), 200)}`
112766
+ ` Response: ${truncate5(r.response.replace(/\n/g, " "), 200)}`
112237
112767
  );
112238
112768
  console.log(` ID: ${r.actionId}`);
112239
112769
  if (r.rating) {
@@ -112392,7 +112922,7 @@ async function getResponse(actionId, options) {
112392
112922
  const time = new Date(msg.createdAt * 1e3).toLocaleString();
112393
112923
  console.log(`
112394
112924
  [${dir}] ${time} - ${msg.author ?? "unknown"}`);
112395
- console.log(truncate4(msg.body, 500));
112925
+ console.log(truncate5(msg.body, 500));
112396
112926
  }
112397
112927
  }
112398
112928
  console.log("");