@memnexus-ai/mx-agent-cli 0.1.165 → 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.
- package/dist/__tests__/claude-md-compose.test.d.ts +16 -0
- package/dist/__tests__/claude-md-compose.test.d.ts.map +1 -0
- package/dist/__tests__/claude-md-compose.test.js +116 -0
- package/dist/__tests__/claude-md-compose.test.js.map +1 -0
- package/dist/__tests__/claude-sync.test.js +15 -0
- package/dist/__tests__/claude-sync.test.js.map +1 -1
- package/dist/__tests__/config-generation.test.d.ts +7 -0
- package/dist/__tests__/config-generation.test.d.ts.map +1 -0
- package/dist/__tests__/config-generation.test.js +141 -0
- package/dist/__tests__/config-generation.test.js.map +1 -0
- package/dist/__tests__/config-sync.test.d.ts +10 -0
- package/dist/__tests__/config-sync.test.d.ts.map +1 -0
- package/dist/__tests__/config-sync.test.js +180 -0
- package/dist/__tests__/config-sync.test.js.map +1 -0
- package/dist/__tests__/project-config.test.js +1 -0
- package/dist/__tests__/project-config.test.js.map +1 -1
- package/dist/agent-config/.mx-config-generation.json +4 -0
- package/dist/agent-config/CLAUDE.md.template +140 -0
- package/dist/agent-config/OWNERS.md +51 -0
- package/dist/agent-config/agents/bar-raiser.md +205 -0
- package/dist/agent-config/agents/dogfood-auditor.md +243 -0
- package/dist/agent-config/agents/git-expert.md +380 -0
- package/dist/agent-config/agents/implementation-engineer.md +387 -0
- package/dist/agent-config/agents/memory-creator.md +330 -0
- package/dist/agent-config/agents/prd-writer.md +478 -0
- package/dist/agent-config/agents/prfaq-writer.md +344 -0
- package/dist/agent-config/agents/prior-art-researcher.md +264 -0
- package/dist/agent-config/agents/qa-tester.md +406 -0
- package/dist/agent-config/agents/red-team.md +330 -0
- package/dist/agent-config/agents/security-reviewer.md +300 -0
- package/dist/agent-config/agents/status-reporter.md +297 -0
- package/dist/agent-config/claude-md/CLAUDE.core.md +105 -0
- package/dist/agent-config/claude-md/CLAUDE.overlay.memnexus.md +68 -0
- package/dist/agent-config/claude-md/compose.mjs +179 -0
- package/dist/agent-config/claude-md/vars.memnexus.json +4 -0
- package/dist/agent-config/hooks/auto-capture-precompact.sh +176 -0
- package/dist/agent-config/hooks/auto-capture-session-end.sh +178 -0
- package/dist/agent-config/hooks/auto-checkpoint.sh +102 -0
- package/dist/agent-config/hooks/ci-validate-hook.sh +499 -0
- package/dist/agent-config/hooks/ci-validate-routes.conf +16 -0
- package/dist/agent-config/hooks/delegation-audit.sh +61 -0
- package/dist/agent-config/hooks/deploy-verification-reminder.sh +179 -0
- package/dist/agent-config/hooks/git-mutation-guard.sh +219 -0
- package/dist/agent-config/hooks/iteration-log-check.sh +212 -0
- package/dist/agent-config/hooks/memory-checkpoint-reminder.sh +103 -0
- package/dist/agent-config/hooks/mx-guard-hook.sh +77 -0
- package/dist/agent-config/hooks/named-memory-write-guard.sh +237 -0
- package/dist/agent-config/hooks/reload-checkpoint.sh +68 -0
- package/dist/agent-config/hooks/set-terminal-appearance.sh +34 -0
- package/dist/agent-config/hooks/workflow-dispatch-guard.sh +184 -0
- package/dist/agent-config/hooks/worktree-guard.sh +258 -0
- package/dist/agent-config/rules/eval-harness.md +33 -0
- package/dist/agent-config/rules/frontend.md +17 -0
- package/dist/agent-config/rules/mcp-protocol.md +15 -0
- package/dist/agent-config/rules/retrieval.md +17 -0
- package/dist/agent-config/settings.json +196 -0
- package/dist/agent-config/shared/eval-tiered-rigor.md +15 -0
- package/dist/agent-config/shared/team-operating-rules.md +37 -0
- package/dist/agent-config/skills/eval-claim-impact/SKILL.md +20 -0
- package/dist/agent-config/skills/eval-harness-review/SKILL.md +21 -0
- package/dist/agent-config/skills/mx-checkpoint/SKILL.md +111 -0
- package/dist/agent-config/skills/mx-fix-pr/SKILL.md +90 -0
- package/dist/agent-config/skills/mx-gap-assessment/SKILL.md +90 -0
- package/dist/agent-config/skills/mx-iteration-close/SKILL.md +81 -0
- package/dist/agent-config/skills/mx-post-merge-sync/SKILL.md +83 -0
- package/dist/agent-config/skills/mx-pre-pr-check/SKILL.md +85 -0
- package/dist/agent-config/skills/mx-save/SKILL.md +102 -0
- package/dist/agent-config/skills/mx-session-start/SKILL.md +85 -0
- package/dist/agent-config/skills/team-gate-checklist/SKILL.md +19 -0
- package/dist/commands/config-sync.d.ts +39 -0
- package/dist/commands/config-sync.d.ts.map +1 -0
- package/dist/commands/config-sync.js +212 -0
- package/dist/commands/config-sync.js.map +1 -0
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +3 -0
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -4
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +6 -0
- package/dist/commands/start.js.map +1 -1
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/bundled-config.d.ts +44 -0
- package/dist/lib/bundled-config.d.ts.map +1 -0
- package/dist/lib/bundled-config.js +164 -0
- package/dist/lib/bundled-config.js.map +1 -0
- package/dist/lib/claude.d.ts.map +1 -1
- package/dist/lib/claude.js +9 -3
- package/dist/lib/claude.js.map +1 -1
- package/dist/lib/config-generation.d.ts +36 -0
- package/dist/lib/config-generation.d.ts.map +1 -0
- package/dist/lib/config-generation.js +73 -0
- package/dist/lib/config-generation.js.map +1 -0
- package/dist/lib/project-config.d.ts +7 -0
- package/dist/lib/project-config.d.ts.map +1 -1
- package/dist/lib/project-config.js +4 -0
- package/dist/lib/project-config.js.map +1 -1
- 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.
|