@cremini/skillpack 1.2.3 → 1.2.4

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/README.md CHANGED
@@ -23,7 +23,7 @@ Skillpack is built for teams that want AI Agents to be deployable, trusted, and
23
23
  1. Download the example
24
24
  - [Garry Tan SkillPack](https://github.com/CreminiAI/skillpack-examples/releases/download/v.0.0.3/garry-tan.zip)
25
25
  - [Company Deep Research SkillPack](https://github.com/FinpeakInc/downloads/releases/download/v.0.0.1/Company-Deep-Research.zip)
26
- 2. Unzip it and Run ./start.sh on Mac OS, and double click start.bat on Windows (see below), the server starts and opens http://127.0.0.1:26313 in your browser
26
+ 2. Unzip it and Run ./start.sh on Mac OS, Or double click start.bat on Windows (see below), the server starts and opens http://127.0.0.1:26313 in your browser
27
27
 
28
28
  ```bash
29
29
  # macOS / Linux
@@ -110,76 +110,14 @@ If present, `AGENTS.md` and `SOUL.md` are read by SkillPack itself when a new ch
110
110
 
111
111
  ## Slack/Telegram Integrations
112
112
 
113
- **Slack Configuration**: requires Slack `App Token` and `Bot Token`<br>
114
- **Telegram configuration**: requires `Bot Token`
113
+ Talk to your Agents on Slack and Telegram
115
114
 
116
- ### Slack App Setup and how to get `App Token` and `Bot Token`
115
+ ### 5 mins to get Slack `App Token` and `Bot Token`
117
116
  https://skillpack.gitbook.io/skillpack-docs/getting-started/slack-integration
118
117
 
119
- 1. Create a new Slack app at https://api.slack.com/apps
120
- 2. Enable Socket Mode (Settings → Socket Mode → Enable)
121
- 3. Generate an App-Level Token with `connections:write` scope. This is **`App Token`**
122
- 4. Add Bot Token Scopes (OAuth & Permissions):
123
-
124
- - `app_mentions:read`
125
- - `channels:history`
126
- - `channels:read`
127
- - `chat:write`
128
- - `files:read`
129
- - `files:write`
130
- - `groups:history`
131
- - `groups:read`
132
- - `im:history`
133
- - `im:read`
134
- - `im:write`
135
- - `users:read`
136
-
137
- 5. Subscribe to Bot Events (Event Subscriptions):
138
-
139
- - `app_mention`
140
- - `message.channels`
141
- - `message.groups`
142
- - `message.im`
143
-
144
- 6. Enable Direct Messages (App Home):
145
- Go to App Home in the left sidebar
146
- Under Show Tabs, enable the Messages Tab
147
- Check Allow users to send Slash commands and messages from the messages tab
148
-
149
- 7. Install the app to your workspace. Get the Bot User OAuth Token. This is **`Bot Token`**
150
- 8. Add the app to any channels where you want the agent to operate (it'll only see messages in channels it's added to)
151
- 9. On the SkillPack buit-in UI http://127.0.0.1:26313, Tap "Connect to Chat App" button and Enter the **`Bot Token`** and **`App Token`**, Save
152
-
153
- ### Telegram Setup and how to get `Bot Token`
118
+ ### 1 min to get Telegram `Bot Token`
154
119
  https://skillpack.gitbook.io/skillpack-docs/getting-started/telegram-integration
155
120
 
156
- 1. **Open Telegram** and search for the official account **`@BotFather`** (it will have a blue verified checkmark).
157
- 2. **Start a chat** by tapping "Start" or sending the `/start` command.
158
- 3. **Send the command** `/newbot` to the BotFather.
159
- 4. **Follow the prompts** to choose a display name and a unique username for your bot. The username must end with the word "bot" (e.g., `MyHelperBot` or `My_Helper_bot`).
160
- 5. **Receive the token**. Once the bot is successfully created, the BotFather will provide you with a message containing your unique API token.
161
- The token will look like a long string of numbers and letters, formatted as `123456789:AABBCCddEeff.... `
162
- 6. On the SkillPack buit-in UI http://127.0.0.1:26313, Tap "Connect to Chat App" button and Enter the **`Bot Token`**, Save
163
-
164
- ### (Optional) Put tokens into data/config.json if you don't use Web UI
165
-
166
- Or Once you have telegram or slack tokens, you can also configure them in `data/config.json` (created at runtime, not included in the zip):
167
- The runtime supports **Slack** and **Telegram** in addition to the built-in web UI.
168
-
169
- ```json
170
- {
171
- "adapters": {
172
- "telegram": {
173
- "token": "123456:ABC-DEF..."
174
- },
175
- "slack": {
176
- "botToken": "xoxb-...",
177
- "appToken": "xapp-..."
178
- }
179
- }
180
- }
181
- ```
182
-
183
121
  ---
184
122
 
185
123
  ## Example Use Cases
package/dist/cli.js CHANGED
@@ -267,6 +267,62 @@ var init_attachment_utils = __esm({
267
267
  }
268
268
  });
269
269
 
270
+ // src/runtime/commands/index.ts
271
+ function resolveCommand(input) {
272
+ const key = input.split(/\s/)[0].toLowerCase();
273
+ return COMMAND_ALIASES[key] ?? null;
274
+ }
275
+ function getVisibleCommands() {
276
+ return COMMAND_REGISTRY.filter((cmd) => cmd.visibleInHelp);
277
+ }
278
+ function getTelegramBotCommands() {
279
+ return COMMAND_REGISTRY.filter((cmd) => cmd.visibleInHelp).map((cmd) => ({
280
+ command: cmd.command,
281
+ description: cmd.description
282
+ }));
283
+ }
284
+ var COMMAND_REGISTRY, COMMAND_ALIASES;
285
+ var init_commands = __esm({
286
+ "src/runtime/commands/index.ts"() {
287
+ "use strict";
288
+ COMMAND_REGISTRY = [
289
+ {
290
+ command: "help",
291
+ description: "Show available commands, skills, and usage tips",
292
+ visibleInHelp: true
293
+ },
294
+ {
295
+ command: "clear",
296
+ description: "Clear current session and start fresh",
297
+ visibleInHelp: true
298
+ },
299
+ {
300
+ command: "restart",
301
+ description: "Restart the server process",
302
+ visibleInHelp: true
303
+ },
304
+ {
305
+ command: "shutdown",
306
+ description: "Shut down the server process",
307
+ visibleInHelp: true
308
+ },
309
+ // "new" is a hidden alias for "clear" — not shown in /help
310
+ {
311
+ command: "new",
312
+ description: "Start a new session (alias for /clear)",
313
+ visibleInHelp: false
314
+ }
315
+ ];
316
+ COMMAND_ALIASES = {
317
+ "/help": "help",
318
+ "/clear": "clear",
319
+ "/new": "clear",
320
+ "/restart": "restart",
321
+ "/shutdown": "shutdown"
322
+ };
323
+ }
324
+ });
325
+
270
326
  // src/runtime/adapters/markdown.ts
271
327
  function unwrapMarkdownSourceBlocks(text) {
272
328
  return text.replace(
@@ -307,6 +363,7 @@ function formatSlackInline(text) {
307
363
  );
308
364
  formatted = formatted.replace(/\*\*([^*\n]+)\*\*/g, "*$1*");
309
365
  formatted = formatted.replace(/__([^_\n]+)__/g, "*$1*");
366
+ formatted = formatted.replace(/^(?:-|\*) /gm, "\u2022 ");
310
367
  return formatted;
311
368
  }
312
369
  function formatTelegramInline(text) {
@@ -386,19 +443,15 @@ var telegram_exports = {};
386
443
  __export(telegram_exports, {
387
444
  TelegramAdapter: () => TelegramAdapter
388
445
  });
389
- import fs11 from "fs";
446
+ import fs12 from "fs";
390
447
  import TelegramBot from "node-telegram-bot-api";
391
- var COMMANDS2, MAX_MESSAGE_LENGTH, ACK_REACTION, TelegramAdapter;
448
+ var MAX_MESSAGE_LENGTH, ACK_REACTION, TelegramAdapter;
392
449
  var init_telegram = __esm({
393
450
  "src/runtime/adapters/telegram.ts"() {
394
451
  "use strict";
395
452
  init_markdown();
396
453
  init_attachment_utils();
397
- COMMANDS2 = {
398
- "/clear": "clear",
399
- "/restart": "restart",
400
- "/shutdown": "shutdown"
401
- };
454
+ init_commands();
402
455
  MAX_MESSAGE_LENGTH = 4096;
403
456
  ACK_REACTION = {
404
457
  type: "emoji",
@@ -422,11 +475,7 @@ var init_telegram = __esm({
422
475
  console.error("[Telegram] Error handling message:", err);
423
476
  });
424
477
  });
425
- await this.bot.setMyCommands([
426
- { command: "clear", description: "Clear current session and start new" },
427
- { command: "restart", description: "Restart the server process" },
428
- { command: "shutdown", description: "Shut down the server process" }
429
- ]);
478
+ await this.bot.setMyCommands(getTelegramBotCommands());
430
479
  const me = await this.bot.getMe();
431
480
  console.log(`[TelegramAdapter] Started as @${me.username}`);
432
481
  }
@@ -466,7 +515,7 @@ var init_telegram = __esm({
466
515
  await this.tryAckReaction(chatId, messageId);
467
516
  if (text) {
468
517
  const commandKey = text.split(/\s/)[0].toLowerCase();
469
- const command = COMMANDS2[commandKey];
518
+ const command = this.resolveTelegramCommand(commandKey);
470
519
  if (command) {
471
520
  const result = await this.agent.handleCommand(command, channelId);
472
521
  await this.sendSafe(chatId, result.message || `/${command} executed.`);
@@ -521,6 +570,9 @@ var init_telegram = __esm({
521
570
  await this.sendFileSafe(chatId, file.filePath, file.caption);
522
571
  }
523
572
  }
573
+ resolveTelegramCommand(commandKey) {
574
+ return resolveCommand(commandKey);
575
+ }
524
576
  // -------------------------------------------------------------------------
525
577
  // Send helpers
526
578
  // -------------------------------------------------------------------------
@@ -697,7 +749,7 @@ var init_telegram = __esm({
697
749
  async sendFileSafe(chatId, filePath, caption) {
698
750
  if (!this.bot) return;
699
751
  try {
700
- if (!fs11.existsSync(filePath)) {
752
+ if (!fs12.existsSync(filePath)) {
701
753
  console.error(`[Telegram] File not found for sending: ${filePath}`);
702
754
  return;
703
755
  }
@@ -717,16 +769,18 @@ var slack_exports = {};
717
769
  __export(slack_exports, {
718
770
  SlackAdapter: () => SlackAdapter
719
771
  });
720
- import fs12 from "fs";
721
- import path11 from "path";
772
+ import fs13 from "fs";
773
+ import path12 from "path";
722
774
  import { App, LogLevel } from "@slack/bolt";
723
- var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, SlackAdapter;
775
+ var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, PROCESSING_MESSAGE, SlackAdapter;
724
776
  var init_slack = __esm({
725
777
  "src/runtime/adapters/slack.ts"() {
726
778
  "use strict";
727
779
  init_markdown();
728
780
  init_attachment_utils();
781
+ init_commands();
729
782
  INLINE_COMMANDS = {
783
+ "/help": "help",
730
784
  "/clear": "clear",
731
785
  "/restart": "restart",
732
786
  "/shutdown": "shutdown"
@@ -738,6 +792,7 @@ var init_slack = __esm({
738
792
  };
739
793
  MAX_MESSAGE_LENGTH2 = 3500;
740
794
  ACK_REACTION2 = "eyes";
795
+ PROCESSING_MESSAGE = "_Processing..._";
741
796
  SlackAdapter = class {
742
797
  name = "slack";
743
798
  app = null;
@@ -807,7 +862,7 @@ var init_slack = __esm({
807
862
  console.error("[Slack] Error handling mention:", err);
808
863
  }
809
864
  });
810
- for (const commandName of Object.keys(SLASH_COMMANDS)) {
865
+ for (const commandName of [...Object.keys(SLASH_COMMANDS), "/new"]) {
811
866
  app.command(commandName, async (args) => {
812
867
  try {
813
868
  await this.handleSlashCommand(args);
@@ -872,7 +927,7 @@ var init_slack = __esm({
872
927
  await this.sendSafe(
873
928
  client,
874
929
  route,
875
- "Mention me with a message, or use `/clear` to reset this thread."
930
+ "Mention me with a message, or use `/clear` or `/new` to reset this thread."
876
931
  );
877
932
  return;
878
933
  }
@@ -890,7 +945,7 @@ var init_slack = __esm({
890
945
  ack
891
946
  }) {
892
947
  const commandName = command?.command;
893
- const mapped = commandName ? SLASH_COMMANDS[commandName] : void 0;
948
+ const mapped = commandName ? this.resolveSlashCommand(commandName) : void 0;
894
949
  if (!this.agent || !mapped) {
895
950
  await this.safeAck(ack, "Unsupported slash command.");
896
951
  return;
@@ -916,6 +971,7 @@ var init_slack = __esm({
916
971
  let hasError = false;
917
972
  let errorMessage = "";
918
973
  const pendingFiles = [];
974
+ const placeholder = await this.sendPlaceholderMessage(client, route);
919
975
  const onEvent = (event) => {
920
976
  if (event.type === "text_delta") {
921
977
  finalText += event.delta;
@@ -943,13 +999,25 @@ var init_slack = __esm({
943
999
  errorMessage = this.getErrorMessage(err);
944
1000
  }
945
1001
  if (hasError) {
946
- await this.sendSafe(client, route, `\u274C Error: ${errorMessage}`);
1002
+ await this.sendOrUpdateSafe(
1003
+ client,
1004
+ route,
1005
+ `\u274C Error: ${errorMessage}`,
1006
+ placeholder
1007
+ );
947
1008
  return;
948
1009
  }
949
1010
  if (finalText.trim()) {
950
- await this.sendLongMessage(client, route, finalText);
1011
+ await this.sendLongMessage(client, route, finalText, placeholder);
951
1012
  } else if (pendingFiles.length === 0) {
952
- await this.sendSafe(client, route, "(No response generated)");
1013
+ await this.sendOrUpdateSafe(
1014
+ client,
1015
+ route,
1016
+ "(No response generated)",
1017
+ placeholder
1018
+ );
1019
+ } else if (placeholder) {
1020
+ await this.deleteMessageSafe(client, route, placeholder.ts);
953
1021
  }
954
1022
  for (const file of pendingFiles) {
955
1023
  await this.sendFileSafe(client, route, file.filePath, file.caption);
@@ -961,7 +1029,7 @@ var init_slack = __esm({
961
1029
  async tryHandleInlineCommand(text, channelId, client, route) {
962
1030
  if (!this.agent) return false;
963
1031
  const commandKey = text.split(/\s/)[0].toLowerCase();
964
- const command = INLINE_COMMANDS[commandKey];
1032
+ const command = this.resolveInlineCommand(commandKey);
965
1033
  if (!command) return false;
966
1034
  const result = await this.agent.handleCommand(command, channelId);
967
1035
  await this.sendSafe(
@@ -971,6 +1039,19 @@ var init_slack = __esm({
971
1039
  );
972
1040
  return true;
973
1041
  }
1042
+ resolveInlineCommand(commandKey) {
1043
+ const resolved = resolveCommand(commandKey);
1044
+ if (resolved) {
1045
+ return resolved;
1046
+ }
1047
+ return INLINE_COMMANDS[commandKey];
1048
+ }
1049
+ resolveSlashCommand(commandName) {
1050
+ if (commandName === "/new") {
1051
+ return "clear";
1052
+ }
1053
+ return SLASH_COMMANDS[commandName];
1054
+ }
974
1055
  resolveSlashCommandTarget(payload, context) {
975
1056
  const teamId = this.getTeamId(payload, context);
976
1057
  const channel = payload?.channel_id;
@@ -988,7 +1069,7 @@ var init_slack = __esm({
988
1069
  );
989
1070
  if (!threadTs) {
990
1071
  return {
991
- message: "No active Skillpack thread found in this channel. Mention the bot first, or run the command inside the thread as `@bot /clear`."
1072
+ message: "No active Skillpack thread found in this channel. Mention the bot first, or run the command inside the thread as `@bot /clear` or `@bot /new`."
992
1073
  };
993
1074
  }
994
1075
  return {
@@ -1017,33 +1098,35 @@ var init_slack = __esm({
1017
1098
  return text.replace(mention, "");
1018
1099
  }
1019
1100
  splitMessage(text) {
1020
- if (text.length <= MAX_MESSAGE_LENGTH2) {
1101
+ if (this.isSlackMessageWithinLimit(text)) {
1021
1102
  return [text];
1022
1103
  }
1023
1104
  const chunks = [];
1024
1105
  let remaining = text;
1025
1106
  while (remaining.length > 0) {
1026
- if (remaining.length <= MAX_MESSAGE_LENGTH2) {
1107
+ if (this.isSlackMessageWithinLimit(remaining)) {
1027
1108
  chunks.push(remaining);
1028
1109
  break;
1029
1110
  }
1030
- let splitAt = remaining.lastIndexOf("\n\n", MAX_MESSAGE_LENGTH2);
1031
- if (splitAt < MAX_MESSAGE_LENGTH2 * 0.5) {
1032
- splitAt = remaining.lastIndexOf("\n", MAX_MESSAGE_LENGTH2);
1033
- }
1034
- if (splitAt < MAX_MESSAGE_LENGTH2 * 0.3) {
1035
- splitAt = remaining.lastIndexOf(" ", MAX_MESSAGE_LENGTH2);
1036
- }
1037
- if (splitAt < 1) {
1038
- splitAt = MAX_MESSAGE_LENGTH2;
1039
- }
1111
+ let splitAt = this.findSlackSafeSplitPoint(remaining);
1040
1112
  chunks.push(remaining.slice(0, splitAt));
1041
1113
  remaining = remaining.slice(splitAt).trimStart();
1042
1114
  }
1043
1115
  return chunks;
1044
1116
  }
1045
- async sendLongMessage(client, route, text) {
1046
- for (const chunk of this.splitMessage(text)) {
1117
+ async sendLongMessage(client, route, text, placeholder) {
1118
+ const chunks = this.splitMessage(text);
1119
+ if (chunks.length === 0) {
1120
+ return;
1121
+ }
1122
+ if (placeholder) {
1123
+ await this.updateMessageSafe(client, route, placeholder.ts, chunks[0]);
1124
+ for (const chunk of chunks.slice(1)) {
1125
+ await this.sendSafe(client, route, chunk);
1126
+ }
1127
+ return;
1128
+ }
1129
+ for (const chunk of chunks) {
1047
1130
  await this.sendWithRetry(client, route, chunk);
1048
1131
  }
1049
1132
  }
@@ -1054,17 +1137,35 @@ var init_slack = __esm({
1054
1137
  console.error("[Slack] Failed to send message:", err);
1055
1138
  }
1056
1139
  }
1140
+ async sendOrUpdateSafe(client, route, text, placeholder) {
1141
+ if (placeholder) {
1142
+ await this.updateMessageSafe(client, route, placeholder.ts, text);
1143
+ return;
1144
+ }
1145
+ await this.sendSafe(client, route, text);
1146
+ }
1147
+ async sendPlaceholderMessage(client, route) {
1148
+ try {
1149
+ return await this.sendWithRetry(client, route, PROCESSING_MESSAGE);
1150
+ } catch (err) {
1151
+ console.error("[Slack] Failed to send placeholder message:", err);
1152
+ return null;
1153
+ }
1154
+ }
1057
1155
  async sendWithRetry(client, route, text, maxRetries = 3) {
1058
1156
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
1059
1157
  try {
1060
- await client.chat.postMessage({
1158
+ const response = await client.chat.postMessage({
1061
1159
  channel: route.channel,
1062
1160
  text: formatSlackMessage(text),
1063
1161
  mrkdwn: true,
1064
1162
  thread_ts: route.threadTs,
1065
1163
  reply_broadcast: false
1066
1164
  });
1067
- return;
1165
+ if (typeof response.ts !== "string") {
1166
+ throw new Error("Slack postMessage response missing ts");
1167
+ }
1168
+ return { ts: response.ts };
1068
1169
  } catch (err) {
1069
1170
  const retryAfter = this.getRetryAfterSeconds(err);
1070
1171
  if (retryAfter && attempt < maxRetries) {
@@ -1079,6 +1180,93 @@ var init_slack = __esm({
1079
1180
  throw err;
1080
1181
  }
1081
1182
  }
1183
+ throw new Error("Slack postMessage failed after retries");
1184
+ }
1185
+ async updateMessageSafe(client, route, ts, text) {
1186
+ try {
1187
+ await this.updateWithRetry(client, route, ts, text);
1188
+ } catch (err) {
1189
+ console.error("[Slack] Failed to update message:", err);
1190
+ await this.deleteMessageSafe(client, route, ts);
1191
+ await this.sendSafe(client, route, text);
1192
+ }
1193
+ }
1194
+ async updateWithRetry(client, route, ts, text, maxRetries = 3) {
1195
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1196
+ try {
1197
+ await client.chat.update({
1198
+ channel: route.channel,
1199
+ ts,
1200
+ text: formatSlackMessage(text),
1201
+ mrkdwn: true
1202
+ });
1203
+ return;
1204
+ } catch (err) {
1205
+ const retryAfter = this.getRetryAfterSeconds(err);
1206
+ if (retryAfter && attempt < maxRetries) {
1207
+ console.log(
1208
+ `[Slack] Rate limited while updating, retrying after ${retryAfter}s...`
1209
+ );
1210
+ await new Promise(
1211
+ (resolve) => setTimeout(resolve, retryAfter * 1e3)
1212
+ );
1213
+ continue;
1214
+ }
1215
+ throw err;
1216
+ }
1217
+ }
1218
+ }
1219
+ async deleteMessageSafe(client, route, ts) {
1220
+ try {
1221
+ await client.chat.delete({
1222
+ channel: route.channel,
1223
+ ts
1224
+ });
1225
+ } catch (err) {
1226
+ console.error("[Slack] Failed to delete placeholder message:", err);
1227
+ }
1228
+ }
1229
+ isSlackMessageWithinLimit(text) {
1230
+ return formatSlackMessage(text).length <= MAX_MESSAGE_LENGTH2;
1231
+ }
1232
+ findSlackSafeSplitPoint(text) {
1233
+ const preferredBreaks = ["\n\n", "\n", " "];
1234
+ const minSplit = Math.floor(MAX_MESSAGE_LENGTH2 * 0.3);
1235
+ for (const token of preferredBreaks) {
1236
+ const index = this.findBestSlackSplitBefore(text, token);
1237
+ if (index >= minSplit) {
1238
+ return index;
1239
+ }
1240
+ }
1241
+ let low = 1;
1242
+ let high = text.length;
1243
+ let best = Math.min(text.length, MAX_MESSAGE_LENGTH2);
1244
+ while (low <= high) {
1245
+ const mid = Math.floor((low + high) / 2);
1246
+ const candidate = text.slice(0, mid);
1247
+ if (this.isSlackMessageWithinLimit(candidate)) {
1248
+ best = mid;
1249
+ low = mid + 1;
1250
+ } else {
1251
+ high = mid - 1;
1252
+ }
1253
+ }
1254
+ return Math.max(1, best);
1255
+ }
1256
+ findBestSlackSplitBefore(text, token) {
1257
+ let fromIndex = Math.min(text.length, MAX_MESSAGE_LENGTH2);
1258
+ while (fromIndex > 0) {
1259
+ const index = text.lastIndexOf(token, fromIndex);
1260
+ if (index < 0) {
1261
+ return -1;
1262
+ }
1263
+ const splitAt = index + token.length;
1264
+ if (this.isSlackMessageWithinLimit(text.slice(0, splitAt))) {
1265
+ return splitAt;
1266
+ }
1267
+ fromIndex = index - 1;
1268
+ }
1269
+ return -1;
1082
1270
  }
1083
1271
  async tryAckReaction(client, event) {
1084
1272
  try {
@@ -1196,12 +1384,12 @@ var init_slack = __esm({
1196
1384
  */
1197
1385
  async sendFileSafe(client, route, filePath, caption) {
1198
1386
  try {
1199
- if (!fs12.existsSync(filePath)) {
1387
+ if (!fs13.existsSync(filePath)) {
1200
1388
  console.error(`[Slack] File not found for sending: ${filePath}`);
1201
1389
  return;
1202
1390
  }
1203
- const filename = path11.basename(filePath);
1204
- const fileContent = fs12.readFileSync(filePath);
1391
+ const filename = path12.basename(filePath);
1392
+ const fileContent = fs13.readFileSync(filePath);
1205
1393
  await client.files.uploadV2({
1206
1394
  channel_id: route.channel,
1207
1395
  thread_ts: route.threadTs,
@@ -2242,15 +2430,15 @@ async function interactiveCreate(workDir) {
2242
2430
  }
2243
2431
 
2244
2432
  // src/commands/run.ts
2245
- import path13 from "path";
2246
- import fs14 from "fs";
2433
+ import path14 from "path";
2434
+ import fs15 from "fs";
2247
2435
  import inquirer2 from "inquirer";
2248
2436
  import chalk4 from "chalk";
2249
2437
 
2250
2438
  // src/runtime/server.ts
2251
2439
  import express from "express";
2252
- import path12 from "path";
2253
- import fs13 from "fs";
2440
+ import path13 from "path";
2441
+ import fs14 from "fs";
2254
2442
  import { fileURLToPath as fileURLToPath2 } from "url";
2255
2443
  import { createServer } from "http";
2256
2444
  import { exec } from "child_process";
@@ -2258,8 +2446,8 @@ import { exec } from "child_process";
2258
2446
  // src/runtime/agent.ts
2259
2447
  init_config();
2260
2448
  init_attachment_utils();
2261
- import path8 from "path";
2262
- import fs8 from "fs";
2449
+ import path9 from "path";
2450
+ import fs9 from "fs";
2263
2451
  import { fileURLToPath } from "url";
2264
2452
  import {
2265
2453
  AuthStorage,
@@ -5115,6 +5303,67 @@ ${lines.join("\n")}`
5115
5303
  };
5116
5304
  }
5117
5305
 
5306
+ // src/runtime/commands/help-command.ts
5307
+ init_commands();
5308
+ import fs8 from "fs";
5309
+ import path8 from "path";
5310
+ function buildHelpMessage(rootDir) {
5311
+ const sections = [];
5312
+ const commands = getVisibleCommands();
5313
+ const commandLines = commands.map(
5314
+ (cmd) => `- \`/${cmd.command}\` \u2014 ${cmd.description}`
5315
+ );
5316
+ sections.push(`\u{1F4CB} **Available Commands**
5317
+
5318
+ ${commandLines.join("\n")}`);
5319
+ const configPath = path8.resolve(rootDir, "skillpack.json");
5320
+ const skills = readInstalledSkills(configPath);
5321
+ if (skills.length > 0) {
5322
+ const skillLines = skills.map(
5323
+ (skill) => `- **${skill.name}**${skill.description ? ` \u2014 ${skill.description}` : ""}`
5324
+ );
5325
+ sections.push(
5326
+ `\u{1F9E9} **Installed Skills** (${skills.length})
5327
+
5328
+ ${skillLines.join("\n")}`
5329
+ );
5330
+ } else {
5331
+ sections.push("\u{1F9E9} **Installed Skills**\nNo skills installed.");
5332
+ }
5333
+ sections.push(
5334
+ [
5335
+ "\u23F0 **Scheduled Tasks**",
5336
+ "",
5337
+ "You can set up recurring tasks using natural language. For example:",
5338
+ "",
5339
+ '- "Send me a daily market briefing every morning at 9 AM"',
5340
+ `- "Summarize this week's trading data every Friday at 6 PM"`,
5341
+ '- "Check for new announcements every 30 minutes"',
5342
+ "",
5343
+ "I will handle the cron scheduling automatically."
5344
+ ].join("\n")
5345
+ );
5346
+ return sections.join("\n\n");
5347
+ }
5348
+ function handleHelpCommand(rootDir) {
5349
+ return {
5350
+ success: true,
5351
+ message: buildHelpMessage(rootDir)
5352
+ };
5353
+ }
5354
+ function readInstalledSkills(configPath) {
5355
+ if (!fs8.existsSync(configPath)) {
5356
+ return [];
5357
+ }
5358
+ try {
5359
+ const raw = fs8.readFileSync(configPath, "utf-8");
5360
+ const config = JSON.parse(raw);
5361
+ return Array.isArray(config.skills) ? config.skills : [];
5362
+ } catch {
5363
+ return [];
5364
+ }
5365
+ }
5366
+
5118
5367
  // src/runtime/agent.ts
5119
5368
  var DEBUG = true;
5120
5369
  var log = (...args) => DEBUG && console.log(...args);
@@ -5127,24 +5376,24 @@ var BUILTIN_SKILL_CREATOR_TEMPLATE_DIR = fileURLToPath(
5127
5376
  var PACK_AGENTS_FILE = "AGENTS.md";
5128
5377
  var PACK_SOUL_FILE = "SOUL.md";
5129
5378
  function materializeBuiltinSkillCreator(rootDir, skillsPath) {
5130
- if (!fs8.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
5379
+ if (!fs9.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
5131
5380
  log(
5132
5381
  `[PackAgent] Built-in skill-creator template missing: ${BUILTIN_SKILL_CREATOR_TEMPLATE_DIR}`
5133
5382
  );
5134
5383
  return null;
5135
5384
  }
5136
- const packConfigPath = path8.resolve(rootDir, "skillpack.json");
5137
- const skillDir = path8.resolve(skillsPath, BUILTIN_SKILL_CREATOR_NAME);
5138
- const skillPath = path8.join(skillDir, "SKILL.md");
5385
+ const packConfigPath = path9.resolve(rootDir, "skillpack.json");
5386
+ const skillDir = path9.resolve(skillsPath, BUILTIN_SKILL_CREATOR_NAME);
5387
+ const skillPath = path9.join(skillDir, "SKILL.md");
5139
5388
  const renderTemplate = (content) => content.replaceAll("{{SKILLS_PATH}}", skillsPath).replaceAll("{{PACK_CONFIG_PATH}}", packConfigPath);
5140
5389
  const copyDir = (srcDir, destDir) => {
5141
- fs8.mkdirSync(destDir, { recursive: true });
5142
- for (const entry of fs8.readdirSync(srcDir, { withFileTypes: true })) {
5390
+ fs9.mkdirSync(destDir, { recursive: true });
5391
+ for (const entry of fs9.readdirSync(srcDir, { withFileTypes: true })) {
5143
5392
  if (entry.name === ".DS_Store") {
5144
5393
  continue;
5145
5394
  }
5146
- const srcPath = path8.join(srcDir, entry.name);
5147
- const destPath = path8.join(destDir, entry.name);
5395
+ const srcPath = path9.join(srcDir, entry.name);
5396
+ const destPath = path9.join(destDir, entry.name);
5148
5397
  if (entry.isDirectory()) {
5149
5398
  copyDir(srcPath, destPath);
5150
5399
  continue;
@@ -5153,17 +5402,17 @@ function materializeBuiltinSkillCreator(rootDir, skillsPath) {
5153
5402
  continue;
5154
5403
  }
5155
5404
  if (entry.name.endsWith(".md") || entry.name.endsWith(".py")) {
5156
- const content = fs8.readFileSync(srcPath, "utf-8");
5157
- fs8.writeFileSync(destPath, renderTemplate(content), "utf-8");
5405
+ const content = fs9.readFileSync(srcPath, "utf-8");
5406
+ fs9.writeFileSync(destPath, renderTemplate(content), "utf-8");
5158
5407
  continue;
5159
5408
  }
5160
- fs8.copyFileSync(srcPath, destPath);
5409
+ fs9.copyFileSync(srcPath, destPath);
5161
5410
  }
5162
5411
  };
5163
- if (!fs8.existsSync(skillDir)) {
5412
+ if (!fs9.existsSync(skillDir)) {
5164
5413
  copyDir(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR, skillDir);
5165
5414
  }
5166
- if (!fs8.existsSync(skillPath)) {
5415
+ if (!fs9.existsSync(skillPath)) {
5167
5416
  log(
5168
5417
  `[PackAgent] Materialized built-in skill-creator but SKILL.md is missing: ${skillPath}`
5169
5418
  );
@@ -5191,11 +5440,11 @@ function overrideBuiltinSkillCreator(base, materializedSkill) {
5191
5440
  };
5192
5441
  }
5193
5442
  function readOptionalPackPromptFile(filePath) {
5194
- if (!fs8.existsSync(filePath)) {
5443
+ if (!fs9.existsSync(filePath)) {
5195
5444
  return void 0;
5196
5445
  }
5197
5446
  try {
5198
- const content = fs8.readFileSync(filePath, "utf-8").trim();
5447
+ const content = fs9.readFileSync(filePath, "utf-8").trim();
5199
5448
  return content.length > 0 ? content : void 0;
5200
5449
  } catch (error) {
5201
5450
  console.warn(`[PackAgent] Warning: Could not read ${filePath}:`, error);
@@ -5203,8 +5452,8 @@ function readOptionalPackPromptFile(filePath) {
5203
5452
  }
5204
5453
  }
5205
5454
  function buildPackPromptBlock(rootDir) {
5206
- const agentsPath = path8.resolve(rootDir, PACK_AGENTS_FILE);
5207
- const soulPath = path8.resolve(rootDir, PACK_SOUL_FILE);
5455
+ const agentsPath = path9.resolve(rootDir, PACK_AGENTS_FILE);
5456
+ const soulPath = path9.resolve(rootDir, PACK_SOUL_FILE);
5208
5457
  const agentsContent = readOptionalPackPromptFile(agentsPath);
5209
5458
  const soulContent = readOptionalPackPromptFile(soulPath);
5210
5459
  if (!agentsContent && !soulContent) {
@@ -5270,7 +5519,7 @@ var PackAgent = class {
5270
5519
  authStorage;
5271
5520
  constructor(options) {
5272
5521
  this.options = options;
5273
- const configPath = path8.resolve(options.rootDir, "data", "config.json");
5522
+ const configPath = path9.resolve(options.rootDir, "data", "config.json");
5274
5523
  const backend = new ConfigFileAuthBackend(configPath);
5275
5524
  this.authStorage = AuthStorage.fromStorage(backend);
5276
5525
  const providerMeta = SUPPORTED_PROVIDERS[options.provider];
@@ -5321,24 +5570,24 @@ var PackAgent = class {
5321
5570
  if (resolvedModel && baseUrl) {
5322
5571
  log(`[PackAgent] Overriding ${provider}/${modelId} baseUrl -> ${baseUrl}`);
5323
5572
  }
5324
- const sessionDir = path8.resolve(
5573
+ const sessionDir = path9.resolve(
5325
5574
  rootDir,
5326
5575
  "data",
5327
5576
  "sessions",
5328
5577
  channelId
5329
5578
  );
5330
- fs8.mkdirSync(sessionDir, { recursive: true });
5579
+ fs9.mkdirSync(sessionDir, { recursive: true });
5331
5580
  const sessionManager = SessionManager.continueRecent(rootDir, sessionDir);
5332
5581
  log(`[PackAgent] Session dir: ${sessionDir}`);
5333
- const workspaceDir = path8.resolve(
5582
+ const workspaceDir = path9.resolve(
5334
5583
  rootDir,
5335
5584
  "data",
5336
5585
  "workspaces",
5337
5586
  channelId
5338
5587
  );
5339
- fs8.mkdirSync(workspaceDir, { recursive: true });
5588
+ fs9.mkdirSync(workspaceDir, { recursive: true });
5340
5589
  log(`[PackAgent] Workspace dir: ${workspaceDir}`);
5341
- const skillsPath = path8.resolve(rootDir, "skills");
5590
+ const skillsPath = path9.resolve(rootDir, "skills");
5342
5591
  log(`[PackAgent] Loading skills from: ${skillsPath}`);
5343
5592
  const materializedSkillCreator = materializeBuiltinSkillCreator(
5344
5593
  rootDir,
@@ -5529,6 +5778,8 @@ ${text}`;
5529
5778
  }
5530
5779
  async handleCommand(command, channelId) {
5531
5780
  switch (command) {
5781
+ case "help":
5782
+ return handleHelpCommand(this.options.rootDir);
5532
5783
  case "new":
5533
5784
  case "clear": {
5534
5785
  const cs = this.channels.get(channelId);
@@ -5537,9 +5788,9 @@ ${text}`;
5537
5788
  this.channels.delete(channelId);
5538
5789
  }
5539
5790
  const { rootDir } = this.options;
5540
- const sessionDir = path8.resolve(rootDir, "data", "sessions", channelId);
5541
- if (fs8.existsSync(sessionDir)) {
5542
- fs8.rmSync(sessionDir, { recursive: true, force: true });
5791
+ const sessionDir = path9.resolve(rootDir, "data", "sessions", channelId);
5792
+ if (fs9.existsSync(sessionDir)) {
5793
+ fs9.rmSync(sessionDir, { recursive: true, force: true });
5543
5794
  log(`[PackAgent] Cleared session dir: ${sessionDir}`);
5544
5795
  }
5545
5796
  return {
@@ -5588,22 +5839,20 @@ ${text}`;
5588
5839
 
5589
5840
  // src/runtime/adapters/web.ts
5590
5841
  init_config();
5591
- import fs9 from "fs";
5592
- import path9 from "path";
5842
+ init_commands();
5843
+ import fs10 from "fs";
5844
+ import path10 from "path";
5593
5845
  import { WebSocketServer } from "ws";
5594
5846
  function getPackConfig(rootDir) {
5595
- const raw = fs9.readFileSync(path9.join(rootDir, "skillpack.json"), "utf-8");
5847
+ const raw = fs10.readFileSync(path10.join(rootDir, "skillpack.json"), "utf-8");
5596
5848
  return JSON.parse(raw);
5597
5849
  }
5598
- var COMMANDS = {
5599
- "/new": "new",
5600
- "/clear": "clear",
5601
- "/restart": "restart",
5602
- "/shutdown": "shutdown"
5603
- };
5604
5850
  function parseCommand(text) {
5605
- const trimmed = text.trim().toLowerCase();
5606
- return COMMANDS[trimmed] ?? null;
5851
+ return resolveCommand(text.trim().toLowerCase());
5852
+ }
5853
+ function sendWsEvent(ws, event) {
5854
+ if (ws.readyState !== ws.OPEN) return;
5855
+ ws.send(JSON.stringify(event));
5607
5856
  }
5608
5857
  function getRuntimeConfigSignature(config) {
5609
5858
  return JSON.stringify({
@@ -5744,23 +5993,23 @@ var WebAdapter = class {
5744
5993
  res.status(400).json({ error: "Missing 'path' query parameter" });
5745
5994
  return;
5746
5995
  }
5747
- const resolvedPath = path9.resolve(filePath);
5748
- const dataDir = path9.resolve(rootDir, "data");
5996
+ const resolvedPath = path10.resolve(filePath);
5997
+ const dataDir = path10.resolve(rootDir, "data");
5749
5998
  if (!resolvedPath.startsWith(dataDir)) {
5750
5999
  res.status(403).json({ error: "Access denied" });
5751
6000
  return;
5752
6001
  }
5753
- if (!fs9.existsSync(resolvedPath)) {
6002
+ if (!fs10.existsSync(resolvedPath)) {
5754
6003
  res.status(404).json({ error: "File not found" });
5755
6004
  return;
5756
6005
  }
5757
- const filename = path9.basename(resolvedPath);
6006
+ const filename = path10.basename(resolvedPath);
5758
6007
  res.setHeader("Content-Type", "application/octet-stream");
5759
6008
  res.setHeader(
5760
6009
  "Content-Disposition",
5761
6010
  `attachment; filename="${filename}"`
5762
6011
  );
5763
- fs9.createReadStream(resolvedPath).pipe(res);
6012
+ fs10.createReadStream(resolvedPath).pipe(res);
5764
6013
  });
5765
6014
  const getScheduler = () => {
5766
6015
  const schedulerAdapter = ctx.adapterMap?.get("scheduler");
@@ -5881,21 +6130,14 @@ var WebAdapter = class {
5881
6130
  const command = parseCommand(text);
5882
6131
  if (command) {
5883
6132
  const result2 = await agent.handleCommand(command, channelId);
5884
- ws.send(
5885
- JSON.stringify({
5886
- type: "command_result",
5887
- command,
5888
- ...result2
5889
- })
5890
- );
5891
- if (command === "clear" || command === "new") {
5892
- ws.send(JSON.stringify({ done: true }));
6133
+ if (result2.message) {
6134
+ sendWsEvent(ws, { type: "text_delta", delta: result2.message });
5893
6135
  }
6136
+ ws.send(JSON.stringify({ done: true }));
5894
6137
  return;
5895
6138
  }
5896
6139
  const onEvent = (event) => {
5897
- if (ws.readyState !== ws.OPEN) return;
5898
- ws.send(JSON.stringify(event));
6140
+ sendWsEvent(ws, event);
5899
6141
  };
5900
6142
  const result = await agent.handleMessage("web", channelId, text, onEvent);
5901
6143
  if (result.errorMessage) {
@@ -5989,28 +6231,28 @@ var Lifecycle = class {
5989
6231
 
5990
6232
  // src/runtime/registry.ts
5991
6233
  import crypto from "crypto";
5992
- import fs10 from "fs";
6234
+ import fs11 from "fs";
5993
6235
  import os from "os";
5994
- import path10 from "path";
5995
- var SKILLPACK_HOME = path10.join(os.homedir(), ".skillpack");
5996
- var LEGACY_REGISTRY_FILE = path10.join(SKILLPACK_HOME, "registry.json");
5997
- var REGISTRY_DIR = path10.join(SKILLPACK_HOME, "registry.d");
6236
+ import path11 from "path";
6237
+ var SKILLPACK_HOME = path11.join(os.homedir(), ".skillpack");
6238
+ var LEGACY_REGISTRY_FILE = path11.join(SKILLPACK_HOME, "registry.json");
6239
+ var REGISTRY_DIR = path11.join(SKILLPACK_HOME, "registry.d");
5998
6240
  var migrationChecked = false;
5999
6241
  function ensureHomeDir() {
6000
- if (!fs10.existsSync(SKILLPACK_HOME)) {
6001
- fs10.mkdirSync(SKILLPACK_HOME, { recursive: true });
6242
+ if (!fs11.existsSync(SKILLPACK_HOME)) {
6243
+ fs11.mkdirSync(SKILLPACK_HOME, { recursive: true });
6002
6244
  }
6003
6245
  }
6004
6246
  function ensureRegistryDir() {
6005
6247
  ensureHomeDir();
6006
- if (!fs10.existsSync(REGISTRY_DIR)) {
6007
- fs10.mkdirSync(REGISTRY_DIR, { recursive: true });
6248
+ if (!fs11.existsSync(REGISTRY_DIR)) {
6249
+ fs11.mkdirSync(REGISTRY_DIR, { recursive: true });
6008
6250
  }
6009
6251
  }
6010
6252
  function canonicalizeDir(dir) {
6011
- const resolved = path10.resolve(dir);
6253
+ const resolved = path11.resolve(dir);
6012
6254
  try {
6013
- return fs10.realpathSync(resolved);
6255
+ return fs11.realpathSync(resolved);
6014
6256
  } catch {
6015
6257
  return resolved;
6016
6258
  }
@@ -6019,7 +6261,7 @@ function hashDir(dir) {
6019
6261
  return crypto.createHash("md5").update(canonicalizeDir(dir)).digest("hex");
6020
6262
  }
6021
6263
  function getEntryPathForCanonicalDir(dir) {
6022
- return path10.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
6264
+ return path11.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
6023
6265
  }
6024
6266
  function getEntryPath(dir) {
6025
6267
  ensureRegistryReady();
@@ -6027,11 +6269,11 @@ function getEntryPath(dir) {
6027
6269
  }
6028
6270
  function listEntryFiles() {
6029
6271
  ensureRegistryReady();
6030
- return fs10.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path10.join(REGISTRY_DIR, file));
6272
+ return fs11.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path11.join(REGISTRY_DIR, file));
6031
6273
  }
6032
6274
  function readEntryFile(filePath) {
6033
6275
  try {
6034
- const raw = fs10.readFileSync(filePath, "utf-8");
6276
+ const raw = fs11.readFileSync(filePath, "utf-8");
6035
6277
  const data = JSON.parse(raw);
6036
6278
  if (typeof data?.dir !== "string" || typeof data?.name !== "string" || typeof data?.version !== "string" || typeof data?.port !== "number" || typeof data?.pid !== "number" && data?.pid !== null || data?.status !== "running" && data?.status !== "stopped") {
6037
6279
  return null;
@@ -6064,8 +6306,8 @@ function writeEntryFile(entry) {
6064
6306
  };
6065
6307
  const entryPath = getEntryPathForCanonicalDir(normalized.dir);
6066
6308
  const tmpPath = createTmpPath(entryPath);
6067
- fs10.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
6068
- fs10.renameSync(tmpPath, entryPath);
6309
+ fs11.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
6310
+ fs11.renameSync(tmpPath, entryPath);
6069
6311
  }
6070
6312
  function migrateLegacyRegistryIfNeeded() {
6071
6313
  if (migrationChecked) {
@@ -6073,14 +6315,14 @@ function migrateLegacyRegistryIfNeeded() {
6073
6315
  }
6074
6316
  migrationChecked = true;
6075
6317
  ensureRegistryDir();
6076
- if (!fs10.existsSync(LEGACY_REGISTRY_FILE)) {
6318
+ if (!fs11.existsSync(LEGACY_REGISTRY_FILE)) {
6077
6319
  return;
6078
6320
  }
6079
6321
  if (listEntryFiles().length > 0) {
6080
6322
  return;
6081
6323
  }
6082
6324
  try {
6083
- const raw = fs10.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
6325
+ const raw = fs11.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
6084
6326
  const data = JSON.parse(raw);
6085
6327
  const packs = Array.isArray(data?.packs) ? data.packs : [];
6086
6328
  for (const pack of packs) {
@@ -6093,7 +6335,7 @@ function migrateLegacyRegistryIfNeeded() {
6093
6335
  } catch {
6094
6336
  }
6095
6337
  }
6096
- fs10.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
6338
+ fs11.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
6097
6339
  } catch (err) {
6098
6340
  console.warn(" [Registry] Failed to migrate legacy registry.json:", err);
6099
6341
  }
@@ -6146,7 +6388,7 @@ function deregister(dir, pid) {
6146
6388
  }
6147
6389
 
6148
6390
  // src/runtime/server.ts
6149
- var __dirname = path12.dirname(fileURLToPath2(import.meta.url));
6391
+ var __dirname = path13.dirname(fileURLToPath2(import.meta.url));
6150
6392
  async function startServer(options) {
6151
6393
  const {
6152
6394
  rootDir,
@@ -6161,8 +6403,8 @@ async function startServer(options) {
6161
6403
  const packConfig = loadConfig(canonicalRootDir);
6162
6404
  const baseUrl = dataConfig.baseUrl?.trim() || void 0;
6163
6405
  const modelId = SUPPORTED_PROVIDERS[provider]?.defaultModelId ?? SUPPORTED_PROVIDERS.openai.defaultModelId;
6164
- const packageRoot = path12.resolve(__dirname, "..");
6165
- const webDir = fs13.existsSync(path12.join(rootDir, "web")) ? path12.join(rootDir, "web") : path12.join(packageRoot, "web");
6406
+ const packageRoot = path13.resolve(__dirname, "..");
6407
+ const webDir = fs14.existsSync(path13.join(rootDir, "web")) ? path13.join(rootDir, "web") : path13.join(packageRoot, "web");
6166
6408
  const app = express();
6167
6409
  app.use(express.json());
6168
6410
  app.use(express.static(webDir));
@@ -6330,23 +6572,23 @@ function findMissingSkills(workDir, config) {
6330
6572
  });
6331
6573
  }
6332
6574
  function copyStartTemplates2(workDir) {
6333
- const templateDir = path13.resolve(
6575
+ const templateDir = path14.resolve(
6334
6576
  new URL("../templates", import.meta.url).pathname
6335
6577
  );
6336
6578
  for (const file of ["start.sh", "start.bat"]) {
6337
- const src = path13.join(templateDir, file);
6338
- const dest = path13.join(workDir, file);
6339
- if (fs14.existsSync(src)) {
6340
- fs14.copyFileSync(src, dest);
6579
+ const src = path14.join(templateDir, file);
6580
+ const dest = path14.join(workDir, file);
6581
+ if (fs15.existsSync(src)) {
6582
+ fs15.copyFileSync(src, dest);
6341
6583
  if (file === "start.sh") {
6342
- fs14.chmodSync(dest, 493);
6584
+ fs15.chmodSync(dest, 493);
6343
6585
  }
6344
6586
  }
6345
6587
  }
6346
6588
  }
6347
6589
  async function runCommand(directory) {
6348
- const workDir = directory ? path13.resolve(directory) : process.cwd();
6349
- fs14.mkdirSync(workDir, { recursive: true });
6590
+ const workDir = directory ? path14.resolve(directory) : process.cwd();
6591
+ fs15.mkdirSync(workDir, { recursive: true });
6350
6592
  if (!configExists(workDir)) {
6351
6593
  console.log(chalk4.blue("\n No skillpack.json found. Let's set one up.\n"));
6352
6594
  const { name, description } = await inquirer2.prompt([
@@ -6391,9 +6633,9 @@ async function runCommand(directory) {
6391
6633
  }
6392
6634
 
6393
6635
  // src/cli.ts
6394
- import fs15 from "fs";
6636
+ import fs16 from "fs";
6395
6637
  var packageJson = JSON.parse(
6396
- fs15.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
6638
+ fs16.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
6397
6639
  );
6398
6640
  var program = new Command();
6399
6641
  program.name("skillpack").description("Assemble, package, and run Agent Skills packs").version(packageJson.version);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cremini/skillpack",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "Pack AI Skills into Local Agents",
5
5
  "type": "module",
6
6
  "repository": {
@@ -65,4 +65,4 @@
65
65
  "tsup": "^8.5.1",
66
66
  "typescript": "^5.9.3"
67
67
  }
68
- }
68
+ }
package/web/js/chat.js CHANGED
@@ -282,21 +282,6 @@ function handleAgentEvent(event) {
282
282
  hideLoadingIndicator();
283
283
  }
284
284
 
285
- // Handle bot command results injected by the backend WebSocket response
286
- if (event.type === "command_result") {
287
- const textBlock = getOrCreateTextBlock();
288
- let resText = `Command \`${event.command}\` succeeded.`;
289
- if (event.errorMessage) {
290
- resText = `Command \`${event.command}\` failed: ${event.errorMessage}`;
291
- } else if (event.result) {
292
- resText = `Command \`${event.command}\` result:\n\n${event.result}`;
293
- }
294
- textBlock.dataset.mdContent += resText;
295
- textBlock.innerHTML = renderMarkdown(textBlock.dataset.mdContent);
296
- scrollToBottom();
297
- return;
298
- }
299
-
300
285
  switch (event.type) {
301
286
  case "agent_start":
302
287
  case "message_start":