@eliya-oss/agent-slack 0.1.12 → 0.1.14

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.
@@ -22,7 +22,8 @@ Non-goal for v0: agent-side summarization. The CLI returns clean context; agents
22
22
  ## Global Flags
23
23
 
24
24
  ```bash
25
- agent-slack [--profile NAME] [--team TEAM_ID] [--token user|bot|admin|app]
25
+ agent-slack [--version] [--help]
26
+ [--profile NAME] [--team TEAM_ID] [--token user|bot|admin|app]
26
27
  [--json] [--format json|ndjson|table] [--pretty] [--full]
27
28
  [--fields FIELD[,FIELD...]]
28
29
  [--limit N] [--cursor CURSOR] [--all]
@@ -35,6 +36,7 @@ Defaults:
35
36
  - `--token user` for user-visible data.
36
37
  - `--format json` when stdout is not a TTY.
37
38
  - `--limit` uses Slack's method default unless a command defines a smaller agent-safe default.
39
+ - `--version` prints the CLI version as a bare line and exits `0`. `--help` prints the command catalog.
38
40
 
39
41
  ## Auth
40
42
 
@@ -116,6 +118,8 @@ agent-slack conversation context CHANNEL_ID [--since 24h] [--include users,threa
116
118
 
117
119
  `conversation context` returns normalized messages, hydrated users, thread refs, file refs, reactions, and permalinks in one deterministic payload for agents.
118
120
 
121
+ `conversation list`, `conversation history`, and `conversation context` accept `--all` to auto-paginate every page, plus `--types`/`--limit` where relevant. These flags are advertised in `describe --json` and `<command> --help` so agents can discover them; without `--all` a large list returns only the first page with `paging.has_more: true`.
122
+
119
123
  ### Threads and messages
120
124
 
121
125
  ```bash
@@ -205,8 +209,9 @@ normalization and returns the raw Slack objects.
205
209
  `tz`, and team fields.
206
210
  - **file**: `id`, `name`, `mimetype`, `size`, and `permalink` when present.
207
211
  Drops thumbnails and private URLs.
208
- - **conversation**: `id`, `name`, `is_private`, `is_archived`, `topic`,
209
- `purpose`, `num_members` when present.
212
+ - **conversation**: `id`, `name`, `is_private`, `is_archived`, `is_member`,
213
+ `topic`, `purpose`, `num_members` when present. `is_member` is kept as an
214
+ explicit boolean so an agent can tell membership from a list.
210
215
  - `conversation context` dedupes thread roots already present in `messages` and
211
216
  hydrates every unique author, including those who appear only in replies.
212
217
 
@@ -240,6 +245,17 @@ NDJSON streaming record:
240
245
  {"ok":true,"type":"slack.message","team_id":"T123","channel_id":"C123","ts":"1710000000.000100","data":{}}
241
246
  ```
242
247
 
248
+ NDJSON enrichment records: when `--include` is combined with `--format ndjson`,
249
+ the hydration maps that `--json` returns under `data.users`/`data.threads`/`data.permalinks`
250
+ are streamed as typed records after the message lines, so the streaming mode is
251
+ not lossy. Message lines stay bare; enrichment lines carry a `type`:
252
+
253
+ ```json
254
+ {"type":"slack.user","data":{"id":"U123","name":"ada","real_name":"Ada"}}
255
+ {"type":"slack.thread","data":{"ts":"1710000000.000100","replies":[]}}
256
+ {"type":"slack.permalink","data":{"ts":"1710000000.000100","permalink":"https://slack.com/..."}}
257
+ ```
258
+
243
259
  Structured error on stderr:
244
260
 
245
261
  ```json
@@ -287,6 +303,9 @@ Schema output includes:
287
303
 
288
304
  - command description and examples
289
305
  - positional args and flags
306
+ - `dataKey`: the key under the response envelope's `data` that holds the
307
+ command's primary payload (for example `conversation list` reports
308
+ `dataKey: "channels"`, so the array is at `data.channels`, not `data`)
290
309
  - payload schema when known
291
310
  - required Slack method/scopes
292
311
  - output schema
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @eliya-oss/agent-slack
2
2
 
3
+ ## 0.1.14
4
+
5
+ ### Patch Changes
6
+
7
+ - d856e3b: Two agent-facing discoverability improvements.
8
+
9
+ - `describe --json` and `<command> --help --json` now report a `dataKey` for each command: the key under the response envelope's `data` that holds the primary payload (for example `conversation list` reports `dataKey: "channels"`, so results are at `data.channels`). Agents no longer have to probe the response shape.
10
+ - Conversation results now keep `is_member` as an explicit boolean, so an agent can tell which channels the active token has joined directly from `conversation list`.
11
+
12
+ ## 0.1.13
13
+
14
+ ### Patch Changes
15
+
16
+ - 48d5879: Three CLI fixes for agents.
17
+
18
+ - `agent-slack --version` now prints the version and exits 0 instead of raising a usage error.
19
+ - `conversation list`, `conversation history`, and `conversation context` now advertise their pagination flags (`--all`, `--types`, `--limit`) in `describe --json` and `--help`, so agents can discover `--all` instead of only getting the first page.
20
+ - `--include users,threads,permalinks` now works with `--format ndjson`: the hydrated users, threads, and permalinks are streamed as typed records (`slack.user`, `slack.thread`, `slack.permalink`) after the message lines. Previously they were only present with `--json` and silently dropped in ndjson.
21
+
3
22
  ## 0.1.12
4
23
 
5
24
  ### Patch Changes
package/dist/main.js CHANGED
@@ -25105,6 +25105,7 @@ const booleanFlags = /* @__PURE__ */ new Set([
25105
25105
  "all",
25106
25106
  "allow-write",
25107
25107
  "help",
25108
+ "version",
25108
25109
  "json",
25109
25110
  "no-color",
25110
25111
  "raw",
@@ -25521,9 +25522,143 @@ const pad = (value, width) => value + " ".repeat(Math.max(0, width - value.lengt
25521
25522
  const widthFor = (column) => column === "text" ? 72 : 24;
25522
25523
  const truncate = (value, width) => value.length > width ? `${value.slice(0, Math.max(0, width - 3))}...` : value;
25523
25524
  //#endregion
25525
+ //#region src/output/normalize.ts
25526
+ const isRec = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
25527
+ const asArray = (value) => Array.isArray(value) ? value : [];
25528
+ const str = (record, key) => typeof record[key] === "string" && record[key].length > 0 ? record[key] : void 0;
25529
+ const slimFile = (input) => {
25530
+ if (!isRec(input)) return input;
25531
+ const out = {
25532
+ id: input.id,
25533
+ name: input.name
25534
+ };
25535
+ if (typeof input.mimetype === "string") out.mimetype = input.mimetype;
25536
+ if (typeof input.size === "number") out.size = input.size;
25537
+ const permalink = str(input, "permalink");
25538
+ if (permalink) out.permalink = permalink;
25539
+ return out;
25540
+ };
25541
+ const slimMessage = (input) => {
25542
+ if (!isRec(input)) return input;
25543
+ const m = input;
25544
+ const out = {};
25545
+ if (typeof m.user === "string") out.user = m.user;
25546
+ else if (typeof m.bot_id === "string") out.bot_id = m.bot_id;
25547
+ const username = str(m, "username");
25548
+ if (username) out.username = username;
25549
+ const subtype = str(m, "subtype");
25550
+ if (subtype) out.subtype = subtype;
25551
+ if (typeof m.ts === "string") out.ts = m.ts;
25552
+ out.text = typeof m.text === "string" ? m.text : "";
25553
+ if (typeof m.thread_ts === "string" && m.thread_ts !== m.ts) out.thread_ts = m.thread_ts;
25554
+ if (typeof m.reply_count === "number") out.reply_count = m.reply_count;
25555
+ const reactions = asArray(m.reactions).filter(isRec).map((r) => ({
25556
+ name: r.name,
25557
+ count: r.count
25558
+ }));
25559
+ if (reactions.length > 0) out.reactions = reactions;
25560
+ const files = asArray(m.files).map(slimFile);
25561
+ if (files.length > 0) out.files = files;
25562
+ if (m.edited !== void 0 && m.edited !== null) out.edited = true;
25563
+ return out;
25564
+ };
25565
+ const slimUser = (input) => {
25566
+ if (!isRec(input)) return input;
25567
+ const u = input;
25568
+ const profile = isRec(u.profile) ? u.profile : {};
25569
+ const out = {
25570
+ id: u.id,
25571
+ name: u.name
25572
+ };
25573
+ const real = str(profile, "display_name") ?? str(u, "real_name") ?? str(profile, "real_name");
25574
+ if (real) out.real_name = real;
25575
+ if (u.is_bot === true) out.is_bot = true;
25576
+ if (u.deleted === true) out.deleted = true;
25577
+ const title = str(profile, "title");
25578
+ if (title) out.title = title;
25579
+ return out;
25580
+ };
25581
+ const slimConversation = (input) => {
25582
+ if (!isRec(input)) return input;
25583
+ const c = input;
25584
+ const out = {
25585
+ id: c.id,
25586
+ name: c.name
25587
+ };
25588
+ if (c.is_private === true) out.is_private = true;
25589
+ if (c.is_archived === true) out.is_archived = true;
25590
+ if (c.is_im === true) out.is_im = true;
25591
+ if (c.is_mpim === true) out.is_mpim = true;
25592
+ if (typeof c.is_member === "boolean") out.is_member = c.is_member;
25593
+ const topic = isRec(c.topic) ? str(c.topic, "value") : void 0;
25594
+ if (topic) out.topic = topic;
25595
+ const purpose = isRec(c.purpose) ? str(c.purpose, "value") : void 0;
25596
+ if (purpose) out.purpose = purpose;
25597
+ if (typeof c.num_members === "number") out.num_members = c.num_members;
25598
+ return out;
25599
+ };
25600
+ const slimTeam = (input) => {
25601
+ if (!isRec(input)) return input;
25602
+ const t = input;
25603
+ const out = {
25604
+ id: t.id,
25605
+ name: t.name
25606
+ };
25607
+ const domain = str(t, "domain");
25608
+ if (domain) out.domain = domain;
25609
+ const emailDomain = str(t, "email_domain");
25610
+ if (emailDomain) out.email_domain = emailDomain;
25611
+ const enterpriseId = str(t, "enterprise_id");
25612
+ if (enterpriseId) out.enterprise_id = enterpriseId;
25613
+ const enterpriseName = str(t, "enterprise_name");
25614
+ if (enterpriseName) out.enterprise_name = enterpriseName;
25615
+ return out;
25616
+ };
25617
+ const stripResponseEnvelope = (response) => {
25618
+ const { ok, response_metadata, ...rest } = response;
25619
+ return rest;
25620
+ };
25621
+ const dataKeyFor = (method) => {
25622
+ switch (method) {
25623
+ case "conversations.history":
25624
+ case "conversations.replies": return "messages";
25625
+ case "conversations.info": return "channel";
25626
+ case "conversations.list": return "channels";
25627
+ case "conversations.members": return "members";
25628
+ case "users.info":
25629
+ case "users.lookupByEmail": return "user";
25630
+ case "users.list": return "users";
25631
+ case "files.info": return "file";
25632
+ case "files.list": return "files";
25633
+ case "team.info": return "team";
25634
+ default: return;
25635
+ }
25636
+ };
25637
+ const normalizeResponse = (method, response) => {
25638
+ if (!isRec(response)) return response;
25639
+ const r = response;
25640
+ switch (method) {
25641
+ case "conversations.history":
25642
+ case "conversations.replies": return { messages: asArray(r.messages).map(slimMessage) };
25643
+ case "conversations.info": return { channel: slimConversation(r.channel) };
25644
+ case "conversations.list": return { channels: asArray(r.channels).map(slimConversation) };
25645
+ case "users.info":
25646
+ case "users.lookupByEmail": return { user: slimUser(r.user) };
25647
+ case "users.list": return { users: asArray(r.members).map(slimUser) };
25648
+ case "files.info": return { file: slimFile(r.file) };
25649
+ case "files.list": return { files: asArray(r.files).map(slimFile) };
25650
+ case "team.info": return { team: slimTeam(r.team) };
25651
+ case "reactions.get": return isRec(r.message) ? {
25652
+ ...stripResponseEnvelope(r),
25653
+ message: slimMessage(r.message)
25654
+ } : stripResponseEnvelope(r);
25655
+ default: return stripResponseEnvelope(r);
25656
+ }
25657
+ };
25658
+ //#endregion
25524
25659
  //#region src/cli/metadata.ts
25525
25660
  createRequire(import.meta.url);
25526
- const packageJson = { version: "0.1.12" };
25661
+ const packageJson = { version: "0.1.14" };
25527
25662
  const PRIMARY_COMMAND_NAME = "agent-slack";
25528
25663
  const SHORT_COMMAND_NAME = "aslk";
25529
25664
  const COMMAND_NAMES = [PRIMARY_COMMAND_NAME, SHORT_COMMAND_NAME];
@@ -25764,6 +25899,12 @@ const commandMetadata = [
25764
25899
  {
25765
25900
  path: ["conversation", "list"],
25766
25901
  summary: "List conversations visible to the active profile.",
25902
+ flags: [
25903
+ "--types",
25904
+ "--limit",
25905
+ "--all",
25906
+ "--json"
25907
+ ],
25767
25908
  methods: ["conversations.list"],
25768
25909
  scopes: [
25769
25910
  "channels:read",
@@ -25773,12 +25914,21 @@ const commandMetadata = [
25773
25914
  ],
25774
25915
  safety: "read",
25775
25916
  output: "conversation list",
25776
- examples: ["agent-slack conversation list --types public_channel,private_channel --json"]
25917
+ examples: ["agent-slack conversation list --types public_channel,private_channel --json", "agent-slack conversation list --all --json"]
25777
25918
  },
25778
25919
  {
25779
25920
  path: ["conversation", "history"],
25780
25921
  summary: "Read conversation history.",
25781
25922
  args: ["CHANNEL_ID"],
25923
+ flags: [
25924
+ "--oldest",
25925
+ "--latest",
25926
+ "--since",
25927
+ "--inclusive",
25928
+ "--limit",
25929
+ "--all",
25930
+ "--json"
25931
+ ],
25782
25932
  methods: ["conversations.history"],
25783
25933
  scopes: [
25784
25934
  "channels:history",
@@ -25794,6 +25944,15 @@ const commandMetadata = [
25794
25944
  path: ["conversation", "context"],
25795
25945
  summary: "Build channel context for agents.",
25796
25946
  args: ["CHANNEL_ID"],
25947
+ flags: [
25948
+ "--since",
25949
+ "--include",
25950
+ "--limit",
25951
+ "--all",
25952
+ "--full",
25953
+ "--format",
25954
+ "--json"
25955
+ ],
25797
25956
  methods: [
25798
25957
  "conversations.history",
25799
25958
  "conversations.replies",
@@ -25890,7 +26049,14 @@ const commandMetadata = [
25890
26049
  output: "download status",
25891
26050
  examples: ["agent-slack file download F123 --out ./artifact.pdf --json"]
25892
26051
  }
25893
- ];
26052
+ ].map((command) => {
26053
+ const method = command.methods?.[0];
26054
+ const dataKey = method === void 0 ? void 0 : dataKeyFor(method);
26055
+ return dataKey === void 0 ? command : {
26056
+ ...command,
26057
+ dataKey
26058
+ };
26059
+ });
25894
26060
  const describeAllCommands = () => ({
25895
26061
  name: PRIMARY_COMMAND_NAME,
25896
26062
  aliases: [SHORT_COMMAND_NAME],
@@ -25911,6 +26077,7 @@ const renderHumanHelp = (path) => {
25911
26077
  command.summary,
25912
26078
  "",
25913
26079
  command.args === void 0 ? "" : `Usage: ${PRIMARY_COMMAND_NAME} ${command.path.join(" ")} ${command.args.join(" ")}`,
26080
+ command.flags === void 0 ? "" : `Flags: ${command.flags.join(" ")}`,
25914
26081
  command.scopes === void 0 ? "" : `Scopes: ${command.scopes.join(", ")}`,
25915
26082
  "",
25916
26083
  "Examples:",
@@ -25977,123 +26144,6 @@ const renderCompletion = (shell) => {
25977
26144
  };
25978
26145
  const summaryFor = (path) => commandMetadata.find((command) => command.path.join(" ") === path)?.summary.replace(/'/g, "") ?? "";
25979
26146
  //#endregion
25980
- //#region src/output/normalize.ts
25981
- const isRec = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
25982
- const asArray = (value) => Array.isArray(value) ? value : [];
25983
- const str = (record, key) => typeof record[key] === "string" && record[key].length > 0 ? record[key] : void 0;
25984
- const slimFile = (input) => {
25985
- if (!isRec(input)) return input;
25986
- const out = {
25987
- id: input.id,
25988
- name: input.name
25989
- };
25990
- if (typeof input.mimetype === "string") out.mimetype = input.mimetype;
25991
- if (typeof input.size === "number") out.size = input.size;
25992
- const permalink = str(input, "permalink");
25993
- if (permalink) out.permalink = permalink;
25994
- return out;
25995
- };
25996
- const slimMessage = (input) => {
25997
- if (!isRec(input)) return input;
25998
- const m = input;
25999
- const out = {};
26000
- if (typeof m.user === "string") out.user = m.user;
26001
- else if (typeof m.bot_id === "string") out.bot_id = m.bot_id;
26002
- const username = str(m, "username");
26003
- if (username) out.username = username;
26004
- const subtype = str(m, "subtype");
26005
- if (subtype) out.subtype = subtype;
26006
- if (typeof m.ts === "string") out.ts = m.ts;
26007
- out.text = typeof m.text === "string" ? m.text : "";
26008
- if (typeof m.thread_ts === "string" && m.thread_ts !== m.ts) out.thread_ts = m.thread_ts;
26009
- if (typeof m.reply_count === "number") out.reply_count = m.reply_count;
26010
- const reactions = asArray(m.reactions).filter(isRec).map((r) => ({
26011
- name: r.name,
26012
- count: r.count
26013
- }));
26014
- if (reactions.length > 0) out.reactions = reactions;
26015
- const files = asArray(m.files).map(slimFile);
26016
- if (files.length > 0) out.files = files;
26017
- if (m.edited !== void 0 && m.edited !== null) out.edited = true;
26018
- return out;
26019
- };
26020
- const slimUser = (input) => {
26021
- if (!isRec(input)) return input;
26022
- const u = input;
26023
- const profile = isRec(u.profile) ? u.profile : {};
26024
- const out = {
26025
- id: u.id,
26026
- name: u.name
26027
- };
26028
- const real = str(profile, "display_name") ?? str(u, "real_name") ?? str(profile, "real_name");
26029
- if (real) out.real_name = real;
26030
- if (u.is_bot === true) out.is_bot = true;
26031
- if (u.deleted === true) out.deleted = true;
26032
- const title = str(profile, "title");
26033
- if (title) out.title = title;
26034
- return out;
26035
- };
26036
- const slimConversation = (input) => {
26037
- if (!isRec(input)) return input;
26038
- const c = input;
26039
- const out = {
26040
- id: c.id,
26041
- name: c.name
26042
- };
26043
- if (c.is_private === true) out.is_private = true;
26044
- if (c.is_archived === true) out.is_archived = true;
26045
- if (c.is_im === true) out.is_im = true;
26046
- if (c.is_mpim === true) out.is_mpim = true;
26047
- const topic = isRec(c.topic) ? str(c.topic, "value") : void 0;
26048
- if (topic) out.topic = topic;
26049
- const purpose = isRec(c.purpose) ? str(c.purpose, "value") : void 0;
26050
- if (purpose) out.purpose = purpose;
26051
- if (typeof c.num_members === "number") out.num_members = c.num_members;
26052
- return out;
26053
- };
26054
- const slimTeam = (input) => {
26055
- if (!isRec(input)) return input;
26056
- const t = input;
26057
- const out = {
26058
- id: t.id,
26059
- name: t.name
26060
- };
26061
- const domain = str(t, "domain");
26062
- if (domain) out.domain = domain;
26063
- const emailDomain = str(t, "email_domain");
26064
- if (emailDomain) out.email_domain = emailDomain;
26065
- const enterpriseId = str(t, "enterprise_id");
26066
- if (enterpriseId) out.enterprise_id = enterpriseId;
26067
- const enterpriseName = str(t, "enterprise_name");
26068
- if (enterpriseName) out.enterprise_name = enterpriseName;
26069
- return out;
26070
- };
26071
- const stripResponseEnvelope = (response) => {
26072
- const { ok, response_metadata, ...rest } = response;
26073
- return rest;
26074
- };
26075
- const normalizeResponse = (method, response) => {
26076
- if (!isRec(response)) return response;
26077
- const r = response;
26078
- switch (method) {
26079
- case "conversations.history":
26080
- case "conversations.replies": return { messages: asArray(r.messages).map(slimMessage) };
26081
- case "conversations.info": return { channel: slimConversation(r.channel) };
26082
- case "conversations.list": return { channels: asArray(r.channels).map(slimConversation) };
26083
- case "users.info":
26084
- case "users.lookupByEmail": return { user: slimUser(r.user) };
26085
- case "users.list": return { users: asArray(r.members).map(slimUser) };
26086
- case "files.info": return { file: slimFile(r.file) };
26087
- case "files.list": return { files: asArray(r.files).map(slimFile) };
26088
- case "team.info": return { team: slimTeam(r.team) };
26089
- case "reactions.get": return isRec(r.message) ? {
26090
- ...stripResponseEnvelope(r),
26091
- message: slimMessage(r.message)
26092
- } : stripResponseEnvelope(r);
26093
- default: return stripResponseEnvelope(r);
26094
- }
26095
- };
26096
- //#endregion
26097
26147
  //#region src/output/projection.ts
26098
26148
  const projectFields = (value, fields) => {
26099
26149
  if (fields === void 0 || fields.trim() === "") return value;
@@ -26249,6 +26299,12 @@ const extractItems = (services, method, response) => {
26249
26299
  const dispatch = async (parsed, services) => {
26250
26300
  const pos = parsed.positionals;
26251
26301
  const [first, second, third] = pos;
26302
+ if (flagBoolean(parsed, "version")) return {
26303
+ method: "version",
26304
+ profile: null,
26305
+ stdoutValue: null,
26306
+ rawStdout: `${CLI_VERSION}\n`
26307
+ };
26252
26308
  if (flagBoolean(parsed, "help") && !flagBoolean(parsed, "json")) return {
26253
26309
  method: "help",
26254
26310
  profile: null,
@@ -26656,6 +26712,26 @@ const conversationContext = async (parsed, services) => {
26656
26712
  response: history.response,
26657
26713
  stdoutValue: context,
26658
26714
  items: messages.map(shape),
26715
+ enrichmentRecords: [
26716
+ ...recordsFromMap(context.users, (_, data) => ({
26717
+ type: "slack.user",
26718
+ data
26719
+ })),
26720
+ ...recordsFromMap(context.threads, (ts, replies) => ({
26721
+ type: "slack.thread",
26722
+ data: {
26723
+ ts,
26724
+ replies
26725
+ }
26726
+ })),
26727
+ ...recordsFromMap(context.permalinks, (ts, permalink) => ({
26728
+ type: "slack.permalink",
26729
+ data: {
26730
+ ts,
26731
+ permalink
26732
+ }
26733
+ }))
26734
+ ],
26659
26735
  ...warnings.length > 0 ? { warnings } : {}
26660
26736
  };
26661
26737
  };
@@ -26736,6 +26812,16 @@ const threadGet = async (parsed, services) => {
26736
26812
  response: result.response,
26737
26813
  stdoutValue: data,
26738
26814
  items: rawMessages.map(shape),
26815
+ enrichmentRecords: [...recordsFromMap(data.users, (_, user) => ({
26816
+ type: "slack.user",
26817
+ data: user
26818
+ })), ...recordsFromMap(data.permalinks, (ts, permalink) => ({
26819
+ type: "slack.permalink",
26820
+ data: {
26821
+ ts,
26822
+ permalink
26823
+ }
26824
+ }))],
26739
26825
  ...warnings.length > 0 ? { warnings } : {}
26740
26826
  };
26741
26827
  };
@@ -26844,7 +26930,7 @@ const methodCall = async (parsed, services, input, options = {}) => {
26844
26930
  };
26845
26931
  const renderDispatchResult = (parsed, result, options = {}) => {
26846
26932
  if (result.rawStdout !== void 0) return result.rawStdout;
26847
- if (flagString(parsed, "format") === "ndjson") return toNdjson(result.items ?? []);
26933
+ if (flagString(parsed, "format") === "ndjson") return toNdjson([...result.items ?? [], ...result.enrichmentRecords ?? []]);
26848
26934
  const data = projectFields(result.stdoutValue, flagString(parsed, "fields"));
26849
26935
  const envelope = successEnvelope({
26850
26936
  method: result.method,
@@ -26901,6 +26987,7 @@ const stringField = (record, name) => typeof record[name] === "string" && record
26901
26987
  const extractArray = (value) => Array.isArray(value) ? value : [];
26902
26988
  const extractLooseRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
26903
26989
  const uniqueStrings = (values) => [...new Set(values.filter((value) => value !== void 0))];
26990
+ const recordsFromMap = (value, toRecord) => Object.entries(extractLooseRecord(value)).map(([key, entry]) => toRecord(key, entry));
26904
26991
  //#endregion
26905
26992
  //#region src/application/execute.ts
26906
26993
  const executeCli = async (argv, services, options = {}) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eliya-oss/agent-slack",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Slack context CLI for AI agents.",
5
5
  "type": "module",
6
6
  "bin": {