@skillrecordings/cli 0.8.0 → 0.10.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
@@ -91720,7 +91720,85 @@ function registerApiCommand(frontCommand) {
91720
91720
  frontCommand.command("api").description("Raw Front API request (escape hatch)").argument("<method>", "HTTP method (GET, POST, PATCH, PUT, DELETE)").argument(
91721
91721
  "<endpoint>",
91722
91722
  "API endpoint path (e.g., /me, /conversations/cnv_xxx)"
91723
- ).option("--data <json>", "Request body as JSON string").action(
91723
+ ).option("--data <json>", "Request body as JSON string").addHelpText(
91724
+ "after",
91725
+ `
91726
+ \u2501\u2501\u2501 Raw Front API Passthrough \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
91727
+
91728
+ Escape hatch for making arbitrary Front API calls. Use this when no
91729
+ typed CLI command exists for what you need.
91730
+
91731
+ ARGUMENTS
91732
+ <method> HTTP method: GET, POST, PATCH, PUT, DELETE
91733
+ <endpoint> API path (leading / is optional \u2014 both work)
91734
+
91735
+ OPTIONS
91736
+ --data <json> Request body as a valid JSON string (for POST, PATCH, PUT)
91737
+
91738
+ COMMON ENDPOINTS
91739
+ Endpoint What it returns
91740
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
91741
+ /me Authenticated identity
91742
+ /inboxes All inboxes
91743
+ /conversations/cnv_xxx Conversation details
91744
+ /conversations/cnv_xxx/messages Messages in a conversation
91745
+ /tags All tags
91746
+ /teammates All teammates
91747
+ /contacts All contacts
91748
+ /accounts All accounts
91749
+ /channels All channels
91750
+ /rules All rules
91751
+
91752
+ ENDPOINT NORMALIZATION
91753
+ Leading slash is optional. These are equivalent:
91754
+ skill front api GET /me
91755
+ skill front api GET me
91756
+
91757
+ OUTPUT
91758
+ Always JSON. Pipe to jq for filtering.
91759
+
91760
+ EXAMPLES
91761
+ # Check authenticated identity
91762
+ skill front api GET /me
91763
+
91764
+ # List all inboxes
91765
+ skill front api GET /inboxes
91766
+
91767
+ # Get a specific conversation
91768
+ skill front api GET /conversations/cnv_abc123
91769
+
91770
+ # Archive a conversation
91771
+ skill front api PATCH /conversations/cnv_abc123 --data '{"status":"archived"}'
91772
+
91773
+ # Apply a tag to a conversation
91774
+ skill front api POST /conversations/cnv_abc123/tags --data '{"tag_ids":["tag_xxx"]}'
91775
+
91776
+ # Create a new tag
91777
+ skill front api POST /tags --data '{"name":"my-new-tag","highlight":"blue"}'
91778
+
91779
+ # Delete a tag
91780
+ skill front api DELETE /tags/tag_xxx
91781
+
91782
+ # List teammates and extract emails
91783
+ skill front api GET /teammates | jq '._results[].email'
91784
+
91785
+ # Get conversation + pipe to jq for specific fields
91786
+ skill front api GET /conversations/cnv_abc123 | jq '{subject, status, assignee: .assignee.email}'
91787
+
91788
+ WHEN TO USE THIS vs TYPED COMMANDS
91789
+ Prefer typed commands when available \u2014 they have better error handling,
91790
+ pagination, and output formatting:
91791
+ skill front search (not: skill front api GET /conversations/search/...)
91792
+ skill front inbox (not: skill front api GET /inboxes)
91793
+ skill front tags list (not: skill front api GET /tags)
91794
+ skill front conversation (not: skill front api GET /conversations/cnv_xxx)
91795
+
91796
+ Use "skill front api" for endpoints without a dedicated command, or when
91797
+ you need the raw response shape.
91798
+
91799
+ Full API docs: https://dev.frontapp.com/reference
91800
+ `
91801
+ ).action(
91724
91802
  async (method, endpoint, options) => {
91725
91803
  try {
91726
91804
  await apiPassthrough(method, endpoint, options);
@@ -92059,7 +92137,47 @@ async function archiveConversations(convId, additionalIds, options) {
92059
92137
  }
92060
92138
  }
92061
92139
  function registerArchiveCommand(frontCommand) {
92062
- 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);
92140
+ 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").addHelpText(
92141
+ "after",
92142
+ `
92143
+ \u2501\u2501\u2501 Archive Conversations \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
92144
+
92145
+ Sets the status of one or more conversations to 'archived' in Front.
92146
+ Accepts conversation IDs (cnv_xxx) or full Front API URLs.
92147
+
92148
+ SINGLE CONVERSATION
92149
+ skill front archive cnv_abc123
92150
+
92151
+ MULTIPLE CONVERSATIONS
92152
+ skill front archive cnv_abc123 cnv_def456 cnv_ghi789
92153
+
92154
+ All IDs are archived concurrently. The summary shows success/failure counts.
92155
+
92156
+ JSON OUTPUT (for scripting)
92157
+ skill front archive cnv_abc123 --json
92158
+ skill front archive cnv_abc123 cnv_def456 --json
92159
+
92160
+ JSON envelope includes per-conversation success/error status.
92161
+
92162
+ BATCH PIPELINE (search \u2192 archive)
92163
+ # Archive all snoozed conversations in an inbox
92164
+ skill front search "is:snoozed" --inbox inb_4bj7r --json \\
92165
+ | jq -r '.data.conversations[].id' \\
92166
+ | xargs -I{} skill front archive {}
92167
+
92168
+ # Archive unassigned conversations older than 30 days
92169
+ skill front search "is:unassigned" --inbox inb_4bj7r --json \\
92170
+ | jq -r '.data.conversations[].id' \\
92171
+ | xargs -I{} skill front archive {}
92172
+
92173
+ For filter-based bulk archiving with built-in safety, use bulk-archive instead.
92174
+
92175
+ RELATED COMMANDS
92176
+ skill front bulk-archive Filter-based bulk archive (--dry-run, rate limiting)
92177
+ skill front conversation View conversation details before archiving
92178
+ skill front search Find conversations by query / filters
92179
+ `
92180
+ ).action(archiveConversations);
92063
92181
  }
92064
92182
 
92065
92183
  // src/commands/front/assign.ts
@@ -92128,7 +92246,58 @@ async function assignConversation(conversationId, teammateId, options) {
92128
92246
  }
92129
92247
  }
92130
92248
  function registerAssignCommand(frontCommand) {
92131
- 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);
92249
+ 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").addHelpText(
92250
+ "after",
92251
+ `
92252
+ \u2501\u2501\u2501 Assign / Unassign Conversations \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
92253
+
92254
+ Assign a conversation to a teammate, or remove the current assignee.
92255
+ Accepts conversation IDs (cnv_xxx) and teammate IDs (tea_xxx).
92256
+
92257
+ ASSIGN
92258
+ skill front assign <conversation-id> <teammate-id>
92259
+
92260
+ The teammate must exist in your Front workspace.
92261
+
92262
+ UNASSIGN
92263
+ skill front assign <conversation-id> --unassign
92264
+
92265
+ Removes the current assignee. Cannot be combined with a teammate ID.
92266
+
92267
+ FINDING IDs
92268
+ Teammate IDs:
92269
+ skill front teammates # Human-readable list
92270
+ skill front teammates --json | jq '.[].id' # Just the IDs
92271
+
92272
+ Conversation IDs:
92273
+ skill front search --inbox inb_xxx --json | jq '.data.conversations[].id'
92274
+ skill front conversation <cnv_xxx> # Verify a conversation
92275
+
92276
+ JSON OUTPUT (--json)
92277
+ Returns a HATEOAS-wrapped object:
92278
+ { type: "assign-result", data: { id, assignee, success } }
92279
+
92280
+ EXAMPLES
92281
+ # Assign a conversation to a teammate
92282
+ skill front assign cnv_abc123 tea_def456
92283
+
92284
+ # Unassign (remove assignee)
92285
+ skill front assign cnv_abc123 --unassign
92286
+
92287
+ # Assign and get JSON output
92288
+ skill front assign cnv_abc123 tea_def456 --json
92289
+
92290
+ # Pipeline: assign all unassigned convos in an inbox to a teammate
92291
+ skill front search --inbox inb_4bj7r --status unassigned --json \\
92292
+ | jq -r '.data.conversations[].id' \\
92293
+ | xargs -I{} skill front assign {} tea_def456
92294
+
92295
+ RELATED COMMANDS
92296
+ skill front teammates List teammates and their IDs
92297
+ skill front conversation <id> View conversation details + current assignee
92298
+ skill front search Find conversations by filters
92299
+ `
92300
+ ).action(assignConversation);
92132
92301
  }
92133
92302
 
92134
92303
  // src/commands/front/bulk-archive.ts
@@ -92415,7 +92584,67 @@ function registerBulkArchiveCommand(parent2) {
92415
92584
  ).option("--tag <name>", "Filter by tag name (contains)").option(
92416
92585
  "--older-than <duration>",
92417
92586
  "Filter by age (e.g., 30d, 7d, 24h, 60m)"
92418
- ).option("--dry-run", "Preview without archiving").option("--json", "JSON output").action(bulkArchiveConversations);
92587
+ ).option("--dry-run", "Preview without archiving").option("--json", "JSON output").addHelpText(
92588
+ "after",
92589
+ `
92590
+ \u2501\u2501\u2501 Bulk Archive Conversations \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
92591
+
92592
+ Archive conversations matching filter criteria from a specific inbox.
92593
+ Requires --inbox and at least one filter. Filters are AND-combined.
92594
+ Rate limiting is built in (100-150ms between API calls).
92595
+
92596
+ \u26A0\uFE0F ALWAYS use --dry-run first to preview what would be archived.
92597
+
92598
+ FILTER OPTIONS
92599
+ --sender <email> Sender email (substring match, case-insensitive)
92600
+ --subject <text> Subject line (substring match, case-insensitive)
92601
+ --status <status> Conversation status (unassigned, assigned, archived)
92602
+ --tag <name> Tag name (substring match, case-insensitive)
92603
+ --older-than <duration> Age filter \u2014 duration format: 30d, 7d, 24h, 60m
92604
+
92605
+ Filters combine with AND: --status unassigned --older-than 30d means
92606
+ conversations that are BOTH unassigned AND older than 30 days.
92607
+
92608
+ DRY RUN (preview first!)
92609
+ skill front bulk-archive --inbox inb_4bj7r --sender "mailer-daemon" --dry-run
92610
+
92611
+ Shows matching count + each conversation ID/subject/reason. No changes made.
92612
+
92613
+ EXECUTE (after verifying dry run)
92614
+ skill front bulk-archive --inbox inb_4bj7r --sender "mailer-daemon"
92615
+
92616
+ PRACTICAL EXAMPLES
92617
+ # Archive all noise from mailer-daemon
92618
+ skill front bulk-archive --inbox inb_4bj7r --sender "mailer-daemon" --dry-run
92619
+ skill front bulk-archive --inbox inb_4bj7r --sender "mailer-daemon"
92620
+
92621
+ # Archive unassigned conversations older than 30 days
92622
+ skill front bulk-archive --inbox inb_4bj7r --status unassigned --older-than 30d --dry-run
92623
+
92624
+ # Archive everything tagged "spam"
92625
+ skill front bulk-archive --inbox inb_4bj7r --tag "spam" --dry-run
92626
+
92627
+ # Archive old daily report emails
92628
+ skill front bulk-archive --inbox inb_4bj7r --subject "Daily Report" --older-than 7d --dry-run
92629
+
92630
+ # List available inboxes (omit --inbox)
92631
+ skill front bulk-archive
92632
+
92633
+ JSON OUTPUT (for scripting)
92634
+ skill front bulk-archive --inbox inb_4bj7r --status unassigned --dry-run --json
92635
+
92636
+ # Count matches
92637
+ skill front bulk-archive ... --dry-run --json | jq '.data.matches | length'
92638
+
92639
+ # Extract matched IDs
92640
+ skill front bulk-archive ... --dry-run --json | jq -r '.data.matches[].id'
92641
+
92642
+ RELATED COMMANDS
92643
+ skill front triage Categorize conversations before archiving
92644
+ skill front archive Archive specific conversations by ID
92645
+ skill front search Find conversations by query / filters
92646
+ `
92647
+ ).action(bulkArchiveConversations);
92419
92648
  }
92420
92649
 
92421
92650
  // src/commands/front/conversation-tag.ts
@@ -92538,8 +92767,87 @@ async function untagConversation(convId, tagNameOrId, options) {
92538
92767
  }
92539
92768
  }
92540
92769
  function registerConversationTagCommands(frontCommand) {
92541
- 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);
92542
- 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);
92770
+ 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").addHelpText(
92771
+ "after",
92772
+ `
92773
+ \u2501\u2501\u2501 Tag a Conversation \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
92774
+
92775
+ Add a tag to a conversation. Accepts a tag name OR a tag ID (tag_xxx).
92776
+ Name lookup is case-insensitive \u2014 "Billing", "billing", and "BILLING" all work.
92777
+
92778
+ USAGE
92779
+ skill front tag <conversation-id> <tag-name-or-id>
92780
+
92781
+ TAG RESOLUTION
92782
+ By name: skill front tag cnv_abc123 "billing" # case-insensitive
92783
+ By ID: skill front tag cnv_abc123 tag_14nmdp # exact ID
92784
+
92785
+ If the name doesn't match any existing tag, the command errors with a hint
92786
+ to run \`skill front tags list\` to see available tags.
92787
+
92788
+ FINDING TAGS
92789
+ skill front tags list # Human-readable list
92790
+ skill front tags list --json | jq '.[].id' # Just the IDs
92791
+ skill front tags list --json | jq '.[] | {id, name}' # IDs + names
92792
+
92793
+ JSON OUTPUT (--json)
92794
+ Returns a HATEOAS-wrapped object:
92795
+ { type: "tag-result", data: { conversationId, tagId, tagName, action: "added" } }
92796
+
92797
+ EXAMPLES
92798
+ # Tag by name
92799
+ skill front tag cnv_abc123 "needs-review"
92800
+
92801
+ # Tag by ID
92802
+ skill front tag cnv_abc123 tag_14nmdp
92803
+
92804
+ # Tag and get JSON output
92805
+ skill front tag cnv_abc123 "billing" --json
92806
+
92807
+ RELATED COMMANDS
92808
+ skill front untag <id> <tag> Remove a tag from a conversation
92809
+ skill front tags list List all available tags
92810
+ skill front conversation <id> View conversation details + current tags
92811
+ `
92812
+ ).action(tagConversation);
92813
+ 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").addHelpText(
92814
+ "after",
92815
+ `
92816
+ \u2501\u2501\u2501 Untag a Conversation \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
92817
+
92818
+ Remove a tag from a conversation. Accepts a tag name OR a tag ID (tag_xxx).
92819
+ Name lookup is case-insensitive \u2014 "Billing", "billing", and "BILLING" all work.
92820
+
92821
+ USAGE
92822
+ skill front untag <conversation-id> <tag-name-or-id>
92823
+
92824
+ TAG RESOLUTION
92825
+ By name: skill front untag cnv_abc123 "billing" # case-insensitive
92826
+ By ID: skill front untag cnv_abc123 tag_14nmdp # exact ID
92827
+
92828
+ If the name doesn't match any existing tag, the command errors with a hint
92829
+ to run \`skill front tags list\` to see available tags.
92830
+
92831
+ JSON OUTPUT (--json)
92832
+ Returns a HATEOAS-wrapped object:
92833
+ { type: "untag-result", data: { conversationId, tagId, tagName, action: "removed" } }
92834
+
92835
+ EXAMPLES
92836
+ # Untag by name
92837
+ skill front untag cnv_abc123 "needs-review"
92838
+
92839
+ # Untag by ID
92840
+ skill front untag cnv_abc123 tag_14nmdp
92841
+
92842
+ # Untag and get JSON output
92843
+ skill front untag cnv_abc123 "billing" --json
92844
+
92845
+ RELATED COMMANDS
92846
+ skill front tag <id> <tag> Add a tag to a conversation
92847
+ skill front tags list List all available tags
92848
+ skill front conversation <id> View conversation details + current tags
92849
+ `
92850
+ ).action(untagConversation);
92543
92851
  }
92544
92852
 
92545
92853
  // src/commands/front/inbox.ts
@@ -92748,7 +93056,100 @@ function registerInboxCommand(front) {
92748
93056
  ).option("--json", "Output as JSON").option(
92749
93057
  "--status <status>",
92750
93058
  "Filter by status (unassigned, assigned, archived)"
92751
- ).option("--tag <tag>", "Filter by tag name").option("--limit <n>", "Limit number of results", "50").action(async (inboxNameOrId, options) => {
93059
+ ).option("--tag <tag>", "Filter by tag name").option("--limit <n>", "Limit number of results", "50").addHelpText(
93060
+ "after",
93061
+ `
93062
+ \u2501\u2501\u2501 Front Inbox Command \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
93063
+
93064
+ Two modes: list all inboxes, or list conversations in a specific inbox.
93065
+
93066
+ MODE 1: LIST ALL INBOXES (no argument)
93067
+ skill front inbox Show all inboxes (ID, name, privacy)
93068
+ skill front inbox --json JSON output with HATEOAS links
93069
+
93070
+ MODE 2: LIST CONVERSATIONS IN AN INBOX (with argument)
93071
+ skill front inbox <inbox-name-or-id> List conversations in an inbox
93072
+ skill front inbox inb_4bj7r By inbox ID
93073
+ skill front inbox "AI Hero" By name (case-insensitive exact match)
93074
+
93075
+ INBOX NAME LOOKUP
93076
+ Pass a human-readable name instead of inb_xxx. The command fetches all
93077
+ inboxes and matches case-insensitively against the full name.
93078
+ Example: "ai hero" matches "AI Hero", "Total TypeScript" matches exactly.
93079
+ If no match is found, an error is thrown with the name you provided.
93080
+
93081
+ OPTIONS
93082
+ Flag Default Description
93083
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
93084
+ --status <status> (none) Filter conversations by status
93085
+ --tag <tag> (none) Filter by tag name or tag_xxx ID
93086
+ --limit <n> 50 Max conversations to return (pages auto)
93087
+ --json false Output as JSON (HATEOAS envelope)
93088
+
93089
+ STATUS VALUES (--status flag)
93090
+ open In the Open tab (not archived, trashed, or snoozed)
93091
+ archived In the Archived tab
93092
+ assigned Has an assignee
93093
+ unassigned No assignee
93094
+ unreplied Last message was inbound (no teammate reply yet)
93095
+ snoozed Snoozed (will reopen later)
93096
+ trashed In Trash
93097
+ waiting Waiting for response
93098
+
93099
+ TAG FILTER (--tag flag)
93100
+ Filter by tag name (human-readable) or tag ID (tag_xxx).
93101
+ The tag value is passed as a Front query filter: tag:"<value>"
93102
+ Examples:
93103
+ --tag "500 Error" Filter by tag name
93104
+ --tag tag_14nmdp Filter by tag ID
93105
+
93106
+ PAGINATION
93107
+ Page size is always 50. If --limit is higher than 50, the command
93108
+ automatically paginates through results until the limit is reached.
93109
+ Progress is shown in the terminal (e.g., "Fetched 150 conversations...").
93110
+
93111
+ JSON + jq PATTERNS
93112
+ # List all inbox IDs
93113
+ skill front inbox --json | jq '.data[].id'
93114
+
93115
+ # Get inbox names and IDs as a table
93116
+ skill front inbox --json | jq '.data[] | {id, name}'
93117
+
93118
+ # Get conversation IDs from an inbox
93119
+ skill front inbox inb_4bj7r --json | jq '.data.conversations[].id'
93120
+
93121
+ # Get conversation summaries
93122
+ skill front inbox inb_4bj7r --json | jq '.data.conversations[] | {id, subject, status}'
93123
+
93124
+ # Count conversations by status
93125
+ skill front inbox inb_4bj7r --json | jq '[.data.conversations[].status] | group_by(.) | map({status: .[0], count: length})'
93126
+
93127
+ EXAMPLES
93128
+ # Step 1: Find inbox IDs
93129
+ skill front inbox
93130
+
93131
+ # Step 2: Look up inbox by name
93132
+ skill front inbox "Total TypeScript"
93133
+
93134
+ # Step 3: Filter unassigned conversations
93135
+ skill front inbox inb_4bj7r --status unassigned
93136
+
93137
+ # Step 4: Filter by tag
93138
+ skill front inbox inb_4bj7r --tag "500 Error"
93139
+
93140
+ # Step 5: Pipe to jq for conversation IDs
93141
+ skill front inbox inb_4bj7r --status open --json | jq '.data.conversations[].id'
93142
+
93143
+ # Limit results to 10
93144
+ skill front inbox inb_4bj7r --limit 10
93145
+
93146
+ RELATED COMMANDS
93147
+ skill front conversation <id> View full conversation with messages
93148
+ skill front search <query> Search across all inboxes with filters
93149
+ skill front report Generate support metrics report
93150
+ skill front triage Triage unassigned conversations
93151
+ `
93152
+ ).action(async (inboxNameOrId, options) => {
92752
93153
  if (!inboxNameOrId) {
92753
93154
  await listInboxes(options || {});
92754
93155
  } else {
@@ -92903,7 +93304,73 @@ Saved to ${output}`);
92903
93304
  }
92904
93305
  }
92905
93306
  function registerPullCommand(parent2) {
92906
- 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);
93307
+ 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").addHelpText(
93308
+ "after",
93309
+ `
93310
+ \u2501\u2501\u2501 Pull Conversations (Eval Dataset Export) \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
93311
+
93312
+ Export conversations from a Front inbox as structured EvalSample objects.
93313
+ Designed for building eval datasets for routing, classification, and
93314
+ canned-response testing.
93315
+
93316
+ OPTIONS
93317
+ -i, --inbox <id> Inbox ID to pull from (inb_xxx). Omit to list
93318
+ available inboxes and their IDs.
93319
+ -l, --limit <n> Max conversations to export (default: 50)
93320
+ -o, --output <file> Write output to a file instead of stdout
93321
+ -f, --filter <term> Only include conversations whose subject or tags
93322
+ contain this text (case-insensitive)
93323
+ --json Output as JSON (HATEOAS-wrapped)
93324
+
93325
+ OUTPUT FORMAT (EvalSample)
93326
+ Each sample includes:
93327
+ - id / conversationId Front conversation ID
93328
+ - subject Conversation subject
93329
+ - customerEmail Sender email address
93330
+ - status Conversation status
93331
+ - tags Array of tag names
93332
+ - triggerMessage Most recent inbound message (id, subject, body, timestamp)
93333
+ - conversationHistory Full message thread (direction, body, timestamp, author)
93334
+ - category Inferred category (see below)
93335
+
93336
+ CATEGORY INFERENCE
93337
+ Category Rule
93338
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
93339
+ refund Tag or subject contains "refund"
93340
+ access Tag contains "access" or subject contains "login"/"access"
93341
+ technical Tag contains "technical" or subject contains "error"/"bug"
93342
+ feedback Subject contains "feedback" or "suggestion"
93343
+ business Subject contains "partnership" or "collaborate"
93344
+ general Everything else (default)
93345
+
93346
+ RATE LIMITING
93347
+ Built-in 100ms delay between conversation message fetches to respect
93348
+ Front API limits. Large exports will take time proportional to --limit.
93349
+
93350
+ EXAMPLES
93351
+ # List available inboxes (no --inbox flag)
93352
+ skill front pull
93353
+
93354
+ # Pull 50 conversations (default limit)
93355
+ skill front pull --inbox inb_4bj7r
93356
+
93357
+ # Pull 200 conversations and save to file
93358
+ skill front pull --inbox inb_4bj7r --limit 200 --output data/eval-dataset.json
93359
+
93360
+ # Pull only refund-related conversations
93361
+ skill front pull --inbox inb_4bj7r --filter refund --output data/refund-samples.json
93362
+
93363
+ # Pipe JSON for analysis
93364
+ skill front pull --inbox inb_4bj7r --json | jq '[.data[] | {id, category, subject}]'
93365
+
93366
+ # Category breakdown
93367
+ skill front pull --inbox inb_4bj7r --json | jq '[.data[].category] | group_by(.) | map({(.[0]): length}) | add'
93368
+
93369
+ RELATED COMMANDS
93370
+ skill eval routing <file> Run routing eval against a dataset
93371
+ skill front inbox List inboxes
93372
+ `
93373
+ ).action(pullConversations);
92907
93374
  }
92908
93375
 
92909
93376
  // src/commands/front/reply.ts
@@ -92971,7 +93438,62 @@ async function replyToConversation(conversationId, options) {
92971
93438
  }
92972
93439
  }
92973
93440
  function registerReplyCommand(frontCommand) {
92974
- 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);
93441
+ 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").addHelpText(
93442
+ "after",
93443
+ `
93444
+ \u2501\u2501\u2501 Draft Reply (HITL \u2014 Human-in-the-Loop) \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
93445
+
93446
+ \u26A0\uFE0F SAFETY: This command creates a DRAFT only. It NEVER auto-sends.
93447
+ The draft appears in Front for a human to review, edit, and send manually.
93448
+ This is by design \u2014 the HITL principle ensures no message goes out without
93449
+ human approval.
93450
+
93451
+ USAGE
93452
+ skill front reply <conversation-id> --body "Your reply text here"
93453
+
93454
+ OPTIONS
93455
+ --body <text> Required. The reply body text.
93456
+ Accepts plain text or HTML.
93457
+ --author <teammate-id> Optional. Teammate ID (tea_xxx) to set as sender.
93458
+ Defaults to the API token owner.
93459
+ --json Output as JSON.
93460
+
93461
+ BODY FORMAT
93462
+ Plain text: --body "Thanks for reaching out. We'll look into this."
93463
+ HTML: --body "<p>Hi there,</p><p>Your refund has been processed.</p>"
93464
+
93465
+ For multi-line plain text, the body will render as-is in the Front draft.
93466
+
93467
+ JSON OUTPUT (--json)
93468
+ Returns a HATEOAS-wrapped object:
93469
+ { type: "draft-reply", data: { id, ... } }
93470
+
93471
+ WORKFLOW
93472
+ 1. Read the conversation first:
93473
+ skill front conversation cnv_abc123 -m
93474
+ 2. Draft a reply:
93475
+ skill front reply cnv_abc123 --body "We've processed your request."
93476
+ 3. Open Front \u2192 review the draft \u2192 edit if needed \u2192 click Send.
93477
+
93478
+ EXAMPLES
93479
+ # Simple draft reply
93480
+ skill front reply cnv_abc123 --body "Thanks, we're looking into this now."
93481
+
93482
+ # HTML reply with specific author
93483
+ skill front reply cnv_abc123 \\
93484
+ --body "<p>Hi! Your license has been transferred.</p>" \\
93485
+ --author tea_def456
93486
+
93487
+ # Draft reply and capture the draft ID
93488
+ skill front reply cnv_abc123 --body "Processing your refund." --json \\
93489
+ | jq '.data.id'
93490
+
93491
+ RELATED COMMANDS
93492
+ skill front conversation <id> -m View conversation + message history
93493
+ skill front message <id> View a specific message body
93494
+ skill front search Find conversations to reply to
93495
+ `
93496
+ ).action(replyToConversation);
92975
93497
  }
92976
93498
 
92977
93499
  // src/commands/front/report.ts
@@ -93176,7 +93698,64 @@ function registerReportCommand(front) {
93176
93698
  "Number of days to include in report",
93177
93699
  parseInt,
93178
93700
  30
93179
- ).option("--json", "Output as JSON").action(generateReport);
93701
+ ).option("--json", "Output as JSON").addHelpText(
93702
+ "after",
93703
+ `
93704
+ \u2501\u2501\u2501 Inbox Forensics Report \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
93705
+
93706
+ Generates a comprehensive report for a Front inbox covering the last N days.
93707
+ --inbox is required. --days defaults to 30.
93708
+
93709
+ WHAT THE REPORT INCLUDES
93710
+ - Overview: total conversations, status breakdown with percentages
93711
+ - Volume by week: bar chart of conversation volume per ISO week
93712
+ - Tag breakdown: top 15 tags by frequency
93713
+ - Top senders: top 10 sender email addresses
93714
+ - Unresolved issues: unassigned conversations (newest first, up to 10 shown)
93715
+
93716
+ BASIC USAGE
93717
+ skill front report --inbox inb_4bj7r
93718
+ skill front report --inbox inb_4bj7r --days 60
93719
+ skill front report --inbox inb_4bj7r --days 7
93720
+
93721
+ JSON OUTPUT (for scripting)
93722
+ skill front report --inbox inb_4bj7r --json
93723
+
93724
+ # Extract unresolved issues
93725
+ skill front report --inbox inb_4bj7r --json | jq '.data.unresolvedIssues'
93726
+
93727
+ # Top senders
93728
+ skill front report --inbox inb_4bj7r --json | jq '.data.topSenders'
93729
+
93730
+ # Volume by week
93731
+ skill front report --inbox inb_4bj7r --json | jq '.data.volumeByWeek'
93732
+
93733
+ # Status breakdown
93734
+ skill front report --inbox inb_4bj7r --json | jq '.data.overview.byStatus'
93735
+
93736
+ # Count of unassigned conversations
93737
+ skill front report --inbox inb_4bj7r --json | jq '.data.unresolvedIssues | length'
93738
+
93739
+ # Senders with more than 5 conversations
93740
+ skill front report --inbox inb_4bj7r --json \\
93741
+ | jq '[.data.topSenders[] | select(.count > 5)]'
93742
+
93743
+ WORKFLOW: REPORT \u2192 TRIAGE \u2192 ARCHIVE
93744
+ # 1. Run report to understand inbox state
93745
+ skill front report --inbox inb_4bj7r
93746
+
93747
+ # 2. Triage to categorize conversations
93748
+ skill front triage --inbox inb_4bj7r
93749
+
93750
+ # 3. Bulk archive the noise
93751
+ skill front bulk-archive --inbox inb_4bj7r --sender "noreply@" --dry-run
93752
+
93753
+ RELATED COMMANDS
93754
+ skill front triage Categorize conversations by intent
93755
+ skill front inbox List and inspect inboxes
93756
+ skill front bulk-archive Archive conversations matching filters
93757
+ `
93758
+ ).action(generateReport);
93180
93759
  }
93181
93760
 
93182
93761
  // src/commands/front/search.ts
@@ -93297,10 +93876,10 @@ async function searchConversations(query, options) {
93297
93876
  }
93298
93877
  }
93299
93878
  function registerSearchCommand(frontCommand) {
93300
- frontCommand.command("search").description(
93879
+ const searchCmd = frontCommand.command("search").description(
93301
93880
  "Search conversations (text, subject, filters). See https://dev.frontapp.com/docs/search-1"
93302
93881
  ).argument(
93303
- "<query>",
93882
+ "[query]",
93304
93883
  'Search query (text, "exact phrase", or filter syntax)'
93305
93884
  ).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(
93306
93885
  "--status <status>",
@@ -93308,31 +93887,90 @@ function registerSearchCommand(frontCommand) {
93308
93887
  ).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").addHelpText(
93309
93888
  "after",
93310
93889
  `
93311
- Front Search Query Syntax:
93312
- Text search: "exact phrase" or word1 word2 (AND logic)
93313
- inbox:inb_xxx Filter by inbox
93314
- tag:tag_xxx Filter by tag
93315
- from:email Filter by sender
93316
- to:email Filter by recipient
93317
- recipient:email Filter by email/handle
93318
- assignee:tea_x Filter by assigned teammate
93319
- participant:t Filter by participating teammate
93320
- is:open Status: open, archived, assigned, unassigned,
93321
- unreplied, snoozed, resolved, waiting
93322
- before:<ts> Before Unix timestamp
93323
- after:<ts> After Unix timestamp
93324
- during:<ts> During day of Unix timestamp
93325
- custom_field:"Name=value"
93326
-
93327
- Multiple filters = AND. Multiple from/to/cc/bcc = OR. Max 15 filters.
93328
-
93329
- Examples:
93330
- skill front search "payment failed" --inbox inb_4bj7r
93331
- skill front search "upgrade" --status unassigned --from user@example.com
93332
- skill front search "from:dale@a.com tag:tag_14nmdp" --limit 50
93333
- skill front search "is:unreplied" --inbox inb_4bj7r --after 1706745600
93890
+ \u2501\u2501\u2501 Front Search Query Syntax \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
93891
+
93892
+ The <query> argument accepts free text and/or inline Front filters.
93893
+ CLI flags (--inbox, --status, etc.) are appended as filters automatically.
93894
+ You can mix both: skill front search "refund" --inbox inb_4bj7r --status open
93895
+
93896
+ TEXT SEARCH
93897
+ word1 word2 AND \u2014 both words must appear in subject or body
93898
+ "exact phrase" Phrase match (quote the phrase)
93899
+
93900
+ FILTERS (use inline in query OR via CLI flags)
93901
+ Filter CLI flag What it matches
93902
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
93903
+ inbox:inb_xxx --inbox <id> Conversations in inbox
93904
+ tag:tag_xxx --tag <id> Conversations with tag
93905
+ from:email --from <email> Sender address
93906
+ to:email (inline only) Recipient address
93907
+ cc:email (inline only) CC'd address
93908
+ bcc:email (inline only) BCC'd address
93909
+ recipient:email (inline only) Any role (from/to/cc/bcc)
93910
+ contact:crd_xxx (inline only) Contact ID in any role
93911
+ assignee:tea_xxx --assignee <id> Assigned teammate
93912
+ author:tea_xxx (inline only) Message author (teammate)
93913
+ participant:tea_xxx (inline only) Any teammate involvement
93914
+ mention:tea_xxx (inline only) Mentioned teammate
93915
+ commenter:tea_xxx (inline only) Commenting teammate
93916
+ link:top_xxx (inline only) Linked topic
93917
+ is:<status> --status <status> Conversation status (see below)
93918
+ before:<unix_ts> --before <timestamp> Messages before timestamp
93919
+ after:<unix_ts> --after <timestamp> Messages after timestamp
93920
+ during:<unix_ts> (inline only) Messages on same day as timestamp
93921
+ custom_field:"K=V" (inline only) Custom field value
93922
+
93923
+ STATUS VALUES (is: filter / --status flag)
93924
+ open In the Open tab (not archived, not trashed, not snoozed)
93925
+ archived In the Archived tab
93926
+ assigned Has an assignee (can combine: is:open is:assigned)
93927
+ unassigned No assignee
93928
+ unreplied Last message was inbound (no teammate reply yet)
93929
+ snoozed Snoozed (will reopen later; API status shows "archived")
93930
+ trashed In Trash
93931
+ waiting Waiting for response
93932
+
93933
+ Status combos: is:open + is:unassigned = open & unassigned
93934
+ is:archived + is:assigned = archived & assigned
93935
+ Conflicts: open vs archived vs trashed vs snoozed are mutually exclusive
93936
+ assigned vs unassigned are mutually exclusive
93937
+
93938
+ FILTER LOGIC
93939
+ All filters combine with AND (results must match every filter).
93940
+ Exception: multiple from/to/cc/bcc use OR within the same filter type.
93941
+ from:a@x.com from:b@x.com \u2192 from A OR from B
93942
+ from:a@x.com to:b@x.com \u2192 from A AND to B
93943
+ Max 15 filters per query.
93944
+
93945
+ EXAMPLES
93946
+ # Find unresolved payment issues in AI Hero inbox
93947
+ skill front search "payment failed" --inbox inb_4bj7r --status unassigned
93948
+
93949
+ # Unreplied conversations from a specific sender
93950
+ skill front search "upgrade" --from user@example.com --status unreplied
93951
+
93952
+ # Complex inline query (filters in the query string itself)
93953
+ skill front search "from:dale@a.com from:laura@a.com tag:tag_14nmdp before:1650364200"
93954
+
93955
+ # All snoozed conversations assigned to a teammate
93956
+ skill front search "is:snoozed assignee:tea_2thf" --inbox inb_4bj7r
93957
+
93958
+ # Search by custom field
93959
+ skill front search 'custom_field:"External ID=12345"'
93960
+
93961
+ # Pipe JSON to jq for IDs only
93962
+ skill front search "is:unassigned" --inbox inb_4bj7r --json | jq '.data.conversations[].id'
93963
+
93964
+ Full docs: https://dev.frontapp.com/docs/search-1
93334
93965
  `
93335
- ).action(searchConversations);
93966
+ ).action((query, options) => {
93967
+ const hasFilters = options.inbox || options.tag || options.assignee || options.status || options.from || options.after || options.before;
93968
+ if (!query && !hasFilters) {
93969
+ searchCmd.help();
93970
+ return;
93971
+ }
93972
+ return searchConversations(query || "", options);
93973
+ });
93336
93974
  }
93337
93975
 
93338
93976
  // src/commands/front/tags.ts
@@ -93881,13 +94519,146 @@ async function cleanupTags(options) {
93881
94519
  }
93882
94520
  }
93883
94521
  function registerTagCommands(frontCommand) {
93884
- const tags = frontCommand.command("tags").description("List, rename, delete, and clean up Front tags");
93885
- tags.command("list").description("List all tags with conversation counts").option("--json", "Output as JSON").option("--unused", "Show only tags with 0 conversations").action(listTags);
93886
- tags.command("delete").description("Delete a tag by ID").argument("<id>", "Tag ID (e.g., tag_xxx)").option("-f, --force", "Skip confirmation prompt").action(deleteTag);
93887
- tags.command("rename").description("Rename a tag").argument("<id>", "Tag ID (e.g., tag_xxx)").argument("<name>", "New tag name").action(renameTag);
94522
+ const tags = frontCommand.command("tags").description("List, rename, delete, and clean up Front tags").addHelpText(
94523
+ "after",
94524
+ `
94525
+ \u2501\u2501\u2501 Tag Management \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
94526
+
94527
+ Manage Front tags: list with usage counts, delete unused, rename,
94528
+ and bulk-clean duplicates / case variants / obsolete tags.
94529
+
94530
+ SUBCOMMANDS
94531
+ list List all tags with conversation counts (--unused, --json)
94532
+ delete Delete a tag by ID (tag_xxx)
94533
+ rename Rename a tag (tag_xxx \u2192 new name)
94534
+ cleanup Find and fix duplicate, case-variant, and obsolete tags
94535
+
94536
+ EXAMPLES
94537
+ skill front tags list
94538
+ skill front tags list --unused --json
94539
+ skill front tags delete tag_abc123 --force
94540
+ skill front tags rename tag_abc123 "billing-issue"
94541
+ skill front tags cleanup
94542
+ skill front tags cleanup --execute
94543
+
94544
+ RELATED COMMANDS
94545
+ skill front tag <cnv_xxx> <tag_xxx> Apply a tag to a conversation
94546
+ skill front untag <cnv_xxx> <tag_xxx> Remove a tag from a conversation
94547
+ `
94548
+ );
94549
+ tags.command("list").description("List all tags with conversation counts").option("--json", "Output as JSON").option("--unused", "Show only tags with 0 conversations").addHelpText(
94550
+ "after",
94551
+ `
94552
+ \u2501\u2501\u2501 Tag List \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
94553
+
94554
+ Lists every tag in the Front workspace with its conversation count.
94555
+ Conversation counts are fetched per-tag (rate-limited, ~5 concurrent).
94556
+
94557
+ OPTIONS
94558
+ --unused Show only tags with 0 conversations (candidates for deletion)
94559
+ --json Output as JSON (HATEOAS-wrapped with links and actions)
94560
+
94561
+ OUTPUT COLUMNS (table mode)
94562
+ ID Tag ID (tag_xxx)
94563
+ Name Tag display name
94564
+ Color Highlight color
94565
+ Convos Number of conversations using this tag (0 shows warning)
94566
+
94567
+ JSON + jq PATTERNS
94568
+ # All unused tags
94569
+ skill front tags list --json | jq '.data[] | select(.conversation_count == 0)'
94570
+
94571
+ # Tag names only
94572
+ skill front tags list --json | jq '.data[].name'
94573
+
94574
+ # Tags sorted by usage (most \u2192 least)
94575
+ skill front tags list --json | jq '.data | sort_by(-.conversation_count)'
94576
+
94577
+ # Count of unused tags
94578
+ skill front tags list --json | jq '[.data[] | select(.conversation_count == 0)] | length'
94579
+
94580
+ NOTE
94581
+ Fetching counts for many tags can take a while due to Front API rate limits.
94582
+ The command batches requests (5 at a time, 100ms between batches).
94583
+ `
94584
+ ).action(listTags);
94585
+ tags.command("delete").description("Delete a tag by ID").argument("<id>", "Tag ID (e.g., tag_xxx)").option("-f, --force", "Skip confirmation prompt").addHelpText(
94586
+ "after",
94587
+ `
94588
+ \u2501\u2501\u2501 Tag Delete \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
94589
+
94590
+ Delete a single tag by its Front ID.
94591
+
94592
+ ARGUMENTS
94593
+ <id> Tag ID in tag_xxx format (find IDs via: skill front tags list --json)
94594
+
94595
+ OPTIONS
94596
+ -f, --force Skip the confirmation prompt (use in scripts)
94597
+
94598
+ BEHAVIOR
94599
+ - Shows tag name, ID, and conversation count before prompting
94600
+ - Warns if the tag is still applied to conversations
94601
+ - Deleting a tag removes it from all conversations that use it
94602
+ - This action is irreversible
94603
+
94604
+ EXAMPLES
94605
+ skill front tags delete tag_abc123
94606
+ skill front tags delete tag_abc123 --force
94607
+ `
94608
+ ).action(deleteTag);
94609
+ tags.command("rename").description("Rename a tag").argument("<id>", "Tag ID (e.g., tag_xxx)").argument("<name>", "New tag name").addHelpText(
94610
+ "after",
94611
+ `
94612
+ \u2501\u2501\u2501 Tag Rename \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
94613
+
94614
+ Rename a tag. The tag keeps its ID and stays applied to all conversations.
94615
+
94616
+ ARGUMENTS
94617
+ <id> Tag ID in tag_xxx format
94618
+ <name> New display name for the tag
94619
+
94620
+ EXAMPLES
94621
+ skill front tags rename tag_abc123 "billing-issue"
94622
+ skill front tags rename tag_abc123 "refund-request"
94623
+
94624
+ NOTE
94625
+ If a tag with the new name already exists, the API will return an error.
94626
+ Use "skill front tags cleanup" to merge duplicates and case variants.
94627
+ `
94628
+ ).action(renameTag);
93888
94629
  tags.command("cleanup").description(
93889
94630
  "Clean up tags: delete duplicates, merge case variants, remove obsolete, create missing standard tags"
93890
- ).option("--execute", "Actually apply changes (default is dry-run)", false).action(cleanupTags);
94631
+ ).option("--execute", "Actually apply changes (default is dry-run)", false).addHelpText(
94632
+ "after",
94633
+ `
94634
+ \u2501\u2501\u2501 Tag Cleanup \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
94635
+
94636
+ Analyze all tags for issues and optionally fix them. Default is dry-run.
94637
+
94638
+ WHAT IT FINDS
94639
+ - Exact duplicates (same name, multiple tag IDs)
94640
+ - Case variants ("Refund" vs "refund" vs "REFUND")
94641
+ - Obsolete tags (date-based like "jan-2022", Gmail imports like "INBOX")
94642
+ - Missing standard tags from the category registry
94643
+
94644
+ WHAT IT DOES (with --execute)
94645
+ - Deletes duplicate tags (keeps the one with most conversations)
94646
+ - Renames case variants to canonical lowercase-hyphenated form
94647
+ - Deletes obsolete/imported tags
94648
+ - Creates missing standard category tags
94649
+
94650
+ OPTIONS
94651
+ --execute Apply the cleanup plan. Without this flag, only a dry-run
94652
+ report is printed (safe to run anytime).
94653
+
94654
+ EXAMPLES
94655
+ # See what would be changed (safe, read-only)
94656
+ skill front tags cleanup
94657
+
94658
+ # Actually apply changes (prompts for confirmation)
94659
+ skill front tags cleanup --execute
94660
+ `
94661
+ ).action(cleanupTags);
93891
94662
  }
93892
94663
 
93893
94664
  // src/commands/front/triage.ts
@@ -94110,7 +94881,72 @@ function registerTriageCommand(front) {
94110
94881
  "-s, --status <status>",
94111
94882
  "Conversation status filter (unassigned, assigned, archived)",
94112
94883
  "unassigned"
94113
- ).option("--auto-archive", "Automatically archive noise and spam").option("--json", "JSON output").action(triageConversations);
94884
+ ).option("--auto-archive", "Automatically archive noise and spam").option("--json", "JSON output").addHelpText(
94885
+ "after",
94886
+ `
94887
+ \u2501\u2501\u2501 AI-Powered Triage \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
94888
+
94889
+ Categorize inbox conversations into actionable, noise, or spam using
94890
+ heuristic rules. Optionally auto-archive the junk.
94891
+
94892
+ OPTIONS
94893
+ -i, --inbox <id> (required) Inbox ID to triage (inb_xxx)
94894
+ -s, --status <status> Conversation status to filter (default: unassigned)
94895
+ Values: unassigned, assigned, archived
94896
+ --auto-archive Archive all noise + spam conversations automatically
94897
+ --json Output as JSON (HATEOAS-wrapped)
94898
+
94899
+ CATEGORIZATION RULES
94900
+ Category Signals
94901
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
94902
+ Noise mailer-daemon, noreply, no-reply, postmaster, newsletter
94903
+ delivery failures, auto-replies, out-of-office,
94904
+ automated reports (daily/weekly/monthly), cert notifications
94905
+ Spam partnership/sponsorship pitches, guest post / link exchange,
94906
+ backlink requests, marketing opportunities
94907
+ Actionable Everything else (real support issues)
94908
+
94909
+ WORKFLOW
94910
+ 1. Triage to see the breakdown:
94911
+ skill front triage --inbox inb_4bj7r
94912
+
94913
+ 2. Review categories in the output (actionable / noise / spam)
94914
+
94915
+ 3. If satisfied, auto-archive the junk:
94916
+ skill front triage --inbox inb_4bj7r --auto-archive
94917
+
94918
+ JSON + jq PATTERNS
94919
+ # Just the stats
94920
+ skill front triage --inbox inb_4bj7r --json | jq '.data.stats'
94921
+
94922
+ # All noise conversation IDs
94923
+ skill front triage --inbox inb_4bj7r --json | jq '[.data.results[] | select(.category == "noise") | .id]'
94924
+
94925
+ # Spam sender emails
94926
+ skill front triage --inbox inb_4bj7r --json | jq '[.data.results[] | select(.category == "spam") | .senderEmail]'
94927
+
94928
+ # Actionable count
94929
+ skill front triage --inbox inb_4bj7r --json | jq '.data.stats.actionable'
94930
+
94931
+ EXAMPLES
94932
+ # Triage unassigned conversations (default)
94933
+ skill front triage --inbox inb_4bj7r
94934
+
94935
+ # Triage assigned conversations
94936
+ skill front triage --inbox inb_4bj7r --status assigned
94937
+
94938
+ # Triage and auto-archive noise + spam
94939
+ skill front triage --inbox inb_4bj7r --auto-archive
94940
+
94941
+ # Pipe JSON for downstream processing
94942
+ skill front triage --inbox inb_4bj7r --json | jq '.data.results[] | select(.category == "actionable")'
94943
+
94944
+ RELATED COMMANDS
94945
+ skill front bulk-archive Bulk-archive conversations by query
94946
+ skill front report Inbox activity report
94947
+ skill front search Search conversations with filters
94948
+ `
94949
+ ).action(triageConversations);
94114
94950
  }
94115
94951
 
94116
94952
  // src/commands/front/index.ts
@@ -94384,10 +95220,245 @@ async function getTeammate(id, options) {
94384
95220
  }
94385
95221
  function registerFrontCommands(program3) {
94386
95222
  const front = program3.command("front").description("Front conversations, inboxes, tags, archival, and reporting");
94387
- front.command("message").description("Get a message by ID (body, author, recipients, attachments)").argument("<id>", "Message ID (e.g., msg_xxx)").option("--json", "Output as JSON").action(getMessage);
94388
- front.command("conversation").description("Get a conversation by ID (status, tags, assignee, messages)").argument("<id>", "Conversation ID (e.g., cnv_xxx)").option("--json", "Output as JSON").option("-m, --messages", "Include message history").action(getConversation2);
94389
- front.command("teammates").description("List all teammates in the workspace").option("--json", "Output as JSON").action(listTeammates);
94390
- front.command("teammate").description("Get teammate details by ID").argument("<id>", "Teammate ID (e.g., tea_xxx or username)").option("--json", "Output as JSON").action(getTeammate);
95223
+ const messageCmd = front.command("message").description("Get a message by ID (body, author, recipients, attachments)").argument("<id>", "Message ID (e.g., msg_xxx)").option("--json", "Output as JSON").action(getMessage);
95224
+ messageCmd.addHelpText(
95225
+ "after",
95226
+ `
95227
+ \u2501\u2501\u2501 Message Details \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
95228
+
95229
+ Fetches a single message from Front by its ID. Returns the full message
95230
+ including HTML body, plaintext body, author, recipients, attachments,
95231
+ and metadata.
95232
+
95233
+ ID FORMAT
95234
+ msg_xxx Front message ID (prefixed with msg_)
95235
+ You can find message IDs from conversation message lists.
95236
+
95237
+ WHAT'S RETURNED
95238
+ Field Description
95239
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
95240
+ id Message ID (msg_xxx)
95241
+ type Message type (email, sms, custom, etc.)
95242
+ subject Message subject line
95243
+ body Full HTML body
95244
+ text Plaintext body (stripped HTML)
95245
+ author Author object (email, id) \u2014 teammate or contact
95246
+ recipients Array of {role, handle} \u2014 from/to/cc/bcc
95247
+ attachments Array of {filename, content_type, size, url}
95248
+ created_at Unix timestamp of message creation
95249
+ metadata Headers, external references
95250
+
95251
+ JSON + jq PATTERNS
95252
+ # Get the HTML body
95253
+ skill front message msg_xxx --json | jq '.data.body'
95254
+
95255
+ # Get the plaintext body
95256
+ skill front message msg_xxx --json | jq '.data.text'
95257
+
95258
+ # Get the author email
95259
+ skill front message msg_xxx --json | jq '.data.author.email'
95260
+
95261
+ # List all recipients
95262
+ skill front message msg_xxx --json | jq '.data.recipients[] | {role, handle}'
95263
+
95264
+ # List attachment filenames
95265
+ skill front message msg_xxx --json | jq '.data.attachments[].filename'
95266
+
95267
+ RELATED COMMANDS
95268
+ skill front conversation <id> -m Find message IDs from a conversation
95269
+ skill front search <query> Search conversations to find threads
95270
+
95271
+ EXAMPLES
95272
+ # Get full message details (human-readable)
95273
+ skill front message msg_1a2b3c
95274
+
95275
+ # Get message as JSON for piping
95276
+ skill front message msg_1a2b3c --json
95277
+
95278
+ # Extract just the body text
95279
+ skill front message msg_1a2b3c --json | jq -r '.data.text'
95280
+
95281
+ # Check who sent a message
95282
+ skill front message msg_1a2b3c --json | jq '{author: .data.author.email, recipients: [.data.recipients[].handle]}'
95283
+ `
95284
+ );
95285
+ const conversationCmd = front.command("conversation").description("Get a conversation by ID (status, tags, assignee, messages)").argument("<id>", "Conversation ID (e.g., cnv_xxx)").option("--json", "Output as JSON").option("-m, --messages", "Include message history").action(getConversation2);
95286
+ conversationCmd.addHelpText(
95287
+ "after",
95288
+ `
95289
+ \u2501\u2501\u2501 Conversation Details \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
95290
+
95291
+ Fetches a conversation from Front by its ID. Returns metadata, tags,
95292
+ assignee, recipient, and optionally the full message history.
95293
+
95294
+ ID FORMAT
95295
+ cnv_xxx Front conversation ID (prefixed with cnv_)
95296
+ Find conversation IDs via search or inbox listing.
95297
+
95298
+ FLAGS
95299
+ -m, --messages Include full message history in the response.
95300
+ Without this flag, only conversation metadata is returned.
95301
+
95302
+ WHAT'S RETURNED
95303
+ Field Description
95304
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
95305
+ id Conversation ID (cnv_xxx)
95306
+ subject Conversation subject line
95307
+ status Current status (see below)
95308
+ created_at Unix timestamp
95309
+ recipient Primary recipient {handle, role}
95310
+ assignee Assigned teammate {id, email} or null
95311
+ tags Array of {id, name} tags on this conversation
95312
+ messages (with -m) Array of full message objects
95313
+
95314
+ STATUS VALUES
95315
+ archived Conversation is archived
95316
+ unassigned Open, no assignee
95317
+ assigned Open, has an assignee
95318
+ deleted In trash
95319
+ waiting Waiting for response
95320
+
95321
+ JSON + jq PATTERNS
95322
+ # Get conversation metadata
95323
+ skill front conversation cnv_xxx --json | jq '.data.conversation'
95324
+
95325
+ # Get all messages (requires -m flag)
95326
+ skill front conversation cnv_xxx -m --json | jq '.data.messages[]'
95327
+
95328
+ # Get just message bodies as plaintext
95329
+ skill front conversation cnv_xxx -m --json | jq -r '.data.messages[].text'
95330
+
95331
+ # Extract tag names
95332
+ skill front conversation cnv_xxx --json | jq '[.data.conversation.tags[].name]'
95333
+
95334
+ # Get assignee email
95335
+ skill front conversation cnv_xxx --json | jq '.data.conversation.assignee.email'
95336
+
95337
+ # Get message count
95338
+ skill front conversation cnv_xxx -m --json | jq '.data.messages | length'
95339
+
95340
+ # Get inbound messages only
95341
+ skill front conversation cnv_xxx -m --json | jq '[.data.messages[] | select(.is_inbound)]'
95342
+
95343
+ RELATED COMMANDS
95344
+ skill front message <id> Get full details for a specific message
95345
+ skill front assign <cnv> <tea> Assign conversation to a teammate
95346
+ skill front tag <cnv> <tag> Add a tag to a conversation
95347
+ skill front reply <cnv> Send a reply to a conversation
95348
+ skill front search <query> Search for conversations
95349
+
95350
+ EXAMPLES
95351
+ # Get conversation overview
95352
+ skill front conversation cnv_abc123
95353
+
95354
+ # Get conversation with full message history
95355
+ skill front conversation cnv_abc123 -m
95356
+
95357
+ # Pipe to jq to extract tags and assignee
95358
+ skill front conversation cnv_abc123 --json | jq '{tags: [.data.conversation.tags[].name], assignee: .data.conversation.assignee.email}'
95359
+
95360
+ # Get the latest message text from a conversation
95361
+ skill front conversation cnv_abc123 -m --json | jq -r '.data.messages[-1].text'
95362
+ `
95363
+ );
95364
+ const teammatesCmd = front.command("teammates").description("List all teammates in the workspace").option("--json", "Output as JSON").action(listTeammates);
95365
+ teammatesCmd.addHelpText(
95366
+ "after",
95367
+ `
95368
+ \u2501\u2501\u2501 List Teammates \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
95369
+
95370
+ Lists all teammates in the Front workspace. Returns each teammate's ID,
95371
+ email, name, username, and availability status.
95372
+
95373
+ WHAT'S RETURNED (per teammate)
95374
+ Field Description
95375
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
95376
+ id Teammate ID (tea_xxx)
95377
+ email Teammate email address
95378
+ first_name First name
95379
+ last_name Last name
95380
+ username Front username
95381
+ is_available Whether teammate is currently available (true/false)
95382
+
95383
+ JSON + jq PATTERNS
95384
+ # Get all teammate IDs
95385
+ skill front teammates --json | jq '[.data[].id]'
95386
+
95387
+ # Get ID + email pairs
95388
+ skill front teammates --json | jq '.data[] | {id, email}'
95389
+
95390
+ # Find a teammate by email
95391
+ skill front teammates --json | jq '.data[] | select(.email == "joel@example.com")'
95392
+
95393
+ # List only available teammates
95394
+ skill front teammates --json | jq '[.data[] | select(.is_available)]'
95395
+
95396
+ # Get a count of teammates
95397
+ skill front teammates --json | jq '.data | length'
95398
+
95399
+ RELATED COMMANDS
95400
+ skill front teammate <id> Get details for a specific teammate
95401
+ skill front assign <cnv> <tea> Assign a conversation to a teammate
95402
+
95403
+ EXAMPLES
95404
+ # List all teammates (human-readable table)
95405
+ skill front teammates
95406
+
95407
+ # List as JSON for scripting
95408
+ skill front teammates --json
95409
+
95410
+ # Find teammate ID by email for use in assign
95411
+ skill front teammates --json | jq -r '.data[] | select(.email | contains("joel")) | .id'
95412
+ `
95413
+ );
95414
+ const teammateCmd = front.command("teammate").description("Get teammate details by ID").argument("<id>", "Teammate ID (e.g., tea_xxx or username)").option("--json", "Output as JSON").action(getTeammate);
95415
+ teammateCmd.addHelpText(
95416
+ "after",
95417
+ `
95418
+ \u2501\u2501\u2501 Teammate Details \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
95419
+
95420
+ Fetches details for a single teammate by their ID. Returns email, name,
95421
+ username, and current availability.
95422
+
95423
+ ID FORMAT
95424
+ tea_xxx Front teammate ID (prefixed with tea_)
95425
+ Find teammate IDs via: skill front teammates
95426
+
95427
+ WHAT'S RETURNED
95428
+ Field Description
95429
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
95430
+ id Teammate ID (tea_xxx)
95431
+ email Teammate email address
95432
+ first_name First name
95433
+ last_name Last name
95434
+ username Front username
95435
+ is_available Whether teammate is currently available (true/false)
95436
+
95437
+ JSON + jq PATTERNS
95438
+ # Get teammate email
95439
+ skill front teammate tea_xxx --json | jq '.data.email'
95440
+
95441
+ # Get full name
95442
+ skill front teammate tea_xxx --json | jq '(.data.first_name + " " + .data.last_name)'
95443
+
95444
+ # Check availability
95445
+ skill front teammate tea_xxx --json | jq '.data.is_available'
95446
+
95447
+ RELATED COMMANDS
95448
+ skill front teammates List all teammates (to find IDs)
95449
+ skill front assign <cnv> <tea> Assign a conversation to this teammate
95450
+
95451
+ EXAMPLES
95452
+ # Get teammate details (human-readable)
95453
+ skill front teammate tea_1a2b3c
95454
+
95455
+ # Get as JSON
95456
+ skill front teammate tea_1a2b3c --json
95457
+
95458
+ # Quick check if teammate is available
95459
+ skill front teammate tea_1a2b3c --json | jq -r 'if .data.is_available then "available" else "away" end'
95460
+ `
95461
+ );
94391
95462
  registerInboxCommand(front);
94392
95463
  registerArchiveCommand(front);
94393
95464
  registerBulkArchiveCommand(front);