@miller-tech/uap 1.11.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,128 +1,429 @@
1
1
  # UAP ForgeCode Integration Plugin
2
- // Auto-generated by: uap hooks install forgecode
3
- // Add this line to ~/.zshrc after installing UAP: source "$PWD/.forge/forgecode.plugin.sh"
2
+ # Auto-generated by: uap hooks install forgecode
3
+ # Add this line to ~/.zshrc after installing UAP: source "$PWD/.forge/forgecode.plugin.sh"
4
4
 
5
5
  _uap_forgecode_session_start() {
6
- # Load recent memory context from UAP database before : command execution
7
-
6
+ # Load recent memory context from UAP database before command execution
7
+
8
8
  local PROJECT_DIR="${FORGE_UAP_PROJECT:-.}"
9
-
10
- if [ ! -f "\$PROJECT_DIR/./agents/data/memory/short_term.db" ]; then exit 0; fi
9
+ local DB_PATH="\$PROJECT_DIR/agents/data/memory/short_term.db"
10
+ local COORD_DB="\$PROJECT_DIR/agents/data/coordination/coordination.db"
11
+ local POLICY_DB="\$PROJECT_DIR/agents/data/memory/policies.db"
12
+ local AGENT_ID="forgecode-\$(date +%s | tail -c 7)"
13
+
14
+ if [ ! -f "\$DB_PATH" ]; then return 0; fi
11
15
 
12
16
  # Clean stale agents from coordination DB (heartbeat >24h old)
13
- if [ -f "\$PROJECT_DIR/./agents/data/coordination/coordination.db" ] && command -v sqlite3 &> /dev/null; then
14
- sqlite3 "\$PROJECT_DIR/./agents/data/coordination/coordination.db" "
17
+ if [ -f "\$COORD_DB" ] && command -v sqlite3 &> /dev/null; then
18
+ sqlite3 "\$COORD_DB" "
15
19
  DELETE FROM work_claims WHERE agent_id IN (
16
20
  SELECT id FROM agent_registry
17
- WHERE status IN ('active','idle') AND last_heartbeat < datetime('now','-24 hours');
21
+ WHERE status IN ('active','idle') AND last_heartbeat < datetime('now','-24 hours')
18
22
  );
23
+ DELETE FROM work_announcements WHERE agent_id IN (
24
+ SELECT id FROM agent_registry
25
+ WHERE status IN ('active','idle') AND last_heartbeat < datetime('now','-24 hours')
26
+ ) AND completed_at IS NULL;
19
27
  UPDATE agent_registry SET status='failed'
20
28
  WHERE status IN ('active','idle') AND last_heartbeat < datetime('now','-24 hours');
21
29
  " 2>/dev/null || true
30
+
31
+ # Register this agent
32
+ sqlite3 "\$COORD_DB" "
33
+ INSERT OR REPLACE INTO agent_registry (id, name, session_id, status, capabilities, started_at, last_heartbeat)
34
+ VALUES ('\$AGENT_ID', 'forgecode', '\$AGENT_ID', 'active', '[]', datetime('now'), datetime('now'));
35
+ " 2>/dev/null || true
36
+
37
+ # Auto-announce session
38
+ sqlite3 "\$COORD_DB" "
39
+ INSERT INTO work_announcements (agent_id, agent_name, intent_type, resource, description, announced_at)
40
+ VALUES ('\$AGENT_ID', 'forgecode', 'editing', 'session-scope', 'Session \$AGENT_ID active', datetime('now'));
41
+ " 2>/dev/null || true
42
+
43
+ # Detect overlaps from other agents
44
+ local OVERLAPS=\$(sqlite3 "\$COORD_DB" "
45
+ SELECT agent_id || ' on ' || resource || ' (' || intent_type || ')'
46
+ FROM work_announcements
47
+ WHERE completed_at IS NULL AND agent_id != '\$AGENT_ID'
48
+ ORDER BY announced_at DESC LIMIT 5;
49
+ " 2>/dev/null || true)
50
+
51
+ if [ -n "\$OVERLAPS" ]; then
52
+ echo "[UAP-ForgeCode] OVERLAP DETECTED: \$OVERLAPS" >&2
53
+ fi
54
+
55
+ export UAP_AGENT_ID="\$AGENT_ID"
56
+ fi
57
+
58
+ # Policy summary with enforcement details
59
+ if [ -f "\$POLICY_DB" ] && command -v sqlite3 &> /dev/null; then
60
+ local ACTIVE_POLICIES=\$(sqlite3 "\$POLICY_DB" "SELECT COUNT(*) FROM policies WHERE isActive=1;" 2>/dev/null || echo 0)
61
+ local REQUIRED_POLICIES=\$(sqlite3 "\$POLICY_DB" "SELECT COUNT(*) FROM policies WHERE isActive=1 AND level='REQUIRED';" 2>/dev/null || echo 0)
62
+ echo "[UAP-ForgeCode] Policies: \$ACTIVE_POLICIES active (\$REQUIRED_POLICIES REQUIRED)" >&2
63
+
64
+ # Inject full policy enforcement context
65
+ local POLICY_LIST=\$(sqlite3 "\$POLICY_DB" "
66
+ SELECT '[' || level || '] ' || name || ' (' || category || ')'
67
+ FROM policies WHERE isActive=1 ORDER BY level DESC, priority DESC;
68
+ " 2>/dev/null || true)
69
+
70
+ if [ -n "\$POLICY_LIST" ]; then
71
+ echo "[UAP-ForgeCode] Active policy enforcement:" >&2
72
+ echo "\$POLICY_LIST" | while IFS= read -r line; do
73
+ echo "[UAP-ForgeCode] \$line" >&2
74
+ done
75
+ fi
22
76
  fi
23
77
 
24
78
  # Inject recent memory context via environment variable for ForgeCode to read
25
79
  local MEMORIES=""
26
-
80
+
27
81
  if command -v sqlite3 &> /dev/null; then
28
- MEMORIES=\$(sqlite3 "\$PROJECT_DIR/./agents/data/memory/short_term.db" "
82
+ MEMORIES=\$(sqlite3 "\$DB_PATH" "
29
83
  SELECT type, substr(content,1,80) FROM memories
30
84
  WHERE timestamp >= datetime('now', '-1 day')
31
85
  ORDER BY id DESC LIMIT 5;
32
86
  " 2>/dev/null || true)
33
-
87
+
34
88
  if [ -n "\$MEMORIES" ]; then
35
- export UAP_CONTEXT="\$MEMORIES"
36
- echo "[UAP-ForgCode] Memory context loaded for session (\$(echo \"\$MEMORIES\" | wc -l)) recent items)" >&2
89
+ export UAP_CONTEXT="\$MEMORIES"
90
+ echo "[UAP-ForgeCode] Memory context loaded (\$(echo \"\$MEMORIES\" | wc -l) recent items)" >&2
37
91
  fi
38
92
  fi
39
-
93
+
40
94
  # Load open loops/high importance decisions from session memories
41
95
  local OPEN_LOOPS=""
42
-
96
+
43
97
  if command -v sqlite3 &> /dev/null; then
44
- OPEN_LOOPS=\$(sqlite3 "\$PROJECT_DIR/./agents/data/memory/short_term.db" "
98
+ OPEN_LOOPS=\$(sqlite3 "\$DB_PATH" "
45
99
  SELECT content FROM session_memories
46
100
  WHERE type IN ('action','goal','decision') AND importance >= 7
47
101
  ORDER BY id DESC LIMIT 5;
48
102
  " 2>/dev/null || true)
49
-
103
+
50
104
  if [ -n "\$OPEN_LOOPS" ]; then
51
- export UAP_OPEN_LOOPS="\$OPEN_LOOPS"
52
- echo "[UAP-ForgCode] ℹ️ Open loops detected (\$(echo \"\$OPEN_LOOPS\" | grep -c .)) high-priority items pending" >&2
105
+ export UAP_OPEN_LOOPS="\$OPEN_LOOPS"
106
+ echo "[UAP-ForgeCode] Open loops: \$(echo \"\$OPEN_LOOPS\" | grep -c .) high-priority items pending" >&2
53
107
  fi
54
108
  fi
55
-
109
+
56
110
  # Warn about stale worktrees if any exist
57
- local WORKTREES_DIR="\$PROJECT_DIR/.worktrees"
111
+ local WORKTREES_DIR="\$PROJECT_DIR/.worktrees"
58
112
  if [ -d "\$WORKTREES_DIR" ]; then
59
113
  local STALE_COUNT=\$(find "\$WORKTREES_DIR" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | wc -l)
60
- if [ "\$STALE_COUNT" -gt 0 ] && command -v find &> /dev/null; then
61
- echo "[UAP-ForgCode] ⚠️ Stale worktrees: \$STALE_COUNT (run 'uap worktree list' to cleanup)" >&2
114
+ if [ "\$STALE_COUNT" -gt 0 ]; then
115
+ echo "[UAP-ForgeCode] Worktrees: \$STALE_COUNT active (run 'uap worktree list' to review)" >&2
62
116
  fi
63
- fi
117
+ fi
64
118
  }
65
119
 
66
120
  _uap_forgecode_pre_compact() {
67
121
  # Save compaction marker before model switches in ForgeCode session
68
-
122
+
123
+ local PROJECT_DIR="${FORGE_UAP_PROJECT:-.}"
124
+ local DB_PATH="\$PROJECT_DIR/agents/data/memory/short_term.db"
125
+ local COORD_DB="\$PROJECT_DIR/agents/data/coordination/coordination.db"
126
+
127
+ if [ ! -f "\$DB_PATH" ]; then return 0; fi
128
+
129
+ local TIMESTAMP=\$(date -u +"%Y-%m-%dT%H:%M:%SZ")
130
+
131
+ # Record compaction event in memory database
132
+ sqlite3 "\$DB_PATH" "INSERT OR IGNORE INTO memories (timestamp, type, content) VALUES ('\${TIMESTAMP}', 'action', '[pre-compact] Context saved before model switch');" 2>/dev/null || true
133
+
134
+ # Flush pending deploys before session ends
135
+ if [ -f "\$COORD_DB" ] && command -v sqlite3 &> /dev/null; then
136
+ local PENDING=\$(sqlite3 "\$COORD_DB" "SELECT COUNT(*) FROM deploy_queue WHERE status='pending';" 2>/dev/null || echo 0)
137
+ if [ "\$PENDING" -gt 0 ] 2>/dev/null && [ -f "\$PROJECT_DIR/dist/bin/cli.js" ]; then
138
+ echo "[UAP-ForgeCode] Flushing \$PENDING pending deploy actions..." >&2
139
+ node "\$PROJECT_DIR/dist/bin/cli.js" deploy flush 2>/dev/null || true
140
+ fi
141
+ fi
142
+
143
+ # Close announcements and deregister agent
144
+ if [ -f "\$COORD_DB" ] && [ -n "\${UAP_AGENT_ID:-}" ]; then
145
+ sqlite3 "\$COORD_DB" "
146
+ UPDATE work_announcements SET completed_at=datetime('now') WHERE agent_id='\$UAP_AGENT_ID' AND completed_at IS NULL;
147
+ UPDATE agent_registry SET status='completed' WHERE id='\$UAP_AGENT_ID';
148
+ " 2>/dev/null || true
149
+ fi
150
+
151
+ echo "[UAP-ForgeCode] Pre-compaction marker saved at \$TIMESTAMP" >&2
152
+ }
153
+
154
+ _uap_forgecode_pre_edit_guard() {
155
+ # Guard against edits outside worktrees. Call before any file edit.
156
+ # Usage: _uap_forgecode_pre_edit_guard "/path/to/file"
157
+ # Returns 0 if allowed, 1 if blocked.
158
+
159
+ local FILE_PATH="\$1"
160
+ local PROJECT_DIR="${FORGE_UAP_PROJECT:-.}"
161
+
162
+ if [ -z "\$FILE_PATH" ]; then
163
+ echo "[UAP-ForgeCode] pre-edit-guard: no file path provided" >&2
164
+ return 1
165
+ fi
166
+
167
+ # Exempt paths that don't require worktree enforcement
168
+ case "\$FILE_PATH" in
169
+ */agents/data/*|*/node_modules/*|*/.uap-backups/*|*/.uap/*|*/.git/*|*/dist/*)
170
+ return 0
171
+ ;;
172
+ esac
173
+
174
+ # Check if path is inside .worktrees/
175
+ if echo "\$FILE_PATH" | grep -q '\.worktrees/' 2>/dev/null; then
176
+ return 0
177
+ fi
178
+
179
+ echo "[UAP-ForgeCode] WORKTREE VIOLATION: Edit blocked for '\$FILE_PATH'" >&2
180
+ echo "[UAP-ForgeCode] File is not inside .worktrees/ and is not an exempt path." >&2
181
+ echo "[UAP-ForgeCode] Run 'uap worktree create <slug>' first, then edit inside .worktrees/NNN-<slug>/." >&2
182
+ return 1
183
+ }
184
+
185
+ _uap_forgecode_pre_bash_guard() {
186
+ # Guard against dangerous bash commands. Call before executing commands.
187
+ # Usage: _uap_forgecode_pre_bash_guard "command string"
188
+ # Returns 0 if allowed, 1 if blocked.
189
+
190
+ local CMD="\$1"
191
+ local PROJECT_DIR="${FORGE_UAP_PROJECT:-.}"
192
+
193
+ if [ -z "\$CMD" ]; then
194
+ return 0
195
+ fi
196
+
197
+ # Block terraform apply/destroy
198
+ if echo "\$CMD" | grep -qE 'terraform\s+(apply|destroy)' 2>/dev/null; then
199
+ echo "[UAP-ForgeCode] BLOCKED: terraform apply/destroy requires explicit approval outside ForgeCode." >&2
200
+ return 1
201
+ fi
202
+
203
+ # Block git push --force
204
+ if echo "\$CMD" | grep -qE 'git\s+push\s+.*--force' 2>/dev/null; then
205
+ echo "[UAP-ForgeCode] BLOCKED: git push --force is prohibited. Use regular push or --force-with-lease." >&2
206
+ return 1
207
+ fi
208
+
209
+ # Block git commit on master/main outside worktrees
210
+ if echo "\$CMD" | grep -qE 'git\s+commit' 2>/dev/null; then
211
+ local CURRENT_BRANCH=\$(git -C "\$PROJECT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
212
+ local IN_WORKTREE="false"
213
+ if echo "\$PWD" | grep -q '\.worktrees/' 2>/dev/null; then
214
+ IN_WORKTREE="true"
215
+ fi
216
+ if [ "\$IN_WORKTREE" = "false" ] && echo "\$CURRENT_BRANCH" | grep -qE '^(main|master)\$' 2>/dev/null; then
217
+ echo "[UAP-ForgeCode] BLOCKED: git commit on \$CURRENT_BRANCH outside a worktree is prohibited." >&2
218
+ echo "[UAP-ForgeCode] Create a worktree first: uap worktree create <slug>" >&2
219
+ return 1
220
+ fi
221
+ fi
222
+
223
+ # Block git push to master/main
224
+ if echo "\$CMD" | grep -qE 'git\s+push\s+(origin\s+)?(main|master)' 2>/dev/null; then
225
+ echo "[UAP-ForgeCode] BLOCKED: Direct push to main/master is prohibited. Use a PR workflow." >&2
226
+ return 1
227
+ fi
228
+
229
+ # Block sed/awk editing package.json version
230
+ if echo "\$CMD" | grep -qE '(sed|awk).*package\.json.*version|version.*package\.json.*(sed|awk)' 2>/dev/null; then
231
+ echo "[UAP-ForgeCode] BLOCKED: Manual version edits in package.json are prohibited." >&2
232
+ echo "[UAP-ForgeCode] Use: npm run version:patch / version:minor / version:major" >&2
233
+ return 1
234
+ fi
235
+
236
+ return 0
237
+ }
238
+
239
+ _uap_forgecode_post_edit_reminder() {
240
+ # Remind about build gates and backups after file edits.
241
+ # Usage: _uap_forgecode_post_edit_reminder "/path/to/file"
242
+
243
+ local FILE_PATH="\$1"
244
+ local PROJECT_DIR="${FORGE_UAP_PROJECT:-.}"
245
+
246
+ if [ -z "\$FILE_PATH" ]; then
247
+ return 0
248
+ fi
249
+
250
+ # TypeScript build gate reminder
251
+ case "\$FILE_PATH" in
252
+ *.ts|*.tsx)
253
+ echo "[UAP-ForgeCode] BUILD GATE: '\$FILE_PATH' was edited. Run 'npm run build' before editing the next file." >&2
254
+ ;;
255
+ esac
256
+
257
+ # Check if backup exists for today
258
+ local TODAY=\$(date +%Y-%m-%d)
259
+ local BASENAME=\$(basename "\$FILE_PATH")
260
+ local BACKUP_DIR="\$PROJECT_DIR/.uap-backups/\$TODAY"
261
+
262
+ if [ ! -d "\$BACKUP_DIR" ] || [ ! -f "\$BACKUP_DIR/\$BASENAME" ]; then
263
+ echo "[UAP-ForgeCode] BACKUP MISSING: No backup found for '\$BASENAME' in .uap-backups/\$TODAY/" >&2
264
+ echo "[UAP-ForgeCode] Run: mkdir -p .uap-backups/\$TODAY && cp \$FILE_PATH .uap-backups/\$TODAY/" >&2
265
+ fi
266
+ }
267
+
268
+ _uap_forgecode_completion_gate() {
269
+ # Check completion gates before claiming DONE.
270
+ # Prints a checklist of required items.
271
+ # Usage: _uap_forgecode_completion_gate
272
+
69
273
  local PROJECT_DIR="${FORGE_UAP_PROJECT:-.}"
70
-
71
- if [ ! -f "\$PROJECT_DIR/./agents/data/memory/short_term.db" ]; then exit 0; fi
72
274
 
275
+ echo "[UAP-ForgeCode] === COMPLETION GATE CHECKLIST ===" >&2
276
+
277
+ # Check for code changes
278
+ local CODE_CHANGES=\$(git -C "\$PROJECT_DIR" diff --name-only HEAD 2>/dev/null || true)
279
+ local STAGED_CHANGES=\$(git -C "\$PROJECT_DIR" diff --cached --name-only 2>/dev/null || true)
280
+ local ALL_CHANGES="\$CODE_CHANGES\$STAGED_CHANGES"
281
+
282
+ if [ -z "\$ALL_CHANGES" ]; then
283
+ echo "[UAP-ForgeCode] [?] No code changes detected (clean working tree)" >&2
284
+ return 0
285
+ fi
286
+
287
+ # Check for test file changes
288
+ local TEST_CHANGES=""
289
+ if [ -n "\$CODE_CHANGES" ]; then
290
+ TEST_CHANGES=\$(echo "\$CODE_CHANGES" | grep -E '\.test\.(ts|tsx|js|jsx)\$' 2>/dev/null || true)
291
+ fi
292
+ if [ -z "\$TEST_CHANGES" ] && [ -n "\$STAGED_CHANGES" ]; then
293
+ TEST_CHANGES=\$(echo "\$STAGED_CHANGES" | grep -E '\.test\.(ts|tsx|js|jsx)\$' 2>/dev/null || true)
294
+ fi
295
+
296
+ if [ -n "\$TEST_CHANGES" ]; then
297
+ echo "[UAP-ForgeCode] [OK] Test files modified" >&2
298
+ else
299
+ echo "[UAP-ForgeCode] [FAIL] No test files changed — at least 2 new tests required" >&2
300
+ fi
301
+
302
+ # Check for version bump (look for package.json version change in recent commits)
303
+ local VERSION_BUMPED="false"
304
+ if git -C "\$PROJECT_DIR" log --oneline -5 2>/dev/null | grep -qiE 'version|bump|release' 2>/dev/null; then
305
+ VERSION_BUMPED="true"
306
+ fi
307
+
308
+ if [ "\$VERSION_BUMPED" = "true" ]; then
309
+ echo "[UAP-ForgeCode] [OK] Version bump detected in recent commits" >&2
310
+ else
311
+ echo "[UAP-ForgeCode] [WARN] No version bump detected — run npm run version:patch/minor/major" >&2
312
+ fi
313
+
314
+ # Check build status
315
+ if [ -f "\$PROJECT_DIR/dist/bin/cli.js" ]; then
316
+ echo "[UAP-ForgeCode] [OK] Build artifacts exist (dist/)" >&2
317
+ else
318
+ echo "[UAP-ForgeCode] [FAIL] No build artifacts — run 'npm run build'" >&2
319
+ fi
320
+
321
+ # Check for TypeScript changes without build
322
+ local TS_CHANGES=""
323
+ if [ -n "\$ALL_CHANGES" ]; then
324
+ TS_CHANGES=\$(echo "\$ALL_CHANGES" | grep -E '\.(ts|tsx)\$' 2>/dev/null || true)
325
+ fi
326
+
327
+ if [ -n "\$TS_CHANGES" ]; then
328
+ echo "[UAP-ForgeCode] [REMIND] TypeScript files changed — verify 'npm run build' passes" >&2
329
+ echo "[UAP-ForgeCode] [REMIND] Run 'tsc --noEmit' for type-check" >&2
330
+ fi
331
+
332
+ echo "[UAP-ForgeCode] === Run all gates: npm run build && npm test ===" >&2
333
+ }
334
+
335
+ _uap_forgecode_session_end() {
336
+ # Clean up session resources on ForgeCode session end.
337
+ # Usage: _uap_forgecode_session_end
338
+
339
+ local PROJECT_DIR="${FORGE_UAP_PROJECT:-.}"
340
+ local DB_PATH="\$PROJECT_DIR/agents/data/memory/short_term.db"
341
+ local COORD_DB="\$PROJECT_DIR/agents/data/coordination/coordination.db"
73
342
  local TIMESTAMP=\$(date -u +"%Y-%m-%dT%H:%M:%SZ")
74
-
75
- # Record compaction event in memory database for session tracking
76
- sqlite3 "\$PROJECT_DIR/./agents/data/memory/short_term.db" "INSERT OR IGNORE INTO memories (timestamp, type, content) VALUES ('\${TIMESTAMP}', 'action', '[pre-compact] Context saved before model switch');" 2>/dev/null || true
77
-
78
- echo "[UAP-ForgCode] Pre-compaction marker saved at \$TIMESTAMP" >&2
343
+
344
+ # Store session end marker in memory DB
345
+ if [ -f "\$DB_PATH" ] && command -v sqlite3 &> /dev/null; then
346
+ sqlite3 "\$DB_PATH" "
347
+ INSERT OR IGNORE INTO memories (timestamp, type, content)
348
+ VALUES ('\$TIMESTAMP', 'action', '[session-end] ForgeCode session ended at \$TIMESTAMP');
349
+ " 2>/dev/null || true
350
+ echo "[UAP-ForgeCode] Session end marker stored at \$TIMESTAMP" >&2
351
+ fi
352
+
353
+ # Clean up agent registration and work claims
354
+ if [ -f "\$COORD_DB" ] && command -v sqlite3 &> /dev/null && [ -n "\${UAP_AGENT_ID:-}" ]; then
355
+ sqlite3 "\$COORD_DB" "
356
+ UPDATE work_announcements SET completed_at=datetime('now')
357
+ WHERE agent_id='\$UAP_AGENT_ID' AND completed_at IS NULL;
358
+ DELETE FROM work_claims WHERE agent_id='\$UAP_AGENT_ID';
359
+ UPDATE agent_registry SET status='completed'
360
+ WHERE id='\$UAP_AGENT_ID';
361
+ " 2>/dev/null || true
362
+ echo "[UAP-ForgeCode] Agent \$UAP_AGENT_ID deregistered" >&2
363
+ fi
364
+
365
+ # Clean up old backups (older than 7 days)
366
+ local BACKUP_DIR="\$PROJECT_DIR/.uap-backups"
367
+ if [ -d "\$BACKUP_DIR" ]; then
368
+ find "\$BACKUP_DIR" -maxdepth 1 -mindepth 1 -type d -mtime +7 -exec rm -rf {} \; 2>/dev/null || true
369
+ echo "[UAP-ForgeCode] Cleaned up backups older than 7 days" >&2
370
+ fi
371
+
372
+ unset UAP_AGENT_ID
373
+ unset UAP_CONTEXT
374
+ unset UAP_OPEN_LOOPS
79
375
  }
80
376
 
81
- # Auto-register hooks with ForgeCode's : command via environment variables
377
+ # Auto-register hooks with ForgeCode's : command via environment variables
82
378
  _uap_forgecode_register() {
83
379
  # Add to ~/.zshrc if not already present (user confirmation required)
84
380
  local ZSHRC="\$HOME/.zshrc"
85
-
381
+
86
382
  if [ -f "\$ZSHRC" ] && ! grep -q "UAP_FORGE_CODE_PLUGIN" "\$ZSHRC"; then
87
383
  echo '# UAP ForgeCode Integration' >> "\$ZSHRC"
88
- echo 'source "$PWD/.forge/forgecode.plugin.sh"' >> "\$ZSHRC"
384
+ echo 'source "$PWD/.forge/forgecode.plugin.sh"' >> "\$ZSHRC"
89
385
  echo '[UAP-ForgCode] Added plugin source to ~/.zshrc - run "source \$HOME/.zshrc" or restart terminal to activate' >&2
90
386
  fi
91
-
387
+
92
388
  # Create config file for ForgeCode settings
93
389
  local CONFIG_PATH="\$PROJECT_DIR/.forge/settings.local.json"
94
-
390
+
95
391
  if [ ! -f "\$CONFIG_PATH" ]; then
96
392
  cat > "\$CONFIG_PATH" <<EOF
97
393
  {
98
394
  "uapIntegration": {
99
395
  "enabled": true,
100
396
  "memoryDbPath": "./agents/data/memory/short_term.db",
101
- "coordinationDbPath": "./agents/data/coordination/coordination.db",
102
- "hooksEnabled": ["sessionStart", "preCompact"]
397
+ "coordinationDbPath": "./agents/data/coordination/coordination.db",
398
+ "hooksEnabled": ["sessionStart", "preCompact", "preEditGuard", "preBashGuard", "postEditReminder", "completionGate", "sessionEnd"]
103
399
  },
104
400
  "worktrees": {
105
401
  "enabled": true,
106
402
  "directory": ".worktrees"
107
403
  }
108
404
  }
109
- EOF
110
- echo "[UAP-ForgCode] Created .forge/settings.local.json with UAP integration config" >&2
405
+ EOF
406
+ echo "[UAP-ForgCode] Created .forge/settings.local.json with UAP integration config" >&2
111
407
  fi
112
-
408
+
113
409
  # Add to gitignore if not present
114
410
  local GITIGNORE="\$PROJECT_DIR/.gitignore"
115
411
  if [ -f "\$GITIGNORE" ] && ! grep -q "settings.local.json" "\$GITIGNORE"; then
116
- echo "# UAP ForgeCode settings (auto-generated)" >> "\$GITIGNORE"
412
+ echo "# UAP ForgeCode settings (auto-generated)" >> "\$GITIGNORE"
117
413
  echo ".forge/settings.local.json" >> "\$GITIGNORE"
118
- echo "[UAP-ForgCode] Added .forge/ to gitignore for local config files" >&2
414
+ echo "[UAP-ForgCode] Added .forge/ to gitignore for local config files" >&2
119
415
  fi
120
-
416
+
121
417
  # Auto-activate hooks in current session if running interactively
122
- _uap_forgecode_session_start &>/dev/null || true
418
+ _uap_forgecode_session_start &>/dev/null || true
123
419
  }
124
420
 
125
421
  # Export functions so they can be sourced by other scripts
126
422
  export -f _uap_forgecode_session_start \
127
423
  _uap_forgecode_pre_compact \
424
+ _uap_forgecode_pre_edit_guard \
425
+ _uap_forgecode_pre_bash_guard \
426
+ _uap_forgecode_post_edit_reminder \
427
+ _uap_forgecode_completion_gate \
428
+ _uap_forgecode_session_end \
128
429
  _uap_forgecode_register;
@@ -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
@@ -56,6 +56,20 @@ if [ -f "${PROJECT_DIR}/dist/bin/cli.js" ]; then
56
56
  node "${PROJECT_DIR}/dist/bin/cli.js" dash session --compact 2>/dev/null || true
57
57
  fi
58
58
 
59
+ # Flush pending deploy queue before session ends
60
+ if [ -f "$COORD_DB" ]; then
61
+ PENDING_DEPLOYS=$(sqlite3 "$COORD_DB" "
62
+ SELECT COUNT(*) FROM deploy_queue WHERE status='pending';
63
+ " 2>/dev/null || echo "0")
64
+
65
+ if [ "$PENDING_DEPLOYS" -gt 0 ] 2>/dev/null; then
66
+ echo "[UAP] Flushing $PENDING_DEPLOYS pending deploy actions before compaction..." >&2
67
+ if [ -f "${PROJECT_DIR}/dist/bin/cli.js" ]; then
68
+ node "${PROJECT_DIR}/dist/bin/cli.js" deploy flush 2>/dev/null || true
69
+ fi
70
+ fi
71
+ fi
72
+
59
73
  # Clean up agents with recent heartbeats (likely from this session being compacted)
60
74
  if [ -f "$COORD_DB" ]; then
61
75
  sqlite3 "$COORD_DB" "