@imdeadpool/guardex 7.0.41 → 7.0.43

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 (85) hide show
  1. package/README.md +68 -13
  2. package/package.json +2 -1
  3. package/skills/gitguardex/SKILL.md +13 -0
  4. package/skills/guardex-merge-skills-to-dev/SKILL.md +59 -0
  5. package/src/agents/cleanup-sessions.js +126 -0
  6. package/src/agents/detect.js +160 -0
  7. package/src/agents/finish.js +172 -0
  8. package/src/agents/inspect.js +189 -0
  9. package/src/agents/launch.js +240 -0
  10. package/src/agents/registry.js +133 -0
  11. package/src/agents/selection-panel.js +571 -0
  12. package/src/agents/sessions.js +151 -0
  13. package/src/agents/start.js +591 -0
  14. package/src/agents/status.js +143 -0
  15. package/src/agents/terminal.js +152 -0
  16. package/src/budget/index.js +343 -0
  17. package/src/ci-init/index.js +265 -0
  18. package/src/cli/args.js +305 -1
  19. package/src/cli/main.js +262 -132
  20. package/src/cockpit/action-runner.js +3 -0
  21. package/src/cockpit/actions.js +80 -0
  22. package/src/cockpit/control.js +1121 -0
  23. package/src/cockpit/index.js +426 -0
  24. package/src/cockpit/keybindings.js +224 -0
  25. package/src/cockpit/kitty-layout.js +549 -0
  26. package/src/cockpit/kitty-tree.js +144 -0
  27. package/src/cockpit/layout.js +224 -0
  28. package/src/cockpit/logs-reader.js +182 -0
  29. package/src/cockpit/menu.js +204 -0
  30. package/src/cockpit/pane-actions.js +597 -0
  31. package/src/cockpit/pane-menu.js +387 -0
  32. package/src/cockpit/projects-finder.js +178 -0
  33. package/src/cockpit/render.js +215 -0
  34. package/src/cockpit/settings-render.js +128 -0
  35. package/src/cockpit/settings.js +124 -0
  36. package/src/cockpit/shortcuts.js +24 -0
  37. package/src/cockpit/sidebar.js +311 -0
  38. package/src/cockpit/state.js +72 -0
  39. package/src/cockpit/theme.js +128 -0
  40. package/src/cockpit/welcome.js +266 -0
  41. package/src/context.js +76 -33
  42. package/src/doctor/index.js +3 -2
  43. package/src/finish/index.js +39 -2
  44. package/src/git/index.js +65 -0
  45. package/src/kitty/command.js +101 -0
  46. package/src/kitty/runtime.js +250 -0
  47. package/src/output/index.js +1 -1
  48. package/src/pr-review.js +241 -0
  49. package/src/scaffold/index.js +19 -0
  50. package/src/submodule/index.js +288 -0
  51. package/src/terminal/index.js +120 -0
  52. package/src/terminal/kitty.js +622 -0
  53. package/src/terminal/tmux.js +126 -0
  54. package/src/tmux/command.js +27 -0
  55. package/src/tmux/session.js +89 -0
  56. package/templates/AGENTS.multiagent-safety.md +27 -1
  57. package/templates/codex/skills/gitguardex/SKILL.md +2 -0
  58. package/templates/githooks/pre-commit +22 -1
  59. package/templates/github/workflows/README.md +87 -0
  60. package/templates/github/workflows/ci-full.yml +55 -0
  61. package/templates/github/workflows/ci.yml +56 -0
  62. package/templates/github/workflows/cr.yml +20 -1
  63. package/templates/scripts/agent-branch-finish.sh +544 -26
  64. package/templates/scripts/agent-branch-start.sh +89 -22
  65. package/templates/scripts/agent-preflight.sh +89 -0
  66. package/templates/scripts/agent-worktree-prune.sh +96 -5
  67. package/templates/scripts/codex-agent.sh +41 -6
  68. package/templates/scripts/openspec/init-plan-workspace.sh +43 -0
  69. package/templates/scripts/review-bot-watch.sh +31 -2
  70. package/templates/scripts/agent-session-state.js +0 -171
  71. package/templates/scripts/install-vscode-active-agents-extension.js +0 -135
  72. package/templates/vscode/guardex-active-agents/README.md +0 -34
  73. package/templates/vscode/guardex-active-agents/extension.js +0 -3782
  74. package/templates/vscode/guardex-active-agents/fileicons/gitguardex-fileicons.json +0 -54
  75. package/templates/vscode/guardex-active-agents/fileicons/icons/agent.svg +0 -5
  76. package/templates/vscode/guardex-active-agents/fileicons/icons/branch.svg +0 -7
  77. package/templates/vscode/guardex-active-agents/fileicons/icons/config.svg +0 -4
  78. package/templates/vscode/guardex-active-agents/fileicons/icons/hook.svg +0 -4
  79. package/templates/vscode/guardex-active-agents/fileicons/icons/openspec.svg +0 -5
  80. package/templates/vscode/guardex-active-agents/fileicons/icons/plan.svg +0 -4
  81. package/templates/vscode/guardex-active-agents/fileicons/icons/spec.svg +0 -5
  82. package/templates/vscode/guardex-active-agents/icon.png +0 -0
  83. package/templates/vscode/guardex-active-agents/media/active-agents-hivemind.svg +0 -14
  84. package/templates/vscode/guardex-active-agents/package.json +0 -169
  85. package/templates/vscode/guardex-active-agents/session-schema.js +0 -1348
@@ -0,0 +1,311 @@
1
+ const path = require('node:path');
2
+ const { colorize, getCockpitTheme } = require('./theme');
3
+
4
+ const DEFAULT_WIDTH = 36;
5
+ const MIN_WIDTH = 12;
6
+
7
+ const STATUS_STATES = new Map([
8
+ ['active', 'active'],
9
+ ['running', 'active'],
10
+ ['working', 'active'],
11
+ ['thinking', 'waiting'],
12
+ ['idle', 'waiting'],
13
+ ['ready', 'waiting'],
14
+ ['waiting', 'waiting'],
15
+ ['done', 'done'],
16
+ ['complete', 'done'],
17
+ ['completed', 'done'],
18
+ ['merged', 'done'],
19
+ ['blocked', 'blocked'],
20
+ ['error', 'failed'],
21
+ ['failed', 'failed'],
22
+ ['stalled', 'stalled'],
23
+ ['dead', 'stalled'],
24
+ ]);
25
+
26
+ const AGENT_LABELS = new Map([
27
+ ['codex', 'cx'],
28
+ ['claude', 'cc'],
29
+ ['claude-code', 'cc'],
30
+ ['claudecode', 'cc'],
31
+ ['cursor', 'cu'],
32
+ ['gemini', 'gm'],
33
+ ]);
34
+
35
+ function text(value, fallback = '') {
36
+ if (typeof value === 'string') {
37
+ return value.trim() || fallback;
38
+ }
39
+ if (value === null || value === undefined) {
40
+ return fallback;
41
+ }
42
+ return String(value).trim() || fallback;
43
+ }
44
+
45
+ function sidebarWidth(options = {}) {
46
+ const width = Number(options.width);
47
+ if (!Number.isFinite(width)) {
48
+ return DEFAULT_WIDTH;
49
+ }
50
+ return Math.max(MIN_WIDTH, Math.floor(width));
51
+ }
52
+
53
+ function truncate(value, width) {
54
+ const raw = value === null || value === undefined ? '' : String(value);
55
+ if (width <= 0) {
56
+ return '';
57
+ }
58
+ if (raw.length <= width) {
59
+ return raw;
60
+ }
61
+ if (width <= 3) {
62
+ return raw.slice(0, width);
63
+ }
64
+ return `${raw.slice(0, width - 3)}...`;
65
+ }
66
+
67
+ function boundLine(value, width) {
68
+ return truncate(value, width);
69
+ }
70
+
71
+ function repoName(state = {}, options = {}) {
72
+ const explicit = text(options.repoName || state.repoName || state.projectName || state.repo);
73
+ if (explicit) {
74
+ return explicit;
75
+ }
76
+
77
+ const repoPath = text(state.repoPath);
78
+ if (!repoPath) {
79
+ return '-';
80
+ }
81
+ return path.basename(repoPath) || repoPath;
82
+ }
83
+
84
+ function agentLabel(agentName) {
85
+ const raw = text(agentName, 'agent').toLowerCase();
86
+ const compact = raw.replace(/[^a-z0-9]/g, '');
87
+ if (AGENT_LABELS.has(raw)) {
88
+ return AGENT_LABELS.get(raw);
89
+ }
90
+ if (AGENT_LABELS.has(compact)) {
91
+ return AGENT_LABELS.get(compact);
92
+ }
93
+ if (compact.includes('codex')) {
94
+ return 'cx';
95
+ }
96
+ if (compact.includes('claude')) {
97
+ return 'cc';
98
+ }
99
+
100
+ const parts = raw.match(/[a-z0-9]+/g) || [];
101
+ if (parts.length >= 2) {
102
+ return `${parts[0][0]}${parts[1][0]}`;
103
+ }
104
+ return truncate(parts[0] || compact || 'ag', 2).padEnd(2, 'g');
105
+ }
106
+
107
+ function statusDot(session = {}) {
108
+ const status = laneState(session);
109
+ if (status === 'active') {
110
+ return '*';
111
+ }
112
+ if (status === 'waiting') {
113
+ return 'o';
114
+ }
115
+ if (status === 'done') {
116
+ return '+';
117
+ }
118
+ if (status === 'missing') {
119
+ return 'x';
120
+ }
121
+ if (status === 'blocked' || status === 'failed' || status === 'stalled') {
122
+ return '!';
123
+ }
124
+ return '.';
125
+ }
126
+
127
+ function laneState(session = {}) {
128
+ const status = text(session.status, 'unknown').toLowerCase();
129
+ if (session.hidden === true || session.visible === false || status === 'hidden') {
130
+ return 'hidden';
131
+ }
132
+ if (session.closed === true || session.closedAt || status === 'closed') {
133
+ return 'closed';
134
+ }
135
+ if (session.worktreeExists === false || session.worktreeMissing === true || status === 'missing' || status === 'missing-worktree') {
136
+ return 'missing';
137
+ }
138
+ return STATUS_STATES.get(status) || status || 'unknown';
139
+ }
140
+
141
+ function sessionId(session = {}) {
142
+ return text(session.id || session.sessionId || session.branch);
143
+ }
144
+
145
+ function isSelected(session, index, state = {}, options = {}) {
146
+ const selectedId = text(options.selectedId || options.selectedSessionId || state.selectedId || state.selectedSessionId);
147
+ if (selectedId && sessionId(session) === selectedId) {
148
+ return true;
149
+ }
150
+
151
+ const selectedBranch = text(options.selectedBranch || state.selectedBranch);
152
+ if (selectedBranch && text(session.branch) === selectedBranch) {
153
+ return true;
154
+ }
155
+
156
+ const selectedIndex = Number.isInteger(options.selectedIndex) ? options.selectedIndex : state.selectedIndex;
157
+ return Number.isInteger(selectedIndex) && selectedIndex === index;
158
+ }
159
+
160
+ function statusToken(status) {
161
+ if (status === 'active' || status === 'done') {
162
+ return 'success';
163
+ }
164
+ if (status === 'waiting') {
165
+ return 'warning';
166
+ }
167
+ if (status === 'blocked' || status === 'failed' || status === 'stalled' || status === 'missing') {
168
+ return status === 'stalled' ? 'warning' : 'danger';
169
+ }
170
+ if (status === 'hidden' || status === 'closed') {
171
+ return 'secondary';
172
+ }
173
+ return 'accent';
174
+ }
175
+
176
+ function laneName(session = {}) {
177
+ const task = text(session.task || session.name || session.title);
178
+ if (task) {
179
+ return task;
180
+ }
181
+
182
+ const branch = text(session.branch);
183
+ if (!branch) {
184
+ return '(no task)';
185
+ }
186
+ return path.basename(branch);
187
+ }
188
+
189
+ function fitRow(left, right, width) {
190
+ if (width <= 0) {
191
+ return '';
192
+ }
193
+
194
+ if (right.length >= width - 2) {
195
+ return truncate(`${left}${right}`, width);
196
+ }
197
+
198
+ const leftWidth = width - right.length;
199
+ return `${truncate(left, leftWidth).padEnd(leftWidth, ' ')}${right}`;
200
+ }
201
+
202
+ function kittyTreeOf(state = {}) {
203
+ const tree = state && typeof state === 'object' ? state.kittyTree : null;
204
+ return tree && typeof tree === 'object' ? tree : null;
205
+ }
206
+
207
+ function classifyTag(window = {}) {
208
+ if (window.kind === 'control') return 'gx';
209
+ if (window.kind === 'agent') return 'cx';
210
+ if (window.kind === 'shell') return 'ba';
211
+ return 'sh';
212
+ }
213
+
214
+ function windowLabel(window = {}, fallback) {
215
+ const explicit = text(window.title);
216
+ if (explicit) return explicit;
217
+ if (window.kind === 'control') return 'gx cockpit';
218
+ if (Array.isArray(window.cmdline) && window.cmdline.length > 0) {
219
+ return path.basename(text(window.cmdline[0])) || fallback;
220
+ }
221
+ return fallback;
222
+ }
223
+
224
+ function renderKittyTreeRows(state, width, options = {}) {
225
+ const tree = kittyTreeOf(state);
226
+ if (!tree) return [];
227
+ const theme = getCockpitTheme(options.theme || (state.settings && state.settings.theme), options);
228
+ const windows = Array.isArray(tree.windows) ? tree.windows : [];
229
+ const lines = [];
230
+ lines.push(colorize(boundLine(text(tree.user, 'user'), width), 'title', theme));
231
+ lines.push(colorize(boundLine(` ${text(tree.sessionLabel, 'session')}`, width), 'secondary', theme));
232
+ if (windows.length === 0) {
233
+ lines.push(colorize(boundLine(' no kitty panes detected', width), 'secondary', theme));
234
+ } else {
235
+ windows.forEach((window, index) => {
236
+ const cursor = window.isFocused ? '>' : ' ';
237
+ const label = windowLabel(window, `pane-${index + 1}`);
238
+ const tag = classifyTag(window);
239
+ const row = ` ${cursor} ${label}`.padEnd(Math.max(width - 6, 6), ' ') + ` [${tag}]`;
240
+ const token = window.isFocused ? 'selected' : 'secondary';
241
+ lines.push(colorize(boundLine(row, width), token, theme));
242
+ });
243
+ }
244
+ if (tree.error) {
245
+ lines.push(colorize(boundLine(` (kitty: ${tree.error})`, width), 'secondary', theme));
246
+ }
247
+ return lines;
248
+ }
249
+
250
+ function renderShortcutRows(width, options) {
251
+ const theme = getCockpitTheme(options.theme, options);
252
+ const rows = [
253
+ ' [n]ew agent [t]erminal',
254
+ ' [l]ogs [p]rojects',
255
+ ' [s]ettings [?] shortcuts',
256
+ ];
257
+ return rows.map((row) => colorize(boundLine(row, width), 'secondary', theme));
258
+ }
259
+
260
+ function renderSessionRow(session, index, state, options) {
261
+ const width = sidebarWidth(options);
262
+ const theme = getCockpitTheme(options.theme || state.theme || (state.settings && state.settings.theme), options);
263
+ const selected = isSelected(session, index, state, options);
264
+ const marker = selected ? '>' : ' ';
265
+ const status = laneState(session);
266
+ const badge = `[${agentLabel(session.agentName || session.agent || session.owner)}] (${status})`;
267
+ const row = fitRow(`${marker} ${laneName(session)}`, ` ${badge}`, width);
268
+
269
+ return selected
270
+ ? colorize(row, 'selected', theme)
271
+ : colorize(row, statusToken(status), theme);
272
+ }
273
+
274
+ function renderSidebar(state = {}, options = {}) {
275
+ const width = sidebarWidth(options);
276
+ const theme = getCockpitTheme(options.theme || state.theme || (state.settings && state.settings.theme), options);
277
+ const title = text(options.title || state.title, 'gx cockpit').toLowerCase() === 'gitguardex'
278
+ ? 'gitguardex'
279
+ : text(options.title || state.title, 'gx cockpit');
280
+ const sessions = Array.isArray(state.sessions) ? state.sessions : [];
281
+ const lines = [
282
+ colorize(boundLine(title, width), 'title', theme),
283
+ colorize(boundLine(repoName(state, options), width), 'secondary', theme),
284
+ ];
285
+
286
+ if (sessions.length === 0) {
287
+ lines.push(boundLine(' no agent lanes', width));
288
+ } else {
289
+ sessions.forEach((session, index) => {
290
+ lines.push(renderSessionRow(session, index, state, options));
291
+ });
292
+ }
293
+
294
+ const treeRows = renderKittyTreeRows(state, width, options);
295
+ if (treeRows.length > 0) {
296
+ lines.push('');
297
+ lines.push(...treeRows);
298
+ }
299
+
300
+ lines.push(...renderShortcutRows(width, options));
301
+
302
+ return `${lines.join('\n')}\n`;
303
+ }
304
+
305
+ module.exports = {
306
+ renderSidebar,
307
+ agentLabel,
308
+ laneState,
309
+ statusDot,
310
+ truncate,
311
+ };
@@ -0,0 +1,72 @@
1
+ const path = require('node:path');
2
+ const cp = require('node:child_process');
3
+ const { buildAgentsStatusPayload } = require('../agents/status');
4
+
5
+ function text(value, fallback = '') {
6
+ if (typeof value === 'string') {
7
+ return value.trim() || fallback;
8
+ }
9
+ if (value === null || value === undefined) {
10
+ return fallback;
11
+ }
12
+ return String(value).trim() || fallback;
13
+ }
14
+
15
+ function readGitValue(repoPath, args) {
16
+ try {
17
+ return cp.execFileSync('git', ['-C', repoPath, ...args], {
18
+ encoding: 'utf8',
19
+ stdio: ['ignore', 'pipe', 'ignore'],
20
+ }).trim();
21
+ } catch (_error) {
22
+ return '';
23
+ }
24
+ }
25
+
26
+ function readBaseBranch(repoPath) {
27
+ const configured = readGitValue(repoPath, ['config', '--get', 'multiagent.baseBranch']);
28
+ if (configured) {
29
+ return configured;
30
+ }
31
+
32
+ const originHead = readGitValue(repoPath, ['symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD']);
33
+ return originHead.replace(/^origin\//, '');
34
+ }
35
+
36
+ function cockpitSessionFromStatus(session) {
37
+ return {
38
+ id: text(session.id),
39
+ agentName: text(session.agent, 'agent'),
40
+ branch: text(session.branch, '(unknown branch)'),
41
+ base: text(session.base),
42
+ worktreePath: text(session.worktreePath, '(unknown worktree)'),
43
+ worktreeExists: Boolean(session.worktreeExists),
44
+ status: text(session.status, 'unknown'),
45
+ activity: text(session.activity),
46
+ task: text(session.task),
47
+ lockCount: Number.isFinite(session.lockCount) ? session.lockCount : 0,
48
+ claimedFiles: Array.isArray(session.claimedFiles) ? session.claimedFiles : [],
49
+ changedFiles: Array.isArray(session.changedFiles) ? session.changedFiles : [],
50
+ metadata: session.metadata && typeof session.metadata === 'object' ? session.metadata : {},
51
+ prUrl: text(session.prUrl),
52
+ prState: text(session.prState),
53
+ };
54
+ }
55
+
56
+ function readCockpitState(repoPath = process.cwd()) {
57
+ const resolvedRepoPath = path.resolve(repoPath);
58
+ const statusPayload = buildAgentsStatusPayload(resolvedRepoPath);
59
+
60
+ return {
61
+ repoPath: resolvedRepoPath,
62
+ baseBranch: readBaseBranch(resolvedRepoPath),
63
+ agentsStatus: statusPayload,
64
+ sessions: statusPayload.sessions.map(cockpitSessionFromStatus),
65
+ };
66
+ }
67
+
68
+ module.exports = {
69
+ readCockpitState,
70
+ readBaseBranch,
71
+ cockpitSessionFromStatus,
72
+ };
@@ -0,0 +1,128 @@
1
+ 'use strict';
2
+
3
+ const RESET = '\x1b[0m';
4
+ const ANSI_PATTERN = /\x1b\[[0-?]*[ -/]*[@-~]/g;
5
+
6
+ const THEME_NAMES = Object.freeze(['blue', 'amber', 'dim', 'high-contrast', 'none']);
7
+
8
+ const PALETTES = Object.freeze({
9
+ blue: {
10
+ accent: '\x1b[36m',
11
+ accentStrong: '\x1b[1;36m',
12
+ border: '\x1b[34m',
13
+ danger: '\x1b[31m',
14
+ secondary: '\x1b[2;90m',
15
+ selected: '\x1b[7;36m',
16
+ success: '\x1b[32m',
17
+ warning: '\x1b[33m',
18
+ },
19
+ amber: {
20
+ accent: '\x1b[33m',
21
+ accentStrong: '\x1b[1;33m',
22
+ border: '\x1b[33m',
23
+ danger: '\x1b[31m',
24
+ secondary: '\x1b[2;90m',
25
+ selected: '\x1b[7;33m',
26
+ success: '\x1b[32m',
27
+ warning: '\x1b[93m',
28
+ },
29
+ dim: {
30
+ accent: '\x1b[2;36m',
31
+ accentStrong: '\x1b[36m',
32
+ border: '\x1b[2;37m',
33
+ danger: '\x1b[2;31m',
34
+ secondary: '\x1b[2;90m',
35
+ selected: '\x1b[7;2m',
36
+ success: '\x1b[2;32m',
37
+ warning: '\x1b[2;33m',
38
+ },
39
+ 'high-contrast': {
40
+ accent: '\x1b[1;37m',
41
+ accentStrong: '\x1b[1;97m',
42
+ border: '\x1b[1;37m',
43
+ danger: '\x1b[1;31m',
44
+ secondary: '\x1b[37m',
45
+ selected: '\x1b[7;1m',
46
+ success: '\x1b[1;32m',
47
+ warning: '\x1b[1;33m',
48
+ },
49
+ none: {},
50
+ });
51
+
52
+ const TOKEN_ALIASES = Object.freeze({
53
+ active: 'success',
54
+ complete: 'success',
55
+ completed: 'success',
56
+ done: 'success',
57
+ error: 'danger',
58
+ failed: 'danger',
59
+ heading: 'accentStrong',
60
+ hidden: 'secondary',
61
+ idle: 'secondary',
62
+ info: 'accent',
63
+ missing: 'danger',
64
+ muted: 'secondary',
65
+ primary: 'accent',
66
+ stalled: 'warning',
67
+ title: 'accentStrong',
68
+ waiting: 'warning',
69
+ });
70
+
71
+ function stripAnsi(text) {
72
+ return String(text || '').replace(ANSI_PATTERN, '');
73
+ }
74
+
75
+ function hasNoColor(options = {}) {
76
+ if (options.noColor === true || options.color === false) {
77
+ return true;
78
+ }
79
+
80
+ const env = options.env && typeof options.env === 'object' ? options.env : process.env;
81
+ if (Object.prototype.hasOwnProperty.call(env, 'NO_COLOR')) {
82
+ return true;
83
+ }
84
+
85
+ const argv = Array.isArray(options.argv) ? options.argv : process.argv;
86
+ return argv.includes('--no-color');
87
+ }
88
+
89
+ function normalizeThemeName(name) {
90
+ const requested = typeof name === 'string' ? name.trim().toLowerCase() : '';
91
+ if (requested === '' || requested === 'default') {
92
+ return 'blue';
93
+ }
94
+ return Object.prototype.hasOwnProperty.call(PALETTES, requested) ? requested : 'blue';
95
+ }
96
+
97
+ function getCockpitTheme(name = 'blue', options = {}) {
98
+ const themeName = normalizeThemeName(name);
99
+ const noColor = themeName === 'none' || hasNoColor(options);
100
+ return {
101
+ name: themeName,
102
+ color: !noColor,
103
+ reset: RESET,
104
+ tokens: noColor ? PALETTES.none : PALETTES[themeName],
105
+ available: THEME_NAMES,
106
+ };
107
+ }
108
+
109
+ function colorize(text, token, theme) {
110
+ const value = String(text || '');
111
+ const current = theme && typeof theme === 'object' && theme.tokens
112
+ ? theme
113
+ : getCockpitTheme(theme);
114
+
115
+ if (!current.color) {
116
+ return value;
117
+ }
118
+
119
+ const resolvedToken = TOKEN_ALIASES[token] || token;
120
+ const code = current.tokens[resolvedToken];
121
+ return code ? `${code}${value}${RESET}` : value;
122
+ }
123
+
124
+ module.exports = {
125
+ getCockpitTheme,
126
+ colorize,
127
+ stripAnsi,
128
+ };