@imdeadpool/guardex 7.0.41 → 7.1.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 (118) hide show
  1. package/README.md +94 -13
  2. package/package.json +3 -1
  3. package/skills/gitguardex/SKILL.md +13 -0
  4. package/skills/guardex-merge-skills-to-dev/SKILL.md +59 -0
  5. package/skills/gx-act/SKILL.md +82 -0
  6. package/src/agents/cleanup-sessions.js +126 -0
  7. package/src/agents/finish.js +172 -0
  8. package/src/agents/inspect.js +202 -0
  9. package/src/agents/launch.js +249 -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 +146 -0
  15. package/src/agents/terminal.js +152 -0
  16. package/src/budget/index.js +344 -0
  17. package/src/ci-init/index.js +265 -0
  18. package/src/cli/args.js +357 -3
  19. package/src/cli/commands/agents.js +364 -0
  20. package/src/cli/commands/bootstrap.js +92 -0
  21. package/src/cli/commands/branch.js +127 -0
  22. package/src/cli/commands/claude.js +674 -0
  23. package/src/cli/commands/doctor.js +268 -0
  24. package/src/cli/commands/finish.js +26 -0
  25. package/src/cli/commands/mcp.js +122 -0
  26. package/src/cli/commands/misc.js +304 -0
  27. package/src/cli/commands/pr.js +439 -0
  28. package/src/cli/commands/prompt.js +92 -0
  29. package/src/cli/commands/release.js +305 -0
  30. package/src/cli/commands/report.js +244 -0
  31. package/src/cli/commands/review.js +32 -0
  32. package/src/cli/commands/setup.js +242 -0
  33. package/src/cli/commands/status.js +338 -0
  34. package/src/cli/commands/watch.js +234 -0
  35. package/src/cli/main.js +85 -3613
  36. package/src/cli/shared/repo-env.js +161 -0
  37. package/src/cli/shared/sandbox.js +417 -0
  38. package/src/cli/shared/scaffolding.js +535 -0
  39. package/src/cli/shared/toolchain-shims.js +420 -0
  40. package/src/cockpit/action-runner.js +3 -0
  41. package/src/cockpit/actions.js +80 -0
  42. package/src/cockpit/control.js +1121 -0
  43. package/src/cockpit/index.js +426 -0
  44. package/src/cockpit/kitty-layout.js +549 -0
  45. package/src/cockpit/kitty-tree.js +144 -0
  46. package/src/cockpit/logs-reader.js +182 -0
  47. package/src/cockpit/menu.js +204 -0
  48. package/src/cockpit/pane-actions.js +597 -0
  49. package/src/cockpit/pane-menu.js +387 -0
  50. package/src/cockpit/projects-finder.js +178 -0
  51. package/src/cockpit/render.js +215 -0
  52. package/src/cockpit/settings-render.js +128 -0
  53. package/src/cockpit/settings.js +124 -0
  54. package/src/cockpit/shortcuts.js +24 -0
  55. package/src/cockpit/sidebar.js +311 -0
  56. package/src/cockpit/state.js +72 -0
  57. package/src/cockpit/theme.js +128 -0
  58. package/src/cockpit/welcome.js +266 -0
  59. package/src/context.js +304 -43
  60. package/src/core/runtime.js +6 -1
  61. package/src/doctor/index.js +45 -15
  62. package/src/finish/index.js +186 -7
  63. package/src/finish/preflight.js +177 -0
  64. package/src/finish/review-gate.js +182 -0
  65. package/src/git/index.js +511 -4
  66. package/src/hooks/index.js +0 -64
  67. package/src/kitty/command.js +101 -0
  68. package/src/kitty/runtime.js +250 -0
  69. package/src/mcp/collect.js +370 -0
  70. package/src/mcp/server.js +157 -0
  71. package/src/output/index.js +68 -2
  72. package/src/pr-review.js +264 -0
  73. package/src/pr.js +381 -0
  74. package/src/sandbox/index.js +13 -2
  75. package/src/scaffold/agent-worktree-prep.js +213 -0
  76. package/src/scaffold/index.js +127 -10
  77. package/src/speckit/index.js +226 -0
  78. package/src/submodule/index.js +288 -0
  79. package/src/terminal/index.js +45 -0
  80. package/src/terminal/kitty.js +622 -0
  81. package/src/terminal/tmux.js +125 -0
  82. package/src/tmux/command.js +27 -0
  83. package/src/tmux/session.js +89 -0
  84. package/src/toolchain/index.js +20 -0
  85. package/templates/AGENTS.monorepo-apps.md +26 -0
  86. package/templates/AGENTS.multiagent-safety.md +63 -323
  87. package/templates/AGENTS.multiagent-safety.min.md +11 -0
  88. package/templates/codex/skills/gitguardex/SKILL.md +2 -0
  89. package/templates/codex/skills/gx-act/SKILL.md +82 -0
  90. package/templates/githooks/pre-commit +44 -20
  91. package/templates/github/workflows/README.md +87 -0
  92. package/templates/github/workflows/ci-full.yml +55 -0
  93. package/templates/github/workflows/ci.yml +56 -0
  94. package/templates/github/workflows/cr.yml +20 -1
  95. package/templates/scripts/agent-branch-finish.sh +519 -23
  96. package/templates/scripts/agent-branch-merge.sh +4 -1
  97. package/templates/scripts/agent-branch-start.sh +176 -24
  98. package/templates/scripts/agent-preflight.sh +115 -0
  99. package/templates/scripts/agent-worktree-prune.sh +96 -5
  100. package/templates/scripts/codex-agent.sh +41 -97
  101. package/templates/scripts/openspec/init-plan-workspace.sh +43 -0
  102. package/templates/scripts/review-bot-watch.sh +31 -2
  103. package/templates/scripts/agent-session-state.js +0 -171
  104. package/templates/scripts/install-vscode-active-agents-extension.js +0 -135
  105. package/templates/vscode/guardex-active-agents/README.md +0 -34
  106. package/templates/vscode/guardex-active-agents/extension.js +0 -3782
  107. package/templates/vscode/guardex-active-agents/fileicons/gitguardex-fileicons.json +0 -54
  108. package/templates/vscode/guardex-active-agents/fileicons/icons/agent.svg +0 -5
  109. package/templates/vscode/guardex-active-agents/fileicons/icons/branch.svg +0 -7
  110. package/templates/vscode/guardex-active-agents/fileicons/icons/config.svg +0 -4
  111. package/templates/vscode/guardex-active-agents/fileicons/icons/hook.svg +0 -4
  112. package/templates/vscode/guardex-active-agents/fileicons/icons/openspec.svg +0 -5
  113. package/templates/vscode/guardex-active-agents/fileicons/icons/plan.svg +0 -4
  114. package/templates/vscode/guardex-active-agents/fileicons/icons/spec.svg +0 -5
  115. package/templates/vscode/guardex-active-agents/icon.png +0 -0
  116. package/templates/vscode/guardex-active-agents/media/active-agents-hivemind.svg +0 -14
  117. package/templates/vscode/guardex-active-agents/package.json +0 -169
  118. package/templates/vscode/guardex-active-agents/session-schema.js +0 -1348
@@ -0,0 +1,426 @@
1
+ const { readCockpitState } = require('./state');
2
+ const { readCockpitSettings } = require('./settings');
3
+ const { renderCockpit } = require('./render');
4
+ const { openKittyCockpit } = require('./kitty-layout');
5
+ const control = require('./control');
6
+ const actions = require('./actions');
7
+ const { normalizeBackendName, selectTerminalBackend } = require('../terminal');
8
+
9
+ const DEFAULT_SESSION_NAME = 'guardex';
10
+ const DEFAULT_BACKEND = 'auto';
11
+ const DEFAULT_INTERACTIVE_BACKEND = 'auto';
12
+
13
+ function parseCockpitArgs(rawArgs = []) {
14
+ const options = {
15
+ sessionName: DEFAULT_SESSION_NAME,
16
+ backend: process.env.GUARDEX_COCKPIT_BACKEND || DEFAULT_BACKEND,
17
+ attach: false,
18
+ target: process.cwd(),
19
+ host: undefined,
20
+ socket: undefined,
21
+ };
22
+
23
+ for (let index = 0; index < rawArgs.length; index += 1) {
24
+ const arg = rawArgs[index];
25
+ if (arg === '--attach') {
26
+ options.attach = true;
27
+ continue;
28
+ }
29
+ if (arg === '--kitty') {
30
+ options.backend = 'kitty';
31
+ continue;
32
+ }
33
+ if (arg === '--host' || arg === '--bootstrap-kitty') {
34
+ options.host = true;
35
+ if (!options.backend || options.backend === 'auto' || options.backend === DEFAULT_BACKEND) {
36
+ options.backend = 'kitty';
37
+ }
38
+ continue;
39
+ }
40
+ if (arg === '--no-host' || arg === '--no-bootstrap-kitty') {
41
+ options.host = false;
42
+ continue;
43
+ }
44
+ if (arg === '--socket') {
45
+ const next = rawArgs[index + 1];
46
+ if (!next || next.startsWith('-')) {
47
+ throw new Error('--socket requires a path');
48
+ }
49
+ options.socket = next;
50
+ if (options.host !== false) options.host = true;
51
+ if (!options.backend || options.backend === 'auto' || options.backend === DEFAULT_BACKEND) {
52
+ options.backend = 'kitty';
53
+ }
54
+ index += 1;
55
+ continue;
56
+ }
57
+ if (arg.startsWith('--socket=')) {
58
+ const next = arg.slice('--socket='.length);
59
+ if (!next) throw new Error('--socket requires a path');
60
+ options.socket = next;
61
+ if (options.host !== false) options.host = true;
62
+ if (!options.backend || options.backend === 'auto' || options.backend === DEFAULT_BACKEND) {
63
+ options.backend = 'kitty';
64
+ }
65
+ continue;
66
+ }
67
+ if (arg === '--session') {
68
+ const next = rawArgs[index + 1];
69
+ if (!next || next.startsWith('-')) {
70
+ throw new Error('--session requires a tmux session name');
71
+ }
72
+ options.sessionName = next;
73
+ index += 1;
74
+ continue;
75
+ }
76
+ if (arg.startsWith('--session=')) {
77
+ options.sessionName = arg.slice('--session='.length);
78
+ if (!options.sessionName) {
79
+ throw new Error('--session requires a tmux session name');
80
+ }
81
+ continue;
82
+ }
83
+ if (arg === '--backend') {
84
+ const next = rawArgs[index + 1];
85
+ if (!next || next.startsWith('-')) {
86
+ throw new Error('--backend requires auto, kitty, or tmux');
87
+ }
88
+ options.backend = normalizeBackendName(next);
89
+ index += 1;
90
+ continue;
91
+ }
92
+ if (arg.startsWith('--backend=')) {
93
+ const next = arg.slice('--backend='.length);
94
+ if (!next) {
95
+ throw new Error('--backend requires auto, kitty, or tmux');
96
+ }
97
+ options.backend = normalizeBackendName(next);
98
+ continue;
99
+ }
100
+ if (arg === '--target' || arg === '-t') {
101
+ const next = rawArgs[index + 1];
102
+ if (!next || next.startsWith('-')) {
103
+ throw new Error(`${arg} requires a repo path`);
104
+ }
105
+ options.target = next;
106
+ index += 1;
107
+ continue;
108
+ }
109
+ throw new Error(`Unknown cockpit option: ${arg}`);
110
+ }
111
+
112
+ return options;
113
+ }
114
+
115
+ function parseCockpitControlArgs(rawArgs = []) {
116
+ const options = {
117
+ refreshMs: undefined,
118
+ target: process.cwd(),
119
+ };
120
+
121
+ for (let index = 0; index < rawArgs.length; index += 1) {
122
+ const arg = rawArgs[index];
123
+ if (arg === '--target' || arg === '-t') {
124
+ const next = rawArgs[index + 1];
125
+ if (!next || next.startsWith('-')) {
126
+ throw new Error(`${arg} requires a repo path`);
127
+ }
128
+ options.target = next;
129
+ index += 1;
130
+ continue;
131
+ }
132
+ if (arg === '--refresh-ms') {
133
+ const next = rawArgs[index + 1];
134
+ if (!next || next.startsWith('-')) {
135
+ throw new Error('--refresh-ms requires a positive integer');
136
+ }
137
+ options.refreshMs = Number.parseInt(next, 10);
138
+ if (!Number.isFinite(options.refreshMs) || options.refreshMs <= 0) {
139
+ throw new Error('--refresh-ms requires a positive integer');
140
+ }
141
+ index += 1;
142
+ continue;
143
+ }
144
+ if (arg.startsWith('--refresh-ms=')) {
145
+ options.refreshMs = Number.parseInt(arg.slice('--refresh-ms='.length), 10);
146
+ if (!Number.isFinite(options.refreshMs) || options.refreshMs <= 0) {
147
+ throw new Error('--refresh-ms requires a positive integer');
148
+ }
149
+ continue;
150
+ }
151
+ throw new Error(`Unknown cockpit control option: ${arg}`);
152
+ }
153
+
154
+ return options;
155
+ }
156
+
157
+ function shellQuote(value) {
158
+ return `'${String(value).replace(/'/g, "'\\''")}'`;
159
+ }
160
+
161
+ function cockpitControlCommand(repoRoot, options = {}) {
162
+ const refresh = Number.isFinite(options.refreshMs) && options.refreshMs > 0
163
+ ? ` --refresh-ms ${Math.floor(options.refreshMs)}`
164
+ : '';
165
+ return `gx cockpit control --target ${shellQuote(repoRoot)}${refresh}`;
166
+ }
167
+
168
+ function terminalBackendOptionsFromDeps(deps = {}) {
169
+ const terminalBackendOptions = { ...(deps.terminalBackendOptions || {}) };
170
+ if (deps.terminalBackends && deps.terminalBackends.kitty) {
171
+ terminalBackendOptions.kittyBackend = deps.terminalBackends.kitty;
172
+ }
173
+ if (deps.terminalBackends && deps.terminalBackends.tmux) {
174
+ terminalBackendOptions.tmuxBackend = deps.terminalBackends.tmux;
175
+ }
176
+ if (deps.tmux) {
177
+ terminalBackendOptions.tmux = { tmux: deps.tmux };
178
+ }
179
+ return terminalBackendOptions;
180
+ }
181
+
182
+ function writeOpenedCockpitMessage({ backend, action, options, repoRoot, controlCommand, stdout, toolName }) {
183
+ if (backend.name === 'tmux' && action === 'attached') {
184
+ stdout.write(`[${toolName}] Attaching tmux session '${options.sessionName}'.\n`);
185
+ return;
186
+ }
187
+
188
+ if (backend.name === 'tmux') {
189
+ stdout.write(`[${toolName}] Created tmux session '${options.sessionName}' in ${repoRoot}.\n`);
190
+ } else {
191
+ stdout.write(`[${toolName}] Created ${backend.name} cockpit window '${options.sessionName}' in ${repoRoot}.\n`);
192
+ }
193
+ stdout.write(`[${toolName}] Control pane: ${controlCommand}\n`);
194
+ }
195
+
196
+ function shouldAutoHost(options = {}, context = {}) {
197
+ if (options.host === true || options.host === false) return false;
198
+ const env = context.env || process.env;
199
+ const optOut = String(env.GUARDEX_AUTO_HOST || '').trim().toLowerCase();
200
+ if (optOut === '0' || optOut === 'false' || optOut === 'no' || optOut === 'off') return false;
201
+ if (env.KITTY_LISTEN_ON) return false;
202
+ const stdout = context.stdout || process.stdout;
203
+ return Boolean(stdout && stdout.isTTY);
204
+ }
205
+
206
+ function openWithBackend(backend, options, repoRoot, controlCommand, deps = {}) {
207
+ const stdout = deps.stdout || process.stdout;
208
+ const toolName = deps.toolName || 'gitguardex';
209
+ const env = deps.env || process.env;
210
+ if (backend.name === 'kitty') {
211
+ const autoHost = shouldAutoHost(options, { env, stdout });
212
+ const result = openKittyCockpit({
213
+ repoRoot,
214
+ sessionName: options.sessionName,
215
+ controlCommand,
216
+ state: deps.state,
217
+ settings: deps.settings,
218
+ readState: deps.readState || readCockpitState,
219
+ readSettings: deps.readSettings || readCockpitSettings,
220
+ dryRun: deps.dryRun,
221
+ controlTitle: options.controlTitle || 'gx cockpit',
222
+ focusControl: deps.focusControl === undefined ? true : deps.focusControl,
223
+ runner: deps.kittyRunner || deps.runner,
224
+ kittyBin: deps.kittyBin || env.GUARDEX_KITTY_BIN,
225
+ env,
226
+ backend,
227
+ bootstrap: options.host === true || (options.host === undefined && autoHost)
228
+ ? true
229
+ : options.host === false ? false : undefined,
230
+ bootstrapWhenHostless: false,
231
+ socket: options.socket,
232
+ hostRunner: deps.kittyHostRunner,
233
+ hostRuntime: deps.kittyHostRuntime,
234
+ spawn: deps.spawn,
235
+ fs: deps.fs,
236
+ sleep: deps.sleep,
237
+ });
238
+ const action = result && result.action ? result.action : 'created';
239
+ writeOpenedCockpitMessage({ backend, action, options, repoRoot, controlCommand, stdout, toolName });
240
+ return { action, backend: backend.name, sessionName: options.sessionName, repoRoot, plan: result.plan };
241
+ }
242
+
243
+ const result = backend.openCockpitLayout({
244
+ repoRoot,
245
+ sessionName: options.sessionName,
246
+ command: controlCommand,
247
+ attach: options.attach,
248
+ });
249
+ const action = result && result.action ? result.action : 'created';
250
+
251
+ writeOpenedCockpitMessage({ backend, action, options, repoRoot, controlCommand, stdout, toolName });
252
+ return { action, backend: backend.name, sessionName: options.sessionName, repoRoot };
253
+ }
254
+
255
+ function backendAvailable(backend) {
256
+ if (!backend || typeof backend.isAvailable !== 'function') return true;
257
+ try {
258
+ return Boolean(backend.isAvailable());
259
+ } catch (_error) {
260
+ return false;
261
+ }
262
+ }
263
+
264
+ function defaultCockpitBackends(preferredBackend, terminalBackendOptions = {}, options = {}) {
265
+ const preferred = normalizeBackendName(preferredBackend || DEFAULT_INTERACTIVE_BACKEND, DEFAULT_INTERACTIVE_BACKEND);
266
+ const seen = new Set();
267
+ const candidates = [];
268
+ const add = (name, addOptions = {}) => {
269
+ if (seen.has(name)) return;
270
+ const backend = selectTerminalBackend(name, terminalBackendOptions);
271
+ if (!backend) return;
272
+ if (addOptions.onlyIfAvailable && !backendAvailable(backend)) return;
273
+ seen.add(name);
274
+ candidates.push(backend);
275
+ };
276
+
277
+ if (preferred === 'auto') {
278
+ if (options.autoHostPermitted) {
279
+ add('kitty');
280
+ } else {
281
+ add('kitty', { onlyIfAvailable: true });
282
+ }
283
+ add('tmux');
284
+ return candidates;
285
+ }
286
+
287
+ add(preferred);
288
+ if (preferred !== 'tmux') add('tmux');
289
+ return candidates;
290
+ }
291
+
292
+ function inlineCockpit(repoRoot, deps = {}) {
293
+ const controlHandle = control.startCockpitControl({
294
+ repoPath: repoRoot,
295
+ stdin: deps.stdin,
296
+ stdout: deps.stdout || process.stdout,
297
+ readState: deps.readState,
298
+ readSettings: deps.readSettings,
299
+ setInterval: deps.setInterval,
300
+ clearInterval: deps.clearInterval,
301
+ });
302
+ return {
303
+ action: 'rendered',
304
+ backend: 'inline',
305
+ sessionName: DEFAULT_SESSION_NAME,
306
+ repoRoot,
307
+ control: controlHandle,
308
+ };
309
+ }
310
+
311
+ function openDefaultCockpit(deps = {}) {
312
+ const {
313
+ resolveRepoRoot,
314
+ env = process.env,
315
+ } = deps;
316
+ if (typeof resolveRepoRoot !== 'function') {
317
+ throw new Error('openDefaultCockpit requires resolveRepoRoot');
318
+ }
319
+
320
+ const target = deps.target || process.cwd();
321
+ const stdout = deps.stdout || process.stdout;
322
+ const options = {
323
+ sessionName: DEFAULT_SESSION_NAME,
324
+ backend: env.GUARDEX_COCKPIT_BACKEND || DEFAULT_INTERACTIVE_BACKEND,
325
+ attach: false,
326
+ target,
327
+ };
328
+ const repoRoot = resolveRepoRoot(target);
329
+ const controlCommand = cockpitControlCommand(repoRoot);
330
+ const terminalBackendOptions = terminalBackendOptionsFromDeps(deps);
331
+ const failures = [];
332
+ const autoHostPermitted = shouldAutoHost({}, { env, stdout });
333
+
334
+ for (const backend of defaultCockpitBackends(options.backend, terminalBackendOptions, { autoHostPermitted })) {
335
+ try {
336
+ return openWithBackend(backend, options, repoRoot, controlCommand, deps);
337
+ } catch (error) {
338
+ failures.push({
339
+ backend: backend.name,
340
+ message: error && error.message ? error.message : String(error),
341
+ });
342
+ }
343
+ }
344
+
345
+ const result = inlineCockpit(repoRoot, deps);
346
+ result.failures = failures;
347
+ return result;
348
+ }
349
+
350
+ function render(repoPath = process.cwd()) {
351
+ return renderCockpit(readCockpitState(repoPath));
352
+ }
353
+
354
+ function startCockpit(options = {}) {
355
+ const repoPath = options.repoPath || process.cwd();
356
+ const refreshMs = Number.isFinite(options.refreshMs) && options.refreshMs > 0
357
+ ? options.refreshMs
358
+ : 2000;
359
+
360
+ const paint = () => {
361
+ process.stdout.write('\x1Bc');
362
+ process.stdout.write(render(repoPath));
363
+ };
364
+
365
+ paint();
366
+ return setInterval(paint, refreshMs);
367
+ }
368
+
369
+ function openCockpit(rawArgs = [], deps = {}) {
370
+ const {
371
+ resolveRepoRoot,
372
+ toolName = 'gitguardex',
373
+ stdout = process.stdout,
374
+ } = deps;
375
+ if (typeof resolveRepoRoot !== 'function') {
376
+ throw new Error('openCockpit requires resolveRepoRoot');
377
+ }
378
+
379
+ if (rawArgs[0] === 'control') {
380
+ const controlOptions = parseCockpitControlArgs(rawArgs.slice(1));
381
+ return control.startCockpitControl({
382
+ repoPath: resolveRepoRoot(controlOptions.target),
383
+ refreshMs: controlOptions.refreshMs,
384
+ stdin: deps.stdin,
385
+ stdout,
386
+ readState: deps.readState,
387
+ readSettings: deps.readSettings,
388
+ setInterval: deps.setInterval,
389
+ clearInterval: deps.clearInterval,
390
+ });
391
+ }
392
+
393
+ const options = parseCockpitArgs(rawArgs);
394
+ const repoRoot = resolveRepoRoot(options.target);
395
+ const controlCommand = cockpitControlCommand(repoRoot);
396
+ const terminalBackendOptions = terminalBackendOptionsFromDeps(deps);
397
+ const backend = selectTerminalBackend(options.backend, terminalBackendOptions);
398
+
399
+ return openWithBackend(backend, options, repoRoot, controlCommand, { ...deps, stdout, toolName });
400
+ }
401
+
402
+ if (require.main === module) {
403
+ startCockpit({
404
+ repoPath: process.argv[2] || process.cwd(),
405
+ refreshMs: Number.parseInt(process.env.GUARDEX_COCKPIT_REFRESH_MS || '2000', 10),
406
+ });
407
+ }
408
+
409
+ module.exports = {
410
+ DEFAULT_SESSION_NAME,
411
+ DEFAULT_BACKEND,
412
+ DEFAULT_INTERACTIVE_BACKEND,
413
+ cockpitControlCommand,
414
+ defaultCockpitBackends,
415
+ parseCockpitArgs,
416
+ parseCockpitControlArgs,
417
+ openDefaultCockpit,
418
+ openCockpit,
419
+ openKittyCockpit,
420
+ render,
421
+ startCockpit,
422
+ ...control,
423
+ ...actions,
424
+ control,
425
+ actions,
426
+ };