kanban-lite 1.2.13 → 1.2.16

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.
@@ -24954,6 +24954,56 @@ async function updateComment(ctx, { cardId, commentId, content, boardId }) {
24954
24954
  });
24955
24955
  return card;
24956
24956
  }
24957
+ async function streamComment(ctx, {
24958
+ cardId,
24959
+ author,
24960
+ boardId,
24961
+ stream,
24962
+ onStart,
24963
+ onChunk
24964
+ }) {
24965
+ if (!author?.trim())
24966
+ throw new Error("Comment author cannot be empty");
24967
+ const card = await ctx.getCard(cardId, boardId);
24968
+ if (!card)
24969
+ throw new Error(`Card not found: ${cardId}`);
24970
+ if (!card.comments)
24971
+ card.comments = [];
24972
+ const maxId = card.comments.reduce((max, c) => {
24973
+ const num = parseInt(c.id.replace("c", ""), 10);
24974
+ return Number.isNaN(num) ? max : Math.max(max, num);
24975
+ }, 0);
24976
+ const commentId = `c${maxId + 1}`;
24977
+ const created = (/* @__PURE__ */ new Date()).toISOString();
24978
+ onStart?.(commentId, author, created);
24979
+ let accumulated = "";
24980
+ for await (const chunk of stream) {
24981
+ accumulated += chunk;
24982
+ onChunk?.(commentId, chunk);
24983
+ }
24984
+ const comment = {
24985
+ id: commentId,
24986
+ author,
24987
+ created,
24988
+ content: accumulated
24989
+ };
24990
+ card.comments.push(comment);
24991
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
24992
+ await ctx._storage.writeCard(card);
24993
+ await appendActivityLog(ctx, {
24994
+ cardId: card.id,
24995
+ boardId: card.boardId || ctx._resolveBoardId(boardId),
24996
+ eventType: "comment.created",
24997
+ text: `Comment added by \`${author}\` (streamed)`,
24998
+ metadata: {
24999
+ commentId: comment.id,
25000
+ author,
25001
+ created: comment.created
25002
+ }
25003
+ }).catch(() => {
25004
+ });
25005
+ return card;
25006
+ }
24957
25007
  async function deleteComment(ctx, { cardId, commentId, boardId }) {
24958
25008
  const card = await ctx.getCard(cardId, boardId);
24959
25009
  if (!card)
@@ -27015,7 +27065,47 @@ var KanbanSDK = class _KanbanSDK {
27015
27065
  this._runAfterEvent("comment.deleted", { ...deletedComment, cardId: mergedInput.cardId }, void 0, card.boardId ?? this._resolveBoardId(mergedInput.boardId));
27016
27066
  return card;
27017
27067
  }
27018
- // --- Log management ---
27068
+ /**
27069
+ * Creates a comment on a card from a streaming text source, persisting it
27070
+ * once the stream is exhausted.
27071
+ *
27072
+ * This method is the streaming counterpart to {@link addComment}. It is
27073
+ * intended for use by AI agents that generate comment text incrementally
27074
+ * (e.g. an LLM `textStream`). The caller may supply `onStart` and `onChunk`
27075
+ * callbacks to fan live progress out to connected WebSocket viewers without
27076
+ * requiring intermediate disk writes.
27077
+ *
27078
+ * @param cardId - The ID of the card to comment on.
27079
+ * @param author - Display name of the streaming author.
27080
+ * @param stream - An `AsyncIterable<string>` that yields text chunks.
27081
+ * @param options.boardId - Optional board ID override.
27082
+ * @param options.onStart - Called once before iteration with the allocated
27083
+ * comment ID, author, and ISO timestamp.
27084
+ * @param options.onChunk - Called after each chunk with the comment ID and
27085
+ * the raw chunk string.
27086
+ * @returns A promise resolving to the updated {@link Card} once the stream
27087
+ * has been fully consumed and the comment has been persisted.
27088
+ * @throws {Error} If the card is not found.
27089
+ * @throws {Error} If `author` is empty.
27090
+ *
27091
+ * @example
27092
+ * ```ts
27093
+ * // Stream an AI SDK textStream as a comment
27094
+ * const { textStream } = await streamText({ model, prompt })
27095
+ * const card = await sdk.streamComment('42', 'ai-agent', textStream, {
27096
+ * onStart: (id, author, created) => broadcast({ type: 'commentStreamStart', cardId: '42', commentId: id, author, created }),
27097
+ * onChunk: (id, chunk) => broadcast({ type: 'commentChunk', cardId: '42', commentId: id, chunk }),
27098
+ * })
27099
+ * ```
27100
+ */
27101
+ async streamComment(cardId, author, stream, options) {
27102
+ const { boardId, onStart, onChunk } = options ?? {};
27103
+ const card = await streamComment(this, { cardId, author, boardId, stream, onStart, onChunk });
27104
+ const newComment = card.comments?.[card.comments.length - 1];
27105
+ if (newComment)
27106
+ this._runAfterEvent("comment.created", { ...newComment, cardId }, void 0, card.boardId ?? this._resolveBoardId(boardId));
27107
+ return card;
27108
+ }
27019
27109
  /**
27020
27110
  * Returns the absolute path to the log file for a card.
27021
27111
  *
@@ -24912,6 +24912,56 @@ async function updateComment(ctx, { cardId, commentId, content, boardId }) {
24912
24912
  });
24913
24913
  return card;
24914
24914
  }
24915
+ async function streamComment(ctx, {
24916
+ cardId,
24917
+ author,
24918
+ boardId,
24919
+ stream,
24920
+ onStart,
24921
+ onChunk
24922
+ }) {
24923
+ if (!author?.trim())
24924
+ throw new Error("Comment author cannot be empty");
24925
+ const card = await ctx.getCard(cardId, boardId);
24926
+ if (!card)
24927
+ throw new Error(`Card not found: ${cardId}`);
24928
+ if (!card.comments)
24929
+ card.comments = [];
24930
+ const maxId = card.comments.reduce((max, c) => {
24931
+ const num = parseInt(c.id.replace("c", ""), 10);
24932
+ return Number.isNaN(num) ? max : Math.max(max, num);
24933
+ }, 0);
24934
+ const commentId = `c${maxId + 1}`;
24935
+ const created = (/* @__PURE__ */ new Date()).toISOString();
24936
+ onStart?.(commentId, author, created);
24937
+ let accumulated = "";
24938
+ for await (const chunk of stream) {
24939
+ accumulated += chunk;
24940
+ onChunk?.(commentId, chunk);
24941
+ }
24942
+ const comment = {
24943
+ id: commentId,
24944
+ author,
24945
+ created,
24946
+ content: accumulated
24947
+ };
24948
+ card.comments.push(comment);
24949
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
24950
+ await ctx._storage.writeCard(card);
24951
+ await appendActivityLog(ctx, {
24952
+ cardId: card.id,
24953
+ boardId: card.boardId || ctx._resolveBoardId(boardId),
24954
+ eventType: "comment.created",
24955
+ text: `Comment added by \`${author}\` (streamed)`,
24956
+ metadata: {
24957
+ commentId: comment.id,
24958
+ author,
24959
+ created: comment.created
24960
+ }
24961
+ }).catch(() => {
24962
+ });
24963
+ return card;
24964
+ }
24915
24965
  async function deleteComment(ctx, { cardId, commentId, boardId }) {
24916
24966
  const card = await ctx.getCard(cardId, boardId);
24917
24967
  if (!card)
@@ -26973,7 +27023,47 @@ var KanbanSDK = class _KanbanSDK {
26973
27023
  this._runAfterEvent("comment.deleted", { ...deletedComment, cardId: mergedInput.cardId }, void 0, card.boardId ?? this._resolveBoardId(mergedInput.boardId));
26974
27024
  return card;
26975
27025
  }
26976
- // --- Log management ---
27026
+ /**
27027
+ * Creates a comment on a card from a streaming text source, persisting it
27028
+ * once the stream is exhausted.
27029
+ *
27030
+ * This method is the streaming counterpart to {@link addComment}. It is
27031
+ * intended for use by AI agents that generate comment text incrementally
27032
+ * (e.g. an LLM `textStream`). The caller may supply `onStart` and `onChunk`
27033
+ * callbacks to fan live progress out to connected WebSocket viewers without
27034
+ * requiring intermediate disk writes.
27035
+ *
27036
+ * @param cardId - The ID of the card to comment on.
27037
+ * @param author - Display name of the streaming author.
27038
+ * @param stream - An `AsyncIterable<string>` that yields text chunks.
27039
+ * @param options.boardId - Optional board ID override.
27040
+ * @param options.onStart - Called once before iteration with the allocated
27041
+ * comment ID, author, and ISO timestamp.
27042
+ * @param options.onChunk - Called after each chunk with the comment ID and
27043
+ * the raw chunk string.
27044
+ * @returns A promise resolving to the updated {@link Card} once the stream
27045
+ * has been fully consumed and the comment has been persisted.
27046
+ * @throws {Error} If the card is not found.
27047
+ * @throws {Error} If `author` is empty.
27048
+ *
27049
+ * @example
27050
+ * ```ts
27051
+ * // Stream an AI SDK textStream as a comment
27052
+ * const { textStream } = await streamText({ model, prompt })
27053
+ * const card = await sdk.streamComment('42', 'ai-agent', textStream, {
27054
+ * onStart: (id, author, created) => broadcast({ type: 'commentStreamStart', cardId: '42', commentId: id, author, created }),
27055
+ * onChunk: (id, chunk) => broadcast({ type: 'commentChunk', cardId: '42', commentId: id, chunk }),
27056
+ * })
27057
+ * ```
27058
+ */
27059
+ async streamComment(cardId, author, stream, options) {
27060
+ const { boardId, onStart, onChunk } = options ?? {};
27061
+ const card = await streamComment(this, { cardId, author, boardId, stream, onStart, onChunk });
27062
+ const newComment = card.comments?.[card.comments.length - 1];
27063
+ if (newComment)
27064
+ this._runAfterEvent("comment.created", { ...newComment, cardId }, void 0, card.boardId ?? this._resolveBoardId(boardId));
27065
+ return card;
27066
+ }
26977
27067
  /**
26978
27068
  * Returns the absolute path to the log file for a card.
26979
27069
  *
@@ -1345,6 +1345,44 @@ export declare class KanbanSDK {
1345
1345
  * ```
1346
1346
  */
1347
1347
  deleteComment(cardId: string, commentId: string, boardId?: string): Promise<Card>;
1348
+ /**
1349
+ * Creates a comment on a card from a streaming text source, persisting it
1350
+ * once the stream is exhausted.
1351
+ *
1352
+ * This method is the streaming counterpart to {@link addComment}. It is
1353
+ * intended for use by AI agents that generate comment text incrementally
1354
+ * (e.g. an LLM `textStream`). The caller may supply `onStart` and `onChunk`
1355
+ * callbacks to fan live progress out to connected WebSocket viewers without
1356
+ * requiring intermediate disk writes.
1357
+ *
1358
+ * @param cardId - The ID of the card to comment on.
1359
+ * @param author - Display name of the streaming author.
1360
+ * @param stream - An `AsyncIterable<string>` that yields text chunks.
1361
+ * @param options.boardId - Optional board ID override.
1362
+ * @param options.onStart - Called once before iteration with the allocated
1363
+ * comment ID, author, and ISO timestamp.
1364
+ * @param options.onChunk - Called after each chunk with the comment ID and
1365
+ * the raw chunk string.
1366
+ * @returns A promise resolving to the updated {@link Card} once the stream
1367
+ * has been fully consumed and the comment has been persisted.
1368
+ * @throws {Error} If the card is not found.
1369
+ * @throws {Error} If `author` is empty.
1370
+ *
1371
+ * @example
1372
+ * ```ts
1373
+ * // Stream an AI SDK textStream as a comment
1374
+ * const { textStream } = await streamText({ model, prompt })
1375
+ * const card = await sdk.streamComment('42', 'ai-agent', textStream, {
1376
+ * onStart: (id, author, created) => broadcast({ type: 'commentStreamStart', cardId: '42', commentId: id, author, created }),
1377
+ * onChunk: (id, chunk) => broadcast({ type: 'commentChunk', cardId: '42', commentId: id, chunk }),
1378
+ * })
1379
+ * ```
1380
+ */
1381
+ streamComment(cardId: string, author: string, stream: AsyncIterable<string>, options?: {
1382
+ boardId?: string;
1383
+ onStart?: (commentId: string, author: string, created: string) => void;
1384
+ onChunk?: (commentId: string, chunk: string) => void;
1385
+ }): Promise<Card>;
1348
1386
  /**
1349
1387
  * Returns the absolute path to the log file for a card.
1350
1388
  *
@@ -25,6 +25,39 @@ export declare function updateComment(ctx: SDKContext, { cardId, commentId, cont
25
25
  content: string;
26
26
  boardId?: string;
27
27
  }): Promise<Card>;
28
+ /**
29
+ * Creates a comment on a card from a streaming text source.
30
+ *
31
+ * Allocates a comment ID immediately, then reads text chunks from the given
32
+ * `AsyncIterable<string>`. After each chunk the optional `onChunk` callback is
33
+ * invoked so callers can broadcast partial content in real-time. When the
34
+ * iterable is exhausted the complete comment is persisted to storage.
35
+ *
36
+ * @param ctx - SDK context.
37
+ * @param options.cardId - ID of the card to comment on.
38
+ * @param options.author - Display name of the author.
39
+ * @param options.stream - Async iterable that yields text chunks.
40
+ * @param options.boardId - Optional board ID override.
41
+ * @param options.onStart - Called once before iteration with the allocated
42
+ * comment ID and author so the caller can broadcast a stream-start event.
43
+ * @param options.onChunk - Called for each chunk with the comment ID and the
44
+ * raw chunk string so the caller can broadcast partial content.
45
+ * @returns The updated card after the comment has been persisted.
46
+ *
47
+ * @example
48
+ * // Stream an AI response as a comment
49
+ * await sdk.streamComment('42', 'agent', aiTextStream, {
50
+ * onChunk: (commentId, chunk) => broadcastCommentChunk(ctx, cardId, commentId, chunk),
51
+ * })
52
+ */
53
+ export declare function streamComment(ctx: SDKContext, { cardId, author, boardId, stream, onStart, onChunk, }: {
54
+ cardId: string;
55
+ author: string;
56
+ boardId?: string;
57
+ stream: AsyncIterable<string>;
58
+ onStart?: (commentId: string, author: string, created: string) => void;
59
+ onChunk?: (commentId: string, chunk: string) => void;
60
+ }): Promise<Card>;
28
61
  /**
29
62
  * Deletes a comment from a card.
30
63
  */
@@ -99,6 +99,12 @@ export interface Comment {
99
99
  created: string;
100
100
  /** Markdown body of the comment. */
101
101
  content: string;
102
+ /**
103
+ * When `true`, the comment is currently being streamed by an agent and has
104
+ * not yet been fully written. The content field contains whatever has
105
+ * accumulated so far. This field is stripped before persisting to storage.
106
+ */
107
+ streaming?: boolean;
102
108
  }
103
109
  /**
104
110
  * A kanban card with all associated metadata.
@@ -640,6 +646,21 @@ export type ExtensionMessage = {
640
646
  type: 'boardLogsUpdated';
641
647
  boardId: string;
642
648
  logs: import('./types').LogEntry[];
649
+ } | {
650
+ type: 'commentStreamStart';
651
+ cardId: string;
652
+ commentId: string;
653
+ author: string;
654
+ created: string;
655
+ } | {
656
+ type: 'commentChunk';
657
+ cardId: string;
658
+ commentId: string;
659
+ chunk: string;
660
+ } | {
661
+ type: 'commentStreamDone';
662
+ cardId: string;
663
+ commentId: string;
643
664
  };
644
665
  export type WebviewMessage = {
645
666
  type: 'ready';