@imdeadpool/guardex 7.0.2 → 7.0.4

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 CHANGED
@@ -373,6 +373,28 @@ npm pack --dry-run
373
373
 
374
374
  ## Release notes
375
375
 
376
+ ### v7.0.4
377
+
378
+ - **Fixed: publish collision on npm.** Advanced the package metadata from `7.0.3` to `7.0.4` so `npm publish` no longer targets an already published version.
379
+ - **Changed: release-note sync for versioning rule.** Added this versioned entry in README in the same change as the package bump to keep publish metadata and release notes aligned.
380
+
381
+ ### v7.0.3
382
+
383
+ - **Branch/worktree naming refactor.** `agent-branch-start.sh` now produces `agent/<role>/<task>-<YYYY-MM-DD>-<HH-MM>` instead of `agent/<role+account-email>/<snapshot-slug>-<task>-<cksum6>`. Codex account names (e.g. `Zeus Edix Hu`) and 6-hex checksums no longer leak into branch or worktree paths.
384
+ - **Role normalization.** `AGENT_NAME` is collapsed to `{claude, codex, <explicit>}` via (in order) the `GUARDEX_AGENT_TYPE` env override, a substring match against `claude`/`codex`, the `CLAUDECODE=1` sentinel, or a fallback to `codex`. Other roles (`integrator`, `executor`, etc.) pass through when set via `GUARDEX_AGENT_TYPE`.
385
+ - **New `--print-name-only` flag** on `agent-branch-start.sh` for deterministic tests; honours `GUARDEX_BRANCH_TIMESTAMP` for reproducible output.
386
+ - **`--tier` flag accepted silently** for CLAUDE.md compatibility (scaffold sizing not wired through yet).
387
+ - Tests `install.test.js` covering the old snapshot-slug format were rewritten to assert the new role-datetime shape.
388
+
389
+ ### v7.0.2
390
+
391
+ - **Fix: `__source-probe-*` worktree leak on conflict exit.** `agent-branch-finish.sh` was registering its `cleanup()` trap *after* the sync-guard rebase block, so when that rebase hit conflicts and the script exited, the throwaway probe worktree was never removed. `gx doctor` sweeps against stalled branches accumulated one new probe per run.
392
+ - The cleanup trap is now installed immediately after probe creation, and aborts any in-progress `rebase`/`merge` before `worktree remove --force` so conflict-stuck probes are cleaned up reliably.
393
+
394
+ ### v7.0.1
395
+
396
+ - Maintenance release.
397
+
376
398
  ### v7.0.0
377
399
 
378
400
  - **Breaking (soft).** Consolidated 17 commands into 12 visible commands with flag-based subcommands. Five removed names (`init`, `install`, `fix`, `scan`, `copy-prompt`, `copy-commands`, `print-agents-snippet`, `review`) still work but print a one-line deprecation notice on stderr and will be removed in v8. See the migration table in "Copy-paste: common commands" above.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imdeadpool/guardex",
3
- "version": "7.0.2",
3
+ "version": "7.0.4",
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,
@@ -23,6 +23,14 @@ if [[ -n "${CODEX_THREAD_ID:-}" || -n "${OMX_SESSION_ID:-}" || "${CODEX_CI:-0}"
23
23
  is_codex_session=1
24
24
  fi
25
25
 
26
+ # Superset of is_codex_session that also covers Claude Code sessions so the
27
+ # protected-branch gate below only triggers for automated agents — humans stay
28
+ # free to commit directly on main/dev/master.
29
+ is_agent_session=$is_codex_session
30
+ if [[ -n "${CLAUDECODE:-}" || -n "${CLAUDE_CODE_SESSION_ID:-}" ]]; then
31
+ is_agent_session=1
32
+ fi
33
+
26
34
  is_vscode_git_context=0
27
35
  if [[ -n "${VSCODE_GIT_IPC_HANDLE:-}" || -n "${VSCODE_GIT_ASKPASS_NODE:-}" || -n "${VSCODE_IPC_HOOK_CLI:-}" ]]; then
28
36
  is_vscode_git_context=1
@@ -124,10 +132,10 @@ MSG
124
132
  fi
125
133
 
126
134
  if [[ "$is_protected_branch" == "1" ]]; then
127
- if [[ "$is_codex_session" != "1" && "$is_vscode_git_context" == "1" ]]; then
128
- if [[ "$allow_vscode_protected_branch_writes" == "1" ]]; then
129
- exit 0
130
- fi
135
+ # Humans may commit directly on protected branches; only agent sessions
136
+ # (Codex / Claude Code / OMX) are blocked.
137
+ if [[ "$is_agent_session" != "1" ]]; then
138
+ exit 0
131
139
  fi
132
140
 
133
141
  if [[ "$is_unborn_branch" == "1" && "$is_codex_session" != "1" ]]; then
@@ -146,16 +154,13 @@ Use an agent branch first:
146
154
  After finishing work:
147
155
  bash scripts/agent-branch-finish.sh
148
156
 
149
- Optional repo opt-in for VS Code protected-branch commits:
150
- git config multiagent.allowVscodeProtectedBranchWrites true
151
-
152
157
  Temporary bypass (not recommended):
153
158
  ALLOW_COMMIT_ON_PROTECTED_BRANCH=1 git commit ...
154
159
  MSG
155
160
  exit 1
156
161
  fi
157
162
 
158
- if [[ "$is_agent_context" == "1" && "$branch" != agent/* ]]; then
163
+ if [[ "$is_agent_session" == "1" && "$branch" != agent/* ]]; then
159
164
  cat >&2 <<'MSG'
160
165
  [agent-branch-guard] Agent commits must run on dedicated agent/* branches.
161
166
  Start an agent branch first:
@@ -28,6 +28,13 @@ if [[ -n "${CODEX_THREAD_ID:-}" || -n "${OMX_SESSION_ID:-}" || "${CODEX_CI:-0}"
28
28
  is_codex_session=1
29
29
  fi
30
30
 
31
+ # Superset covering Claude Code so only agents are blocked from pushing to
32
+ # protected refs; humans push directly from their primary checkout.
33
+ is_agent_session=$is_codex_session
34
+ if [[ -n "${CLAUDECODE:-}" || -n "${CLAUDE_CODE_SESSION_ID:-}" ]]; then
35
+ is_agent_session=1
36
+ fi
37
+
31
38
  protected_branches_raw="${GUARDEX_PROTECTED_BRANCHES:-$(git config --get multiagent.protectedBranches || true)}"
32
39
  if [[ -z "$protected_branches_raw" ]]; then
33
40
  protected_branches_raw="dev main master"
@@ -69,6 +76,11 @@ if [[ "${#blocked_refs[@]}" -gt 0 ]]; then
69
76
  exit 1
70
77
  fi
71
78
 
79
+ # Humans may push directly to protected branches; only agent sessions are blocked.
80
+ if [[ "$is_agent_session" != "1" ]]; then
81
+ exit 0
82
+ fi
83
+
72
84
  if [[ "$is_vscode_git_context" == "1" && "$allow_vscode_protected_branch_writes" == "1" ]]; then
73
85
  exit 0
74
86
  fi
@@ -10,6 +10,7 @@ OPENSPEC_AUTO_INIT_RAW="${GUARDEX_OPENSPEC_AUTO_INIT:-false}"
10
10
  OPENSPEC_PLAN_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_PLAN_SLUG:-}"
11
11
  OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
12
12
  OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
13
+ PRINT_NAME_ONLY=0
13
14
  POSITIONAL_ARGS=()
14
15
 
15
16
  while [[ $# -gt 0 ]]; do
@@ -27,6 +28,15 @@ while [[ $# -gt 0 ]]; do
27
28
  BASE_BRANCH_EXPLICIT=1
28
29
  shift 2
29
30
  ;;
31
+ --print-name-only)
32
+ PRINT_NAME_ONLY=1
33
+ shift
34
+ ;;
35
+ --tier)
36
+ # Accepted for CLAUDE.md compatibility; scaffold size is not yet wired
37
+ # through this script. Consume the value so callers can pass it.
38
+ shift 2
39
+ ;;
30
40
  --in-place|--allow-in-place)
31
41
  echo "[agent-branch-start] In-place branch mode is disabled." >&2
32
42
  echo "[agent-branch-start] This command always creates an isolated worktree to keep your active checkout unchanged." >&2
@@ -46,7 +56,7 @@ while [[ $# -gt 0 ]]; do
46
56
  ;;
47
57
  -*)
48
58
  echo "[agent-branch-start] Unknown option: $1" >&2
49
- echo "Usage: $0 [task] [agent] [base] [--worktree-root <path>]" >&2
59
+ echo "Usage: $0 [task] [agent] [base] [--worktree-root <path>] [--print-name-only]" >&2
50
60
  exit 1
51
61
  ;;
52
62
  *)
@@ -86,16 +96,6 @@ sanitize_slug() {
86
96
  printf '%s' "$slug"
87
97
  }
88
98
 
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
99
  normalize_positive_int() {
100
100
  local raw="$1"
101
101
  local fallback="$2"
@@ -123,29 +123,58 @@ shorten_slug() {
123
123
  printf '%s' "$shortened"
124
124
  }
125
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}"
126
+ # Collapse arbitrary agent identifiers to a clean role token: claude | codex |
127
+ # <other-kebab>. Priority: GUARDEX_AGENT_TYPE env override, then the raw
128
+ # AGENT_NAME (if it contains 'claude' or 'codex'), then CLAUDECODE=1 sentinel
129
+ # (set by Claude Code CLI), else fall back to 'codex'. Any other role name
130
+ # (integrator, executor, rust-port, etc.) is preserved as-is after slug
131
+ # sanitization.
132
+ normalize_role() {
133
+ local raw_agent="$1"
134
+ local override="${GUARDEX_AGENT_TYPE:-}"
135
+ if [[ -n "$override" ]]; then
136
+ sanitize_slug "$override" "agent"
137
+ return 0
138
+ fi
139
+ local lowered
140
+ lowered="$(printf '%s' "$raw_agent" | tr '[:upper:]' '[:lower:]')"
141
+ if [[ "$lowered" == *claude* ]]; then
142
+ printf 'claude'
143
+ return 0
144
+ fi
145
+ if [[ "$lowered" == *codex* ]]; then
146
+ printf 'codex'
147
+ return 0
148
+ fi
149
+ if [[ -n "${CLAUDECODE:-}" && "${CLAUDECODE}" != "0" && "${CLAUDECODE}" != "false" ]]; then
150
+ printf 'claude'
151
+ return 0
152
+ fi
153
+ # Unrecognized raw name (rust-port-lead, some-worker, empty, ...): default to
154
+ # codex. To get a different role (integrator, executor, ...) pass the role
155
+ # explicitly via GUARDEX_AGENT_TYPE, handled above.
156
+ printf 'codex'
131
157
  }
132
158
 
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"
159
+ # Timestamp the branch/worktree/openspec slug so parallel agents never collide
160
+ # and names sort chronologically. Format: YYYY-MM-DD-HH-MM (local time).
161
+ # Colons are illegal in git refs, so the HH:MM the user sees is stored as
162
+ # HH-MM in the slug. Can be overridden for tests via GUARDEX_BRANCH_TIMESTAMP.
163
+ compose_branch_timestamp() {
164
+ if [[ -n "${GUARDEX_BRANCH_TIMESTAMP:-}" ]]; then
165
+ printf '%s' "$GUARDEX_BRANCH_TIMESTAMP"
145
166
  return 0
146
167
  fi
147
- checksum_part="$(checksum_slug_suffix "$task_slug")"
148
- printf '%s-%s' "$task_part" "$checksum_part"
168
+ date +%Y-%m-%d-%H-%M
169
+ }
170
+
171
+ compose_branch_descriptor() {
172
+ local task_slug="$1"
173
+ local stamp="$2"
174
+ local task_max task_part
175
+ task_max="$(normalize_positive_int "${GUARDEX_BRANCH_TASK_SLUG_MAX:-40}" "40")"
176
+ task_part="$(shorten_slug "$task_slug" "$task_max")"
177
+ printf '%s-%s' "$task_part" "$stamp"
149
178
  }
150
179
 
151
180
  normalize_bool() {
@@ -192,24 +221,6 @@ resolve_openspec_capability_slug() {
192
221
  sanitize_slug "$task_slug" "general-behavior"
193
222
  }
194
223
 
195
- resolve_active_codex_snapshot_name() {
196
- local override="${GUARDEX_CODEX_AUTH_SNAPSHOT:-}"
197
- if [[ -n "$override" ]]; then
198
- printf '%s' "$override"
199
- return 0
200
- fi
201
-
202
- local codex_auth_bin="${GUARDEX_CODEX_AUTH_BIN:-codex-auth}"
203
- if ! command -v "$codex_auth_bin" >/dev/null 2>&1; then
204
- return 0
205
- fi
206
-
207
- "$codex_auth_bin" list 2>/dev/null \
208
- | sed -n 's/^[[:space:]]*\*[[:space:]]\+//p' \
209
- | head -n 1 \
210
- | tr -d '\r' || true
211
- }
212
-
213
224
  has_local_changes() {
214
225
  local root="$1"
215
226
  if ! git -C "$root" diff --quiet; then
@@ -383,6 +394,18 @@ if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -z "$BASE_BRANCH" ]]; then
383
394
  exit 1
384
395
  fi
385
396
 
397
+ task_slug="$(sanitize_slug "$TASK_NAME" "task")"
398
+ agent_slug="$(normalize_role "$AGENT_NAME")"
399
+ branch_timestamp="$(compose_branch_timestamp)"
400
+ branch_descriptor="$(compose_branch_descriptor "$task_slug" "$branch_timestamp")"
401
+ branch_name_base="agent/${agent_slug}/${branch_descriptor}"
402
+
403
+ branch_name="$branch_name_base"
404
+ if [[ "$PRINT_NAME_ONLY" -eq 1 ]]; then
405
+ printf '%s\n' "$branch_name"
406
+ exit 0
407
+ fi
408
+
386
409
  if [[ "$BASE_BRANCH_EXPLICIT" -eq 0 ]]; then
387
410
  current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
388
411
  protected_branches_raw="$(resolve_protected_branches "$repo_root")"
@@ -411,16 +434,7 @@ else
411
434
  start_ref="${BASE_BRANCH}"
412
435
  fi
413
436
 
414
- task_slug="$(sanitize_slug "$TASK_NAME" "task")"
415
- agent_slug_raw="$(sanitize_slug "$AGENT_NAME" "agent")"
416
- agent_slug="$(shorten_slug "$agent_slug_raw" "${GUARDEX_BRANCH_AGENT_SLUG_MAX:-24}")"
417
- snapshot_name="$(resolve_active_codex_snapshot_name)"
418
- snapshot_slug="$(sanitize_optional_slug "$snapshot_name" "snapshot")"
419
- branch_descriptor="$(compose_branch_descriptor "$snapshot_slug" "$task_slug")"
420
437
  timestamp="$(date +%Y%m%d-%H%M%S)"
421
- branch_name_base="agent/${agent_slug}/${branch_descriptor}"
422
-
423
- branch_name="$branch_name_base"
424
438
  branch_suffix=2
425
439
  while git show-ref --verify --quiet "refs/heads/${branch_name}"; do
426
440
  branch_name="${branch_name_base}-${branch_suffix}"