@memnexus-ai/mx-agent-cli 0.1.166 → 0.1.167

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.
Files changed (96) hide show
  1. package/dist/__tests__/claude-sync.test.js +15 -0
  2. package/dist/__tests__/claude-sync.test.js.map +1 -1
  3. package/dist/__tests__/config-generation.test.d.ts +7 -0
  4. package/dist/__tests__/config-generation.test.d.ts.map +1 -0
  5. package/dist/__tests__/config-generation.test.js +141 -0
  6. package/dist/__tests__/config-generation.test.js.map +1 -0
  7. package/dist/__tests__/config-sync.test.d.ts +10 -0
  8. package/dist/__tests__/config-sync.test.d.ts.map +1 -0
  9. package/dist/__tests__/config-sync.test.js +180 -0
  10. package/dist/__tests__/config-sync.test.js.map +1 -0
  11. package/dist/__tests__/project-config.test.js +1 -0
  12. package/dist/__tests__/project-config.test.js.map +1 -1
  13. package/dist/agent-config/.mx-config-generation.json +4 -0
  14. package/dist/agent-config/CLAUDE.md.template +140 -0
  15. package/dist/agent-config/OWNERS.md +51 -0
  16. package/dist/agent-config/agents/bar-raiser.md +205 -0
  17. package/dist/agent-config/agents/dogfood-auditor.md +243 -0
  18. package/dist/agent-config/agents/git-expert.md +380 -0
  19. package/dist/agent-config/agents/implementation-engineer.md +387 -0
  20. package/dist/agent-config/agents/memory-creator.md +330 -0
  21. package/dist/agent-config/agents/prd-writer.md +478 -0
  22. package/dist/agent-config/agents/prfaq-writer.md +344 -0
  23. package/dist/agent-config/agents/prior-art-researcher.md +264 -0
  24. package/dist/agent-config/agents/qa-tester.md +406 -0
  25. package/dist/agent-config/agents/red-team.md +330 -0
  26. package/dist/agent-config/agents/security-reviewer.md +300 -0
  27. package/dist/agent-config/agents/status-reporter.md +297 -0
  28. package/dist/agent-config/claude-md/CLAUDE.core.md +105 -0
  29. package/dist/agent-config/claude-md/CLAUDE.overlay.memnexus.md +68 -0
  30. package/dist/agent-config/claude-md/compose.mjs +179 -0
  31. package/dist/agent-config/claude-md/vars.memnexus.json +4 -0
  32. package/dist/agent-config/hooks/auto-capture-precompact.sh +176 -0
  33. package/dist/agent-config/hooks/auto-capture-session-end.sh +178 -0
  34. package/dist/agent-config/hooks/auto-checkpoint.sh +102 -0
  35. package/dist/agent-config/hooks/ci-validate-hook.sh +499 -0
  36. package/dist/agent-config/hooks/ci-validate-routes.conf +16 -0
  37. package/dist/agent-config/hooks/delegation-audit.sh +61 -0
  38. package/dist/agent-config/hooks/deploy-verification-reminder.sh +179 -0
  39. package/dist/agent-config/hooks/git-mutation-guard.sh +219 -0
  40. package/dist/agent-config/hooks/iteration-log-check.sh +212 -0
  41. package/dist/agent-config/hooks/memory-checkpoint-reminder.sh +103 -0
  42. package/dist/agent-config/hooks/mx-guard-hook.sh +77 -0
  43. package/dist/agent-config/hooks/named-memory-write-guard.sh +237 -0
  44. package/dist/agent-config/hooks/reload-checkpoint.sh +68 -0
  45. package/dist/agent-config/hooks/set-terminal-appearance.sh +34 -0
  46. package/dist/agent-config/hooks/workflow-dispatch-guard.sh +184 -0
  47. package/dist/agent-config/hooks/worktree-guard.sh +258 -0
  48. package/dist/agent-config/rules/eval-harness.md +33 -0
  49. package/dist/agent-config/rules/frontend.md +17 -0
  50. package/dist/agent-config/rules/mcp-protocol.md +15 -0
  51. package/dist/agent-config/rules/retrieval.md +17 -0
  52. package/dist/agent-config/settings.json +196 -0
  53. package/dist/agent-config/shared/eval-tiered-rigor.md +15 -0
  54. package/dist/agent-config/shared/team-operating-rules.md +37 -0
  55. package/dist/agent-config/skills/eval-claim-impact/SKILL.md +20 -0
  56. package/dist/agent-config/skills/eval-harness-review/SKILL.md +21 -0
  57. package/dist/agent-config/skills/mx-checkpoint/SKILL.md +111 -0
  58. package/dist/agent-config/skills/mx-fix-pr/SKILL.md +90 -0
  59. package/dist/agent-config/skills/mx-gap-assessment/SKILL.md +90 -0
  60. package/dist/agent-config/skills/mx-iteration-close/SKILL.md +81 -0
  61. package/dist/agent-config/skills/mx-post-merge-sync/SKILL.md +83 -0
  62. package/dist/agent-config/skills/mx-pre-pr-check/SKILL.md +85 -0
  63. package/dist/agent-config/skills/mx-save/SKILL.md +102 -0
  64. package/dist/agent-config/skills/mx-session-start/SKILL.md +85 -0
  65. package/dist/agent-config/skills/team-gate-checklist/SKILL.md +19 -0
  66. package/dist/commands/config-sync.d.ts +39 -0
  67. package/dist/commands/config-sync.d.ts.map +1 -0
  68. package/dist/commands/config-sync.js +212 -0
  69. package/dist/commands/config-sync.js.map +1 -0
  70. package/dist/commands/create.d.ts.map +1 -1
  71. package/dist/commands/create.js +3 -0
  72. package/dist/commands/create.js.map +1 -1
  73. package/dist/commands/init.d.ts.map +1 -1
  74. package/dist/commands/init.js +12 -4
  75. package/dist/commands/init.js.map +1 -1
  76. package/dist/commands/start.d.ts.map +1 -1
  77. package/dist/commands/start.js +6 -0
  78. package/dist/commands/start.js.map +1 -1
  79. package/dist/index.js +23 -0
  80. package/dist/index.js.map +1 -1
  81. package/dist/lib/bundled-config.d.ts +44 -0
  82. package/dist/lib/bundled-config.d.ts.map +1 -0
  83. package/dist/lib/bundled-config.js +164 -0
  84. package/dist/lib/bundled-config.js.map +1 -0
  85. package/dist/lib/claude.d.ts.map +1 -1
  86. package/dist/lib/claude.js +9 -3
  87. package/dist/lib/claude.js.map +1 -1
  88. package/dist/lib/config-generation.d.ts +36 -0
  89. package/dist/lib/config-generation.d.ts.map +1 -0
  90. package/dist/lib/config-generation.js +73 -0
  91. package/dist/lib/config-generation.js.map +1 -0
  92. package/dist/lib/project-config.d.ts +7 -0
  93. package/dist/lib/project-config.d.ts.map +1 -1
  94. package/dist/lib/project-config.js +4 -0
  95. package/dist/lib/project-config.js.map +1 -1
  96. package/package.json +2 -2
@@ -0,0 +1,184 @@
1
+ #!/bin/bash
2
+ # workflow-dispatch-guard.sh — PreToolUse hook (matcher: Bash)
3
+ # Blocks spurious `gh workflow run` calls when the reason CI didn't trigger
4
+ # is simply path filters — the changed files don't match the workflow's paths.
5
+ #
6
+ # Agents frequently misdiagnose path-filtered skips as skip-duplicate-actions
7
+ # deduplication and respond by manually dispatching the workflow. This is
8
+ # almost always wrong and produces spurious CI runs on unchanged packages.
9
+ #
10
+ # Logic:
11
+ # 1. Intercept `gh workflow run <name>` commands
12
+ # 2. Read the workflow YAML to extract on.push.paths / on.pull_request.paths
13
+ # 3. Get changed files vs origin/main
14
+ # 4. If no changed files match the workflow paths → block with explanation
15
+ # 5. If paths match, no path filters defined, or anything is uncertain → allow
16
+ #
17
+ # Deployed by syncClaudeConfig() via mx-agent-system/agent-config/hooks/.
18
+ #
19
+ # Exit codes:
20
+ # 0 = allow
21
+ # 2 = block (path filters explain why CI didn't trigger — dispatch not needed)
22
+
23
+ # Fail-open: any unexpected error → allow the command through
24
+ trap 'exit 0' ERR
25
+
26
+ INPUT=$(cat)
27
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
28
+
29
+ # Only intercept gh workflow run
30
+ if [[ ! "$COMMAND" =~ ^gh\ workflow\ run ]]; then
31
+ exit 0
32
+ fi
33
+
34
+ # ── Extract workflow name from command ───────────────────────────────────────
35
+ # Handles: gh workflow run foo.yml
36
+ # gh workflow run foo.yml --ref branch
37
+ # gh workflow run "foo.yml" ...
38
+ WORKFLOW_NAME=$(echo "$COMMAND" | sed -E 's/^gh[[:space:]]+workflow[[:space:]]+run[[:space:]]+([^[:space:]]+).*/\1/' | tr -d '"'"'")
39
+
40
+ if [[ -z "$WORKFLOW_NAME" ]]; then
41
+ exit 0 # Can't parse workflow name — fail-open
42
+ fi
43
+
44
+ # ── Find repo root ────────────────────────────────────────────────────────────
45
+ REPO_ROOT=""
46
+ DIR="$(pwd)"
47
+ for _ in $(seq 1 10); do
48
+ if [[ -d "$DIR/.github/workflows" ]]; then
49
+ REPO_ROOT="$DIR"
50
+ break
51
+ fi
52
+ DIR="$(dirname "$DIR")"
53
+ done
54
+
55
+ if [[ -z "$REPO_ROOT" ]]; then
56
+ exit 0 # Can't find repo root — fail-open
57
+ fi
58
+
59
+ # ── Locate the workflow file ──────────────────────────────────────────────────
60
+ WORKFLOW_FILE="$REPO_ROOT/.github/workflows/$WORKFLOW_NAME"
61
+ if [[ ! -f "$WORKFLOW_FILE" ]]; then
62
+ # Selector may be a display name or numeric ID — try resolving via gh CLI
63
+ if command -v gh &>/dev/null; then
64
+ RESOLVED_PATH=$(gh workflow view "$WORKFLOW_NAME" --json path -q .path 2>/dev/null || true)
65
+ if [[ -n "$RESOLVED_PATH" && -f "$REPO_ROOT/$RESOLVED_PATH" ]]; then
66
+ WORKFLOW_FILE="$REPO_ROOT/$RESOLVED_PATH"
67
+ else
68
+ exit 0 # Unresolvable selector — fail-open
69
+ fi
70
+ else
71
+ exit 0 # gh not available — fail-open
72
+ fi
73
+ fi
74
+
75
+ # ── Extract path filters from the workflow ───────────────────────────────────
76
+ # Pull all entries under on.push.paths and on.pull_request.paths.
77
+ # yq is not guaranteed to be installed; use python3 (available everywhere) or
78
+ # fall back to a grep-based heuristic. Fail-open if neither works.
79
+
80
+ extract_paths() {
81
+ local file="$1"
82
+ local _result
83
+ # Try python3 + PyYAML first (most reliable)
84
+ if command -v python3 &>/dev/null; then
85
+ _result=$(python3 - "$file" <<'PYEOF' 2>/dev/null
86
+ import sys, yaml
87
+ try:
88
+ with open(sys.argv[1]) as f:
89
+ data = yaml.safe_load(f)
90
+ on = data.get('on', data.get(True, {})) # 'on' can parse as True in PyYAML
91
+ if not isinstance(on, dict):
92
+ sys.exit(0)
93
+ paths = []
94
+ for trigger in ('push', 'pull_request'):
95
+ t = on.get(trigger, {})
96
+ if isinstance(t, dict):
97
+ paths.extend(t.get('paths', []))
98
+ print('\n'.join(paths))
99
+ except Exception:
100
+ sys.exit(0)
101
+ PYEOF
102
+ )
103
+ if [[ -n "$_result" ]]; then
104
+ echo "$_result"
105
+ return
106
+ fi
107
+ # PyYAML not installed or produced no output — fall through to yq/awk
108
+ fi
109
+ # Fallback: yq if available
110
+ if command -v yq &>/dev/null; then
111
+ yq e '.on.push.paths[], .on.pull_request.paths[]' "$file" 2>/dev/null | grep -v '^null$' || true
112
+ return
113
+ fi
114
+ # Last resort: grep for '- ' lines after a 'paths:' line (fragile, but better than nothing)
115
+ awk '/paths:/{found=1; next} found && /^[[:space:]]*-[[:space:]]/{gsub(/^[[:space:]]*-[[:space:]]*/,""); print; next} found && /^[[:space:]]*[^-]/{found=0}' "$file" 2>/dev/null || true
116
+ }
117
+
118
+ PATH_FILTERS=$(extract_paths "$WORKFLOW_FILE")
119
+
120
+ # No path filters defined → workflow triggers on all pushes → dispatch is plausible
121
+ if [[ -z "$PATH_FILTERS" ]]; then
122
+ exit 0
123
+ fi
124
+
125
+ # ── Get changed files on this branch ─────────────────────────────────────────
126
+ CHANGED_FILES=$(git -C "$REPO_ROOT" diff --name-only "origin/main...HEAD" 2>/dev/null || true)
127
+
128
+ if [[ -z "$CHANGED_FILES" ]]; then
129
+ exit 0 # Can't determine changed files — fail-open
130
+ fi
131
+
132
+ # ── Check if any changed file matches any path filter ────────────────────────
133
+ # Workflow path filters support glob patterns (e.g. `mcp-server/**`).
134
+ # Use bash glob matching via case statement.
135
+
136
+ matches_any_filter() {
137
+ local file="$1"
138
+ while IFS= read -r pattern; do
139
+ [[ -z "$pattern" ]] && continue
140
+ # Intentional glob matching — unquoted RHS is required for ** patterns
141
+ # shellcheck disable=SC2053
142
+ [[ $file == $pattern ]] && return 0
143
+ done <<< "$PATH_FILTERS"
144
+ return 1
145
+ }
146
+
147
+ HAS_MATCH=0
148
+ while IFS= read -r f; do
149
+ [[ -z "$f" ]] && continue
150
+ if matches_any_filter "$f"; then
151
+ HAS_MATCH=1
152
+ break
153
+ fi
154
+ done <<< "$CHANGED_FILES"
155
+
156
+ # Changed files match the workflow's path filters → dispatch may be legitimate
157
+ if [[ $HAS_MATCH -eq 1 ]]; then
158
+ exit 0
159
+ fi
160
+
161
+ # ── Block: path filters explain why CI didn't trigger ────────────────────────
162
+ # Route to stderr — the harness surfaces a blocking hook's stderr only
163
+ # (KI-HOOKS-STDERR-SYSTEMIC).
164
+ {
165
+ echo ""
166
+ echo "workflow-dispatch-guard: blocked \`$COMMAND\`"
167
+ echo ""
168
+ echo "CI didn't trigger because your changed files don't match the path filters"
169
+ echo "in $(basename "$WORKFLOW_FILE"). This is expected behaviour — not a CI bug."
170
+ echo ""
171
+ echo "Workflow monitors:"
172
+ while IFS= read -r p; do
173
+ [[ -n "$p" ]] && echo " $p"
174
+ done <<< "$PATH_FILTERS"
175
+ echo ""
176
+ echo "Your changed files (vs origin/main):"
177
+ while IFS= read -r f; do
178
+ [[ -n "$f" ]] && echo " $f"
179
+ done <<< "$CHANGED_FILES"
180
+ echo ""
181
+ echo "No manual dispatch needed. If CI genuinely failed or needs re-running,"
182
+ echo "push a new commit that touches the paths above."
183
+ } >&2
184
+ exit 2
@@ -0,0 +1,258 @@
1
+ #!/bin/bash
2
+ # Worktree isolation guard - runs before every Bash command
3
+ # Exit 0 = allow, Exit 2 = block (non-zero exit blocks the command)
4
+ #
5
+ # This hook enforces worktree isolation by:
6
+ # 1. Blocking cd commands that leave the worktree directory
7
+ # 2. Blocking dangerous git operations (belt-and-suspenders with settings.json)
8
+ #
9
+ # OUTPUT ROUTING (KI-HOOKS-STDERR-SYSTEMIC): all block messages go to stderr —
10
+ # the harness surfaces a blocking hook's stderr, not its stdout.
11
+ #
12
+ # FALSE-POSITIVE HARDENING (block-report 2026-07-06): the git/cd patterns are
13
+ # matched against a "scannable" copy of the command with heredoc BODIES removed
14
+ # and are anchored to command-segment starts (^, or after ; & | && ||). This
15
+ # stops documentation text that merely MENTIONS a blocked verb — e.g. a memory
16
+ # or status report written via `cat <<EOF ... git merge --ff-only ... EOF`, or
17
+ # `echo "use git merge"` — from being blocked. Heredoc/quoted PROSE is data, not
18
+ # a command; unlike git-mutation-guard (destructive-git risk warrants accepting
19
+ # prose false-positives), worktree-guard blocking documentation about branches
20
+ # is not justified, so it is fixed here. NOTE: a REAL `git merge --ff-only` is
21
+ # still blocked — that is intended isolation policy (sync via
22
+ # `git rebase origin/main`, and settings.json also hard-denies `git merge *` at
23
+ # the permission layer, which fires before this hook).
24
+ #
25
+ # Requires environment variables set by `worktree start`:
26
+ # - CLAUDE_WORKTREE_PATH: Absolute path to worktree (e.g., /workspace/.worktrees/foo)
27
+ # - CLAUDE_WORKTREE_NAME: Worktree name (e.g., foo)
28
+
29
+ set -euo pipefail
30
+
31
+ # Read the tool input from stdin (JSON format)
32
+ INPUT=$(cat)
33
+
34
+ # Extract the command - handle both direct command and tool_input formats
35
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // .command // empty' 2>/dev/null)
36
+
37
+ if [[ -z "$COMMAND" ]]; then
38
+ # Can't parse command, allow it (fail open)
39
+ exit 0
40
+ fi
41
+
42
+ # Get worktree path from environment
43
+ WORKTREE_PATH="${CLAUDE_WORKTREE_PATH:-}"
44
+
45
+ # If no worktree restriction set, allow everything
46
+ if [[ -z "$WORKTREE_PATH" ]]; then
47
+ exit 0
48
+ fi
49
+
50
+ # Normalize worktree path (remove trailing slash)
51
+ WORKTREE_PATH="${WORKTREE_PATH%/}"
52
+
53
+ # Get current working directory
54
+ CURRENT_DIR=$(pwd)
55
+
56
+ # --- heredoc-body stripping (consumer-aware) --------------------------------
57
+ # Drop a heredoc body ONLY when its consuming command is inert (documentation/
58
+ # report text via cat/tee/redirect-to-file/non-shell language). When the operator
59
+ # line names a SHELL interpreter (bash/sh/zsh/dash/ksh/eval/source/`.`) or pipes
60
+ # the body into one (`| ...sh`), the body IS executed, so it is KEPT and still
61
+ # scanned — a blocked verb there is a real command, not prose. The operator line
62
+ # itself is always preserved. Covers `<<EOF`, `<<-EOF`, `<<"EOF"`, `<<'EOF'`.
63
+ strip_heredocs() {
64
+ awk '
65
+ !inbody {
66
+ line = $0
67
+ if (match(line, /<<[-]?[[:space:]]*["'"'"']?[A-Za-z_][A-Za-z0-9_]*["'"'"']?/)) {
68
+ d = substr(line, RSTART, RLENGTH)
69
+ gsub(/<<[-]?[[:space:]]*["'"'"']?/, "", d)
70
+ gsub(/["'"'"'].*/, "", d)
71
+ delim = d
72
+ inbody = 1
73
+ keepbody = 0
74
+ if (line ~ /(^|[^[:alnum:]_])(bash|sh|zsh|dash|ksh|eval|source)([^[:alnum:]_]|$)/) keepbody = 1
75
+ if (line ~ /(^|[[:space:]])\.[[:space:]]/) keepbody = 1
76
+ if (line ~ /\|[^|]*(bash|sh|zsh|dash|ksh|eval)([^[:alnum:]_]|$)/) keepbody = 1
77
+ }
78
+ print line
79
+ next
80
+ }
81
+ {
82
+ t = $0
83
+ gsub(/^[[:space:]]+/, "", t)
84
+ if (t == delim) { inbody = 0; keepbody = 0; next }
85
+ if (keepbody) print $0
86
+ }
87
+ '
88
+ }
89
+
90
+ SCAN=$(printf '%s' "$COMMAND" | strip_heredocs)
91
+
92
+ # ============================================================
93
+ # Rule 1: Block cd to paths outside worktree
94
+ # ============================================================
95
+ # FALSE-POSITIVE HARDENING (#3909): a `cd` only counts as real navigation when
96
+ # it STARTS a command segment (CDSEG: start-of-line, or after a separator
97
+ # `; & | ( {`, or a `then`/`do`/`else` shell keyword) — the same segment-anchor
98
+ # approach as git-mutation-guard.sh's GITSEG (line ~148). Applied to the
99
+ # already-heredoc-stripped SCAN. In ADDITION, an absolute target that is not a
100
+ # directory ON THE LOCAL FILESYSTEM is ignored: a cd to a locally-nonexistent
101
+ # path is harmless (it would just fail), and this is what removes the
102
+ # remote-payload false-positive class — the `cd` runs on a REMOTE host, not
103
+ # here. Examples that must NOT block:
104
+ # az vm run-command ... --scripts 'bash -c "... ; cd /home/azureuser/app ; ..."'
105
+ # ssh host "cd /var/www/remote-app && git pull"
106
+ # printf 'cd /remote/only/path\n'
107
+ # Relative paths already fail open this way (empty RESOLVED below); this makes
108
+ # absolute-path handling consistent. A REAL `cd /etc && ...` at a segment start
109
+ # that resolves to an existing local dir outside the worktree still blocks.
110
+ CDSEG='(^|[;&|({]|\s(then|do|else)\s)\s*'
111
+
112
+ if echo "$SCAN" | grep -qE "${CDSEG}cd\s+"; then
113
+ # Extract cd target(s) that appear at a command-segment start only. The
114
+ # grep match may carry a leading separator/keyword prefix; strip everything
115
+ # up to and including the anchored `cd ` token, then strip surrounding quotes.
116
+ CD_TARGETS=$(echo "$SCAN" | grep -oE "${CDSEG}cd\s+[^;&|<>]+" \
117
+ | sed -E 's/^.*\bcd[[:space:]]+//' \
118
+ | tr -d '"'"'"'`' || true)
119
+
120
+ for TARGET in $CD_TARGETS; do
121
+ # Skip empty targets
122
+ [[ -z "$TARGET" ]] && continue
123
+
124
+ # Resolve the path
125
+ if [[ "$TARGET" == /* ]]; then
126
+ # Absolute path
127
+ RESOLVED="$TARGET"
128
+ elif [[ "$TARGET" == "~"* ]]; then
129
+ # Home directory path
130
+ RESOLVED="${TARGET/#\~/$HOME}"
131
+ else
132
+ # Relative path - resolve from current directory
133
+ RESOLVED=$(cd "$CURRENT_DIR" 2>/dev/null && cd "$TARGET" 2>/dev/null && pwd) || RESOLVED=""
134
+ fi
135
+
136
+ # Normalize resolved path
137
+ RESOLVED="${RESOLVED%/}"
138
+
139
+ # Ignore targets that are not a directory on the LOCAL filesystem. A cd to a
140
+ # locally-nonexistent path is harmless (it just fails), and this drops the
141
+ # remote-payload false-positive class (#3909). Relative paths already fail
142
+ # open via the resolution above (empty RESOLVED).
143
+ [[ -n "$RESOLVED" && -d "$RESOLVED" ]] || continue
144
+
145
+ # Allow /tmp and /var/tmp
146
+ if [[ "$RESOLVED" == /tmp* ]] || [[ "$RESOLVED" == /var/tmp* ]]; then
147
+ continue
148
+ fi
149
+
150
+ # Allow paths within worktree
151
+ if [[ "$RESOLVED" == "$WORKTREE_PATH"* ]]; then
152
+ continue
153
+ fi
154
+
155
+ # Allow the scratchpad directory
156
+ if [[ "$RESOLVED" == *"/scratchpad"* ]]; then
157
+ continue
158
+ fi
159
+
160
+ # Allow the user's home directory (for inspecting global tools, configs, etc.)
161
+ if [[ -n "$HOME" ]] && [[ "$RESOLVED" == "$HOME"* ]]; then
162
+ continue
163
+ fi
164
+
165
+ # Block: path is outside worktree
166
+ if [[ -n "$RESOLVED" ]]; then
167
+ # Route to stderr — the harness surfaces a blocking hook's stderr only.
168
+ {
169
+ echo ""
170
+ echo "BLOCKED: Cannot navigate outside worktree"
171
+ echo " Target: $TARGET"
172
+ echo " Resolved: $RESOLVED"
173
+ echo " Allowed: $WORKTREE_PATH (and /tmp)"
174
+ echo ""
175
+ echo "You are working in worktree: ${CLAUDE_WORKTREE_NAME:-unknown}"
176
+ echo "Stay within your assigned worktree directory."
177
+ echo ""
178
+ } >&2
179
+ exit 2
180
+ fi
181
+ done
182
+ fi
183
+
184
+ # ============================================================
185
+ # Rule 2: Block git operations that could tangle state
186
+ # (Belt-and-suspenders with settings.json deny rules)
187
+ # ============================================================
188
+
189
+ # Segment anchor: a command starts at ^, or right after a separator. Besides
190
+ # ; & | (which cover ; && || and pipe/background), include ( ` and $ so a git op
191
+ # inside a subshell `(git ...)`, backticks `` `git ...` ``, or command
192
+ # substitution `$(git ...)` is still caught (SHOULD FIX 1). Prose like
193
+ # "planned a git merge" has a plain space before "git" and stays allowed.
194
+ SEG='(^|[;&|(`$])\s*'
195
+
196
+ # Block checkout to main
197
+ if echo "$SCAN" | grep -qE "${SEG}git\s+(checkout|switch)\s+main(\s|$)"; then
198
+ {
199
+ echo ""
200
+ echo "BLOCKED: Cannot switch to main branch"
201
+ echo " Command: $COMMAND"
202
+ echo " Worktree: ${CLAUDE_WORKTREE_NAME:-unknown}"
203
+ echo ""
204
+ echo "To work on main, use a different worktree or the root workspace."
205
+ echo ""
206
+ } >&2
207
+ exit 2
208
+ fi
209
+
210
+ # Block checkout -b (new branch creation)
211
+ if echo "$SCAN" | grep -qE "${SEG}git\s+(checkout\s+-b|switch\s+(-c|--create))"; then
212
+ {
213
+ echo ""
214
+ echo "BLOCKED: Cannot create new branches"
215
+ echo " Command: $COMMAND"
216
+ echo " Worktree: ${CLAUDE_WORKTREE_NAME:-unknown}"
217
+ echo ""
218
+ echo "Work on your assigned branch. Create PRs to merge changes."
219
+ echo ""
220
+ } >&2
221
+ exit 2
222
+ fi
223
+
224
+ # Block merge. NOTE: this also blocks `git merge --ff-only` (e.g. a branch-sync
225
+ # fast-forward). That is intended worktree-isolation policy — sync via
226
+ # `git rebase origin/main`, not merge. The block-report on 2026-07-06 was that
227
+ # the reason was invisible (message went to stdout); routing to stderr fixes the
228
+ # "blocked with no explanation" symptom.
229
+ if echo "$SCAN" | grep -qE "${SEG}git\s+merge\s"; then
230
+ {
231
+ echo ""
232
+ echo "BLOCKED: Cannot merge branches directly"
233
+ echo " Command: $COMMAND"
234
+ echo " Worktree: ${CLAUDE_WORKTREE_NAME:-unknown}"
235
+ echo ""
236
+ echo "Use pull requests to merge changes, or 'git rebase origin/main' to sync."
237
+ echo ""
238
+ } >&2
239
+ exit 2
240
+ fi
241
+
242
+ # Block push to main
243
+ if echo "$SCAN" | grep -qE "${SEG}git\s+push\s+.*\s*origin\s+main"; then
244
+ {
245
+ echo ""
246
+ echo "BLOCKED: Cannot push to main"
247
+ echo " Command: $COMMAND"
248
+ echo ""
249
+ echo "Push to your worktree branch and create a PR."
250
+ echo ""
251
+ } >&2
252
+ exit 2
253
+ fi
254
+
255
+ # ============================================================
256
+ # All checks passed - allow the command
257
+ # ============================================================
258
+ exit 0
@@ -0,0 +1,33 @@
1
+ ---
2
+ paths:
3
+ - "benchmarks/**"
4
+ - "swe-bench-cl/**"
5
+ - "mcp-server/agent-tests/**"
6
+ - "core-api/scripts/*eval*"
7
+ - "core-api/scripts/*benchmark*"
8
+ ---
9
+
10
+ # Eval Harness Rules
11
+
12
+ Eval work is decision infrastructure. Treat harness code, fixtures, scoring, analysis, and benchmark data as high-risk.
13
+
14
+ ## Required Practices
15
+
16
+ - Put new benchmark implementations under `benchmarks/<benchmark-slug>/` unless the user explicitly directs a different location.
17
+ - Preregister the eval design before running scored comparisons: task set, variants, metrics, exclusion rules, and pass/fail interpretation.
18
+ - Add deterministic fixtures for every scoring path, including negative controls, case sensitivity, empty results, malformed data, and boundary cases.
19
+ - Validate the harness with replay runs against known transcripts before trusting aggregate results.
20
+ - Use an independent Code Reviewer for harness, scoring, sampling, persistence, and report-generation changes.
21
+ - Maintain a claim ledger for findings. Every claim must have source data, reproduction steps, and a confidence level.
22
+ - Do not convert exploratory evidence into a benchmark conclusion without checking whether a harness bug could explain it.
23
+
24
+ ## Visual Investigation
25
+
26
+ - For nontrivial evals, prefer a small UI or notebook-style workbench that lets reviewers inspect tasks, traces, memory retrieved, scoring decisions, and aggregate deltas.
27
+ - The visual tool is not a substitute for fixtures or replay validation; it is for investigation and reviewer confidence.
28
+
29
+ ## Memory-Specific Diagnostics
30
+
31
+ - Capture what memory was retrieved, shown, ignored, reused, contradicted, or invalidated in each run.
32
+ - Ask the agent for a brief post-run memory-use reflection when diagnosing memory impact, but do not treat self-report as ground truth.
33
+ - Check whether memory anchored the agent on a dead end, repeated an invalid patch, or suppressed exploration.
@@ -0,0 +1,17 @@
1
+ ---
2
+ paths:
3
+ - "customer-portal/**"
4
+ - "marketing-site/**"
5
+ - "coe/**"
6
+ - "product/**/src/**"
7
+ ---
8
+
9
+ # Frontend Rules
10
+
11
+ Frontend changes need visual verification, not just unit tests.
12
+
13
+ - Match the existing app's design system, density, spacing, and interaction patterns.
14
+ - Verify responsive behavior on mobile and desktop before claiming completion.
15
+ - Check for text overflow, overlap, broken loading/error states, and empty-state regressions.
16
+ - Use screenshots or Playwright when the change affects layout, navigation, canvas, media, or user workflows.
17
+ - Do not add instructional in-app copy to compensate for unclear controls; improve the interaction instead.
@@ -0,0 +1,15 @@
1
+ ---
2
+ paths:
3
+ - "mcp-server/**"
4
+ - "packages/**/mcp*/**"
5
+ ---
6
+
7
+ # MCP Protocol Rules
8
+
9
+ MCP changes affect agent behavior and external tool contracts.
10
+
11
+ - Preserve tool schemas and response shapes unless the change is explicitly versioned and documented.
12
+ - Add or update protocol-level tests for tool argument validation, error handling, and response compatibility.
13
+ - Verify behavior through the MCP surface, not only internal helper functions.
14
+ - Check existing memories and issues for known MCP regressions before making claims about current behavior.
15
+ - Treat noisy or partial tool failures as data to investigate, not as proof that the feature works or fails.
@@ -0,0 +1,17 @@
1
+ ---
2
+ paths:
3
+ - "core-api/src/repositories/**"
4
+ - "core-api/src/**/retrieval/**"
5
+ - "core-api/scripts/retrieval-*"
6
+ - "retrieval-workbench/**"
7
+ ---
8
+
9
+ # Retrieval Rules
10
+
11
+ Retrieval changes need corpus-level evidence and careful interpretation.
12
+
13
+ - Define the corpus, query set, filters, and expected behavior before comparing variants.
14
+ - Include negative controls and hard filters in tests, especially conversation, topic, scope, and time filters.
15
+ - Separate retrieval quality, summarization quality, and UI/tool formatting when diagnosing failures.
16
+ - Preserve superseded and invalidated semantics. Do not surface stale memories as current facts without clear labeling.
17
+ - Report confidence with evidence: sample size, representative failures, and whether the delta survives manual inspection.