github-issue-tower-defence-management 2.18.0 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/bin/adapter/entry-points/console/consoleOperationApi.js +12 -1
  3. package/bin/adapter/entry-points/console/consoleOperationApi.js.map +1 -1
  4. package/bin/adapter/entry-points/console/ui-dist/assets/{index-CfqusL5j.js → index-BYDbK1Fj.js} +31 -31
  5. package/bin/adapter/entry-points/console/ui-dist/assets/{index-DshIymWu.css → index-KyQISRuQ.css} +1 -1
  6. package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
  7. package/bin/adapter/entry-points/console/webServer.js +2 -0
  8. package/bin/adapter/entry-points/console/webServer.js.map +1 -1
  9. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +41 -0
  10. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
  11. package/package.json +1 -1
  12. package/src/adapter/entry-points/console/consoleOperationApi.test.ts +30 -0
  13. package/src/adapter/entry-points/console/consoleOperationApi.ts +14 -0
  14. package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +31 -0
  15. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +12 -0
  16. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +1 -0
  17. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.stories.tsx +14 -0
  18. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.test.tsx +40 -0
  19. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.tsx +37 -0
  20. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.stories.tsx +1 -0
  21. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.test.tsx +15 -0
  22. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.tsx +4 -0
  23. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts +11 -0
  24. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +19 -0
  25. package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.ts +9 -1
  26. package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.ts +1 -0
  27. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +67 -0
  28. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +7 -0
  29. package/src/adapter/entry-points/console/ui/src/index.css +6 -0
  30. package/src/adapter/entry-points/console/ui-dist/assets/{index-CfqusL5j.js → index-BYDbK1Fj.js} +31 -31
  31. package/src/adapter/entry-points/console/ui-dist/assets/{index-DshIymWu.css → index-KyQISRuQ.css} +1 -1
  32. package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
  33. package/src/adapter/entry-points/console/webServer.ts +3 -0
  34. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +150 -0
  35. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +70 -0
  36. package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +1 -0
  37. package/types/adapter/entry-points/console/consoleOperationApi.d.ts +1 -0
  38. package/types/adapter/entry-points/console/consoleOperationApi.d.ts.map +1 -1
  39. package/types/adapter/entry-points/console/webServer.d.ts.map +1 -1
  40. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +1 -0
  41. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
  42. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +1 -0
  43. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
@@ -14,6 +14,7 @@ const handlers: ConsoleOperationHandlers = {
14
14
  onSetInTmuxByHuman: jest.fn(),
15
15
  onClose: jest.fn(),
16
16
  onOkAndAwaitingWorkspace: jest.fn(),
17
+ onDeleteAllComments: jest.fn(),
17
18
  };
18
19
 
19
20
  const prItem = consoleListItemsFixture[0];
@@ -141,4 +142,18 @@ describe('ConsoleOperationMenu', () => {
141
142
  );
142
143
  expect(getByText('Reject')).not.toBeDisabled();
143
144
  });
145
+
146
+ it('renders the dangerous actions toggle on all tabs', () => {
147
+ const { getByText } = render(
148
+ <ConsoleOperationMenu
149
+ tab="prs"
150
+ item={prItem}
151
+ hasPullRequest
152
+ rejectEnabled={false}
153
+ statusOptions={consoleStatusOptionsFixture}
154
+ handlers={handlers}
155
+ />,
156
+ );
157
+ expect(getByText('⚠')).toBeInTheDocument();
158
+ });
144
159
  });
@@ -8,6 +8,7 @@ import type {
8
8
  ConsoleTabName,
9
9
  } from '../../logic/types';
10
10
  import { ConsoleCloseActions } from './ConsoleCloseActions';
11
+ import { ConsoleDangerousActions } from './ConsoleDangerousActions';
11
12
  import { ConsoleNextActionDateActions } from './ConsoleNextActionDateActions';
12
13
  import { ConsolePullRequestReviewActions } from './ConsolePullRequestReviewActions';
13
14
  import { ConsoleStatusActions } from './ConsoleStatusActions';
@@ -30,6 +31,9 @@ export const ConsoleOperationMenu = ({
30
31
  }: ConsoleOperationBarProps) => {
31
32
  return (
32
33
  <div className="console-operation-bar">
34
+ <ConsoleDangerousActions
35
+ onDeleteAllComments={handlers.onDeleteAllComments}
36
+ />
33
37
  {hasPullRequest && (
34
38
  <ConsolePullRequestReviewActions
35
39
  onReview={handlers.onReview}
@@ -7,6 +7,7 @@ import {
7
7
  encodeAttachmentContent,
8
8
  postConsoleAttachment,
9
9
  postConsoleComment,
10
+ postConsoleDeleteAllComments,
10
11
  postConsoleOperation,
11
12
  postConsoleReviewComment,
12
13
  } from '../lib/consoleApi';
@@ -74,6 +75,7 @@ export type ConsoleOperationsApi = {
74
75
  side: ConsoleReviewCommentSide,
75
76
  body: string,
76
77
  ) => Promise<void>;
78
+ deleteAllComments: (item: ConsoleListItem) => Promise<void>;
77
79
  };
78
80
 
79
81
  export const reviewRequest = (
@@ -391,6 +393,14 @@ export const useConsoleOperations = (
391
393
  [pjcode],
392
394
  );
393
395
 
396
+ const deleteAllComments = useCallback(
397
+ async (item: ConsoleListItem) => {
398
+ await postConsoleDeleteAllComments({ issueUrl: item.url });
399
+ invalidateItemContent(item);
400
+ },
401
+ [invalidateItemContent],
402
+ );
403
+
394
404
  return {
395
405
  reviewPullRequest,
396
406
  setNextActionDate,
@@ -402,5 +412,6 @@ export const useConsoleOperations = (
402
412
  addComment,
403
413
  uploadAttachment,
404
414
  addInlineReviewComment,
415
+ deleteAllComments,
405
416
  };
406
417
  };
@@ -482,6 +482,25 @@ export const postConsoleReorderStory = async (
482
482
  }
483
483
  };
484
484
 
485
+ export const DELETE_ALL_COMMENTS_OPERATION_PATH = '/api/deleteallcomments';
486
+
487
+ export type ConsoleDeleteAllCommentsRequest = {
488
+ issueUrl: string;
489
+ };
490
+
491
+ export const postConsoleDeleteAllComments = async (
492
+ request: ConsoleDeleteAllCommentsRequest,
493
+ ): Promise<void> => {
494
+ const response = await fetch(DELETE_ALL_COMMENTS_OPERATION_PATH, {
495
+ method: 'POST',
496
+ headers: { 'Content-Type': 'application/json' },
497
+ body: JSON.stringify(request),
498
+ });
499
+ if (!response.ok) {
500
+ throw new Error(await readOperationErrorReason(response));
501
+ }
502
+ };
503
+
485
504
  export type ProjectListResponse = {
486
505
  pjcodes: string[];
487
506
  workflowImprovementIssueUrl: string | null;
@@ -21,7 +21,8 @@ export type ConsoleActionKind =
21
21
  | { type: 'set_status'; optionName: string }
22
22
  | { type: 'set_in_tmux_by_human'; optionName: string }
23
23
  | { type: 'close'; action: ConsoleCloseAction }
24
- | { type: 'ok_and_awaiting_workspace' };
24
+ | { type: 'ok_and_awaiting_workspace' }
25
+ | { type: 'delete_all_comments' };
25
26
 
26
27
  export const ACTION_TOAST_DELAY_MS = 5000;
27
28
 
@@ -67,6 +68,8 @@ export const actionToastMessage = (
67
68
  return 'ok → Awaiting Workspace';
68
69
  case 'close':
69
70
  return kind.action === 'close' ? 'Closed' : 'Closed as not planned';
71
+ case 'delete_all_comments':
72
+ return 'Deleted all comments';
70
73
  }
71
74
  };
72
75
 
@@ -94,6 +97,8 @@ export const actionToastColor = (
94
97
  return 'blue';
95
98
  case 'close':
96
99
  return 'red';
100
+ case 'delete_all_comments':
101
+ return 'red';
97
102
  }
98
103
  };
99
104
 
@@ -104,6 +109,9 @@ export const actionAdvances = (
104
109
  if (kind.type === 'next_action_date') {
105
110
  return isManualTriageTab(tab);
106
111
  }
112
+ if (kind.type === 'delete_all_comments') {
113
+ return false;
114
+ }
107
115
  return true;
108
116
  };
109
117
 
@@ -43,6 +43,7 @@ export type ConsoleOperationHandlers = {
43
43
  onSetInTmuxByHuman: (option: ConsoleFieldOption) => void;
44
44
  onClose: (action: ConsoleCloseAction) => void;
45
45
  onOkAndAwaitingWorkspace: (option: ConsoleFieldOption) => void;
46
+ onDeleteAllComments: () => void;
46
47
  };
47
48
 
48
49
  export const IN_TMUX_BY_HUMAN_NAME = 'In Tmux by human';
@@ -82,6 +82,7 @@ const buildOperations = (): ConsoleOperationsApi => ({
82
82
  })),
83
83
  uploadAttachment: jest.fn(async () => ''),
84
84
  addInlineReviewComment: jest.fn(async () => {}),
85
+ deleteAllComments: jest.fn(async () => {}),
85
86
  });
86
87
 
87
88
  const findCommentsPanelToggle = (container: HTMLElement): HTMLElement => {
@@ -559,4 +560,70 @@ describe('ConsoleItemDetailContainer', () => {
559
560
  ]);
560
561
  expect(reviewCall?.[3][0].body).not.toBe('');
561
562
  });
563
+
564
+ it('routes delete-all-comments through onQueueAction with the correct kind and commit', async () => {
565
+ const operations = buildOperations();
566
+ const onQueueAction = jest.fn();
567
+ const { getByText } = render(
568
+ <ConsoleItemDetailContainer
569
+ tab="todo-by-agent"
570
+ item={issueItem}
571
+ caches={buildCaches()}
572
+ operations={operations}
573
+ statusOptions={consoleStatusOptionsFixture}
574
+ storyColors={consoleStoryColorsFixture}
575
+ storyName="TDPM Console port"
576
+ overlayStatus={null}
577
+ now={Date.parse('2026-06-19T12:00:00.000Z')}
578
+ onQueueAction={onQueueAction}
579
+ />,
580
+ );
581
+
582
+ await waitFor(() => {
583
+ expect(getByText('⚠')).toBeInTheDocument();
584
+ });
585
+
586
+ fireEvent.click(getByText('⚠'));
587
+ fireEvent.click(getByText('Delete All Comments'));
588
+
589
+ expect(onQueueAction).toHaveBeenCalledTimes(1);
590
+ const input = onQueueAction.mock.calls[0][0];
591
+ expect(input.kind).toEqual({ type: 'delete_all_comments' });
592
+ expect(input.item).toBe(issueItem);
593
+ expect(operations.deleteAllComments).not.toHaveBeenCalled();
594
+
595
+ await input.commit();
596
+ expect(operations.deleteAllComments).toHaveBeenCalledWith(issueItem);
597
+ });
598
+
599
+ it('surfaces errors from deleteAllComments via the queue rather than swallowing them', async () => {
600
+ const operations = buildOperations();
601
+ const error = new Error('GitHub API failure');
602
+ (operations.deleteAllComments as jest.Mock).mockRejectedValue(error);
603
+ const onQueueAction = jest.fn();
604
+ const { getByText } = render(
605
+ <ConsoleItemDetailContainer
606
+ tab="todo-by-agent"
607
+ item={issueItem}
608
+ caches={buildCaches()}
609
+ operations={operations}
610
+ statusOptions={consoleStatusOptionsFixture}
611
+ storyColors={consoleStoryColorsFixture}
612
+ storyName="TDPM Console port"
613
+ overlayStatus={null}
614
+ now={Date.parse('2026-06-19T12:00:00.000Z')}
615
+ onQueueAction={onQueueAction}
616
+ />,
617
+ );
618
+
619
+ await waitFor(() => {
620
+ expect(getByText('⚠')).toBeInTheDocument();
621
+ });
622
+
623
+ fireEvent.click(getByText('⚠'));
624
+ fireEvent.click(getByText('Delete All Comments'));
625
+
626
+ const input = onQueueAction.mock.calls[0][0];
627
+ await expect(input.commit()).rejects.toThrow('GitHub API failure');
628
+ });
562
629
  });
@@ -262,6 +262,13 @@ export const ConsoleItemDetailContainer = ({
262
262
  commit: () => operations.okAndMoveToAwaitingWorkspace(item, option),
263
263
  });
264
264
  },
265
+ onDeleteAllComments: () => {
266
+ onQueueAction({
267
+ kind: { type: 'delete_all_comments' },
268
+ item,
269
+ commit: () => operations.deleteAllComments(item),
270
+ });
271
+ },
265
272
  };
266
273
 
267
274
  const awaitingWorkspaceOption =
@@ -1133,6 +1133,12 @@ body {
1133
1133
  color: #79c0ff;
1134
1134
  }
1135
1135
 
1136
+ .console-op-button-danger {
1137
+ background: #3a1518;
1138
+ border-color: #f85149;
1139
+ color: #f85149;
1140
+ }
1141
+
1136
1142
  .console-pr-section {
1137
1143
  border: 1px solid #30363d;
1138
1144
  border-radius: 8px;