@integrity-labs/agt-cli 0.10.28 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2980,7 +2980,7 @@ var require_compile = __commonJS({
2980
2980
  const schOrFunc = root.refs[ref];
2981
2981
  if (schOrFunc)
2982
2982
  return schOrFunc;
2983
- let _sch = resolve.call(this, root, ref);
2983
+ let _sch = resolve2.call(this, root, ref);
2984
2984
  if (_sch === void 0) {
2985
2985
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2986
2986
  const { schemaId } = this.opts;
@@ -3007,7 +3007,7 @@ var require_compile = __commonJS({
3007
3007
  function sameSchemaEnv(s1, s2) {
3008
3008
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3009
3009
  }
3010
- function resolve(root, ref) {
3010
+ function resolve2(root, ref) {
3011
3011
  let sch;
3012
3012
  while (typeof (sch = this.refs[ref]) == "string")
3013
3013
  ref = sch;
@@ -3582,7 +3582,7 @@ var require_fast_uri = __commonJS({
3582
3582
  }
3583
3583
  return uri;
3584
3584
  }
3585
- function resolve(baseURI, relativeURI, options) {
3585
+ function resolve2(baseURI, relativeURI, options) {
3586
3586
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3587
3587
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3588
3588
  schemelessOptions.skipEscape = true;
@@ -3809,7 +3809,7 @@ var require_fast_uri = __commonJS({
3809
3809
  var fastUri = {
3810
3810
  SCHEMES,
3811
3811
  normalize,
3812
- resolve,
3812
+ resolve: resolve2,
3813
3813
  resolveComponent,
3814
3814
  equal,
3815
3815
  serialize,
@@ -12619,7 +12619,7 @@ var Protocol = class {
12619
12619
  return;
12620
12620
  }
12621
12621
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
12622
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
12622
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
12623
12623
  options?.signal?.throwIfAborted();
12624
12624
  }
12625
12625
  } catch (error2) {
@@ -12636,7 +12636,7 @@ var Protocol = class {
12636
12636
  */
12637
12637
  request(request, resultSchema, options) {
12638
12638
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
12639
- return new Promise((resolve, reject) => {
12639
+ return new Promise((resolve2, reject) => {
12640
12640
  const earlyReject = (error2) => {
12641
12641
  reject(error2);
12642
12642
  };
@@ -12714,7 +12714,7 @@ var Protocol = class {
12714
12714
  if (!parseResult.success) {
12715
12715
  reject(parseResult.error);
12716
12716
  } else {
12717
- resolve(parseResult.data);
12717
+ resolve2(parseResult.data);
12718
12718
  }
12719
12719
  } catch (error2) {
12720
12720
  reject(error2);
@@ -12975,12 +12975,12 @@ var Protocol = class {
12975
12975
  }
12976
12976
  } catch {
12977
12977
  }
12978
- return new Promise((resolve, reject) => {
12978
+ return new Promise((resolve2, reject) => {
12979
12979
  if (signal.aborted) {
12980
12980
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
12981
12981
  return;
12982
12982
  }
12983
- const timeoutId = setTimeout(resolve, interval);
12983
+ const timeoutId = setTimeout(resolve2, interval);
12984
12984
  signal.addEventListener("abort", () => {
12985
12985
  clearTimeout(timeoutId);
12986
12986
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -13850,20 +13850,24 @@ var StdioServerTransport = class {
13850
13850
  this.onclose?.();
13851
13851
  }
13852
13852
  send(message) {
13853
- return new Promise((resolve) => {
13853
+ return new Promise((resolve2) => {
13854
13854
  const json = serializeMessage(message);
13855
13855
  if (this._stdout.write(json)) {
13856
- resolve();
13856
+ resolve2();
13857
13857
  } else {
13858
- this._stdout.once("drain", resolve);
13858
+ this._stdout.once("drain", resolve2);
13859
13859
  }
13860
13860
  });
13861
13861
  }
13862
13862
  };
13863
13863
 
13864
13864
  // src/slack-channel.ts
13865
+ import { readFileSync, statSync } from "fs";
13866
+ import { basename, resolve } from "path";
13867
+ import { homedir } from "os";
13865
13868
  var BOT_TOKEN = process.env.SLACK_BOT_TOKEN;
13866
13869
  var APP_TOKEN = process.env.SLACK_APP_TOKEN;
13870
+ var AGENT_CODE_NAME = process.env.AGT_AGENT_CODE_NAME ?? null;
13867
13871
  var ALLOWED_USERS = new Set(
13868
13872
  (process.env.SLACK_ALLOWED_USERS ?? "").split(",").map((s) => s.trim()).filter(Boolean)
13869
13873
  );
@@ -13925,7 +13929,8 @@ var mcp = new Server(
13925
13929
  "For DMs, respond directly.",
13926
13930
  "Messages with auto_followed=true are from threads you previously participated in.",
13927
13931
  "For auto-followed messages, use relevance judgment: only reply if you have something useful to add.",
13928
- "Do NOT reply to every auto-followed message \u2014 skip if the conversation has moved on, the message is directed at someone else, or your input would not add value."
13932
+ "Do NOT reply to every auto-followed message \u2014 skip if the conversation has moved on, the message is directed at someone else, or your input would not add value.",
13933
+ "To deliver a file (PDF, image, report), save it under your project dir and call slack.upload_file with path, channel, and thread_ts."
13929
13934
  ].join(" ")
13930
13935
  }
13931
13936
  );
@@ -13984,6 +13989,40 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
13984
13989
  },
13985
13990
  required: ["channel", "thread_ts"]
13986
13991
  }
13992
+ },
13993
+ {
13994
+ name: "slack.upload_file",
13995
+ description: "Upload a file from the agent project dir to a Slack channel or thread. Use this for PDFs, images, reports, and any binary deliverables the agent generates. Path must be inside the agent's project directory.",
13996
+ inputSchema: {
13997
+ type: "object",
13998
+ properties: {
13999
+ path: {
14000
+ type: "string",
14001
+ description: "Absolute path to the local file. Must be within the agent's project dir (~/.augmented/<codeName>/project/)."
14002
+ },
14003
+ channel: {
14004
+ type: "string",
14005
+ description: "Slack channel ID to share the file in (from the channel attribute in the <channel> tag)"
14006
+ },
14007
+ thread_ts: {
14008
+ type: "string",
14009
+ description: "Optional thread timestamp to attach the file to a specific thread"
14010
+ },
14011
+ title: {
14012
+ type: "string",
14013
+ description: "File title shown in Slack (defaults to filename)"
14014
+ },
14015
+ initial_comment: {
14016
+ type: "string",
14017
+ description: "Optional message posted alongside the file in the channel/thread"
14018
+ },
14019
+ filename: {
14020
+ type: "string",
14021
+ description: "Override the filename shown in Slack (defaults to the basename of path)"
14022
+ }
14023
+ },
14024
+ required: ["path", "channel"]
14025
+ }
13987
14026
  }
13988
14027
  ]
13989
14028
  }));
@@ -14106,6 +14145,115 @@ ${formatted}` : "Thread is empty or not found."
14106
14145
  };
14107
14146
  }
14108
14147
  }
14148
+ if (name === "slack.upload_file") {
14149
+ const {
14150
+ path,
14151
+ channel,
14152
+ thread_ts,
14153
+ title,
14154
+ initial_comment,
14155
+ filename
14156
+ } = args;
14157
+ if (!AGENT_CODE_NAME) {
14158
+ return {
14159
+ content: [{
14160
+ type: "text",
14161
+ text: "Upload refused: AGT_AGENT_CODE_NAME not set in the MCP server env. Reprovision the slack channel so the upload tool can scope paths."
14162
+ }],
14163
+ isError: true
14164
+ };
14165
+ }
14166
+ const allowedRoot = resolve(homedir(), ".augmented", AGENT_CODE_NAME, "project") + "/";
14167
+ const resolvedPath = resolve(path);
14168
+ if (!resolvedPath.startsWith(allowedRoot)) {
14169
+ return {
14170
+ content: [{
14171
+ type: "text",
14172
+ text: `Upload refused: ${resolvedPath} is outside the agent's project dir (${allowedRoot}). Move the file under that dir first.`
14173
+ }],
14174
+ isError: true
14175
+ };
14176
+ }
14177
+ let bytes;
14178
+ let size;
14179
+ try {
14180
+ const stat = statSync(resolvedPath);
14181
+ if (!stat.isFile()) {
14182
+ return {
14183
+ content: [{ type: "text", text: `Upload refused: ${resolvedPath} is not a regular file.` }],
14184
+ isError: true
14185
+ };
14186
+ }
14187
+ size = stat.size;
14188
+ bytes = readFileSync(resolvedPath);
14189
+ } catch (err) {
14190
+ return {
14191
+ content: [{ type: "text", text: `Failed to read file: ${err.message}` }],
14192
+ isError: true
14193
+ };
14194
+ }
14195
+ const fname = filename ?? basename(resolvedPath);
14196
+ const displayTitle = title ?? fname;
14197
+ try {
14198
+ const step1 = await fetch(
14199
+ `https://slack.com/api/files.getUploadURLExternal?filename=${encodeURIComponent(fname)}&length=${size}`,
14200
+ { headers: { Authorization: `Bearer ${BOT_TOKEN}` } }
14201
+ );
14202
+ const step1Data = await step1.json();
14203
+ if (!step1Data.ok || !step1Data.upload_url || !step1Data.file_id) {
14204
+ return {
14205
+ content: [{ type: "text", text: `Slack files.getUploadURLExternal failed: ${step1Data.error ?? "unknown"}` }],
14206
+ isError: true
14207
+ };
14208
+ }
14209
+ const step2 = await fetch(step1Data.upload_url, {
14210
+ method: "POST",
14211
+ body: bytes
14212
+ });
14213
+ if (!step2.ok) {
14214
+ return {
14215
+ content: [{ type: "text", text: `Slack upload PUT failed: ${step2.status} ${step2.statusText}` }],
14216
+ isError: true
14217
+ };
14218
+ }
14219
+ const step3Body = {
14220
+ files: [{ id: step1Data.file_id, title: displayTitle }],
14221
+ channel_id: channel
14222
+ };
14223
+ if (thread_ts) step3Body.thread_ts = thread_ts;
14224
+ if (initial_comment) step3Body.initial_comment = initial_comment;
14225
+ const step3 = await fetch("https://slack.com/api/files.completeUploadExternal", {
14226
+ method: "POST",
14227
+ headers: {
14228
+ "Content-Type": "application/json",
14229
+ Authorization: `Bearer ${BOT_TOKEN}`
14230
+ },
14231
+ body: JSON.stringify(step3Body)
14232
+ });
14233
+ const step3Data = await step3.json();
14234
+ if (!step3Data.ok) {
14235
+ return {
14236
+ content: [{ type: "text", text: `Slack files.completeUploadExternal failed: ${step3Data.error ?? "unknown"}` }],
14237
+ isError: true
14238
+ };
14239
+ }
14240
+ if (channel && thread_ts) {
14241
+ clearPendingMessage(channel, thread_ts);
14242
+ }
14243
+ const permalink = step3Data.files?.[0]?.permalink;
14244
+ return {
14245
+ content: [{
14246
+ type: "text",
14247
+ text: permalink ? `uploaded ${fname} (${size} bytes) \u2014 ${permalink}` : `uploaded ${fname} (${size} bytes)`
14248
+ }]
14249
+ };
14250
+ } catch (err) {
14251
+ return {
14252
+ content: [{ type: "text", text: `Failed: ${err.message}` }],
14253
+ isError: true
14254
+ };
14255
+ }
14256
+ }
14109
14257
  throw new Error(`Unknown tool: ${name}`);
14110
14258
  });
14111
14259
  await mcp.connect(new StdioServerTransport());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.10.28",
3
+ "version": "0.11.0",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {