@imdeadpool/guardex 7.0.39 → 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.
- package/README.md +85 -16
- package/package.json +2 -1
- package/skills/gitguardex/SKILL.md +13 -0
- package/skills/guardex-merge-skills-to-dev/SKILL.md +59 -0
- package/src/agents/cleanup-sessions.js +126 -0
- package/src/agents/detect.js +160 -0
- package/src/agents/finish.js +172 -0
- package/src/agents/inspect.js +189 -0
- package/src/agents/launch.js +240 -0
- package/src/agents/registry.js +133 -0
- package/src/agents/selection-panel.js +571 -0
- package/src/agents/sessions.js +151 -0
- package/src/agents/start.js +591 -0
- package/src/agents/status.js +143 -0
- package/src/agents/terminal.js +152 -0
- package/src/budget/index.js +343 -0
- package/src/ci-init/index.js +265 -0
- package/src/cli/args.js +305 -1
- package/src/cli/main.js +262 -132
- package/src/cockpit/action-runner.js +3 -0
- package/src/cockpit/actions.js +80 -0
- package/src/cockpit/control.js +1121 -0
- package/src/cockpit/index.js +426 -0
- package/src/cockpit/keybindings.js +224 -0
- package/src/cockpit/kitty-layout.js +549 -0
- package/src/cockpit/kitty-tree.js +144 -0
- package/src/cockpit/layout.js +224 -0
- package/src/cockpit/logs-reader.js +182 -0
- package/src/cockpit/menu.js +204 -0
- package/src/cockpit/pane-actions.js +597 -0
- package/src/cockpit/pane-menu.js +387 -0
- package/src/cockpit/projects-finder.js +178 -0
- package/src/cockpit/render.js +215 -0
- package/src/cockpit/settings-render.js +128 -0
- package/src/cockpit/settings.js +124 -0
- package/src/cockpit/shortcuts.js +24 -0
- package/src/cockpit/sidebar.js +311 -0
- package/src/cockpit/state.js +72 -0
- package/src/cockpit/theme.js +128 -0
- package/src/cockpit/welcome.js +266 -0
- package/src/context.js +78 -35
- package/src/doctor/index.js +4 -3
- package/src/finish/index.js +39 -2
- package/src/git/index.js +65 -0
- package/src/kitty/command.js +101 -0
- package/src/kitty/runtime.js +250 -0
- package/src/output/index.js +1 -1
- package/src/pr-review.js +241 -0
- package/src/scaffold/index.js +19 -0
- package/src/submodule/index.js +288 -0
- package/src/terminal/index.js +120 -0
- package/src/terminal/kitty.js +622 -0
- package/src/terminal/tmux.js +126 -0
- package/src/tmux/command.js +27 -0
- package/src/tmux/session.js +89 -0
- package/templates/AGENTS.multiagent-safety.md +421 -37
- package/templates/codex/skills/gitguardex/SKILL.md +2 -0
- package/templates/githooks/pre-commit +22 -1
- package/templates/github/workflows/README.md +87 -0
- package/templates/github/workflows/ci-full.yml +55 -0
- package/templates/github/workflows/ci.yml +56 -0
- package/templates/github/workflows/cr.yml +20 -1
- package/templates/scripts/agent-branch-finish.sh +545 -27
- package/templates/scripts/agent-branch-start.sh +193 -21
- package/templates/scripts/agent-preflight.sh +89 -0
- package/templates/scripts/agent-worktree-prune.sh +96 -5
- package/templates/scripts/codex-agent.sh +41 -6
- package/templates/scripts/openspec/init-plan-workspace.sh +43 -0
- package/templates/scripts/review-bot-watch.sh +31 -2
- package/templates/scripts/agent-session-state.js +0 -171
- package/templates/scripts/install-vscode-active-agents-extension.js +0 -135
- package/templates/vscode/guardex-active-agents/README.md +0 -34
- package/templates/vscode/guardex-active-agents/extension.js +0 -3782
- package/templates/vscode/guardex-active-agents/fileicons/gitguardex-fileicons.json +0 -54
- package/templates/vscode/guardex-active-agents/fileicons/icons/agent.svg +0 -5
- package/templates/vscode/guardex-active-agents/fileicons/icons/branch.svg +0 -7
- package/templates/vscode/guardex-active-agents/fileicons/icons/config.svg +0 -4
- package/templates/vscode/guardex-active-agents/fileicons/icons/hook.svg +0 -4
- package/templates/vscode/guardex-active-agents/fileicons/icons/openspec.svg +0 -5
- package/templates/vscode/guardex-active-agents/fileicons/icons/plan.svg +0 -4
- package/templates/vscode/guardex-active-agents/fileicons/icons/spec.svg +0 -5
- package/templates/vscode/guardex-active-agents/icon.png +0 -0
- package/templates/vscode/guardex-active-agents/media/active-agents-hivemind.svg +0 -14
- package/templates/vscode/guardex-active-agents/package.json +0 -169
- package/templates/vscode/guardex-active-agents/session-schema.js +0 -1348
package/README.md
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
and human teammates working the same codebase at the same time.
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
|
-
Guardian T-Rex for your multi-agent repo. Isolated worktrees, file locks, and PR-only merges stop parallel Codex & Claude agents from overwriting each other's work. Auto-wires Oh My Codex, Oh My Claude, OpenSpec, and Caveman. See [about_description.txt](./about_description.txt).
|
|
13
12
|
|
|
14
13
|
<p align="center">
|
|
15
14
|
<a href="https://www.npmjs.com/package/@imdeadpool/guardex"><img alt="npm version" src="https://img.shields.io/npm/v/%40imdeadpool%2Fguardex?label=npm&style=flat-square&color=cb3837&logo=npm&logoColor=white"></a>
|
|
@@ -25,10 +24,11 @@ Guardian T-Rex for your multi-agent repo. Isolated worktrees, file locks, and PR
|
|
|
25
24
|
<a href="#01--install-in-one-line">Install</a> ·
|
|
26
25
|
<a href="#03--what-it-does">What it does</a> ·
|
|
27
26
|
<a href="#04--daily-workflow">Workflow</a> ·
|
|
28
|
-
<a href="#05--
|
|
29
|
-
<a href="#
|
|
30
|
-
<a href="#08--
|
|
31
|
-
<a href="#
|
|
27
|
+
<a href="#05--dmux-style-multi-agent-cockpit">Cockpit</a> ·
|
|
28
|
+
<a href="#06--what-gx-shows-first">gx status</a> ·
|
|
29
|
+
<a href="#08--commands">Commands</a> ·
|
|
30
|
+
<a href="#09--v6--v7-migration">Migration</a> ·
|
|
31
|
+
<a href="#11--companion-tools">Companions</a>
|
|
32
32
|
</p>
|
|
33
33
|
|
|
34
34
|
---
|
|
@@ -57,6 +57,12 @@ gx setup # hooks, state, OMX / OpenSpec / caveman wiring — one shot
|
|
|
57
57
|
> feels rough — especially around **cleanup**, **finish**, **merge**, or
|
|
58
58
|
> **recovery** flows — sorry. We're patching as we find things.
|
|
59
59
|
|
|
60
|
+
> [!CAUTION]
|
|
61
|
+
> **Recommended default: macOS or Linux with Codex CLI.** OMX is primarily
|
|
62
|
+
> designed and actively tuned for that path. Native Windows and Codex App are
|
|
63
|
+
> not the default experience, may break or behave inconsistently, and currently
|
|
64
|
+
> receive less support.
|
|
65
|
+
|
|
60
66
|
---
|
|
61
67
|
|
|
62
68
|
## The problem
|
|
@@ -136,7 +142,30 @@ gx branch finish --branch "$(git rev-parse --abbrev-ref HEAD)" \
|
|
|
136
142
|
|
|
137
143
|
---
|
|
138
144
|
|
|
139
|
-
## `05`
|
|
145
|
+
## `05` dmux-style multi-agent cockpit
|
|
146
|
+
|
|
147
|
+
GitGuardex now has a dmux-style cockpit for starting, inspecting, and
|
|
148
|
+
finishing isolated agent lanes from one terminal workspace. It is not a
|
|
149
|
+
dmux clone: GitGuardex keeps safety as the core and adds orchestration
|
|
150
|
+
on top of isolated worktrees, file locks, protected base branches, and
|
|
151
|
+
PR-only finish.
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
gx cockpit
|
|
155
|
+
gx agents start "fix auth tests" --agent codex --base main --claim test/auth.test.js
|
|
156
|
+
gx agents start "fix auth tests" --panel --codex-accounts 3 --base main
|
|
157
|
+
gx agents start "update setup docs" --agent claude --base main --claim README.md
|
|
158
|
+
gx agents status
|
|
159
|
+
gx agents files --branch agent/codex/fix-auth-tests-2026-04-29-21-30
|
|
160
|
+
gx agents diff --branch agent/claude/update-setup-docs-2026-04-29-21-31
|
|
161
|
+
gx agents finish --branch agent/codex/fix-auth-tests-2026-04-29-21-30
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Long-form guide: [docs/agents-cockpit.md](./docs/agents-cockpit.md).
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## `06` What `gx` shows first
|
|
140
169
|
|
|
141
170
|
Before you branch, repair, or start agents, run plain `gx`. It gives you
|
|
142
171
|
a one-screen status for the CLI, global helpers, repo safety service,
|
|
@@ -157,7 +186,7 @@ $ gx
|
|
|
157
186
|
● oh-my-codex active
|
|
158
187
|
● oh-my-claude-sisyphus active
|
|
159
188
|
● @fission-ai/openspec active
|
|
160
|
-
●
|
|
189
|
+
● colony active
|
|
161
190
|
● cavekit optional · not installed
|
|
162
191
|
● gh authenticated
|
|
163
192
|
|
|
@@ -171,7 +200,7 @@ the compact layout everywhere.
|
|
|
171
200
|
|
|
172
201
|
---
|
|
173
202
|
|
|
174
|
-
## `
|
|
203
|
+
## `07` How `AGENTS.md` is handled
|
|
175
204
|
|
|
176
205
|
> [!IMPORTANT]
|
|
177
206
|
> **GitGuardex never overwrites your guidance.** Only content between
|
|
@@ -183,12 +212,13 @@ the compact layout everywhere.
|
|
|
183
212
|
| --- | --- |
|
|
184
213
|
| `AGENTS.md` **with** markers | Refreshes **only** the managed block. |
|
|
185
214
|
| `AGENTS.md` **without** markers | Appends the managed block to the end. |
|
|
186
|
-
| No `AGENTS.md` | Creates it with the managed block. |
|
|
215
|
+
| No `AGENTS.md` | Creates it with the managed block, then links `CLAUDE.md` to it. |
|
|
216
|
+
| No root `CLAUDE.md` | Creates a `CLAUDE.md` symlink to `AGENTS.md`. |
|
|
187
217
|
| A root `CLAUDE.md` | Leaves it alone. |
|
|
188
218
|
|
|
189
219
|
---
|
|
190
220
|
|
|
191
|
-
## `
|
|
221
|
+
## `08` Commands
|
|
192
222
|
|
|
193
223
|
### Core
|
|
194
224
|
|
|
@@ -210,6 +240,18 @@ the compact layout everywhere.
|
|
|
210
240
|
| `gx sync` | Sync current agent branch against base. |
|
|
211
241
|
| `gx release` | Update the GitHub release from README notes. |
|
|
212
242
|
|
|
243
|
+
### Multi-agent cockpit
|
|
244
|
+
|
|
245
|
+
| command | does |
|
|
246
|
+
| --- | --- |
|
|
247
|
+
| `gx cockpit` | Create or attach to a repo tmux cockpit session with a status pane. |
|
|
248
|
+
| `gx agents start "<task>" --agent codex` | Start an isolated Codex lane for a task. |
|
|
249
|
+
| `gx agents start "<task>" --agent claude` | Start an isolated Claude Code lane for a task. |
|
|
250
|
+
| `gx agents status` | Show repo agent service status. |
|
|
251
|
+
| `gx agents files --branch <agent/...>` | List files changed by one agent lane. |
|
|
252
|
+
| `gx agents diff --branch <agent/...>` | Show the diff for one agent lane. |
|
|
253
|
+
| `gx agents finish --branch <agent/...>` | Finish one agent session through the existing PR flow. |
|
|
254
|
+
|
|
213
255
|
```bash
|
|
214
256
|
gx release # create/update the current GitHub release from README notes
|
|
215
257
|
```
|
|
@@ -228,7 +270,7 @@ gx protect reset # back to: dev · main · master
|
|
|
228
270
|
|
|
229
271
|
---
|
|
230
272
|
|
|
231
|
-
## `
|
|
273
|
+
## `09` v6 → v7 migration
|
|
232
274
|
|
|
233
275
|
Five commands were consolidated into flags. Old names still work and
|
|
234
276
|
print a deprecation notice; they'll be removed in v8.
|
|
@@ -246,7 +288,7 @@ print a deprecation notice; they'll be removed in v8.
|
|
|
246
288
|
|
|
247
289
|
---
|
|
248
290
|
|
|
249
|
-
## `
|
|
291
|
+
## `10` Known rough edges
|
|
250
292
|
|
|
251
293
|
Being honest about where this still has issues:
|
|
252
294
|
|
|
@@ -266,6 +308,33 @@ Being honest about where this still has issues:
|
|
|
266
308
|
<details open>
|
|
267
309
|
<summary><strong>v7.x</strong></summary>
|
|
268
310
|
|
|
311
|
+
### v7.0.42
|
|
312
|
+
- Bumped `@imdeadpool/guardex` from `7.0.41` to `7.0.42` so the current
|
|
313
|
+
`main` payload can publish under a fresh npm version after `7.0.41` reached
|
|
314
|
+
the registry.
|
|
315
|
+
- Improves the agent-session and cockpit workflow: `gx agents start/status`
|
|
316
|
+
now share canonical session storage, agent lanes can be previewed, claimed,
|
|
317
|
+
finished by session, and launched through safer supported-agent metadata,
|
|
318
|
+
and cockpit can render live session state through tmux-backed panes.
|
|
319
|
+
- Hardens setup and status hygiene by ignoring local `.codex/` state during
|
|
320
|
+
setup/doctor, avoiding generic OpenSpec dirty-worktree reuse, and pruning
|
|
321
|
+
stale agent sessions from user-facing status surfaces.
|
|
322
|
+
|
|
323
|
+
### v7.0.41
|
|
324
|
+
- Bumped `@imdeadpool/guardex` from `7.0.40` to `7.0.41` so the current
|
|
325
|
+
`main` payload can publish under a fresh npm version after the `v7.0.40`
|
|
326
|
+
GitHub release landed without a matching npm registry package.
|
|
327
|
+
- Ships the Colony companion setup as the default global companion surface:
|
|
328
|
+
`colony` maps to `@imdeadpool/colony-cli`, README setup documents
|
|
329
|
+
`colony install --ide ...`, and status tests/images expect Colony instead
|
|
330
|
+
of cavemem.
|
|
331
|
+
|
|
332
|
+
### v7.0.40
|
|
333
|
+
- Bumped `@imdeadpool/guardex` from `7.0.39` to `7.0.40` so the current
|
|
334
|
+
`main` payload can publish under a fresh npm version after `7.0.39` reached
|
|
335
|
+
the registry.
|
|
336
|
+
- No new CLI command behavior is introduced in this release lane.
|
|
337
|
+
|
|
269
338
|
### v7.0.39
|
|
270
339
|
- Bumped `@imdeadpool/guardex` from `7.0.38` to `7.0.39` so the current
|
|
271
340
|
`main` payload can publish under a fresh npm version after `7.0.38` reached
|
|
@@ -302,24 +371,24 @@ Being honest about where this still has issues:
|
|
|
302
371
|
|
|
303
372
|
---
|
|
304
373
|
|
|
305
|
-
## `
|
|
374
|
+
## `11` Companion tools
|
|
306
375
|
|
|
307
376
|
All optional — but if you're running many agents, you probably want them.
|
|
308
377
|
`gx status` auto-detects each one and reports it in the `Global services`
|
|
309
378
|
block.
|
|
310
379
|
|
|
311
|
-
Install repo skills with `npx skills add recodee/gitguardex`; `npx skills add recodee/` opens the recodee namespace. `gx setup` does not auto-run `npx skills add ...`. If the picker does not show a separate `guardex` skill, that is expected.
|
|
380
|
+
Install repo skills with `npx skills add recodee/gitguardex`; the npm package also ships the root `skills/` catalog so the npx installer can read the GitGuardex skill from the published tarball. `npx skills add recodee/` opens the recodee namespace. `gx setup` does not auto-run `npx skills add ...`. If the picker does not show a separate `guardex` skill, that is expected.
|
|
312
381
|
|
|
313
382
|
| Tool | What it does | Stars |
|
|
314
383
|
| --- | --- | --- |
|
|
315
384
|
| [**oh-my-codex**](https://github.com/Yeachan-Heo/oh-my-codex) — `npm i -g oh-my-codex` | Codex config + skills framework. Merged into every agent worktree so each spawned Codex starts with the same tuned config. | [](https://github.com/Yeachan-Heo/oh-my-codex) |
|
|
316
385
|
| [**oh-my-claudecode**](https://github.com/Yeachan-Heo/oh-my-claudecode) — `npm i -g oh-my-claude-sisyphus@latest` | Claude-side mirror of oh-my-codex. Skills, commands, and defaults for every Claude Code session. | [](https://github.com/Yeachan-Heo/oh-my-claudecode) |
|
|
317
386
|
| [**OpenSpec**](https://github.com/Fission-AI/OpenSpec) — `npm i -g @fission-ai/openspec` | Structured plan / change / apply / archive flow so long agent runs don't drift off-task. | [](https://github.com/Fission-AI/OpenSpec) |
|
|
318
|
-
| [**
|
|
387
|
+
| [**Colony**](https://github.com/recodeee/colony) — `npm i -g @imdeadpool/colony-cli` | Multi-agent task coordination and handoff routing. After install, register runtimes with `colony install --ide codex`, `colony install --ide claude-code`, `colony install --ide cursor`, `colony install --ide gemini-cli`, or `colony install --ide opencode`, then verify with `colony status`. | [](https://github.com/recodeee/colony) |
|
|
319
388
|
| [**cavekit**](https://github.com/JuliusBrussee/cavekit) — `npx skills add JuliusBrussee/cavekit` | Spec-driven build loop with `spec`, `build`, `check`, `caveman`, `backprop` skills bundled in. | [](https://github.com/JuliusBrussee/cavekit) |
|
|
320
389
|
| [**caveman**](https://github.com/JuliusBrussee/caveman) — `npx skills add JuliusBrussee/caveman` | Ultra-compressed response mode for Claude / Codex. Less output-token churn on long reviews and debug loops. | [](https://github.com/JuliusBrussee/caveman) |
|
|
321
390
|
| [**codex-account-switcher**](https://github.com/recodeecom/codex-account-switcher-cli) — `npm i -g @imdeadpool/codex-account-switcher` | Multi-identity Codex account switcher. Auto-registers accounts on `codex login`; switch with one command. | [](https://github.com/recodeecom/codex-account-switcher-cli) |
|
|
322
|
-
| [**GitHub CLI (`gh`)**](https://github.com/cli/cli) — see [cli.github.com](https://cli.github.com/) | Required for PR / merge automation. `gx branch finish --via-pr --wait-for-merge` depends on it. | [](https://github.com/cli/cli) |
|
|
391
|
+
| [**GitHub CLI (`gh`)**](https://github.com/cli/cli) — see [cli.github.com](https://cli.github.com/) | Required for PR / merge automation. `gx branch finish --via-pr --wait-for-merge` depends on it. If `ghx` is on `PATH`, Guardex uses that GitHub CLI cache proxy automatically; set `GUARDEX_GH_BIN=gh` to force direct `gh`. | [](https://github.com/cli/cli) |
|
|
323
392
|
|
|
324
393
|
---
|
|
325
394
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imdeadpool/guardex",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.43",
|
|
4
4
|
"description": "Guardian T-Rex for your multi-agent repo. Isolated worktrees, file locks, and PR-only merges stop parallel Codex & Claude agents from overwriting each other's work. Auto-wires Oh My Codex, Oh My Claude, OpenSpec, and Caveman.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"preferGlobal": true,
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"files": [
|
|
40
40
|
"bin",
|
|
41
41
|
"src",
|
|
42
|
+
"skills",
|
|
42
43
|
"templates",
|
|
43
44
|
"README.md",
|
|
44
45
|
"LICENSE",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gitguardex
|
|
3
|
+
description: "Repo guardrail check and repair."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use when repo safety may be broken.
|
|
7
|
+
|
|
8
|
+
`gx status` -> `gx doctor` -> `gx status --strict`
|
|
9
|
+
|
|
10
|
+
Bootstrap: `gx setup`
|
|
11
|
+
Ops: `gx branch start "<task>" "<agent>"`, `gx locks claim --branch "<agent-branch>" <file...>`, `gx branch finish --branch "<agent-branch>" --base <base> --via-pr --wait-for-merge --cleanup`, `gx finish --all`, `gx cleanup`
|
|
12
|
+
|
|
13
|
+
When inspecting or verifying, prefer `rtk` compact wrappers if available (`rtk git status`, `rtk grep`, `rtk test <cmd>`). Do not wrap commands whose stdout is parsed by scripts.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: guardex-merge-skills-to-dev
|
|
3
|
+
description: "Use when you need to merge SKILL.md updates from agent branches/worktrees into the local base branch (default: dev) with the multiagent-safety flow."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GuardeX Merge Skills to dev
|
|
7
|
+
|
|
8
|
+
Use this skill when you only want to promote Codex skill file updates into the base branch (normally `dev`) without editing the visible base checkout directly.
|
|
9
|
+
|
|
10
|
+
## What this merges
|
|
11
|
+
|
|
12
|
+
- `skills/**/SKILL.md`
|
|
13
|
+
- `.codex/skills/**/SKILL.md`
|
|
14
|
+
- `templates/codex/skills/**/SKILL.md`
|
|
15
|
+
|
|
16
|
+
## Merge runbook (safe path)
|
|
17
|
+
|
|
18
|
+
1. Resolve the base branch:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
BASE_BRANCH="$(git config --get multiagent.baseBranch || echo dev)"
|
|
22
|
+
echo "$BASE_BRANCH"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
2. Start a dedicated integration sandbox from base:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
gx branch start "merge-skill-files-to-${BASE_BRANCH}" "skill-merge" "$BASE_BRANCH"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
3. Enter the sandbox worktree printed by the command above.
|
|
32
|
+
|
|
33
|
+
4. Pull only skill files from each source agent branch:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
SOURCE_BRANCH="<agent-branch>"
|
|
37
|
+
git checkout "$SOURCE_BRANCH" -- ':(glob)skills/**/SKILL.md' ':(glob).codex/skills/**/SKILL.md' ':(glob)templates/codex/skills/**/SKILL.md'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
5. Verify scope before commit:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
git status --short
|
|
44
|
+
git diff --name-only
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
6. Commit and merge back to base using guardex finish flow:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
git add skills .codex/skills templates/codex/skills
|
|
51
|
+
git commit -m "Merge skill file updates into ${BASE_BRANCH}"
|
|
52
|
+
gx branch finish --branch "$(git rev-parse --abbrev-ref HEAD)" --base "$BASE_BRANCH" --via-pr --wait-for-merge --cleanup
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Notes
|
|
56
|
+
|
|
57
|
+
- If a source branch has non-skill changes, this runbook keeps them out of the merge.
|
|
58
|
+
- If merge conflicts occur, resolve only within the skill files, then rerun `gx branch finish`.
|
|
59
|
+
- Do not commit directly on `dev`/`main`; always merge through an agent branch/worktree.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
listAgentSessions,
|
|
5
|
+
removeAgentSession,
|
|
6
|
+
} = require('./sessions');
|
|
7
|
+
const { branchExists: defaultBranchExists } = require('../git');
|
|
8
|
+
const { TOOL_NAME } = require('../context');
|
|
9
|
+
|
|
10
|
+
const DEFAULT_STALE_AGE_MINUTES = 24 * 60;
|
|
11
|
+
const TERMINAL_STATUSES = new Set(['finished', 'pr-opened', 'failed']);
|
|
12
|
+
|
|
13
|
+
function parseTimestamp(value) {
|
|
14
|
+
if (!value) return null;
|
|
15
|
+
const timestamp = Date.parse(value);
|
|
16
|
+
return Number.isFinite(timestamp) ? timestamp : null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function sessionAgeMinutes(session, nowMs) {
|
|
20
|
+
const timestamp = parseTimestamp(session.updatedAt) ?? parseTimestamp(session.createdAt);
|
|
21
|
+
if (timestamp === null) return null;
|
|
22
|
+
return Math.max(0, Math.floor((nowMs - timestamp) / 60000));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function evaluateSession(session, repoRoot, options) {
|
|
26
|
+
const reasons = [];
|
|
27
|
+
const worktreePath = session.worktreePath || '';
|
|
28
|
+
const branch = session.branch || '';
|
|
29
|
+
|
|
30
|
+
if (worktreePath && !options.existsSync(worktreePath)) {
|
|
31
|
+
reasons.push('missing-worktree');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (branch && !options.branchExists(repoRoot, branch)) {
|
|
35
|
+
reasons.push('missing-branch');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const status = session.status || '';
|
|
39
|
+
const ageMinutes = sessionAgeMinutes(session, options.nowMs);
|
|
40
|
+
if (
|
|
41
|
+
TERMINAL_STATUSES.has(status)
|
|
42
|
+
&& ageMinutes !== null
|
|
43
|
+
&& ageMinutes >= options.staleAgeMinutes
|
|
44
|
+
) {
|
|
45
|
+
reasons.push('terminal-status-old');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
...session,
|
|
50
|
+
ageMinutes,
|
|
51
|
+
reasons,
|
|
52
|
+
stale: reasons.length > 0,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function cleanupAgentSessions(repoRoot, rawOptions = {}) {
|
|
57
|
+
const options = {
|
|
58
|
+
dryRun: Boolean(rawOptions.dryRun),
|
|
59
|
+
staleAgeMinutes: rawOptions.staleAgeMinutes ?? DEFAULT_STALE_AGE_MINUTES,
|
|
60
|
+
nowMs: rawOptions.nowMs ?? Date.now(),
|
|
61
|
+
existsSync: rawOptions.existsSync || fs.existsSync,
|
|
62
|
+
branchExists: rawOptions.branchExists || defaultBranchExists,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const sessions = listAgentSessions(repoRoot);
|
|
66
|
+
const candidates = sessions
|
|
67
|
+
.map((session) => evaluateSession(session, repoRoot, options))
|
|
68
|
+
.filter((session) => session.stale);
|
|
69
|
+
|
|
70
|
+
const removed = [];
|
|
71
|
+
if (!options.dryRun) {
|
|
72
|
+
for (const session of candidates) {
|
|
73
|
+
if (removeAgentSession(repoRoot, session.id)) {
|
|
74
|
+
removed.push(session.id);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
schemaVersion: 1,
|
|
81
|
+
repoRoot,
|
|
82
|
+
dryRun: options.dryRun,
|
|
83
|
+
staleAgeMinutes: options.staleAgeMinutes,
|
|
84
|
+
inspected: sessions.length,
|
|
85
|
+
candidates,
|
|
86
|
+
removed,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function formatSessionLine(session, verb) {
|
|
91
|
+
const reasonText = session.reasons.join(',');
|
|
92
|
+
return `- ${verb} ${session.id} status=${session.status || '-'} branch=${session.branch || '-'} ` +
|
|
93
|
+
`worktree=${session.worktreePath || '-'} reasons=${reasonText}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function renderCleanupSessionsResult(result, options = {}) {
|
|
97
|
+
if (options.json) return `${JSON.stringify(result, null, 2)}\n`;
|
|
98
|
+
|
|
99
|
+
const action = result.dryRun ? 'would remove' : 'removed';
|
|
100
|
+
const lines = [
|
|
101
|
+
`[${TOOL_NAME}] Agent session cleanup: ${action} ${result.dryRun ? result.candidates.length : result.removed.length} ` +
|
|
102
|
+
`of ${result.inspected} (${result.repoRoot})`,
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
if (result.candidates.length === 0) {
|
|
106
|
+
lines.push('- no stale session metadata found');
|
|
107
|
+
} else {
|
|
108
|
+
for (const session of result.candidates) {
|
|
109
|
+
lines.push(formatSessionLine(session, action));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return `${lines.join('\n')}\n`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function runCleanupSessionsCommand(repoRoot, options = {}) {
|
|
117
|
+
return renderCleanupSessionsResult(cleanupAgentSessions(repoRoot, options), options);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
DEFAULT_STALE_AGE_MINUTES,
|
|
122
|
+
TERMINAL_STATUSES,
|
|
123
|
+
cleanupAgentSessions,
|
|
124
|
+
renderCleanupSessionsResult,
|
|
125
|
+
runCleanupSessionsCommand,
|
|
126
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
const registry = require('./registry');
|
|
2
|
+
const { run } = require('../core/runtime');
|
|
3
|
+
|
|
4
|
+
function registryEntries() {
|
|
5
|
+
if (typeof registry.getAgentDefinitions === 'function') {
|
|
6
|
+
const definitions = registry.getAgentDefinitions();
|
|
7
|
+
if (Array.isArray(definitions)) {
|
|
8
|
+
return definitions;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (Array.isArray(registry.AGENT_IDS) && typeof registry.getAgentDefinition === 'function') {
|
|
13
|
+
return registry.AGENT_IDS
|
|
14
|
+
.map((agentId) => registry.getAgentDefinition(agentId))
|
|
15
|
+
.filter((entry) => entry && typeof entry === 'object');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const source =
|
|
19
|
+
registry.agents ||
|
|
20
|
+
registry.AGENTS ||
|
|
21
|
+
registry.registry ||
|
|
22
|
+
registry.entries ||
|
|
23
|
+
registry.default ||
|
|
24
|
+
registry;
|
|
25
|
+
|
|
26
|
+
if (Array.isArray(source)) {
|
|
27
|
+
return source;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (source && typeof source === 'object') {
|
|
31
|
+
return Object.entries(source)
|
|
32
|
+
.filter(([, entry]) => entry && typeof entry === 'object')
|
|
33
|
+
.map(([id, entry]) => ({ id, ...entry }));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function findAgent(agentId) {
|
|
40
|
+
if (typeof registry.getAgentDefinition === 'function') {
|
|
41
|
+
const entry = registry.getAgentDefinition(agentId);
|
|
42
|
+
if (entry) return entry;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (typeof registry.resolveAgent === 'function') {
|
|
46
|
+
try {
|
|
47
|
+
const entry = registry.resolveAgent(agentId);
|
|
48
|
+
if (entry) return entry;
|
|
49
|
+
} catch (_error) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (typeof registry.getAgent === 'function') {
|
|
55
|
+
const entry = registry.getAgent(agentId);
|
|
56
|
+
if (entry) return entry;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return registryEntries().find((entry) => entry.id === agentId);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function registryAgentIds() {
|
|
63
|
+
if (Array.isArray(registry.AGENT_IDS)) {
|
|
64
|
+
return [...registry.AGENT_IDS];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (typeof registry.getAgentDefinitions === 'function') {
|
|
68
|
+
const definitions = registry.getAgentDefinitions();
|
|
69
|
+
if (Array.isArray(definitions)) {
|
|
70
|
+
return definitions.map((entry) => entry && entry.id).filter(Boolean);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return registryEntries().map((entry) => entry.id).filter(Boolean);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeDetectCommand(detectCommand) {
|
|
78
|
+
if (Array.isArray(detectCommand)) {
|
|
79
|
+
const [cmd, ...args] = detectCommand;
|
|
80
|
+
return { cmd, args, command: detectCommand.join(' ') };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (typeof detectCommand === 'string') {
|
|
84
|
+
const [cmd, ...args] = detectCommand.trim().split(/\s+/).filter(Boolean);
|
|
85
|
+
return { cmd, args, command: detectCommand.trim() };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (detectCommand && typeof detectCommand === 'object') {
|
|
89
|
+
const cmd = detectCommand.cmd || detectCommand.command || detectCommand.bin;
|
|
90
|
+
const args = Array.isArray(detectCommand.args) ? detectCommand.args : [];
|
|
91
|
+
return {
|
|
92
|
+
cmd,
|
|
93
|
+
args,
|
|
94
|
+
command: [cmd, ...args].filter(Boolean).join(' '),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { cmd: null, args: [], command: null };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function resultError(result) {
|
|
102
|
+
if (result.error) {
|
|
103
|
+
return result.error.message || String(result.error);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const output = `${result.stderr || ''}${result.stdout || ''}`.trim();
|
|
107
|
+
if (output) return output;
|
|
108
|
+
|
|
109
|
+
if (typeof result.status === 'number') {
|
|
110
|
+
return `detect command exited with status ${result.status}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return 'detect command failed';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function detectionResult(entry, available, command, error = null) {
|
|
117
|
+
return {
|
|
118
|
+
id: entry.id,
|
|
119
|
+
label: entry.label || entry.id,
|
|
120
|
+
available,
|
|
121
|
+
command,
|
|
122
|
+
error,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function detectAgent(agentId) {
|
|
127
|
+
const entry = findAgent(agentId);
|
|
128
|
+
if (!entry) {
|
|
129
|
+
const known = registryAgentIds();
|
|
130
|
+
const suffix = known.length > 0 ? ` (known agents: ${known.join(', ')})` : '';
|
|
131
|
+
return detectionResult({ id: agentId, label: agentId }, false, null, `unknown agent: ${agentId}${suffix}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const { cmd, args, command } = normalizeDetectCommand(entry.detectCommand);
|
|
135
|
+
if (!cmd) {
|
|
136
|
+
return detectionResult(entry, false, command, 'missing detectCommand');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const result = run(cmd, args, { stdio: 'pipe' });
|
|
140
|
+
if (!result.error && result.status === 0) {
|
|
141
|
+
return detectionResult(entry, true, command, null);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return detectionResult(entry, false, command, resultError(result));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function detectAgents(agentIds) {
|
|
148
|
+
const ids = Array.isArray(agentIds) ? agentIds : registryAgentIds();
|
|
149
|
+
return ids.map((agentId) => detectAgent(agentId));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function detectAvailableAgents() {
|
|
153
|
+
return detectAgents().filter((agent) => agent.available);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = {
|
|
157
|
+
detectAgent,
|
|
158
|
+
detectAgents,
|
|
159
|
+
detectAvailableAgents,
|
|
160
|
+
};
|