@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/cli/hooks.js +151 -45
- package/dist/cli/hooks.js.map +1 -1
- package/dist/coordination/service.d.ts +11 -0
- package/dist/coordination/service.d.ts.map +1 -1
- package/dist/coordination/service.js +34 -3
- package/dist/coordination/service.js.map +1 -1
- package/dist/dashboard/data-seeder.d.ts +33 -0
- package/dist/dashboard/data-seeder.d.ts.map +1 -0
- package/dist/dashboard/data-seeder.js +247 -0
- package/dist/dashboard/data-seeder.js.map +1 -0
- package/dist/dashboard/data-service.d.ts +99 -0
- package/dist/dashboard/data-service.d.ts.map +1 -1
- package/dist/dashboard/data-service.js +549 -31
- package/dist/dashboard/data-service.js.map +1 -1
- package/dist/dashboard/index.d.ts +4 -2
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +2 -1
- package/dist/dashboard/index.js.map +1 -1
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +17 -0
- package/dist/dashboard/server.js.map +1 -1
- package/package.json +1 -1
- package/templates/hooks/forgecode.plugin.sh +348 -47
- package/templates/hooks/post-compact.sh +111 -0
- package/templates/hooks/post-tool-use-edit-write.sh +38 -0
- package/templates/hooks/pre-compact.sh +14 -0
- package/templates/hooks/pre-tool-use-bash.sh +79 -0
- package/templates/hooks/pre-tool-use-edit-write.sh +44 -0
- package/templates/hooks/session-end.sh +25 -40
- package/templates/hooks/stop.sh +142 -0
|
@@ -1,128 +1,429 @@
|
|
|
1
1
|
# UAP ForgeCode Integration Plugin
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
7
|
-
|
|
6
|
+
# Load recent memory context from UAP database before command execution
|
|
7
|
+
|
|
8
8
|
local PROJECT_DIR="${FORGE_UAP_PROJECT:-.}"
|
|
9
|
-
|
|
10
|
-
|
|
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 "\$
|
|
14
|
-
sqlite3 "\$
|
|
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 "\$
|
|
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-
|
|
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 "\$
|
|
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-
|
|
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 ]
|
|
61
|
-
echo "[UAP-
|
|
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
|
-
#
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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]
|
|
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]
|
|
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" "
|