github-issue-tower-defence-management 1.105.0 → 1.106.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 (33) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +13 -0
  3. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  4. package/bin/adapter/entry-points/handlers/staleTmuxSessionCleaner.js +18 -0
  5. package/bin/adapter/entry-points/handlers/staleTmuxSessionCleaner.js.map +1 -0
  6. package/bin/adapter/repositories/NodeTmuxSessionRepository.js +22 -0
  7. package/bin/adapter/repositories/NodeTmuxSessionRepository.js.map +1 -1
  8. package/bin/domain/entities/LiveTmuxSession.js +3 -0
  9. package/bin/domain/entities/LiveTmuxSession.js.map +1 -0
  10. package/bin/domain/usecases/StaleTmuxSessionKillUseCase.js +58 -0
  11. package/bin/domain/usecases/StaleTmuxSessionKillUseCase.js.map +1 -0
  12. package/package.json +1 -1
  13. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +17 -0
  14. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.test.ts +170 -0
  15. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts +40 -0
  16. package/src/adapter/repositories/NodeTmuxSessionRepository.test.ts +81 -0
  17. package/src/adapter/repositories/NodeTmuxSessionRepository.ts +35 -0
  18. package/src/domain/entities/LiveTmuxSession.ts +4 -0
  19. package/src/domain/usecases/StaleTmuxSessionKillUseCase.test.ts +286 -0
  20. package/src/domain/usecases/StaleTmuxSessionKillUseCase.ts +102 -0
  21. package/src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts +4 -0
  22. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +6 -0
  23. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  24. package/types/adapter/entry-points/handlers/staleTmuxSessionCleaner.d.ts +12 -0
  25. package/types/adapter/entry-points/handlers/staleTmuxSessionCleaner.d.ts.map +1 -0
  26. package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts +3 -0
  27. package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts.map +1 -1
  28. package/types/domain/entities/LiveTmuxSession.d.ts +5 -0
  29. package/types/domain/entities/LiveTmuxSession.d.ts.map +1 -0
  30. package/types/domain/usecases/StaleTmuxSessionKillUseCase.d.ts +19 -0
  31. package/types/domain/usecases/StaleTmuxSessionKillUseCase.d.ts.map +1 -0
  32. package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts +3 -0
  33. package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts.map +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.106.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.105.0...v1.106.0) (2026-06-26)
2
+
3
+
4
+ ### Features
5
+
6
+ * **schedule:** run stale tmux session cleanup in the scheduled cycle ([#1001](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1001)) ([d6c9759](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/d6c975999c9ccee48fc743149e440d4c2e80e7b3))
7
+
1
8
  # [1.105.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.104.4...v1.105.0) (2026-06-26)
2
9
 
3
10
 
@@ -49,6 +49,7 @@ const machineStatusWriter_1 = require("./machineStatusWriter");
49
49
  const tokenStatusWriter_1 = require("./tokenStatusWriter");
50
50
  const inTmuxByHumanDataWriter_1 = require("./inTmuxByHumanDataWriter");
51
51
  const inTmuxByHumanSessionReconciler_1 = require("./inTmuxByHumanSessionReconciler");
52
+ const staleTmuxSessionCleaner_1 = require("./staleTmuxSessionCleaner");
52
53
  const rotationOrderFileWriter_1 = require("./rotationOrderFileWriter");
53
54
  const projectConfig_1 = require("../cli/projectConfig");
54
55
  const SystemDateRepository_1 = require("../../repositories/SystemDateRepository");
@@ -669,6 +670,18 @@ class HandleScheduledEventUseCaseHandler {
669
670
  catch (error) {
670
671
  console.error(`Failed to reconcile in-tmux-by-human sessions: ${error instanceof Error ? error.message : String(error)}`);
671
672
  }
673
+ try {
674
+ await (0, staleTmuxSessionCleaner_1.cleanStaleTmuxSessions)({
675
+ project: result.project,
676
+ allowCacheMinutes: mergedInput.allowIssueCacheMinutes,
677
+ issueRepository,
678
+ localCommandRunner: nodeLocalCommandRunner,
679
+ now: inTmuxNow,
680
+ });
681
+ }
682
+ catch (error) {
683
+ console.error(`Failed to clean stale tmux sessions: ${error instanceof Error ? error.message : String(error)}`);
684
+ }
672
685
  }
673
686
  return result;
674
687
  };
@@ -1 +1 @@
1
- {"version":3,"file":"HandleScheduledEventUseCaseHandler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"HandleScheduledEventUseCaseHandler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cleanStaleTmuxSessions = void 0;
4
+ const StaleTmuxSessionKillUseCase_1 = require("../../../domain/usecases/StaleTmuxSessionKillUseCase");
5
+ const NodeTmuxSessionRepository_1 = require("../../repositories/NodeTmuxSessionRepository");
6
+ const cleanStaleTmuxSessions = async (params) => {
7
+ const { project, allowCacheMinutes, issueRepository, localCommandRunner, now, } = params;
8
+ const useCase = new StaleTmuxSessionKillUseCase_1.StaleTmuxSessionKillUseCase(issueRepository, new NodeTmuxSessionRepository_1.NodeTmuxSessionRepository(localCommandRunner));
9
+ await useCase.run({
10
+ project,
11
+ allowCacheMinutes,
12
+ excludedStatus: StaleTmuxSessionKillUseCase_1.DEFAULT_EXCLUDED_STATUS,
13
+ idleThresholdSeconds: StaleTmuxSessionKillUseCase_1.DEFAULT_IDLE_THRESHOLD_SECONDS,
14
+ now,
15
+ });
16
+ };
17
+ exports.cleanStaleTmuxSessions = cleanStaleTmuxSessions;
18
+ //# sourceMappingURL=staleTmuxSessionCleaner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staleTmuxSessionCleaner.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts"],"names":[],"mappings":";;;AAGA,sGAI8D;AAC9D,4FAAyF;AAUlF,MAAM,sBAAsB,GAAG,KAAK,EACzC,MAAoC,EACrB,EAAE;IACjB,MAAM,EACJ,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,GAAG,GACJ,GAAG,MAAM,CAAC;IACX,MAAM,OAAO,GAAG,IAAI,yDAA2B,CAC7C,eAAe,EACf,IAAI,qDAAyB,CAAC,kBAAkB,CAAC,CAClD,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,OAAO;QACP,iBAAiB;QACjB,cAAc,EAAE,qDAAuB;QACvC,oBAAoB,EAAE,4DAA8B;QACpD,GAAG;KACJ,CAAC,CAAC;AACL,CAAC,CAAC;AArBW,QAAA,sBAAsB,0BAqBjC"}
@@ -14,6 +14,22 @@ class NodeTmuxSessionRepository {
14
14
  .map((line) => line.trim())
15
15
  .filter((line) => line.length > 0);
16
16
  };
17
+ this.listLiveSessionsWithActivity = async () => {
18
+ const { stdout, exitCode } = await this.localCommandRunner.runCommand('tmux', ['list-sessions', '-F', '#{session_name} #{session_activity}']);
19
+ if (exitCode !== 0) {
20
+ return [];
21
+ }
22
+ return stdout
23
+ .split('\n')
24
+ .map((line) => line.trim())
25
+ .filter((line) => line.length > 0)
26
+ .map((line) => {
27
+ const separatorIndex = line.lastIndexOf(' ');
28
+ const sessionName = line.slice(0, separatorIndex);
29
+ const activityEpochSeconds = Number(line.slice(separatorIndex + 1));
30
+ return { sessionName, activityEpochSeconds };
31
+ });
32
+ };
17
33
  this.listInteractiveProcessCommandLines = async () => {
18
34
  const { stdout, exitCode } = await this.localCommandRunner.runCommand('ps', ['-eo', 'args=']);
19
35
  if (exitCode !== 0) {
@@ -39,6 +55,12 @@ class NodeTmuxSessionRepository {
39
55
  issueUrl,
40
56
  ]);
41
57
  };
58
+ this.killSession = async (sessionName) => {
59
+ const { stderr, exitCode } = await this.localCommandRunner.runCommand('tmux', ['kill-session', '-t', sessionName]);
60
+ if (exitCode !== 0) {
61
+ throw new Error(`Failed to kill tmux session "${sessionName}": exit code ${exitCode}${stderr ? `: ${stderr}` : ''}`);
62
+ }
63
+ };
42
64
  }
43
65
  }
44
66
  exports.NodeTmuxSessionRepository = NodeTmuxSessionRepository;
@@ -1 +1 @@
1
- {"version":3,"file":"NodeTmuxSessionRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":";;;AAGA,MAAa,yBAAyB;IACpC,YAA6B,kBAAsC;QAAtC,uBAAkB,GAAlB,kBAAkB,CAAoB;QAEnE,yBAAoB,GAAG,KAAK,IAAuB,EAAE;YACnD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,UAAU,CACnE,MAAM,EACN,CAAC,eAAe,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAC3C,CAAC;YACF,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;gBACnB,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,OAAO,MAAM;iBACV,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;iBAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC;QAEF,uCAAkC,GAAG,KAAK,IAAuB,EAAE;YACjE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,UAAU,CACnE,IAAI,EACJ,CAAC,KAAK,EAAE,OAAO,CAAC,CACjB,CAAC;YACF,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;gBACnB,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,OAAO,MAAM;iBACV,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;iBAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC;QAEF,0BAAqB,GAAG,KAAK,EAC3B,WAAmB,EACnB,eAAuB,EACvB,QAAgB,EACD,EAAE;YACjB,MAAM,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,MAAM,EAAE;gBAC/C,aAAa;gBACb,IAAI;gBACJ,IAAI;gBACJ,IAAI;gBACJ,WAAW;gBACX,IAAI;gBACJ,KAAK;gBACL,gBAAgB;gBAChB,IAAI;gBACJ,eAAe;gBACf,QAAQ;aACT,CAAC,CAAC;QACL,CAAC,CAAC;IAhDoE,CAAC;CAiDxE;AAlDD,8DAkDC"}
1
+ {"version":3,"file":"NodeTmuxSessionRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=LiveTmuxSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LiveTmuxSession.js","sourceRoot":"","sources":["../../../src/domain/entities/LiveTmuxSession.ts"],"names":[],"mappings":""}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StaleTmuxSessionKillUseCase = exports.DEFAULT_IDLE_THRESHOLD_SECONDS = exports.DEFAULT_EXCLUDED_STATUS = void 0;
4
+ const WorkflowStatus_1 = require("../entities/WorkflowStatus");
5
+ const InTmuxByHumanSessionReconcileUseCase_1 = require("./intmux/InTmuxByHumanSessionReconcileUseCase");
6
+ exports.DEFAULT_EXCLUDED_STATUS = WorkflowStatus_1.IN_TMUX_STATUS_NAME;
7
+ exports.DEFAULT_IDLE_THRESHOLD_SECONDS = 24 * 60 * 60;
8
+ class StaleTmuxSessionKillUseCase {
9
+ constructor(issueRepository, tmuxSessionRepository) {
10
+ this.issueRepository = issueRepository;
11
+ this.tmuxSessionRepository = tmuxSessionRepository;
12
+ this.run = async (params) => {
13
+ const liveSessions = await this.tmuxSessionRepository.listLiveSessionsWithActivity();
14
+ const openIssues = await this.issueRepository.getAllOpened(params.project, params.allowCacheMinutes);
15
+ const issueBySessionName = new Map();
16
+ for (const issue of openIssues) {
17
+ issueBySessionName.set((0, InTmuxByHumanSessionReconcileUseCase_1.toTmuxSessionName)(issue.url), issue);
18
+ }
19
+ const nowEpochSeconds = Math.floor(params.now.getTime() / 1000);
20
+ const killCandidates = [];
21
+ for (const session of liveSessions) {
22
+ const reason = this.evaluateKillReason(session, issueBySessionName.get(session.sessionName) ?? null, nowEpochSeconds, params.excludedStatus, params.idleThresholdSeconds);
23
+ if (reason !== null) {
24
+ killCandidates.push({ sessionName: session.sessionName, reason });
25
+ }
26
+ }
27
+ console.log(`Stale tmux session cleanup: ${killCandidates.length} kill candidate(s) of ${liveSessions.length} live session(s).`);
28
+ for (const candidate of killCandidates) {
29
+ console.log(`Kill candidate: ${candidate.sessionName} (${candidate.reason})`);
30
+ }
31
+ for (const candidate of killCandidates) {
32
+ await this.tmuxSessionRepository.killSession(candidate.sessionName);
33
+ console.log(`Killed tmux session: ${candidate.sessionName} (${candidate.reason})`);
34
+ }
35
+ };
36
+ this.evaluateKillReason = (session, issue, nowEpochSeconds, excludedStatus, idleThresholdSeconds) => {
37
+ if (issue !== null) {
38
+ if (issue.status !== excludedStatus) {
39
+ return `mapped to open issue ${issue.url} with status "${issue.status ?? 'null'}" which is not the excluded status "${excludedStatus}"`;
40
+ }
41
+ if (issue.nextActionDate !== null) {
42
+ return `mapped to open issue ${issue.url} which has a next action date set`;
43
+ }
44
+ if (issue.nextActionHour !== null) {
45
+ return `mapped to open issue ${issue.url} which has a next action hour set`;
46
+ }
47
+ return null;
48
+ }
49
+ const idleSeconds = nowEpochSeconds - session.activityEpochSeconds;
50
+ if (idleSeconds >= idleThresholdSeconds) {
51
+ return `maps to no open issue and has been idle for ${idleSeconds} seconds (threshold ${idleThresholdSeconds} seconds)`;
52
+ }
53
+ return null;
54
+ };
55
+ }
56
+ }
57
+ exports.StaleTmuxSessionKillUseCase = StaleTmuxSessionKillUseCase;
58
+ //# sourceMappingURL=StaleTmuxSessionKillUseCase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StaleTmuxSessionKillUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/StaleTmuxSessionKillUseCase.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.105.0",
3
+ "version": "1.106.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -8,6 +8,7 @@ import { writeMachineStatus } from './machineStatusWriter';
8
8
  import { writeTokenStatus } from './tokenStatusWriter';
9
9
  import { writeInTmuxByHumanData } from './inTmuxByHumanDataWriter';
10
10
  import { reconcileInTmuxByHumanSessions } from './inTmuxByHumanSessionReconciler';
11
+ import { cleanStaleTmuxSessions } from './staleTmuxSessionCleaner';
11
12
  import { writeRotationOrderFile } from './rotationOrderFileWriter';
12
13
  import {
13
14
  fetchProjectReadme,
@@ -507,6 +508,22 @@ export class HandleScheduledEventUseCaseHandler {
507
508
  }`,
508
509
  );
509
510
  }
511
+
512
+ try {
513
+ await cleanStaleTmuxSessions({
514
+ project: result.project,
515
+ allowCacheMinutes: mergedInput.allowIssueCacheMinutes,
516
+ issueRepository,
517
+ localCommandRunner: nodeLocalCommandRunner,
518
+ now: inTmuxNow,
519
+ });
520
+ } catch (error) {
521
+ console.error(
522
+ `Failed to clean stale tmux sessions: ${
523
+ error instanceof Error ? error.message : String(error)
524
+ }`,
525
+ );
526
+ }
510
527
  }
511
528
  return result;
512
529
  };
@@ -0,0 +1,170 @@
1
+ import { Issue } from '../../../domain/entities/Issue';
2
+ import { Project } from '../../../domain/entities/Project';
3
+ import { LocalCommandRunner } from '../../../domain/usecases/adapter-interfaces/LocalCommandRunner';
4
+ import { IssueRepository } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
5
+ import { IN_TMUX_STATUS_NAME } from '../../../domain/entities/WorkflowStatus';
6
+ import { cleanStaleTmuxSessions } from './staleTmuxSessionCleaner';
7
+
8
+ const NOW = new Date('2026-06-26T00:00:00.000Z');
9
+ const NOW_EPOCH_SECONDS = Math.floor(NOW.getTime() / 1000);
10
+ const ALLOW_CACHE_MINUTES = 10;
11
+
12
+ const makeProject = (): Project => ({
13
+ id: 'project-1',
14
+ url: 'https://github.com/users/user/projects/1',
15
+ databaseId: 1,
16
+ name: 'Test Project',
17
+ status: {
18
+ name: 'Status',
19
+ fieldId: 'field-1',
20
+ statuses: [],
21
+ },
22
+ nextActionDate: null,
23
+ nextActionHour: null,
24
+ story: null,
25
+ remainingEstimationMinutes: null,
26
+ dependedIssueUrlSeparatedByComma: null,
27
+ completionDate50PercentConfidence: null,
28
+ });
29
+
30
+ const makeIssue = (overrides: Partial<Issue> = {}): Issue => ({
31
+ nameWithOwner: 'demo/repo',
32
+ number: 1,
33
+ title: 'Issue 1',
34
+ state: 'OPEN',
35
+ status: IN_TMUX_STATUS_NAME,
36
+ story: null,
37
+ nextActionDate: null,
38
+ nextActionHour: null,
39
+ estimationMinutes: null,
40
+ dependedIssueUrls: [],
41
+ completionDate50PercentConfidence: null,
42
+ url: 'https://github.com/demo/repo/issues/1',
43
+ assignees: [],
44
+ labels: [],
45
+ org: 'demo',
46
+ repo: 'repo',
47
+ body: '',
48
+ itemId: 'item-1',
49
+ isPr: false,
50
+ isInProgress: false,
51
+ isClosed: false,
52
+ createdAt: new Date(),
53
+ author: '',
54
+ closingIssueReferenceUrls: [],
55
+ ...overrides,
56
+ });
57
+
58
+ type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
59
+
60
+ const createMockRunner = (): Mocked<LocalCommandRunner> => ({
61
+ runCommand: jest.fn(),
62
+ });
63
+
64
+ const createMockIssueRepository = (
65
+ issues: Issue[],
66
+ ): Pick<IssueRepository, 'getAllOpened'> => ({
67
+ getAllOpened: jest.fn().mockResolvedValue(issues),
68
+ });
69
+
70
+ describe('cleanStaleTmuxSessions', () => {
71
+ beforeEach(() => {
72
+ jest.spyOn(console, 'log').mockImplementation(() => undefined);
73
+ });
74
+
75
+ afterEach(() => {
76
+ jest.restoreAllMocks();
77
+ });
78
+
79
+ it('kills a session mapping to an open issue whose status is not the excluded status', async () => {
80
+ const runner = createMockRunner();
81
+ runner.runCommand.mockImplementation(async (program, args) => {
82
+ if (program === 'tmux' && args[0] === 'list-sessions') {
83
+ return {
84
+ stdout: `https_//github_com/demo/repo/issues/1 ${NOW_EPOCH_SECONDS}\n`,
85
+ stderr: '',
86
+ exitCode: 0,
87
+ };
88
+ }
89
+ return { stdout: '', stderr: '', exitCode: 0 };
90
+ });
91
+
92
+ await cleanStaleTmuxSessions({
93
+ project: makeProject(),
94
+ allowCacheMinutes: ALLOW_CACHE_MINUTES,
95
+ issueRepository: createMockIssueRepository([
96
+ makeIssue({ status: 'In Progress' }),
97
+ ]),
98
+ localCommandRunner: runner,
99
+ now: NOW,
100
+ });
101
+
102
+ const killCall = runner.runCommand.mock.calls.find(
103
+ (call) => call[0] === 'tmux' && call[1][0] === 'kill-session',
104
+ );
105
+ expect(killCall?.[1]).toEqual([
106
+ 'kill-session',
107
+ '-t',
108
+ 'https_//github_com/demo/repo/issues/1',
109
+ ]);
110
+ });
111
+
112
+ it('does not kill an excluded-status session that has no reactivation trigger', async () => {
113
+ const runner = createMockRunner();
114
+ runner.runCommand.mockImplementation(async (program, args) => {
115
+ if (program === 'tmux' && args[0] === 'list-sessions') {
116
+ return {
117
+ stdout: `https_//github_com/demo/repo/issues/1 ${NOW_EPOCH_SECONDS}\n`,
118
+ stderr: '',
119
+ exitCode: 0,
120
+ };
121
+ }
122
+ return { stdout: '', stderr: '', exitCode: 0 };
123
+ });
124
+
125
+ await cleanStaleTmuxSessions({
126
+ project: makeProject(),
127
+ allowCacheMinutes: ALLOW_CACHE_MINUTES,
128
+ issueRepository: createMockIssueRepository([
129
+ makeIssue({ status: IN_TMUX_STATUS_NAME }),
130
+ ]),
131
+ localCommandRunner: runner,
132
+ now: NOW,
133
+ });
134
+
135
+ const killCalls = runner.runCommand.mock.calls.filter(
136
+ (call) => call[0] === 'tmux' && call[1][0] === 'kill-session',
137
+ );
138
+ expect(killCalls).toHaveLength(0);
139
+ });
140
+
141
+ it('kills a no-task session idle at least 24 hours and spares a recently active one', async () => {
142
+ const runner = createMockRunner();
143
+ const idleActivity = NOW_EPOCH_SECONDS - 24 * 60 * 60;
144
+ const recentActivity = NOW_EPOCH_SECONDS - 24 * 60 * 60 + 1;
145
+ runner.runCommand.mockImplementation(async (program, args) => {
146
+ if (program === 'tmux' && args[0] === 'list-sessions') {
147
+ return {
148
+ stdout: `idle_no_task ${idleActivity}\nrecent_no_task ${recentActivity}\n`,
149
+ stderr: '',
150
+ exitCode: 0,
151
+ };
152
+ }
153
+ return { stdout: '', stderr: '', exitCode: 0 };
154
+ });
155
+
156
+ await cleanStaleTmuxSessions({
157
+ project: makeProject(),
158
+ allowCacheMinutes: ALLOW_CACHE_MINUTES,
159
+ issueRepository: createMockIssueRepository([]),
160
+ localCommandRunner: runner,
161
+ now: NOW,
162
+ });
163
+
164
+ const killCalls = runner.runCommand.mock.calls.filter(
165
+ (call) => call[0] === 'tmux' && call[1][0] === 'kill-session',
166
+ );
167
+ expect(killCalls).toHaveLength(1);
168
+ expect(killCalls[0][1]).toEqual(['kill-session', '-t', 'idle_no_task']);
169
+ });
170
+ });
@@ -0,0 +1,40 @@
1
+ import { Project } from '../../../domain/entities/Project';
2
+ import { LocalCommandRunner } from '../../../domain/usecases/adapter-interfaces/LocalCommandRunner';
3
+ import { IssueRepository } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
4
+ import {
5
+ StaleTmuxSessionKillUseCase,
6
+ DEFAULT_EXCLUDED_STATUS,
7
+ DEFAULT_IDLE_THRESHOLD_SECONDS,
8
+ } from '../../../domain/usecases/StaleTmuxSessionKillUseCase';
9
+ import { NodeTmuxSessionRepository } from '../../repositories/NodeTmuxSessionRepository';
10
+
11
+ export type CleanStaleTmuxSessionsParams = {
12
+ project: Project;
13
+ allowCacheMinutes: number;
14
+ issueRepository: Pick<IssueRepository, 'getAllOpened'>;
15
+ localCommandRunner: LocalCommandRunner;
16
+ now: Date;
17
+ };
18
+
19
+ export const cleanStaleTmuxSessions = async (
20
+ params: CleanStaleTmuxSessionsParams,
21
+ ): Promise<void> => {
22
+ const {
23
+ project,
24
+ allowCacheMinutes,
25
+ issueRepository,
26
+ localCommandRunner,
27
+ now,
28
+ } = params;
29
+ const useCase = new StaleTmuxSessionKillUseCase(
30
+ issueRepository,
31
+ new NodeTmuxSessionRepository(localCommandRunner),
32
+ );
33
+ await useCase.run({
34
+ project,
35
+ allowCacheMinutes,
36
+ excludedStatus: DEFAULT_EXCLUDED_STATUS,
37
+ idleThresholdSeconds: DEFAULT_IDLE_THRESHOLD_SECONDS,
38
+ now,
39
+ });
40
+ };
@@ -44,6 +44,87 @@ describe('NodeTmuxSessionRepository', () => {
44
44
  });
45
45
  });
46
46
 
47
+ describe('listLiveSessionsWithActivity', () => {
48
+ it('parses session names and activity epoch seconds and drops blank lines', async () => {
49
+ const runner = createMockRunner();
50
+ runner.runCommand.mockResolvedValue({
51
+ stdout:
52
+ 'https_//github_com/owner/repo/issues/9 1700000000\nno_task_session 1699000000\n\n',
53
+ stderr: '',
54
+ exitCode: 0,
55
+ });
56
+ const repository = new NodeTmuxSessionRepository(runner);
57
+
58
+ const result = await repository.listLiveSessionsWithActivity();
59
+
60
+ expect(result).toEqual([
61
+ {
62
+ sessionName: 'https_//github_com/owner/repo/issues/9',
63
+ activityEpochSeconds: 1700000000,
64
+ },
65
+ {
66
+ sessionName: 'no_task_session',
67
+ activityEpochSeconds: 1699000000,
68
+ },
69
+ ]);
70
+ expect(runner.runCommand.mock.calls[0][0]).toBe('tmux');
71
+ expect(runner.runCommand.mock.calls[0][1]).toEqual([
72
+ 'list-sessions',
73
+ '-F',
74
+ '#{session_name} #{session_activity}',
75
+ ]);
76
+ });
77
+
78
+ it('returns an empty list when tmux exits non-zero', async () => {
79
+ const runner = createMockRunner();
80
+ runner.runCommand.mockResolvedValue({
81
+ stdout: '',
82
+ stderr: 'no server running',
83
+ exitCode: 1,
84
+ });
85
+ const repository = new NodeTmuxSessionRepository(runner);
86
+
87
+ const result = await repository.listLiveSessionsWithActivity();
88
+
89
+ expect(result).toEqual([]);
90
+ });
91
+ });
92
+
93
+ describe('killSession', () => {
94
+ it('kills the tmux session by name', async () => {
95
+ const runner = createMockRunner();
96
+ runner.runCommand.mockResolvedValue({
97
+ stdout: '',
98
+ stderr: '',
99
+ exitCode: 0,
100
+ });
101
+ const repository = new NodeTmuxSessionRepository(runner);
102
+
103
+ await repository.killSession('no_task_session');
104
+
105
+ expect(runner.runCommand.mock.calls[0][0]).toBe('tmux');
106
+ expect(runner.runCommand.mock.calls[0][1]).toEqual([
107
+ 'kill-session',
108
+ '-t',
109
+ 'no_task_session',
110
+ ]);
111
+ });
112
+
113
+ it('throws when tmux exits non-zero', async () => {
114
+ const runner = createMockRunner();
115
+ runner.runCommand.mockResolvedValue({
116
+ stdout: '',
117
+ stderr: "can't find session",
118
+ exitCode: 1,
119
+ });
120
+ const repository = new NodeTmuxSessionRepository(runner);
121
+
122
+ await expect(repository.killSession('missing_session')).rejects.toThrow(
123
+ 'Failed to kill tmux session "missing_session": exit code 1',
124
+ );
125
+ });
126
+ });
127
+
47
128
  describe('listInteractiveProcessCommandLines', () => {
48
129
  it('parses process command lines from ps output', async () => {
49
130
  const runner = createMockRunner();
@@ -1,5 +1,6 @@
1
1
  import { LocalCommandRunner } from '../../domain/usecases/adapter-interfaces/LocalCommandRunner';
2
2
  import { TmuxSessionRepository } from '../../domain/usecases/adapter-interfaces/TmuxSessionRepository';
3
+ import { LiveTmuxSession } from '../../domain/entities/LiveTmuxSession';
3
4
 
4
5
  export class NodeTmuxSessionRepository implements TmuxSessionRepository {
5
6
  constructor(private readonly localCommandRunner: LocalCommandRunner) {}
@@ -18,6 +19,26 @@ export class NodeTmuxSessionRepository implements TmuxSessionRepository {
18
19
  .filter((line) => line.length > 0);
19
20
  };
20
21
 
22
+ listLiveSessionsWithActivity = async (): Promise<LiveTmuxSession[]> => {
23
+ const { stdout, exitCode } = await this.localCommandRunner.runCommand(
24
+ 'tmux',
25
+ ['list-sessions', '-F', '#{session_name} #{session_activity}'],
26
+ );
27
+ if (exitCode !== 0) {
28
+ return [];
29
+ }
30
+ return stdout
31
+ .split('\n')
32
+ .map((line) => line.trim())
33
+ .filter((line) => line.length > 0)
34
+ .map((line) => {
35
+ const separatorIndex = line.lastIndexOf(' ');
36
+ const sessionName = line.slice(0, separatorIndex);
37
+ const activityEpochSeconds = Number(line.slice(separatorIndex + 1));
38
+ return { sessionName, activityEpochSeconds };
39
+ });
40
+ };
41
+
21
42
  listInteractiveProcessCommandLines = async (): Promise<string[]> => {
22
43
  const { stdout, exitCode } = await this.localCommandRunner.runCommand(
23
44
  'ps',
@@ -51,4 +72,18 @@ export class NodeTmuxSessionRepository implements TmuxSessionRepository {
51
72
  issueUrl,
52
73
  ]);
53
74
  };
75
+
76
+ killSession = async (sessionName: string): Promise<void> => {
77
+ const { stderr, exitCode } = await this.localCommandRunner.runCommand(
78
+ 'tmux',
79
+ ['kill-session', '-t', sessionName],
80
+ );
81
+ if (exitCode !== 0) {
82
+ throw new Error(
83
+ `Failed to kill tmux session "${sessionName}": exit code ${exitCode}${
84
+ stderr ? `: ${stderr}` : ''
85
+ }`,
86
+ );
87
+ }
88
+ };
54
89
  }
@@ -0,0 +1,4 @@
1
+ export type LiveTmuxSession = {
2
+ sessionName: string;
3
+ activityEpochSeconds: number;
4
+ };
@@ -0,0 +1,286 @@
1
+ import {
2
+ StaleTmuxSessionKillUseCase,
3
+ DEFAULT_EXCLUDED_STATUS,
4
+ DEFAULT_IDLE_THRESHOLD_SECONDS,
5
+ } from './StaleTmuxSessionKillUseCase';
6
+ import { IssueRepository } from './adapter-interfaces/IssueRepository';
7
+ import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
8
+ import { toTmuxSessionName } from './intmux/InTmuxByHumanSessionReconcileUseCase';
9
+ import { Issue } from '../entities/Issue';
10
+ import { LiveTmuxSession } from '../entities/LiveTmuxSession';
11
+ import { Project } from '../entities/Project';
12
+ import { IN_TMUX_STATUS_NAME } from '../entities/WorkflowStatus';
13
+
14
+ type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
15
+
16
+ const createMockProject = (): Project => ({
17
+ id: 'project-1',
18
+ url: 'https://github.com/users/user/projects/1',
19
+ databaseId: 1,
20
+ name: 'Test Project',
21
+ status: {
22
+ name: 'Status',
23
+ fieldId: 'field-1',
24
+ statuses: [],
25
+ },
26
+ nextActionDate: null,
27
+ nextActionHour: null,
28
+ story: null,
29
+ remainingEstimationMinutes: null,
30
+ dependedIssueUrlSeparatedByComma: null,
31
+ completionDate50PercentConfidence: null,
32
+ });
33
+
34
+ let issueCounter = 0;
35
+ const createMockIssue = (overrides: Partial<Issue> = {}): Issue => {
36
+ issueCounter += 1;
37
+ return {
38
+ nameWithOwner: 'user/repo',
39
+ number: issueCounter,
40
+ title: `Test Issue ${issueCounter}`,
41
+ state: 'OPEN',
42
+ status: IN_TMUX_STATUS_NAME,
43
+ story: null,
44
+ nextActionDate: null,
45
+ nextActionHour: null,
46
+ estimationMinutes: null,
47
+ dependedIssueUrls: [],
48
+ completionDate50PercentConfidence: null,
49
+ url: `https://github.com/user/repo/issues/${issueCounter}`,
50
+ assignees: [],
51
+ labels: [],
52
+ org: 'user',
53
+ repo: 'repo',
54
+ body: '',
55
+ itemId: `item-${issueCounter}`,
56
+ isPr: false,
57
+ isInProgress: false,
58
+ isClosed: false,
59
+ createdAt: new Date(),
60
+ author: 'testuser',
61
+ closingIssueReferenceUrls: [],
62
+ ...overrides,
63
+ };
64
+ };
65
+
66
+ describe('StaleTmuxSessionKillUseCase', () => {
67
+ let useCase: StaleTmuxSessionKillUseCase;
68
+ let mockIssueRepository: Mocked<Pick<IssueRepository, 'getAllOpened'>>;
69
+ let mockTmuxSessionRepository: Mocked<
70
+ Pick<TmuxSessionRepository, 'listLiveSessionsWithActivity' | 'killSession'>
71
+ >;
72
+ let mockProject: Project;
73
+ const now = new Date('2026-06-26T00:00:00Z');
74
+ const nowEpochSeconds = Math.floor(now.getTime() / 1000);
75
+ const allowCacheMinutes = 10;
76
+
77
+ const runParams = (): {
78
+ project: Project;
79
+ allowCacheMinutes: number;
80
+ excludedStatus: string;
81
+ idleThresholdSeconds: number;
82
+ now: Date;
83
+ } => ({
84
+ project: mockProject,
85
+ allowCacheMinutes,
86
+ excludedStatus: DEFAULT_EXCLUDED_STATUS,
87
+ idleThresholdSeconds: DEFAULT_IDLE_THRESHOLD_SECONDS,
88
+ now,
89
+ });
90
+
91
+ const setLiveSessions = (sessions: LiveTmuxSession[]): void => {
92
+ mockTmuxSessionRepository.listLiveSessionsWithActivity.mockResolvedValue(
93
+ sessions,
94
+ );
95
+ };
96
+
97
+ beforeEach(() => {
98
+ jest.resetAllMocks();
99
+ jest.spyOn(console, 'log').mockImplementation(() => undefined);
100
+ mockProject = createMockProject();
101
+ mockIssueRepository = {
102
+ getAllOpened: jest.fn().mockResolvedValue([]),
103
+ };
104
+ mockTmuxSessionRepository = {
105
+ listLiveSessionsWithActivity: jest.fn().mockResolvedValue([]),
106
+ killSession: jest.fn().mockResolvedValue(undefined),
107
+ };
108
+ useCase = new StaleTmuxSessionKillUseCase(
109
+ mockIssueRepository,
110
+ mockTmuxSessionRepository,
111
+ );
112
+ });
113
+
114
+ it('exposes the excluded status and idle threshold as named constants', () => {
115
+ expect(DEFAULT_EXCLUDED_STATUS).toBe('In Tmux by human');
116
+ expect(DEFAULT_EXCLUDED_STATUS).toBe(IN_TMUX_STATUS_NAME);
117
+ expect(DEFAULT_IDLE_THRESHOLD_SECONDS).toBe(86400);
118
+ });
119
+
120
+ it('lists live sessions with activity and loads open issues for the project', async () => {
121
+ setLiveSessions([]);
122
+ await useCase.run(runParams());
123
+ expect(
124
+ mockTmuxSessionRepository.listLiveSessionsWithActivity,
125
+ ).toHaveBeenCalledTimes(1);
126
+ expect(mockIssueRepository.getAllOpened).toHaveBeenCalledWith(
127
+ mockProject,
128
+ allowCacheMinutes,
129
+ );
130
+ });
131
+
132
+ it('kills a session mapping to an open issue whose status is not the excluded status', async () => {
133
+ const issue = createMockIssue({ status: 'In Progress' });
134
+ const sessionName = toTmuxSessionName(issue.url);
135
+ setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
136
+ mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
137
+ await useCase.run(runParams());
138
+ expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
139
+ sessionName,
140
+ );
141
+ });
142
+
143
+ it('kills a session mapping to an open issue whose status is null', async () => {
144
+ const issue = createMockIssue({ status: null });
145
+ const sessionName = toTmuxSessionName(issue.url);
146
+ setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
147
+ mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
148
+ await useCase.run(runParams());
149
+ expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
150
+ sessionName,
151
+ );
152
+ });
153
+
154
+ it('kills an excluded-status session that has a next action date set', async () => {
155
+ const issue = createMockIssue({
156
+ status: DEFAULT_EXCLUDED_STATUS,
157
+ nextActionDate: new Date('2026-06-27T00:00:00Z'),
158
+ });
159
+ const sessionName = toTmuxSessionName(issue.url);
160
+ setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
161
+ mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
162
+ await useCase.run(runParams());
163
+ expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
164
+ sessionName,
165
+ );
166
+ });
167
+
168
+ it('kills an excluded-status session that has a next action hour set', async () => {
169
+ const issue = createMockIssue({
170
+ status: DEFAULT_EXCLUDED_STATUS,
171
+ nextActionHour: 9,
172
+ });
173
+ const sessionName = toTmuxSessionName(issue.url);
174
+ setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
175
+ mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
176
+ await useCase.run(runParams());
177
+ expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
178
+ sessionName,
179
+ );
180
+ });
181
+
182
+ it('never kills an excluded-status session that has no reactivation trigger', async () => {
183
+ const issue = createMockIssue({
184
+ status: DEFAULT_EXCLUDED_STATUS,
185
+ nextActionDate: null,
186
+ nextActionHour: null,
187
+ });
188
+ const sessionName = toTmuxSessionName(issue.url);
189
+ setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
190
+ mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
191
+ await useCase.run(runParams());
192
+ expect(mockTmuxSessionRepository.killSession).not.toHaveBeenCalled();
193
+ });
194
+
195
+ it('kills a no-task session that has been idle at least the idle threshold', async () => {
196
+ const sessionName = 'no_task_session';
197
+ const idleActivity = nowEpochSeconds - DEFAULT_IDLE_THRESHOLD_SECONDS;
198
+ setLiveSessions([{ sessionName, activityEpochSeconds: idleActivity }]);
199
+ mockIssueRepository.getAllOpened.mockResolvedValue([]);
200
+ await useCase.run(runParams());
201
+ expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
202
+ sessionName,
203
+ );
204
+ });
205
+
206
+ it('never kills a no-task session that was active within the idle threshold', async () => {
207
+ const sessionName = 'no_task_session';
208
+ const recentActivity = nowEpochSeconds - DEFAULT_IDLE_THRESHOLD_SECONDS + 1;
209
+ setLiveSessions([{ sessionName, activityEpochSeconds: recentActivity }]);
210
+ mockIssueRepository.getAllOpened.mockResolvedValue([]);
211
+ await useCase.run(runParams());
212
+ expect(mockTmuxSessionRepository.killSession).not.toHaveBeenCalled();
213
+ });
214
+
215
+ it('does nothing when there are no live sessions', async () => {
216
+ setLiveSessions([]);
217
+ mockIssueRepository.getAllOpened.mockResolvedValue([]);
218
+ await useCase.run(runParams());
219
+ expect(mockTmuxSessionRepository.killSession).not.toHaveBeenCalled();
220
+ });
221
+
222
+ it('maps a session back to its issue using the dot-and-colon-to-underscore convention', async () => {
223
+ const issue = createMockIssue({
224
+ url: 'https://github.com/owner/repo/issues/9',
225
+ status: 'In Progress',
226
+ });
227
+ const sessionName = 'https_//github_com/owner/repo/issues/9';
228
+ expect(toTmuxSessionName(issue.url)).toBe(sessionName);
229
+ setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
230
+ mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
231
+ await useCase.run(runParams());
232
+ expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
233
+ sessionName,
234
+ );
235
+ });
236
+
237
+ it('enumerates and logs the kill-candidate list before killing', async () => {
238
+ const logSpy = jest.spyOn(console, 'log');
239
+ const killOrder: string[] = [];
240
+ mockTmuxSessionRepository.killSession.mockImplementation(
241
+ async (sessionName: string) => {
242
+ killOrder.push(sessionName);
243
+ },
244
+ );
245
+ const issue = createMockIssue({ status: 'In Progress' });
246
+ const sessionName = toTmuxSessionName(issue.url);
247
+ setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
248
+ mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
249
+
250
+ await useCase.run(runParams());
251
+
252
+ const candidateLogIndex = logSpy.mock.calls.findIndex((call) =>
253
+ String(call[0]).startsWith('Kill candidate:'),
254
+ );
255
+ const killedLogIndex = logSpy.mock.calls.findIndex((call) =>
256
+ String(call[0]).startsWith('Killed tmux session:'),
257
+ );
258
+ expect(candidateLogIndex).toBeGreaterThanOrEqual(0);
259
+ expect(killedLogIndex).toBeGreaterThan(candidateLogIndex);
260
+ expect(killOrder).toEqual([sessionName]);
261
+ });
262
+
263
+ it('does not suppress errors raised while killing a session', async () => {
264
+ const issue = createMockIssue({ status: 'In Progress' });
265
+ const sessionName = toTmuxSessionName(issue.url);
266
+ setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
267
+ mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
268
+ mockTmuxSessionRepository.killSession.mockRejectedValue(
269
+ new Error('tmux kill failed'),
270
+ );
271
+ await expect(useCase.run(runParams())).rejects.toThrow('tmux kill failed');
272
+ });
273
+
274
+ it('does not kill any session when loading open issues fails', async () => {
275
+ const sessionName = 'no_task_session';
276
+ const idleActivity = nowEpochSeconds - DEFAULT_IDLE_THRESHOLD_SECONDS;
277
+ setLiveSessions([{ sessionName, activityEpochSeconds: idleActivity }]);
278
+ mockIssueRepository.getAllOpened.mockRejectedValue(
279
+ new Error('failed to load issues'),
280
+ );
281
+ await expect(useCase.run(runParams())).rejects.toThrow(
282
+ 'failed to load issues',
283
+ );
284
+ expect(mockTmuxSessionRepository.killSession).not.toHaveBeenCalled();
285
+ });
286
+ });
@@ -0,0 +1,102 @@
1
+ import { Issue } from '../entities/Issue';
2
+ import { LiveTmuxSession } from '../entities/LiveTmuxSession';
3
+ import { Project } from '../entities/Project';
4
+ import { IN_TMUX_STATUS_NAME } from '../entities/WorkflowStatus';
5
+ import { IssueRepository } from './adapter-interfaces/IssueRepository';
6
+ import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
7
+ import { toTmuxSessionName } from './intmux/InTmuxByHumanSessionReconcileUseCase';
8
+
9
+ export const DEFAULT_EXCLUDED_STATUS = IN_TMUX_STATUS_NAME;
10
+ export const DEFAULT_IDLE_THRESHOLD_SECONDS = 24 * 60 * 60;
11
+
12
+ type KillCandidate = {
13
+ sessionName: string;
14
+ reason: string;
15
+ };
16
+
17
+ export class StaleTmuxSessionKillUseCase {
18
+ constructor(
19
+ private readonly issueRepository: Pick<IssueRepository, 'getAllOpened'>,
20
+ private readonly tmuxSessionRepository: Pick<
21
+ TmuxSessionRepository,
22
+ 'listLiveSessionsWithActivity' | 'killSession'
23
+ >,
24
+ ) {}
25
+
26
+ run = async (params: {
27
+ project: Project;
28
+ allowCacheMinutes: number;
29
+ excludedStatus: string;
30
+ idleThresholdSeconds: number;
31
+ now: Date;
32
+ }): Promise<void> => {
33
+ const liveSessions =
34
+ await this.tmuxSessionRepository.listLiveSessionsWithActivity();
35
+ const openIssues = await this.issueRepository.getAllOpened(
36
+ params.project,
37
+ params.allowCacheMinutes,
38
+ );
39
+ const issueBySessionName = new Map<string, Issue>();
40
+ for (const issue of openIssues) {
41
+ issueBySessionName.set(toTmuxSessionName(issue.url), issue);
42
+ }
43
+
44
+ const nowEpochSeconds = Math.floor(params.now.getTime() / 1000);
45
+ const killCandidates: KillCandidate[] = [];
46
+ for (const session of liveSessions) {
47
+ const reason = this.evaluateKillReason(
48
+ session,
49
+ issueBySessionName.get(session.sessionName) ?? null,
50
+ nowEpochSeconds,
51
+ params.excludedStatus,
52
+ params.idleThresholdSeconds,
53
+ );
54
+ if (reason !== null) {
55
+ killCandidates.push({ sessionName: session.sessionName, reason });
56
+ }
57
+ }
58
+
59
+ console.log(
60
+ `Stale tmux session cleanup: ${killCandidates.length} kill candidate(s) of ${liveSessions.length} live session(s).`,
61
+ );
62
+ for (const candidate of killCandidates) {
63
+ console.log(
64
+ `Kill candidate: ${candidate.sessionName} (${candidate.reason})`,
65
+ );
66
+ }
67
+
68
+ for (const candidate of killCandidates) {
69
+ await this.tmuxSessionRepository.killSession(candidate.sessionName);
70
+ console.log(
71
+ `Killed tmux session: ${candidate.sessionName} (${candidate.reason})`,
72
+ );
73
+ }
74
+ };
75
+
76
+ private evaluateKillReason = (
77
+ session: LiveTmuxSession,
78
+ issue: Issue | null,
79
+ nowEpochSeconds: number,
80
+ excludedStatus: string,
81
+ idleThresholdSeconds: number,
82
+ ): string | null => {
83
+ if (issue !== null) {
84
+ if (issue.status !== excludedStatus) {
85
+ return `mapped to open issue ${issue.url} with status "${issue.status ?? 'null'}" which is not the excluded status "${excludedStatus}"`;
86
+ }
87
+ if (issue.nextActionDate !== null) {
88
+ return `mapped to open issue ${issue.url} which has a next action date set`;
89
+ }
90
+ if (issue.nextActionHour !== null) {
91
+ return `mapped to open issue ${issue.url} which has a next action hour set`;
92
+ }
93
+ return null;
94
+ }
95
+
96
+ const idleSeconds = nowEpochSeconds - session.activityEpochSeconds;
97
+ if (idleSeconds >= idleThresholdSeconds) {
98
+ return `maps to no open issue and has been idle for ${idleSeconds} seconds (threshold ${idleThresholdSeconds} seconds)`;
99
+ }
100
+ return null;
101
+ };
102
+ }
@@ -1,9 +1,13 @@
1
+ import { LiveTmuxSession } from '../../entities/LiveTmuxSession';
2
+
1
3
  export interface TmuxSessionRepository {
2
4
  listLiveSessionNames: () => Promise<string[]>;
5
+ listLiveSessionsWithActivity: () => Promise<LiveTmuxSession[]>;
3
6
  listInteractiveProcessCommandLines: () => Promise<string[]>;
4
7
  launchDetachedSession: (
5
8
  sessionName: string,
6
9
  launcherCommand: string,
7
10
  issueUrl: string,
8
11
  ) => Promise<void>;
12
+ killSession: (sessionName: string) => Promise<void>;
9
13
  }
@@ -60,10 +60,16 @@ const createFakeTmuxSessionRepository = (state: {
60
60
  return {
61
61
  launches,
62
62
  listLiveSessionNames: async () => state.liveSessionNames,
63
+ listLiveSessionsWithActivity: async () =>
64
+ state.liveSessionNames.map((sessionName) => ({
65
+ sessionName,
66
+ activityEpochSeconds: 0,
67
+ })),
63
68
  listInteractiveProcessCommandLines: async () => state.processCommandLines,
64
69
  launchDetachedSession: async (sessionName, launcherCommand, issueUrl) => {
65
70
  launches.push({ sessionName, launcherCommand, issueUrl });
66
71
  },
72
+ killSession: async () => undefined,
67
73
  };
68
74
  };
69
75
 
@@ -1 +1 @@
1
- {"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAgC3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,KAChB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,IAAI,EAAE,CAAC;KACzB,GAAG,IAAI,CAAC,CAwbP;CACH"}
1
+ {"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAgC3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,KAChB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,IAAI,EAAE,CAAC;KACzB,GAAG,IAAI,CAAC,CAwcP;CACH"}
@@ -0,0 +1,12 @@
1
+ import { Project } from '../../../domain/entities/Project';
2
+ import { LocalCommandRunner } from '../../../domain/usecases/adapter-interfaces/LocalCommandRunner';
3
+ import { IssueRepository } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
4
+ export type CleanStaleTmuxSessionsParams = {
5
+ project: Project;
6
+ allowCacheMinutes: number;
7
+ issueRepository: Pick<IssueRepository, 'getAllOpened'>;
8
+ localCommandRunner: LocalCommandRunner;
9
+ now: Date;
10
+ };
11
+ export declare const cleanStaleTmuxSessions: (params: CleanStaleTmuxSessionsParams) => Promise<void>;
12
+ //# sourceMappingURL=staleTmuxSessionCleaner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staleTmuxSessionCleaner.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AACpG,OAAO,EAAE,eAAe,EAAE,MAAM,6DAA6D,CAAC;AAQ9F,MAAM,MAAM,4BAA4B,GAAG;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;IACvD,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,QAAQ,4BAA4B,KACnC,OAAO,CAAC,IAAI,CAmBd,CAAC"}
@@ -1,10 +1,13 @@
1
1
  import { LocalCommandRunner } from '../../domain/usecases/adapter-interfaces/LocalCommandRunner';
2
2
  import { TmuxSessionRepository } from '../../domain/usecases/adapter-interfaces/TmuxSessionRepository';
3
+ import { LiveTmuxSession } from '../../domain/entities/LiveTmuxSession';
3
4
  export declare class NodeTmuxSessionRepository implements TmuxSessionRepository {
4
5
  private readonly localCommandRunner;
5
6
  constructor(localCommandRunner: LocalCommandRunner);
6
7
  listLiveSessionNames: () => Promise<string[]>;
8
+ listLiveSessionsWithActivity: () => Promise<LiveTmuxSession[]>;
7
9
  listInteractiveProcessCommandLines: () => Promise<string[]>;
8
10
  launchDetachedSession: (sessionName: string, launcherCommand: string, issueUrl: string) => Promise<void>;
11
+ killSession: (sessionName: string) => Promise<void>;
9
12
  }
10
13
  //# sourceMappingURL=NodeTmuxSessionRepository.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NodeTmuxSessionRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gEAAgE,CAAC;AAEvG,qBAAa,yBAA0B,YAAW,qBAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBAAlB,kBAAkB,EAAE,kBAAkB;IAEnE,oBAAoB,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAYhD;IAEF,kCAAkC,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAY9D;IAEF,qBAAqB,GACnB,aAAa,MAAM,EACnB,iBAAiB,MAAM,EACvB,UAAU,MAAM,KACf,OAAO,CAAC,IAAI,CAAC,CAcd;CACH"}
1
+ {"version":3,"file":"NodeTmuxSessionRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gEAAgE,CAAC;AACvG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAExE,qBAAa,yBAA0B,YAAW,qBAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBAAlB,kBAAkB,EAAE,kBAAkB;IAEnE,oBAAoB,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAYhD;IAEF,4BAA4B,QAAa,OAAO,CAAC,eAAe,EAAE,CAAC,CAkBjE;IAEF,kCAAkC,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAY9D;IAEF,qBAAqB,GACnB,aAAa,MAAM,EACnB,iBAAiB,MAAM,EACvB,UAAU,MAAM,KACf,OAAO,CAAC,IAAI,CAAC,CAcd;IAEF,WAAW,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAYtD;CACH"}
@@ -0,0 +1,5 @@
1
+ export type LiveTmuxSession = {
2
+ sessionName: string;
3
+ activityEpochSeconds: number;
4
+ };
5
+ //# sourceMappingURL=LiveTmuxSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LiveTmuxSession.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/LiveTmuxSession.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { Project } from '../entities/Project';
2
+ import { IssueRepository } from './adapter-interfaces/IssueRepository';
3
+ import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
4
+ export declare const DEFAULT_EXCLUDED_STATUS = "In Tmux by human";
5
+ export declare const DEFAULT_IDLE_THRESHOLD_SECONDS: number;
6
+ export declare class StaleTmuxSessionKillUseCase {
7
+ private readonly issueRepository;
8
+ private readonly tmuxSessionRepository;
9
+ constructor(issueRepository: Pick<IssueRepository, 'getAllOpened'>, tmuxSessionRepository: Pick<TmuxSessionRepository, 'listLiveSessionsWithActivity' | 'killSession'>);
10
+ run: (params: {
11
+ project: Project;
12
+ allowCacheMinutes: number;
13
+ excludedStatus: string;
14
+ idleThresholdSeconds: number;
15
+ now: Date;
16
+ }) => Promise<void>;
17
+ private evaluateKillReason;
18
+ }
19
+ //# sourceMappingURL=StaleTmuxSessionKillUseCase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StaleTmuxSessionKillUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/StaleTmuxSessionKillUseCase.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AAGnF,eAAO,MAAM,uBAAuB,qBAAsB,CAAC;AAC3D,eAAO,MAAM,8BAA8B,QAAe,CAAC;AAO3D,qBAAa,2BAA2B;IAEpC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;gBADrB,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,EACtD,qBAAqB,EAAE,IAAI,CAC1C,qBAAqB,EACrB,8BAA8B,GAAG,aAAa,CAC/C;IAGH,GAAG,GAAU,QAAQ;QACnB,OAAO,EAAE,OAAO,CAAC;QACjB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,cAAc,EAAE,MAAM,CAAC;QACvB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,GAAG,EAAE,IAAI,CAAC;KACX,KAAG,OAAO,CAAC,IAAI,CAAC,CA0Cf;IAEF,OAAO,CAAC,kBAAkB,CAyBxB;CACH"}
@@ -1,6 +1,9 @@
1
+ import { LiveTmuxSession } from '../../entities/LiveTmuxSession';
1
2
  export interface TmuxSessionRepository {
2
3
  listLiveSessionNames: () => Promise<string[]>;
4
+ listLiveSessionsWithActivity: () => Promise<LiveTmuxSession[]>;
3
5
  listInteractiveProcessCommandLines: () => Promise<string[]>;
4
6
  launchDetachedSession: (sessionName: string, launcherCommand: string, issueUrl: string) => Promise<void>;
7
+ killSession: (sessionName: string) => Promise<void>;
5
8
  }
6
9
  //# sourceMappingURL=TmuxSessionRepository.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TmuxSessionRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,oBAAoB,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9C,kCAAkC,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5D,qBAAqB,EAAE,CACrB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB"}
1
+ {"version":3,"file":"TmuxSessionRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,MAAM,WAAW,qBAAqB;IACpC,oBAAoB,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9C,4BAA4B,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC/D,kCAAkC,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5D,qBAAqB,EAAE,CACrB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD"}