@imdeadpool/guardex 7.0.12 → 7.0.13
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 +24 -2
- package/bin/multiagent-safety.js +85 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
# GitGuardex — Guardian T-Rex for your repo
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@imdeadpool/guardex)
|
|
4
|
-
[](https://www.npmjs.com/package/@imdeadpool/guardex)
|
|
4
|
+
[](https://www.npmjs.com/package/@imdeadpool/guardex)
|
|
5
|
+
[](https://github.com/recodeee/gitguardex/stargazers)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
8
|
+
[](https://github.com/recodeee/gitguardex/actions/workflows/ci.yml)
|
|
9
|
+
[](https://github.com/recodeee/gitguardex/actions/workflows/release.yml)
|
|
10
|
+
[](https://github.com/recodeee/gitguardex/actions/workflows/codeql.yml)
|
|
5
11
|
[](https://securityscorecards.dev/viewer/?uri=github.com/recodeee/gitguardex)
|
|
6
12
|
|
|
7
13
|
**GitGuardex is a safety layer for parallel agent work in git repos.** If you're running more than one Codex or Claude agent on the same codebase, this is what keeps them from deleting each other's work.
|
|
@@ -20,6 +26,8 @@ I was running ~30 Codex agents in parallel and hit a wall: they kept working on
|
|
|
20
26
|
|
|
21
27
|
GitGuardex exists to stop that loop. Every agent gets its own worktree, claims the files it's touching, and can't clobber files another agent has claimed. Your local branch stays clean; agents stay in their lanes.
|
|
22
28
|
|
|
29
|
+
### Solution
|
|
30
|
+
|
|
23
31
|
```mermaid
|
|
24
32
|
flowchart LR
|
|
25
33
|
A[Agent A adds assertions in a shared test] --> S[Several agents touch the same files]
|
|
@@ -34,6 +42,8 @@ flowchart LR
|
|
|
34
42
|
I --> S
|
|
35
43
|
```
|
|
36
44
|
|
|
45
|
+
### Dashboard
|
|
46
|
+
|
|
37
47
|

|
|
38
48
|
|
|
39
49
|
Coming soon: [recodee.com](https://recodee.com) — live account health, usage, routing, and capacity in one place.
|
|
@@ -277,6 +287,7 @@ npm i -g oh-my-codex
|
|
|
277
287
|
```
|
|
278
288
|
|
|
279
289
|
Repo: <https://github.com/Yeachan-Heo/oh-my-codex>
|
|
290
|
+
[](https://github.com/Yeachan-Heo/oh-my-codex)
|
|
280
291
|
|
|
281
292
|
### oh-my-claudecode — Claude Code equivalent
|
|
282
293
|
|
|
@@ -287,6 +298,7 @@ npm i -g oh-my-claude-sisyphus@latest
|
|
|
287
298
|
```
|
|
288
299
|
|
|
289
300
|
Repo: <https://github.com/Yeachan-Heo/oh-my-claudecode>
|
|
301
|
+
[](https://github.com/Yeachan-Heo/oh-my-claudecode)
|
|
290
302
|
|
|
291
303
|
### Caveman — output compression for long agent runs
|
|
292
304
|
|
|
@@ -297,6 +309,7 @@ npx skills add JuliusBrussee/caveman
|
|
|
297
309
|
```
|
|
298
310
|
|
|
299
311
|
Repo: <https://github.com/JuliusBrussee/caveman>
|
|
312
|
+
[](https://github.com/JuliusBrussee/caveman)
|
|
300
313
|
|
|
301
314
|
### Cavemem — local persistent memory for agents
|
|
302
315
|
|
|
@@ -309,6 +322,7 @@ cavemem status
|
|
|
309
322
|
```
|
|
310
323
|
|
|
311
324
|
Repo: <https://github.com/JuliusBrussee/cavemem>
|
|
325
|
+
[](https://github.com/JuliusBrussee/cavemem)
|
|
312
326
|
|
|
313
327
|
### Cavekit — spec-driven build loop
|
|
314
328
|
|
|
@@ -319,6 +333,7 @@ npx skills add JuliusBrussee/cavekit
|
|
|
319
333
|
```
|
|
320
334
|
|
|
321
335
|
Repo: <https://github.com/JuliusBrussee/cavekit>
|
|
336
|
+
[](https://github.com/JuliusBrussee/cavekit)
|
|
322
337
|
|
|
323
338
|
### OpenSpec — spec-driven workflows
|
|
324
339
|
|
|
@@ -329,6 +344,7 @@ npm i -g @fission-ai/openspec
|
|
|
329
344
|
```
|
|
330
345
|
|
|
331
346
|
Repo: <https://github.com/Fission-AI/OpenSpec>
|
|
347
|
+
[](https://github.com/Fission-AI/OpenSpec)
|
|
332
348
|
|
|
333
349
|
### codex-auth — multi-account switcher
|
|
334
350
|
|
|
@@ -344,6 +360,7 @@ codex-auth current
|
|
|
344
360
|
```
|
|
345
361
|
|
|
346
362
|
Repo: [recodeecom/codex-account-switcher-cli](https://github.com/recodeecom/codex-account-switcher-cli)
|
|
363
|
+
[](https://github.com/recodeecom/codex-account-switcher-cli)
|
|
347
364
|
|
|
348
365
|
### GitHub CLI (`gh`)
|
|
349
366
|
|
|
@@ -490,6 +507,11 @@ npm pack --dry-run
|
|
|
490
507
|
<details>
|
|
491
508
|
<summary><strong>v7.x</strong></summary>
|
|
492
509
|
|
|
510
|
+
### v7.0.13
|
|
511
|
+
- `gx status` and `gx setup` now present the Claude companion as `oh-my-claudecode` while still installing the published npm package `oh-my-claude-sisyphus`.
|
|
512
|
+
- When that dependency is inactive or the user declines the optional install, Guardex now prints the upstream repo URL so the missing dependency is explicit instead of hidden behind the npm package name.
|
|
513
|
+
- Bumped `@imdeadpool/guardex` from `7.0.12` → `7.0.13` after npm rejected a republish over the already-published `7.0.12`.
|
|
514
|
+
|
|
493
515
|
### v7.0.12
|
|
494
516
|
- Fixed the self-update handoff after `gx` installs a newer global package. When the on-disk install advances, GitGuardex now restarts into the installed CLI instead of continuing in the old process and printing the stale in-memory version.
|
|
495
517
|
- This removes the confusing `Updated to latest published version` followed by `CLI: ...7.0.10` mismatch that happened when `7.0.11` finished installing during the same `gx` invocation.
|
package/bin/multiagent-safety.js
CHANGED
|
@@ -13,15 +13,26 @@ const SHORT_TOOL_NAME = 'gx';
|
|
|
13
13
|
const LEGACY_NAMES = ['guardex', 'multiagent-safety'];
|
|
14
14
|
const OPENSPEC_PACKAGE = '@fission-ai/openspec';
|
|
15
15
|
const OMC_PACKAGE = 'oh-my-claude-sisyphus';
|
|
16
|
+
const OMC_REPO_URL = 'https://github.com/Yeachan-Heo/oh-my-claudecode';
|
|
16
17
|
const CAVEMEM_PACKAGE = 'cavemem';
|
|
17
18
|
const NPX_BIN = process.env.GUARDEX_NPX_BIN || 'npx';
|
|
18
19
|
const GUARDEX_HOME_DIR = path.resolve(process.env.GUARDEX_HOME_DIR || os.homedir());
|
|
20
|
+
const GLOBAL_TOOLCHAIN_SERVICES = [
|
|
21
|
+
{ name: 'oh-my-codex', packageName: 'oh-my-codex' },
|
|
22
|
+
{
|
|
23
|
+
name: 'oh-my-claudecode',
|
|
24
|
+
packageName: OMC_PACKAGE,
|
|
25
|
+
dependencyUrl: OMC_REPO_URL,
|
|
26
|
+
},
|
|
27
|
+
{ name: OPENSPEC_PACKAGE, packageName: OPENSPEC_PACKAGE },
|
|
28
|
+
{ name: CAVEMEM_PACKAGE, packageName: CAVEMEM_PACKAGE },
|
|
29
|
+
{
|
|
30
|
+
name: '@imdeadpool/codex-account-switcher',
|
|
31
|
+
packageName: '@imdeadpool/codex-account-switcher',
|
|
32
|
+
},
|
|
33
|
+
];
|
|
19
34
|
const GLOBAL_TOOLCHAIN_PACKAGES = [
|
|
20
|
-
|
|
21
|
-
OMC_PACKAGE,
|
|
22
|
-
OPENSPEC_PACKAGE,
|
|
23
|
-
CAVEMEM_PACKAGE,
|
|
24
|
-
'@imdeadpool/codex-account-switcher',
|
|
35
|
+
...GLOBAL_TOOLCHAIN_SERVICES.map((service) => service.packageName),
|
|
25
36
|
];
|
|
26
37
|
const OPTIONAL_LOCAL_COMPANION_TOOLS = [
|
|
27
38
|
{
|
|
@@ -3629,6 +3640,36 @@ function resolveGlobalInstallApproval(options) {
|
|
|
3629
3640
|
return { approved: true, source: 'prompt' };
|
|
3630
3641
|
}
|
|
3631
3642
|
|
|
3643
|
+
function getGlobalToolchainService(packageName) {
|
|
3644
|
+
const service = GLOBAL_TOOLCHAIN_SERVICES.find(
|
|
3645
|
+
(candidate) => candidate.packageName === packageName,
|
|
3646
|
+
);
|
|
3647
|
+
return service || { name: packageName, packageName };
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3650
|
+
function formatGlobalToolchainServiceName(packageName) {
|
|
3651
|
+
return getGlobalToolchainService(packageName).name;
|
|
3652
|
+
}
|
|
3653
|
+
|
|
3654
|
+
function describeMissingGlobalDependencyWarnings(packageNames) {
|
|
3655
|
+
return packageNames
|
|
3656
|
+
.map((packageName) => getGlobalToolchainService(packageName))
|
|
3657
|
+
.filter((service) => service.dependencyUrl)
|
|
3658
|
+
.map(
|
|
3659
|
+
(service) =>
|
|
3660
|
+
`Guardex needs ${service.name} as a dependency: ${service.dependencyUrl}`,
|
|
3661
|
+
);
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3664
|
+
function buildMissingCompanionInstallPrompt(missingPackages, missingLocalTools) {
|
|
3665
|
+
const dependencyWarnings = describeMissingGlobalDependencyWarnings(missingPackages);
|
|
3666
|
+
const installCommands = describeCompanionInstallCommands(missingPackages, missingLocalTools);
|
|
3667
|
+
const dependencyPrefix = dependencyWarnings.length > 0
|
|
3668
|
+
? `${dependencyWarnings.join(' ')} `
|
|
3669
|
+
: '';
|
|
3670
|
+
return `${dependencyPrefix}Install missing companion tools now? (${installCommands.join(' && ')})`;
|
|
3671
|
+
}
|
|
3672
|
+
|
|
3632
3673
|
function detectGlobalToolchainPackages() {
|
|
3633
3674
|
const result = run(NPM_BIN, ['list', '-g', '--depth=0', '--json']);
|
|
3634
3675
|
if (result.status !== 0) {
|
|
@@ -3721,17 +3762,15 @@ function describeCompanionInstallCommands(missingPackages, missingLocalTools) {
|
|
|
3721
3762
|
return commands;
|
|
3722
3763
|
}
|
|
3723
3764
|
|
|
3724
|
-
function askGlobalInstallForMissing(options, missingPackages) {
|
|
3765
|
+
function askGlobalInstallForMissing(options, missingPackages, missingLocalTools) {
|
|
3725
3766
|
const approval = resolveGlobalInstallApproval(options);
|
|
3726
3767
|
if (!approval.approved) {
|
|
3727
3768
|
return approval;
|
|
3728
3769
|
}
|
|
3729
3770
|
|
|
3730
|
-
const missingLocalTools = detectOptionalLocalCompanionTools().filter((tool) => tool.status !== 'active');
|
|
3731
|
-
const installCommands = describeCompanionInstallCommands(missingPackages, missingLocalTools);
|
|
3732
3771
|
if (approval.source === 'prompt') {
|
|
3733
3772
|
const approved = promptYesNoStrict(
|
|
3734
|
-
|
|
3773
|
+
buildMissingCompanionInstallPrompt(missingPackages, missingLocalTools),
|
|
3735
3774
|
);
|
|
3736
3775
|
return { approved, source: 'prompt' };
|
|
3737
3776
|
}
|
|
@@ -3750,7 +3789,10 @@ function installGlobalToolchain(options) {
|
|
|
3750
3789
|
console.log(`[${TOOL_NAME}] ⚠️ Could not detect global packages: ${detection.error}`);
|
|
3751
3790
|
} else {
|
|
3752
3791
|
if (detection.installed.length > 0) {
|
|
3753
|
-
console.log(
|
|
3792
|
+
console.log(
|
|
3793
|
+
`[${TOOL_NAME}] Already installed globally: ` +
|
|
3794
|
+
`${detection.installed.map((pkg) => formatGlobalToolchainServiceName(pkg)).join(', ')}`,
|
|
3795
|
+
);
|
|
3754
3796
|
}
|
|
3755
3797
|
const installedLocalTools = localCompanionTools
|
|
3756
3798
|
.filter((tool) => tool.status === 'active')
|
|
@@ -3765,9 +3807,14 @@ function installGlobalToolchain(options) {
|
|
|
3765
3807
|
|
|
3766
3808
|
const missingPackages = detection.ok ? detection.missing : [...GLOBAL_TOOLCHAIN_PACKAGES];
|
|
3767
3809
|
const missingLocalTools = localCompanionTools.filter((tool) => tool.status !== 'active');
|
|
3768
|
-
const approval = askGlobalInstallForMissing(options, missingPackages);
|
|
3810
|
+
const approval = askGlobalInstallForMissing(options, missingPackages, missingLocalTools);
|
|
3769
3811
|
if (!approval.approved) {
|
|
3770
|
-
return {
|
|
3812
|
+
return {
|
|
3813
|
+
status: 'skipped',
|
|
3814
|
+
reason: approval.source,
|
|
3815
|
+
missingPackages,
|
|
3816
|
+
missingLocalTools,
|
|
3817
|
+
};
|
|
3771
3818
|
}
|
|
3772
3819
|
|
|
3773
3820
|
const installed = [];
|
|
@@ -4142,11 +4189,21 @@ function status(rawArgs) {
|
|
|
4142
4189
|
|
|
4143
4190
|
const toolchain = detectGlobalToolchainPackages();
|
|
4144
4191
|
const npmServices = GLOBAL_TOOLCHAIN_PACKAGES.map((pkg) => {
|
|
4192
|
+
const service = getGlobalToolchainService(pkg);
|
|
4145
4193
|
if (!toolchain.ok) {
|
|
4146
|
-
return {
|
|
4194
|
+
return {
|
|
4195
|
+
name: service.name,
|
|
4196
|
+
displayName: service.name,
|
|
4197
|
+
packageName: pkg,
|
|
4198
|
+
dependencyUrl: service.dependencyUrl || null,
|
|
4199
|
+
status: 'unknown',
|
|
4200
|
+
};
|
|
4147
4201
|
}
|
|
4148
4202
|
return {
|
|
4149
|
-
name:
|
|
4203
|
+
name: service.name,
|
|
4204
|
+
displayName: service.name,
|
|
4205
|
+
packageName: pkg,
|
|
4206
|
+
dependencyUrl: service.dependencyUrl || null,
|
|
4150
4207
|
status: toolchain.installed.includes(pkg) ? 'active' : 'inactive',
|
|
4151
4208
|
};
|
|
4152
4209
|
});
|
|
@@ -4224,6 +4281,13 @@ function status(rawArgs) {
|
|
|
4224
4281
|
console.log(
|
|
4225
4282
|
`[${TOOL_NAME}] Optional companion tools inactive: ${inactiveOptionalCompanions.join(', ')}`,
|
|
4226
4283
|
);
|
|
4284
|
+
for (const warning of describeMissingGlobalDependencyWarnings(
|
|
4285
|
+
npmServices
|
|
4286
|
+
.filter((service) => service.status === 'inactive')
|
|
4287
|
+
.map((service) => service.packageName),
|
|
4288
|
+
)) {
|
|
4289
|
+
console.log(`[${TOOL_NAME}] ${warning}`);
|
|
4290
|
+
}
|
|
4227
4291
|
console.log(
|
|
4228
4292
|
`[${TOOL_NAME}] Run '${SHORT_TOOL_NAME} setup' to install missing companions with an explicit Y/N prompt.`,
|
|
4229
4293
|
);
|
|
@@ -4881,6 +4945,13 @@ function setup(rawArgs) {
|
|
|
4881
4945
|
`[${TOOL_NAME}] Skipping companion installs (non-interactive mode). ` +
|
|
4882
4946
|
`Use --yes-global-install to force or run interactively for Y/N prompt.`,
|
|
4883
4947
|
);
|
|
4948
|
+
} else if (globalInstallStatus.status === 'skipped') {
|
|
4949
|
+
console.log(`[${TOOL_NAME}] ⚠️ Companion installs skipped by user choice.`);
|
|
4950
|
+
for (const warning of describeMissingGlobalDependencyWarnings(
|
|
4951
|
+
globalInstallStatus.missingPackages || [],
|
|
4952
|
+
)) {
|
|
4953
|
+
console.log(`[${TOOL_NAME}] ⚠️ ${warning}`);
|
|
4954
|
+
}
|
|
4884
4955
|
}
|
|
4885
4956
|
const requiredSystemTools = detectRequiredSystemTools();
|
|
4886
4957
|
const missingSystemTools = requiredSystemTools.filter((tool) => tool.status !== 'active');
|