@imdeadpool/guardex 7.0.21 → 7.0.22

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.
@@ -218,18 +218,48 @@ fi
218
218
 
219
219
  get_worktree_for_branch() {
220
220
  local branch="$1"
221
- git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" '
221
+ git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${agent_worktree_root}/__source-probe-" '
222
222
  $1 == "worktree" { wt = $2 }
223
- $1 == "branch" && $2 == target { print wt; exit }
223
+ $1 == "branch" && $2 == target {
224
+ if (index(wt, probe_prefix) != 1) {
225
+ print wt
226
+ exit
227
+ }
228
+ }
224
229
  '
225
230
  }
226
231
 
232
+ remove_stale_source_probe_worktrees() {
233
+ local branch="$1"
234
+ local stale_probe=""
235
+
236
+ while IFS= read -r stale_probe; do
237
+ [[ -z "$stale_probe" ]] && continue
238
+ [[ "$stale_probe" == "$current_worktree" ]] && continue
239
+
240
+ echo "[agent-branch-finish] Removing stale source-probe worktree for '${branch}': ${stale_probe}" >&2
241
+ git -C "$stale_probe" rebase --abort >/dev/null 2>&1 || true
242
+ git -C "$stale_probe" merge --abort >/dev/null 2>&1 || true
243
+ git -C "$repo_root" worktree remove "$stale_probe" --force >/dev/null 2>&1 || true
244
+ done < <(
245
+ git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${agent_worktree_root}/__source-probe-" '
246
+ $1 == "worktree" { wt = $2 }
247
+ $1 == "branch" && $2 == target {
248
+ if (index(wt, probe_prefix) == 1) {
249
+ print wt
250
+ }
251
+ }
252
+ '
253
+ )
254
+ }
255
+
227
256
  is_clean_worktree() {
228
257
  local wt="$1"
229
258
  git -C "$wt" diff --quiet -- . ":(exclude).omx/state/agent-file-locks.json" \
230
259
  && git -C "$wt" diff --cached --quiet -- . ":(exclude).omx/state/agent-file-locks.json"
231
260
  }
232
261
 
262
+ remove_stale_source_probe_worktrees "$SOURCE_BRANCH"
233
263
  source_worktree="$(get_worktree_for_branch "$SOURCE_BRANCH")"
234
264
  created_source_probe=0
235
265
  source_probe_path=""
@@ -295,8 +325,13 @@ if [[ "$should_require_sync" -eq 1 ]] && git -C "$repo_root" show-ref --verify -
295
325
 
296
326
  echo "[agent-sync-guard] Auto-sync failed while rebasing '${SOURCE_BRANCH}' onto origin/${BASE_BRANCH}." >&2
297
327
  if [[ "$rebase_active" -eq 1 ]]; then
298
- echo "[agent-sync-guard] Resolve conflicts, then run: git -C \"$source_worktree\" rebase --continue" >&2
299
- echo "[agent-sync-guard] Or abort: git -C \"$source_worktree\" rebase --abort" >&2
328
+ if [[ "$created_source_probe" -eq 1 ]]; then
329
+ echo "[agent-sync-guard] Temporary source-probe worktree will be cleaned up on exit." >&2
330
+ echo "[agent-sync-guard] Reattach '${SOURCE_BRANCH}' in a regular worktree, then rebase it onto origin/${BASE_BRANCH} manually." >&2
331
+ else
332
+ echo "[agent-sync-guard] Resolve conflicts, then run: git -C \"$source_worktree\" rebase --continue" >&2
333
+ echo "[agent-sync-guard] Or abort: git -C \"$source_worktree\" rebase --abort" >&2
334
+ fi
300
335
  fi
301
336
  exit 1
302
337
  fi
@@ -366,6 +401,14 @@ is_local_branch_delete_error() {
366
401
  return 1
367
402
  }
368
403
 
404
+ is_remote_branch_missing_error() {
405
+ local output="$1"
406
+ if [[ "$output" == *"remote ref does not exist"* ]] || [[ "$output" == *"failed to push some refs"* ]]; then
407
+ return 0
408
+ fi
409
+ return 1
410
+ }
411
+
369
412
  read_pr_state() {
370
413
  local state_line
371
414
  state_line="$("$GH_BIN" pr view "$SOURCE_BRANCH" --json state,mergedAt,url --jq '[.state, (.mergedAt // ""), (.url // "")] | join("\u001f")' 2>/dev/null || true)"
@@ -568,7 +611,15 @@ if [[ "$CLEANUP_AFTER_MERGE" -eq 1 ]]; then
568
611
 
569
612
  if [[ "$PUSH_ENABLED" -eq 1 && "$DELETE_REMOTE_BRANCH" -eq 1 ]]; then
570
613
  if git -C "$repo_root" ls-remote --exit-code --heads origin "$SOURCE_BRANCH" >/dev/null 2>&1; then
571
- git -C "$repo_root" push origin --delete "$SOURCE_BRANCH"
614
+ remote_delete_output=""
615
+ if ! remote_delete_output="$(git -C "$repo_root" push origin --delete "$SOURCE_BRANCH" 2>&1)"; then
616
+ if is_remote_branch_missing_error "$remote_delete_output"; then
617
+ echo "[agent-branch-finish] Remote branch '${SOURCE_BRANCH}' was already deleted; continuing cleanup." >&2
618
+ else
619
+ echo "$remote_delete_output" >&2
620
+ exit 1
621
+ fi
622
+ fi
572
623
  fi
573
624
  fi
574
625
 
@@ -111,6 +111,13 @@ is_managed_worktree_path() {
111
111
  return 1
112
112
  }
113
113
 
114
+ is_temporary_worktree_path() {
115
+ local entry="$1"
116
+ local name
117
+ name="$(basename "$entry")"
118
+ [[ "$name" == __agent_integrate-* || "$name" == __source-probe-* ]]
119
+ }
120
+
114
121
  resolve_base_branch() {
115
122
  local configured=""
116
123
  local current=""
@@ -425,7 +432,9 @@ process_entry() {
425
432
  local remove_reason=""
426
433
  local branch_delete_mode="safe"
427
434
 
428
- if [[ -z "$branch_ref" ]]; then
435
+ if is_temporary_worktree_path "$wt"; then
436
+ remove_reason="temporary-worktree"
437
+ elif [[ -z "$branch_ref" ]]; then
429
438
  remove_reason="detached-worktree"
430
439
  elif ! git -C "$repo_root" show-ref --verify --quiet "refs/heads/${branch}"; then
431
440
  remove_reason="missing-branch"
@@ -452,6 +461,11 @@ process_entry() {
452
461
  return
453
462
  fi
454
463
 
464
+ if [[ "$remove_reason" == "temporary-worktree" ]]; then
465
+ git -C "$wt" rebase --abort >/dev/null 2>&1 || true
466
+ git -C "$wt" merge --abort >/dev/null 2>&1 || true
467
+ fi
468
+
455
469
  if [[ "$FORCE_DIRTY" -ne 1 ]] && ! is_clean_worktree "$wt"; then
456
470
  skipped_dirty=$((skipped_dirty + 1))
457
471
  echo "[agent-worktree-prune] Skipping dirty worktree (${remove_reason}): ${wt}"
@@ -17,9 +17,11 @@ node scripts/install-vscode-active-agents-extension.js
17
17
 
18
18
  What it does:
19
19
 
20
+ - Bundles a local GitGuardex icon so repo installs show branded extension metadata inside VS Code.
20
21
  - Adds an `Active Agents` view to the Source Control container.
21
22
  - Renders one repo node per live Guardex workspace with grouped `ACTIVE AGENTS` and `CHANGES` sections.
22
23
  - Splits live sessions inside `ACTIVE AGENTS` into `BLOCKED`, `WORKING NOW`, `IDLE`, `STALLED`, and `DEAD` groups so stuck, active, and inactive lanes stand out immediately.
24
+ - Mirrors the same live state in the VS Code status bar so the selected session or active-agent count stays visible outside the tree.
23
25
  - Shows one row per live Guardex sandbox session inside those activity groups.
24
26
  - Shows repo-root git changes in a sibling `CHANGES` section when the guarded repo itself is dirty.
25
27
  - Derives session state from dirty worktree status, git conflict markers, PID liveness, and recent file mtimes, surfaces working/dead counts in the repo/header summary, and shows changed-file counts for active edits.
@@ -20,6 +20,10 @@ const SESSION_SCAN_EXCLUDE_GLOB = '**/{node_modules,.git,.omx/agent-worktrees,.o
20
20
  const WORKTREE_LOCK_SCAN_EXCLUDE_GLOB = '**/{node_modules,.git}/**';
21
21
  const SESSION_SCAN_LIMIT = 200;
22
22
  const REFRESH_DEBOUNCE_MS = 250;
23
+ const ACTIVE_AGENTS_MANIFEST_RELATIVE = path.join('vscode', 'guardex-active-agents', 'package.json');
24
+ const ACTIVE_AGENTS_INSTALL_SCRIPT_RELATIVE = path.join('scripts', 'install-vscode-active-agents-extension.js');
25
+ const RELOAD_WINDOW_ACTION = 'Reload Window';
26
+ const UPDATE_LATER_ACTION = 'Later';
23
27
  const SESSION_ACTIVITY_GROUPS = [
24
28
  { kind: 'blocked', label: 'BLOCKED' },
25
29
  { kind: 'working', label: 'WORKING NOW' },
@@ -93,10 +97,83 @@ function sessionIdleDecoration(session, now = Date.now()) {
93
97
  return undefined;
94
98
  }
95
99
 
100
+ function formatCountLabel(count, singular, plural = `${singular}s`) {
101
+ return `${count} ${count === 1 ? singular : plural}`;
102
+ }
103
+
104
+ function sessionIdentityLabel(session) {
105
+ const agentName = typeof session?.agentName === 'string' ? session.agentName.trim() : '';
106
+ const taskName = typeof session?.taskName === 'string' ? session.taskName.trim() : '';
107
+ const label = typeof session?.label === 'string' ? session.label.trim() : '';
108
+
109
+ if (agentName && taskName) {
110
+ return `${agentName} · ${taskName}`;
111
+ }
112
+ if (agentName && label) {
113
+ return `${agentName} · ${label}`;
114
+ }
115
+
116
+ return agentName || taskName || label || 'session';
117
+ }
118
+
119
+ function sessionCommitPlaceholder(session) {
120
+ if (!session?.branch) {
121
+ return 'Pick an Active Agents session to commit its worktree.';
122
+ }
123
+
124
+ return `Commit ${sessionIdentityLabel(session)} on ${session.branch} · ${formatCountLabel(session.lockCount || 0, 'lock')} (Ctrl+Enter)`;
125
+ }
126
+
127
+ function agentNameFromBranch(branch) {
128
+ const segments = String(branch || '')
129
+ .split('/')
130
+ .map((segment) => segment.trim())
131
+ .filter(Boolean);
132
+ if (segments[0] === 'agent' && segments[1]) {
133
+ return segments[1];
134
+ }
135
+ return segments[0] || 'lock';
136
+ }
137
+
138
+ function agentBadgeFromBranch(branch) {
139
+ const normalized = agentNameFromBranch(branch).toUpperCase().replace(/[^A-Z0-9]/g, '');
140
+ return normalized.slice(0, 2) || 'LK';
141
+ }
142
+
143
+ function buildActiveAgentsStatusSummary(summary) {
144
+ const activeCount = Math.max(0, (summary?.sessionCount || 0) - (summary?.deadCount || 0));
145
+ if (activeCount > 0) {
146
+ return `$(git-branch) ${formatCountLabel(activeCount, 'active agent')}`;
147
+ }
148
+ return `$(git-branch) ${formatCountLabel(summary?.sessionCount || 0, 'tracked session')}`;
149
+ }
150
+
151
+ function buildActiveAgentsStatusTooltip(selectedSession, summary) {
152
+ if (selectedSession?.branch) {
153
+ return [
154
+ selectedSession.branch,
155
+ sessionIdentityLabel(selectedSession),
156
+ formatCountLabel(selectedSession.lockCount || 0, 'lock'),
157
+ selectedSession.worktreePath,
158
+ 'Click to open Source Control.',
159
+ ].filter(Boolean).join('\n');
160
+ }
161
+
162
+ const activeCount = Math.max(0, (summary?.sessionCount || 0) - (summary?.deadCount || 0));
163
+ return [
164
+ formatCountLabel(activeCount, 'active agent'),
165
+ formatCountLabel(summary?.workingCount || 0, 'working now session', 'working now sessions'),
166
+ summary?.deadCount ? formatCountLabel(summary.deadCount, 'dead session') : '',
167
+ 'Click to open Source Control.',
168
+ ].filter(Boolean).join('\n');
169
+ }
170
+
96
171
  class SessionDecorationProvider {
97
172
  constructor(nowProvider = () => Date.now()) {
98
173
  this.nowProvider = nowProvider;
99
174
  this.sessionsByUri = new Map();
175
+ this.lockEntriesByFileUri = new Map();
176
+ this.selectedBranch = '';
100
177
  this.onDidChangeFileDecorationsEmitter = new vscode.EventEmitter();
101
178
  this.onDidChangeFileDecorations = this.onDidChangeFileDecorationsEmitter.event;
102
179
  }
@@ -107,13 +184,54 @@ class SessionDecorationProvider {
107
184
  );
108
185
  }
109
186
 
187
+ updateLockEntries(repoEntries) {
188
+ const nextEntriesByUri = new Map();
189
+ for (const entry of repoEntries || []) {
190
+ for (const [relativePath, lockEntry] of entry.lockEntries || []) {
191
+ nextEntriesByUri.set(
192
+ vscode.Uri.file(path.join(entry.repoRoot, relativePath)).toString(),
193
+ { branch: lockEntry.branch },
194
+ );
195
+ }
196
+ }
197
+ this.lockEntriesByFileUri = nextEntriesByUri;
198
+ }
199
+
200
+ setSelectedBranch(branch) {
201
+ this.selectedBranch = typeof branch === 'string' ? branch.trim() : '';
202
+ }
203
+
110
204
  refresh() {
111
205
  this.onDidChangeFileDecorationsEmitter.fire();
112
206
  }
113
207
 
114
208
  provideFileDecoration(uri) {
115
209
  if (!uri || uri.scheme !== SESSION_DECORATION_SCHEME) {
116
- return undefined;
210
+ if (!uri || uri.scheme !== 'file') {
211
+ return undefined;
212
+ }
213
+
214
+ const lockEntry = this.lockEntriesByFileUri.get(uri.toString());
215
+ if (!lockEntry?.branch) {
216
+ return undefined;
217
+ }
218
+
219
+ const ownsSelectedSession = Boolean(this.selectedBranch) && lockEntry.branch === this.selectedBranch;
220
+ return {
221
+ badge: agentBadgeFromBranch(lockEntry.branch),
222
+ tooltip: ownsSelectedSession
223
+ ? `Locked by selected session ${lockEntry.branch}`
224
+ : this.selectedBranch
225
+ ? `Locked by ${lockEntry.branch} (selected session: ${this.selectedBranch})`
226
+ : `Locked by ${lockEntry.branch}`,
227
+ color: new vscode.ThemeColor(
228
+ ownsSelectedSession
229
+ ? 'gitDecoration.modifiedResourceForeground'
230
+ : this.selectedBranch
231
+ ? 'list.errorForeground'
232
+ : 'list.warningForeground',
233
+ ),
234
+ };
117
235
  }
118
236
 
119
237
  return sessionIdleDecoration(this.sessionsByUri.get(uri.toString()), this.nowProvider());
@@ -451,6 +569,120 @@ function readCurrentBranch(repoRoot) {
451
569
  }
452
570
  }
453
571
 
572
+ function parseSimpleSemver(version) {
573
+ const parts = String(version || '')
574
+ .split('.')
575
+ .map((part) => Number.parseInt(part, 10));
576
+ if (parts.length !== 3 || parts.some((part) => Number.isNaN(part))) {
577
+ return null;
578
+ }
579
+ return parts;
580
+ }
581
+
582
+ function compareSimpleSemver(left, right) {
583
+ const leftParts = parseSimpleSemver(left);
584
+ const rightParts = parseSimpleSemver(right);
585
+ if (!leftParts || !rightParts) {
586
+ return 0;
587
+ }
588
+
589
+ for (let index = 0; index < leftParts.length; index += 1) {
590
+ if (leftParts[index] !== rightParts[index]) {
591
+ return leftParts[index] - rightParts[index];
592
+ }
593
+ }
594
+
595
+ return 0;
596
+ }
597
+
598
+ function readJsonFile(filePath) {
599
+ try {
600
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
601
+ } catch (_error) {
602
+ return null;
603
+ }
604
+ }
605
+
606
+ function resolveActiveAgentsAutoUpdateCandidate(installedVersion) {
607
+ const candidates = [];
608
+
609
+ for (const workspaceFolder of vscode.workspace.workspaceFolders || []) {
610
+ const repoRoot = workspaceFolder?.uri?.fsPath;
611
+ if (!repoRoot) {
612
+ continue;
613
+ }
614
+
615
+ const manifestPath = path.join(repoRoot, ACTIVE_AGENTS_MANIFEST_RELATIVE);
616
+ const installScriptPath = path.join(repoRoot, ACTIVE_AGENTS_INSTALL_SCRIPT_RELATIVE);
617
+ if (!fs.existsSync(manifestPath) || !fs.existsSync(installScriptPath)) {
618
+ continue;
619
+ }
620
+
621
+ const manifest = readJsonFile(manifestPath);
622
+ const nextVersion = typeof manifest?.version === 'string' ? manifest.version.trim() : '';
623
+ if (!nextVersion || compareSimpleSemver(nextVersion, installedVersion) <= 0) {
624
+ continue;
625
+ }
626
+
627
+ candidates.push({ repoRoot, installScriptPath, version: nextVersion });
628
+ }
629
+
630
+ candidates.sort((left, right) => compareSimpleSemver(right.version, left.version));
631
+ return candidates[0] || null;
632
+ }
633
+
634
+ function runActiveAgentsInstallScript(repoRoot, installScriptPath) {
635
+ return new Promise((resolve, reject) => {
636
+ cp.execFile(
637
+ process.execPath,
638
+ [installScriptPath],
639
+ { cwd: repoRoot, encoding: 'utf8' },
640
+ (error, stdout, stderr) => {
641
+ if (error) {
642
+ reject(new Error(String(stderr || stdout || error.message || '').trim() || 'install failed'));
643
+ return;
644
+ }
645
+ resolve({ stdout, stderr });
646
+ },
647
+ );
648
+ });
649
+ }
650
+
651
+ async function maybeAutoUpdateActiveAgentsExtension(context) {
652
+ const installedVersion = typeof context?.extension?.packageJSON?.version === 'string'
653
+ ? context.extension.packageJSON.version.trim()
654
+ : '';
655
+ if (!installedVersion) {
656
+ return;
657
+ }
658
+
659
+ const candidate = resolveActiveAgentsAutoUpdateCandidate(installedVersion);
660
+ if (!candidate) {
661
+ return;
662
+ }
663
+
664
+ try {
665
+ await runActiveAgentsInstallScript(candidate.repoRoot, candidate.installScriptPath);
666
+ } catch (error) {
667
+ const failure = typeof error?.message === 'string' && error.message.trim()
668
+ ? error.message.trim()
669
+ : 'install failed';
670
+ vscode.window.showWarningMessage?.(
671
+ `GitGuardex Active Agents could not auto-update to ${candidate.version}: ${failure}`,
672
+ );
673
+ return;
674
+ }
675
+
676
+ const selection = await vscode.window.showInformationMessage?.(
677
+ `GitGuardex Active Agents updated to ${candidate.version}. Reload Window to use the newest companion.`,
678
+ RELOAD_WINDOW_ACTION,
679
+ UPDATE_LATER_ACTION,
680
+ );
681
+ if (selection === RELOAD_WINDOW_ACTION) {
682
+ await vscode.commands.executeCommand('workbench.action.reloadWindow');
683
+ }
684
+ }
685
+
454
686
  function decorateSession(session, lockRegistry) {
455
687
  return {
456
688
  ...session,
@@ -836,6 +1068,11 @@ class ActiveAgentsProvider {
836
1068
  this.treeView = null;
837
1069
  this.lockRegistryByRepoRoot = new Map();
838
1070
  this.selectedSession = null;
1071
+ this.viewSummary = {
1072
+ sessionCount: 0,
1073
+ workingCount: 0,
1074
+ deadCount: 0,
1075
+ };
839
1076
  }
840
1077
 
841
1078
  getTreeItem(element) {
@@ -856,6 +1093,7 @@ class ActiveAgentsProvider {
856
1093
  const currentKey = sessionSelectionKey(this.selectedSession);
857
1094
  const nextKey = sessionSelectionKey(nextSession);
858
1095
  this.selectedSession = nextSession;
1096
+ this.decorationProvider?.setSelectedBranch(nextSession?.branch || '');
859
1097
  if (currentKey !== nextKey) {
860
1098
  this.onDidChangeSelectedSessionEmitter.fire(this.selectedSession);
861
1099
  }
@@ -865,6 +1103,10 @@ class ActiveAgentsProvider {
865
1103
  return this.selectedSession ? { ...this.selectedSession } : null;
866
1104
  }
867
1105
 
1106
+ getViewSummary() {
1107
+ return { ...this.viewSummary };
1108
+ }
1109
+
868
1110
  syncSelectedSession(repoEntries) {
869
1111
  if (!this.selectedSession) {
870
1112
  return;
@@ -882,6 +1124,11 @@ class ActiveAgentsProvider {
882
1124
  }
883
1125
 
884
1126
  const activeCount = Math.max(0, sessionCount - deadCount);
1127
+ this.viewSummary = {
1128
+ sessionCount,
1129
+ workingCount,
1130
+ deadCount,
1131
+ };
885
1132
  const badgeTooltipParts = [];
886
1133
  if (activeCount > 0) {
887
1134
  badgeTooltipParts.push(`${activeCount} active agent${activeCount === 1 ? '' : 's'}`);
@@ -918,6 +1165,7 @@ class ActiveAgentsProvider {
918
1165
 
919
1166
  this.updateViewState(sessionCount, workingCount, deadCount);
920
1167
  this.decorationProvider?.updateSessions(repoEntries.flatMap((entry) => entry.sessions));
1168
+ this.decorationProvider?.updateLockEntries(repoEntries);
921
1169
  return repoEntries;
922
1170
  }
923
1171
 
@@ -996,6 +1244,7 @@ class ActiveAgentsProvider {
996
1244
  changes: readRepoChanges(repoRoot).map((change) => (
997
1245
  decorateChange(change, lockRegistry, currentBranch)
998
1246
  )),
1247
+ lockEntries: Array.from(lockRegistry.entriesByPath.entries()),
999
1248
  };
1000
1249
  });
1001
1250
  }
@@ -1080,6 +1329,9 @@ function activate(context) {
1080
1329
  'gitguardex.activeAgents.commitInput',
1081
1330
  'Active Agents Commit',
1082
1331
  );
1332
+ const activeAgentsStatusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10);
1333
+ activeAgentsStatusItem.name = 'GitGuardex Active Agents';
1334
+ activeAgentsStatusItem.command = 'gitguardex.activeAgents.focus';
1083
1335
  provider.attachTreeView(treeView);
1084
1336
  const scheduleRefresh = () => refreshController.scheduleRefresh();
1085
1337
  const refresh = () => void refreshController.refreshNow();
@@ -1089,11 +1341,24 @@ function activate(context) {
1089
1341
  const updateCommitInput = (session) => {
1090
1342
  sourceControl.inputBox.enabled = true;
1091
1343
  sourceControl.inputBox.visible = true;
1092
- sourceControl.inputBox.placeholder = session?.label
1093
- ? `Commit ${session.label} (Ctrl+Enter)`
1094
- : 'Pick an Active Agents session to commit its worktree.';
1344
+ sourceControl.inputBox.placeholder = sessionCommitPlaceholder(session);
1345
+ };
1346
+ const updateStatusBar = () => {
1347
+ const selectedSession = provider.getSelectedSession();
1348
+ const summary = provider.getViewSummary();
1349
+ if ((summary.sessionCount || 0) <= 0) {
1350
+ activeAgentsStatusItem.hide();
1351
+ return;
1352
+ }
1353
+
1354
+ activeAgentsStatusItem.text = selectedSession?.branch
1355
+ ? `$(git-branch) ${sessionIdentityLabel(selectedSession)} · ${formatCountLabel(selectedSession.lockCount || 0, 'lock')}`
1356
+ : buildActiveAgentsStatusSummary(summary);
1357
+ activeAgentsStatusItem.tooltip = buildActiveAgentsStatusTooltip(selectedSession, summary);
1358
+ activeAgentsStatusItem.show();
1095
1359
  };
1096
1360
  updateCommitInput(null);
1361
+ updateStatusBar();
1097
1362
  const commitSelectedSession = async () => {
1098
1363
  const selectedSession = provider.getSelectedSession();
1099
1364
  if (!selectedSession?.worktreePath) {
@@ -1140,15 +1405,27 @@ function activate(context) {
1140
1405
  scheduleRefresh();
1141
1406
  };
1142
1407
 
1143
- provider.onDidChangeSelectedSession(updateCommitInput);
1408
+ provider.onDidChangeSelectedSession((session) => {
1409
+ updateCommitInput(session);
1410
+ updateStatusBar();
1411
+ decorationProvider.refresh();
1412
+ });
1413
+ provider.onDidChangeTreeData(() => {
1414
+ updateCommitInput(provider.getSelectedSession());
1415
+ updateStatusBar();
1416
+ });
1144
1417
 
1145
1418
  context.subscriptions.push(
1146
1419
  treeView,
1147
1420
  sourceControl,
1421
+ activeAgentsStatusItem,
1148
1422
  refreshController,
1149
1423
  vscode.window.registerFileDecorationProvider(decorationProvider),
1150
1424
  vscode.commands.registerCommand('gitguardex.activeAgents.startAgent', () => startAgentFromPrompt(refresh)),
1151
1425
  vscode.commands.registerCommand('gitguardex.activeAgents.refresh', refresh),
1426
+ vscode.commands.registerCommand('gitguardex.activeAgents.focus', async () => {
1427
+ await vscode.commands.executeCommand('workbench.view.scm');
1428
+ }),
1152
1429
  vscode.commands.registerCommand('gitguardex.activeAgents.commitSelectedSession', commitSelectedSession),
1153
1430
  vscode.commands.registerCommand('gitguardex.activeAgents.openWorktree', async (session) => {
1154
1431
  if (!session?.worktreePath) {
@@ -1190,6 +1467,7 @@ function activate(context) {
1190
1467
  ...bindRefreshWatcher(worktreeLockWatcher, scheduleRefresh),
1191
1468
  );
1192
1469
  void refreshController.refreshNow();
1470
+ void maybeAutoUpdateActiveAgentsExtension(context);
1193
1471
  }
1194
1472
 
1195
1473
  function deactivate() {}
@@ -3,8 +3,9 @@
3
3
  "displayName": "GitGuardex Active Agents",
4
4
  "description": "Shows live Guardex sandbox sessions and repo changes inside VS Code Source Control.",
5
5
  "publisher": "recodeee",
6
- "version": "0.0.1",
6
+ "version": "0.0.3",
7
7
  "license": "MIT",
8
+ "icon": "icon.png",
8
9
  "engines": {
9
10
  "vscode": "^1.88.0"
10
11
  },
@@ -13,6 +14,7 @@
13
14
  "Other"
14
15
  ],
15
16
  "activationEvents": [
17
+ "onStartupFinished",
16
18
  "workspaceContains:.omx/state/active-sessions",
17
19
  "workspaceContains:.omx/agent-worktrees",
18
20
  "workspaceContains:.omc/agent-worktrees",