kanban-lite 1.2.13 → 1.2.15
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/dist/cli.js +226 -2
- package/dist/extension.js +143 -1
- package/dist/mcp-server.js +140 -1
- package/dist/sdk/index.cjs +91 -1
- package/dist/sdk/index.mjs +91 -1
- package/dist/sdk/sdk/KanbanSDK.d.ts +38 -0
- package/dist/sdk/sdk/modules/comments.d.ts +33 -0
- package/dist/sdk/shared/types.d.ts +21 -0
- package/dist/standalone-webview/index.js +14 -14
- package/dist/standalone-webview/index.js.map +1 -1
- package/dist/standalone-webview/style.css +1 -1
- package/dist/standalone.js +143 -1
- package/package.json +1 -1
- package/src/cli/index.ts +33 -1
- package/src/mcp-server/index.ts +52 -0
- package/src/sdk/KanbanSDK.ts +51 -1
- package/src/sdk/modules/comments.ts +91 -0
- package/src/shared/types.ts +9 -0
- package/src/standalone/broadcastService.ts +40 -0
- package/src/standalone/internal/routes/tasks.ts +45 -1
- package/src/webview/App.tsx +31 -0
- package/src/webview/assets/main.css +42 -0
- package/src/webview/components/CommentsSection.tsx +28 -20
package/dist/sdk/index.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
*
|
package/dist/sdk/index.mjs
CHANGED
|
@@ -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
|
-
|
|
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';
|