@miller-tech/uap 1.12.0 → 1.13.1

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.
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env bash
2
+ # UAP Post-Compact Compliance Re-injection — INFORMATIONAL hook
3
+ # Event: PostCompact
4
+ # Re-injects policy awareness after context compaction.
5
+ # Always exits 0 (never blocks).
6
+ set -euo pipefail
7
+
8
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-${FACTORY_PROJECT_DIR:-${CURSOR_PROJECT_DIR:-.}}}"
9
+ DB_PATH="${PROJECT_DIR}/agents/data/memory/short_term.db"
10
+ COORD_DB="${PROJECT_DIR}/agents/data/coordination/coordination.db"
11
+
12
+ output=""
13
+
14
+ # ─── Active Policy Summary ──────────────────────────────────────
15
+ output+="<system-reminder>"$'\n'
16
+ output+="## UAP COMPLIANCE RESTORED (Post-Compact)"$'\n'
17
+ output+=""$'\n'
18
+ output+="Context was compacted. All policies remain in effect:"$'\n'
19
+ output+=""$'\n'
20
+ output+="### BLOCKING HOOKS (hard enforcement):"$'\n'
21
+ output+="- **Worktree File Guard**: Edit/Write operations BLOCKED outside .worktrees/"$'\n'
22
+ output+="- **Dangerous Command Guard**: terraform apply/destroy, git push --force, direct master commits BLOCKED"$'\n'
23
+ output+="- **Completion Gate**: Stop/completion checked for test, build, version gates"$'\n'
24
+ output+=""$'\n'
25
+ output+="### ACTIVE POLICIES [all REQUIRED]:"$'\n'
26
+ output+="1. worktree-enforcement — All file changes in .worktrees/NNN-<slug>/"$'\n'
27
+ output+="2. worktree-file-guard — Edit/Write paths must be inside worktrees"$'\n'
28
+ output+="3. pre-edit-build-gate — npm run build before/after .ts edits"$'\n'
29
+ output+="4. completion-gate — 2+ tests, build, lint, version bump before DONE"$'\n'
30
+ output+="5. semver-versioning — npm run version:patch/minor/major (no manual edits)"$'\n'
31
+ output+="6. mandatory-file-backup — Backup files before modification"$'\n'
32
+ output+="7. iac-state-parity — All infra changes reflected in IaC"$'\n'
33
+ output+="8. iac-pipeline-enforcement — No local terraform apply/destroy"$'\n'
34
+ output+="9. kubectl-verify-backport — kubectl changes backported to IaC"$'\n'
35
+ output+="10. definition-of-done-iac — Pipeline apply + kubectl verify required"$'\n'
36
+ output+="11. image-asset-verification — Script-based, not vision-based [RECOMMENDED]"$'\n'
37
+ output+=""$'\n'
38
+ output+="### MANDATORY BEFORE WORK:"$'\n'
39
+ output+="- Verify you are in a worktree: pwd must contain .worktrees/"$'\n'
40
+ output+="- Run: uap task ready"$'\n'
41
+ output+="- Query memory: uap memory query \"<current task>\""$'\n'
42
+ output+=""$'\n'
43
+
44
+ # ─── Restore session context from memory ─────────────────────────
45
+ if [ -f "$DB_PATH" ]; then
46
+ recent=$(sqlite3 "$DB_PATH" "
47
+ SELECT type || ': ' || substr(content, 1, 100) FROM memories
48
+ WHERE timestamp >= datetime('now', '-2 hours')
49
+ ORDER BY id DESC LIMIT 5;
50
+ " 2>/dev/null || true)
51
+
52
+ if [ -n "$recent" ]; then
53
+ output+="### Recent Memory (last 2h):"$'\n'
54
+ output+="$recent"$'\n'
55
+ output+=""$'\n'
56
+ fi
57
+
58
+ # Session decisions
59
+ decisions=$(sqlite3 "$DB_PATH" "
60
+ SELECT substr(content, 1, 120) FROM session_memories
61
+ WHERE type = 'decision' AND importance >= 6
62
+ ORDER BY id DESC LIMIT 3;
63
+ " 2>/dev/null || true)
64
+
65
+ if [ -n "$decisions" ]; then
66
+ output+="### Recent Decisions:"$'\n'
67
+ output+="$decisions"$'\n'
68
+ output+=""$'\n'
69
+ fi
70
+ fi
71
+
72
+ # ─── Multi-agent coordination status ────────────────────────────
73
+ if [ -f "$COORD_DB" ]; then
74
+ active_work=$(sqlite3 "$COORD_DB" "
75
+ SELECT agent_id || ' editing ' || resource
76
+ FROM work_announcements
77
+ WHERE completed_at IS NULL
78
+ ORDER BY announced_at DESC LIMIT 5;
79
+ " 2>/dev/null || true)
80
+
81
+ if [ -n "$active_work" ]; then
82
+ output+="### Active Work (coordinate — do not conflict):"$'\n'
83
+ output+="$active_work"$'\n'
84
+ output+=""$'\n'
85
+ fi
86
+ fi
87
+
88
+ # ─── Worktree status ────────────────────────────────────────────
89
+ CURRENT_BRANCH=$(git -C "$PROJECT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
90
+ IS_WORKTREE="false"
91
+ if echo "$PROJECT_DIR" | grep -q '\.worktrees/'; then
92
+ IS_WORKTREE="true"
93
+ fi
94
+ GIT_DIR=$(git -C "$PROJECT_DIR" rev-parse --git-dir 2>/dev/null || true)
95
+ GIT_COMMON=$(git -C "$PROJECT_DIR" rev-parse --git-common-dir 2>/dev/null || true)
96
+ if [ -n "$GIT_DIR" ] && [ -n "$GIT_COMMON" ] && [ "$GIT_DIR" != "$GIT_COMMON" ]; then
97
+ IS_WORKTREE="true"
98
+ fi
99
+
100
+ if [ "$IS_WORKTREE" = "false" ] && { [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "master" ]; }; then
101
+ output+="### ⚠ WORKTREE VIOLATION: You are on $CURRENT_BRANCH in the project root."$'\n'
102
+ output+="Run: uap worktree create <slug> BEFORE any file edits."$'\n'
103
+ output+=""$'\n'
104
+ else
105
+ output+="### Worktree: ACTIVE (branch: $CURRENT_BRANCH)"$'\n'
106
+ output+=""$'\n'
107
+ fi
108
+
109
+ output+="</system-reminder>"$'\n'
110
+
111
+ echo "$output"
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bash
2
+ # UAP Build Gate Reminder — INFORMATIONAL hook
3
+ # Event: PostToolUse (matcher: Edit|Write)
4
+ # Reminds about pre-edit build gate after .ts file modifications.
5
+ # Enforces: pre-edit-build-gate policy.
6
+ # Always exits 0 (never blocks).
7
+ set -euo pipefail
8
+
9
+ # Read tool input from stdin (JSON)
10
+ INPUT=$(cat)
11
+
12
+ # Extract file_path from tool_input
13
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty' 2>/dev/null || true)
14
+
15
+ # If we can't determine the file path, exit silently
16
+ if [ -z "$FILE_PATH" ]; then
17
+ exit 0
18
+ fi
19
+
20
+ # Only remind for TypeScript files
21
+ if echo "$FILE_PATH" | grep -qE '\.tsx?$'; then
22
+ echo "[BUILD GATE] TypeScript file modified: $(basename "$FILE_PATH"). Run 'npm run build' before editing the next file. See policies/pre-edit-build-gate.md"
23
+ fi
24
+
25
+ # Remind about file backup policy for any file edit
26
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
27
+ BACKUP_DIR="${PROJECT_DIR}/.uap-backups/$(date +%Y-%m-%d)"
28
+ RELATIVE_PATH="${FILE_PATH#"$PROJECT_DIR"/}"
29
+
30
+ # Check if backup exists for this file
31
+ if [ ! -f "${BACKUP_DIR}/${RELATIVE_PATH}" ] 2>/dev/null; then
32
+ # Only warn for source files, not generated or runtime files
33
+ if ! echo "$FILE_PATH" | grep -qE '(node_modules|dist|\.uap-backups|agents/data|\.git)/'; then
34
+ echo "[BACKUP REMINDER] No backup found for $(basename "$FILE_PATH"). Policy requires: mkdir -p $(dirname "${BACKUP_DIR}/${RELATIVE_PATH}") && cp \"$FILE_PATH\" \"${BACKUP_DIR}/${RELATIVE_PATH}\""
35
+ fi
36
+ fi
37
+
38
+ exit 0
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env bash
2
+ # UAP Dangerous Command Guard — BLOCKING hook
3
+ # Event: PreToolUse (matcher: Bash)
4
+ # Exit 2 = BLOCK the command. Exit 0 = allow.
5
+ # Enforces: iac-pipeline-enforcement, worktree-enforcement, git safety policies.
6
+ set -euo pipefail
7
+
8
+ # Read tool input from stdin (JSON)
9
+ INPUT=$(cat)
10
+
11
+ # Extract command from tool_input
12
+ CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null || true)
13
+
14
+ # If we can't determine the command, fail open
15
+ if [ -z "$CMD" ]; then
16
+ exit 0
17
+ fi
18
+
19
+ # ─── IaC Pipeline Enforcement ───────────────────────────────────
20
+ # Block local terraform apply/destroy (policies/iac-pipeline-enforcement.md)
21
+ # Allow: terraform fmt, validate, init, plan, output, show, state list, graph
22
+ if echo "$CMD" | grep -qiE '\bterraform\s+(apply|destroy)\b'; then
23
+ echo "BLOCKED [iac-pipeline-enforcement]: terraform apply/destroy MUST go through CI/CD pipeline. Local execution is prohibited. Use: terraform fmt, validate, or plan locally. See policies/iac-pipeline-enforcement.md" >&2
24
+ exit 2
25
+ fi
26
+
27
+ # ─── Git Force Push Protection ──────────────────────────────────
28
+ # Block force pushes to any branch
29
+ if echo "$CMD" | grep -qE 'git\s+push\s+.*--force|git\s+push\s+-f\b|git\s+push\s+.*--force-with-lease'; then
30
+ echo "BLOCKED [git-safety]: Force push is prohibited. Use standard push and resolve conflicts through PRs. If you believe this is necessary, ask the user for explicit approval first." >&2
31
+ exit 2
32
+ fi
33
+
34
+ # ─── Direct Master/Main Commit Protection ───────────────────────
35
+ # Block git commit when on master/main AND not inside a worktree
36
+ if echo "$CMD" | grep -qE '\bgit\s+commit\b'; then
37
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
38
+ CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null || true)
39
+ CHECK_DIR="${CWD:-$PROJECT_DIR}"
40
+
41
+ # Only block if NOT inside a worktree directory
42
+ if ! echo "$CHECK_DIR" | grep -q '\.worktrees/'; then
43
+ CURRENT_BRANCH=$(git -C "$CHECK_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
44
+ if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "master" ]; then
45
+ # Allow automated version bump commits (message contains "bump version")
46
+ if echo "$CMD" | grep -qE 'chore: bump version|version:patch|version:minor|version:major|version-bump'; then
47
+ exit 0
48
+ fi
49
+ echo "BLOCKED [worktree-enforcement]: Direct commits to ${CURRENT_BRANCH} are prohibited. Create a worktree first: uap worktree create <slug>. See policies/worktree-enforcement.md" >&2
50
+ exit 2
51
+ fi
52
+ fi
53
+ fi
54
+
55
+ # ─── Direct Push to Master/Main Protection ──────────────────────
56
+ # Block git push targeting main/master directly (not through PR)
57
+ if echo "$CMD" | grep -qE '\bgit\s+push\b'; then
58
+ # Block explicit pushes to main/master
59
+ if echo "$CMD" | grep -qE '\bgit\s+push\s+(origin\s+)?(main|master)\b'; then
60
+ # Allow push after version bump (git push && git push --tags pattern)
61
+ if echo "$CMD" | grep -qE 'git\s+push\s+--tags|git\s+push\s*&&\s*git\s+push\s+--tags'; then
62
+ exit 0
63
+ fi
64
+ echo "BLOCKED [worktree-enforcement]: Direct push to main/master is prohibited. Use: uap worktree pr <id> to create a PR instead. See policies/worktree-enforcement.md" >&2
65
+ exit 2
66
+ fi
67
+ fi
68
+
69
+ # ─── Destructive Git Operations ─────────────────────────────────
70
+ # Block git reset --hard and git clean -f outside worktrees
71
+ if echo "$CMD" | grep -qE '\bgit\s+reset\s+--hard\b|\bgit\s+clean\s+-[a-z]*f'; then
72
+ CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null || true)
73
+ if ! echo "${CWD:-.}" | grep -q '\.worktrees/'; then
74
+ echo "BLOCKED [git-safety]: Destructive git operations (reset --hard, clean -f) are prohibited outside worktrees. These can destroy uncommitted work in the project root." >&2
75
+ exit 2
76
+ fi
77
+ fi
78
+
79
+ # ─── Manual Version Edit Protection ─────────────────────────────
80
+ # Block direct edits to package.json version field via sed/awk
81
+ if echo "$CMD" | grep -qE "(sed|awk).*package\.json.*(version|\"version\")|((sed|awk).*version.*package\.json)|(jq.*\.version.*package\.json)"; then
82
+ echo "BLOCKED [semver-versioning]: Manual package.json version edits are prohibited. Use: npm run version:patch, version:minor, or version:major. See policies/semver-versioning.md" >&2
83
+ exit 2
84
+ fi
85
+
86
+ # Command allowed
87
+ exit 0
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env bash
2
+ # UAP Worktree File Guard — BLOCKING hook
3
+ # Event: PreToolUse (matcher: Edit|Write)
4
+ # Exit 2 = BLOCK the edit/write. Exit 0 = allow.
5
+ # Enforces: worktree-file-guard, worktree-enforcement policies.
6
+ set -euo pipefail
7
+
8
+ # Read tool input from stdin (JSON)
9
+ INPUT=$(cat)
10
+
11
+ # Extract file_path from tool_input
12
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty' 2>/dev/null || true)
13
+
14
+ # If we can't determine the file path, fail open
15
+ if [ -z "$FILE_PATH" ]; then
16
+ exit 0
17
+ fi
18
+
19
+ # Exempt paths — runtime data, not source code
20
+ EXEMPT_PATTERNS=(
21
+ "agents/data/"
22
+ "node_modules/"
23
+ ".uap-backups/"
24
+ ".uap/"
25
+ ".git/"
26
+ "dist/"
27
+ "/tmp/"
28
+ "/dev/"
29
+ )
30
+
31
+ for pattern in "${EXEMPT_PATTERNS[@]}"; do
32
+ if echo "$FILE_PATH" | grep -q "$pattern"; then
33
+ exit 0
34
+ fi
35
+ done
36
+
37
+ # Allow if path is inside a worktree
38
+ if echo "$FILE_PATH" | grep -q '\.worktrees/'; then
39
+ exit 0
40
+ fi
41
+
42
+ # BLOCK: path is outside worktrees and not exempt
43
+ echo '{"decision":"block","reason":"WORKTREE POLICY VIOLATION: File path is outside .worktrees/. All edits must target files inside a worktree. Run: uap worktree create <slug> then edit files in .worktrees/NNN-<slug>/. See policies/worktree-file-guard.md"}' >&2
44
+ exit 2
@@ -1,53 +1,38 @@
1
1
  #!/usr/bin/env bash
2
- # UAP Session End Hook (universal - all coding harnesses)
3
- # Records session completion in memory and cleans up agent state.
4
- # Fails safely - never blocks the agent.
2
+ # UAP Session End Hook Cleanup and archival
3
+ # Event: SessionEnd
4
+ # Stores final session summary and cleans up coordination state.
5
+ # Always exits 0 (never blocks).
5
6
  set -euo pipefail
6
7
 
7
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-${FACTORY_PROJECT_DIR:-${CURSOR_PROJECT_DIR:-${UAP_PROJECT_DIR:-.}}}}"
8
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-${FACTORY_PROJECT_DIR:-${CURSOR_PROJECT_DIR:-.}}}"
8
9
  DB_PATH="${PROJECT_DIR}/agents/data/memory/short_term.db"
9
10
  COORD_DB="${PROJECT_DIR}/agents/data/coordination/coordination.db"
10
-
11
- if [ ! -f "$DB_PATH" ]; then
12
- exit 0
13
- fi
14
-
15
11
  TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
16
12
 
17
- # Record session end marker in memory
18
- sqlite3 "$DB_PATH" "
19
- INSERT OR IGNORE INTO memories (timestamp, type, content)
20
- VALUES ('$TIMESTAMP', 'action', '[post-session] Session completed at $TIMESTAMP');
21
- " 2>/dev/null || true
22
-
23
- # Count decisions stored this session
24
- session_decisions=$(sqlite3 "$DB_PATH" "
25
- SELECT COUNT(*) FROM session_memories
26
- WHERE timestamp >= datetime('now', '-4 hours')
27
- AND type = 'decision';
28
- " 2>/dev/null || echo "0")
29
-
30
- if [ "$session_decisions" = "0" ]; then
31
- echo "<system-reminder>"
32
- echo "WARNING: No decisions stored this session."
33
- echo "Consider storing a summary before ending:"
34
- echo " sqlite3 ./agents/data/memory/short_term.db \"INSERT INTO session_memories (session_id,timestamp,type,content,importance) VALUES ('current',datetime('now'),'decision','<summary>',7);\""
35
- echo "</system-reminder>"
13
+ # Store session end marker
14
+ if [ -f "$DB_PATH" ]; then
15
+ sqlite3 "$DB_PATH" "
16
+ INSERT OR IGNORE INTO memories (timestamp, type, content)
17
+ VALUES ('$TIMESTAMP', 'action', '[session-end] Session terminated at $TIMESTAMP');
18
+ " 2>/dev/null || true
36
19
  fi
37
20
 
38
- # Clean up agent registrations from this session
21
+ # Clean up all active agents and work claims
39
22
  if [ -f "$COORD_DB" ]; then
40
23
  sqlite3 "$COORD_DB" "
41
- DELETE FROM work_claims WHERE agent_id IN (
42
- SELECT id FROM agent_registry
43
- WHERE status='active' AND last_heartbeat >= datetime('now','-10 minutes')
44
- );
45
- UPDATE work_announcements SET completed_at='$TIMESTAMP'
46
- WHERE completed_at IS NULL AND agent_id IN (
47
- SELECT id FROM agent_registry
48
- WHERE status='active' AND last_heartbeat >= datetime('now','-10 minutes')
49
- );
50
- UPDATE agent_registry SET status='completed'
51
- WHERE status='active' AND last_heartbeat >= datetime('now','-10 minutes');
24
+ UPDATE work_announcements SET completed_at = '$TIMESTAMP'
25
+ WHERE completed_at IS NULL;
26
+ DELETE FROM work_claims;
27
+ UPDATE agent_registry SET status = 'completed'
28
+ WHERE status IN ('active', 'idle');
52
29
  " 2>/dev/null || true
53
30
  fi
31
+
32
+ # Clean up backup files older than 7 days (retention policy)
33
+ BACKUP_DIR="${PROJECT_DIR}/.uap-backups"
34
+ if [ -d "$BACKUP_DIR" ]; then
35
+ find "$BACKUP_DIR" -maxdepth 1 -mindepth 1 -type d -mtime +7 -exec rm -rf {} \; 2>/dev/null || true
36
+ fi
37
+
38
+ exit 0
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env bash
2
+ # UAP Completion Gate + Session Cleanup — Stop hook
3
+ # Event: Stop
4
+ # Checks completion gates and cleans up session state.
5
+ # Exit 2 = BLOCK stop (force agent to continue). Exit 0 = allow stop.
6
+ # Enforces: completion-gate, mandatory-testing-deployment policies.
7
+ set -euo pipefail
8
+
9
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-${FACTORY_PROJECT_DIR:-${CURSOR_PROJECT_DIR:-.}}}"
10
+ DB_PATH="${PROJECT_DIR}/agents/data/memory/short_term.db"
11
+ COORD_DB="${PROJECT_DIR}/agents/data/coordination/coordination.db"
12
+
13
+ # ─── Detect if code was changed ─────────────────────────────────
14
+ CODE_CHANGED="false"
15
+ TS_CHANGED="false"
16
+ TEST_FILES_CHANGED="false"
17
+ UNCOMMITTED_CHANGES="false"
18
+
19
+ # Check for uncommitted changes in the working tree
20
+ CHANGED_FILES=$(git -C "$PROJECT_DIR" diff --name-only HEAD 2>/dev/null || true)
21
+ STAGED_FILES=$(git -C "$PROJECT_DIR" diff --cached --name-only 2>/dev/null || true)
22
+ UNTRACKED_FILES=$(git -C "$PROJECT_DIR" ls-files --others --exclude-standard 2>/dev/null || true)
23
+
24
+ ALL_CHANGES="${CHANGED_FILES}${STAGED_FILES}${UNTRACKED_FILES}"
25
+
26
+ if [ -n "$ALL_CHANGES" ]; then
27
+ UNCOMMITTED_CHANGES="true"
28
+
29
+ # Check for source code changes
30
+ if echo "$ALL_CHANGES" | grep -qE '\.(ts|tsx|js|jsx)$'; then
31
+ CODE_CHANGED="true"
32
+ fi
33
+
34
+ # Check for TypeScript changes specifically
35
+ if echo "$ALL_CHANGES" | grep -qE '\.tsx?$'; then
36
+ TS_CHANGED="true"
37
+ fi
38
+
39
+ # Check for test file changes
40
+ if echo "$ALL_CHANGES" | grep -qE 'test/.*\.(ts|tsx|js|jsx)$'; then
41
+ TEST_FILES_CHANGED="true"
42
+ fi
43
+ fi
44
+
45
+ # ─── Completion Gate Checklist ───────────────────────────────────
46
+ output=""
47
+ warnings=0
48
+
49
+ if [ "$CODE_CHANGED" = "true" ]; then
50
+ output+="## COMPLETION GATE CHECKLIST"$'\n'
51
+ output+=""$'\n'
52
+
53
+ # Gate 1: New tests written
54
+ if [ "$TEST_FILES_CHANGED" = "true" ]; then
55
+ output+="[PASS] New test files modified/added"$'\n'
56
+ else
57
+ output+="[WARN] No test files modified — completion-gate requires 2+ new tests for code changes"$'\n'
58
+ warnings=$((warnings + 1))
59
+ fi
60
+
61
+ # Gate 2: Build check (heuristic — check if dist/ is newer than last src change)
62
+ if [ -d "${PROJECT_DIR}/dist" ]; then
63
+ DIST_TIME=$(stat -c %Y "${PROJECT_DIR}/dist" 2>/dev/null || echo "0")
64
+ SRC_TIME=$(find "${PROJECT_DIR}/src" -name "*.ts" -newer "${PROJECT_DIR}/dist" 2>/dev/null | head -1)
65
+ if [ -z "$SRC_TIME" ]; then
66
+ output+="[PASS] Build appears up-to-date (dist/ newer than src/)"$'\n'
67
+ else
68
+ output+="[WARN] Build may be stale — run 'npm run build' to verify"$'\n'
69
+ warnings=$((warnings + 1))
70
+ fi
71
+ else
72
+ output+="[WARN] No dist/ directory — run 'npm run build'"$'\n'
73
+ warnings=$((warnings + 1))
74
+ fi
75
+
76
+ # Gate 3: Uncommitted changes
77
+ if [ -n "$STAGED_FILES" ] || [ -n "$CHANGED_FILES" ]; then
78
+ output+="[WARN] Uncommitted changes detected — commit or stash before version bump"$'\n'
79
+ warnings=$((warnings + 1))
80
+ fi
81
+
82
+ # Gate 4: Version bump check (was package.json version changed?)
83
+ VERSION_BUMPED="false"
84
+ if echo "$ALL_CHANGES" | grep -q "package.json"; then
85
+ # Check if version field actually changed
86
+ VERSION_DIFF=$(git -C "$PROJECT_DIR" diff HEAD -- package.json 2>/dev/null | grep -E '^\+.*"version"' || true)
87
+ if [ -n "$VERSION_DIFF" ]; then
88
+ VERSION_BUMPED="true"
89
+ fi
90
+ fi
91
+ if [ "$VERSION_BUMPED" = "true" ]; then
92
+ output+="[PASS] Version bump detected in package.json"$'\n'
93
+ else
94
+ output+="[WARN] No version bump — run 'npm run version:patch/minor/major' before claiming done"$'\n'
95
+ warnings=$((warnings + 1))
96
+ fi
97
+
98
+ output+=""$'\n'
99
+
100
+ if [ "$warnings" -gt 0 ]; then
101
+ output+="$warnings completion gate warning(s). Review policies/completion-gate.md before claiming task done."$'\n'
102
+ else
103
+ output+="All completion gates appear satisfied."$'\n'
104
+ fi
105
+ fi
106
+
107
+ # ─── Session Cleanup ─────────────────────────────────────────────
108
+ # Store session marker in memory DB
109
+ if [ -f "$DB_PATH" ]; then
110
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
111
+ sqlite3 "$DB_PATH" "
112
+ INSERT OR IGNORE INTO memories (timestamp, type, content)
113
+ VALUES ('$TIMESTAMP', 'action', '[session-end] Agent stopping at $TIMESTAMP. Code changed: $CODE_CHANGED, Tests: $TEST_FILES_CHANGED, Warnings: $warnings');
114
+ " 2>/dev/null || true
115
+ fi
116
+
117
+ # Mark agent as completed in coordination DB
118
+ if [ -f "$COORD_DB" ]; then
119
+ # Complete all active announcements for agents from this session
120
+ sqlite3 "$COORD_DB" "
121
+ UPDATE work_announcements SET completed_at = datetime('now')
122
+ WHERE completed_at IS NULL AND agent_id IN (
123
+ SELECT id FROM agent_registry
124
+ WHERE status = 'active' AND last_heartbeat >= datetime('now', '-5 minutes')
125
+ );
126
+ DELETE FROM work_claims WHERE agent_id IN (
127
+ SELECT id FROM agent_registry
128
+ WHERE status = 'active' AND last_heartbeat >= datetime('now', '-5 minutes')
129
+ );
130
+ UPDATE agent_registry SET status = 'completed'
131
+ WHERE status = 'active' AND last_heartbeat >= datetime('now', '-5 minutes');
132
+ " 2>/dev/null || true
133
+ fi
134
+
135
+ # Output the checklist (informational — shown to model)
136
+ if [ -n "$output" ]; then
137
+ echo "$output"
138
+ fi
139
+
140
+ # Allow stop (exit 0) — we use warnings, not hard blocks, because
141
+ # the model needs agency to decide when it's truly done vs still working.
142
+ exit 0