@skillrecordings/cli 0.9.0 → 0.10.1
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 +1258 -266
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -91673,6 +91673,51 @@ init_esm_shims();
|
|
|
91673
91673
|
|
|
91674
91674
|
// src/commands/front/api.ts
|
|
91675
91675
|
init_esm_shims();
|
|
91676
|
+
|
|
91677
|
+
// src/commands/front/json-output.ts
|
|
91678
|
+
init_esm_shims();
|
|
91679
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "fs";
|
|
91680
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
91681
|
+
import { join as join12 } from "path";
|
|
91682
|
+
var STDOUT_LIMIT = 64 * 1024;
|
|
91683
|
+
function writeJsonOutput(data2) {
|
|
91684
|
+
const json = JSON.stringify(data2, null, 2);
|
|
91685
|
+
if (json.length <= STDOUT_LIMIT) {
|
|
91686
|
+
console.log(json);
|
|
91687
|
+
return;
|
|
91688
|
+
}
|
|
91689
|
+
const dir = join12(tmpdir2(), "skill-front");
|
|
91690
|
+
mkdirSync5(dir, { recursive: true });
|
|
91691
|
+
const filepath = join12(dir, `${Date.now()}.json`);
|
|
91692
|
+
writeFileSync8(filepath, json);
|
|
91693
|
+
const envelope = {
|
|
91694
|
+
_type: data2?._type ?? "result",
|
|
91695
|
+
_file: filepath,
|
|
91696
|
+
_size: `${(json.length / 1024).toFixed(1)}KB`,
|
|
91697
|
+
_hint: `cat ${filepath} | jq`
|
|
91698
|
+
};
|
|
91699
|
+
const d = data2;
|
|
91700
|
+
if (d.data && typeof d.data === "object") {
|
|
91701
|
+
const inner = d.data;
|
|
91702
|
+
if (inner.total !== void 0) envelope.total = inner.total;
|
|
91703
|
+
if (inner.query !== void 0) envelope.query = inner.query;
|
|
91704
|
+
if (Array.isArray(inner.conversations)) {
|
|
91705
|
+
envelope.conversations = inner.conversations.map(
|
|
91706
|
+
(c) => ({
|
|
91707
|
+
id: c.id,
|
|
91708
|
+
subject: c.subject,
|
|
91709
|
+
status: c.status
|
|
91710
|
+
})
|
|
91711
|
+
);
|
|
91712
|
+
}
|
|
91713
|
+
}
|
|
91714
|
+
if (Array.isArray(d._actions) && d._actions.length > 0) {
|
|
91715
|
+
envelope._actions = d._actions;
|
|
91716
|
+
}
|
|
91717
|
+
console.log(JSON.stringify(envelope, null, 2));
|
|
91718
|
+
}
|
|
91719
|
+
|
|
91720
|
+
// src/commands/front/api.ts
|
|
91676
91721
|
function getFrontClient() {
|
|
91677
91722
|
const apiToken = process.env.FRONT_API_TOKEN;
|
|
91678
91723
|
if (!apiToken) {
|
|
@@ -91714,13 +91759,91 @@ async function apiPassthrough(method, endpoint, options) {
|
|
|
91714
91759
|
`Unsupported method: ${method}. Use GET, POST, PATCH, PUT, or DELETE.`
|
|
91715
91760
|
);
|
|
91716
91761
|
}
|
|
91717
|
-
|
|
91762
|
+
writeJsonOutput(result);
|
|
91718
91763
|
}
|
|
91719
91764
|
function registerApiCommand(frontCommand) {
|
|
91720
91765
|
frontCommand.command("api").description("Raw Front API request (escape hatch)").argument("<method>", "HTTP method (GET, POST, PATCH, PUT, DELETE)").argument(
|
|
91721
91766
|
"<endpoint>",
|
|
91722
91767
|
"API endpoint path (e.g., /me, /conversations/cnv_xxx)"
|
|
91723
|
-
).option("--data <json>", "Request body as JSON string").
|
|
91768
|
+
).option("--data <json>", "Request body as JSON string").addHelpText(
|
|
91769
|
+
"after",
|
|
91770
|
+
`
|
|
91771
|
+
\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
|
|
91772
|
+
|
|
91773
|
+
Escape hatch for making arbitrary Front API calls. Use this when no
|
|
91774
|
+
typed CLI command exists for what you need.
|
|
91775
|
+
|
|
91776
|
+
ARGUMENTS
|
|
91777
|
+
<method> HTTP method: GET, POST, PATCH, PUT, DELETE
|
|
91778
|
+
<endpoint> API path (leading / is optional \u2014 both work)
|
|
91779
|
+
|
|
91780
|
+
OPTIONS
|
|
91781
|
+
--data <json> Request body as a valid JSON string (for POST, PATCH, PUT)
|
|
91782
|
+
|
|
91783
|
+
COMMON ENDPOINTS
|
|
91784
|
+
Endpoint What it returns
|
|
91785
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
91786
|
+
/me Authenticated identity
|
|
91787
|
+
/inboxes All inboxes
|
|
91788
|
+
/conversations/cnv_xxx Conversation details
|
|
91789
|
+
/conversations/cnv_xxx/messages Messages in a conversation
|
|
91790
|
+
/tags All tags
|
|
91791
|
+
/teammates All teammates
|
|
91792
|
+
/contacts All contacts
|
|
91793
|
+
/accounts All accounts
|
|
91794
|
+
/channels All channels
|
|
91795
|
+
/rules All rules
|
|
91796
|
+
|
|
91797
|
+
ENDPOINT NORMALIZATION
|
|
91798
|
+
Leading slash is optional. These are equivalent:
|
|
91799
|
+
skill front api GET /me
|
|
91800
|
+
skill front api GET me
|
|
91801
|
+
|
|
91802
|
+
OUTPUT
|
|
91803
|
+
Always JSON. Pipe to jq for filtering.
|
|
91804
|
+
|
|
91805
|
+
EXAMPLES
|
|
91806
|
+
# Check authenticated identity
|
|
91807
|
+
skill front api GET /me
|
|
91808
|
+
|
|
91809
|
+
# List all inboxes
|
|
91810
|
+
skill front api GET /inboxes
|
|
91811
|
+
|
|
91812
|
+
# Get a specific conversation
|
|
91813
|
+
skill front api GET /conversations/cnv_abc123
|
|
91814
|
+
|
|
91815
|
+
# Archive a conversation
|
|
91816
|
+
skill front api PATCH /conversations/cnv_abc123 --data '{"status":"archived"}'
|
|
91817
|
+
|
|
91818
|
+
# Apply a tag to a conversation
|
|
91819
|
+
skill front api POST /conversations/cnv_abc123/tags --data '{"tag_ids":["tag_xxx"]}'
|
|
91820
|
+
|
|
91821
|
+
# Create a new tag
|
|
91822
|
+
skill front api POST /tags --data '{"name":"my-new-tag","highlight":"blue"}'
|
|
91823
|
+
|
|
91824
|
+
# Delete a tag
|
|
91825
|
+
skill front api DELETE /tags/tag_xxx
|
|
91826
|
+
|
|
91827
|
+
# List teammates and extract emails
|
|
91828
|
+
skill front api GET /teammates | jq '._results[].email'
|
|
91829
|
+
|
|
91830
|
+
# Get conversation + pipe to jq for specific fields
|
|
91831
|
+
skill front api GET /conversations/cnv_abc123 | jq '{subject, status, assignee: .assignee.email}'
|
|
91832
|
+
|
|
91833
|
+
WHEN TO USE THIS vs TYPED COMMANDS
|
|
91834
|
+
Prefer typed commands when available \u2014 they have better error handling,
|
|
91835
|
+
pagination, and output formatting:
|
|
91836
|
+
skill front search (not: skill front api GET /conversations/search/...)
|
|
91837
|
+
skill front inbox (not: skill front api GET /inboxes)
|
|
91838
|
+
skill front tags list (not: skill front api GET /tags)
|
|
91839
|
+
skill front conversation (not: skill front api GET /conversations/cnv_xxx)
|
|
91840
|
+
|
|
91841
|
+
Use "skill front api" for endpoints without a dedicated command, or when
|
|
91842
|
+
you need the raw response shape.
|
|
91843
|
+
|
|
91844
|
+
Full API docs: https://dev.frontapp.com/reference
|
|
91845
|
+
`
|
|
91846
|
+
).action(
|
|
91724
91847
|
async (method, endpoint, options) => {
|
|
91725
91848
|
try {
|
|
91726
91849
|
await apiPassthrough(method, endpoint, options);
|
|
@@ -92000,16 +92123,12 @@ async function archiveConversations(convId, additionalIds, options) {
|
|
|
92000
92123
|
};
|
|
92001
92124
|
})
|
|
92002
92125
|
);
|
|
92003
|
-
|
|
92004
|
-
|
|
92005
|
-
|
|
92006
|
-
|
|
92007
|
-
|
|
92008
|
-
|
|
92009
|
-
}),
|
|
92010
|
-
null,
|
|
92011
|
-
2
|
|
92012
|
-
)
|
|
92126
|
+
writeJsonOutput(
|
|
92127
|
+
hateoasWrap({
|
|
92128
|
+
type: "archive-result",
|
|
92129
|
+
command: `skill front archive ${allIds.map(normalizeId).join(" ")} --json`,
|
|
92130
|
+
data: results2
|
|
92131
|
+
})
|
|
92013
92132
|
);
|
|
92014
92133
|
return;
|
|
92015
92134
|
}
|
|
@@ -92059,7 +92178,47 @@ async function archiveConversations(convId, additionalIds, options) {
|
|
|
92059
92178
|
}
|
|
92060
92179
|
}
|
|
92061
92180
|
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").
|
|
92181
|
+
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(
|
|
92182
|
+
"after",
|
|
92183
|
+
`
|
|
92184
|
+
\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
|
|
92185
|
+
|
|
92186
|
+
Sets the status of one or more conversations to 'archived' in Front.
|
|
92187
|
+
Accepts conversation IDs (cnv_xxx) or full Front API URLs.
|
|
92188
|
+
|
|
92189
|
+
SINGLE CONVERSATION
|
|
92190
|
+
skill front archive cnv_abc123
|
|
92191
|
+
|
|
92192
|
+
MULTIPLE CONVERSATIONS
|
|
92193
|
+
skill front archive cnv_abc123 cnv_def456 cnv_ghi789
|
|
92194
|
+
|
|
92195
|
+
All IDs are archived concurrently. The summary shows success/failure counts.
|
|
92196
|
+
|
|
92197
|
+
JSON OUTPUT (for scripting)
|
|
92198
|
+
skill front archive cnv_abc123 --json
|
|
92199
|
+
skill front archive cnv_abc123 cnv_def456 --json
|
|
92200
|
+
|
|
92201
|
+
JSON envelope includes per-conversation success/error status.
|
|
92202
|
+
|
|
92203
|
+
BATCH PIPELINE (search \u2192 archive)
|
|
92204
|
+
# Archive all snoozed conversations in an inbox
|
|
92205
|
+
skill front search "is:snoozed" --inbox inb_4bj7r --json \\
|
|
92206
|
+
| jq -r '.data.conversations[].id' \\
|
|
92207
|
+
| xargs -I{} skill front archive {}
|
|
92208
|
+
|
|
92209
|
+
# Archive unassigned conversations older than 30 days
|
|
92210
|
+
skill front search "is:unassigned" --inbox inb_4bj7r --json \\
|
|
92211
|
+
| jq -r '.data.conversations[].id' \\
|
|
92212
|
+
| xargs -I{} skill front archive {}
|
|
92213
|
+
|
|
92214
|
+
For filter-based bulk archiving with built-in safety, use bulk-archive instead.
|
|
92215
|
+
|
|
92216
|
+
RELATED COMMANDS
|
|
92217
|
+
skill front bulk-archive Filter-based bulk archive (--dry-run, rate limiting)
|
|
92218
|
+
skill front conversation View conversation details before archiving
|
|
92219
|
+
skill front search Find conversations by query / filters
|
|
92220
|
+
`
|
|
92221
|
+
).action(archiveConversations);
|
|
92063
92222
|
}
|
|
92064
92223
|
|
|
92065
92224
|
// src/commands/front/assign.ts
|
|
@@ -92089,20 +92248,16 @@ async function assignConversation(conversationId, teammateId, options) {
|
|
|
92089
92248
|
const assigneeId = options.unassign ? "" : normalizeId2(teammateId);
|
|
92090
92249
|
await front.conversations.updateAssignee(convId, assigneeId);
|
|
92091
92250
|
if (options.json) {
|
|
92092
|
-
|
|
92093
|
-
|
|
92094
|
-
|
|
92095
|
-
|
|
92096
|
-
|
|
92097
|
-
|
|
92098
|
-
|
|
92099
|
-
|
|
92100
|
-
|
|
92101
|
-
|
|
92102
|
-
}),
|
|
92103
|
-
null,
|
|
92104
|
-
2
|
|
92105
|
-
)
|
|
92251
|
+
writeJsonOutput(
|
|
92252
|
+
hateoasWrap({
|
|
92253
|
+
type: "assign-result",
|
|
92254
|
+
command: options.unassign ? `skill front assign ${convId} --unassign --json` : `skill front assign ${convId} ${assigneeId} --json`,
|
|
92255
|
+
data: {
|
|
92256
|
+
id: convId,
|
|
92257
|
+
assignee: options.unassign ? null : assigneeId,
|
|
92258
|
+
success: true
|
|
92259
|
+
}
|
|
92260
|
+
})
|
|
92106
92261
|
);
|
|
92107
92262
|
} else {
|
|
92108
92263
|
if (options.unassign) {
|
|
@@ -92128,7 +92283,58 @@ async function assignConversation(conversationId, teammateId, options) {
|
|
|
92128
92283
|
}
|
|
92129
92284
|
}
|
|
92130
92285
|
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").
|
|
92286
|
+
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(
|
|
92287
|
+
"after",
|
|
92288
|
+
`
|
|
92289
|
+
\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
|
|
92290
|
+
|
|
92291
|
+
Assign a conversation to a teammate, or remove the current assignee.
|
|
92292
|
+
Accepts conversation IDs (cnv_xxx) and teammate IDs (tea_xxx).
|
|
92293
|
+
|
|
92294
|
+
ASSIGN
|
|
92295
|
+
skill front assign <conversation-id> <teammate-id>
|
|
92296
|
+
|
|
92297
|
+
The teammate must exist in your Front workspace.
|
|
92298
|
+
|
|
92299
|
+
UNASSIGN
|
|
92300
|
+
skill front assign <conversation-id> --unassign
|
|
92301
|
+
|
|
92302
|
+
Removes the current assignee. Cannot be combined with a teammate ID.
|
|
92303
|
+
|
|
92304
|
+
FINDING IDs
|
|
92305
|
+
Teammate IDs:
|
|
92306
|
+
skill front teammates # Human-readable list
|
|
92307
|
+
skill front teammates --json | jq '.[].id' # Just the IDs
|
|
92308
|
+
|
|
92309
|
+
Conversation IDs:
|
|
92310
|
+
skill front search --inbox inb_xxx --json | jq '.data.conversations[].id'
|
|
92311
|
+
skill front conversation <cnv_xxx> # Verify a conversation
|
|
92312
|
+
|
|
92313
|
+
JSON OUTPUT (--json)
|
|
92314
|
+
Returns a HATEOAS-wrapped object:
|
|
92315
|
+
{ type: "assign-result", data: { id, assignee, success } }
|
|
92316
|
+
|
|
92317
|
+
EXAMPLES
|
|
92318
|
+
# Assign a conversation to a teammate
|
|
92319
|
+
skill front assign cnv_abc123 tea_def456
|
|
92320
|
+
|
|
92321
|
+
# Unassign (remove assignee)
|
|
92322
|
+
skill front assign cnv_abc123 --unassign
|
|
92323
|
+
|
|
92324
|
+
# Assign and get JSON output
|
|
92325
|
+
skill front assign cnv_abc123 tea_def456 --json
|
|
92326
|
+
|
|
92327
|
+
# Pipeline: assign all unassigned convos in an inbox to a teammate
|
|
92328
|
+
skill front search --inbox inb_4bj7r --status unassigned --json \\
|
|
92329
|
+
| jq -r '.data.conversations[].id' \\
|
|
92330
|
+
| xargs -I{} skill front assign {} tea_def456
|
|
92331
|
+
|
|
92332
|
+
RELATED COMMANDS
|
|
92333
|
+
skill front teammates List teammates and their IDs
|
|
92334
|
+
skill front conversation <id> View conversation details + current assignee
|
|
92335
|
+
skill front search Find conversations by filters
|
|
92336
|
+
`
|
|
92337
|
+
).action(assignConversation);
|
|
92132
92338
|
}
|
|
92133
92339
|
|
|
92134
92340
|
// src/commands/front/bulk-archive.ts
|
|
@@ -92293,16 +92499,12 @@ Found ${result.matches.length} matching conversations`);
|
|
|
92293
92499
|
}
|
|
92294
92500
|
if (dryRun) {
|
|
92295
92501
|
if (json) {
|
|
92296
|
-
|
|
92297
|
-
|
|
92298
|
-
|
|
92299
|
-
|
|
92300
|
-
|
|
92301
|
-
|
|
92302
|
-
}),
|
|
92303
|
-
null,
|
|
92304
|
-
2
|
|
92305
|
-
)
|
|
92502
|
+
writeJsonOutput(
|
|
92503
|
+
hateoasWrap({
|
|
92504
|
+
type: "bulk-archive-result",
|
|
92505
|
+
command: `skill front bulk-archive --inbox ${inbox} --dry-run --json`,
|
|
92506
|
+
data: result
|
|
92507
|
+
})
|
|
92306
92508
|
);
|
|
92307
92509
|
} else {
|
|
92308
92510
|
console.log("\nMatching conversations:");
|
|
@@ -92322,16 +92524,12 @@ Run without --dry-run to archive ${result.matches.length} conversation(s)`
|
|
|
92322
92524
|
if (!json) {
|
|
92323
92525
|
console.log("\nNo conversations to archive.");
|
|
92324
92526
|
} else {
|
|
92325
|
-
|
|
92326
|
-
|
|
92327
|
-
|
|
92328
|
-
|
|
92329
|
-
|
|
92330
|
-
|
|
92331
|
-
}),
|
|
92332
|
-
null,
|
|
92333
|
-
2
|
|
92334
|
-
)
|
|
92527
|
+
writeJsonOutput(
|
|
92528
|
+
hateoasWrap({
|
|
92529
|
+
type: "bulk-archive-result",
|
|
92530
|
+
command: `skill front bulk-archive --inbox ${inbox} --json`,
|
|
92531
|
+
data: result
|
|
92532
|
+
})
|
|
92335
92533
|
);
|
|
92336
92534
|
}
|
|
92337
92535
|
return;
|
|
@@ -92361,16 +92559,12 @@ Run without --dry-run to archive ${result.matches.length} conversation(s)`
|
|
|
92361
92559
|
await new Promise((r) => setTimeout(r, 150));
|
|
92362
92560
|
}
|
|
92363
92561
|
if (json) {
|
|
92364
|
-
|
|
92365
|
-
|
|
92366
|
-
|
|
92367
|
-
|
|
92368
|
-
|
|
92369
|
-
|
|
92370
|
-
}),
|
|
92371
|
-
null,
|
|
92372
|
-
2
|
|
92373
|
-
)
|
|
92562
|
+
writeJsonOutput(
|
|
92563
|
+
hateoasWrap({
|
|
92564
|
+
type: "bulk-archive-result",
|
|
92565
|
+
command: `skill front bulk-archive --inbox ${inbox} --json`,
|
|
92566
|
+
data: result
|
|
92567
|
+
})
|
|
92374
92568
|
);
|
|
92375
92569
|
} else {
|
|
92376
92570
|
console.log("\n\nBulk Archive Results:");
|
|
@@ -92415,7 +92609,67 @@ function registerBulkArchiveCommand(parent2) {
|
|
|
92415
92609
|
).option("--tag <name>", "Filter by tag name (contains)").option(
|
|
92416
92610
|
"--older-than <duration>",
|
|
92417
92611
|
"Filter by age (e.g., 30d, 7d, 24h, 60m)"
|
|
92418
|
-
).option("--dry-run", "Preview without archiving").option("--json", "JSON output").
|
|
92612
|
+
).option("--dry-run", "Preview without archiving").option("--json", "JSON output").addHelpText(
|
|
92613
|
+
"after",
|
|
92614
|
+
`
|
|
92615
|
+
\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
|
|
92616
|
+
|
|
92617
|
+
Archive conversations matching filter criteria from a specific inbox.
|
|
92618
|
+
Requires --inbox and at least one filter. Filters are AND-combined.
|
|
92619
|
+
Rate limiting is built in (100-150ms between API calls).
|
|
92620
|
+
|
|
92621
|
+
\u26A0\uFE0F ALWAYS use --dry-run first to preview what would be archived.
|
|
92622
|
+
|
|
92623
|
+
FILTER OPTIONS
|
|
92624
|
+
--sender <email> Sender email (substring match, case-insensitive)
|
|
92625
|
+
--subject <text> Subject line (substring match, case-insensitive)
|
|
92626
|
+
--status <status> Conversation status (unassigned, assigned, archived)
|
|
92627
|
+
--tag <name> Tag name (substring match, case-insensitive)
|
|
92628
|
+
--older-than <duration> Age filter \u2014 duration format: 30d, 7d, 24h, 60m
|
|
92629
|
+
|
|
92630
|
+
Filters combine with AND: --status unassigned --older-than 30d means
|
|
92631
|
+
conversations that are BOTH unassigned AND older than 30 days.
|
|
92632
|
+
|
|
92633
|
+
DRY RUN (preview first!)
|
|
92634
|
+
skill front bulk-archive --inbox inb_4bj7r --sender "mailer-daemon" --dry-run
|
|
92635
|
+
|
|
92636
|
+
Shows matching count + each conversation ID/subject/reason. No changes made.
|
|
92637
|
+
|
|
92638
|
+
EXECUTE (after verifying dry run)
|
|
92639
|
+
skill front bulk-archive --inbox inb_4bj7r --sender "mailer-daemon"
|
|
92640
|
+
|
|
92641
|
+
PRACTICAL EXAMPLES
|
|
92642
|
+
# Archive all noise from mailer-daemon
|
|
92643
|
+
skill front bulk-archive --inbox inb_4bj7r --sender "mailer-daemon" --dry-run
|
|
92644
|
+
skill front bulk-archive --inbox inb_4bj7r --sender "mailer-daemon"
|
|
92645
|
+
|
|
92646
|
+
# Archive unassigned conversations older than 30 days
|
|
92647
|
+
skill front bulk-archive --inbox inb_4bj7r --status unassigned --older-than 30d --dry-run
|
|
92648
|
+
|
|
92649
|
+
# Archive everything tagged "spam"
|
|
92650
|
+
skill front bulk-archive --inbox inb_4bj7r --tag "spam" --dry-run
|
|
92651
|
+
|
|
92652
|
+
# Archive old daily report emails
|
|
92653
|
+
skill front bulk-archive --inbox inb_4bj7r --subject "Daily Report" --older-than 7d --dry-run
|
|
92654
|
+
|
|
92655
|
+
# List available inboxes (omit --inbox)
|
|
92656
|
+
skill front bulk-archive
|
|
92657
|
+
|
|
92658
|
+
JSON OUTPUT (for scripting)
|
|
92659
|
+
skill front bulk-archive --inbox inb_4bj7r --status unassigned --dry-run --json
|
|
92660
|
+
|
|
92661
|
+
# Count matches
|
|
92662
|
+
skill front bulk-archive ... --dry-run --json | jq '.data.matches | length'
|
|
92663
|
+
|
|
92664
|
+
# Extract matched IDs
|
|
92665
|
+
skill front bulk-archive ... --dry-run --json | jq -r '.data.matches[].id'
|
|
92666
|
+
|
|
92667
|
+
RELATED COMMANDS
|
|
92668
|
+
skill front triage Categorize conversations before archiving
|
|
92669
|
+
skill front archive Archive specific conversations by ID
|
|
92670
|
+
skill front search Find conversations by query / filters
|
|
92671
|
+
`
|
|
92672
|
+
).action(bulkArchiveConversations);
|
|
92419
92673
|
}
|
|
92420
92674
|
|
|
92421
92675
|
// src/commands/front/conversation-tag.ts
|
|
@@ -92454,21 +92708,17 @@ async function tagConversation(convId, tagNameOrId, options) {
|
|
|
92454
92708
|
const tag = await resolveTag(front, tagNameOrId);
|
|
92455
92709
|
await front.conversations.addTag(normalizedConvId, tag.id);
|
|
92456
92710
|
if (options.json) {
|
|
92457
|
-
|
|
92458
|
-
|
|
92459
|
-
|
|
92460
|
-
|
|
92461
|
-
|
|
92462
|
-
|
|
92463
|
-
|
|
92464
|
-
|
|
92465
|
-
|
|
92466
|
-
|
|
92467
|
-
|
|
92468
|
-
}),
|
|
92469
|
-
null,
|
|
92470
|
-
2
|
|
92471
|
-
)
|
|
92711
|
+
writeJsonOutput(
|
|
92712
|
+
hateoasWrap({
|
|
92713
|
+
type: "tag-result",
|
|
92714
|
+
command: `skill front tag ${normalizedConvId} ${tagNameOrId} --json`,
|
|
92715
|
+
data: {
|
|
92716
|
+
conversationId: normalizedConvId,
|
|
92717
|
+
tagId: tag.id,
|
|
92718
|
+
tagName: tag.name,
|
|
92719
|
+
action: "added"
|
|
92720
|
+
}
|
|
92721
|
+
})
|
|
92472
92722
|
);
|
|
92473
92723
|
return;
|
|
92474
92724
|
}
|
|
@@ -92500,21 +92750,17 @@ async function untagConversation(convId, tagNameOrId, options) {
|
|
|
92500
92750
|
const tag = await resolveTag(front, tagNameOrId);
|
|
92501
92751
|
await front.conversations.removeTag(normalizedConvId, tag.id);
|
|
92502
92752
|
if (options.json) {
|
|
92503
|
-
|
|
92504
|
-
|
|
92505
|
-
|
|
92506
|
-
|
|
92507
|
-
|
|
92508
|
-
|
|
92509
|
-
|
|
92510
|
-
|
|
92511
|
-
|
|
92512
|
-
|
|
92513
|
-
|
|
92514
|
-
}),
|
|
92515
|
-
null,
|
|
92516
|
-
2
|
|
92517
|
-
)
|
|
92753
|
+
writeJsonOutput(
|
|
92754
|
+
hateoasWrap({
|
|
92755
|
+
type: "untag-result",
|
|
92756
|
+
command: `skill front untag ${normalizedConvId} ${tagNameOrId} --json`,
|
|
92757
|
+
data: {
|
|
92758
|
+
conversationId: normalizedConvId,
|
|
92759
|
+
tagId: tag.id,
|
|
92760
|
+
tagName: tag.name,
|
|
92761
|
+
action: "removed"
|
|
92762
|
+
}
|
|
92763
|
+
})
|
|
92518
92764
|
);
|
|
92519
92765
|
return;
|
|
92520
92766
|
}
|
|
@@ -92538,8 +92784,87 @@ async function untagConversation(convId, tagNameOrId, options) {
|
|
|
92538
92784
|
}
|
|
92539
92785
|
}
|
|
92540
92786
|
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").
|
|
92542
|
-
|
|
92787
|
+
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(
|
|
92788
|
+
"after",
|
|
92789
|
+
`
|
|
92790
|
+
\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
|
|
92791
|
+
|
|
92792
|
+
Add a tag to a conversation. Accepts a tag name OR a tag ID (tag_xxx).
|
|
92793
|
+
Name lookup is case-insensitive \u2014 "Billing", "billing", and "BILLING" all work.
|
|
92794
|
+
|
|
92795
|
+
USAGE
|
|
92796
|
+
skill front tag <conversation-id> <tag-name-or-id>
|
|
92797
|
+
|
|
92798
|
+
TAG RESOLUTION
|
|
92799
|
+
By name: skill front tag cnv_abc123 "billing" # case-insensitive
|
|
92800
|
+
By ID: skill front tag cnv_abc123 tag_14nmdp # exact ID
|
|
92801
|
+
|
|
92802
|
+
If the name doesn't match any existing tag, the command errors with a hint
|
|
92803
|
+
to run \`skill front tags list\` to see available tags.
|
|
92804
|
+
|
|
92805
|
+
FINDING TAGS
|
|
92806
|
+
skill front tags list # Human-readable list
|
|
92807
|
+
skill front tags list --json | jq '.[].id' # Just the IDs
|
|
92808
|
+
skill front tags list --json | jq '.[] | {id, name}' # IDs + names
|
|
92809
|
+
|
|
92810
|
+
JSON OUTPUT (--json)
|
|
92811
|
+
Returns a HATEOAS-wrapped object:
|
|
92812
|
+
{ type: "tag-result", data: { conversationId, tagId, tagName, action: "added" } }
|
|
92813
|
+
|
|
92814
|
+
EXAMPLES
|
|
92815
|
+
# Tag by name
|
|
92816
|
+
skill front tag cnv_abc123 "needs-review"
|
|
92817
|
+
|
|
92818
|
+
# Tag by ID
|
|
92819
|
+
skill front tag cnv_abc123 tag_14nmdp
|
|
92820
|
+
|
|
92821
|
+
# Tag and get JSON output
|
|
92822
|
+
skill front tag cnv_abc123 "billing" --json
|
|
92823
|
+
|
|
92824
|
+
RELATED COMMANDS
|
|
92825
|
+
skill front untag <id> <tag> Remove a tag from a conversation
|
|
92826
|
+
skill front tags list List all available tags
|
|
92827
|
+
skill front conversation <id> View conversation details + current tags
|
|
92828
|
+
`
|
|
92829
|
+
).action(tagConversation);
|
|
92830
|
+
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(
|
|
92831
|
+
"after",
|
|
92832
|
+
`
|
|
92833
|
+
\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
|
|
92834
|
+
|
|
92835
|
+
Remove a tag from a conversation. Accepts a tag name OR a tag ID (tag_xxx).
|
|
92836
|
+
Name lookup is case-insensitive \u2014 "Billing", "billing", and "BILLING" all work.
|
|
92837
|
+
|
|
92838
|
+
USAGE
|
|
92839
|
+
skill front untag <conversation-id> <tag-name-or-id>
|
|
92840
|
+
|
|
92841
|
+
TAG RESOLUTION
|
|
92842
|
+
By name: skill front untag cnv_abc123 "billing" # case-insensitive
|
|
92843
|
+
By ID: skill front untag cnv_abc123 tag_14nmdp # exact ID
|
|
92844
|
+
|
|
92845
|
+
If the name doesn't match any existing tag, the command errors with a hint
|
|
92846
|
+
to run \`skill front tags list\` to see available tags.
|
|
92847
|
+
|
|
92848
|
+
JSON OUTPUT (--json)
|
|
92849
|
+
Returns a HATEOAS-wrapped object:
|
|
92850
|
+
{ type: "untag-result", data: { conversationId, tagId, tagName, action: "removed" } }
|
|
92851
|
+
|
|
92852
|
+
EXAMPLES
|
|
92853
|
+
# Untag by name
|
|
92854
|
+
skill front untag cnv_abc123 "needs-review"
|
|
92855
|
+
|
|
92856
|
+
# Untag by ID
|
|
92857
|
+
skill front untag cnv_abc123 tag_14nmdp
|
|
92858
|
+
|
|
92859
|
+
# Untag and get JSON output
|
|
92860
|
+
skill front untag cnv_abc123 "billing" --json
|
|
92861
|
+
|
|
92862
|
+
RELATED COMMANDS
|
|
92863
|
+
skill front tag <id> <tag> Add a tag to a conversation
|
|
92864
|
+
skill front tags list List all available tags
|
|
92865
|
+
skill front conversation <id> View conversation details + current tags
|
|
92866
|
+
`
|
|
92867
|
+
).action(untagConversation);
|
|
92543
92868
|
}
|
|
92544
92869
|
|
|
92545
92870
|
// src/commands/front/inbox.ts
|
|
@@ -92585,19 +92910,15 @@ async function listInboxes(options) {
|
|
|
92585
92910
|
const inboxList = await front.inboxes.list();
|
|
92586
92911
|
const inboxes = inboxList._results ?? [];
|
|
92587
92912
|
if (options.json) {
|
|
92588
|
-
|
|
92589
|
-
|
|
92590
|
-
|
|
92591
|
-
|
|
92592
|
-
|
|
92593
|
-
|
|
92594
|
-
|
|
92595
|
-
|
|
92596
|
-
|
|
92597
|
-
}),
|
|
92598
|
-
null,
|
|
92599
|
-
2
|
|
92600
|
-
)
|
|
92913
|
+
writeJsonOutput(
|
|
92914
|
+
hateoasWrap({
|
|
92915
|
+
type: "inbox-list",
|
|
92916
|
+
command: "skill front inbox --json",
|
|
92917
|
+
data: inboxes,
|
|
92918
|
+
links: inboxListLinks(
|
|
92919
|
+
inboxes.map((i) => ({ id: i.id, name: i.name }))
|
|
92920
|
+
)
|
|
92921
|
+
})
|
|
92601
92922
|
);
|
|
92602
92923
|
return;
|
|
92603
92924
|
}
|
|
@@ -92676,24 +92997,20 @@ async function listConversations(inboxNameOrId, options) {
|
|
|
92676
92997
|
`);
|
|
92677
92998
|
}
|
|
92678
92999
|
if (options.json) {
|
|
92679
|
-
|
|
92680
|
-
|
|
92681
|
-
|
|
92682
|
-
|
|
92683
|
-
|
|
92684
|
-
|
|
92685
|
-
|
|
92686
|
-
|
|
92687
|
-
|
|
92688
|
-
|
|
92689
|
-
|
|
92690
|
-
|
|
92691
|
-
|
|
92692
|
-
|
|
92693
|
-
}),
|
|
92694
|
-
null,
|
|
92695
|
-
2
|
|
92696
|
-
)
|
|
93000
|
+
writeJsonOutput(
|
|
93001
|
+
hateoasWrap({
|
|
93002
|
+
type: "conversation-list",
|
|
93003
|
+
command: `skill front inbox ${inbox.id} --json`,
|
|
93004
|
+
data: {
|
|
93005
|
+
total: conversations.length,
|
|
93006
|
+
conversations
|
|
93007
|
+
},
|
|
93008
|
+
links: conversationListLinks(
|
|
93009
|
+
conversations.map((c) => ({ id: c.id, subject: c.subject })),
|
|
93010
|
+
inbox.id
|
|
93011
|
+
),
|
|
93012
|
+
actions: conversationListActions(inbox.id)
|
|
93013
|
+
})
|
|
92697
93014
|
);
|
|
92698
93015
|
return;
|
|
92699
93016
|
}
|
|
@@ -92748,7 +93065,100 @@ function registerInboxCommand(front) {
|
|
|
92748
93065
|
).option("--json", "Output as JSON").option(
|
|
92749
93066
|
"--status <status>",
|
|
92750
93067
|
"Filter by status (unassigned, assigned, archived)"
|
|
92751
|
-
).option("--tag <tag>", "Filter by tag name").option("--limit <n>", "Limit number of results", "50").
|
|
93068
|
+
).option("--tag <tag>", "Filter by tag name").option("--limit <n>", "Limit number of results", "50").addHelpText(
|
|
93069
|
+
"after",
|
|
93070
|
+
`
|
|
93071
|
+
\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
|
|
93072
|
+
|
|
93073
|
+
Two modes: list all inboxes, or list conversations in a specific inbox.
|
|
93074
|
+
|
|
93075
|
+
MODE 1: LIST ALL INBOXES (no argument)
|
|
93076
|
+
skill front inbox Show all inboxes (ID, name, privacy)
|
|
93077
|
+
skill front inbox --json JSON output with HATEOAS links
|
|
93078
|
+
|
|
93079
|
+
MODE 2: LIST CONVERSATIONS IN AN INBOX (with argument)
|
|
93080
|
+
skill front inbox <inbox-name-or-id> List conversations in an inbox
|
|
93081
|
+
skill front inbox inb_4bj7r By inbox ID
|
|
93082
|
+
skill front inbox "AI Hero" By name (case-insensitive exact match)
|
|
93083
|
+
|
|
93084
|
+
INBOX NAME LOOKUP
|
|
93085
|
+
Pass a human-readable name instead of inb_xxx. The command fetches all
|
|
93086
|
+
inboxes and matches case-insensitively against the full name.
|
|
93087
|
+
Example: "ai hero" matches "AI Hero", "Total TypeScript" matches exactly.
|
|
93088
|
+
If no match is found, an error is thrown with the name you provided.
|
|
93089
|
+
|
|
93090
|
+
OPTIONS
|
|
93091
|
+
Flag Default Description
|
|
93092
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
93093
|
+
--status <status> (none) Filter conversations by status
|
|
93094
|
+
--tag <tag> (none) Filter by tag name or tag_xxx ID
|
|
93095
|
+
--limit <n> 50 Max conversations to return (pages auto)
|
|
93096
|
+
--json false Output as JSON (HATEOAS envelope)
|
|
93097
|
+
|
|
93098
|
+
STATUS VALUES (--status flag)
|
|
93099
|
+
open In the Open tab (not archived, trashed, or snoozed)
|
|
93100
|
+
archived In the Archived tab
|
|
93101
|
+
assigned Has an assignee
|
|
93102
|
+
unassigned No assignee
|
|
93103
|
+
unreplied Last message was inbound (no teammate reply yet)
|
|
93104
|
+
snoozed Snoozed (will reopen later)
|
|
93105
|
+
trashed In Trash
|
|
93106
|
+
waiting Waiting for response
|
|
93107
|
+
|
|
93108
|
+
TAG FILTER (--tag flag)
|
|
93109
|
+
Filter by tag name (human-readable) or tag ID (tag_xxx).
|
|
93110
|
+
The tag value is passed as a Front query filter: tag:"<value>"
|
|
93111
|
+
Examples:
|
|
93112
|
+
--tag "500 Error" Filter by tag name
|
|
93113
|
+
--tag tag_14nmdp Filter by tag ID
|
|
93114
|
+
|
|
93115
|
+
PAGINATION
|
|
93116
|
+
Page size is always 50. If --limit is higher than 50, the command
|
|
93117
|
+
automatically paginates through results until the limit is reached.
|
|
93118
|
+
Progress is shown in the terminal (e.g., "Fetched 150 conversations...").
|
|
93119
|
+
|
|
93120
|
+
JSON + jq PATTERNS
|
|
93121
|
+
# List all inbox IDs
|
|
93122
|
+
skill front inbox --json | jq '.data[].id'
|
|
93123
|
+
|
|
93124
|
+
# Get inbox names and IDs as a table
|
|
93125
|
+
skill front inbox --json | jq '.data[] | {id, name}'
|
|
93126
|
+
|
|
93127
|
+
# Get conversation IDs from an inbox
|
|
93128
|
+
skill front inbox inb_4bj7r --json | jq '.data.conversations[].id'
|
|
93129
|
+
|
|
93130
|
+
# Get conversation summaries
|
|
93131
|
+
skill front inbox inb_4bj7r --json | jq '.data.conversations[] | {id, subject, status}'
|
|
93132
|
+
|
|
93133
|
+
# Count conversations by status
|
|
93134
|
+
skill front inbox inb_4bj7r --json | jq '[.data.conversations[].status] | group_by(.) | map({status: .[0], count: length})'
|
|
93135
|
+
|
|
93136
|
+
EXAMPLES
|
|
93137
|
+
# Step 1: Find inbox IDs
|
|
93138
|
+
skill front inbox
|
|
93139
|
+
|
|
93140
|
+
# Step 2: Look up inbox by name
|
|
93141
|
+
skill front inbox "Total TypeScript"
|
|
93142
|
+
|
|
93143
|
+
# Step 3: Filter unassigned conversations
|
|
93144
|
+
skill front inbox inb_4bj7r --status unassigned
|
|
93145
|
+
|
|
93146
|
+
# Step 4: Filter by tag
|
|
93147
|
+
skill front inbox inb_4bj7r --tag "500 Error"
|
|
93148
|
+
|
|
93149
|
+
# Step 5: Pipe to jq for conversation IDs
|
|
93150
|
+
skill front inbox inb_4bj7r --status open --json | jq '.data.conversations[].id'
|
|
93151
|
+
|
|
93152
|
+
# Limit results to 10
|
|
93153
|
+
skill front inbox inb_4bj7r --limit 10
|
|
93154
|
+
|
|
93155
|
+
RELATED COMMANDS
|
|
93156
|
+
skill front conversation <id> View full conversation with messages
|
|
93157
|
+
skill front search <query> Search across all inboxes with filters
|
|
93158
|
+
skill front report Generate support metrics report
|
|
93159
|
+
skill front triage Triage unassigned conversations
|
|
93160
|
+
`
|
|
93161
|
+
).action(async (inboxNameOrId, options) => {
|
|
92752
93162
|
if (!inboxNameOrId) {
|
|
92753
93163
|
await listInboxes(options || {});
|
|
92754
93164
|
} else {
|
|
@@ -92759,7 +93169,7 @@ function registerInboxCommand(front) {
|
|
|
92759
93169
|
|
|
92760
93170
|
// src/commands/front/pull-conversations.ts
|
|
92761
93171
|
init_esm_shims();
|
|
92762
|
-
import { writeFileSync as
|
|
93172
|
+
import { writeFileSync as writeFileSync9 } from "fs";
|
|
92763
93173
|
async function pullConversations(options) {
|
|
92764
93174
|
const { inbox, limit: limit2 = 50, output, filter: filter4, json = false } = options;
|
|
92765
93175
|
const frontToken = process.env.FRONT_API_TOKEN;
|
|
@@ -92878,20 +93288,16 @@ Built ${samples.length} eval samples`);
|
|
|
92878
93288
|
console.log(` ${cat}: ${count}`);
|
|
92879
93289
|
}
|
|
92880
93290
|
if (output) {
|
|
92881
|
-
|
|
93291
|
+
writeFileSync9(output, JSON.stringify(samples, null, 2));
|
|
92882
93292
|
console.log(`
|
|
92883
93293
|
Saved to ${output}`);
|
|
92884
93294
|
} else if (json) {
|
|
92885
|
-
|
|
92886
|
-
|
|
92887
|
-
|
|
92888
|
-
|
|
92889
|
-
|
|
92890
|
-
|
|
92891
|
-
}),
|
|
92892
|
-
null,
|
|
92893
|
-
2
|
|
92894
|
-
)
|
|
93295
|
+
writeJsonOutput(
|
|
93296
|
+
hateoasWrap({
|
|
93297
|
+
type: "eval-dataset",
|
|
93298
|
+
command: `skill front pull --inbox ${inbox} --json`,
|
|
93299
|
+
data: samples
|
|
93300
|
+
})
|
|
92895
93301
|
);
|
|
92896
93302
|
}
|
|
92897
93303
|
} catch (error) {
|
|
@@ -92903,7 +93309,73 @@ Saved to ${output}`);
|
|
|
92903
93309
|
}
|
|
92904
93310
|
}
|
|
92905
93311
|
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").
|
|
93312
|
+
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(
|
|
93313
|
+
"after",
|
|
93314
|
+
`
|
|
93315
|
+
\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
|
|
93316
|
+
|
|
93317
|
+
Export conversations from a Front inbox as structured EvalSample objects.
|
|
93318
|
+
Designed for building eval datasets for routing, classification, and
|
|
93319
|
+
canned-response testing.
|
|
93320
|
+
|
|
93321
|
+
OPTIONS
|
|
93322
|
+
-i, --inbox <id> Inbox ID to pull from (inb_xxx). Omit to list
|
|
93323
|
+
available inboxes and their IDs.
|
|
93324
|
+
-l, --limit <n> Max conversations to export (default: 50)
|
|
93325
|
+
-o, --output <file> Write output to a file instead of stdout
|
|
93326
|
+
-f, --filter <term> Only include conversations whose subject or tags
|
|
93327
|
+
contain this text (case-insensitive)
|
|
93328
|
+
--json Output as JSON (HATEOAS-wrapped)
|
|
93329
|
+
|
|
93330
|
+
OUTPUT FORMAT (EvalSample)
|
|
93331
|
+
Each sample includes:
|
|
93332
|
+
- id / conversationId Front conversation ID
|
|
93333
|
+
- subject Conversation subject
|
|
93334
|
+
- customerEmail Sender email address
|
|
93335
|
+
- status Conversation status
|
|
93336
|
+
- tags Array of tag names
|
|
93337
|
+
- triggerMessage Most recent inbound message (id, subject, body, timestamp)
|
|
93338
|
+
- conversationHistory Full message thread (direction, body, timestamp, author)
|
|
93339
|
+
- category Inferred category (see below)
|
|
93340
|
+
|
|
93341
|
+
CATEGORY INFERENCE
|
|
93342
|
+
Category Rule
|
|
93343
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
93344
|
+
refund Tag or subject contains "refund"
|
|
93345
|
+
access Tag contains "access" or subject contains "login"/"access"
|
|
93346
|
+
technical Tag contains "technical" or subject contains "error"/"bug"
|
|
93347
|
+
feedback Subject contains "feedback" or "suggestion"
|
|
93348
|
+
business Subject contains "partnership" or "collaborate"
|
|
93349
|
+
general Everything else (default)
|
|
93350
|
+
|
|
93351
|
+
RATE LIMITING
|
|
93352
|
+
Built-in 100ms delay between conversation message fetches to respect
|
|
93353
|
+
Front API limits. Large exports will take time proportional to --limit.
|
|
93354
|
+
|
|
93355
|
+
EXAMPLES
|
|
93356
|
+
# List available inboxes (no --inbox flag)
|
|
93357
|
+
skill front pull
|
|
93358
|
+
|
|
93359
|
+
# Pull 50 conversations (default limit)
|
|
93360
|
+
skill front pull --inbox inb_4bj7r
|
|
93361
|
+
|
|
93362
|
+
# Pull 200 conversations and save to file
|
|
93363
|
+
skill front pull --inbox inb_4bj7r --limit 200 --output data/eval-dataset.json
|
|
93364
|
+
|
|
93365
|
+
# Pull only refund-related conversations
|
|
93366
|
+
skill front pull --inbox inb_4bj7r --filter refund --output data/refund-samples.json
|
|
93367
|
+
|
|
93368
|
+
# Pipe JSON for analysis
|
|
93369
|
+
skill front pull --inbox inb_4bj7r --json | jq '[.data[] | {id, category, subject}]'
|
|
93370
|
+
|
|
93371
|
+
# Category breakdown
|
|
93372
|
+
skill front pull --inbox inb_4bj7r --json | jq '[.data[].category] | group_by(.) | map({(.[0]): length}) | add'
|
|
93373
|
+
|
|
93374
|
+
RELATED COMMANDS
|
|
93375
|
+
skill eval routing <file> Run routing eval against a dataset
|
|
93376
|
+
skill front inbox List inboxes
|
|
93377
|
+
`
|
|
93378
|
+
).action(pullConversations);
|
|
92907
93379
|
}
|
|
92908
93380
|
|
|
92909
93381
|
// src/commands/front/reply.ts
|
|
@@ -92930,16 +93402,12 @@ async function replyToConversation(conversationId, options) {
|
|
|
92930
93402
|
}
|
|
92931
93403
|
);
|
|
92932
93404
|
if (options.json) {
|
|
92933
|
-
|
|
92934
|
-
|
|
92935
|
-
|
|
92936
|
-
|
|
92937
|
-
|
|
92938
|
-
|
|
92939
|
-
}),
|
|
92940
|
-
null,
|
|
92941
|
-
2
|
|
92942
|
-
)
|
|
93405
|
+
writeJsonOutput(
|
|
93406
|
+
hateoasWrap({
|
|
93407
|
+
type: "draft-reply",
|
|
93408
|
+
command: `skill front reply ${normalizedId} --body ${JSON.stringify(options.body)}${options.author ? ` --author ${options.author}` : ""} --json`,
|
|
93409
|
+
data: draft
|
|
93410
|
+
})
|
|
92943
93411
|
);
|
|
92944
93412
|
return;
|
|
92945
93413
|
}
|
|
@@ -92971,7 +93439,62 @@ async function replyToConversation(conversationId, options) {
|
|
|
92971
93439
|
}
|
|
92972
93440
|
}
|
|
92973
93441
|
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").
|
|
93442
|
+
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(
|
|
93443
|
+
"after",
|
|
93444
|
+
`
|
|
93445
|
+
\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
|
|
93446
|
+
|
|
93447
|
+
\u26A0\uFE0F SAFETY: This command creates a DRAFT only. It NEVER auto-sends.
|
|
93448
|
+
The draft appears in Front for a human to review, edit, and send manually.
|
|
93449
|
+
This is by design \u2014 the HITL principle ensures no message goes out without
|
|
93450
|
+
human approval.
|
|
93451
|
+
|
|
93452
|
+
USAGE
|
|
93453
|
+
skill front reply <conversation-id> --body "Your reply text here"
|
|
93454
|
+
|
|
93455
|
+
OPTIONS
|
|
93456
|
+
--body <text> Required. The reply body text.
|
|
93457
|
+
Accepts plain text or HTML.
|
|
93458
|
+
--author <teammate-id> Optional. Teammate ID (tea_xxx) to set as sender.
|
|
93459
|
+
Defaults to the API token owner.
|
|
93460
|
+
--json Output as JSON.
|
|
93461
|
+
|
|
93462
|
+
BODY FORMAT
|
|
93463
|
+
Plain text: --body "Thanks for reaching out. We'll look into this."
|
|
93464
|
+
HTML: --body "<p>Hi there,</p><p>Your refund has been processed.</p>"
|
|
93465
|
+
|
|
93466
|
+
For multi-line plain text, the body will render as-is in the Front draft.
|
|
93467
|
+
|
|
93468
|
+
JSON OUTPUT (--json)
|
|
93469
|
+
Returns a HATEOAS-wrapped object:
|
|
93470
|
+
{ type: "draft-reply", data: { id, ... } }
|
|
93471
|
+
|
|
93472
|
+
WORKFLOW
|
|
93473
|
+
1. Read the conversation first:
|
|
93474
|
+
skill front conversation cnv_abc123 -m
|
|
93475
|
+
2. Draft a reply:
|
|
93476
|
+
skill front reply cnv_abc123 --body "We've processed your request."
|
|
93477
|
+
3. Open Front \u2192 review the draft \u2192 edit if needed \u2192 click Send.
|
|
93478
|
+
|
|
93479
|
+
EXAMPLES
|
|
93480
|
+
# Simple draft reply
|
|
93481
|
+
skill front reply cnv_abc123 --body "Thanks, we're looking into this now."
|
|
93482
|
+
|
|
93483
|
+
# HTML reply with specific author
|
|
93484
|
+
skill front reply cnv_abc123 \\
|
|
93485
|
+
--body "<p>Hi! Your license has been transferred.</p>" \\
|
|
93486
|
+
--author tea_def456
|
|
93487
|
+
|
|
93488
|
+
# Draft reply and capture the draft ID
|
|
93489
|
+
skill front reply cnv_abc123 --body "Processing your refund." --json \\
|
|
93490
|
+
| jq '.data.id'
|
|
93491
|
+
|
|
93492
|
+
RELATED COMMANDS
|
|
93493
|
+
skill front conversation <id> -m View conversation + message history
|
|
93494
|
+
skill front message <id> View a specific message body
|
|
93495
|
+
skill front search Find conversations to reply to
|
|
93496
|
+
`
|
|
93497
|
+
).action(replyToConversation);
|
|
92975
93498
|
}
|
|
92976
93499
|
|
|
92977
93500
|
// src/commands/front/report.ts
|
|
@@ -93074,18 +93597,14 @@ async function generateReport(options) {
|
|
|
93074
93597
|
);
|
|
93075
93598
|
if (json) {
|
|
93076
93599
|
const unresolvedIds = report.unresolvedIssues.map((i) => i.id);
|
|
93077
|
-
|
|
93078
|
-
|
|
93079
|
-
|
|
93080
|
-
|
|
93081
|
-
|
|
93082
|
-
|
|
93083
|
-
|
|
93084
|
-
|
|
93085
|
-
}),
|
|
93086
|
-
null,
|
|
93087
|
-
2
|
|
93088
|
-
)
|
|
93600
|
+
writeJsonOutput(
|
|
93601
|
+
hateoasWrap({
|
|
93602
|
+
type: "report",
|
|
93603
|
+
command: `skill front report --inbox ${inbox} --json`,
|
|
93604
|
+
data: report,
|
|
93605
|
+
links: reportLinks(inbox, unresolvedIds),
|
|
93606
|
+
actions: reportActions(inbox)
|
|
93607
|
+
})
|
|
93089
93608
|
);
|
|
93090
93609
|
} else {
|
|
93091
93610
|
printReport(report);
|
|
@@ -93176,7 +93695,64 @@ function registerReportCommand(front) {
|
|
|
93176
93695
|
"Number of days to include in report",
|
|
93177
93696
|
parseInt,
|
|
93178
93697
|
30
|
|
93179
|
-
).option("--json", "Output as JSON").
|
|
93698
|
+
).option("--json", "Output as JSON").addHelpText(
|
|
93699
|
+
"after",
|
|
93700
|
+
`
|
|
93701
|
+
\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
|
|
93702
|
+
|
|
93703
|
+
Generates a comprehensive report for a Front inbox covering the last N days.
|
|
93704
|
+
--inbox is required. --days defaults to 30.
|
|
93705
|
+
|
|
93706
|
+
WHAT THE REPORT INCLUDES
|
|
93707
|
+
- Overview: total conversations, status breakdown with percentages
|
|
93708
|
+
- Volume by week: bar chart of conversation volume per ISO week
|
|
93709
|
+
- Tag breakdown: top 15 tags by frequency
|
|
93710
|
+
- Top senders: top 10 sender email addresses
|
|
93711
|
+
- Unresolved issues: unassigned conversations (newest first, up to 10 shown)
|
|
93712
|
+
|
|
93713
|
+
BASIC USAGE
|
|
93714
|
+
skill front report --inbox inb_4bj7r
|
|
93715
|
+
skill front report --inbox inb_4bj7r --days 60
|
|
93716
|
+
skill front report --inbox inb_4bj7r --days 7
|
|
93717
|
+
|
|
93718
|
+
JSON OUTPUT (for scripting)
|
|
93719
|
+
skill front report --inbox inb_4bj7r --json
|
|
93720
|
+
|
|
93721
|
+
# Extract unresolved issues
|
|
93722
|
+
skill front report --inbox inb_4bj7r --json | jq '.data.unresolvedIssues'
|
|
93723
|
+
|
|
93724
|
+
# Top senders
|
|
93725
|
+
skill front report --inbox inb_4bj7r --json | jq '.data.topSenders'
|
|
93726
|
+
|
|
93727
|
+
# Volume by week
|
|
93728
|
+
skill front report --inbox inb_4bj7r --json | jq '.data.volumeByWeek'
|
|
93729
|
+
|
|
93730
|
+
# Status breakdown
|
|
93731
|
+
skill front report --inbox inb_4bj7r --json | jq '.data.overview.byStatus'
|
|
93732
|
+
|
|
93733
|
+
# Count of unassigned conversations
|
|
93734
|
+
skill front report --inbox inb_4bj7r --json | jq '.data.unresolvedIssues | length'
|
|
93735
|
+
|
|
93736
|
+
# Senders with more than 5 conversations
|
|
93737
|
+
skill front report --inbox inb_4bj7r --json \\
|
|
93738
|
+
| jq '[.data.topSenders[] | select(.count > 5)]'
|
|
93739
|
+
|
|
93740
|
+
WORKFLOW: REPORT \u2192 TRIAGE \u2192 ARCHIVE
|
|
93741
|
+
# 1. Run report to understand inbox state
|
|
93742
|
+
skill front report --inbox inb_4bj7r
|
|
93743
|
+
|
|
93744
|
+
# 2. Triage to categorize conversations
|
|
93745
|
+
skill front triage --inbox inb_4bj7r
|
|
93746
|
+
|
|
93747
|
+
# 3. Bulk archive the noise
|
|
93748
|
+
skill front bulk-archive --inbox inb_4bj7r --sender "noreply@" --dry-run
|
|
93749
|
+
|
|
93750
|
+
RELATED COMMANDS
|
|
93751
|
+
skill front triage Categorize conversations by intent
|
|
93752
|
+
skill front inbox List and inspect inboxes
|
|
93753
|
+
skill front bulk-archive Archive conversations matching filters
|
|
93754
|
+
`
|
|
93755
|
+
).action(generateReport);
|
|
93180
93756
|
}
|
|
93181
93757
|
|
|
93182
93758
|
// src/commands/front/search.ts
|
|
@@ -93235,24 +93811,20 @@ async function searchConversations(query, options) {
|
|
|
93235
93811
|
console.log("");
|
|
93236
93812
|
}
|
|
93237
93813
|
if (options.json) {
|
|
93238
|
-
|
|
93239
|
-
|
|
93240
|
-
|
|
93241
|
-
|
|
93242
|
-
|
|
93243
|
-
|
|
93244
|
-
|
|
93245
|
-
|
|
93246
|
-
|
|
93247
|
-
|
|
93248
|
-
|
|
93249
|
-
|
|
93250
|
-
|
|
93251
|
-
|
|
93252
|
-
}),
|
|
93253
|
-
null,
|
|
93254
|
-
2
|
|
93255
|
-
)
|
|
93814
|
+
writeJsonOutput(
|
|
93815
|
+
hateoasWrap({
|
|
93816
|
+
type: "search-results",
|
|
93817
|
+
command: `skill front search ${JSON.stringify(fullQuery)} --json`,
|
|
93818
|
+
data: {
|
|
93819
|
+
query: fullQuery,
|
|
93820
|
+
total: conversations.length,
|
|
93821
|
+
conversations
|
|
93822
|
+
},
|
|
93823
|
+
links: conversationListLinks(
|
|
93824
|
+
conversations.map((c) => ({ id: c.id, subject: c.subject }))
|
|
93825
|
+
),
|
|
93826
|
+
actions: options.inbox ? conversationListActions(options.inbox) : []
|
|
93827
|
+
})
|
|
93256
93828
|
);
|
|
93257
93829
|
return;
|
|
93258
93830
|
}
|
|
@@ -93382,6 +93954,17 @@ EXAMPLES
|
|
|
93382
93954
|
# Pipe JSON to jq for IDs only
|
|
93383
93955
|
skill front search "is:unassigned" --inbox inb_4bj7r --json | jq '.data.conversations[].id'
|
|
93384
93956
|
|
|
93957
|
+
LARGE RESULTS
|
|
93958
|
+
When --json output exceeds 64KB (common with 25+ conversations), results are
|
|
93959
|
+
automatically written to a temp file. Stdout gets a summary with the file path:
|
|
93960
|
+
{ "_file": "/tmp/skill-front/1738692000.json", "total": 50, ... }
|
|
93961
|
+
|
|
93962
|
+
To always get the full file:
|
|
93963
|
+
skill front search "..." --json > results.json
|
|
93964
|
+
|
|
93965
|
+
To process the spilled file:
|
|
93966
|
+
cat /tmp/skill-front/*.json | jq '.data.conversations[].id'
|
|
93967
|
+
|
|
93385
93968
|
Full docs: https://dev.frontapp.com/docs/search-1
|
|
93386
93969
|
`
|
|
93387
93970
|
).action((query, options) => {
|
|
@@ -93521,20 +94104,16 @@ async function listTags(options) {
|
|
|
93521
94104
|
);
|
|
93522
94105
|
const filteredTags = options.unused ? tagsWithCounts.filter((t2) => t2.conversation_count === 0) : tagsWithCounts;
|
|
93523
94106
|
if (options.json) {
|
|
93524
|
-
|
|
93525
|
-
|
|
93526
|
-
|
|
93527
|
-
|
|
93528
|
-
|
|
93529
|
-
|
|
93530
|
-
|
|
93531
|
-
|
|
93532
|
-
|
|
93533
|
-
|
|
93534
|
-
}),
|
|
93535
|
-
null,
|
|
93536
|
-
2
|
|
93537
|
-
)
|
|
94107
|
+
writeJsonOutput(
|
|
94108
|
+
hateoasWrap({
|
|
94109
|
+
type: "tag-list",
|
|
94110
|
+
command: `skill front tags list${options.unused ? " --unused" : ""} --json`,
|
|
94111
|
+
data: filteredTags,
|
|
94112
|
+
links: tagListLinks(
|
|
94113
|
+
filteredTags.map((t2) => ({ id: t2.id, name: t2.name }))
|
|
94114
|
+
),
|
|
94115
|
+
actions: tagListActions()
|
|
94116
|
+
})
|
|
93538
94117
|
);
|
|
93539
94118
|
return;
|
|
93540
94119
|
}
|
|
@@ -93940,13 +94519,146 @@ async function cleanupTags(options) {
|
|
|
93940
94519
|
}
|
|
93941
94520
|
}
|
|
93942
94521
|
function registerTagCommands(frontCommand) {
|
|
93943
|
-
const tags = frontCommand.command("tags").description("List, rename, delete, and clean up Front tags")
|
|
93944
|
-
|
|
93945
|
-
|
|
93946
|
-
|
|
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);
|
|
93947
94629
|
tags.command("cleanup").description(
|
|
93948
94630
|
"Clean up tags: delete duplicates, merge case variants, remove obsolete, create missing standard tags"
|
|
93949
|
-
).option("--execute", "Actually apply changes (default is dry-run)", false).
|
|
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);
|
|
93950
94662
|
}
|
|
93951
94663
|
|
|
93952
94664
|
// src/commands/front/triage.ts
|
|
@@ -94045,21 +94757,17 @@ Fetching ${status} conversations from inbox ${inbox}...`);
|
|
|
94045
94757
|
});
|
|
94046
94758
|
}
|
|
94047
94759
|
if (json) {
|
|
94048
|
-
|
|
94049
|
-
|
|
94050
|
-
|
|
94051
|
-
|
|
94052
|
-
|
|
94053
|
-
|
|
94054
|
-
|
|
94055
|
-
|
|
94056
|
-
|
|
94057
|
-
|
|
94058
|
-
|
|
94059
|
-
}),
|
|
94060
|
-
null,
|
|
94061
|
-
2
|
|
94062
|
-
)
|
|
94760
|
+
writeJsonOutput(
|
|
94761
|
+
hateoasWrap({
|
|
94762
|
+
type: "triage-result",
|
|
94763
|
+
command: `skill front triage --inbox ${inbox} --json`,
|
|
94764
|
+
data: {
|
|
94765
|
+
total: allConversations.length,
|
|
94766
|
+
stats: stats4,
|
|
94767
|
+
results
|
|
94768
|
+
},
|
|
94769
|
+
actions: triageActions(inbox)
|
|
94770
|
+
})
|
|
94063
94771
|
);
|
|
94064
94772
|
return;
|
|
94065
94773
|
}
|
|
@@ -94169,7 +94877,72 @@ function registerTriageCommand(front) {
|
|
|
94169
94877
|
"-s, --status <status>",
|
|
94170
94878
|
"Conversation status filter (unassigned, assigned, archived)",
|
|
94171
94879
|
"unassigned"
|
|
94172
|
-
).option("--auto-archive", "Automatically archive noise and spam").option("--json", "JSON output").
|
|
94880
|
+
).option("--auto-archive", "Automatically archive noise and spam").option("--json", "JSON output").addHelpText(
|
|
94881
|
+
"after",
|
|
94882
|
+
`
|
|
94883
|
+
\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
|
|
94884
|
+
|
|
94885
|
+
Categorize inbox conversations into actionable, noise, or spam using
|
|
94886
|
+
heuristic rules. Optionally auto-archive the junk.
|
|
94887
|
+
|
|
94888
|
+
OPTIONS
|
|
94889
|
+
-i, --inbox <id> (required) Inbox ID to triage (inb_xxx)
|
|
94890
|
+
-s, --status <status> Conversation status to filter (default: unassigned)
|
|
94891
|
+
Values: unassigned, assigned, archived
|
|
94892
|
+
--auto-archive Archive all noise + spam conversations automatically
|
|
94893
|
+
--json Output as JSON (HATEOAS-wrapped)
|
|
94894
|
+
|
|
94895
|
+
CATEGORIZATION RULES
|
|
94896
|
+
Category Signals
|
|
94897
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
94898
|
+
Noise mailer-daemon, noreply, no-reply, postmaster, newsletter
|
|
94899
|
+
delivery failures, auto-replies, out-of-office,
|
|
94900
|
+
automated reports (daily/weekly/monthly), cert notifications
|
|
94901
|
+
Spam partnership/sponsorship pitches, guest post / link exchange,
|
|
94902
|
+
backlink requests, marketing opportunities
|
|
94903
|
+
Actionable Everything else (real support issues)
|
|
94904
|
+
|
|
94905
|
+
WORKFLOW
|
|
94906
|
+
1. Triage to see the breakdown:
|
|
94907
|
+
skill front triage --inbox inb_4bj7r
|
|
94908
|
+
|
|
94909
|
+
2. Review categories in the output (actionable / noise / spam)
|
|
94910
|
+
|
|
94911
|
+
3. If satisfied, auto-archive the junk:
|
|
94912
|
+
skill front triage --inbox inb_4bj7r --auto-archive
|
|
94913
|
+
|
|
94914
|
+
JSON + jq PATTERNS
|
|
94915
|
+
# Just the stats
|
|
94916
|
+
skill front triage --inbox inb_4bj7r --json | jq '.data.stats'
|
|
94917
|
+
|
|
94918
|
+
# All noise conversation IDs
|
|
94919
|
+
skill front triage --inbox inb_4bj7r --json | jq '[.data.results[] | select(.category == "noise") | .id]'
|
|
94920
|
+
|
|
94921
|
+
# Spam sender emails
|
|
94922
|
+
skill front triage --inbox inb_4bj7r --json | jq '[.data.results[] | select(.category == "spam") | .senderEmail]'
|
|
94923
|
+
|
|
94924
|
+
# Actionable count
|
|
94925
|
+
skill front triage --inbox inb_4bj7r --json | jq '.data.stats.actionable'
|
|
94926
|
+
|
|
94927
|
+
EXAMPLES
|
|
94928
|
+
# Triage unassigned conversations (default)
|
|
94929
|
+
skill front triage --inbox inb_4bj7r
|
|
94930
|
+
|
|
94931
|
+
# Triage assigned conversations
|
|
94932
|
+
skill front triage --inbox inb_4bj7r --status assigned
|
|
94933
|
+
|
|
94934
|
+
# Triage and auto-archive noise + spam
|
|
94935
|
+
skill front triage --inbox inb_4bj7r --auto-archive
|
|
94936
|
+
|
|
94937
|
+
# Pipe JSON for downstream processing
|
|
94938
|
+
skill front triage --inbox inb_4bj7r --json | jq '.data.results[] | select(.category == "actionable")'
|
|
94939
|
+
|
|
94940
|
+
RELATED COMMANDS
|
|
94941
|
+
skill front bulk-archive Bulk-archive conversations by query
|
|
94942
|
+
skill front report Inbox activity report
|
|
94943
|
+
skill front search Search conversations with filters
|
|
94944
|
+
`
|
|
94945
|
+
).action(triageConversations);
|
|
94173
94946
|
}
|
|
94174
94947
|
|
|
94175
94948
|
// src/commands/front/index.ts
|
|
@@ -94207,17 +94980,13 @@ async function getMessage(id, options) {
|
|
|
94207
94980
|
const front = getFrontClient9();
|
|
94208
94981
|
const message = await front.messages.get(normalizeId6(id));
|
|
94209
94982
|
if (options.json) {
|
|
94210
|
-
|
|
94211
|
-
|
|
94212
|
-
|
|
94213
|
-
|
|
94214
|
-
|
|
94215
|
-
|
|
94216
|
-
|
|
94217
|
-
}),
|
|
94218
|
-
null,
|
|
94219
|
-
2
|
|
94220
|
-
)
|
|
94983
|
+
writeJsonOutput(
|
|
94984
|
+
hateoasWrap({
|
|
94985
|
+
type: "message",
|
|
94986
|
+
command: `skill front message ${normalizeId6(id)} --json`,
|
|
94987
|
+
data: message,
|
|
94988
|
+
links: messageLinks(message.id)
|
|
94989
|
+
})
|
|
94221
94990
|
);
|
|
94222
94991
|
return;
|
|
94223
94992
|
}
|
|
@@ -94276,18 +95045,14 @@ async function getConversation2(id, options) {
|
|
|
94276
95045
|
}
|
|
94277
95046
|
if (options.json) {
|
|
94278
95047
|
const convId = normalizeId6(id);
|
|
94279
|
-
|
|
94280
|
-
|
|
94281
|
-
|
|
94282
|
-
|
|
94283
|
-
|
|
94284
|
-
|
|
94285
|
-
|
|
94286
|
-
|
|
94287
|
-
}),
|
|
94288
|
-
null,
|
|
94289
|
-
2
|
|
94290
|
-
)
|
|
95048
|
+
writeJsonOutput(
|
|
95049
|
+
hateoasWrap({
|
|
95050
|
+
type: "conversation",
|
|
95051
|
+
command: `skill front conversation ${convId} --json`,
|
|
95052
|
+
data: { conversation, messages },
|
|
95053
|
+
links: conversationLinks(conversation.id),
|
|
95054
|
+
actions: conversationActions(conversation.id)
|
|
95055
|
+
})
|
|
94291
95056
|
);
|
|
94292
95057
|
return;
|
|
94293
95058
|
}
|
|
@@ -94345,19 +95110,15 @@ async function listTeammates(options) {
|
|
|
94345
95110
|
const front = getFrontSdkClient2();
|
|
94346
95111
|
const result = await front.teammates.list();
|
|
94347
95112
|
if (options.json) {
|
|
94348
|
-
|
|
94349
|
-
|
|
94350
|
-
|
|
94351
|
-
|
|
94352
|
-
|
|
94353
|
-
|
|
94354
|
-
|
|
94355
|
-
|
|
94356
|
-
|
|
94357
|
-
}),
|
|
94358
|
-
null,
|
|
94359
|
-
2
|
|
94360
|
-
)
|
|
95113
|
+
writeJsonOutput(
|
|
95114
|
+
hateoasWrap({
|
|
95115
|
+
type: "teammate-list",
|
|
95116
|
+
command: "skill front teammates --json",
|
|
95117
|
+
data: result._results,
|
|
95118
|
+
links: teammateListLinks(
|
|
95119
|
+
result._results.map((t2) => ({ id: t2.id, email: t2.email }))
|
|
95120
|
+
)
|
|
95121
|
+
})
|
|
94361
95122
|
);
|
|
94362
95123
|
return;
|
|
94363
95124
|
}
|
|
@@ -94398,17 +95159,13 @@ async function getTeammate(id, options) {
|
|
|
94398
95159
|
const front = getFrontSdkClient2();
|
|
94399
95160
|
const teammate = await front.teammates.get(id);
|
|
94400
95161
|
if (options.json) {
|
|
94401
|
-
|
|
94402
|
-
|
|
94403
|
-
|
|
94404
|
-
|
|
94405
|
-
|
|
94406
|
-
|
|
94407
|
-
|
|
94408
|
-
}),
|
|
94409
|
-
null,
|
|
94410
|
-
2
|
|
94411
|
-
)
|
|
95162
|
+
writeJsonOutput(
|
|
95163
|
+
hateoasWrap({
|
|
95164
|
+
type: "teammate",
|
|
95165
|
+
command: `skill front teammate ${id} --json`,
|
|
95166
|
+
data: teammate,
|
|
95167
|
+
links: teammateLinks(teammate.id)
|
|
95168
|
+
})
|
|
94412
95169
|
);
|
|
94413
95170
|
return;
|
|
94414
95171
|
}
|
|
@@ -94443,10 +95200,245 @@ async function getTeammate(id, options) {
|
|
|
94443
95200
|
}
|
|
94444
95201
|
function registerFrontCommands(program3) {
|
|
94445
95202
|
const front = program3.command("front").description("Front conversations, inboxes, tags, archival, and reporting");
|
|
94446
|
-
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);
|
|
94447
|
-
|
|
94448
|
-
|
|
94449
|
-
|
|
95203
|
+
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);
|
|
95204
|
+
messageCmd.addHelpText(
|
|
95205
|
+
"after",
|
|
95206
|
+
`
|
|
95207
|
+
\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
|
|
95208
|
+
|
|
95209
|
+
Fetches a single message from Front by its ID. Returns the full message
|
|
95210
|
+
including HTML body, plaintext body, author, recipients, attachments,
|
|
95211
|
+
and metadata.
|
|
95212
|
+
|
|
95213
|
+
ID FORMAT
|
|
95214
|
+
msg_xxx Front message ID (prefixed with msg_)
|
|
95215
|
+
You can find message IDs from conversation message lists.
|
|
95216
|
+
|
|
95217
|
+
WHAT'S RETURNED
|
|
95218
|
+
Field Description
|
|
95219
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
95220
|
+
id Message ID (msg_xxx)
|
|
95221
|
+
type Message type (email, sms, custom, etc.)
|
|
95222
|
+
subject Message subject line
|
|
95223
|
+
body Full HTML body
|
|
95224
|
+
text Plaintext body (stripped HTML)
|
|
95225
|
+
author Author object (email, id) \u2014 teammate or contact
|
|
95226
|
+
recipients Array of {role, handle} \u2014 from/to/cc/bcc
|
|
95227
|
+
attachments Array of {filename, content_type, size, url}
|
|
95228
|
+
created_at Unix timestamp of message creation
|
|
95229
|
+
metadata Headers, external references
|
|
95230
|
+
|
|
95231
|
+
JSON + jq PATTERNS
|
|
95232
|
+
# Get the HTML body
|
|
95233
|
+
skill front message msg_xxx --json | jq '.data.body'
|
|
95234
|
+
|
|
95235
|
+
# Get the plaintext body
|
|
95236
|
+
skill front message msg_xxx --json | jq '.data.text'
|
|
95237
|
+
|
|
95238
|
+
# Get the author email
|
|
95239
|
+
skill front message msg_xxx --json | jq '.data.author.email'
|
|
95240
|
+
|
|
95241
|
+
# List all recipients
|
|
95242
|
+
skill front message msg_xxx --json | jq '.data.recipients[] | {role, handle}'
|
|
95243
|
+
|
|
95244
|
+
# List attachment filenames
|
|
95245
|
+
skill front message msg_xxx --json | jq '.data.attachments[].filename'
|
|
95246
|
+
|
|
95247
|
+
RELATED COMMANDS
|
|
95248
|
+
skill front conversation <id> -m Find message IDs from a conversation
|
|
95249
|
+
skill front search <query> Search conversations to find threads
|
|
95250
|
+
|
|
95251
|
+
EXAMPLES
|
|
95252
|
+
# Get full message details (human-readable)
|
|
95253
|
+
skill front message msg_1a2b3c
|
|
95254
|
+
|
|
95255
|
+
# Get message as JSON for piping
|
|
95256
|
+
skill front message msg_1a2b3c --json
|
|
95257
|
+
|
|
95258
|
+
# Extract just the body text
|
|
95259
|
+
skill front message msg_1a2b3c --json | jq -r '.data.text'
|
|
95260
|
+
|
|
95261
|
+
# Check who sent a message
|
|
95262
|
+
skill front message msg_1a2b3c --json | jq '{author: .data.author.email, recipients: [.data.recipients[].handle]}'
|
|
95263
|
+
`
|
|
95264
|
+
);
|
|
95265
|
+
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);
|
|
95266
|
+
conversationCmd.addHelpText(
|
|
95267
|
+
"after",
|
|
95268
|
+
`
|
|
95269
|
+
\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
|
|
95270
|
+
|
|
95271
|
+
Fetches a conversation from Front by its ID. Returns metadata, tags,
|
|
95272
|
+
assignee, recipient, and optionally the full message history.
|
|
95273
|
+
|
|
95274
|
+
ID FORMAT
|
|
95275
|
+
cnv_xxx Front conversation ID (prefixed with cnv_)
|
|
95276
|
+
Find conversation IDs via search or inbox listing.
|
|
95277
|
+
|
|
95278
|
+
FLAGS
|
|
95279
|
+
-m, --messages Include full message history in the response.
|
|
95280
|
+
Without this flag, only conversation metadata is returned.
|
|
95281
|
+
|
|
95282
|
+
WHAT'S RETURNED
|
|
95283
|
+
Field Description
|
|
95284
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
95285
|
+
id Conversation ID (cnv_xxx)
|
|
95286
|
+
subject Conversation subject line
|
|
95287
|
+
status Current status (see below)
|
|
95288
|
+
created_at Unix timestamp
|
|
95289
|
+
recipient Primary recipient {handle, role}
|
|
95290
|
+
assignee Assigned teammate {id, email} or null
|
|
95291
|
+
tags Array of {id, name} tags on this conversation
|
|
95292
|
+
messages (with -m) Array of full message objects
|
|
95293
|
+
|
|
95294
|
+
STATUS VALUES
|
|
95295
|
+
archived Conversation is archived
|
|
95296
|
+
unassigned Open, no assignee
|
|
95297
|
+
assigned Open, has an assignee
|
|
95298
|
+
deleted In trash
|
|
95299
|
+
waiting Waiting for response
|
|
95300
|
+
|
|
95301
|
+
JSON + jq PATTERNS
|
|
95302
|
+
# Get conversation metadata
|
|
95303
|
+
skill front conversation cnv_xxx --json | jq '.data.conversation'
|
|
95304
|
+
|
|
95305
|
+
# Get all messages (requires -m flag)
|
|
95306
|
+
skill front conversation cnv_xxx -m --json | jq '.data.messages[]'
|
|
95307
|
+
|
|
95308
|
+
# Get just message bodies as plaintext
|
|
95309
|
+
skill front conversation cnv_xxx -m --json | jq -r '.data.messages[].text'
|
|
95310
|
+
|
|
95311
|
+
# Extract tag names
|
|
95312
|
+
skill front conversation cnv_xxx --json | jq '[.data.conversation.tags[].name]'
|
|
95313
|
+
|
|
95314
|
+
# Get assignee email
|
|
95315
|
+
skill front conversation cnv_xxx --json | jq '.data.conversation.assignee.email'
|
|
95316
|
+
|
|
95317
|
+
# Get message count
|
|
95318
|
+
skill front conversation cnv_xxx -m --json | jq '.data.messages | length'
|
|
95319
|
+
|
|
95320
|
+
# Get inbound messages only
|
|
95321
|
+
skill front conversation cnv_xxx -m --json | jq '[.data.messages[] | select(.is_inbound)]'
|
|
95322
|
+
|
|
95323
|
+
RELATED COMMANDS
|
|
95324
|
+
skill front message <id> Get full details for a specific message
|
|
95325
|
+
skill front assign <cnv> <tea> Assign conversation to a teammate
|
|
95326
|
+
skill front tag <cnv> <tag> Add a tag to a conversation
|
|
95327
|
+
skill front reply <cnv> Send a reply to a conversation
|
|
95328
|
+
skill front search <query> Search for conversations
|
|
95329
|
+
|
|
95330
|
+
EXAMPLES
|
|
95331
|
+
# Get conversation overview
|
|
95332
|
+
skill front conversation cnv_abc123
|
|
95333
|
+
|
|
95334
|
+
# Get conversation with full message history
|
|
95335
|
+
skill front conversation cnv_abc123 -m
|
|
95336
|
+
|
|
95337
|
+
# Pipe to jq to extract tags and assignee
|
|
95338
|
+
skill front conversation cnv_abc123 --json | jq '{tags: [.data.conversation.tags[].name], assignee: .data.conversation.assignee.email}'
|
|
95339
|
+
|
|
95340
|
+
# Get the latest message text from a conversation
|
|
95341
|
+
skill front conversation cnv_abc123 -m --json | jq -r '.data.messages[-1].text'
|
|
95342
|
+
`
|
|
95343
|
+
);
|
|
95344
|
+
const teammatesCmd = front.command("teammates").description("List all teammates in the workspace").option("--json", "Output as JSON").action(listTeammates);
|
|
95345
|
+
teammatesCmd.addHelpText(
|
|
95346
|
+
"after",
|
|
95347
|
+
`
|
|
95348
|
+
\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
|
|
95349
|
+
|
|
95350
|
+
Lists all teammates in the Front workspace. Returns each teammate's ID,
|
|
95351
|
+
email, name, username, and availability status.
|
|
95352
|
+
|
|
95353
|
+
WHAT'S RETURNED (per teammate)
|
|
95354
|
+
Field Description
|
|
95355
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
95356
|
+
id Teammate ID (tea_xxx)
|
|
95357
|
+
email Teammate email address
|
|
95358
|
+
first_name First name
|
|
95359
|
+
last_name Last name
|
|
95360
|
+
username Front username
|
|
95361
|
+
is_available Whether teammate is currently available (true/false)
|
|
95362
|
+
|
|
95363
|
+
JSON + jq PATTERNS
|
|
95364
|
+
# Get all teammate IDs
|
|
95365
|
+
skill front teammates --json | jq '[.data[].id]'
|
|
95366
|
+
|
|
95367
|
+
# Get ID + email pairs
|
|
95368
|
+
skill front teammates --json | jq '.data[] | {id, email}'
|
|
95369
|
+
|
|
95370
|
+
# Find a teammate by email
|
|
95371
|
+
skill front teammates --json | jq '.data[] | select(.email == "joel@example.com")'
|
|
95372
|
+
|
|
95373
|
+
# List only available teammates
|
|
95374
|
+
skill front teammates --json | jq '[.data[] | select(.is_available)]'
|
|
95375
|
+
|
|
95376
|
+
# Get a count of teammates
|
|
95377
|
+
skill front teammates --json | jq '.data | length'
|
|
95378
|
+
|
|
95379
|
+
RELATED COMMANDS
|
|
95380
|
+
skill front teammate <id> Get details for a specific teammate
|
|
95381
|
+
skill front assign <cnv> <tea> Assign a conversation to a teammate
|
|
95382
|
+
|
|
95383
|
+
EXAMPLES
|
|
95384
|
+
# List all teammates (human-readable table)
|
|
95385
|
+
skill front teammates
|
|
95386
|
+
|
|
95387
|
+
# List as JSON for scripting
|
|
95388
|
+
skill front teammates --json
|
|
95389
|
+
|
|
95390
|
+
# Find teammate ID by email for use in assign
|
|
95391
|
+
skill front teammates --json | jq -r '.data[] | select(.email | contains("joel")) | .id'
|
|
95392
|
+
`
|
|
95393
|
+
);
|
|
95394
|
+
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);
|
|
95395
|
+
teammateCmd.addHelpText(
|
|
95396
|
+
"after",
|
|
95397
|
+
`
|
|
95398
|
+
\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
|
|
95399
|
+
|
|
95400
|
+
Fetches details for a single teammate by their ID. Returns email, name,
|
|
95401
|
+
username, and current availability.
|
|
95402
|
+
|
|
95403
|
+
ID FORMAT
|
|
95404
|
+
tea_xxx Front teammate ID (prefixed with tea_)
|
|
95405
|
+
Find teammate IDs via: skill front teammates
|
|
95406
|
+
|
|
95407
|
+
WHAT'S RETURNED
|
|
95408
|
+
Field Description
|
|
95409
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
95410
|
+
id Teammate ID (tea_xxx)
|
|
95411
|
+
email Teammate email address
|
|
95412
|
+
first_name First name
|
|
95413
|
+
last_name Last name
|
|
95414
|
+
username Front username
|
|
95415
|
+
is_available Whether teammate is currently available (true/false)
|
|
95416
|
+
|
|
95417
|
+
JSON + jq PATTERNS
|
|
95418
|
+
# Get teammate email
|
|
95419
|
+
skill front teammate tea_xxx --json | jq '.data.email'
|
|
95420
|
+
|
|
95421
|
+
# Get full name
|
|
95422
|
+
skill front teammate tea_xxx --json | jq '(.data.first_name + " " + .data.last_name)'
|
|
95423
|
+
|
|
95424
|
+
# Check availability
|
|
95425
|
+
skill front teammate tea_xxx --json | jq '.data.is_available'
|
|
95426
|
+
|
|
95427
|
+
RELATED COMMANDS
|
|
95428
|
+
skill front teammates List all teammates (to find IDs)
|
|
95429
|
+
skill front assign <cnv> <tea> Assign a conversation to this teammate
|
|
95430
|
+
|
|
95431
|
+
EXAMPLES
|
|
95432
|
+
# Get teammate details (human-readable)
|
|
95433
|
+
skill front teammate tea_1a2b3c
|
|
95434
|
+
|
|
95435
|
+
# Get as JSON
|
|
95436
|
+
skill front teammate tea_1a2b3c --json
|
|
95437
|
+
|
|
95438
|
+
# Quick check if teammate is available
|
|
95439
|
+
skill front teammate tea_1a2b3c --json | jq -r 'if .data.is_available then "available" else "away" end'
|
|
95440
|
+
`
|
|
95441
|
+
);
|
|
94450
95442
|
registerInboxCommand(front);
|
|
94451
95443
|
registerArchiveCommand(front);
|
|
94452
95444
|
registerBulkArchiveCommand(front);
|
|
@@ -112631,7 +113623,7 @@ Total: ${result.totalDurationMs}ms`);
|
|
|
112631
113623
|
|
|
112632
113624
|
// src/commands/responses.ts
|
|
112633
113625
|
init_esm_shims();
|
|
112634
|
-
import { writeFileSync as
|
|
113626
|
+
import { writeFileSync as writeFileSync10 } from "fs";
|
|
112635
113627
|
function formatDate3(date) {
|
|
112636
113628
|
return date.toLocaleString("en-US", {
|
|
112637
113629
|
month: "short",
|
|
@@ -113132,7 +114124,7 @@ async function exportResponses(options) {
|
|
|
113132
114124
|
}
|
|
113133
114125
|
const outputJson = JSON.stringify(exportData, null, 2);
|
|
113134
114126
|
if (options.output) {
|
|
113135
|
-
|
|
114127
|
+
writeFileSync10(options.output, outputJson, "utf-8");
|
|
113136
114128
|
console.log(
|
|
113137
114129
|
`Exported ${exportData.length} responses to ${options.output}`
|
|
113138
114130
|
);
|