@cremini/skillpack 1.2.4 → 1.2.5-beta.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.
Files changed (2) hide show
  1. package/dist/cli.js +502 -60
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -323,6 +323,20 @@ var init_commands = __esm({
323
323
  }
324
324
  });
325
325
 
326
+ // src/runtime/adapters/types.ts
327
+ var types_exports = {};
328
+ __export(types_exports, {
329
+ isMessageSender: () => isMessageSender
330
+ });
331
+ function isMessageSender(adapter) {
332
+ return typeof adapter.sendMessage === "function";
333
+ }
334
+ var init_types = __esm({
335
+ "src/runtime/adapters/types.ts"() {
336
+ "use strict";
337
+ }
338
+ });
339
+
326
340
  // src/runtime/adapters/markdown.ts
327
341
  function unwrapMarkdownSourceBlocks(text) {
328
342
  return text.replace(
@@ -443,7 +457,7 @@ var telegram_exports = {};
443
457
  __export(telegram_exports, {
444
458
  TelegramAdapter: () => TelegramAdapter
445
459
  });
446
- import fs12 from "fs";
460
+ import fs13 from "fs";
447
461
  import TelegramBot from "node-telegram-bot-api";
448
462
  var MAX_MESSAGE_LENGTH, ACK_REACTION, TelegramAdapter;
449
463
  var init_telegram = __esm({
@@ -463,12 +477,14 @@ var init_telegram = __esm({
463
477
  agent = null;
464
478
  options;
465
479
  rootDir = "";
480
+ ipcBroadcaster = null;
466
481
  constructor(options) {
467
482
  this.options = options;
468
483
  }
469
484
  async start(ctx) {
470
485
  this.agent = ctx.agent;
471
486
  this.rootDir = ctx.rootDir;
487
+ this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
472
488
  this.bot = new TelegramBot(this.options.token, { polling: true });
473
489
  this.bot.on("message", (msg) => {
474
490
  this.handleTelegramMessage(msg).catch((err) => {
@@ -510,6 +526,15 @@ var init_telegram = __esm({
510
526
  const messageId = msg.message_id;
511
527
  const text = (msg.text || msg.caption || "").trim();
512
528
  const channelId = `telegram-${chatId}`;
529
+ this.ipcBroadcaster?.broadcastInbound(
530
+ channelId,
531
+ "telegram",
532
+ {
533
+ id: String(msg.from?.id || ""),
534
+ username: msg.from?.username || msg.from?.first_name || ""
535
+ },
536
+ text
537
+ );
513
538
  const attachments = await this.extractAttachments(msg, channelId);
514
539
  if (!text && attachments.length === 0) return;
515
540
  await this.tryAckReaction(chatId, messageId);
@@ -539,6 +564,7 @@ var init_telegram = __esm({
539
564
  });
540
565
  break;
541
566
  }
567
+ this.ipcBroadcaster?.broadcastAgentEvent(channelId, event);
542
568
  };
543
569
  try {
544
570
  const userText = text || "(User sent an attachment)";
@@ -749,7 +775,7 @@ var init_telegram = __esm({
749
775
  async sendFileSafe(chatId, filePath, caption) {
750
776
  if (!this.bot) return;
751
777
  try {
752
- if (!fs12.existsSync(filePath)) {
778
+ if (!fs13.existsSync(filePath)) {
753
779
  console.error(`[Telegram] File not found for sending: ${filePath}`);
754
780
  return;
755
781
  }
@@ -769,8 +795,8 @@ var slack_exports = {};
769
795
  __export(slack_exports, {
770
796
  SlackAdapter: () => SlackAdapter
771
797
  });
772
- import fs13 from "fs";
773
- import path12 from "path";
798
+ import fs14 from "fs";
799
+ import path13 from "path";
774
800
  import { App, LogLevel } from "@slack/bolt";
775
801
  var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, PROCESSING_MESSAGE, SlackAdapter;
776
802
  var init_slack = __esm({
@@ -801,12 +827,14 @@ var init_slack = __esm({
801
827
  botUserId = null;
802
828
  lastThreadByChannel = /* @__PURE__ */ new Map();
803
829
  rootDir = "";
830
+ ipcBroadcaster = null;
804
831
  constructor(options) {
805
832
  this.options = options;
806
833
  }
807
834
  async start(ctx) {
808
835
  this.agent = ctx.agent;
809
836
  this.rootDir = ctx.rootDir;
837
+ this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
810
838
  this.app = new App({
811
839
  token: this.options.botToken,
812
840
  appToken: this.options.appToken,
@@ -892,6 +920,15 @@ var init_slack = __esm({
892
920
  const teamId = this.getTeamId(body, context);
893
921
  const channelId = `slack-dm-${teamId}-${event.channel}`;
894
922
  const route = { channel: event.channel };
923
+ this.ipcBroadcaster?.broadcastInbound(
924
+ channelId,
925
+ "slack",
926
+ {
927
+ id: String(event.user || ""),
928
+ username: String(event.user || "")
929
+ },
930
+ text
931
+ );
895
932
  const attachments = await this.extractSlackFiles(event, channelId, client);
896
933
  if (!text && attachments.length === 0) return;
897
934
  await this.tryAckReaction(client, event);
@@ -922,6 +959,15 @@ var init_slack = __esm({
922
959
  threadTs
923
960
  );
924
961
  const text = this.stripBotMention(event.text || "").trim();
962
+ this.ipcBroadcaster?.broadcastInbound(
963
+ channelId,
964
+ "slack",
965
+ {
966
+ id: String(event.user || ""),
967
+ username: String(event.user || "")
968
+ },
969
+ text
970
+ );
925
971
  const attachments = await this.extractSlackFiles(event, channelId, client);
926
972
  if (!text && attachments.length === 0) {
927
973
  await this.sendSafe(
@@ -981,6 +1027,7 @@ var init_slack = __esm({
981
1027
  caption: event.caption
982
1028
  });
983
1029
  }
1030
+ this.ipcBroadcaster?.broadcastAgentEvent(channelId, event);
984
1031
  };
985
1032
  try {
986
1033
  const result = await this.agent.handleMessage(
@@ -1384,12 +1431,12 @@ var init_slack = __esm({
1384
1431
  */
1385
1432
  async sendFileSafe(client, route, filePath, caption) {
1386
1433
  try {
1387
- if (!fs13.existsSync(filePath)) {
1434
+ if (!fs14.existsSync(filePath)) {
1388
1435
  console.error(`[Slack] File not found for sending: ${filePath}`);
1389
1436
  return;
1390
1437
  }
1391
- const filename = path12.basename(filePath);
1392
- const fileContent = fs13.readFileSync(filePath);
1438
+ const filename = path13.basename(filePath);
1439
+ const fileContent = fs14.readFileSync(filePath);
1393
1440
  await client.files.uploadV2({
1394
1441
  channel_id: route.channel,
1395
1442
  thread_ts: route.threadTs,
@@ -1406,20 +1453,6 @@ var init_slack = __esm({
1406
1453
  }
1407
1454
  });
1408
1455
 
1409
- // src/runtime/adapters/types.ts
1410
- var types_exports = {};
1411
- __export(types_exports, {
1412
- isMessageSender: () => isMessageSender
1413
- });
1414
- function isMessageSender(adapter) {
1415
- return typeof adapter.sendMessage === "function";
1416
- }
1417
- var init_types = __esm({
1418
- "src/runtime/adapters/types.ts"() {
1419
- "use strict";
1420
- }
1421
- });
1422
-
1423
1456
  // src/runtime/adapters/scheduler.ts
1424
1457
  var scheduler_exports = {};
1425
1458
  __export(scheduler_exports, {
@@ -2430,15 +2463,15 @@ async function interactiveCreate(workDir) {
2430
2463
  }
2431
2464
 
2432
2465
  // src/commands/run.ts
2433
- import path14 from "path";
2434
- import fs15 from "fs";
2466
+ import path15 from "path";
2467
+ import fs16 from "fs";
2435
2468
  import inquirer2 from "inquirer";
2436
2469
  import chalk4 from "chalk";
2437
2470
 
2438
2471
  // src/runtime/server.ts
2439
2472
  import express from "express";
2440
- import path13 from "path";
2441
- import fs14 from "fs";
2473
+ import path14 from "path";
2474
+ import fs15 from "fs";
2442
2475
  import { fileURLToPath as fileURLToPath2 } from "url";
2443
2476
  import { createServer } from "http";
2444
2477
  import { exec } from "child_process";
@@ -5835,6 +5868,9 @@ ${text}`;
5835
5868
  /** Reserved: restore a historical session */
5836
5869
  async restoreSession(_sessionId) {
5837
5870
  }
5871
+ getActiveChannelIds() {
5872
+ return Array.from(this.channels.keys());
5873
+ }
5838
5874
  };
5839
5875
 
5840
5876
  // src/runtime/adapters/web.ts
@@ -5868,9 +5904,11 @@ var WebAdapter = class {
5868
5904
  name = "web";
5869
5905
  wss = null;
5870
5906
  agent = null;
5907
+ ipcBroadcaster = null;
5871
5908
  async start(ctx) {
5872
5909
  const { agent, server, app, rootDir, lifecycle } = ctx;
5873
5910
  this.agent = agent;
5911
+ this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
5874
5912
  const currentConf = configManager.getConfig();
5875
5913
  let apiKey = currentConf.apiKey || "";
5876
5914
  let currentProvider = currentConf.provider || "openai";
@@ -6138,6 +6176,7 @@ var WebAdapter = class {
6138
6176
  }
6139
6177
  const onEvent = (event) => {
6140
6178
  sendWsEvent(ws, event);
6179
+ this.ipcBroadcaster?.broadcastAgentEvent(channelId, event);
6141
6180
  };
6142
6181
  const result = await agent.handleMessage("web", channelId, text, onEvent);
6143
6182
  if (result.errorMessage) {
@@ -6155,6 +6194,367 @@ var WebAdapter = class {
6155
6194
  }
6156
6195
  };
6157
6196
 
6197
+ // src/runtime/adapters/ipc.ts
6198
+ init_config();
6199
+
6200
+ // src/runtime/services/conversation.ts
6201
+ import fs11 from "fs";
6202
+ import path11 from "path";
6203
+ import {
6204
+ parseSessionEntries
6205
+ } from "@mariozechner/pi-coding-agent";
6206
+ var ConversationService = class {
6207
+ constructor(rootDir) {
6208
+ this.rootDir = rootDir;
6209
+ }
6210
+ /**
6211
+ * Scan data/sessions and return conversation summaries sorted by recency.
6212
+ */
6213
+ listConversations(activeChannels) {
6214
+ const sessionsDir = path11.resolve(this.rootDir, "data", "sessions");
6215
+ const channelIds = new Set(activeChannels);
6216
+ if (fs11.existsSync(sessionsDir)) {
6217
+ for (const entry of fs11.readdirSync(sessionsDir)) {
6218
+ const channelDir = path11.join(sessionsDir, entry);
6219
+ try {
6220
+ if (fs11.statSync(channelDir).isDirectory()) {
6221
+ channelIds.add(entry);
6222
+ }
6223
+ } catch {
6224
+ }
6225
+ }
6226
+ }
6227
+ const results = [];
6228
+ for (const channelId of channelIds) {
6229
+ const channelDir = path11.join(sessionsDir, channelId);
6230
+ const sessionFile = this.findLatestSessionFile(channelDir);
6231
+ let messageCount = 0;
6232
+ let lastMessageAt = "";
6233
+ let lastMessagePreview = "";
6234
+ if (sessionFile) {
6235
+ const entries = this.loadEntries(sessionFile);
6236
+ const messages = entries.filter(
6237
+ (entry) => entry.type === "message"
6238
+ );
6239
+ messageCount = messages.length;
6240
+ const lastMessage = messages[messages.length - 1];
6241
+ if (lastMessage) {
6242
+ lastMessageAt = lastMessage.timestamp;
6243
+ lastMessagePreview = this.extractTextPreview(lastMessage, 100);
6244
+ }
6245
+ }
6246
+ results.push({
6247
+ channelId,
6248
+ platform: this.detectPlatform(channelId),
6249
+ sessionFile,
6250
+ messageCount,
6251
+ lastMessageAt,
6252
+ lastMessagePreview
6253
+ });
6254
+ }
6255
+ return results.sort((a, b) => {
6256
+ const recency = (b.lastMessageAt || "").localeCompare(a.lastMessageAt || "");
6257
+ if (recency !== 0) return recency;
6258
+ return a.channelId.localeCompare(b.channelId);
6259
+ });
6260
+ }
6261
+ /**
6262
+ * Load latest messages for a channel in a simplified format.
6263
+ */
6264
+ getMessages(channelId, limit = 100) {
6265
+ const channelDir = path11.resolve(
6266
+ this.rootDir,
6267
+ "data",
6268
+ "sessions",
6269
+ channelId
6270
+ );
6271
+ const sessionFile = this.findLatestSessionFile(channelDir);
6272
+ if (!sessionFile) return [];
6273
+ const safeLimit = Number.isFinite(limit) ? Math.max(0, Math.floor(limit)) : 100;
6274
+ if (safeLimit === 0) return [];
6275
+ const entries = this.loadEntries(sessionFile);
6276
+ const messages = [];
6277
+ for (const entry of entries) {
6278
+ if (entry.type !== "message") continue;
6279
+ const role = entry.message?.role;
6280
+ if (role !== "user" && role !== "assistant") continue;
6281
+ const text = this.extractText(entry.message);
6282
+ if (!text) continue;
6283
+ const toolCalls = role === "assistant" ? this.extractToolCallSummaries(entry.message) : void 0;
6284
+ messages.push({
6285
+ id: entry.id,
6286
+ role,
6287
+ text,
6288
+ timestamp: entry.timestamp,
6289
+ toolCalls
6290
+ });
6291
+ }
6292
+ return messages.slice(-safeLimit);
6293
+ }
6294
+ findLatestSessionFile(channelDir) {
6295
+ if (!fs11.existsSync(channelDir)) return null;
6296
+ let stats;
6297
+ try {
6298
+ stats = fs11.statSync(channelDir);
6299
+ } catch {
6300
+ return null;
6301
+ }
6302
+ if (!stats.isDirectory()) return null;
6303
+ const files = fs11.readdirSync(channelDir).filter((file) => file.endsWith(".jsonl")).sort((a, b) => b.localeCompare(a));
6304
+ return files[0] ? path11.join(channelDir, files[0]) : null;
6305
+ }
6306
+ loadEntries(filePath) {
6307
+ try {
6308
+ const content = fs11.readFileSync(filePath, "utf-8");
6309
+ const fileEntries = parseSessionEntries(content);
6310
+ return fileEntries.filter((entry) => entry.type !== "session");
6311
+ } catch (err) {
6312
+ console.warn(`[ConversationService] Failed to load ${filePath}:`, err);
6313
+ return [];
6314
+ }
6315
+ }
6316
+ extractText(message) {
6317
+ if (!message?.content) return "";
6318
+ if (typeof message.content === "string") return message.content.trim();
6319
+ if (!Array.isArray(message.content)) return "";
6320
+ return message.content.filter((item) => item?.type === "text").map((item) => typeof item?.text === "string" ? item.text : "").join("").trim();
6321
+ }
6322
+ extractTextPreview(entry, maxLen) {
6323
+ const text = this.extractText(entry.message);
6324
+ return text.length > maxLen ? `${text.slice(0, maxLen)}\u2026` : text;
6325
+ }
6326
+ extractToolCallSummaries(message) {
6327
+ if (!Array.isArray(message?.content)) return void 0;
6328
+ const toolCalls = message.content.filter((item) => item?.type === "toolCall").map((item) => ({
6329
+ name: typeof item?.name === "string" && item.name ? item.name : "unknown",
6330
+ isError: false
6331
+ }));
6332
+ return toolCalls.length > 0 ? toolCalls : void 0;
6333
+ }
6334
+ detectPlatform(channelId) {
6335
+ if (channelId.startsWith("telegram-")) return "telegram";
6336
+ if (channelId.startsWith("slack-")) return "slack";
6337
+ if (channelId.startsWith("scheduler-")) return "scheduler";
6338
+ return "web";
6339
+ }
6340
+ };
6341
+
6342
+ // src/runtime/adapters/ipc.ts
6343
+ init_types();
6344
+ var IpcAdapter = class {
6345
+ name = "ipc";
6346
+ agent = null;
6347
+ rootDir = "";
6348
+ adapterMap = null;
6349
+ conversationService = null;
6350
+ messageListener;
6351
+ started = false;
6352
+ async start(ctx) {
6353
+ if (typeof process.send !== "function") {
6354
+ return;
6355
+ }
6356
+ this.agent = ctx.agent;
6357
+ this.rootDir = ctx.rootDir;
6358
+ this.adapterMap = ctx.adapterMap ?? null;
6359
+ this.conversationService = new ConversationService(ctx.rootDir);
6360
+ this.messageListener = (message) => {
6361
+ if (!this.isIpcRequest(message)) return;
6362
+ void this.handleRequest(message);
6363
+ };
6364
+ process.on("message", this.messageListener);
6365
+ this.started = true;
6366
+ console.log("[IpcAdapter] Started");
6367
+ }
6368
+ async stop() {
6369
+ if (this.messageListener) {
6370
+ process.off("message", this.messageListener);
6371
+ this.messageListener = void 0;
6372
+ }
6373
+ if (this.started) {
6374
+ console.log("[IpcAdapter] Stopped");
6375
+ }
6376
+ this.started = false;
6377
+ }
6378
+ notifyReady(port) {
6379
+ this.sendIpc({
6380
+ type: "ready",
6381
+ port
6382
+ });
6383
+ }
6384
+ broadcastInbound(channelId, platform, sender, text) {
6385
+ this.sendIpc({
6386
+ type: "inbound_message",
6387
+ channelId,
6388
+ platform,
6389
+ sender,
6390
+ text,
6391
+ timestamp: Date.now()
6392
+ });
6393
+ }
6394
+ broadcastAgentEvent(channelId, event) {
6395
+ this.sendIpc({
6396
+ type: "agent_event",
6397
+ channelId,
6398
+ event
6399
+ });
6400
+ }
6401
+ isIpcRequest(message) {
6402
+ if (!message || typeof message !== "object") return false;
6403
+ const maybe = message;
6404
+ return typeof maybe.id === "string" && typeof maybe.type === "string";
6405
+ }
6406
+ async handleRequest(request) {
6407
+ if (!this.agent || !this.conversationService) {
6408
+ this.replyError(request.id, "IPC adapter is not ready yet");
6409
+ return;
6410
+ }
6411
+ try {
6412
+ switch (request.type) {
6413
+ case "get_conversations": {
6414
+ const activeChannels = new Set(this.agent.getActiveChannelIds());
6415
+ const conversations = this.conversationService.listConversations(activeChannels);
6416
+ this.reply(request.id, conversations);
6417
+ return;
6418
+ }
6419
+ case "get_messages": {
6420
+ if (!request.channelId || typeof request.channelId !== "string") {
6421
+ this.replyError(request.id, "channelId is required");
6422
+ return;
6423
+ }
6424
+ const messages = this.conversationService.getMessages(
6425
+ request.channelId,
6426
+ request.limit ?? 100
6427
+ );
6428
+ this.reply(request.id, messages);
6429
+ return;
6430
+ }
6431
+ case "send_message": {
6432
+ if (!request.channelId || typeof request.channelId !== "string") {
6433
+ this.replyError(request.id, "channelId is required");
6434
+ return;
6435
+ }
6436
+ if (typeof request.text !== "string") {
6437
+ this.replyError(request.id, "text is required");
6438
+ return;
6439
+ }
6440
+ const platform = this.detectPlatform(request.channelId);
6441
+ let fullText = "";
6442
+ const result = await this.agent.handleMessage(
6443
+ platform,
6444
+ request.channelId,
6445
+ request.text,
6446
+ (event) => {
6447
+ if (event.type === "text_delta") {
6448
+ fullText += event.delta;
6449
+ }
6450
+ this.broadcastAgentEvent(request.channelId, event);
6451
+ }
6452
+ );
6453
+ if (fullText.trim() && platform !== "web" && platform !== "scheduler") {
6454
+ const adapter = this.adapterMap?.get(platform);
6455
+ if (adapter && isMessageSender(adapter)) {
6456
+ await adapter.sendMessage(request.channelId, fullText);
6457
+ }
6458
+ }
6459
+ this.reply(request.id, {
6460
+ ...result,
6461
+ text: fullText
6462
+ });
6463
+ return;
6464
+ }
6465
+ case "command": {
6466
+ if (!request.channelId || typeof request.channelId !== "string") {
6467
+ this.replyError(request.id, "channelId is required");
6468
+ return;
6469
+ }
6470
+ const result = await this.agent.handleCommand(request.command, request.channelId);
6471
+ this.reply(request.id, result);
6472
+ return;
6473
+ }
6474
+ case "get_config": {
6475
+ this.reply(request.id, configManager.getConfig());
6476
+ return;
6477
+ }
6478
+ case "update_config": {
6479
+ configManager.save(this.rootDir, request.updates || {});
6480
+ const updated = configManager.getConfig();
6481
+ const provider = updated.provider || "openai";
6482
+ this.agent.updateAuth(provider, updated.apiKey);
6483
+ this.reply(request.id, updated);
6484
+ return;
6485
+ }
6486
+ case "get_status": {
6487
+ this.reply(request.id, {
6488
+ status: "running",
6489
+ pid: process.pid
6490
+ });
6491
+ return;
6492
+ }
6493
+ case "get_scheduled_jobs": {
6494
+ const scheduler = this.getSchedulerAdapter();
6495
+ this.reply(request.id, scheduler ? scheduler.listJobs() : []);
6496
+ return;
6497
+ }
6498
+ case "add_scheduled_job": {
6499
+ const scheduler = this.getSchedulerAdapter();
6500
+ if (!scheduler) {
6501
+ this.replyError(request.id, "Scheduler adapter is not available");
6502
+ return;
6503
+ }
6504
+ const result = scheduler.addJob(request.job);
6505
+ if (!result.success) {
6506
+ this.replyError(request.id, result.message);
6507
+ return;
6508
+ }
6509
+ this.reply(request.id, result);
6510
+ return;
6511
+ }
6512
+ case "remove_scheduled_job": {
6513
+ const scheduler = this.getSchedulerAdapter();
6514
+ if (!scheduler) {
6515
+ this.replyError(request.id, "Scheduler adapter is not available");
6516
+ return;
6517
+ }
6518
+ const result = scheduler.removeJob(request.name);
6519
+ if (!result.success) {
6520
+ this.replyError(request.id, result.message);
6521
+ return;
6522
+ }
6523
+ this.reply(request.id, result);
6524
+ return;
6525
+ }
6526
+ }
6527
+ } catch (err) {
6528
+ this.replyError(
6529
+ request.id,
6530
+ err instanceof Error ? err.message : String(err)
6531
+ );
6532
+ }
6533
+ }
6534
+ getSchedulerAdapter() {
6535
+ const adapter = this.adapterMap?.get("scheduler");
6536
+ if (!adapter) return null;
6537
+ return adapter;
6538
+ }
6539
+ detectPlatform(channelId) {
6540
+ if (channelId.startsWith("telegram-")) return "telegram";
6541
+ if (channelId.startsWith("slack-")) return "slack";
6542
+ if (channelId.startsWith("scheduler-")) return "scheduler";
6543
+ return "web";
6544
+ }
6545
+ sendIpc(payload) {
6546
+ if (typeof process.send === "function") {
6547
+ process.send(payload);
6548
+ }
6549
+ }
6550
+ reply(id, data) {
6551
+ this.sendIpc({ id, type: "result", data });
6552
+ }
6553
+ replyError(id, message) {
6554
+ this.sendIpc({ id, type: "error", message });
6555
+ }
6556
+ };
6557
+
6158
6558
  // src/runtime/server.ts
6159
6559
  init_config();
6160
6560
 
@@ -6231,28 +6631,28 @@ var Lifecycle = class {
6231
6631
 
6232
6632
  // src/runtime/registry.ts
6233
6633
  import crypto from "crypto";
6234
- import fs11 from "fs";
6634
+ import fs12 from "fs";
6235
6635
  import os from "os";
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");
6636
+ import path12 from "path";
6637
+ var SKILLPACK_HOME = path12.join(os.homedir(), ".skillpack");
6638
+ var LEGACY_REGISTRY_FILE = path12.join(SKILLPACK_HOME, "registry.json");
6639
+ var REGISTRY_DIR = path12.join(SKILLPACK_HOME, "registry.d");
6240
6640
  var migrationChecked = false;
6241
6641
  function ensureHomeDir() {
6242
- if (!fs11.existsSync(SKILLPACK_HOME)) {
6243
- fs11.mkdirSync(SKILLPACK_HOME, { recursive: true });
6642
+ if (!fs12.existsSync(SKILLPACK_HOME)) {
6643
+ fs12.mkdirSync(SKILLPACK_HOME, { recursive: true });
6244
6644
  }
6245
6645
  }
6246
6646
  function ensureRegistryDir() {
6247
6647
  ensureHomeDir();
6248
- if (!fs11.existsSync(REGISTRY_DIR)) {
6249
- fs11.mkdirSync(REGISTRY_DIR, { recursive: true });
6648
+ if (!fs12.existsSync(REGISTRY_DIR)) {
6649
+ fs12.mkdirSync(REGISTRY_DIR, { recursive: true });
6250
6650
  }
6251
6651
  }
6252
6652
  function canonicalizeDir(dir) {
6253
- const resolved = path11.resolve(dir);
6653
+ const resolved = path12.resolve(dir);
6254
6654
  try {
6255
- return fs11.realpathSync(resolved);
6655
+ return fs12.realpathSync(resolved);
6256
6656
  } catch {
6257
6657
  return resolved;
6258
6658
  }
@@ -6261,7 +6661,7 @@ function hashDir(dir) {
6261
6661
  return crypto.createHash("md5").update(canonicalizeDir(dir)).digest("hex");
6262
6662
  }
6263
6663
  function getEntryPathForCanonicalDir(dir) {
6264
- return path11.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
6664
+ return path12.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
6265
6665
  }
6266
6666
  function getEntryPath(dir) {
6267
6667
  ensureRegistryReady();
@@ -6269,11 +6669,11 @@ function getEntryPath(dir) {
6269
6669
  }
6270
6670
  function listEntryFiles() {
6271
6671
  ensureRegistryReady();
6272
- return fs11.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path11.join(REGISTRY_DIR, file));
6672
+ return fs12.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path12.join(REGISTRY_DIR, file));
6273
6673
  }
6274
6674
  function readEntryFile(filePath) {
6275
6675
  try {
6276
- const raw = fs11.readFileSync(filePath, "utf-8");
6676
+ const raw = fs12.readFileSync(filePath, "utf-8");
6277
6677
  const data = JSON.parse(raw);
6278
6678
  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") {
6279
6679
  return null;
@@ -6306,8 +6706,8 @@ function writeEntryFile(entry) {
6306
6706
  };
6307
6707
  const entryPath = getEntryPathForCanonicalDir(normalized.dir);
6308
6708
  const tmpPath = createTmpPath(entryPath);
6309
- fs11.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
6310
- fs11.renameSync(tmpPath, entryPath);
6709
+ fs12.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
6710
+ fs12.renameSync(tmpPath, entryPath);
6311
6711
  }
6312
6712
  function migrateLegacyRegistryIfNeeded() {
6313
6713
  if (migrationChecked) {
@@ -6315,14 +6715,14 @@ function migrateLegacyRegistryIfNeeded() {
6315
6715
  }
6316
6716
  migrationChecked = true;
6317
6717
  ensureRegistryDir();
6318
- if (!fs11.existsSync(LEGACY_REGISTRY_FILE)) {
6718
+ if (!fs12.existsSync(LEGACY_REGISTRY_FILE)) {
6319
6719
  return;
6320
6720
  }
6321
6721
  if (listEntryFiles().length > 0) {
6322
6722
  return;
6323
6723
  }
6324
6724
  try {
6325
- const raw = fs11.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
6725
+ const raw = fs12.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
6326
6726
  const data = JSON.parse(raw);
6327
6727
  const packs = Array.isArray(data?.packs) ? data.packs : [];
6328
6728
  for (const pack of packs) {
@@ -6335,7 +6735,7 @@ function migrateLegacyRegistryIfNeeded() {
6335
6735
  } catch {
6336
6736
  }
6337
6737
  }
6338
- fs11.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
6738
+ fs12.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
6339
6739
  } catch (err) {
6340
6740
  console.warn(" [Registry] Failed to migrate legacy registry.json:", err);
6341
6741
  }
@@ -6388,7 +6788,7 @@ function deregister(dir, pid) {
6388
6788
  }
6389
6789
 
6390
6790
  // src/runtime/server.ts
6391
- var __dirname = path13.dirname(fileURLToPath2(import.meta.url));
6791
+ var __dirname = path14.dirname(fileURLToPath2(import.meta.url));
6392
6792
  async function startServer(options) {
6393
6793
  const {
6394
6794
  rootDir,
@@ -6403,8 +6803,8 @@ async function startServer(options) {
6403
6803
  const packConfig = loadConfig(canonicalRootDir);
6404
6804
  const baseUrl = dataConfig.baseUrl?.trim() || void 0;
6405
6805
  const modelId = SUPPORTED_PROVIDERS[provider]?.defaultModelId ?? SUPPORTED_PROVIDERS.openai.defaultModelId;
6406
- const packageRoot = path13.resolve(__dirname, "..");
6407
- const webDir = fs14.existsSync(path13.join(rootDir, "web")) ? path13.join(rootDir, "web") : path13.join(packageRoot, "web");
6806
+ const packageRoot = path14.resolve(__dirname, "..");
6807
+ const webDir = fs15.existsSync(path14.join(rootDir, "web")) ? path14.join(rootDir, "web") : path14.join(packageRoot, "web");
6408
6808
  const app = express();
6409
6809
  app.use(express.json());
6410
6810
  app.use(express.static(webDir));
@@ -6432,8 +6832,31 @@ async function startServer(options) {
6432
6832
  });
6433
6833
  const adapters = [];
6434
6834
  const adapterMap = /* @__PURE__ */ new Map();
6835
+ const hasIpcChannel = typeof process.send === "function";
6836
+ const ipcAdapter = new IpcAdapter();
6837
+ if (hasIpcChannel) {
6838
+ await ipcAdapter.start({
6839
+ agent,
6840
+ server,
6841
+ app,
6842
+ rootDir,
6843
+ lifecycle,
6844
+ adapterMap
6845
+ });
6846
+ adapters.push(ipcAdapter);
6847
+ adapterMap.set(ipcAdapter.name, ipcAdapter);
6848
+ }
6849
+ const ipcBroadcaster = hasIpcChannel ? ipcAdapter : void 0;
6435
6850
  const webAdapter = new WebAdapter();
6436
- await webAdapter.start({ agent, server, app, rootDir, lifecycle, adapterMap });
6851
+ await webAdapter.start({
6852
+ agent,
6853
+ server,
6854
+ app,
6855
+ rootDir,
6856
+ lifecycle,
6857
+ adapterMap,
6858
+ ipcBroadcaster
6859
+ });
6437
6860
  adapters.push(webAdapter);
6438
6861
  adapterMap.set(webAdapter.name, webAdapter);
6439
6862
  if (dataConfig.adapters?.telegram?.token) {
@@ -6442,7 +6865,15 @@ async function startServer(options) {
6442
6865
  const telegramAdapter = new TelegramAdapter2({
6443
6866
  token: dataConfig.adapters.telegram.token
6444
6867
  });
6445
- await telegramAdapter.start({ agent, server, app, rootDir, lifecycle });
6868
+ await telegramAdapter.start({
6869
+ agent,
6870
+ server,
6871
+ app,
6872
+ rootDir,
6873
+ lifecycle,
6874
+ adapterMap,
6875
+ ipcBroadcaster
6876
+ });
6446
6877
  adapters.push(telegramAdapter);
6447
6878
  adapterMap.set(telegramAdapter.name, telegramAdapter);
6448
6879
  } catch (err) {
@@ -6462,7 +6893,15 @@ async function startServer(options) {
6462
6893
  botToken: slackConfig.botToken,
6463
6894
  appToken: slackConfig.appToken
6464
6895
  });
6465
- await slackAdapter.start({ agent, server, app, rootDir, lifecycle });
6896
+ await slackAdapter.start({
6897
+ agent,
6898
+ server,
6899
+ app,
6900
+ rootDir,
6901
+ lifecycle,
6902
+ adapterMap,
6903
+ ipcBroadcaster
6904
+ });
6466
6905
  adapters.push(slackAdapter);
6467
6906
  adapterMap.set(slackAdapter.name, slackAdapter);
6468
6907
  } catch (err) {
@@ -6525,6 +6964,9 @@ async function startServer(options) {
6525
6964
  } catch (err) {
6526
6965
  console.warn(" [Registry] Could not register pack:", err);
6527
6966
  }
6967
+ if (hasIpcChannel) {
6968
+ ipcAdapter.notifyReady(typeof actualPort === "number" ? actualPort : port);
6969
+ }
6528
6970
  if (!daemonRun) {
6529
6971
  const cmd = process.platform === "darwin" ? `open ${url}` : process.platform === "win32" ? `start ${url}` : `xdg-open ${url}`;
6530
6972
  exec(cmd, (err) => {
@@ -6572,23 +7014,23 @@ function findMissingSkills(workDir, config) {
6572
7014
  });
6573
7015
  }
6574
7016
  function copyStartTemplates2(workDir) {
6575
- const templateDir = path14.resolve(
7017
+ const templateDir = path15.resolve(
6576
7018
  new URL("../templates", import.meta.url).pathname
6577
7019
  );
6578
7020
  for (const file of ["start.sh", "start.bat"]) {
6579
- const src = path14.join(templateDir, file);
6580
- const dest = path14.join(workDir, file);
6581
- if (fs15.existsSync(src)) {
6582
- fs15.copyFileSync(src, dest);
7021
+ const src = path15.join(templateDir, file);
7022
+ const dest = path15.join(workDir, file);
7023
+ if (fs16.existsSync(src)) {
7024
+ fs16.copyFileSync(src, dest);
6583
7025
  if (file === "start.sh") {
6584
- fs15.chmodSync(dest, 493);
7026
+ fs16.chmodSync(dest, 493);
6585
7027
  }
6586
7028
  }
6587
7029
  }
6588
7030
  }
6589
7031
  async function runCommand(directory) {
6590
- const workDir = directory ? path14.resolve(directory) : process.cwd();
6591
- fs15.mkdirSync(workDir, { recursive: true });
7032
+ const workDir = directory ? path15.resolve(directory) : process.cwd();
7033
+ fs16.mkdirSync(workDir, { recursive: true });
6592
7034
  if (!configExists(workDir)) {
6593
7035
  console.log(chalk4.blue("\n No skillpack.json found. Let's set one up.\n"));
6594
7036
  const { name, description } = await inquirer2.prompt([
@@ -6633,9 +7075,9 @@ async function runCommand(directory) {
6633
7075
  }
6634
7076
 
6635
7077
  // src/cli.ts
6636
- import fs16 from "fs";
7078
+ import fs17 from "fs";
6637
7079
  var packageJson = JSON.parse(
6638
- fs16.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
7080
+ fs17.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
6639
7081
  );
6640
7082
  var program = new Command();
6641
7083
  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.4",
3
+ "version": "1.2.5-beta.1",
4
4
  "description": "Pack AI Skills into Local Agents",
5
5
  "type": "module",
6
6
  "repository": {