@imdeadpool/guardex 7.0.36 → 7.0.38
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 +14 -0
- package/package.json +1 -1
- package/src/cli/main.js +114 -1
- package/src/context.js +4 -0
- package/src/doctor/index.js +66 -1
- package/templates/scripts/agent-branch-finish.sh +69 -2
- package/templates/vscode/guardex-active-agents/extension.js +181 -25
- package/templates/vscode/guardex-active-agents/package.json +3 -3
package/README.md
CHANGED
|
@@ -266,6 +266,20 @@ Being honest about where this still has issues:
|
|
|
266
266
|
<details open>
|
|
267
267
|
<summary><strong>v7.x</strong></summary>
|
|
268
268
|
|
|
269
|
+
### v7.0.38
|
|
270
|
+
- Bumped `@imdeadpool/guardex` from `7.0.37` to `7.0.38` so the current
|
|
271
|
+
`main` payload can publish under a fresh npm version after `7.0.37` reached
|
|
272
|
+
the registry.
|
|
273
|
+
- No new CLI command behavior is introduced in this release lane.
|
|
274
|
+
|
|
275
|
+
### v7.0.37
|
|
276
|
+
- Bumped `@imdeadpool/guardex` from `7.0.36` to `7.0.37` so the current
|
|
277
|
+
package can publish under a fresh npm version after `7.0.36` reached the
|
|
278
|
+
registry.
|
|
279
|
+
- Synced the shipped Active Agents template with the canonical VS Code
|
|
280
|
+
extension source so Colony task counts and details install with the package.
|
|
281
|
+
- No new CLI command behavior is introduced in this release lane.
|
|
282
|
+
|
|
269
283
|
### v7.0.36
|
|
270
284
|
- Bumped `@imdeadpool/guardex` from `7.0.35` to `7.0.36` so the latest
|
|
271
285
|
branch-finish cwd-prune fix can ship under a fresh npm version after PR #424.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imdeadpool/guardex",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.38",
|
|
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
|
@@ -656,7 +656,7 @@ function runSetupInSandbox(options, blocked, repoLabel = '') {
|
|
|
656
656
|
const nestedResult = run(
|
|
657
657
|
process.execPath,
|
|
658
658
|
[__filename, ...buildSandboxSetupArgs(options, sandboxTarget)],
|
|
659
|
-
{ cwd: metadata.worktreePath },
|
|
659
|
+
{ cwd: metadata.worktreePath, env: { GUARDEX_DOCTOR_SANDBOX: '1' } },
|
|
660
660
|
);
|
|
661
661
|
if (isSpawnFailure(nestedResult)) {
|
|
662
662
|
throw nestedResult.error;
|
|
@@ -701,6 +701,12 @@ function runSetupInSandbox(options, blocked, repoLabel = '') {
|
|
|
701
701
|
console.log(`[${TOOL_NAME}] ${autoFinishSummary.details[0]}`);
|
|
702
702
|
}
|
|
703
703
|
|
|
704
|
+
const prunePayload = doctorModule.pruneStaleAgentWorktrees(scanResult.repoRoot, {
|
|
705
|
+
baseBranch: currentBaseBranch,
|
|
706
|
+
dryRun: syncOptions.dryRun,
|
|
707
|
+
});
|
|
708
|
+
printWorktreePruneSummary(prunePayload, { baseBranch: currentBaseBranch });
|
|
709
|
+
|
|
704
710
|
const cleanupResult = cleanupProtectedBaseSandbox(blocked.repoRoot, metadata);
|
|
705
711
|
console.log(
|
|
706
712
|
`[${TOOL_NAME}] Protected-base setup sandbox cleanup: ${cleanupResult.note} ` +
|
|
@@ -1755,6 +1761,26 @@ function runScanInternal(options) {
|
|
|
1755
1761
|
};
|
|
1756
1762
|
}
|
|
1757
1763
|
|
|
1764
|
+
function printWorktreePruneSummary(payload, options = {}) {
|
|
1765
|
+
if (!payload || payload.enabled === false) {
|
|
1766
|
+
if (payload && payload.details && payload.details[0]) {
|
|
1767
|
+
console.log(`[${TOOL_NAME}] ${payload.details[0]}`);
|
|
1768
|
+
}
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
if (!payload.ran) {
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
const baseLabel = options.baseBranch ? ` (base=${options.baseBranch})` : '';
|
|
1775
|
+
const tag = payload.status === 'failed' ? '⚠️' : (payload.status === 'dry-run' ? '🔍' : '🧹');
|
|
1776
|
+
console.log(
|
|
1777
|
+
`[${TOOL_NAME}] ${tag} Stale agent-worktree prune${baseLabel}: status=${payload.status}`,
|
|
1778
|
+
);
|
|
1779
|
+
for (const detail of payload.details || []) {
|
|
1780
|
+
console.log(`[${TOOL_NAME}] ${detail}`);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1758
1784
|
function printScanResult(scan, json = false) {
|
|
1759
1785
|
if (json) {
|
|
1760
1786
|
process.stdout.write(
|
|
@@ -2369,6 +2395,12 @@ function doctor(rawArgs) {
|
|
|
2369
2395
|
configureHooks,
|
|
2370
2396
|
autoFinishReadyAgentBranches: doctorModule.autoFinishReadyAgentBranches,
|
|
2371
2397
|
});
|
|
2398
|
+
const primaryBaseBranch = currentBranchName(blocked.repoRoot);
|
|
2399
|
+
const prunePayload = doctorModule.pruneStaleAgentWorktrees(blocked.repoRoot, {
|
|
2400
|
+
baseBranch: primaryBaseBranch,
|
|
2401
|
+
dryRun: singleRepoOptions.dryRun,
|
|
2402
|
+
});
|
|
2403
|
+
printWorktreePruneSummary(prunePayload, { baseBranch: primaryBaseBranch });
|
|
2372
2404
|
return;
|
|
2373
2405
|
}
|
|
2374
2406
|
|
|
@@ -2390,6 +2422,12 @@ function doctor(rawArgs) {
|
|
|
2390
2422
|
dryRun: singleRepoOptions.dryRun,
|
|
2391
2423
|
waitForMerge: singleRepoOptions.waitForMerge,
|
|
2392
2424
|
});
|
|
2425
|
+
const prunePayload = scanResult.guardexEnabled === false
|
|
2426
|
+
? { enabled: false, ran: false, status: 'skipped', details: ['Guardex disabled for this repo.'] }
|
|
2427
|
+
: doctorModule.pruneStaleAgentWorktrees(scanResult.repoRoot, {
|
|
2428
|
+
baseBranch: currentBaseBranch,
|
|
2429
|
+
dryRun: singleRepoOptions.dryRun,
|
|
2430
|
+
});
|
|
2393
2431
|
const safe = scanResult.guardexEnabled === false || (scanResult.errors === 0 && scanResult.warnings === 0);
|
|
2394
2432
|
const musafe = safe;
|
|
2395
2433
|
|
|
@@ -2414,6 +2452,7 @@ function doctor(rawArgs) {
|
|
|
2414
2452
|
findings: scanResult.findings,
|
|
2415
2453
|
},
|
|
2416
2454
|
autoFinish: autoFinishSummary,
|
|
2455
|
+
worktreePrune: prunePayload,
|
|
2417
2456
|
},
|
|
2418
2457
|
null,
|
|
2419
2458
|
2,
|
|
@@ -2434,6 +2473,7 @@ function doctor(rawArgs) {
|
|
|
2434
2473
|
baseBranch: currentBaseBranch,
|
|
2435
2474
|
verbose: singleRepoOptions.verboseAutoFinish,
|
|
2436
2475
|
});
|
|
2476
|
+
printWorktreePruneSummary(prunePayload, { baseBranch: currentBaseBranch });
|
|
2437
2477
|
if (safe) {
|
|
2438
2478
|
console.log(colorizeDoctorOutput(`[${TOOL_NAME}] ✅ Repo is fully safe.`, 'safe'));
|
|
2439
2479
|
} else {
|
|
@@ -2967,6 +3007,12 @@ function setup(rawArgs) {
|
|
|
2967
3007
|
aggregateErrors += sandboxResult.scanResult.errors;
|
|
2968
3008
|
aggregateWarnings += sandboxResult.scanResult.warnings;
|
|
2969
3009
|
lastScanResult = sandboxResult.scanResult;
|
|
3010
|
+
const primaryBaseBranch = currentBranchName(blocked.repoRoot);
|
|
3011
|
+
const prunePayload = doctorModule.pruneStaleAgentWorktrees(blocked.repoRoot, {
|
|
3012
|
+
baseBranch: primaryBaseBranch,
|
|
3013
|
+
dryRun: perRepoOptions.dryRun,
|
|
3014
|
+
});
|
|
3015
|
+
printWorktreePruneSummary(prunePayload, { baseBranch: primaryBaseBranch });
|
|
2970
3016
|
continue;
|
|
2971
3017
|
}
|
|
2972
3018
|
|
|
@@ -2992,6 +3038,13 @@ function setup(rawArgs) {
|
|
|
2992
3038
|
printAutoFinishSummary(autoFinishSummary, {
|
|
2993
3039
|
baseBranch: currentBaseBranch,
|
|
2994
3040
|
});
|
|
3041
|
+
const prunePayload = scanResult.guardexEnabled === false
|
|
3042
|
+
? { enabled: false, ran: false, status: 'skipped', details: ['Guardex disabled for this repo.'] }
|
|
3043
|
+
: doctorModule.pruneStaleAgentWorktrees(scanResult.repoRoot, {
|
|
3044
|
+
baseBranch: currentBaseBranch,
|
|
3045
|
+
dryRun: perRepoOptions.dryRun,
|
|
3046
|
+
});
|
|
3047
|
+
printWorktreePruneSummary(prunePayload, { baseBranch: currentBaseBranch });
|
|
2995
3048
|
printSetupRepoHints(scanResult.repoRoot, currentBaseBranch, repoLabel);
|
|
2996
3049
|
|
|
2997
3050
|
aggregateErrors += scanResult.errors;
|
|
@@ -3380,6 +3433,64 @@ function branch(rawArgs) {
|
|
|
3380
3433
|
);
|
|
3381
3434
|
}
|
|
3382
3435
|
|
|
3436
|
+
// `gx pivot` — single-tool-call escape from a protected branch into an isolated
|
|
3437
|
+
// agent worktree. AI agents (Claude Code / Codex) cannot set the bypass env
|
|
3438
|
+
// vars from inside a tool call, so they need a whitelisted command that does
|
|
3439
|
+
// the whole hop: branch+worktree creation, dirty-tree migration, and a clean
|
|
3440
|
+
// trailer (`WORKTREE_PATH=...`, `BRANCH=...`, `NEXT_STEP=cd ...`) the agent can
|
|
3441
|
+
// parse to know exactly where to `cd`.
|
|
3442
|
+
//
|
|
3443
|
+
// On an existing agent/* branch, `gx pivot` short-circuits and just prints the
|
|
3444
|
+
// current worktree path — safe to call as a no-op.
|
|
3445
|
+
function pivot(rawArgs) {
|
|
3446
|
+
const { target, passthrough } = extractTargetedArgs(rawArgs);
|
|
3447
|
+
const repoRoot = resolveRepoRoot(target);
|
|
3448
|
+
const headProc = run('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: repoRoot });
|
|
3449
|
+
const currentBranch = String(headProc.stdout || '').trim();
|
|
3450
|
+
if (currentBranch.startsWith('agent/')) {
|
|
3451
|
+
const wtProc = run('git', ['rev-parse', '--show-toplevel'], { cwd: repoRoot });
|
|
3452
|
+
const wtPath = String(wtProc.stdout || '').trim() || repoRoot;
|
|
3453
|
+
process.stdout.write(`[${TOOL_NAME} pivot] Already on agent branch '${currentBranch}'.\n`);
|
|
3454
|
+
process.stdout.write(`WORKTREE_PATH=${wtPath}\n`);
|
|
3455
|
+
process.stdout.write(`BRANCH=${currentBranch}\n`);
|
|
3456
|
+
process.stdout.write(`NEXT_STEP=cd "${wtPath}"\n`);
|
|
3457
|
+
process.exitCode = 0;
|
|
3458
|
+
return;
|
|
3459
|
+
}
|
|
3460
|
+
const result = runPackageAsset('branchStart', passthrough, { cwd: repoRoot });
|
|
3461
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
3462
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
3463
|
+
if (result.status !== 0) {
|
|
3464
|
+
process.exitCode = result.status || 1;
|
|
3465
|
+
return;
|
|
3466
|
+
}
|
|
3467
|
+
const stdoutText = String(result.stdout || '');
|
|
3468
|
+
const wtMatch = stdoutText.match(/^\[agent-branch-start\] Worktree:\s+(.+)$/m);
|
|
3469
|
+
const branchMatch = stdoutText.match(/^\[agent-branch-start\] Created branch:\s+(.+)$/m);
|
|
3470
|
+
if (wtMatch) {
|
|
3471
|
+
const wtPath = wtMatch[1].trim();
|
|
3472
|
+
process.stdout.write('\n');
|
|
3473
|
+
process.stdout.write(`WORKTREE_PATH=${wtPath}\n`);
|
|
3474
|
+
if (branchMatch) process.stdout.write(`BRANCH=${branchMatch[1].trim()}\n`);
|
|
3475
|
+
process.stdout.write(`NEXT_STEP=cd "${wtPath}"\n`);
|
|
3476
|
+
}
|
|
3477
|
+
process.exitCode = 0;
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
// `gx ship` — alias for the canonical "I am done" command. Defaults to
|
|
3481
|
+
// `finish --via-pr --wait-for-merge --cleanup` so AI agents don't strand
|
|
3482
|
+
// commits or worktrees by accident. Any explicit user-supplied flags survive.
|
|
3483
|
+
function ship(rawArgs) {
|
|
3484
|
+
const args = Array.isArray(rawArgs) ? rawArgs.slice() : [];
|
|
3485
|
+
const ensureFlag = (flag) => {
|
|
3486
|
+
if (!args.includes(flag)) args.push(flag);
|
|
3487
|
+
};
|
|
3488
|
+
ensureFlag('--via-pr');
|
|
3489
|
+
ensureFlag('--wait-for-merge');
|
|
3490
|
+
ensureFlag('--cleanup');
|
|
3491
|
+
return finish(args);
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3383
3494
|
function locks(rawArgs) {
|
|
3384
3495
|
const { target, passthrough } = extractTargetedArgs(rawArgs);
|
|
3385
3496
|
const result = runPackageAsset('lockTool', passthrough, { cwd: resolveRepoRoot(target) });
|
|
@@ -3637,6 +3748,8 @@ async function main() {
|
|
|
3637
3748
|
if (command === 'prompt') return prompt(rest);
|
|
3638
3749
|
if (command === 'doctor') return doctor(rest);
|
|
3639
3750
|
if (command === 'branch') return branch(rest);
|
|
3751
|
+
if (command === 'pivot') return pivot(rest);
|
|
3752
|
+
if (command === 'ship') return ship(rest);
|
|
3640
3753
|
if (command === 'locks') return locks(rest);
|
|
3641
3754
|
if (command === 'worktree') return worktree(rest);
|
|
3642
3755
|
if (command === 'hook') return hook(rest);
|
package/src/context.js
CHANGED
|
@@ -338,6 +338,8 @@ const SUGGESTIBLE_COMMANDS = [
|
|
|
338
338
|
'setup',
|
|
339
339
|
'doctor',
|
|
340
340
|
'branch',
|
|
341
|
+
'pivot',
|
|
342
|
+
'ship',
|
|
341
343
|
'locks',
|
|
342
344
|
'worktree',
|
|
343
345
|
'hook',
|
|
@@ -383,7 +385,9 @@ const CLI_COMMAND_GROUPS = [
|
|
|
383
385
|
label: 'Branch workflow',
|
|
384
386
|
description: 'The sandbox → commit → PR → merge loop for agent-owned branches.',
|
|
385
387
|
commands: [
|
|
388
|
+
['pivot', 'Auto-pivot from a protected branch into a fresh agent worktree (single tool call for AI agents)'],
|
|
386
389
|
['branch', 'CLI-owned branch workflow surface (start/finish/merge)'],
|
|
390
|
+
['ship', 'Stage + commit + push + PR + auto-merge + cleanup (alias for `finish --via-pr --wait-for-merge --cleanup`)'],
|
|
387
391
|
['finish', 'Commit + PR + merge completed agent branches (--all, --branch)'],
|
|
388
392
|
['merge', 'Create/reuse an integration lane and merge overlapping agent branches'],
|
|
389
393
|
['sync', 'Sync agent branches with origin/<base>'],
|
package/src/doctor/index.js
CHANGED
|
@@ -1006,6 +1006,70 @@ function autoFinishReadyAgentBranches(repoRoot, options = {}) {
|
|
|
1006
1006
|
return summary;
|
|
1007
1007
|
}
|
|
1008
1008
|
|
|
1009
|
+
function pruneStaleAgentWorktrees(repoRoot, options = {}) {
|
|
1010
|
+
const summary = {
|
|
1011
|
+
enabled: true,
|
|
1012
|
+
ran: false,
|
|
1013
|
+
status: 'skipped',
|
|
1014
|
+
details: [],
|
|
1015
|
+
};
|
|
1016
|
+
|
|
1017
|
+
const dryRun = Boolean(options.dryRun);
|
|
1018
|
+
const baseBranch = String(options.baseBranch || '').trim();
|
|
1019
|
+
|
|
1020
|
+
if (String(process.env.GUARDEX_DOCTOR_SANDBOX || '') === '1') {
|
|
1021
|
+
summary.enabled = false;
|
|
1022
|
+
summary.details.push('Skipped stale-worktree prune inside doctor sandbox pass.');
|
|
1023
|
+
return summary;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
if (String(process.env.GUARDEX_SKIP_AUTO_WORKTREE_PRUNE || '') === '1') {
|
|
1027
|
+
summary.enabled = false;
|
|
1028
|
+
summary.details.push('Skipped stale-worktree prune (GUARDEX_SKIP_AUTO_WORKTREE_PRUNE=1).');
|
|
1029
|
+
return summary;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
const idleMinutesRaw = Number(options.idleMinutes);
|
|
1033
|
+
const idleMinutes = Number.isFinite(idleMinutesRaw) && idleMinutesRaw >= 0
|
|
1034
|
+
? Math.floor(idleMinutesRaw)
|
|
1035
|
+
: 60;
|
|
1036
|
+
|
|
1037
|
+
const args = [
|
|
1038
|
+
'--idle-minutes', String(idleMinutes),
|
|
1039
|
+
'--delete-branches',
|
|
1040
|
+
'--delete-remote-branches',
|
|
1041
|
+
'--include-pr-merged',
|
|
1042
|
+
'--force-dirty',
|
|
1043
|
+
];
|
|
1044
|
+
if (baseBranch && baseBranch !== 'HEAD' && !baseBranch.startsWith('agent/')) {
|
|
1045
|
+
args.push('--base', baseBranch);
|
|
1046
|
+
}
|
|
1047
|
+
if (dryRun) {
|
|
1048
|
+
args.push('--dry-run');
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
const runResult = runPackageAsset('worktreePrune', args, { cwd: repoRoot });
|
|
1052
|
+
summary.ran = true;
|
|
1053
|
+
const stdout = String(runResult.stdout || '').trim();
|
|
1054
|
+
const stderr = String(runResult.stderr || '').trim();
|
|
1055
|
+
if (stdout) {
|
|
1056
|
+
for (const line of stdout.split('\n')) {
|
|
1057
|
+
if (line.trim()) summary.details.push(line);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
if (runResult.status === 0) {
|
|
1061
|
+
summary.status = dryRun ? 'dry-run' : 'pruned';
|
|
1062
|
+
} else {
|
|
1063
|
+
summary.status = 'failed';
|
|
1064
|
+
if (stderr) {
|
|
1065
|
+
summary.details.push(`[error] ${stderr.split('\n').slice(-2).join(' | ')}`);
|
|
1066
|
+
} else {
|
|
1067
|
+
summary.details.push(`[error] worktreePrune exited with status ${runResult.status}`);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return summary;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1009
1073
|
function executeDoctorSandboxLifecycle(options, blocked, metadata, integrations) {
|
|
1010
1074
|
const execution = createDoctorSandboxExecutionState();
|
|
1011
1075
|
const dryRun = Boolean(options.dryRun);
|
|
@@ -1206,7 +1270,7 @@ function runDoctorInSandbox(options, blocked, rawIntegrations = {}) {
|
|
|
1206
1270
|
const nestedResult = run(
|
|
1207
1271
|
process.execPath,
|
|
1208
1272
|
[require.main?.filename || process.argv[1], ...buildSandboxDoctorArgs(options, sandboxTarget)],
|
|
1209
|
-
{ cwd: metadata.worktreePath },
|
|
1273
|
+
{ cwd: metadata.worktreePath, env: { GUARDEX_DOCTOR_SANDBOX: '1' } },
|
|
1210
1274
|
);
|
|
1211
1275
|
if (isSpawnFailure(nestedResult)) {
|
|
1212
1276
|
throw nestedResult.error;
|
|
@@ -1242,5 +1306,6 @@ module.exports = {
|
|
|
1242
1306
|
emitDoctorSandboxJsonOutput,
|
|
1243
1307
|
emitDoctorSandboxConsoleOutput,
|
|
1244
1308
|
autoFinishReadyAgentBranches,
|
|
1309
|
+
pruneStaleAgentWorktrees,
|
|
1245
1310
|
runDoctorInSandbox,
|
|
1246
1311
|
};
|
|
@@ -165,7 +165,11 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
|
165
165
|
fi
|
|
166
166
|
|
|
167
167
|
repo_root="$(git rev-parse --show-toplevel)"
|
|
168
|
-
|
|
168
|
+
# The physical cwd may be a subdirectory inside the source worktree. Cleanup
|
|
169
|
+
# decisions need the enclosing worktree root, otherwise finishing from `src/`
|
|
170
|
+
# can delete the caller's cwd and turn a successful merge into a false shell
|
|
171
|
+
# failure.
|
|
172
|
+
current_worktree="$repo_root"
|
|
169
173
|
common_git_dir_raw="$(git -C "$repo_root" rev-parse --git-common-dir)"
|
|
170
174
|
if [[ "$common_git_dir_raw" == /* ]]; then
|
|
171
175
|
common_git_dir="$common_git_dir_raw"
|
|
@@ -223,7 +227,68 @@ if [[ "$SOURCE_BRANCH" == "$BASE_BRANCH" ]]; then
|
|
|
223
227
|
exit 1
|
|
224
228
|
fi
|
|
225
229
|
|
|
230
|
+
cleanup_missing_merged_source_branch() {
|
|
231
|
+
local state_line=""
|
|
232
|
+
local parsed_state=""
|
|
233
|
+
local parsed_merged_at=""
|
|
234
|
+
local parsed_url=""
|
|
235
|
+
local remote_delete_output=""
|
|
236
|
+
local prune_args=()
|
|
237
|
+
|
|
238
|
+
if [[ "$MERGE_MODE" != "pr" || "$CLEANUP_AFTER_MERGE" -ne 1 ]]; then
|
|
239
|
+
return 1
|
|
240
|
+
fi
|
|
241
|
+
if ! command -v "$GH_BIN" >/dev/null 2>&1; then
|
|
242
|
+
return 1
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
state_line="$("$GH_BIN" pr list \
|
|
246
|
+
--state merged \
|
|
247
|
+
--head "$SOURCE_BRANCH" \
|
|
248
|
+
--base "$BASE_BRANCH" \
|
|
249
|
+
--json state,mergedAt,url \
|
|
250
|
+
--jq 'sort_by(.mergedAt // "") | reverse | (.[0] // {}) | [(.state // ""), (.mergedAt // ""), (.url // "")] | join("\u001f")' \
|
|
251
|
+
2>/dev/null || true)"
|
|
252
|
+
if [[ -z "$state_line" ]]; then
|
|
253
|
+
return 1
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
IFS=$'\x1f' read -r parsed_state parsed_merged_at parsed_url <<< "$state_line"
|
|
257
|
+
if [[ "$parsed_state" != "MERGED" && -z "$parsed_merged_at" ]]; then
|
|
258
|
+
return 1
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
echo "[agent-branch-finish] Local source branch '${SOURCE_BRANCH}' is already absent, but a merged PR exists; continuing cleanup." >&2
|
|
262
|
+
if [[ -n "$parsed_url" ]]; then
|
|
263
|
+
echo "[agent-branch-finish] Merged PR: ${parsed_url}" >&2
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
run_guardex_cli locks release --branch "$SOURCE_BRANCH" >/dev/null 2>&1 || true
|
|
267
|
+
|
|
268
|
+
if [[ "$PUSH_ENABLED" -eq 1 && "$DELETE_REMOTE_BRANCH" -eq 1 ]]; then
|
|
269
|
+
if git -C "$repo_root" ls-remote --exit-code --heads origin "$SOURCE_BRANCH" >/dev/null 2>&1; then
|
|
270
|
+
if ! remote_delete_output="$(git -C "$repo_root" push origin --delete "$SOURCE_BRANCH" 2>&1)"; then
|
|
271
|
+
echo "[agent-branch-finish] Warning: remote branch cleanup failed for '${SOURCE_BRANCH}'." >&2
|
|
272
|
+
[[ -n "$remote_delete_output" ]] && echo "$remote_delete_output" >&2
|
|
273
|
+
fi
|
|
274
|
+
fi
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
prune_args=(worktree prune --base "$BASE_BRANCH" --only-dirty-worktrees --delete-branches)
|
|
278
|
+
if [[ "$DELETE_REMOTE_BRANCH" -eq 1 ]]; then
|
|
279
|
+
prune_args+=(--delete-remote-branches)
|
|
280
|
+
fi
|
|
281
|
+
if ! run_guardex_cli "${prune_args[@]}"; then
|
|
282
|
+
echo "[agent-branch-finish] Warning: automatic worktree prune failed." >&2
|
|
283
|
+
echo "[agent-branch-finish] You can run manual cleanup: gx cleanup --base ${BASE_BRANCH}" >&2
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via pr flow and found source branch/worktree already cleaned."
|
|
287
|
+
exit 0
|
|
288
|
+
}
|
|
289
|
+
|
|
226
290
|
if ! git -C "$repo_root" show-ref --verify --quiet "refs/heads/${SOURCE_BRANCH}"; then
|
|
291
|
+
cleanup_missing_merged_source_branch
|
|
227
292
|
echo "[agent-branch-finish] Local source branch does not exist: ${SOURCE_BRANCH}" >&2
|
|
228
293
|
exit 1
|
|
229
294
|
fi
|
|
@@ -846,10 +911,12 @@ if [[ "$CLEANUP_AFTER_MERGE" -eq 1 ]]; then
|
|
|
846
911
|
echo "[agent-branch-finish] You can run manual cleanup: gx cleanup --base ${BASE_BRANCH}" >&2
|
|
847
912
|
fi
|
|
848
913
|
|
|
849
|
-
echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and cleaned source branch/worktree."
|
|
850
914
|
if [[ "$source_worktree" == "$current_worktree" && "$source_worktree" == "${agent_worktree_root}"/* && -d "$source_worktree" ]]; then
|
|
915
|
+
echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and cleaned source branch/remote."
|
|
851
916
|
echo "[agent-branch-finish] Current worktree '${source_worktree}' still exists because it is the active shell cwd." >&2
|
|
852
917
|
echo "[agent-branch-finish] Leave this directory, then run: gx cleanup --base ${BASE_BRANCH}" >&2
|
|
918
|
+
else
|
|
919
|
+
echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and cleaned source branch/worktree."
|
|
853
920
|
fi
|
|
854
921
|
else
|
|
855
922
|
pivot_to_repo_root_before_prune
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const fs = require('node:fs');
|
|
2
2
|
const path = require('node:path');
|
|
3
3
|
const cp = require('node:child_process');
|
|
4
|
+
const http = require('node:http');
|
|
5
|
+
const os = require('node:os');
|
|
4
6
|
const vscode = require('vscode');
|
|
5
7
|
const {
|
|
6
8
|
clearWorktreeActivityCache,
|
|
@@ -40,6 +42,84 @@ const ACTIVE_AGENTS_EXTENSION_ID = 'Recodee.gitguardex-active-agents';
|
|
|
40
42
|
const RESTART_EXTENSION_HOST_COMMAND = 'workbench.action.restartExtensionHost';
|
|
41
43
|
const REFRESH_POLL_INTERVAL_MS = 30_000;
|
|
42
44
|
const INSPECT_PANEL_VIEW_TYPE = 'gitguardex.activeAgents.inspect';
|
|
45
|
+
const COLONY_DEFAULT_PORT = 37777;
|
|
46
|
+
const COLONY_SNAPSHOT_TTL_MS = 5_000;
|
|
47
|
+
const COLONY_FETCH_TIMEOUT_MS = 800;
|
|
48
|
+
|
|
49
|
+
function colonyDataDir() {
|
|
50
|
+
return process.env.COLONY_HOME
|
|
51
|
+
|| process.env.CAVEMEM_HOME
|
|
52
|
+
|| path.join(os.homedir(), '.colony');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readColonyPort() {
|
|
56
|
+
try {
|
|
57
|
+
const raw = fs.readFileSync(path.join(colonyDataDir(), 'settings.json'), 'utf8');
|
|
58
|
+
const parsed = JSON.parse(raw);
|
|
59
|
+
const port = Number(parsed?.workerPort);
|
|
60
|
+
return Number.isFinite(port) && port > 0 ? port : COLONY_DEFAULT_PORT;
|
|
61
|
+
} catch (_error) {
|
|
62
|
+
return COLONY_DEFAULT_PORT;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function fetchColonyJson(urlPath) {
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
const req = http.get(
|
|
69
|
+
{
|
|
70
|
+
hostname: '127.0.0.1',
|
|
71
|
+
port: readColonyPort(),
|
|
72
|
+
path: urlPath,
|
|
73
|
+
timeout: COLONY_FETCH_TIMEOUT_MS,
|
|
74
|
+
},
|
|
75
|
+
(res) => {
|
|
76
|
+
if (res.statusCode !== 200) {
|
|
77
|
+
res.resume();
|
|
78
|
+
resolve(null);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let body = '';
|
|
82
|
+
res.setEncoding('utf8');
|
|
83
|
+
res.on('data', (chunk) => {
|
|
84
|
+
body += chunk;
|
|
85
|
+
});
|
|
86
|
+
res.on('end', () => {
|
|
87
|
+
try {
|
|
88
|
+
resolve(JSON.parse(body));
|
|
89
|
+
} catch (_error) {
|
|
90
|
+
resolve(null);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
req.on('error', () => resolve(null));
|
|
96
|
+
req.on('timeout', () => {
|
|
97
|
+
req.destroy();
|
|
98
|
+
resolve(null);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const colonyTasksCache = new Map();
|
|
104
|
+
|
|
105
|
+
async function readColonyTasksForRepo(repoRoot) {
|
|
106
|
+
const cached = colonyTasksCache.get(repoRoot);
|
|
107
|
+
if (cached && Date.now() - cached.at < COLONY_SNAPSHOT_TTL_MS) {
|
|
108
|
+
return cached.tasks;
|
|
109
|
+
}
|
|
110
|
+
const tasks = await fetchColonyJson(
|
|
111
|
+
`/api/colony/tasks?repo_root=${encodeURIComponent(repoRoot)}`,
|
|
112
|
+
);
|
|
113
|
+
const resolved = Array.isArray(tasks) ? tasks : [];
|
|
114
|
+
colonyTasksCache.set(repoRoot, { at: Date.now(), tasks: resolved });
|
|
115
|
+
return resolved;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function compactColonyBranchLabel(branch) {
|
|
119
|
+
if (typeof branch !== 'string' || !branch) return 'unknown';
|
|
120
|
+
const parts = branch.split('/').filter(Boolean);
|
|
121
|
+
return parts.length > 2 ? parts.slice(-2).join('/') : branch;
|
|
122
|
+
}
|
|
43
123
|
const GIT_CONFIGURATION_SECTION = 'git';
|
|
44
124
|
const REPO_SCAN_IGNORED_FOLDERS_SETTING = 'repositoryScanIgnoredFolders';
|
|
45
125
|
const BUNDLED_FILE_ICONS_MANIFEST_RELATIVE = path.join('fileicons', 'gitguardex-fileicons.json');
|
|
@@ -56,7 +136,7 @@ const MANAGED_REPO_SCAN_IGNORED_FOLDERS = [
|
|
|
56
136
|
const SESSION_ACTIVITY_GROUPS = [
|
|
57
137
|
{ kind: 'blocked', label: 'BLOCKED' },
|
|
58
138
|
{ kind: 'working', label: 'WORKING NOW' },
|
|
59
|
-
{ kind: 'finished', label: '
|
|
139
|
+
{ kind: 'finished', label: 'NEEDS CLEANUP' },
|
|
60
140
|
{ kind: 'idle', label: 'THINKING' },
|
|
61
141
|
{ kind: 'stalled', label: 'STALLED' },
|
|
62
142
|
{ kind: 'dead', label: 'DEAD' },
|
|
@@ -491,7 +571,7 @@ function buildActiveAgentsStatusSummary(summary) {
|
|
|
491
571
|
if (workingCount > 0 || finishedCount > 0 || idleCount > 0) {
|
|
492
572
|
const parts = [`${workingCount} working`];
|
|
493
573
|
if (finishedCount > 0) {
|
|
494
|
-
parts.push(`${finishedCount}
|
|
574
|
+
parts.push(`${finishedCount} needs cleanup`);
|
|
495
575
|
}
|
|
496
576
|
parts.push(`${idleCount} idle`);
|
|
497
577
|
return `$(git-branch) ${parts.join(' · ')}`;
|
|
@@ -514,7 +594,7 @@ function buildActiveAgentsStatusTooltip(selectedSession, summary) {
|
|
|
514
594
|
return [
|
|
515
595
|
formatCountLabel(activeCount, 'active agent'),
|
|
516
596
|
formatCountLabel(summary?.workingCount || 0, 'working now session', 'working now sessions'),
|
|
517
|
-
formatCountLabel(summary?.finishedCount || 0, '
|
|
597
|
+
formatCountLabel(summary?.finishedCount || 0, 'needs cleanup session'),
|
|
518
598
|
formatCountLabel(summary?.idleCount || 0, 'idle session'),
|
|
519
599
|
formatCountLabel(summary?.unassignedChangeCount || 0, 'unassigned change'),
|
|
520
600
|
formatCountLabel(summary?.lockedFileCount || 0, 'locked file'),
|
|
@@ -601,7 +681,7 @@ function sessionFreshnessLabel(session, now = Date.now()) {
|
|
|
601
681
|
return 'Needs attention';
|
|
602
682
|
}
|
|
603
683
|
if (session.activityKind === 'finished') {
|
|
604
|
-
return '
|
|
684
|
+
return 'Needs cleanup';
|
|
605
685
|
}
|
|
606
686
|
if (session.activityKind === 'stalled') {
|
|
607
687
|
return 'Possibly stale';
|
|
@@ -631,7 +711,7 @@ function sessionStatusLabel(session) {
|
|
|
631
711
|
case 'working':
|
|
632
712
|
return 'Working';
|
|
633
713
|
case 'finished':
|
|
634
|
-
return '
|
|
714
|
+
return 'Needs cleanup';
|
|
635
715
|
case 'idle':
|
|
636
716
|
return 'Idle';
|
|
637
717
|
case 'stalled':
|
|
@@ -838,12 +918,20 @@ function buildWorktreeBranchDescription(sessions) {
|
|
|
838
918
|
function buildOverviewDescription(summary) {
|
|
839
919
|
return [
|
|
840
920
|
formatCountLabel(summary?.workingCount || 0, 'working agent'),
|
|
841
|
-
formatCountLabel(summary?.finishedCount || 0, '
|
|
921
|
+
formatCountLabel(summary?.finishedCount || 0, 'needs cleanup agent'),
|
|
842
922
|
formatCountLabel(summary?.idleCount || 0, 'idle agent'),
|
|
923
|
+
summary?.colonyTaskCount
|
|
924
|
+
? formatCountLabel(summary.colonyTaskCount, 'colony task')
|
|
925
|
+
: '',
|
|
926
|
+
summary?.pendingHandoffCount
|
|
927
|
+
? formatCountLabel(summary.pendingHandoffCount, 'pending handoff')
|
|
928
|
+
: '',
|
|
843
929
|
formatCountLabel(summary?.unassignedChangeCount || 0, 'unassigned change'),
|
|
844
930
|
formatCountLabel(summary?.lockedFileCount || 0, 'locked file'),
|
|
845
931
|
formatCountLabel(summary?.conflictCount || 0, 'conflict'),
|
|
846
|
-
]
|
|
932
|
+
]
|
|
933
|
+
.filter(Boolean)
|
|
934
|
+
.join(' · ');
|
|
847
935
|
}
|
|
848
936
|
|
|
849
937
|
function buildRepoDescription(summary) {
|
|
@@ -1252,7 +1340,9 @@ class RepoItem extends vscode.TreeItem {
|
|
|
1252
1340
|
this.changes = changes;
|
|
1253
1341
|
this.unassignedChanges = options.unassignedChanges || [];
|
|
1254
1342
|
this.lockEntries = options.lockEntries || [];
|
|
1255
|
-
this.
|
|
1343
|
+
this.colonyTasks = Array.isArray(options.colonyTasks) ? options.colonyTasks : [];
|
|
1344
|
+
this.overview = options.overview
|
|
1345
|
+
|| buildRepoOverview(sessions, this.unassignedChanges, this.lockEntries, this.colonyTasks);
|
|
1256
1346
|
this.description = buildRepoDescription(this.overview);
|
|
1257
1347
|
this.tooltip = buildRepoTooltip(repoRoot, this.overview);
|
|
1258
1348
|
this.iconPath = themeIcon('repo');
|
|
@@ -2524,7 +2614,8 @@ function countChangedPaths(repoRoot, sessions, changes) {
|
|
|
2524
2614
|
return changedKeys.size;
|
|
2525
2615
|
}
|
|
2526
2616
|
|
|
2527
|
-
function buildRepoOverview(sessions, unassignedChanges, lockEntries) {
|
|
2617
|
+
function buildRepoOverview(sessions, unassignedChanges, lockEntries, colonyTasks = []) {
|
|
2618
|
+
const colonyTaskList = Array.isArray(colonyTasks) ? colonyTasks : [];
|
|
2528
2619
|
return {
|
|
2529
2620
|
sessionCount: sessions.length,
|
|
2530
2621
|
workingCount: countWorkingSessions(sessions),
|
|
@@ -2536,6 +2627,11 @@ function buildRepoOverview(sessions, unassignedChanges, lockEntries) {
|
|
|
2536
2627
|
(total, session) => total + (session.conflictCount || 0),
|
|
2537
2628
|
0,
|
|
2538
2629
|
) + (unassignedChanges || []).filter((change) => change.hasForeignLock).length,
|
|
2630
|
+
colonyTaskCount: colonyTaskList.length,
|
|
2631
|
+
pendingHandoffCount: colonyTaskList.reduce(
|
|
2632
|
+
(total, task) => total + (task.pending_handoff_count || 0),
|
|
2633
|
+
0,
|
|
2634
|
+
),
|
|
2539
2635
|
};
|
|
2540
2636
|
}
|
|
2541
2637
|
|
|
@@ -2829,7 +2925,9 @@ function buildWorkingNowNodes(sessions) {
|
|
|
2829
2925
|
function buildIdleThinkingNodes(sessions) {
|
|
2830
2926
|
const sessionEntries = sortSessionsForIdleThinking(
|
|
2831
2927
|
sessions.filter((session) => !(
|
|
2832
|
-
session.activityKind === 'working'
|
|
2928
|
+
session.activityKind === 'working'
|
|
2929
|
+
|| session.activityKind === 'blocked'
|
|
2930
|
+
|| session.activityKind === 'finished'
|
|
2833
2931
|
)),
|
|
2834
2932
|
).map((session) => ({
|
|
2835
2933
|
projectRelativePath: resolveSessionProjectRelativePath(session),
|
|
@@ -2839,6 +2937,17 @@ function buildIdleThinkingNodes(sessions) {
|
|
|
2839
2937
|
return buildProjectScopedItems(sessionEntries, { rootLabel: 'Repo root' });
|
|
2840
2938
|
}
|
|
2841
2939
|
|
|
2940
|
+
function buildNeedsCleanupNodes(sessions) {
|
|
2941
|
+
const sessionEntries = sessions
|
|
2942
|
+
.filter((session) => session.activityKind === 'finished')
|
|
2943
|
+
.map((session) => ({
|
|
2944
|
+
projectRelativePath: resolveSessionProjectRelativePath(session),
|
|
2945
|
+
sessions: [session],
|
|
2946
|
+
item: new SessionItem(session, buildSessionDetailItems(session)),
|
|
2947
|
+
}));
|
|
2948
|
+
return buildProjectScopedItems(sessionEntries, { rootLabel: 'Repo root' });
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2842
2951
|
function buildUnassignedChangeNodes(changes) {
|
|
2843
2952
|
return sortUnassignedChanges(changes).map((change) => new ChangeItem(change, {
|
|
2844
2953
|
label: compactRelativePath(change.relativePath),
|
|
@@ -3023,12 +3132,14 @@ class ActiveAgentsProvider {
|
|
|
3023
3132
|
|
|
3024
3133
|
const { repoRootChanges } = partitionChangesByOwnership(sessions, changes);
|
|
3025
3134
|
const unassignedChanges = sortUnassignedChanges(repoRootChanges);
|
|
3135
|
+
const colonyTasks = Array.isArray(entry.colonyTasks) ? entry.colonyTasks : [];
|
|
3026
3136
|
return {
|
|
3027
3137
|
...entry,
|
|
3028
3138
|
sessions,
|
|
3029
3139
|
changes,
|
|
3030
3140
|
unassignedChanges,
|
|
3031
|
-
|
|
3141
|
+
colonyTasks,
|
|
3142
|
+
overview: buildRepoOverview(sessions, unassignedChanges, entry.lockEntries, colonyTasks),
|
|
3032
3143
|
};
|
|
3033
3144
|
});
|
|
3034
3145
|
|
|
@@ -3107,6 +3218,15 @@ class ActiveAgentsProvider {
|
|
|
3107
3218
|
}));
|
|
3108
3219
|
}
|
|
3109
3220
|
|
|
3221
|
+
const needsCleanupItems = buildNeedsCleanupNodes(element.sessions);
|
|
3222
|
+
if (needsCleanupItems.length > 0) {
|
|
3223
|
+
sectionItems.push(new SectionItem('Needs cleanup', needsCleanupItems, {
|
|
3224
|
+
description: String(needsCleanupItems.length),
|
|
3225
|
+
collapsedState: vscode.TreeItemCollapsibleState.Collapsed,
|
|
3226
|
+
iconId: 'pass-filled',
|
|
3227
|
+
}));
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3110
3230
|
const idleThinkingItems = buildIdleThinkingNodes(element.sessions);
|
|
3111
3231
|
if (idleThinkingItems.length > 0) {
|
|
3112
3232
|
sectionItems.push(new SectionItem('Idle / thinking', idleThinkingItems, {
|
|
@@ -3140,6 +3260,37 @@ class ActiveAgentsProvider {
|
|
|
3140
3260
|
iconId: 'file-directory',
|
|
3141
3261
|
}));
|
|
3142
3262
|
}
|
|
3263
|
+
const colonyTaskList = Array.isArray(element.colonyTasks) ? element.colonyTasks : [];
|
|
3264
|
+
if (colonyTaskList.length > 0) {
|
|
3265
|
+
const colonyItems = colonyTaskList.map((task) => {
|
|
3266
|
+
const pendingLabel = task.pending_handoff_count > 0
|
|
3267
|
+
? formatCountLabel(task.pending_handoff_count, 'pending handoff')
|
|
3268
|
+
: 'quiet';
|
|
3269
|
+
const participantLabel =
|
|
3270
|
+
(task.participants || []).map((p) => p.agent).filter(Boolean).join(', ')
|
|
3271
|
+
|| 'no participants';
|
|
3272
|
+
return new DetailItem(
|
|
3273
|
+
`#${task.id} · ${compactColonyBranchLabel(task.branch)}`,
|
|
3274
|
+
`${participantLabel} · ${pendingLabel}`,
|
|
3275
|
+
{
|
|
3276
|
+
iconId: task.pending_handoff_count > 0 ? 'warning' : 'comment-discussion',
|
|
3277
|
+
tooltip: [
|
|
3278
|
+
task.branch,
|
|
3279
|
+
`task #${task.id}`,
|
|
3280
|
+
participantLabel,
|
|
3281
|
+
task.pending_handoff_count > 0
|
|
3282
|
+
? formatCountLabel(task.pending_handoff_count, 'pending handoff')
|
|
3283
|
+
: '',
|
|
3284
|
+
].filter(Boolean).join('\n'),
|
|
3285
|
+
},
|
|
3286
|
+
);
|
|
3287
|
+
});
|
|
3288
|
+
advancedItems.push(new SectionItem('Colony tasks', colonyItems, {
|
|
3289
|
+
description: String(colonyItems.length),
|
|
3290
|
+
collapsedState: vscode.TreeItemCollapsibleState.Collapsed,
|
|
3291
|
+
iconId: 'organization',
|
|
3292
|
+
}));
|
|
3293
|
+
}
|
|
3143
3294
|
if (advancedItems.length > 0) {
|
|
3144
3295
|
sectionItems.push(new SectionItem('Advanced details', advancedItems, {
|
|
3145
3296
|
description: String(advancedItems.length),
|
|
@@ -3166,24 +3317,29 @@ class ActiveAgentsProvider {
|
|
|
3166
3317
|
overview: entry.overview,
|
|
3167
3318
|
unassignedChanges: entry.unassignedChanges,
|
|
3168
3319
|
lockEntries: entry.lockEntries,
|
|
3320
|
+
colonyTasks: entry.colonyTasks,
|
|
3169
3321
|
}));
|
|
3170
3322
|
}
|
|
3171
3323
|
|
|
3172
3324
|
async loadRepoEntries() {
|
|
3173
3325
|
const repoEntries = await findRepoSessionEntries();
|
|
3174
|
-
return
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
repoRoot
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3326
|
+
return Promise.all(
|
|
3327
|
+
repoEntries.map(async (entry) => {
|
|
3328
|
+
const repoRoot = entry.repoRoot;
|
|
3329
|
+
const lockRegistry = this.getLockRegistryForRepo(repoRoot);
|
|
3330
|
+
const currentBranch = readCurrentBranch(repoRoot);
|
|
3331
|
+
const colonyTasks = await readColonyTasksForRepo(repoRoot);
|
|
3332
|
+
return {
|
|
3333
|
+
repoRoot,
|
|
3334
|
+
sessions: entry.sessions.map((session) => decorateSession(session, lockRegistry)),
|
|
3335
|
+
changes: readRepoChanges(repoRoot).map((change) => (
|
|
3336
|
+
decorateChange(change, lockRegistry, currentBranch)
|
|
3337
|
+
)),
|
|
3338
|
+
lockEntries: Array.from(lockRegistry.entriesByPath.entries()),
|
|
3339
|
+
colonyTasks,
|
|
3340
|
+
};
|
|
3341
|
+
}),
|
|
3342
|
+
);
|
|
3187
3343
|
}
|
|
3188
3344
|
}
|
|
3189
3345
|
|
|
@@ -3453,7 +3609,7 @@ function activate(context) {
|
|
|
3453
3609
|
vscode.commands.registerCommand('gitguardex.activeAgents.refresh', refresh),
|
|
3454
3610
|
vscode.commands.registerCommand('gitguardex.activeAgents.restart', restartActiveAgents),
|
|
3455
3611
|
vscode.commands.registerCommand('gitguardex.activeAgents.focus', async () => {
|
|
3456
|
-
await vscode.commands.executeCommand('workbench.view.extension.gitguardex
|
|
3612
|
+
await vscode.commands.executeCommand('workbench.view.extension.gitguardex-active-agents-container');
|
|
3457
3613
|
}),
|
|
3458
3614
|
vscode.commands.registerCommand('gitguardex.activeAgents.commitSelectedSession', commitSelectedSession),
|
|
3459
3615
|
vscode.commands.registerCommand('gitguardex.activeAgents.openWorktree', async (session) => {
|
|
@@ -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.
|
|
6
|
+
"version": "0.0.20",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"icon": "icon.png",
|
|
9
9
|
"engines": {
|
|
@@ -79,14 +79,14 @@
|
|
|
79
79
|
"viewsContainers": {
|
|
80
80
|
"activitybar": [
|
|
81
81
|
{
|
|
82
|
-
"id": "gitguardex
|
|
82
|
+
"id": "gitguardex-active-agents-container",
|
|
83
83
|
"title": "Active Agents",
|
|
84
84
|
"icon": "media/active-agents-hivemind.svg"
|
|
85
85
|
}
|
|
86
86
|
]
|
|
87
87
|
},
|
|
88
88
|
"views": {
|
|
89
|
-
"gitguardex
|
|
89
|
+
"gitguardex-active-agents-container": [
|
|
90
90
|
{
|
|
91
91
|
"id": "gitguardex.activeAgents",
|
|
92
92
|
"name": "Active Agents",
|