From fb4ebf8a8bfb10308953aaf34eaa0648390872f3 Mon Sep 17 00:00:00 2001 From: NagyVikt Date: Sat, 25 Apr 2026 15:24:25 +0200 Subject: [PATCH] Prepare fresh npm release 7.0.37 Bump the package to the next unpublished patch version and record README/OpenSpec release notes so the registry can accept a new publish. The release lane also syncs the Active Agents extension template with the canonical workspace source because the package metadata tests proved the shipped bundle was stale after the Colony task UI merge. Constraint: npm latest was 7.0.36 before this lane Constraint: Release verification requires template/runtime parity for shipped VS Code Active Agents assets Rejected: Bump only package.json | package-lock and README release notes must stay synchronized Confidence: high Scope-risk: narrow Directive: Keep vscode/guardex-active-agents/extension.js and templates/vscode/guardex-active-agents/extension.js byte-for-byte synchronized when either changes Tested: npm test Tested: node --check bin/multiagent-safety.js Tested: npm pack --dry-run Tested: openspec validate --specs Not-tested: npm publish requires registry auth/OTP or trusted publishing --- README.md | 8 + .../.openspec.yaml | 2 + .../notes.md | 7 + package-lock.json | 4 +- package.json | 2 +- .../vscode/guardex-active-agents/extension.js | 168 ++++++++++++++++-- 6 files changed, 171 insertions(+), 20 deletions(-) create mode 100644 openspec/changes/agent-codex-release-guardex-7-0-37-2026-04-25-15-11/.openspec.yaml create mode 100644 openspec/changes/agent-codex-release-guardex-7-0-37-2026-04-25-15-11/notes.md diff --git a/README.md b/README.md index 9f012db..ff6024b 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,14 @@ Being honest about where this still has issues:
v7.x +### v7.0.37 +- Bumped `@imdeadpool/guardex` from `7.0.36` to `7.0.37` so the current + package can publish under a fresh npm version after `7.0.36` reached the + registry. +- Synced the shipped Active Agents template with the canonical VS Code + extension source so Colony task counts and details install with the package. +- No new CLI command behavior is introduced in this release lane. + ### v7.0.36 - Bumped `@imdeadpool/guardex` from `7.0.35` to `7.0.36` so the latest branch-finish cwd-prune fix can ship under a fresh npm version after PR #424. diff --git a/openspec/changes/agent-codex-release-guardex-7-0-37-2026-04-25-15-11/.openspec.yaml b/openspec/changes/agent-codex-release-guardex-7-0-37-2026-04-25-15-11/.openspec.yaml new file mode 100644 index 0000000..1b75776 --- /dev/null +++ b/openspec/changes/agent-codex-release-guardex-7-0-37-2026-04-25-15-11/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-04-25 diff --git a/openspec/changes/agent-codex-release-guardex-7-0-37-2026-04-25-15-11/notes.md b/openspec/changes/agent-codex-release-guardex-7-0-37-2026-04-25-15-11/notes.md new file mode 100644 index 0000000..f6030fc --- /dev/null +++ b/openspec/changes/agent-codex-release-guardex-7-0-37-2026-04-25-15-11/notes.md @@ -0,0 +1,7 @@ +# Release notes + +- Bump `@imdeadpool/guardex` from `7.0.36` to `7.0.37` for the next publishable release. +- Registry baseline before the bump: `@imdeadpool/guardex@7.0.36` with `latest` pointing to `7.0.36`. +- GitHub release target: `v7.0.37`. +- Synced `templates/vscode/guardex-active-agents/extension.js` with the canonical `vscode/guardex-active-agents/extension.js` bundle so package install parity tests pass. +- No CLI behavior changes are introduced in this release lane. diff --git a/package-lock.json b/package-lock.json index 2a01c3a..afcb51a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imdeadpool/guardex", - "version": "7.0.36", + "version": "7.0.37", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imdeadpool/guardex", - "version": "7.0.36", + "version": "7.0.37", "license": "MIT", "dependencies": { "jsonc-parser": "3.3.1", diff --git a/package.json b/package.json index 2122f43..9b52864 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imdeadpool/guardex", - "version": "7.0.36", + "version": "7.0.37", "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.", "license": "MIT", "preferGlobal": true, diff --git a/templates/vscode/guardex-active-agents/extension.js b/templates/vscode/guardex-active-agents/extension.js index 39557c3..1b70667 100644 --- a/templates/vscode/guardex-active-agents/extension.js +++ b/templates/vscode/guardex-active-agents/extension.js @@ -1,6 +1,8 @@ const fs = require('node:fs'); const path = require('node:path'); const cp = require('node:child_process'); +const http = require('node:http'); +const os = require('node:os'); const vscode = require('vscode'); const { clearWorktreeActivityCache, @@ -40,6 +42,84 @@ const ACTIVE_AGENTS_EXTENSION_ID = 'Recodee.gitguardex-active-agents'; const RESTART_EXTENSION_HOST_COMMAND = 'workbench.action.restartExtensionHost'; const REFRESH_POLL_INTERVAL_MS = 30_000; const INSPECT_PANEL_VIEW_TYPE = 'gitguardex.activeAgents.inspect'; +const COLONY_DEFAULT_PORT = 37777; +const COLONY_SNAPSHOT_TTL_MS = 5_000; +const COLONY_FETCH_TIMEOUT_MS = 800; + +function colonyDataDir() { + return process.env.COLONY_HOME + || process.env.CAVEMEM_HOME + || path.join(os.homedir(), '.colony'); +} + +function readColonyPort() { + try { + const raw = fs.readFileSync(path.join(colonyDataDir(), 'settings.json'), 'utf8'); + const parsed = JSON.parse(raw); + const port = Number(parsed?.workerPort); + return Number.isFinite(port) && port > 0 ? port : COLONY_DEFAULT_PORT; + } catch (_error) { + return COLONY_DEFAULT_PORT; + } +} + +function fetchColonyJson(urlPath) { + return new Promise((resolve) => { + const req = http.get( + { + hostname: '127.0.0.1', + port: readColonyPort(), + path: urlPath, + timeout: COLONY_FETCH_TIMEOUT_MS, + }, + (res) => { + if (res.statusCode !== 200) { + res.resume(); + resolve(null); + return; + } + let body = ''; + res.setEncoding('utf8'); + res.on('data', (chunk) => { + body += chunk; + }); + res.on('end', () => { + try { + resolve(JSON.parse(body)); + } catch (_error) { + resolve(null); + } + }); + }, + ); + req.on('error', () => resolve(null)); + req.on('timeout', () => { + req.destroy(); + resolve(null); + }); + }); +} + +const colonyTasksCache = new Map(); + +async function readColonyTasksForRepo(repoRoot) { + const cached = colonyTasksCache.get(repoRoot); + if (cached && Date.now() - cached.at < COLONY_SNAPSHOT_TTL_MS) { + return cached.tasks; + } + const tasks = await fetchColonyJson( + `/api/colony/tasks?repo_root=${encodeURIComponent(repoRoot)}`, + ); + const resolved = Array.isArray(tasks) ? tasks : []; + colonyTasksCache.set(repoRoot, { at: Date.now(), tasks: resolved }); + return resolved; +} + +function compactColonyBranchLabel(branch) { + if (typeof branch !== 'string' || !branch) return 'unknown'; + const parts = branch.split('/').filter(Boolean); + return parts.length > 2 ? parts.slice(-2).join('/') : branch; +} const GIT_CONFIGURATION_SECTION = 'git'; const REPO_SCAN_IGNORED_FOLDERS_SETTING = 'repositoryScanIgnoredFolders'; const BUNDLED_FILE_ICONS_MANIFEST_RELATIVE = path.join('fileicons', 'gitguardex-fileicons.json'); @@ -840,10 +920,18 @@ function buildOverviewDescription(summary) { formatCountLabel(summary?.workingCount || 0, 'working agent'), formatCountLabel(summary?.finishedCount || 0, 'finished agent'), formatCountLabel(summary?.idleCount || 0, 'idle agent'), + summary?.colonyTaskCount + ? formatCountLabel(summary.colonyTaskCount, 'colony task') + : '', + summary?.pendingHandoffCount + ? formatCountLabel(summary.pendingHandoffCount, 'pending handoff') + : '', formatCountLabel(summary?.unassignedChangeCount || 0, 'unassigned change'), formatCountLabel(summary?.lockedFileCount || 0, 'locked file'), formatCountLabel(summary?.conflictCount || 0, 'conflict'), - ].join(' · '); + ] + .filter(Boolean) + .join(' · '); } function buildRepoDescription(summary) { @@ -1252,7 +1340,9 @@ class RepoItem extends vscode.TreeItem { this.changes = changes; this.unassignedChanges = options.unassignedChanges || []; this.lockEntries = options.lockEntries || []; - this.overview = options.overview || buildRepoOverview(sessions, this.unassignedChanges, this.lockEntries); + this.colonyTasks = Array.isArray(options.colonyTasks) ? options.colonyTasks : []; + this.overview = options.overview + || buildRepoOverview(sessions, this.unassignedChanges, this.lockEntries, this.colonyTasks); this.description = buildRepoDescription(this.overview); this.tooltip = buildRepoTooltip(repoRoot, this.overview); this.iconPath = themeIcon('repo'); @@ -2524,7 +2614,8 @@ function countChangedPaths(repoRoot, sessions, changes) { return changedKeys.size; } -function buildRepoOverview(sessions, unassignedChanges, lockEntries) { +function buildRepoOverview(sessions, unassignedChanges, lockEntries, colonyTasks = []) { + const colonyTaskList = Array.isArray(colonyTasks) ? colonyTasks : []; return { sessionCount: sessions.length, workingCount: countWorkingSessions(sessions), @@ -2536,6 +2627,11 @@ function buildRepoOverview(sessions, unassignedChanges, lockEntries) { (total, session) => total + (session.conflictCount || 0), 0, ) + (unassignedChanges || []).filter((change) => change.hasForeignLock).length, + colonyTaskCount: colonyTaskList.length, + pendingHandoffCount: colonyTaskList.reduce( + (total, task) => total + (task.pending_handoff_count || 0), + 0, + ), }; } @@ -3023,12 +3119,14 @@ class ActiveAgentsProvider { const { repoRootChanges } = partitionChangesByOwnership(sessions, changes); const unassignedChanges = sortUnassignedChanges(repoRootChanges); + const colonyTasks = Array.isArray(entry.colonyTasks) ? entry.colonyTasks : []; return { ...entry, sessions, changes, unassignedChanges, - overview: buildRepoOverview(sessions, unassignedChanges, entry.lockEntries), + colonyTasks, + overview: buildRepoOverview(sessions, unassignedChanges, entry.lockEntries, colonyTasks), }; }); @@ -3140,6 +3238,37 @@ class ActiveAgentsProvider { iconId: 'file-directory', })); } + const colonyTaskList = Array.isArray(element.colonyTasks) ? element.colonyTasks : []; + if (colonyTaskList.length > 0) { + const colonyItems = colonyTaskList.map((task) => { + const pendingLabel = task.pending_handoff_count > 0 + ? formatCountLabel(task.pending_handoff_count, 'pending handoff') + : 'quiet'; + const participantLabel = + (task.participants || []).map((p) => p.agent).filter(Boolean).join(', ') + || 'no participants'; + return new DetailItem( + `#${task.id} · ${compactColonyBranchLabel(task.branch)}`, + `${participantLabel} · ${pendingLabel}`, + { + iconId: task.pending_handoff_count > 0 ? 'warning' : 'comment-discussion', + tooltip: [ + task.branch, + `task #${task.id}`, + participantLabel, + task.pending_handoff_count > 0 + ? formatCountLabel(task.pending_handoff_count, 'pending handoff') + : '', + ].filter(Boolean).join('\n'), + }, + ); + }); + advancedItems.push(new SectionItem('Colony tasks', colonyItems, { + description: String(colonyItems.length), + collapsedState: vscode.TreeItemCollapsibleState.Collapsed, + iconId: 'organization', + })); + } if (advancedItems.length > 0) { sectionItems.push(new SectionItem('Advanced details', advancedItems, { description: String(advancedItems.length), @@ -3166,24 +3295,29 @@ class ActiveAgentsProvider { overview: entry.overview, unassignedChanges: entry.unassignedChanges, lockEntries: entry.lockEntries, + colonyTasks: entry.colonyTasks, })); } async loadRepoEntries() { const repoEntries = await findRepoSessionEntries(); - return repoEntries.map((entry) => { - const repoRoot = entry.repoRoot; - const lockRegistry = this.getLockRegistryForRepo(repoRoot); - const currentBranch = readCurrentBranch(repoRoot); - return { - repoRoot, - sessions: entry.sessions.map((session) => decorateSession(session, lockRegistry)), - changes: readRepoChanges(repoRoot).map((change) => ( - decorateChange(change, lockRegistry, currentBranch) - )), - lockEntries: Array.from(lockRegistry.entriesByPath.entries()), - }; - }); + return Promise.all( + repoEntries.map(async (entry) => { + const repoRoot = entry.repoRoot; + const lockRegistry = this.getLockRegistryForRepo(repoRoot); + const currentBranch = readCurrentBranch(repoRoot); + const colonyTasks = await readColonyTasksForRepo(repoRoot); + return { + repoRoot, + sessions: entry.sessions.map((session) => decorateSession(session, lockRegistry)), + changes: readRepoChanges(repoRoot).map((change) => ( + decorateChange(change, lockRegistry, currentBranch) + )), + lockEntries: Array.from(lockRegistry.entriesByPath.entries()), + colonyTasks, + }; + }), + ); } }