github-issue-tower-defence-management 1.109.1 → 1.109.2

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 (26) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +3 -3
  3. package/bin/adapter/entry-points/cli/index.js +10 -10
  4. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  5. package/bin/adapter/entry-points/console/dashboardComposeService.js +7 -6
  6. package/bin/adapter/entry-points/console/dashboardComposeService.js.map +1 -1
  7. package/bin/adapter/entry-points/console/webServer.js +5 -4
  8. package/bin/adapter/entry-points/console/webServer.js.map +1 -1
  9. package/bin/domain/usecases/dashboard/DashboardProjectCode.js +19 -0
  10. package/bin/domain/usecases/dashboard/DashboardProjectCode.js.map +1 -0
  11. package/package.json +1 -1
  12. package/src/adapter/entry-points/cli/index.ts +14 -14
  13. package/src/adapter/entry-points/console/dashboardComposeService.test.ts +31 -28
  14. package/src/adapter/entry-points/console/dashboardComposeService.ts +13 -10
  15. package/src/adapter/entry-points/console/dashboardEmitterComposerKey.test.ts +175 -0
  16. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +1 -1
  17. package/src/adapter/entry-points/console/webServer.test.ts +23 -23
  18. package/src/adapter/entry-points/console/webServer.ts +5 -4
  19. package/src/domain/usecases/dashboard/DashboardProjectCode.test.ts +32 -0
  20. package/src/domain/usecases/dashboard/DashboardProjectCode.ts +20 -0
  21. package/types/adapter/entry-points/console/dashboardComposeService.d.ts +1 -1
  22. package/types/adapter/entry-points/console/dashboardComposeService.d.ts.map +1 -1
  23. package/types/adapter/entry-points/console/webServer.d.ts +2 -2
  24. package/types/adapter/entry-points/console/webServer.d.ts.map +1 -1
  25. package/types/domain/usecases/dashboard/DashboardProjectCode.d.ts +4 -0
  26. package/types/domain/usecases/dashboard/DashboardProjectCode.d.ts.map +1 -0
@@ -22,8 +22,8 @@ describe('buildComposeDashboardInput', () => {
22
22
  it('reads every requested project code in order and yields null for absent files', () => {
23
23
  const dataDir = makeDataDir();
24
24
  try {
25
- writeProject(dataDir, 'um', {
26
- pjcode: 'um',
25
+ writeProject(dataDir, 'umino', {
26
+ pjcode: 'umino',
27
27
  capturedAt: 'x',
28
28
  unread: 3,
29
29
  todo: 1,
@@ -36,7 +36,7 @@ describe('buildComposeDashboardInput', () => {
36
36
  });
37
37
  const input = buildComposeDashboardInput({
38
38
  dashboardDataDir: dataDir,
39
- projectCodes: ['um', 'xc'],
39
+ projectNames: ['umino', 'xcare'],
40
40
  });
41
41
  expect(input.projects).toEqual([
42
42
  {
@@ -62,10 +62,10 @@ describe('buildComposeDashboardInput', () => {
62
62
  it('treats a project file with a missing field as an absent row', () => {
63
63
  const dataDir = makeDataDir();
64
64
  try {
65
- writeProject(dataDir, 'um', { unread: 1, todo: 1 });
65
+ writeProject(dataDir, 'umino', { unread: 1, todo: 1 });
66
66
  const input = buildComposeDashboardInput({
67
67
  dashboardDataDir: dataDir,
68
- projectCodes: ['um'],
68
+ projectNames: ['umino'],
69
69
  });
70
70
  expect(input.projects[0].row).toBeNull();
71
71
  } finally {
@@ -77,10 +77,13 @@ describe('buildComposeDashboardInput', () => {
77
77
  const dataDir = makeDataDir();
78
78
  try {
79
79
  fs.mkdirSync(path.join(dataDir, 'projects'), { recursive: true });
80
- fs.writeFileSync(path.join(dataDir, 'projects', 'um.json'), 'not json');
80
+ fs.writeFileSync(
81
+ path.join(dataDir, 'projects', 'umino.json'),
82
+ 'not json',
83
+ );
81
84
  const input = buildComposeDashboardInput({
82
85
  dashboardDataDir: dataDir,
83
- projectCodes: ['um'],
86
+ projectNames: ['umino'],
84
87
  });
85
88
  expect(input.projects[0].row).toBeNull();
86
89
  } finally {
@@ -104,7 +107,7 @@ describe('buildComposeDashboardInput', () => {
104
107
  );
105
108
  const input = buildComposeDashboardInput({
106
109
  dashboardDataDir: dataDir,
107
- projectCodes: [],
110
+ projectNames: [],
108
111
  });
109
112
  expect(input.machineStatus).toEqual({
110
113
  memPct: 55,
@@ -134,7 +137,7 @@ describe('buildComposeDashboardInput', () => {
134
137
  );
135
138
  const input = buildComposeDashboardInput({
136
139
  dashboardDataDir: dataDir,
137
- projectCodes: [],
140
+ projectNames: [],
138
141
  });
139
142
  expect(input.machineStatus?.cycleMinutes).toBeNull();
140
143
  } finally {
@@ -147,7 +150,7 @@ describe('buildComposeDashboardInput', () => {
147
150
  try {
148
151
  const input = buildComposeDashboardInput({
149
152
  dashboardDataDir: dataDir,
150
- projectCodes: [],
153
+ projectNames: [],
151
154
  });
152
155
  expect(input.machineStatus).toBeNull();
153
156
  } finally {
@@ -179,7 +182,7 @@ describe('buildComposeDashboardInput', () => {
179
182
  );
180
183
  const input = buildComposeDashboardInput({
181
184
  dashboardDataDir: dataDir,
182
- projectCodes: [],
185
+ projectNames: [],
183
186
  });
184
187
  expect(input.tokens).toEqual([
185
188
  {
@@ -221,7 +224,7 @@ describe('buildComposeDashboardInput', () => {
221
224
  );
222
225
  const input = buildComposeDashboardInput({
223
226
  dashboardDataDir: dataDir,
224
- projectCodes: [],
227
+ projectNames: [],
225
228
  });
226
229
  expect(input.tokens[0].color).toBe('Y');
227
230
  expect(input.tokens[0].fiveHourUtilizationPercent).toBeNull();
@@ -235,7 +238,7 @@ describe('buildComposeDashboardInput', () => {
235
238
  try {
236
239
  const input = buildComposeDashboardInput({
237
240
  dashboardDataDir: dataDir,
238
- projectCodes: [],
241
+ projectNames: [],
239
242
  });
240
243
  expect(input.tokens).toEqual([]);
241
244
  } finally {
@@ -248,8 +251,8 @@ describe('composeDashboardText', () => {
248
251
  it('composes the full byte-identical dashboard text from the data files', () => {
249
252
  const dataDir = makeDataDir();
250
253
  try {
251
- writeProject(dataDir, 'um', {
252
- pjcode: 'um',
254
+ writeProject(dataDir, 'umino', {
255
+ pjcode: 'umino',
253
256
  capturedAt: 'x',
254
257
  unread: 3,
255
258
  todo: 1,
@@ -292,7 +295,7 @@ describe('composeDashboardText', () => {
292
295
  expect(
293
296
  composeDashboardText({
294
297
  dashboardDataDir: dataDir,
295
- projectCodes: ['um', 'xc'],
298
+ projectNames: ['umino', 'xcare'],
296
299
  }),
297
300
  ).toBe(
298
301
  '<tt>M55%&nbsp;C62%&nbsp;D89%&nbsp;cy14</tt><br>\n' +
@@ -329,7 +332,7 @@ describe('dashboardComposeFilesPresent', () => {
329
332
  };
330
333
 
331
334
  const minimalProject = {
332
- pjcode: 'um',
335
+ pjcode: 'umino',
333
336
  capturedAt: 'x',
334
337
  unread: 0,
335
338
  todo: 0,
@@ -345,12 +348,12 @@ describe('dashboardComposeFilesPresent', () => {
345
348
  const dataDir = makeDataDir();
346
349
  try {
347
350
  writeMachineAndToken(dataDir);
348
- writeProject(dataDir, 'um', minimalProject);
349
- writeProject(dataDir, 'xc', { ...minimalProject, pjcode: 'xc' });
351
+ writeProject(dataDir, 'umino', minimalProject);
352
+ writeProject(dataDir, 'xcare', { ...minimalProject, pjcode: 'xcare' });
350
353
  expect(
351
354
  dashboardComposeFilesPresent({
352
355
  dashboardDataDir: dataDir,
353
- projectCodes: ['um', 'xc'],
356
+ projectNames: ['umino', 'xcare'],
354
357
  }),
355
358
  ).toBe(true);
356
359
  } finally {
@@ -364,7 +367,7 @@ describe('dashboardComposeFilesPresent', () => {
364
367
  expect(
365
368
  dashboardComposeFilesPresent({
366
369
  dashboardDataDir: dataDir,
367
- projectCodes: ['um'],
370
+ projectNames: ['umino'],
368
371
  }),
369
372
  ).toBe(false);
370
373
  } finally {
@@ -376,11 +379,11 @@ describe('dashboardComposeFilesPresent', () => {
376
379
  const dataDir = makeDataDir();
377
380
  try {
378
381
  writeMachineAndToken(dataDir);
379
- writeProject(dataDir, 'um', minimalProject);
382
+ writeProject(dataDir, 'umino', minimalProject);
380
383
  expect(
381
384
  dashboardComposeFilesPresent({
382
385
  dashboardDataDir: dataDir,
383
- projectCodes: ['um', 'xc'],
386
+ projectNames: ['umino', 'xcare'],
384
387
  }),
385
388
  ).toBe(false);
386
389
  } finally {
@@ -395,11 +398,11 @@ describe('dashboardComposeFilesPresent', () => {
395
398
  path.join(dataDir, 'token-status.json'),
396
399
  JSON.stringify({ tokens: [], capturedAt: 'x' }),
397
400
  );
398
- writeProject(dataDir, 'um', minimalProject);
401
+ writeProject(dataDir, 'umino', minimalProject);
399
402
  expect(
400
403
  dashboardComposeFilesPresent({
401
404
  dashboardDataDir: dataDir,
402
- projectCodes: ['um'],
405
+ projectNames: ['umino'],
403
406
  }),
404
407
  ).toBe(false);
405
408
  } finally {
@@ -421,11 +424,11 @@ describe('dashboardComposeFilesPresent', () => {
421
424
  capturedAt: 'x',
422
425
  }),
423
426
  );
424
- writeProject(dataDir, 'um', minimalProject);
427
+ writeProject(dataDir, 'umino', minimalProject);
425
428
  expect(
426
429
  dashboardComposeFilesPresent({
427
430
  dashboardDataDir: dataDir,
428
- projectCodes: ['um'],
431
+ projectNames: ['umino'],
429
432
  }),
430
433
  ).toBe(false);
431
434
  } finally {
@@ -440,7 +443,7 @@ describe('dashboardComposeFilesPresent', () => {
440
443
  expect(
441
444
  dashboardComposeFilesPresent({
442
445
  dashboardDataDir: dataDir,
443
- projectCodes: [],
446
+ projectNames: [],
444
447
  }),
445
448
  ).toBe(false);
446
449
  } finally {
@@ -6,6 +6,7 @@ import {
6
6
  ComposeDashboardProject,
7
7
  ComposeDashboardUseCase,
8
8
  } from '../../../domain/usecases/dashboard/ComposeDashboardUseCase';
9
+ import { toDashboardDisplayLabel } from '../../../domain/usecases/dashboard/DashboardProjectCode';
9
10
  import { DashboardRow } from '../../../domain/usecases/dashboard/GenerateDashboardRowUseCase';
10
11
  import {
11
12
  TokenStatus,
@@ -14,7 +15,7 @@ import {
14
15
 
15
16
  export type DashboardComposeOptions = {
16
17
  dashboardDataDir: string;
17
- projectCodes: string[];
18
+ projectNames: string[];
18
19
  };
19
20
 
20
21
  const isRecord = (value: unknown): value is Record<string, unknown> =>
@@ -66,10 +67,12 @@ const parseDashboardRow = (value: unknown): DashboardRow | null => {
66
67
 
67
68
  const readProjectRow = (
68
69
  dashboardDataDir: string,
69
- code: string,
70
+ projectName: string,
70
71
  ): DashboardRow | null =>
71
72
  parseDashboardRow(
72
- readJsonFile(path.join(dashboardDataDir, 'projects', `${code}.json`)),
73
+ readJsonFile(
74
+ path.join(dashboardDataDir, 'projects', `${projectName}.json`),
75
+ ),
73
76
  );
74
77
 
75
78
  const parseLoad = (value: unknown): [number, number, number] | null => {
@@ -158,10 +161,10 @@ const readTokenStatuses = (dashboardDataDir: string): TokenStatus[] => {
158
161
  export const buildComposeDashboardInput = (
159
162
  options: DashboardComposeOptions,
160
163
  ): ComposeDashboardInput => {
161
- const projects: ComposeDashboardProject[] = options.projectCodes.map(
162
- (code) => ({
163
- code,
164
- row: readProjectRow(options.dashboardDataDir, code),
164
+ const projects: ComposeDashboardProject[] = options.projectNames.map(
165
+ (projectName) => ({
166
+ code: toDashboardDisplayLabel(projectName),
167
+ row: readProjectRow(options.dashboardDataDir, projectName),
165
168
  }),
166
169
  );
167
170
  return {
@@ -182,14 +185,14 @@ const isExistingFile = (filePath: string): boolean => {
182
185
  export const dashboardComposeFilesPresent = (
183
186
  options: DashboardComposeOptions,
184
187
  ): boolean => {
185
- if (options.projectCodes.length === 0) {
188
+ if (options.projectNames.length === 0) {
186
189
  return false;
187
190
  }
188
191
  const requiredFiles = [
189
192
  path.join(options.dashboardDataDir, 'machine-status.json'),
190
193
  path.join(options.dashboardDataDir, 'token-status.json'),
191
- ...options.projectCodes.map((code) =>
192
- path.join(options.dashboardDataDir, 'projects', `${code}.json`),
194
+ ...options.projectNames.map((projectName) =>
195
+ path.join(options.dashboardDataDir, 'projects', `${projectName}.json`),
193
196
  ),
194
197
  ];
195
198
  return requiredFiles.every((filePath) => isExistingFile(filePath));
@@ -0,0 +1,175 @@
1
+ import * as fs from 'fs';
2
+ import * as os from 'os';
3
+ import * as path from 'path';
4
+ import { Issue } from '../../../domain/entities/Issue';
5
+ import {
6
+ DASHBOARD_PROJECT_NAMES,
7
+ toDashboardDisplayLabel,
8
+ } from '../../../domain/usecases/dashboard/DashboardProjectCode';
9
+ import { writeDashboardRow } from '../handlers/dashboardRowWriter';
10
+ import {
11
+ composeDashboardText,
12
+ dashboardComposeFilesPresent,
13
+ } from './dashboardComposeService';
14
+ import { DEFAULT_DASHBOARD_PROJECT_NAMES } from './webServer';
15
+
16
+ const ASSIGNEE = 'HiromiShikata';
17
+
18
+ let issueCounter = 0;
19
+ const makeIssue = (overrides: Partial<Issue>): Issue => {
20
+ issueCounter += 1;
21
+ return {
22
+ nameWithOwner: 'demo/repo',
23
+ number: issueCounter,
24
+ title: `Issue ${issueCounter}`,
25
+ state: 'OPEN',
26
+ status: null,
27
+ story: null,
28
+ nextActionDate: null,
29
+ nextActionHour: null,
30
+ estimationMinutes: null,
31
+ dependedIssueUrls: [],
32
+ completionDate50PercentConfidence: null,
33
+ url: `https://github.com/demo/repo/issues/${issueCounter}`,
34
+ assignees: [ASSIGNEE],
35
+ labels: [],
36
+ org: 'demo',
37
+ repo: 'repo',
38
+ body: '',
39
+ itemId: `item-${issueCounter}`,
40
+ isPr: false,
41
+ isInProgress: false,
42
+ isClosed: false,
43
+ createdAt: new Date('2026-06-13T08:18:45.000Z'),
44
+ author: 'someone',
45
+ closingIssueReferenceUrls: [],
46
+ ...overrides,
47
+ };
48
+ };
49
+
50
+ describe('dashboard emitter filename matches composer lookup key', () => {
51
+ let dir: string;
52
+
53
+ beforeEach(() => {
54
+ issueCounter = 0;
55
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), 'dashboard-key-'));
56
+ });
57
+
58
+ afterEach(() => {
59
+ fs.rmSync(dir, { recursive: true, force: true });
60
+ });
61
+
62
+ it.each(DASHBOARD_PROJECT_NAMES)(
63
+ 'emits projects/%s.json under the full project name the composer reads',
64
+ (projectName) => {
65
+ writeDashboardRow({
66
+ dashboardDataDir: dir,
67
+ pjcode: projectName,
68
+ assigneeLogin: ASSIGNEE,
69
+ issues: [makeIssue({ status: 'Unread' })],
70
+ generatedAt: '2026-06-26T12:00:00.000Z',
71
+ });
72
+
73
+ const composerLookupPath = path.join(
74
+ dir,
75
+ 'projects',
76
+ `${projectName}.json`,
77
+ );
78
+ expect(fs.existsSync(composerLookupPath)).toBe(true);
79
+ },
80
+ );
81
+
82
+ it('detects compose files present when every project name file exists', () => {
83
+ for (const projectName of DASHBOARD_PROJECT_NAMES) {
84
+ writeDashboardRow({
85
+ dashboardDataDir: dir,
86
+ pjcode: projectName,
87
+ assigneeLogin: ASSIGNEE,
88
+ issues: [makeIssue({ status: 'Unread' })],
89
+ generatedAt: '2026-06-26T12:00:00.000Z',
90
+ });
91
+ }
92
+ fs.writeFileSync(
93
+ path.join(dir, 'machine-status.json'),
94
+ JSON.stringify({
95
+ memPct: 1,
96
+ cpuPct: 2,
97
+ diskPct: 3,
98
+ load: [0, 0, 0],
99
+ cycleMinutes: 1,
100
+ }),
101
+ );
102
+ fs.writeFileSync(
103
+ path.join(dir, 'token-status.json'),
104
+ JSON.stringify({ tokens: [] }),
105
+ );
106
+
107
+ expect(
108
+ dashboardComposeFilesPresent({
109
+ dashboardDataDir: dir,
110
+ projectNames: DEFAULT_DASHBOARD_PROJECT_NAMES,
111
+ }),
112
+ ).toBe(true);
113
+ });
114
+
115
+ it('composes the full grid with no fallback row and shows the 2-char display labels', () => {
116
+ const issuesByName: Record<string, Issue[]> = {
117
+ umino: [makeIssue({ status: 'Unread' }), makeIssue({ status: 'Unread' })],
118
+ xmile: [makeIssue({ status: 'Awaiting Quality Check' })],
119
+ xcare: [makeIssue({ status: 'Awaiting Workspace' })],
120
+ utage3: [makeIssue({ status: 'Todo by human' })],
121
+ };
122
+ for (const projectName of DASHBOARD_PROJECT_NAMES) {
123
+ writeDashboardRow({
124
+ dashboardDataDir: dir,
125
+ pjcode: projectName,
126
+ assigneeLogin: ASSIGNEE,
127
+ issues: issuesByName[projectName],
128
+ generatedAt: '2026-06-26T12:00:00.000Z',
129
+ });
130
+ }
131
+ fs.writeFileSync(
132
+ path.join(dir, 'machine-status.json'),
133
+ JSON.stringify({
134
+ memPct: 55,
135
+ cpuPct: 62,
136
+ diskPct: 93,
137
+ load: [16, 23, 40],
138
+ cycleMinutes: 13,
139
+ }),
140
+ );
141
+ fs.writeFileSync(
142
+ path.join(dir, 'token-status.json'),
143
+ JSON.stringify({ tokens: [] }),
144
+ );
145
+
146
+ const composed = composeDashboardText({
147
+ dashboardDataDir: dir,
148
+ projectNames: DEFAULT_DASHBOARD_PROJECT_NAMES,
149
+ });
150
+
151
+ const wrap = (line: string): string =>
152
+ `<tt>${line.replace(/ /g, '&nbsp;')}</tt><br>`;
153
+ const expected =
154
+ [
155
+ wrap('M55% C62% D93% cy13'),
156
+ wrap('LA 16 23 40'),
157
+ wrap('pj unr tdo aqc fal prp aws dep'),
158
+ wrap(
159
+ `🟢${toDashboardDisplayLabel('umino')} 2 0 0 0 0 0 0`,
160
+ ),
161
+ wrap(
162
+ `🟢${toDashboardDisplayLabel('xmile')} 0 0 1 0 0 0 0`,
163
+ ),
164
+ wrap(
165
+ `🟢${toDashboardDisplayLabel('xcare')} 0 0 0 0 0 1 0`,
166
+ ),
167
+ wrap(
168
+ `🟢${toDashboardDisplayLabel('utage3')} 0 1 0 0 0 0 0`,
169
+ ),
170
+ wrap(''),
171
+ ].join('\n') + '\n';
172
+ expect(composed).toBe(expected);
173
+ expect(composed).not.toContain('--');
174
+ });
175
+ });
@@ -372,7 +372,7 @@ export const startConsoleE2eHarness = async (): Promise<ConsoleE2eHarness> => {
372
372
  inTmuxDataDir: null,
373
373
  dashboardDir: null,
374
374
  dashboardDataDir: null,
375
- dashboardProjectCodes: [],
375
+ dashboardProjectNames: [],
376
376
  port: 0,
377
377
  });
378
378
 
@@ -182,7 +182,7 @@ describe('webServer integration', () => {
182
182
  inTmuxDataDir: null,
183
183
  dashboardDir: null,
184
184
  dashboardDataDir: null,
185
- dashboardProjectCodes: [],
185
+ dashboardProjectNames: [],
186
186
  port: 0,
187
187
  });
188
188
  const address = server.address();
@@ -201,7 +201,7 @@ describe('webServer integration', () => {
201
201
  inTmuxDataDir: null,
202
202
  dashboardDir: null,
203
203
  dashboardDataDir: null,
204
- dashboardProjectCodes: [],
204
+ dashboardProjectNames: [],
205
205
  port: 0,
206
206
  });
207
207
  try {
@@ -229,7 +229,7 @@ describe('webServer integration', () => {
229
229
  inTmuxDataDir: null,
230
230
  dashboardDir: null,
231
231
  dashboardDataDir: null,
232
- dashboardProjectCodes: [],
232
+ dashboardProjectNames: [],
233
233
  port: 0,
234
234
  });
235
235
  try {
@@ -261,7 +261,7 @@ describe('webServer integration', () => {
261
261
  inTmuxDataDir: null,
262
262
  dashboardDir: null,
263
263
  dashboardDataDir: null,
264
- dashboardProjectCodes: [],
264
+ dashboardProjectNames: [],
265
265
  port: 0,
266
266
  });
267
267
  try {
@@ -295,7 +295,7 @@ describe('webServer integration', () => {
295
295
  inTmuxDataDir: null,
296
296
  dashboardDir: null,
297
297
  dashboardDataDir: null,
298
- dashboardProjectCodes: [],
298
+ dashboardProjectNames: [],
299
299
  port: 0,
300
300
  });
301
301
  try {
@@ -326,7 +326,7 @@ describe('webServer integration', () => {
326
326
  inTmuxDataDir: null,
327
327
  dashboardDir: null,
328
328
  dashboardDataDir: null,
329
- dashboardProjectCodes: [],
329
+ dashboardProjectNames: [],
330
330
  port: 0,
331
331
  });
332
332
  try {
@@ -351,7 +351,7 @@ describe('webServer integration', () => {
351
351
  inTmuxDataDir: null,
352
352
  dashboardDir: null,
353
353
  dashboardDataDir: null,
354
- dashboardProjectCodes: [],
354
+ dashboardProjectNames: [],
355
355
  port: 0,
356
356
  });
357
357
  try {
@@ -379,7 +379,7 @@ describe('webServer integration', () => {
379
379
  inTmuxDataDir: null,
380
380
  dashboardDir: null,
381
381
  dashboardDataDir: null,
382
- dashboardProjectCodes: [],
382
+ dashboardProjectNames: [],
383
383
  port: 0,
384
384
  });
385
385
  try {
@@ -519,7 +519,7 @@ describe('webServer new routes integration', () => {
519
519
  inTmuxDataDir: null,
520
520
  dashboardDir: null,
521
521
  dashboardDataDir: null,
522
- dashboardProjectCodes: [],
522
+ dashboardProjectNames: [],
523
523
  port: 0,
524
524
  });
525
525
  try {
@@ -558,7 +558,7 @@ describe('webServer new routes integration', () => {
558
558
  inTmuxDataDir: null,
559
559
  dashboardDir: null,
560
560
  dashboardDataDir: null,
561
- dashboardProjectCodes: [],
561
+ dashboardProjectNames: [],
562
562
  issueRepository,
563
563
  issueTitleStateCache: new IssueTitleStateCache(),
564
564
  port: 0,
@@ -594,7 +594,7 @@ describe('webServer new routes integration', () => {
594
594
  inTmuxDataDir: null,
595
595
  dashboardDir: null,
596
596
  dashboardDataDir: null,
597
- dashboardProjectCodes: [],
597
+ dashboardProjectNames: [],
598
598
  issueRepository,
599
599
  resolveProject: async (pjcode) =>
600
600
  pjcode === 'umino' ? { pjcode, project: buildProject() } : null,
@@ -645,7 +645,7 @@ describe('webServer new routes integration', () => {
645
645
  inTmuxDataDir: null,
646
646
  dashboardDir: null,
647
647
  dashboardDataDir: null,
648
- dashboardProjectCodes: [],
648
+ dashboardProjectNames: [],
649
649
  issueRepository,
650
650
  resolveProject: async (pjcode) =>
651
651
  pjcode === 'umino' ? { pjcode, project: buildProject() } : null,
@@ -693,7 +693,7 @@ describe('webServer new routes integration', () => {
693
693
  inTmuxDataDir: null,
694
694
  dashboardDir: null,
695
695
  dashboardDataDir: null,
696
- dashboardProjectCodes: [],
696
+ dashboardProjectNames: [],
697
697
  issueRepository,
698
698
  resolveProject: async (pjcode) =>
699
699
  pjcode === 'umino' ? { pjcode, project: buildProject() } : null,
@@ -731,7 +731,7 @@ describe('webServer new routes integration', () => {
731
731
  inTmuxDataDir: null,
732
732
  dashboardDir: null,
733
733
  dashboardDataDir: null,
734
- dashboardProjectCodes: [],
734
+ dashboardProjectNames: [],
735
735
  issueRepository,
736
736
  resolveProject: async (pjcode) =>
737
737
  pjcode === 'umino' ? { pjcode, project: buildProject() } : null,
@@ -784,7 +784,7 @@ describe('webServer new routes integration', () => {
784
784
  inTmuxDataDir: null,
785
785
  dashboardDir: null,
786
786
  dashboardDataDir: null,
787
- dashboardProjectCodes: [],
787
+ dashboardProjectNames: [],
788
788
  port: 0,
789
789
  });
790
790
  try {
@@ -932,7 +932,7 @@ describe('webServer flat in-tmux-by-human route integration', () => {
932
932
  inTmuxDataDir,
933
933
  dashboardDir: null,
934
934
  dashboardDataDir: null,
935
- dashboardProjectCodes: [],
935
+ dashboardProjectNames: [],
936
936
  port: 0,
937
937
  });
938
938
  return { server, tmpDir, inTmuxDataDir };
@@ -1048,7 +1048,7 @@ describe('webServer flat in-tmux-by-human route integration', () => {
1048
1048
  inTmuxDataDir: null,
1049
1049
  dashboardDir: null,
1050
1050
  dashboardDataDir: null,
1051
- dashboardProjectCodes: [],
1051
+ dashboardProjectNames: [],
1052
1052
  port: 0,
1053
1053
  });
1054
1054
  try {
@@ -1121,9 +1121,9 @@ describe('webServer dashboard /tdpm.txt route integration', () => {
1121
1121
  const writeDataFiles = (dataDir: string): void => {
1122
1122
  fs.mkdirSync(path.join(dataDir, 'projects'), { recursive: true });
1123
1123
  fs.writeFileSync(
1124
- path.join(dataDir, 'projects', 'um.json'),
1124
+ path.join(dataDir, 'projects', 'umino.json'),
1125
1125
  JSON.stringify({
1126
- pjcode: 'um',
1126
+ pjcode: 'umino',
1127
1127
  capturedAt: '2026-06-26T00:00:00.000Z',
1128
1128
  unread: 3,
1129
1129
  todo: 1,
@@ -1136,9 +1136,9 @@ describe('webServer dashboard /tdpm.txt route integration', () => {
1136
1136
  }),
1137
1137
  );
1138
1138
  fs.writeFileSync(
1139
- path.join(dataDir, 'projects', 'xc.json'),
1139
+ path.join(dataDir, 'projects', 'xcare.json'),
1140
1140
  JSON.stringify({
1141
- pjcode: 'xc',
1141
+ pjcode: 'xcare',
1142
1142
  capturedAt: '2026-06-26T00:00:00.000Z',
1143
1143
  unread: 0,
1144
1144
  todo: 0,
@@ -1209,7 +1209,7 @@ describe('webServer dashboard /tdpm.txt route integration', () => {
1209
1209
  inTmuxDataDir: null,
1210
1210
  dashboardDir: overrides.dashboardDir,
1211
1211
  dashboardDataDir: overrides.dashboardDataDir,
1212
- dashboardProjectCodes: ['um', 'xc'],
1212
+ dashboardProjectNames: ['umino', 'xcare'],
1213
1213
  port: 0,
1214
1214
  });
1215
1215
  return { server, tmpDir };
@@ -1453,7 +1453,7 @@ describe('webServer image proxy', () => {
1453
1453
  inTmuxDataDir: null,
1454
1454
  dashboardDir: null,
1455
1455
  dashboardDataDir: null,
1456
- dashboardProjectCodes: [],
1456
+ dashboardProjectNames: [],
1457
1457
  githubToken: token,
1458
1458
  imageFetcher: fetcher,
1459
1459
  port: 0,
@@ -30,10 +30,11 @@ import {
30
30
  composeDashboardText,
31
31
  dashboardComposeFilesPresent,
32
32
  } from './dashboardComposeService';
33
+ import { DASHBOARD_PROJECT_NAMES } from '../../../domain/usecases/dashboard/DashboardProjectCode';
33
34
 
34
35
  export const DEFAULT_WEB_PORT = 9981;
35
36
 
36
- export const DEFAULT_DASHBOARD_PROJECT_CODES = ['um', 'xm', 'xc', 'ut'];
37
+ export const DEFAULT_DASHBOARD_PROJECT_NAMES = DASHBOARD_PROJECT_NAMES;
37
38
 
38
39
  export const CONSOLE_TOKEN_HEADER = 'x-pv-token';
39
40
 
@@ -165,7 +166,7 @@ export type WebServerOptions = {
165
166
  inTmuxDataDir: string | null;
166
167
  dashboardDir: string | null;
167
168
  dashboardDataDir: string | null;
168
- dashboardProjectCodes: string[];
169
+ dashboardProjectNames: string[];
169
170
  githubToken?: string | null;
170
171
  imageFetcher?: ImageFetcher | null;
171
172
  issueRepository?: IssueRepository | null;
@@ -555,12 +556,12 @@ export const resolveDashboardContent = (
555
556
  options.dashboardDataDir !== null &&
556
557
  dashboardComposeFilesPresent({
557
558
  dashboardDataDir: options.dashboardDataDir,
558
- projectCodes: options.dashboardProjectCodes,
559
+ projectNames: options.dashboardProjectNames,
559
560
  })
560
561
  ) {
561
562
  const dashboardText = composeDashboardText({
562
563
  dashboardDataDir: options.dashboardDataDir,
563
- projectCodes: options.dashboardProjectCodes,
564
+ projectNames: options.dashboardProjectNames,
564
565
  });
565
566
  return Buffer.from(dashboardText, 'utf-8');
566
567
  }