@imdeadpool/guardex 7.0.38 → 7.0.41

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 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>
@@ -157,7 +156,7 @@ $ gx
157
156
  ● oh-my-codex active
158
157
  ● oh-my-claude-sisyphus active
159
158
  ● @fission-ai/openspec active
160
- cavemem active
159
+ colony active
161
160
  ● cavekit optional · not installed
162
161
  ● gh authenticated
163
162
 
@@ -266,6 +265,27 @@ Being honest about where this still has issues:
266
265
  <details open>
267
266
  <summary><strong>v7.x</strong></summary>
268
267
 
268
+ ### v7.0.41
269
+ - Bumped `@imdeadpool/guardex` from `7.0.40` to `7.0.41` so the current
270
+ `main` payload can publish under a fresh npm version after the `v7.0.40`
271
+ GitHub release landed without a matching npm registry package.
272
+ - Ships the Colony companion setup as the default global companion surface:
273
+ `colony` maps to `@imdeadpool/colony-cli`, README setup documents
274
+ `colony install --ide ...`, and status tests/images expect Colony instead
275
+ of cavemem.
276
+
277
+ ### v7.0.40
278
+ - Bumped `@imdeadpool/guardex` from `7.0.39` to `7.0.40` so the current
279
+ `main` payload can publish under a fresh npm version after `7.0.39` reached
280
+ the registry.
281
+ - No new CLI command behavior is introduced in this release lane.
282
+
283
+ ### v7.0.39
284
+ - Bumped `@imdeadpool/guardex` from `7.0.38` to `7.0.39` so the current
285
+ `main` payload can publish under a fresh npm version after `7.0.38` reached
286
+ the registry.
287
+ - No new CLI command behavior is introduced in this release lane.
288
+
269
289
  ### v7.0.38
270
290
  - Bumped `@imdeadpool/guardex` from `7.0.37` to `7.0.38` so the current
271
291
  `main` payload can publish under a fresh npm version after `7.0.37` reached
@@ -309,7 +329,7 @@ Install repo skills with `npx skills add recodee/gitguardex`; `npx skills add re
309
329
  | [**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. | [![stars](https://img.shields.io/github/stars/Yeachan-Heo/oh-my-codex?style=social)](https://github.com/Yeachan-Heo/oh-my-codex) |
310
330
  | [**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. | [![stars](https://img.shields.io/github/stars/Yeachan-Heo/oh-my-claudecode?style=social)](https://github.com/Yeachan-Heo/oh-my-claudecode) |
311
331
  | [**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. | [![stars](https://img.shields.io/github/stars/Fission-AI/OpenSpec?style=social)](https://github.com/Fission-AI/OpenSpec) |
312
- | [**cavemem**](https://github.com/JuliusBrussee/cavemem) — `npm i -g cavemem` | Local persistent memory for agents via SQLite + MCP. Retains compressed history across runs. | [![stars](https://img.shields.io/github/stars/JuliusBrussee/cavemem?style=social)](https://github.com/JuliusBrussee/cavemem) |
332
+ | [**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`. | [![stars](https://img.shields.io/github/stars/recodeee/colony?style=social)](https://github.com/recodeee/colony) |
313
333
  | [**cavekit**](https://github.com/JuliusBrussee/cavekit) — `npx skills add JuliusBrussee/cavekit` | Spec-driven build loop with `spec`, `build`, `check`, `caveman`, `backprop` skills bundled in. | [![stars](https://img.shields.io/github/stars/JuliusBrussee/cavekit?style=social)](https://github.com/JuliusBrussee/cavekit) |
314
334
  | [**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. | [![stars](https://img.shields.io/github/stars/JuliusBrussee/caveman?style=social)](https://github.com/JuliusBrussee/caveman) |
315
335
  | [**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. | [![stars](https://img.shields.io/github/stars/recodeecom/codex-account-switcher-cli?style=social)](https://github.com/recodeecom/codex-account-switcher-cli) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imdeadpool/guardex",
3
- "version": "7.0.38",
3
+ "version": "7.0.41",
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,
package/src/cli/main.js CHANGED
@@ -408,8 +408,9 @@ function runSetupBootstrapInternal(options) {
408
408
  }
409
409
 
410
410
  function extractAgentBranchStartMetadata(output) {
411
- const branchMatch = String(output || '').match(/^\[agent-branch-start\] Created branch: (.+)$/m);
412
- const worktreeMatch = String(output || '').match(/^\[agent-branch-start\] Worktree: (.+)$/m);
411
+ const outputText = String(output || '');
412
+ const branchMatch = outputText.match(/^\[agent-branch-start\] (?:Created branch|Reusing existing branch): (.+)$/m);
413
+ const worktreeMatch = outputText.match(/^\[agent-branch-start\] Worktree: (.+)$/m);
413
414
  return {
414
415
  branch: branchMatch ? branchMatch[1].trim() : '',
415
416
  worktreePath: worktreeMatch ? worktreeMatch[1].trim() : '',
@@ -3466,7 +3467,7 @@ function pivot(rawArgs) {
3466
3467
  }
3467
3468
  const stdoutText = String(result.stdout || '');
3468
3469
  const wtMatch = stdoutText.match(/^\[agent-branch-start\] Worktree:\s+(.+)$/m);
3469
- const branchMatch = stdoutText.match(/^\[agent-branch-start\] Created branch:\s+(.+)$/m);
3470
+ const branchMatch = stdoutText.match(/^\[agent-branch-start\] (?:Created branch|Reusing existing branch):\s+(.+)$/m);
3470
3471
  if (wtMatch) {
3471
3472
  const wtPath = wtMatch[1].trim();
3472
3473
  process.stdout.write('\n');
package/src/context.js CHANGED
@@ -21,7 +21,7 @@ const GLOBAL_INSTALL_COMMAND = `npm i -g ${packageJson.name}`;
21
21
  const OPENSPEC_PACKAGE = '@fission-ai/openspec';
22
22
  const OMC_PACKAGE = 'oh-my-claude-sisyphus';
23
23
  const OMC_REPO_URL = 'https://github.com/Yeachan-Heo/oh-my-claudecode';
24
- const CAVEMEM_PACKAGE = 'cavemem';
24
+ const COLONY_PACKAGE = '@imdeadpool/colony-cli';
25
25
  const NPX_BIN = process.env.GUARDEX_NPX_BIN || 'npx';
26
26
  const GUARDEX_HOME_DIR = path.resolve(process.env.GUARDEX_HOME_DIR || os.homedir());
27
27
  const GLOBAL_TOOLCHAIN_SERVICES = [
@@ -32,7 +32,7 @@ const GLOBAL_TOOLCHAIN_SERVICES = [
32
32
  dependencyUrl: OMC_REPO_URL,
33
33
  },
34
34
  { name: OPENSPEC_PACKAGE, packageName: OPENSPEC_PACKAGE },
35
- { name: CAVEMEM_PACKAGE, packageName: CAVEMEM_PACKAGE },
35
+ { name: 'colony', packageName: COLONY_PACKAGE },
36
36
  {
37
37
  name: '@imdeadpool/codex-account-switcher',
38
38
  packageName: '@imdeadpool/codex-account-switcher',
@@ -674,7 +674,7 @@ module.exports = {
674
674
  OPENSPEC_PACKAGE,
675
675
  OMC_PACKAGE,
676
676
  OMC_REPO_URL,
677
- CAVEMEM_PACKAGE,
677
+ COLONY_PACKAGE,
678
678
  NPX_BIN,
679
679
  GUARDEX_HOME_DIR,
680
680
  GLOBAL_TOOLCHAIN_SERVICES,
@@ -310,7 +310,7 @@ function extractAgentBranchFinishPrUrl(output) {
310
310
  function doctorFinishFlowIsPending(output) {
311
311
  return (
312
312
  /\[agent-branch-finish\] PR merge not completed yet; leaving PR open\./.test(output) ||
313
- /\[agent-branch-finish\] Merge pending review\/check policy\. Branch cleanup skipped for now\./.test(output) ||
313
+ /\[agent-branch-finish\] PR pending review\/check policy\./.test(output) ||
314
314
  /\[agent-branch-finish\] PR auto-merge enabled; waiting for required checks\/reviews\./.test(output)
315
315
  );
316
316
  }
@@ -65,8 +65,9 @@ function assertProtectedMainWriteAllowed(options, commandName) {
65
65
  }
66
66
 
67
67
  function extractAgentBranchStartMetadata(output) {
68
- const branchMatch = String(output || '').match(/^\[agent-branch-start\] Created branch: (.+)$/m);
69
- const worktreeMatch = String(output || '').match(/^\[agent-branch-start\] Worktree: (.+)$/m);
68
+ const outputText = String(output || '');
69
+ const branchMatch = outputText.match(/^\[agent-branch-start\] (?:Created branch|Reusing existing branch): (.+)$/m);
70
+ const worktreeMatch = outputText.match(/^\[agent-branch-start\] Worktree: (.+)$/m);
70
71
  return {
71
72
  branch: branchMatch ? branchMatch[1].trim() : '',
72
73
  worktreePath: worktreeMatch ? worktreeMatch[1].trim() : '',
@@ -1,63 +1,421 @@
1
1
  <!-- multiagent-safety:START -->
2
- ## Multi-Agent Execution Contract (GX)
2
+ ## Multi-Agent Execution Contract: Guardex + Colony
3
3
 
4
- **Repo toggle.** Guardex is enabled by default. If the repo root `.env` sets `GUARDEX_ON=0`, `false`, `no`, or `off`, treat this entire Guardex contract as disabled for the repo and do not require Guardex worktrees, lock claims, completion flow, or OpenSpec workflow until `GUARDEX_ON` is re-enabled.
4
+ ### Repo toggle
5
5
 
6
- **Repo toggle examples.** Add one of these lines to the repo-root `.env` file:
7
- `GUARDEX_ON=0` disables Guardex for that repo.
8
- `GUARDEX_ON=1` explicitly enables Guardex for that repo again.
6
+ Guardex is enabled by default.
9
7
 
10
- **Task-size routing.** Small tasks stay in direct caveman-only mode. For typos, single-file tweaks, one-liners, version bumps, or similarly bounded asks, solve directly and do not escalate into heavy OMX orchestration just because a keyword appears. Treat `quick:`, `simple:`, `tiny:`, `minor:`, `small:`, `just:`, and `only:` as explicit lightweight escape hatches.
11
- Promote to OMX orchestration only when the task is medium/large: multi-file behavior changes, API/schema work, refactors, migrations, architecture, cross-cutting scope, or long prompts. Heavy OMX modes (`ralph`, `autopilot`, `team`, `ultrawork`, `swarm`, `ralplan`) are for that larger scope. If the task grows while working, upgrade then.
8
+ If the repo-root `.env` sets any of these values, treat this entire Guardex contract as disabled for the repo:
12
9
 
13
- ## Token / Context Budget
10
+ ```text
11
+ GUARDEX_ON=0
12
+ GUARDEX_ON=false
13
+ GUARDEX_ON=no
14
+ GUARDEX_ON=off
15
+ ```
16
+
17
+ When disabled, do not require Guardex worktrees, lock claims, completion flow, or OpenSpec workflow until `GUARDEX_ON` is re-enabled.
18
+
19
+ To explicitly enable:
20
+
21
+ ```text
22
+ GUARDEX_ON=1
23
+ ```
24
+
25
+ ### Core rules
26
+
27
+ - Work from an `agent/*` branch and worktree, never directly on the protected base branch.
28
+ - Claim files before edits.
29
+ - Use Colony for coordination before falling back to OMX state/notepad.
30
+ - Use OpenSpec for durable behavior contracts and change-driven work.
31
+ - Keep outputs compact: less word, same proof.
32
+ - Commit, push, and open/update a PR for completed work unless the user explicitly says to keep it local.
33
+ - Do not embed stale memory dumps, generated status snapshots, PR transcripts, session history, or long logs in this file.
34
+
35
+ ### Task-size routing
36
+
37
+ Small tasks stay direct and caveman-only.
38
+
39
+ For typos, single-file tweaks, one-liners, version bumps, comment-only changes, or similarly bounded asks, solve directly and do not escalate into heavy orchestration just because a keyword appears.
40
+
41
+ Treat these prefixes as explicit lightweight escape hatches:
42
+
43
+ - `quick:`
44
+ - `simple:`
45
+ - `tiny:`
46
+ - `minor:`
47
+ - `small:`
48
+ - `just:`
49
+ - `only:`
50
+
51
+ Promote to full Guardex / OMX orchestration only when scope grows into:
52
+
53
+ - multi-file behavior change
54
+ - API/schema work
55
+ - refactor
56
+ - migration
57
+ - architecture
58
+ - cross-cutting scope
59
+ - long prompt
60
+ - multi-agent execution
61
+
62
+ ### Colony coordination loop
63
+
64
+ Use Colony as the primary coordination surface.
65
+
66
+ On every startup, resume, follow-up, or "continue" request, run this order:
67
+
68
+ 1. `mcp__colony__hivemind_context`
69
+ 2. `mcp__colony__attention_inbox`
70
+ 3. `mcp__colony__task_ready_for_agent`
71
+ 4. `mcp__colony__search` only when prior decisions, earlier lanes, file history, or error context matter.
72
+
73
+ Rules:
74
+
75
+ - Use `task_ready_for_agent` to choose work.
76
+ - Use `task_list` only for browsing/debugging. Do not use `task_list` as the normal work picker.
77
+ - If an agent reaches for `task_list` repeatedly while choosing work, stop and call `task_ready_for_agent` instead. `task_list` is an inventory tool, not a scheduler.
78
+ - Before editing files on an active task, call `task_claim_file` for each touched file.
79
+ - Use `task_post` for task-thread notes, decisions, blockers, and working-state updates.
80
+ - Use `task_message` / `task_messages` for directed agent-to-agent communication.
81
+ - Use `get_observations` only after compact Colony tools return IDs worth hydrating.
82
+
83
+ Fallback:
84
+
85
+ - Colony is considered unavailable only when the MCP namespace is missing, the tool call fails, or the installed Colony server does not expose the required tool.
86
+ - If `attention_inbox` or `task_ready_for_agent` is missing, fall back to `hivemind_context`, then `task_list`, then hydrate only the relevant task IDs.
87
+ - Do not skip Colony just because OMX state exists. OMX is fallback, not the first coordination source.
88
+ - Read `.omx/state` and `.omx/notepad.md` only when Colony is unavailable, missing the needed state, or the task explicitly depends on legacy OMX state.
89
+ - Keep `.omx/notepad.md` lean: live handoffs only.
90
+
91
+ ### Working-state notes
92
+
93
+ Colony is preferred over generic notepad state.
94
+
95
+ A working-state note should be task-scoped, searchable, and useful to another agent resuming the lane.
96
+
97
+ When saving progress, use a task-scoped Colony note when possible:
98
+
99
+ ```text
100
+ task_post kind=note
101
+ content="branch=<branch>; task=<task>; blocker=<blocker>; next=<next>; evidence=<path|command|PR|spec>"
102
+ ```
103
+
104
+ Use exactly these fields for handoff-style notes:
105
+
106
+ - `branch`
107
+ - `task`
108
+ - `blocker`
109
+ - `next`
110
+ - `evidence`
111
+
112
+ Do not store long proof dumps, stale narrative, or full logs in notepads. Put bulky proof in OpenSpec artifacts, PRs, or command output.
113
+
114
+ ### Token / context budget
14
115
 
15
116
  Default: less word, same proof.
16
117
 
17
- - For prompts about `token inefficiency`, `reviewer mode`, `minimal token overhead`, or session waste patterns, switch into low-overhead mode: plan in at most 4 bullets, execute by phase, batch related reads/commands, avoid duplicate reads and interactive loops, keep outputs compact, and verify once per phase.
18
- - Low output alone is not a defect. A bounded run that finishes in roughly <=10 steps is usually fine; low output spread across 20+ steps with rising per-turn input is fragmentation and should be treated as context growth first.
19
- - Startup / resume summaries stay tiny: `branch`, `task`, `blocker`, `next step`, and `evidence`.
20
- - Memory-driven starts stay ordered: read active `.omx/state` first, then one live `.omx/notepad.md` handoff, then external memory only when the task depends on prior repo decisions, a previous lane, or ambiguous continuity. Stop after the first 1-2 relevant hits.
21
- - Front-load scaffold/path discovery into one grouped inspection pass. Avoid serial `ls` / `find` / `rg` / `cat` retries that only rediscover the same path state.
22
- - Treat repeated `write_stdin`, repeated `sed` / `cat` peeks, and tiny diagnostic follow-up checks as strong negative signals. If they appear alongside climbing input cost, stop the probe loop and batch the next phase.
118
+ - For prompts about `token inefficiency`, `reviewer mode`, `minimal token overhead`, or session waste patterns, switch into low-overhead mode.
119
+ - Plan in at most 4 bullets.
120
+ - Execute by phase.
121
+ - Batch related reads and commands.
122
+ - Avoid duplicate reads and interactive loops.
123
+ - Keep outputs compact.
124
+ - Verify once per phase.
125
+ - Low output alone is not a defect. A bounded run that finishes in roughly <=10 steps is usually fine.
126
+ - Low output spread across 20+ steps with rising per-turn input is fragmentation and should be treated as context growth first.
127
+ - Startup / resume summaries stay tiny: `branch`, `task`, `blocker`, `next`, and `evidence`.
128
+ - Front-load scaffold/path discovery into one grouped inspection pass. Avoid serial `ls` / `find` / `rg` / `cat` retries that rediscover the same path state.
129
+ - Treat repeated `write_stdin`, repeated `sed` / `cat` peeks, and tiny diagnostic follow-up checks as strong negative signals.
130
+ - If a session turns fragmented, collapse back to inspect once, patch once, verify once, and summarize once.
23
131
  - Tool / hook summaries stay tiny: command, status, last meaningful lines only. Drop routine hook boilerplate.
24
132
  - Keep raw terminal interaction out of long-lived context. For `write_stdin` or interactive babysitting, retain only process, action sent, current result, and next action.
25
133
  - Keep execution log separate from reasoning context: full commands/stdout belong in logs, while prompt context keeps only the latest 1-2 checkpoints plus the newest tool-result summary.
26
- - Treat local edit/commit, remote publish/PR, CI diagnosis, and cleanup as bounded phases. Do not spend fresh narration or approval turns on obvious safe follow-ons inside an already authorized phase unless the risk changes.
27
- - When a session turns fragmented, collapse back to inspect once, patch once, verify once, and summarize once.
28
- - Use a fixed checkpoint shape when compacting: `Task`, `Done`, `Current status`, and `Next`.
29
- - Keep `.omx/notepad.md` lean: live handoffs only. Use exactly `branch`, `task`, `blocker`, `next step`, and `evidence`; move narrative proof into OpenSpec artifacts, PRs, or command output.
134
+ - Treat local edit/commit, remote publish/PR, CI diagnosis, and cleanup as bounded phases.
135
+ - Do not spend fresh narration or approval turns on obvious safe follow-ons inside an already authorized phase unless the risk changes.
136
+
137
+ ### Caveman style
138
+
139
+ Commentary and progress updates use smart-caveman `ultra` by default:
140
+
141
+ - Answer order stays fixed: answer first, cause next, fix or next step last.
142
+ - drop filler
143
+ - use fragments when clear
144
+ - answer first
145
+ - cause next
146
+ - fix or next step last
147
+
148
+ Keep exact literals unchanged:
149
+
150
+ - code
151
+ - commands
152
+ - file paths
153
+ - flags
154
+ - env vars
155
+ - URLs
156
+ - numbers
157
+ - timestamps
158
+ - error text
159
+
160
+ Switch back to `lite` or normal wording for:
30
161
 
31
- ## OMX Caveman Style
162
+ - security warnings
163
+ - irreversible actions
164
+ - privacy/compliance notes
165
+ - ordered instructions where fragments may confuse
166
+ - confused users
167
+ - commits
168
+ - PR text
169
+ - specs
170
+ - logs
171
+ - blocker evidence
32
172
 
33
- - Commentary and progress updates use smart-caveman `ultra` by default: drop articles, filler, pleasantries, and hedging. Fragments are fine when they stay clear.
34
- - Answer order stays fixed: answer first, cause next, fix or next step last. If yes/no fits, say yes/no first.
35
- - Keep literals exact: code, commands, file paths, flags, env vars, URLs, numbers, timestamps, and error text are never caveman-compressed.
36
- - Auto-clarity wins: switch back to `lite` or normal wording for security warnings, irreversible actions, privacy/compliance notes, ordered instructions where fragments may confuse, or when the user is confused and needs more detail.
37
- - Boundaries stay normal/exact for code, commits, PR text, specs, logs, and blocker evidence.
173
+ Never caveman-compress commands, file paths, specs, logs, or blocker evidence.
174
+
175
+ ### Isolation
176
+
177
+ Every task runs on a dedicated `agent/*` branch and worktree.
178
+
179
+ Start with:
180
+
181
+ ```bash
182
+ gx branch start "<task>" "<agent-name>"
183
+ ```
184
+
185
+ Treat the base branch (`main` / `dev`) as read-only while an agent branch is active.
38
186
 
39
- **Isolation.** Every task runs on a dedicated `agent/*` branch + worktree. Start with `gx branch start "<task>" "<agent-name>"`. Treat the base branch (`main`/`dev`) as read-only while an agent branch is active. The `.githooks/post-checkout` hook auto-reverts primary-branch switches during agent sessions and auto-stashes a dirty tree before reverting - bypass only with `GUARDEX_ALLOW_PRIMARY_BRANCH_SWITCH=1`.
40
187
  For every new task, including follow-up work in the same chat/session, if an assigned agent sub-branch/worktree is already open, continue in that sub-branch instead of creating a fresh lane unless the user explicitly redirects scope.
41
- Never implement directly on the local/base branch checkout; keep it unchanged and perform all edits in the agent sub-branch/worktree.
42
188
 
43
- **Primary-tree lock (blocking).** On the primary checkout, do NOT run any of: `git checkout <ref>`, `git switch <ref>`, `git switch -c ...`, `git checkout -b ...`, or `git worktree add <path> <existing-agent-branch>`. The only branch-changing commands allowed on primary are `git fetch` and `git pull --ff-only` against the protected branch itself. To work on any `agent/*` branch, run `gx branch start ...` first, then `cd` into the printed `.omc/agent-worktrees/...` path and run every subsequent git command from inside that worktree. If you find yourself typing `git checkout agent/...` or `git switch agent/...` from the primary cwd, stop - that is the mistake that flips primary onto an agent branch.
189
+ Never implement directly on the local/base branch checkout. Keep it unchanged and perform all edits in the agent sub-branch/worktree.
190
+
191
+ ### Primary-tree lock
192
+
193
+ On the primary checkout, do not run:
194
+
195
+ ```bash
196
+ git checkout <ref>
197
+ git switch <ref>
198
+ git switch -c ...
199
+ git checkout -b ...
200
+ git worktree add <path> <existing-agent-branch>
201
+ ```
202
+
203
+ Allowed on primary:
204
+
205
+ ```bash
206
+ git fetch
207
+ git pull --ff-only
208
+ ```
209
+
210
+ To work on any `agent/*` branch, run `gx branch start ...` first, then `cd` into the printed worktree path and run every subsequent git command from inside that worktree.
211
+
212
+ If you are about to type `git checkout agent/...` or `git switch agent/...` from the primary checkout, stop. That is the mistake that flips primary onto an agent branch.
213
+
214
+ ### Dirty-tree rule
215
+
216
+ Finish or stash edits inside the worktree they belong to before any branch switch on primary.
217
+
218
+ The post-checkout guard may auto-stash a dirty primary tree as:
219
+
220
+ ```text
221
+ guardex-auto-revert <ts> <prev>-><new>
222
+ ```
223
+
224
+ That is a safety net, not a workflow. Do not rely on it routinely.
225
+
226
+ Recover stashed changes with:
227
+
228
+ ```bash
229
+ git stash list | grep 'guardex-auto-revert'
230
+ ```
231
+
232
+ ### Ownership
44
233
 
45
- **Dirty-tree rule.** Finish or stash edits inside the worktree they belong to before any branch switch on primary. The post-checkout guard auto-stashes a dirty primary tree as `guardex-auto-revert <ts> <prev>-><new>` before reverting, but that is a safety net, not a workflow; do not rely on it routinely. Recover stashed changes with `git stash list | grep 'guardex-auto-revert'`.
234
+ Before editing, claim files.
46
235
 
47
- **Ownership.** Before editing, claim files: `gx locks claim --branch "<agent-branch>" <file...>`. Before deleting, confirm the path is in your claim. Don't edit outside your scope unless reassigned.
236
+ Preferred Colony path when on an active task:
48
237
 
49
- **Handoff gate.** Post a one-line handoff note (plan/change, owned scope, intended action) before editing. Re-read the latest handoffs before replacing others' code.
238
+ ```text
239
+ mcp__colony__task_claim_file
240
+ ```
241
+
242
+ Guardex lock path:
243
+
244
+ ```bash
245
+ gx locks claim --branch "<agent-branch>" <file...>
246
+ ```
247
+
248
+ Before deleting, confirm the path is in your claim.
249
+
250
+ Do not edit outside your scope unless reassigned.
251
+
252
+ If another agent owns or recently touched nearby code:
253
+
254
+ 1. read latest Colony context
255
+ 2. post a handoff or question
256
+ 3. avoid reverting unrelated changes
257
+ 4. report conflicts instead of overwriting
258
+
259
+ ### Handoff gate
260
+
261
+ Before editing, post a one-line handoff note through Colony `task_post` when a task is active.
262
+
263
+ Use `.omx/notepad.md` only when Colony is unavailable or the lane explicitly depends on legacy OMX state.
264
+
265
+ Handoff shape:
266
+
267
+ ```text
268
+ branch=<branch>; task=<task>; blocker=<blocker>; next=<next>; evidence=<path|command|PR|spec>
269
+ ```
270
+
271
+ Re-read latest Colony context before replacing another agent's code.
272
+
273
+ ### Completion
274
+
275
+ Finish with:
276
+
277
+ ```bash
278
+ gx branch finish --branch "<agent-branch>" --via-pr --wait-for-merge --cleanup
279
+ ```
280
+
281
+ or:
282
+
283
+ ```bash
284
+ gx finish --all
285
+ ```
286
+
287
+ Task is complete only when:
288
+
289
+ 1. changes are committed
290
+ 2. branch is pushed
291
+ 3. PR URL is recorded
292
+ 4. PR state is `MERGED`
293
+ 5. sandbox worktree is pruned
294
+ 6. final handoff records proof
295
+
296
+ If anything blocks, append a `BLOCKED:` note and stop. Do not half-finish.
50
297
 
51
- **Completion.** Finish with `gx branch finish --branch "<agent-branch>" --via-pr --wait-for-merge --cleanup` (or `gx finish --all`). Task is only complete when: commit pushed, PR URL recorded, state = `MERGED`, sandbox worktree pruned. If anything blocks, append a `BLOCKED:` note and stop - don't half-finish.
52
298
  OMX completion policy: when a task is done, the agent must commit the task changes, push the agent branch, and create/update a PR before considering the branch complete.
53
299
 
54
- **Parallel safety.** Assume other agents edit nearby. Never revert unrelated changes. Report conflicts in the handoff.
300
+ ### Parallel safety
301
+
302
+ Assume other agents edit nearby.
303
+
304
+ - Never revert unrelated changes.
305
+ - Never simplify or delete critical shared paths without explicit request and regression coverage.
306
+ - Report conflicts in the handoff.
307
+ - Prefer compatibility-preserving changes over endpoint-specific rewrites when other agents may be changing adjacent systems.
308
+
309
+ ### Reporting
310
+
311
+ Every completion handoff includes:
312
+
313
+ ```text
314
+ branch
315
+ task
316
+ files changed
317
+ behavior touched
318
+ verification commands/results
319
+ PR URL
320
+ merge state
321
+ sandbox cleanup state
322
+ risks/follow-ups
323
+ ```
324
+
325
+ If blocked, use:
326
+
327
+ ```text
328
+ BLOCKED:
329
+ branch=<branch>
330
+ task=<task>
331
+ blocker=<blocker>
332
+ next=<next>
333
+ evidence=<path|command|PR|spec>
334
+ ```
335
+
336
+ ### Open questions
337
+
338
+ If Codex/Claude hits an unresolved question, branching decision, or blocker that should survive chat, record it in:
339
+
340
+ ```text
341
+ openspec/plan/<plan-slug>/open-questions.md
342
+ ```
343
+
344
+ as an unchecked item:
345
+
346
+ ```md
347
+ - [ ] Question or blocker...
348
+ ```
349
+
350
+ Resolve it in-place when answered instead of burying it in chat-only notes.
351
+
352
+ ### OpenSpec
353
+
354
+ OpenSpec is the source of truth for change-driven repo work.
355
+
356
+ For change-driven tasks, keep:
357
+
358
+ ```text
359
+ openspec/changes/<slug>/tasks.md
360
+ ```
361
+
362
+ current during work, not batched at the end.
363
+
364
+ Task scaffolds and manual task edits must include a final completion/cleanup section that ends with PR merge + sandbox cleanup and records PR URL + final `MERGED` evidence.
365
+
366
+ Validate specs before archive:
367
+
368
+ ```bash
369
+ openspec validate --specs
370
+ ```
371
+
372
+ Never archive unverified work.
373
+
374
+ For `T0` / small `T1` lanes, use the compact Colony spec path when available. One Colony handoff plus `colony-spec.md` is enough. Do not create proposal/spec/tasks unless the task grows.
375
+
376
+ For `T2` / `T3` lanes, keep proposal, spec, design, and tasks live while implementing.
377
+
378
+ ### Version bumps
379
+
380
+ If a change bumps a published version, the same PR records release notes in the appropriate OpenSpec artifact or release-note mechanism for the repo.
381
+
382
+ Do not edit `CHANGELOG.md` directly unless the repo explicitly requires manual changelog edits.
383
+
384
+ ### Verification gates
385
+
386
+ Before claiming completion, run the narrowest meaningful verification for the touched area.
387
+
388
+ Examples:
389
+
390
+ ```bash
391
+ pnpm test
392
+ pnpm typecheck
393
+ pnpm lint
394
+ ```
395
+
396
+ If a command cannot run, record:
397
+
398
+ ```text
399
+ command
400
+ reason it could not run
401
+ risk
402
+ next
403
+ ```
404
+
405
+ Do not claim green verification without command output evidence.
406
+
407
+ ### What not to put in this file
55
408
 
56
- **Reporting.** Every completion handoff includes: files changed, behavior touched, verification commands + results, risks/follow-ups.
409
+ Do not embed:
57
410
 
58
- **Open questions.** If Codex/Claude hits an unresolved question, branching decision, or blocker that should survive chat, record it in `openspec/plan/<plan-slug>/open-questions.md` as an unchecked `- [ ]` item. Resolve it in-place when answered instead of burying it in chat-only notes.
411
+ - stale memory dumps
412
+ - PR transcripts
413
+ - long logs
414
+ - generated status snapshots
415
+ - session history
416
+ - full OpenSpec examples
417
+ - repeated copies of long workflow docs
59
418
 
60
- **OpenSpec (when change-driven).** Keep `openspec/changes/<slug>/tasks.md` checkboxes current during work, not batched at the end. Task scaffolds and manual task edits must include an explicit final completion/cleanup section that ends with PR merge + sandbox cleanup (`gx finish --via-pr --wait-for-merge --cleanup` or `gx branch finish ... --cleanup`) and records PR URL + final `MERGED` evidence. Verify specs with `openspec validate --specs` before archive. Don't archive unverified.
419
+ Keep this section as the hard multi-agent contract. Put long examples and recovery docs in repo-specific workflow docs.
61
420
 
62
- **Version bumps.** If a change bumps a published version, the same PR updates release notes/changelog.
63
421
  <!-- multiagent-safety:END -->
@@ -508,7 +508,7 @@ is_local_branch_delete_error() {
508
508
 
509
509
  is_remote_branch_missing_error() {
510
510
  local output="$1"
511
- if [[ "$output" == *"remote ref does not exist"* ]] || [[ "$output" == *"failed to push some refs"* ]]; then
511
+ if [[ "$output" == *"remote ref does not exist"* ]]; then
512
512
  return 0
513
513
  fi
514
514
  return 1
@@ -831,7 +831,7 @@ if [[ "$PUSH_ENABLED" -eq 1 ]]; then
831
831
  echo "[agent-branch-finish] Merge did not complete within wait window; keeping branch open." >&2
832
832
  exit 1
833
833
  fi
834
- echo "[agent-branch-finish] Merge pending review/check policy. Branch cleanup skipped for now." >&2
834
+ echo "[agent-branch-finish] PR pending review/check policy. Worktree retained for now; the autofinish watcher (or 'gx worktree prune --include-pr-merged --delete-branches') will prune it after merge. Verify with 'git worktree list' before claiming the worktree is still on disk." >&2
835
835
  exit 0
836
836
  fi
837
837
  echo "[agent-branch-finish] PR flow failed." >&2
@@ -893,8 +893,8 @@ if [[ "$CLEANUP_AFTER_MERGE" -eq 1 ]]; then
893
893
  if is_remote_branch_missing_error "$remote_delete_output"; then
894
894
  echo "[agent-branch-finish] Remote branch '${SOURCE_BRANCH}' was already deleted; continuing cleanup." >&2
895
895
  else
896
+ echo "[agent-branch-finish] Warning: remote branch cleanup failed for '${SOURCE_BRANCH}' after merge; continuing local cleanup." >&2
896
897
  echo "$remote_delete_output" >&2
897
- exit 1
898
898
  fi
899
899
  fi
900
900
  fi
@@ -288,7 +288,7 @@ if [[ -z "$TARGET_BRANCH" ]]; then
288
288
  fi
289
289
 
290
290
  printf '%s\n' "$start_output"
291
- TARGET_BRANCH="$(printf '%s\n' "$start_output" | sed -n 's/^\[agent-branch-start\] Created branch: //p' | head -n 1)"
291
+ TARGET_BRANCH="$(printf '%s\n' "$start_output" | sed -n -E 's/^\[agent-branch-start\] (Created branch|Reusing existing branch): //p' | head -n 1)"
292
292
  target_worktree="$(printf '%s\n' "$start_output" | sed -n 's/^\[agent-branch-start\] Worktree: //p' | head -n 1)"
293
293
  if [[ -z "$TARGET_BRANCH" || -z "$target_worktree" ]]; then
294
294
  echo "[agent-branch-merge] Unable to parse target branch/worktree from agent-branch-start output." >&2
@@ -15,6 +15,7 @@ OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
15
15
  OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
16
16
  OPENSPEC_MASTERPLAN_LABEL_RAW="${GUARDEX_OPENSPEC_MASTERPLAN_LABEL-masterplan}"
17
17
  OPENSPEC_TIER_RAW="${GUARDEX_OPENSPEC_TIER:-T3}"
18
+ REUSE_EXISTING_RAW="${GUARDEX_BRANCH_START_REUSE_EXISTING:-true}"
18
19
  PRINT_NAME_ONLY=0
19
20
  POSITIONAL_ARGS=()
20
21
 
@@ -58,6 +59,14 @@ while [[ $# -gt 0 ]]; do
58
59
  OPENSPEC_TIER_RAW="${2:-$OPENSPEC_TIER_RAW}"
59
60
  shift 2
60
61
  ;;
62
+ --reuse-existing|--reuse)
63
+ REUSE_EXISTING_RAW="true"
64
+ shift
65
+ ;;
66
+ --new|--no-reuse|--no-reuse-existing)
67
+ REUSE_EXISTING_RAW="false"
68
+ shift
69
+ ;;
61
70
  --in-place|--allow-in-place)
62
71
  echo "[agent-branch-start] In-place branch mode is disabled." >&2
63
72
  echo "[agent-branch-start] This command always creates an isolated worktree to keep your active checkout unchanged." >&2
@@ -78,7 +87,7 @@ while [[ $# -gt 0 ]]; do
78
87
  ;;
79
88
  -*)
80
89
  echo "[agent-branch-start] Unknown option: $1" >&2
81
- echo "Usage: $0 [task] [agent] [base] [--worktree-root <path>] [--print-name-only]" >&2
90
+ echo "Usage: $0 [task] [agent] [base] [--worktree-root <path>] [--reuse-existing|--new] [--print-name-only]" >&2
82
91
  exit 1
83
92
  ;;
84
93
  *)
@@ -90,7 +99,7 @@ done
90
99
 
91
100
  if [[ "${#POSITIONAL_ARGS[@]}" -gt 3 ]]; then
92
101
  echo "[agent-branch-start] Too many positional arguments." >&2
93
- echo "Usage: $0 [task] [agent] [base] [--worktree-root <path>]" >&2
102
+ echo "Usage: $0 [task] [agent] [base] [--worktree-root <path>] [--reuse-existing|--new]" >&2
94
103
  exit 1
95
104
  fi
96
105
 
@@ -254,6 +263,7 @@ normalize_bool() {
254
263
  }
255
264
 
256
265
  OPENSPEC_AUTO_INIT="$(normalize_bool "$OPENSPEC_AUTO_INIT_RAW" "1")"
266
+ REUSE_EXISTING_WORKTREE="$(normalize_bool "$REUSE_EXISTING_RAW" "1")"
257
267
 
258
268
  normalize_tier() {
259
269
  local raw="${1:-}"
@@ -370,6 +380,22 @@ resolve_worktree_leaf() {
370
380
  printf '%s' "${branch_name//\//__}"
371
381
  }
372
382
 
383
+ print_reused_agent_worktree() {
384
+ local branch_name="$1"
385
+ local worktree_path="$2"
386
+
387
+ echo "[agent-branch-start] Reusing existing branch: ${branch_name}"
388
+ echo "[agent-branch-start] Worktree: ${worktree_path}"
389
+ echo "[agent-branch-start] OpenSpec tier: ${OPENSPEC_TIER}"
390
+ echo "[agent-branch-start] OpenSpec change: existing worktree"
391
+ echo "[agent-branch-start] OpenSpec plan: existing worktree"
392
+ echo "[agent-branch-start] Next steps:"
393
+ echo " cd \"${worktree_path}\""
394
+ echo " gx locks claim --branch \"${branch_name}\" <file...>"
395
+ echo " # continue work in this existing sandbox"
396
+ echo " gx branch finish --branch \"${branch_name}\" --via-pr --wait-for-merge"
397
+ }
398
+
373
399
  has_local_changes() {
374
400
  local root="$1"
375
401
  if ! git -C "$root" diff --quiet; then
@@ -384,6 +410,101 @@ has_local_changes() {
384
410
  return 1
385
411
  }
386
412
 
413
+ meaningful_slug_tokens() {
414
+ local raw="$1"
415
+ printf '%s' "$raw" \
416
+ | tr '[:upper:]' '[:lower:]' \
417
+ | tr '/_' '--' \
418
+ | tr '-' '\n' \
419
+ | awk '
420
+ length($0) < 4 { next }
421
+ $0 ~ /^[0-9]+$/ { next }
422
+ $0 ~ /^(agent|agents|branch|codex|claude|continue|dirty|existing|fix|from|implement|make|matching|reuse|start|task|that|this|update|with|worktree|worktrees)$/ { next }
423
+ !seen[$0]++ { print }
424
+ '
425
+ }
426
+
427
+ token_match_score() {
428
+ local task_slug="$1"
429
+ local branch_descriptor="$2"
430
+ local task_tokens branch_tokens token score
431
+ task_tokens="$(meaningful_slug_tokens "$task_slug")"
432
+ branch_tokens="$(meaningful_slug_tokens "$branch_descriptor")"
433
+ score=0
434
+
435
+ if [[ -z "$task_tokens" ]] || [[ -z "$branch_tokens" ]]; then
436
+ printf '0'
437
+ return 0
438
+ fi
439
+
440
+ while IFS= read -r token; do
441
+ if grep -Fxq "$token" <<<"$branch_tokens"; then
442
+ score=$((score + 1))
443
+ fi
444
+ done <<<"$task_tokens"
445
+
446
+ printf '%s' "$score"
447
+ }
448
+
449
+ managed_worktree_roots() {
450
+ local repo="$1"
451
+ local explicit_root="$2"
452
+ local root
453
+ local seen_roots=$'\n'
454
+
455
+ for root in \
456
+ "${repo}/${explicit_root}" \
457
+ "${repo}/.omx/agent-worktrees" \
458
+ "${repo}/.omc/agent-worktrees"; do
459
+ if [[ -n "$root" && "$seen_roots" != *$'\n'"$root"$'\n'* ]]; then
460
+ seen_roots+="${root}"$'\n'
461
+ printf '%s\n' "$root"
462
+ fi
463
+ done
464
+ }
465
+
466
+ find_matching_dirty_agent_worktree() {
467
+ local repo="$1"
468
+ local worktree_root_rel="$2"
469
+ local task_slug="$3"
470
+ local agent_slug="$4"
471
+ local best_score=0
472
+ local best_branch=""
473
+ local best_worktree=""
474
+ local best_count=0
475
+ local root entry branch descriptor score
476
+
477
+ while IFS= read -r root; do
478
+ [[ -d "$root" ]] || continue
479
+ while IFS= read -r entry; do
480
+ [[ -d "$entry" ]] || continue
481
+ if ! branch="$(git -C "$entry" rev-parse --abbrev-ref HEAD 2>/dev/null)"; then
482
+ continue
483
+ fi
484
+ [[ "$branch" == "agent/${agent_slug}/"* ]] || continue
485
+ has_local_changes "$entry" || continue
486
+
487
+ descriptor="${branch#agent/${agent_slug}/}"
488
+ score="$(token_match_score "$task_slug" "$descriptor")"
489
+ [[ "$score" =~ ^[0-9]+$ ]] || score=0
490
+ [[ "$score" -gt 0 ]] || continue
491
+
492
+ if [[ "$score" -gt "$best_score" ]]; then
493
+ best_score="$score"
494
+ best_branch="$branch"
495
+ best_worktree="$entry"
496
+ best_count=1
497
+ elif [[ "$score" -eq "$best_score" ]]; then
498
+ best_count=$((best_count + 1))
499
+ fi
500
+ done < <(find "$root" -mindepth 1 -maxdepth 1 -type d -print 2>/dev/null | sort)
501
+ done < <(managed_worktree_roots "$repo" "$worktree_root_rel")
502
+
503
+ if [[ "$best_score" -gt 0 && "$best_count" -eq 1 && -n "$best_branch" && -n "$best_worktree" ]]; then
504
+ printf '%s\t%s\n' "$best_branch" "$best_worktree"
505
+ fi
506
+ }
507
+
387
508
  resolve_stash_ref_by_message() {
388
509
  local root="$1"
389
510
  local message="$2"
@@ -550,6 +671,12 @@ if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -z "$BASE_BRANCH" ]]; then
550
671
  exit 1
551
672
  fi
552
673
 
674
+ current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
675
+ if [[ "$REUSE_EXISTING_WORKTREE" -eq 1 && "$current_branch" == agent/* ]]; then
676
+ print_reused_agent_worktree "$current_branch" "$repo_root"
677
+ exit 0
678
+ fi
679
+
553
680
  task_slug="$(sanitize_slug "$TASK_NAME" "task")"
554
681
  agent_slug="$(normalize_role "$AGENT_NAME")"
555
682
  if [[ "$WORKTREE_ROOT_EXPLICIT" -eq 0 ]]; then
@@ -565,6 +692,16 @@ if [[ "$PRINT_NAME_ONLY" -eq 1 ]]; then
565
692
  exit 0
566
693
  fi
567
694
 
695
+ if [[ "$REUSE_EXISTING_WORKTREE" -eq 1 ]]; then
696
+ matching_dirty_worktree="$(find_matching_dirty_agent_worktree "$repo_root" "$WORKTREE_ROOT_REL" "$task_slug" "$agent_slug")"
697
+ if [[ -n "$matching_dirty_worktree" ]]; then
698
+ IFS=$'\t' read -r reused_branch reused_worktree <<<"$matching_dirty_worktree"
699
+ echo "[agent-branch-start] Matched dirty managed worktree for requested task."
700
+ print_reused_agent_worktree "$reused_branch" "$reused_worktree"
701
+ exit 0
702
+ fi
703
+ fi
704
+
568
705
  if [[ "$BASE_BRANCH_EXPLICIT" -eq 0 ]]; then
569
706
  current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
570
707
  protected_branches_raw="$(resolve_protected_branches "$repo_root")"
@@ -681,6 +818,7 @@ if [[ -n "$auto_transfer_stash_ref" ]]; then
681
818
  fi
682
819
  fi
683
820
 
821
+ hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" ".venv"
684
822
  hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "node_modules"
685
823
  hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "apps/frontend/node_modules"
686
824
  hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "apps/backend/node_modules"
@@ -2395,6 +2395,74 @@ function isPathWithin(parentPath, targetPath) {
2395
2395
  return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath));
2396
2396
  }
2397
2397
 
2398
+ function normalizeAbsolutePath(value) {
2399
+ return typeof value === 'string' && value.trim() ? path.resolve(value) : '';
2400
+ }
2401
+
2402
+ function isManagedWorktreePath(worktreePath) {
2403
+ const normalizedWorktreePath = normalizeAbsolutePath(worktreePath);
2404
+ if (!normalizedWorktreePath) {
2405
+ return false;
2406
+ }
2407
+
2408
+ return MANAGED_WORKTREE_RELATIVE_ROOTS.some((relativeRoot) => {
2409
+ const normalizedRelativeRoot = path.normalize(relativeRoot);
2410
+ const marker = `${path.sep}${normalizedRelativeRoot}${path.sep}`;
2411
+ return normalizedWorktreePath.includes(marker);
2412
+ });
2413
+ }
2414
+
2415
+ function removeDeletedWorktreeWorkspaceFolder(worktreePath) {
2416
+ if (typeof vscode.workspace.updateWorkspaceFolders !== 'function') {
2417
+ return false;
2418
+ }
2419
+
2420
+ const normalizedWorktreePath = normalizeAbsolutePath(worktreePath);
2421
+ if (!normalizedWorktreePath) {
2422
+ return false;
2423
+ }
2424
+
2425
+ const workspaceFolders = vscode.workspace.workspaceFolders || [];
2426
+ const folderIndex = workspaceFolders.findIndex((folder) => (
2427
+ normalizeAbsolutePath(folder?.uri?.fsPath) === normalizedWorktreePath
2428
+ ));
2429
+ if (folderIndex < 0) {
2430
+ return false;
2431
+ }
2432
+
2433
+ try {
2434
+ return vscode.workspace.updateWorkspaceFolders(folderIndex, 1) === true;
2435
+ } catch (_error) {
2436
+ return false;
2437
+ }
2438
+ }
2439
+
2440
+ async function closeDeletedWorktreeRepository(worktreePath) {
2441
+ const normalizedWorktreePath = normalizeAbsolutePath(worktreePath);
2442
+ if (!normalizedWorktreePath || fs.existsSync(normalizedWorktreePath)) {
2443
+ return false;
2444
+ }
2445
+
2446
+ try {
2447
+ await vscode.commands.executeCommand('git.close', vscode.Uri.file(normalizedWorktreePath));
2448
+ } catch (_error) {
2449
+ // The Git extension may have already removed this repository.
2450
+ }
2451
+
2452
+ removeDeletedWorktreeWorkspaceFolder(normalizedWorktreePath);
2453
+ return true;
2454
+ }
2455
+
2456
+ function findDeletedManagedWorkspaceFolders() {
2457
+ return (vscode.workspace.workspaceFolders || [])
2458
+ .map((folder) => normalizeAbsolutePath(folder?.uri?.fsPath))
2459
+ .filter((workspacePath) => (
2460
+ workspacePath
2461
+ && !fs.existsSync(workspacePath)
2462
+ && isManagedWorktreePath(workspacePath)
2463
+ ));
2464
+ }
2465
+
2398
2466
  function localizeChangeForSession(session, change) {
2399
2467
  if (!change?.absolutePath || !isPathWithin(session.worktreePath, change.absolutePath)) {
2400
2468
  return null;
@@ -3434,6 +3502,8 @@ class ActiveAgentsRefreshController {
3434
3502
  this.inspectPanelManager = inspectPanelManager;
3435
3503
  this.refreshTimer = null;
3436
3504
  this.sessionWatchers = new Map();
3505
+ this.closedMissingWorktreeRepositories = new Set();
3506
+ this.observedWorktreePaths = new Set();
3437
3507
  }
3438
3508
 
3439
3509
  scheduleRefresh() {
@@ -3456,8 +3526,23 @@ class ActiveAgentsRefreshController {
3456
3526
  const repoEntries = await findRepoSessionEntries();
3457
3527
  const liveSessionKeys = new Set();
3458
3528
 
3529
+ for (const workspacePath of findDeletedManagedWorkspaceFolders()) {
3530
+ await this.closeMissingWorktreeRepository(workspacePath);
3531
+ }
3532
+
3459
3533
  for (const entry of repoEntries) {
3460
3534
  for (const session of entry.sessions) {
3535
+ const worktreePath = sessionWorktreePath(session);
3536
+ const normalizedWorktreePath = normalizeAbsolutePath(worktreePath);
3537
+ if (normalizedWorktreePath && !fs.existsSync(normalizedWorktreePath)) {
3538
+ await this.closeMissingWorktreeRepository(normalizedWorktreePath);
3539
+ continue;
3540
+ }
3541
+ if (normalizedWorktreePath) {
3542
+ this.closedMissingWorktreeRepositories.delete(normalizedWorktreePath);
3543
+ this.observedWorktreePaths.add(normalizedWorktreePath);
3544
+ }
3545
+
3461
3546
  const sessionKey = resolveSessionWatcherKey(session);
3462
3547
  liveSessionKeys.add(sessionKey);
3463
3548
  if (this.sessionWatchers.has(sessionKey)) {
@@ -3468,8 +3553,20 @@ class ActiveAgentsRefreshController {
3468
3553
  resolveSessionGitIndexPath(session.worktreePath),
3469
3554
  );
3470
3555
  const disposables = bindRefreshWatcher(watcher, () => this.scheduleRefresh());
3471
- this.sessionWatchers.set(sessionKey, { watcher, disposables });
3556
+ this.sessionWatchers.set(sessionKey, {
3557
+ watcher,
3558
+ disposables,
3559
+ worktreePath: normalizedWorktreePath,
3560
+ });
3561
+ }
3562
+ }
3563
+
3564
+ for (const observedWorktreePath of this.observedWorktreePaths) {
3565
+ if (fs.existsSync(observedWorktreePath)) {
3566
+ this.closedMissingWorktreeRepositories.delete(observedWorktreePath);
3567
+ continue;
3472
3568
  }
3569
+ await this.closeMissingWorktreeRepository(observedWorktreePath);
3473
3570
  }
3474
3571
 
3475
3572
  for (const [sessionKey, entry] of this.sessionWatchers) {
@@ -3477,12 +3574,25 @@ class ActiveAgentsRefreshController {
3477
3574
  continue;
3478
3575
  }
3479
3576
 
3577
+ if (entry.worktreePath && !fs.existsSync(entry.worktreePath)) {
3578
+ await this.closeMissingWorktreeRepository(entry.worktreePath);
3579
+ }
3480
3580
  disposeAll(entry.disposables);
3481
3581
  entry.watcher.dispose();
3482
3582
  this.sessionWatchers.delete(sessionKey);
3483
3583
  }
3484
3584
  }
3485
3585
 
3586
+ async closeMissingWorktreeRepository(worktreePath) {
3587
+ const normalizedWorktreePath = normalizeAbsolutePath(worktreePath);
3588
+ if (!normalizedWorktreePath || this.closedMissingWorktreeRepositories.has(normalizedWorktreePath)) {
3589
+ return;
3590
+ }
3591
+
3592
+ this.closedMissingWorktreeRepositories.add(normalizedWorktreePath);
3593
+ await closeDeletedWorktreeRepository(normalizedWorktreePath);
3594
+ }
3595
+
3486
3596
  dispose() {
3487
3597
  if (this.refreshTimer) {
3488
3598
  clearTimeout(this.refreshTimer);
@@ -3,7 +3,7 @@
3
3
  "displayName": "GitGuardex Active Agents",
4
4
  "description": "Shows live Guardex sandbox sessions and repo changes in a dedicated VS Code Active Agents sidebar.",
5
5
  "publisher": "Recodee",
6
- "version": "0.0.20",
6
+ "version": "0.0.21",
7
7
  "license": "MIT",
8
8
  "icon": "icon.png",
9
9
  "engines": {