@imdeadpool/guardex 5.0.16 → 6.0.0
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 +20 -6
- package/bin/multiagent-safety.js +31 -24
- package/package.json +1 -2
- package/templates/AGENTS.multiagent-safety.md +1 -1
- package/templates/githooks/post-merge +3 -3
- package/templates/githooks/pre-commit +7 -7
- package/templates/githooks/pre-push +2 -2
- package/templates/scripts/agent-branch-finish.sh +5 -5
- package/templates/scripts/agent-branch-start.sh +76 -16
- package/templates/scripts/agent-worktree-prune.sh +3 -3
- package/templates/scripts/codex-agent.sh +17 -17
- package/templates/scripts/review-bot-watch.sh +11 -11
package/README.md
CHANGED
|
@@ -349,11 +349,11 @@ openspec update
|
|
|
349
349
|
### OpenSpec in agent sub-branches
|
|
350
350
|
|
|
351
351
|
- `scripts/codex-agent.sh` enforces OpenSpec workspaces before it launches Codex in each sandbox branch/worktree.
|
|
352
|
-
- `scripts/agent-branch-start.sh` can scaffold both `openspec/changes/<agent-branch-slug>/` and `openspec/plan/<agent-branch-slug>/` when you set `
|
|
353
|
-
- Set `
|
|
354
|
-
- Set `
|
|
355
|
-
- Set `
|
|
356
|
-
- Set `
|
|
352
|
+
- `scripts/agent-branch-start.sh` can scaffold both `openspec/changes/<agent-branch-slug>/` and `openspec/plan/<agent-branch-slug>/` when you set `GUARDEX_OPENSPEC_AUTO_INIT=true`.
|
|
353
|
+
- Set `GUARDEX_OPENSPEC_AUTO_INIT=false` (default for `agent-branch-start`) to skip branch-start auto-bootstrap.
|
|
354
|
+
- Set `GUARDEX_OPENSPEC_PLAN_SLUG=<kebab-case-slug>` to force a specific plan workspace name.
|
|
355
|
+
- Set `GUARDEX_OPENSPEC_CHANGE_SLUG=<kebab-case-slug>` to force a specific change workspace name.
|
|
356
|
+
- Set `GUARDEX_OPENSPEC_CAPABILITY_SLUG=<kebab-case-slug>` to override the default capability folder used for `spec.md` scaffolding.
|
|
357
357
|
|
|
358
358
|
## Security and maintenance posture
|
|
359
359
|
|
|
@@ -372,9 +372,23 @@ npm pack --dry-run
|
|
|
372
372
|
|
|
373
373
|
## Release notes
|
|
374
374
|
|
|
375
|
+
### v6.0.0
|
|
376
|
+
|
|
377
|
+
- **Breaking** — removed the legacy `musafety` bin alias and all `MUSAFETY_*` environment variables. Callers must migrate to the `guardex` / `gx` bins and the `GUARDEX_*` env-var surface.
|
|
378
|
+
- **Breaking** — bootstrap manifest filename changed from `musafety-bootstrap-manifest.json` to `guardex-bootstrap-manifest.json`; existing sandbox worktrees must be pruned + re-bootstrapped (or have their manifest manually renamed).
|
|
379
|
+
- Rebranded all remaining `musafety` / `Musafety` / `MUSAFETY` codename tokens to `guardex` / `Guardex` / `GUARDEX` across scripts, templates, hooks, tests, and docs.
|
|
380
|
+
- The descriptive phrase `multiagent-safety` (including `bin/multiagent-safety.js` and `templates/AGENTS.multiagent-safety.md`) is preserved intentionally — only the short codename changed.
|
|
381
|
+
- Bumped package version from `5.0.17` to `6.0.0` for the next npm publish.
|
|
382
|
+
|
|
383
|
+
### v5.0.17
|
|
384
|
+
|
|
385
|
+
- Bumped package version from `5.0.16` to `5.0.17` for the next npm publish.
|
|
386
|
+
|
|
375
387
|
### v5.0.16
|
|
376
388
|
|
|
377
389
|
- Fixed `gx doctor` runtime crash (`parseDoctorArgs is not defined`) by restoring the doctor argument parser for `--target` and `--strict`.
|
|
390
|
+
- Fixed `gx doctor` command routing so the repair-first doctor flow remains the active command path (duplicate legacy doctor definition no longer overrides it).
|
|
391
|
+
- Updated worktree change detection to run `git status --porcelain --untracked-files=normal --` for consistent normal untracked-file behavior.
|
|
378
392
|
- Added regression coverage that asserts the doctor parser function exists in `bin/multiagent-safety.js`.
|
|
379
393
|
- Bumped package version from `5.0.15` to `5.0.16`.
|
|
380
394
|
|
|
@@ -411,7 +425,7 @@ npm pack --dry-run
|
|
|
411
425
|
|
|
412
426
|
### v5.0.9
|
|
413
427
|
|
|
414
|
-
- Enforced OpenSpec workspace bootstrap for sandbox agent execution: `scripts/codex-agent.sh` now initializes `openspec/plan/<agent-branch-slug>/` before launching Codex, and `scripts/agent-branch-start.sh` supports `
|
|
428
|
+
- Enforced OpenSpec workspace bootstrap for sandbox agent execution: `scripts/codex-agent.sh` now initializes `openspec/plan/<agent-branch-slug>/` before launching Codex, and `scripts/agent-branch-start.sh` supports `GUARDEX_OPENSPEC_AUTO_INIT` plus `GUARDEX_OPENSPEC_PLAN_SLUG`.
|
|
415
429
|
- Tightened doctor auto-finish correctness: sandbox finish now waits for merge and exits non-zero if the PR closes without merge, so repair flows are not reported as complete when policy blocks merge.
|
|
416
430
|
- Updated package version from `5.0.8` to `5.0.9` for the next npm publish.
|
|
417
431
|
|
package/bin/multiagent-safety.js
CHANGED
|
@@ -9,14 +9,14 @@ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
|
9
9
|
|
|
10
10
|
const TOOL_NAME = 'guardex';
|
|
11
11
|
const SHORT_TOOL_NAME = 'gx';
|
|
12
|
-
const LEGACY_NAMES = ['
|
|
12
|
+
const LEGACY_NAMES = ['guardex', 'multiagent-safety'];
|
|
13
13
|
const OPENSPEC_PACKAGE = '@fission-ai/openspec';
|
|
14
14
|
const GLOBAL_TOOLCHAIN_PACKAGES = [
|
|
15
15
|
'oh-my-codex',
|
|
16
16
|
OPENSPEC_PACKAGE,
|
|
17
17
|
'@imdeadpool/codex-account-switcher',
|
|
18
18
|
];
|
|
19
|
-
const GH_BIN = process.env.
|
|
19
|
+
const GH_BIN = process.env.GUARDEX_GH_BIN || 'gh';
|
|
20
20
|
const REQUIRED_SYSTEM_TOOLS = [
|
|
21
21
|
{
|
|
22
22
|
name: 'gh',
|
|
@@ -26,11 +26,11 @@ const REQUIRED_SYSTEM_TOOLS = [
|
|
|
26
26
|
},
|
|
27
27
|
];
|
|
28
28
|
const MAINTAINER_RELEASE_REPO = path.resolve(
|
|
29
|
-
process.env.
|
|
29
|
+
process.env.GUARDEX_RELEASE_REPO || '/tmp/multiagent-safety',
|
|
30
30
|
);
|
|
31
|
-
const NPM_BIN = process.env.
|
|
32
|
-
const OPENSPEC_BIN = process.env.
|
|
33
|
-
const SCORECARD_BIN = process.env.
|
|
31
|
+
const NPM_BIN = process.env.GUARDEX_NPM_BIN || 'npm';
|
|
32
|
+
const OPENSPEC_BIN = process.env.GUARDEX_OPENSPEC_BIN || 'openspec';
|
|
33
|
+
const SCORECARD_BIN = process.env.GUARDEX_SCORECARD_BIN || 'scorecard';
|
|
34
34
|
const GIT_PROTECTED_BRANCHES_KEY = 'multiagent.protectedBranches';
|
|
35
35
|
const GIT_BASE_BRANCH_KEY = 'multiagent.baseBranch';
|
|
36
36
|
const GIT_SYNC_STRATEGY_KEY = 'multiagent.sync.strategy';
|
|
@@ -1405,7 +1405,7 @@ function finishDoctorSandboxBranch(blocked, metadata) {
|
|
|
1405
1405
|
note: 'origin remote missing; skipped auto-finish',
|
|
1406
1406
|
};
|
|
1407
1407
|
}
|
|
1408
|
-
const explicitGhBin = Boolean(String(process.env.
|
|
1408
|
+
const explicitGhBin = Boolean(String(process.env.GUARDEX_GH_BIN || '').trim());
|
|
1409
1409
|
if (!explicitGhBin && !originRemoteLooksLikeGithub(blocked.repoRoot)) {
|
|
1410
1410
|
return {
|
|
1411
1411
|
status: 'skipped',
|
|
@@ -1413,7 +1413,7 @@ function finishDoctorSandboxBranch(blocked, metadata) {
|
|
|
1413
1413
|
};
|
|
1414
1414
|
}
|
|
1415
1415
|
|
|
1416
|
-
const ghBin = process.env.
|
|
1416
|
+
const ghBin = process.env.GUARDEX_GH_BIN || 'gh';
|
|
1417
1417
|
if (!isCommandAvailable(ghBin)) {
|
|
1418
1418
|
return {
|
|
1419
1419
|
status: 'skipped',
|
|
@@ -1429,7 +1429,7 @@ function finishDoctorSandboxBranch(blocked, metadata) {
|
|
|
1429
1429
|
};
|
|
1430
1430
|
}
|
|
1431
1431
|
|
|
1432
|
-
const rawWaitTimeoutSeconds = Number.parseInt(process.env.
|
|
1432
|
+
const rawWaitTimeoutSeconds = Number.parseInt(process.env.GUARDEX_FINISH_WAIT_TIMEOUT_SECONDS || '1800', 10);
|
|
1433
1433
|
const waitTimeoutSeconds =
|
|
1434
1434
|
Number.isFinite(rawWaitTimeoutSeconds) && rawWaitTimeoutSeconds >= 30 ? rawWaitTimeoutSeconds : 1800;
|
|
1435
1435
|
const finishTimeoutMs = Math.max(180_000, (waitTimeoutSeconds + 60) * 1000);
|
|
@@ -2117,7 +2117,14 @@ function mapWorktreePathsByBranch(repoRoot) {
|
|
|
2117
2117
|
}
|
|
2118
2118
|
|
|
2119
2119
|
function hasSignificantWorkingTreeChanges(worktreePath) {
|
|
2120
|
-
const result = run('git', [
|
|
2120
|
+
const result = run('git', [
|
|
2121
|
+
'-C',
|
|
2122
|
+
worktreePath,
|
|
2123
|
+
'status',
|
|
2124
|
+
'--porcelain',
|
|
2125
|
+
'--untracked-files=normal',
|
|
2126
|
+
'--',
|
|
2127
|
+
]);
|
|
2121
2128
|
if (result.status !== 0) {
|
|
2122
2129
|
return true;
|
|
2123
2130
|
}
|
|
@@ -2163,15 +2170,15 @@ function autoFinishReadyAgentBranches(repoRoot, options = {}) {
|
|
|
2163
2170
|
return summary;
|
|
2164
2171
|
}
|
|
2165
2172
|
|
|
2166
|
-
if (String(process.env.
|
|
2173
|
+
if (String(process.env.GUARDEX_DOCTOR_SANDBOX || '') === '1') {
|
|
2167
2174
|
summary.enabled = false;
|
|
2168
2175
|
summary.details.push('Skipped auto-finish sweep inside doctor sandbox pass.');
|
|
2169
2176
|
return summary;
|
|
2170
2177
|
}
|
|
2171
2178
|
|
|
2172
|
-
if (String(process.env.
|
|
2179
|
+
if (String(process.env.GUARDEX_SKIP_AUTO_FINISH_READY_BRANCHES || '') === '1') {
|
|
2173
2180
|
summary.enabled = false;
|
|
2174
|
-
summary.details.push('Skipped auto-finish sweep (
|
|
2181
|
+
summary.details.push('Skipped auto-finish sweep (GUARDEX_SKIP_AUTO_FINISH_READY_BRANCHES=1).');
|
|
2175
2182
|
return summary;
|
|
2176
2183
|
}
|
|
2177
2184
|
|
|
@@ -2194,14 +2201,14 @@ function autoFinishReadyAgentBranches(repoRoot, options = {}) {
|
|
|
2194
2201
|
summary.details.push('Skipped auto-finish sweep (origin remote missing).');
|
|
2195
2202
|
return summary;
|
|
2196
2203
|
}
|
|
2197
|
-
const explicitGhBin = Boolean(String(process.env.
|
|
2204
|
+
const explicitGhBin = Boolean(String(process.env.GUARDEX_GH_BIN || '').trim());
|
|
2198
2205
|
if (!explicitGhBin && !originRemoteLooksLikeGithub(repoRoot)) {
|
|
2199
2206
|
summary.enabled = false;
|
|
2200
2207
|
summary.details.push('Skipped auto-finish sweep (origin remote is not GitHub).');
|
|
2201
2208
|
return summary;
|
|
2202
2209
|
}
|
|
2203
2210
|
|
|
2204
|
-
const ghBin = process.env.
|
|
2211
|
+
const ghBin = process.env.GUARDEX_GH_BIN || 'gh';
|
|
2205
2212
|
if (run(ghBin, ['--version']).status !== 0) {
|
|
2206
2213
|
summary.enabled = false;
|
|
2207
2214
|
summary.details.push(`Skipped auto-finish sweep (${ghBin} not available).`);
|
|
@@ -3144,12 +3151,12 @@ function parseNpmVersionOutput(stdout) {
|
|
|
3144
3151
|
}
|
|
3145
3152
|
}
|
|
3146
3153
|
|
|
3147
|
-
function
|
|
3148
|
-
if (envFlagEnabled('
|
|
3154
|
+
function checkForGuardexUpdate() {
|
|
3155
|
+
if (envFlagEnabled('GUARDEX_SKIP_UPDATE_CHECK')) {
|
|
3149
3156
|
return { checked: false, reason: 'disabled' };
|
|
3150
3157
|
}
|
|
3151
3158
|
|
|
3152
|
-
const forceCheck = envFlagEnabled('
|
|
3159
|
+
const forceCheck = envFlagEnabled('GUARDEX_FORCE_UPDATE_CHECK');
|
|
3153
3160
|
if (!forceCheck && !isInteractiveTerminal()) {
|
|
3154
3161
|
return { checked: false, reason: 'non-interactive' };
|
|
3155
3162
|
}
|
|
@@ -3181,14 +3188,14 @@ function printUpdateAvailableBanner(current, latest) {
|
|
|
3181
3188
|
}
|
|
3182
3189
|
|
|
3183
3190
|
function maybeSelfUpdateBeforeStatus() {
|
|
3184
|
-
const check =
|
|
3191
|
+
const check = checkForGuardexUpdate();
|
|
3185
3192
|
if (!check.checked || !check.updateAvailable) {
|
|
3186
3193
|
return;
|
|
3187
3194
|
}
|
|
3188
3195
|
|
|
3189
3196
|
printUpdateAvailableBanner(check.current, check.latest);
|
|
3190
3197
|
|
|
3191
|
-
const autoApproval = parseAutoApproval('
|
|
3198
|
+
const autoApproval = parseAutoApproval('GUARDEX_AUTO_UPDATE_APPROVAL');
|
|
3192
3199
|
const interactive = isInteractiveTerminal();
|
|
3193
3200
|
|
|
3194
3201
|
if (!interactive && autoApproval == null) {
|
|
@@ -3217,11 +3224,11 @@ function maybeSelfUpdateBeforeStatus() {
|
|
|
3217
3224
|
}
|
|
3218
3225
|
|
|
3219
3226
|
function checkForOpenSpecPackageUpdate() {
|
|
3220
|
-
if (envFlagEnabled('
|
|
3227
|
+
if (envFlagEnabled('GUARDEX_SKIP_OPENSPEC_UPDATE_CHECK')) {
|
|
3221
3228
|
return { checked: false, reason: 'disabled' };
|
|
3222
3229
|
}
|
|
3223
3230
|
|
|
3224
|
-
const forceCheck = envFlagEnabled('
|
|
3231
|
+
const forceCheck = envFlagEnabled('GUARDEX_FORCE_OPENSPEC_UPDATE_CHECK');
|
|
3225
3232
|
if (!forceCheck && !isInteractiveTerminal()) {
|
|
3226
3233
|
return { checked: false, reason: 'non-interactive' };
|
|
3227
3234
|
}
|
|
@@ -3271,7 +3278,7 @@ function maybeOpenSpecUpdateBeforeStatus() {
|
|
|
3271
3278
|
|
|
3272
3279
|
printOpenSpecUpdateAvailableBanner(check.current, check.latest);
|
|
3273
3280
|
|
|
3274
|
-
const autoApproval = parseAutoApproval('
|
|
3281
|
+
const autoApproval = parseAutoApproval('GUARDEX_AUTO_OPENSPEC_UPDATE_APPROVAL');
|
|
3275
3282
|
const interactive = isInteractiveTerminal();
|
|
3276
3283
|
|
|
3277
3284
|
if (!interactive && autoApproval == null) {
|
|
@@ -4659,7 +4666,7 @@ function initWorkspace(rawArgs) {
|
|
|
4659
4666
|
}
|
|
4660
4667
|
}
|
|
4661
4668
|
|
|
4662
|
-
function
|
|
4669
|
+
function doctorAudit(rawArgs) {
|
|
4663
4670
|
const options = parseDoctorArgs(rawArgs);
|
|
4664
4671
|
const repoRoot = resolveRepoRoot(options.target);
|
|
4665
4672
|
const failures = [];
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imdeadpool/guardex",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "GuardeX: the Guardian T-Rex for your repo, with hardened multi-agent git guardrails.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"preferGlobal": true,
|
|
7
7
|
"bin": {
|
|
8
8
|
"guardex": "bin/multiagent-safety.js",
|
|
9
9
|
"gx": "bin/multiagent-safety.js",
|
|
10
|
-
"musafety": "bin/multiagent-safety.js",
|
|
11
10
|
"multiagent-safety": "bin/multiagent-safety.js"
|
|
12
11
|
},
|
|
13
12
|
"scripts": {
|
|
@@ -57,7 +57,7 @@ openspec/plan/<agent-branch-slug>/
|
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
For manual `scripts/agent-branch-start.sh` usage, enable auto-bootstrap with
|
|
60
|
-
`
|
|
60
|
+
`GUARDEX_OPENSPEC_AUTO_INIT=true` or scaffold manually before implementation:
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
63
|
bash scripts/openspec/init-change-workspace.sh "<change-slug>" "<capability-slug>"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
if [[ "${
|
|
4
|
+
if [[ "${GUARDEX_DISABLE_POST_MERGE_CLEANUP:-0}" == "1" ]]; then
|
|
5
5
|
exit 0
|
|
6
6
|
fi
|
|
7
7
|
|
|
@@ -15,7 +15,7 @@ if [[ -z "$branch" || "$branch" == "HEAD" ]]; then
|
|
|
15
15
|
exit 0
|
|
16
16
|
fi
|
|
17
17
|
|
|
18
|
-
base_branch="${
|
|
18
|
+
base_branch="${GUARDEX_BASE_BRANCH:-$(git -C "$repo_root" config --get multiagent.baseBranch || true)}"
|
|
19
19
|
if [[ -z "$base_branch" ]]; then
|
|
20
20
|
base_branch="dev"
|
|
21
21
|
fi
|
|
@@ -29,7 +29,7 @@ if [[ ! -f "$cli_path" ]]; then
|
|
|
29
29
|
exit 0
|
|
30
30
|
fi
|
|
31
31
|
|
|
32
|
-
node_bin="${
|
|
32
|
+
node_bin="${GUARDEX_NODE_BIN:-node}"
|
|
33
33
|
if ! command -v "$node_bin" >/dev/null 2>&1; then
|
|
34
34
|
exit 0
|
|
35
35
|
fi
|
|
@@ -28,7 +28,7 @@ if [[ -n "${VSCODE_GIT_IPC_HANDLE:-}" || -n "${VSCODE_GIT_ASKPASS_NODE:-}" || -n
|
|
|
28
28
|
is_vscode_git_context=1
|
|
29
29
|
fi
|
|
30
30
|
|
|
31
|
-
allow_vscode_protected_raw="${
|
|
31
|
+
allow_vscode_protected_raw="${GUARDEX_ALLOW_VSCODE_PROTECTED_BRANCH_WRITES:-$(git config --get multiagent.allowVscodeProtectedBranchWrites || true)}"
|
|
32
32
|
if [[ -z "$allow_vscode_protected_raw" ]]; then
|
|
33
33
|
allow_vscode_protected_raw="false"
|
|
34
34
|
fi
|
|
@@ -41,7 +41,7 @@ case "$allow_vscode_protected" in
|
|
|
41
41
|
*) allow_vscode_protected_branch_writes=0 ;;
|
|
42
42
|
esac
|
|
43
43
|
|
|
44
|
-
protected_branches_raw="${
|
|
44
|
+
protected_branches_raw="${GUARDEX_PROTECTED_BRANCHES:-$(git config --get multiagent.protectedBranches || true)}"
|
|
45
45
|
if [[ -z "$protected_branches_raw" ]]; then
|
|
46
46
|
protected_branches_raw="dev main master"
|
|
47
47
|
fi
|
|
@@ -55,7 +55,7 @@ for protected_branch in $protected_branches_raw; do
|
|
|
55
55
|
fi
|
|
56
56
|
done
|
|
57
57
|
|
|
58
|
-
codex_require_agent_branch_raw="${
|
|
58
|
+
codex_require_agent_branch_raw="${GUARDEX_CODEX_REQUIRE_AGENT_BRANCH:-$(git config --get multiagent.codexRequireAgentBranch || true)}"
|
|
59
59
|
if [[ -z "$codex_require_agent_branch_raw" ]]; then
|
|
60
60
|
codex_require_agent_branch_raw="true"
|
|
61
61
|
fi
|
|
@@ -86,7 +86,7 @@ if [[ "$is_codex_session" == "1" && "$is_protected_branch" == "1" ]]; then
|
|
|
86
86
|
fi
|
|
87
87
|
fi
|
|
88
88
|
|
|
89
|
-
if [[ "$should_require_codex_agent_branch" == "1" && "${
|
|
89
|
+
if [[ "$should_require_codex_agent_branch" == "1" && "${GUARDEX_ALLOW_CODEX_ON_NON_AGENT:-0}" != "1" ]]; then
|
|
90
90
|
if [[ "$is_codex_session" == "1" && "$branch" != agent/* ]]; then
|
|
91
91
|
if [[ "$is_protected_branch" == "1" ]]; then
|
|
92
92
|
if [[ "$is_codex_managed_only_commit_on_protected" == "1" ]]; then
|
|
@@ -103,7 +103,7 @@ Or manually:
|
|
|
103
103
|
Then commit from the created agent/* branch.
|
|
104
104
|
|
|
105
105
|
Temporary bypass (not recommended):
|
|
106
|
-
|
|
106
|
+
GUARDEX_ALLOW_CODEX_ON_NON_AGENT=1 git commit ...
|
|
107
107
|
MSG
|
|
108
108
|
exit 1
|
|
109
109
|
fi
|
|
@@ -115,7 +115,7 @@ Use isolated branch/worktree first:
|
|
|
115
115
|
Then commit from the created agent/* branch.
|
|
116
116
|
|
|
117
117
|
Temporary bypass (not recommended):
|
|
118
|
-
|
|
118
|
+
GUARDEX_ALLOW_CODEX_ON_NON_AGENT=1 git commit ...
|
|
119
119
|
Disable this rule for a repo (not recommended):
|
|
120
120
|
git config multiagent.codexRequireAgentBranch false
|
|
121
121
|
MSG
|
|
@@ -169,7 +169,7 @@ MSG
|
|
|
169
169
|
fi
|
|
170
170
|
|
|
171
171
|
if [[ "$branch" == agent/* ]]; then
|
|
172
|
-
if [[ "${
|
|
172
|
+
if [[ "${GUARDEX_AUTOCLAIM_STAGED_LOCKS:-1}" == "1" ]]; then
|
|
173
173
|
while IFS= read -r staged_file; do
|
|
174
174
|
[[ -z "$staged_file" ]] && continue
|
|
175
175
|
[[ "$staged_file" == ".omx/state/agent-file-locks.json" ]] && continue
|
|
@@ -10,7 +10,7 @@ if [[ -n "${VSCODE_GIT_IPC_HANDLE:-}" || -n "${VSCODE_GIT_ASKPASS_NODE:-}" || -n
|
|
|
10
10
|
is_vscode_git_context=1
|
|
11
11
|
fi
|
|
12
12
|
|
|
13
|
-
allow_vscode_protected_raw="${
|
|
13
|
+
allow_vscode_protected_raw="${GUARDEX_ALLOW_VSCODE_PROTECTED_BRANCH_WRITES:-$(git config --get multiagent.allowVscodeProtectedBranchWrites || true)}"
|
|
14
14
|
if [[ -z "$allow_vscode_protected_raw" ]]; then
|
|
15
15
|
allow_vscode_protected_raw="false"
|
|
16
16
|
fi
|
|
@@ -28,7 +28,7 @@ if [[ -n "${CODEX_THREAD_ID:-}" || -n "${OMX_SESSION_ID:-}" || "${CODEX_CI:-0}"
|
|
|
28
28
|
is_codex_session=1
|
|
29
29
|
fi
|
|
30
30
|
|
|
31
|
-
protected_branches_raw="${
|
|
31
|
+
protected_branches_raw="${GUARDEX_PROTECTED_BRANCHES:-$(git config --get multiagent.protectedBranches || true)}"
|
|
32
32
|
if [[ -z "$protected_branches_raw" ]]; then
|
|
33
33
|
protected_branches_raw="dev main master"
|
|
34
34
|
fi
|
|
@@ -8,11 +8,11 @@ PUSH_ENABLED=1
|
|
|
8
8
|
DELETE_REMOTE_BRANCH=0
|
|
9
9
|
DELETE_REMOTE_BRANCH_EXPLICIT=0
|
|
10
10
|
MERGE_MODE="auto"
|
|
11
|
-
GH_BIN="${
|
|
12
|
-
CLEANUP_AFTER_MERGE_RAW="${
|
|
13
|
-
WAIT_FOR_MERGE_RAW="${
|
|
14
|
-
WAIT_TIMEOUT_SECONDS_RAW="${
|
|
15
|
-
WAIT_POLL_SECONDS_RAW="${
|
|
11
|
+
GH_BIN="${GUARDEX_GH_BIN:-gh}"
|
|
12
|
+
CLEANUP_AFTER_MERGE_RAW="${GUARDEX_FINISH_CLEANUP:-false}"
|
|
13
|
+
WAIT_FOR_MERGE_RAW="${GUARDEX_FINISH_WAIT_FOR_MERGE:-false}"
|
|
14
|
+
WAIT_TIMEOUT_SECONDS_RAW="${GUARDEX_FINISH_WAIT_TIMEOUT_SECONDS:-1800}"
|
|
15
|
+
WAIT_POLL_SECONDS_RAW="${GUARDEX_FINISH_WAIT_POLL_SECONDS:-10}"
|
|
16
16
|
|
|
17
17
|
normalize_bool() {
|
|
18
18
|
local raw="${1:-}"
|
|
@@ -6,10 +6,10 @@ AGENT_NAME="agent"
|
|
|
6
6
|
BASE_BRANCH=""
|
|
7
7
|
BASE_BRANCH_EXPLICIT=0
|
|
8
8
|
WORKTREE_ROOT_REL=".omx/agent-worktrees"
|
|
9
|
-
OPENSPEC_AUTO_INIT_RAW="${
|
|
10
|
-
OPENSPEC_PLAN_SLUG_OVERRIDE="${
|
|
11
|
-
OPENSPEC_CHANGE_SLUG_OVERRIDE="${
|
|
12
|
-
OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${
|
|
9
|
+
OPENSPEC_AUTO_INIT_RAW="${GUARDEX_OPENSPEC_AUTO_INIT:-false}"
|
|
10
|
+
OPENSPEC_PLAN_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_PLAN_SLUG:-}"
|
|
11
|
+
OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
|
|
12
|
+
OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
|
|
13
13
|
POSITIONAL_ARGS=()
|
|
14
14
|
|
|
15
15
|
while [[ $# -gt 0 ]]; do
|
|
@@ -86,6 +86,68 @@ sanitize_slug() {
|
|
|
86
86
|
printf '%s' "$slug"
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
sanitize_optional_slug() {
|
|
90
|
+
local raw="$1"
|
|
91
|
+
local fallback="${2:-snapshot}"
|
|
92
|
+
if [[ -z "$raw" ]]; then
|
|
93
|
+
printf ''
|
|
94
|
+
return 0
|
|
95
|
+
fi
|
|
96
|
+
sanitize_slug "$raw" "$fallback"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
normalize_positive_int() {
|
|
100
|
+
local raw="$1"
|
|
101
|
+
local fallback="$2"
|
|
102
|
+
if [[ "$raw" =~ ^[0-9]+$ ]] && [[ "$raw" -gt 0 ]]; then
|
|
103
|
+
printf '%s' "$raw"
|
|
104
|
+
return 0
|
|
105
|
+
fi
|
|
106
|
+
printf '%s' "$fallback"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
shorten_slug() {
|
|
110
|
+
local slug="$1"
|
|
111
|
+
local raw_max="$2"
|
|
112
|
+
local max_len
|
|
113
|
+
max_len="$(normalize_positive_int "$raw_max" "32")"
|
|
114
|
+
if [[ "${#slug}" -le "$max_len" ]]; then
|
|
115
|
+
printf '%s' "$slug"
|
|
116
|
+
return 0
|
|
117
|
+
fi
|
|
118
|
+
local shortened="${slug:0:max_len}"
|
|
119
|
+
shortened="$(printf '%s' "$shortened" | sed -E 's/-+$//')"
|
|
120
|
+
if [[ -z "$shortened" ]]; then
|
|
121
|
+
shortened="${slug:0:max_len}"
|
|
122
|
+
fi
|
|
123
|
+
printf '%s' "$shortened"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
checksum_slug_suffix() {
|
|
127
|
+
local raw="$1"
|
|
128
|
+
local checksum
|
|
129
|
+
checksum="$(printf '%s' "$raw" | cksum | awk '{print $1}')"
|
|
130
|
+
printf '%s' "${checksum:0:6}"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
compose_branch_descriptor() {
|
|
134
|
+
local snapshot_slug="$1"
|
|
135
|
+
local task_slug="$2"
|
|
136
|
+
local snapshot_max task_max task_part snapshot_part checksum_input checksum_part
|
|
137
|
+
snapshot_max="$(normalize_positive_int "${GUARDEX_BRANCH_SNAPSHOT_SLUG_MAX:-18}" "18")"
|
|
138
|
+
task_max="$(normalize_positive_int "${GUARDEX_BRANCH_TASK_SLUG_MAX:-36}" "36")"
|
|
139
|
+
task_part="$(shorten_slug "$task_slug" "$task_max")"
|
|
140
|
+
if [[ -n "$snapshot_slug" ]]; then
|
|
141
|
+
snapshot_part="$(shorten_slug "$snapshot_slug" "$snapshot_max")"
|
|
142
|
+
checksum_input="${snapshot_slug}--${task_slug}"
|
|
143
|
+
checksum_part="$(checksum_slug_suffix "$checksum_input")"
|
|
144
|
+
printf '%s-%s-%s' "$snapshot_part" "$task_part" "$checksum_part"
|
|
145
|
+
return 0
|
|
146
|
+
fi
|
|
147
|
+
checksum_part="$(checksum_slug_suffix "$task_slug")"
|
|
148
|
+
printf '%s-%s' "$task_part" "$checksum_part"
|
|
149
|
+
}
|
|
150
|
+
|
|
89
151
|
normalize_bool() {
|
|
90
152
|
local raw="${1:-}"
|
|
91
153
|
local fallback="${2:-0}"
|
|
@@ -131,13 +193,13 @@ resolve_openspec_capability_slug() {
|
|
|
131
193
|
}
|
|
132
194
|
|
|
133
195
|
resolve_active_codex_snapshot_name() {
|
|
134
|
-
local override="${
|
|
196
|
+
local override="${GUARDEX_CODEX_AUTH_SNAPSHOT:-}"
|
|
135
197
|
if [[ -n "$override" ]]; then
|
|
136
198
|
printf '%s' "$override"
|
|
137
199
|
return 0
|
|
138
200
|
fi
|
|
139
201
|
|
|
140
|
-
local codex_auth_bin="${
|
|
202
|
+
local codex_auth_bin="${GUARDEX_CODEX_AUTH_BIN:-codex-auth}"
|
|
141
203
|
if ! command -v "$codex_auth_bin" >/dev/null 2>&1; then
|
|
142
204
|
return 0
|
|
143
205
|
fi
|
|
@@ -165,7 +227,7 @@ has_local_changes() {
|
|
|
165
227
|
resolve_protected_branches() {
|
|
166
228
|
local root="$1"
|
|
167
229
|
local raw
|
|
168
|
-
raw="${
|
|
230
|
+
raw="${GUARDEX_PROTECTED_BRANCHES:-$(git -C "$root" config --get multiagent.protectedBranches || true)}"
|
|
169
231
|
if [[ -z "$raw" ]]; then
|
|
170
232
|
raw="dev main master"
|
|
171
233
|
fi
|
|
@@ -350,15 +412,13 @@ else
|
|
|
350
412
|
fi
|
|
351
413
|
|
|
352
414
|
task_slug="$(sanitize_slug "$TASK_NAME" "task")"
|
|
353
|
-
|
|
415
|
+
agent_slug_raw="$(sanitize_slug "$AGENT_NAME" "agent")"
|
|
416
|
+
agent_slug="$(shorten_slug "$agent_slug_raw" "${GUARDEX_BRANCH_AGENT_SLUG_MAX:-24}")"
|
|
354
417
|
snapshot_name="$(resolve_active_codex_snapshot_name)"
|
|
355
|
-
snapshot_slug="$(
|
|
418
|
+
snapshot_slug="$(sanitize_optional_slug "$snapshot_name" "snapshot")"
|
|
419
|
+
branch_descriptor="$(compose_branch_descriptor "$snapshot_slug" "$task_slug")"
|
|
356
420
|
timestamp="$(date +%Y%m%d-%H%M%S)"
|
|
357
|
-
|
|
358
|
-
branch_name_base="agent/${agent_slug}/${snapshot_slug}-${task_slug}"
|
|
359
|
-
else
|
|
360
|
-
branch_name_base="agent/${agent_slug}/${task_slug}"
|
|
361
|
-
fi
|
|
421
|
+
branch_name_base="agent/${agent_slug}/${branch_descriptor}"
|
|
362
422
|
|
|
363
423
|
branch_name="$branch_name_base"
|
|
364
424
|
branch_suffix=2
|
|
@@ -386,7 +446,7 @@ current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null ||
|
|
|
386
446
|
protected_branches_raw="$(resolve_protected_branches "$repo_root")"
|
|
387
447
|
if [[ -n "$current_branch" && "$current_branch" != "HEAD" ]] && is_protected_branch_name "$current_branch" "$protected_branches_raw"; then
|
|
388
448
|
if has_local_changes "$repo_root"; then
|
|
389
|
-
auto_transfer_message="
|
|
449
|
+
auto_transfer_message="guardex-auto-transfer-${timestamp}-${agent_slug}-${task_slug}"
|
|
390
450
|
if git -C "$repo_root" stash push --include-untracked --message "$auto_transfer_message" >/dev/null 2>&1; then
|
|
391
451
|
auto_transfer_stash_ref="$(
|
|
392
452
|
git -C "$repo_root" stash list \
|
|
@@ -401,7 +461,7 @@ if [[ -n "$current_branch" && "$current_branch" != "HEAD" ]] && is_protected_bra
|
|
|
401
461
|
fi
|
|
402
462
|
|
|
403
463
|
git -C "$repo_root" worktree add -b "$branch_name" "$worktree_path" "$start_ref"
|
|
404
|
-
git -C "$repo_root" config "branch.${branch_name}.
|
|
464
|
+
git -C "$repo_root" config "branch.${branch_name}.guardexBase" "$BASE_BRANCH" >/dev/null 2>&1 || true
|
|
405
465
|
|
|
406
466
|
if git -C "$repo_root" show-ref --verify --quiet "refs/remotes/origin/${BASE_BRANCH}"; then
|
|
407
467
|
git -C "$worktree_path" branch --set-upstream-to="origin/${BASE_BRANCH}" "$branch_name" >/dev/null 2>&1 || true
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
BASE_BRANCH="${
|
|
4
|
+
BASE_BRANCH="${GUARDEX_BASE_BRANCH:-}"
|
|
5
5
|
BASE_BRANCH_EXPLICIT=0
|
|
6
6
|
DRY_RUN=0
|
|
7
7
|
FORCE_DIRTY=0
|
|
@@ -10,7 +10,7 @@ DELETE_REMOTE_BRANCHES=0
|
|
|
10
10
|
ONLY_DIRTY_WORKTREES=0
|
|
11
11
|
TARGET_BRANCH=""
|
|
12
12
|
IDLE_MINUTES=0
|
|
13
|
-
NOW_EPOCH_RAW="${
|
|
13
|
+
NOW_EPOCH_RAW="${GUARDEX_PRUNE_NOW_EPOCH:-}"
|
|
14
14
|
IDLE_SECONDS=0
|
|
15
15
|
NOW_EPOCH=0
|
|
16
16
|
|
|
@@ -117,7 +117,7 @@ if [[ ! "$IDLE_MINUTES" =~ ^[0-9]+$ ]]; then
|
|
|
117
117
|
fi
|
|
118
118
|
|
|
119
119
|
if [[ -n "$NOW_EPOCH_RAW" && ! "$NOW_EPOCH_RAW" =~ ^[0-9]+$ ]]; then
|
|
120
|
-
echo "[agent-worktree-prune]
|
|
120
|
+
echo "[agent-worktree-prune] GUARDEX_PRUNE_NOW_EPOCH must be a unix timestamp integer." >&2
|
|
121
121
|
exit 1
|
|
122
122
|
fi
|
|
123
123
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
TASK_NAME="${
|
|
5
|
-
AGENT_NAME="${
|
|
6
|
-
BASE_BRANCH="${
|
|
4
|
+
TASK_NAME="${GUARDEX_TASK_NAME:-task}"
|
|
5
|
+
AGENT_NAME="${GUARDEX_AGENT_NAME:-agent}"
|
|
6
|
+
BASE_BRANCH="${GUARDEX_BASE_BRANCH:-}"
|
|
7
7
|
BASE_BRANCH_EXPLICIT=0
|
|
8
|
-
CODEX_BIN="${
|
|
9
|
-
AUTO_FINISH_RAW="${
|
|
10
|
-
AUTO_REVIEW_ON_CONFLICT_RAW="${
|
|
11
|
-
AUTO_CLEANUP_RAW="${
|
|
12
|
-
AUTO_WAIT_FOR_MERGE_RAW="${
|
|
13
|
-
OPENSPEC_AUTO_INIT_RAW="${
|
|
14
|
-
OPENSPEC_PLAN_SLUG_OVERRIDE="${
|
|
15
|
-
OPENSPEC_CHANGE_SLUG_OVERRIDE="${
|
|
16
|
-
OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${
|
|
8
|
+
CODEX_BIN="${GUARDEX_CODEX_BIN:-codex}"
|
|
9
|
+
AUTO_FINISH_RAW="${GUARDEX_CODEX_AUTO_FINISH:-true}"
|
|
10
|
+
AUTO_REVIEW_ON_CONFLICT_RAW="${GUARDEX_CODEX_AUTO_REVIEW_ON_CONFLICT:-true}"
|
|
11
|
+
AUTO_CLEANUP_RAW="${GUARDEX_CODEX_AUTO_CLEANUP:-true}"
|
|
12
|
+
AUTO_WAIT_FOR_MERGE_RAW="${GUARDEX_CODEX_WAIT_FOR_MERGE:-true}"
|
|
13
|
+
OPENSPEC_AUTO_INIT_RAW="${GUARDEX_OPENSPEC_AUTO_INIT:-true}"
|
|
14
|
+
OPENSPEC_PLAN_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_PLAN_SLUG:-}"
|
|
15
|
+
OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
|
|
16
|
+
OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
|
|
17
17
|
|
|
18
18
|
normalize_bool() {
|
|
19
19
|
local raw="${1:-}"
|
|
@@ -275,7 +275,7 @@ start_sandbox_fallback() {
|
|
|
275
275
|
fi
|
|
276
276
|
|
|
277
277
|
git -C "$repo_root" worktree add -b "$branch_name" "$worktree_path" "$start_ref" >/dev/null
|
|
278
|
-
git -C "$repo_root" config "branch.${branch_name}.
|
|
278
|
+
git -C "$repo_root" config "branch.${branch_name}.guardexBase" "$base_branch" >/dev/null 2>&1 || true
|
|
279
279
|
if git -C "$repo_root" show-ref --verify --quiet "refs/remotes/origin/${base_branch}"; then
|
|
280
280
|
git -C "$worktree_path" branch --set-upstream-to="origin/${base_branch}" "$branch_name" >/dev/null 2>&1 || true
|
|
281
281
|
fi
|
|
@@ -298,7 +298,7 @@ initial_repo_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/nu
|
|
|
298
298
|
start_output=""
|
|
299
299
|
start_status=0
|
|
300
300
|
set +e
|
|
301
|
-
start_output="$(
|
|
301
|
+
start_output="$(GUARDEX_OPENSPEC_AUTO_INIT=0 bash "${repo_root}/scripts/agent-branch-start.sh" "${start_args[@]}" 2>&1)"
|
|
302
302
|
start_status=$?
|
|
303
303
|
set -e
|
|
304
304
|
|
|
@@ -548,7 +548,7 @@ auto_commit_worktree_changes() {
|
|
|
548
548
|
fi
|
|
549
549
|
|
|
550
550
|
local default_message="Auto-finish: ${TASK_NAME}"
|
|
551
|
-
local commit_message="${
|
|
551
|
+
local commit_message="${GUARDEX_CODEX_AUTO_COMMIT_MESSAGE:-$default_message}"
|
|
552
552
|
local commit_output=""
|
|
553
553
|
|
|
554
554
|
if commit_output="$(git -C "$wt" commit -m "$commit_message" 2>&1)"; then
|
|
@@ -662,8 +662,8 @@ run_finish_flow() {
|
|
|
662
662
|
fi
|
|
663
663
|
|
|
664
664
|
if has_origin_remote; then
|
|
665
|
-
if ! command -v "${
|
|
666
|
-
echo "[codex-agent] Auto-finish requires GitHub CLI for PR flow; command not found: ${
|
|
665
|
+
if ! command -v "${GUARDEX_GH_BIN:-gh}" >/dev/null 2>&1 && ! command -v gh >/dev/null 2>&1; then
|
|
666
|
+
echo "[codex-agent] Auto-finish requires GitHub CLI for PR flow; command not found: ${GUARDEX_GH_BIN:-gh}" >&2
|
|
667
667
|
return 2
|
|
668
668
|
fi
|
|
669
669
|
finish_args+=(--via-pr)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
INTERVAL_SECONDS="${
|
|
5
|
-
AGENT_NAME="${
|
|
6
|
-
TASK_PREFIX="${
|
|
7
|
-
STATE_FILE="${
|
|
8
|
-
BASE_BRANCH="${
|
|
9
|
-
ONLY_PR="${
|
|
10
|
-
RETRY_FAILED_RAW="${
|
|
11
|
-
INCLUDE_DRAFT_RAW="${
|
|
4
|
+
INTERVAL_SECONDS="${GUARDEX_REVIEW_BOT_INTERVAL_SECONDS:-30}"
|
|
5
|
+
AGENT_NAME="${GUARDEX_REVIEW_BOT_AGENT_NAME:-guardex-review-bot}"
|
|
6
|
+
TASK_PREFIX="${GUARDEX_REVIEW_BOT_TASK_PREFIX:-review-merge}"
|
|
7
|
+
STATE_FILE="${GUARDEX_REVIEW_BOT_STATE_FILE:-}"
|
|
8
|
+
BASE_BRANCH="${GUARDEX_REVIEW_BOT_BASE_BRANCH:-}"
|
|
9
|
+
ONLY_PR="${GUARDEX_REVIEW_BOT_ONLY_PR:-}"
|
|
10
|
+
RETRY_FAILED_RAW="${GUARDEX_REVIEW_BOT_RETRY_FAILED:-false}"
|
|
11
|
+
INCLUDE_DRAFT_RAW="${GUARDEX_REVIEW_BOT_INCLUDE_DRAFT:-false}"
|
|
12
12
|
|
|
13
13
|
usage() {
|
|
14
14
|
cat <<'USAGE'
|
|
@@ -30,7 +30,7 @@ Options:
|
|
|
30
30
|
-h, --help Show this help
|
|
31
31
|
|
|
32
32
|
Environment overrides:
|
|
33
|
-
|
|
33
|
+
GUARDEX_REVIEW_BOT_PROMPT_APPEND Additional instructions appended to each Codex prompt
|
|
34
34
|
USAGE
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -213,8 +213,8 @@ Strict task:
|
|
|
213
213
|
5) Do not touch unrelated PRs.
|
|
214
214
|
PROMPT
|
|
215
215
|
|
|
216
|
-
if [[ -n "${
|
|
217
|
-
printf '\n%s\n' "${
|
|
216
|
+
if [[ -n "${GUARDEX_REVIEW_BOT_PROMPT_APPEND:-}" ]]; then
|
|
217
|
+
printf '\n%s\n' "${GUARDEX_REVIEW_BOT_PROMPT_APPEND}"
|
|
218
218
|
fi
|
|
219
219
|
}
|
|
220
220
|
|