trellis 3.1.20 → 3.1.30

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.
@@ -619,6 +619,44 @@ var init_profile = () => {};
619
619
  // src/scaffold/write.ts
620
620
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync3 } from "fs";
621
621
  import { join as join4 } from "path";
622
+ function writeTrellisHooks(rootPath, ide) {
623
+ const hooksDir = join4(rootPath, ".cursor", "hooks");
624
+ const trellisHarnessDir = join4(hooksDir, "trellis-harness");
625
+ const adaptersDir = join4(hooksDir, "adapters");
626
+ if (!existsSync3(trellisHarnessDir)) {
627
+ mkdirSync2(trellisHarnessDir, { recursive: true });
628
+ }
629
+ if (!existsSync3(adaptersDir)) {
630
+ mkdirSync2(adaptersDir, { recursive: true });
631
+ }
632
+ writeFileSync2(join4(trellisHarnessDir, "pre-prompt-recall.sh"), renderPrePromptRecallScript(), "utf-8");
633
+ writeFileSync2(join4(trellisHarnessDir, "post-tool-oplog.sh"), renderPostToolOplogScript(), "utf-8");
634
+ writeFileSync2(join4(trellisHarnessDir, "post-tool-memory-capture.sh"), renderPostToolMemoryCaptureScript(), "utf-8");
635
+ writeFileSync2(join4(trellisHarnessDir, "stop-triage.sh"), renderStopTriageScript(), "utf-8");
636
+ writeFileSync2(join4(trellisHarnessDir, "bug-intake.sh"), renderBugIntakeScript(), "utf-8");
637
+ writeFileSync2(join4(trellisHarnessDir, "bug-investigate.sh"), renderBugInvestigateScript(), "utf-8");
638
+ writeFileSync2(join4(trellisHarnessDir, "milestone-triage.sh"), renderMilestoneTriageScript(), "utf-8");
639
+ writeFileSync2(join4(trellisHarnessDir, "cycle-planning.sh"), renderCyclePlanningScript(), "utf-8");
640
+ const adapterName = ide === "windsurf" ? "cascade-adapter.sh" : `${ide}-adapter.sh`;
641
+ writeFileSync2(join4(adaptersDir, adapterName), renderAdapterScript(ide), "utf-8");
642
+ switch (ide) {
643
+ case "cursor":
644
+ writeFileSync2(join4(rootPath, ".cursor", "hooks.json"), JSON.stringify(renderCursorHooksConfig(), null, 2), "utf-8");
645
+ break;
646
+ case "windsurf":
647
+ writeFileSync2(join4(rootPath, ".windsurf", "hooks.json"), JSON.stringify(renderWindsurfHooksConfig(), null, 2), "utf-8");
648
+ break;
649
+ case "claude":
650
+ writeFileSync2(join4(rootPath, ".claude", "settings.local.json"), JSON.stringify(renderClaudeHooksConfig(), null, 2), "utf-8");
651
+ break;
652
+ case "codex":
653
+ writeFileSync2(join4(rootPath, ".codex", "hooks.json"), JSON.stringify(renderCodexHooksConfig(), null, 2), "utf-8");
654
+ break;
655
+ case "gemini":
656
+ writeFileSync2(join4(rootPath, ".gemini", "settings.json"), JSON.stringify(renderGeminiHooksConfig(), null, 2), "utf-8");
657
+ break;
658
+ }
659
+ }
622
660
  function renderAgentsMd(profile, context) {
623
661
  const userName = profile?.name ?? "the user";
624
662
  const userBio = profile?.bio || "(No bio provided \u2014 run `trellis season` to set up your profile.)";
@@ -899,6 +937,800 @@ function renderGeminiConfig(input) {
899
937
  features: input.plugins
900
938
  };
901
939
  }
940
+ function renderPrePromptRecallScript() {
941
+ return `#!/usr/bin/env bash
942
+ # Pre-prompt memory and context recall for Trellis integration
943
+ # Normalized contract: TRELLIS_ORIGIN, TRELLIS_DESK_ROOT, TRELLIS_HOOK_OUTPUT
944
+ set -euo pipefail
945
+
946
+ # Import desk root detection
947
+ source "$(dirname "$0")/../../desk-root.sh"
948
+
949
+ # Environment variables from contract
950
+ ORIGIN="\${TRELLIS_ORIGIN:-unknown}"
951
+ OUTPUT="\${TRELLIS_HOOK_OUTPUT:-stdout}"
952
+
953
+ # Only run if we're in a Trellis workspace
954
+ if ! command -v trellis >/dev/null 2>&1; then
955
+ exit 0
956
+ fi
957
+
958
+ # Try to get Trellis context (non-blocking)
959
+ if [ -f ".trellis/config.json" ]; then
960
+ # Query recent memories and entities for context
961
+ CONTEXT_OUTPUT=$(trellis query --limit 5 --format json 2>/dev/null || echo '{"entities":[],"relations":[]}')
962
+
963
+ # Query recent operations for context
964
+ OPS_OUTPUT=$(trellis log --limit 3 --format json 2>/dev/null || echo '{"operations":[]}')
965
+
966
+ # Query active issues
967
+ ISSUES_OUTPUT=$(trellis issue list --status active --format json 2>/dev/null || echo '{"issues":[]}')
968
+
969
+ # Format output based on hook type
970
+ case "$OUTPUT" in
971
+ "agent-stop")
972
+ # For Codex/Gemini blocking hooks
973
+ echo "{}"
974
+ ;;
975
+ "gemini")
976
+ # For Gemini CLI format
977
+ cat << EOF
978
+ {
979
+ "decision": "allow",
980
+ "context": {
981
+ "memories": $CONTEXT_OUTPUT,
982
+ "operations": $OPS_OUTPUT,
983
+ "issues": $ISSUES_OUTPUT,
984
+ "origin": "$ORIGIN"
985
+ }
986
+ }
987
+ EOF
988
+ ;;
989
+ *)
990
+ # Default stdout for Cursor/Windsurf/Claude
991
+ echo ""
992
+ echo "\uD83C\uDF3F Trellis Context ($ORIGIN):"
993
+ echo " Recent memories: $(echo "$CONTEXT_OUTPUT" | jq -r '.entities | length' 2>/dev/null || echo "0")"
994
+ echo " Recent ops: $(echo "$OPS_OUTPUT" | jq -r '.operations | length' 2>/dev/null || echo "0")"
995
+ echo " Active issues: $(echo "$ISSUES_OUTPUT" | jq -r '.issues | length' 2>/dev/null || echo "0")"
996
+ echo ""
997
+ ;;
998
+ esac
999
+ fi
1000
+
1001
+ exit 0`;
1002
+ }
1003
+ function renderPostToolOplogScript() {
1004
+ return `#!/usr/bin/env bash
1005
+ # Post-tool operation logging for Trellis integration
1006
+ # Normalized contract: TRELLIS_ORIGIN, TRELLIS_DESK_ROOT, stdin with tool data
1007
+ set -euo pipefail
1008
+
1009
+ # Import desk root detection
1010
+ source "$(dirname "$0")/../../desk-root.sh"
1011
+
1012
+ # Environment variables from contract
1013
+ ORIGIN="\${TRELLIS_ORIGIN:-unknown}"
1014
+
1015
+ # Read tool data from stdin (normalized format)
1016
+ TOOL_DATA=$(cat)
1017
+
1018
+ # Extract relevant information from tool data
1019
+ # Expected format: JSON with tool, action, file_path, etc.
1020
+ TOOL_NAME=$(echo "$TOOL_DATA" | jq -r '.tool // "unknown"' 2>/dev/null || echo "unknown")
1021
+ ACTION=$(echo "$TOOL_DATA" | jq -r '.action // "unknown"' 2>/dev/null || echo "unknown")
1022
+ FILE_PATH=$(echo "$TOOL_DATA" | jq -r '.file_path // .filePath // .path // ""' 2>/dev/null || echo "")
1023
+
1024
+ # Only run if we're in a Trellis workspace
1025
+ if ! command -v trellis >/dev/null 2>&1; then
1026
+ exit 0
1027
+ fi
1028
+
1029
+ if [ -f ".trellis/config.json" ]; then
1030
+ # Create operation log entry
1031
+ OP_ENTRY=$(cat << EOF
1032
+ {
1033
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)",
1034
+ "origin": "$ORIGIN",
1035
+ "tool": "$TOOL_NAME",
1036
+ "action": "$ACTION",
1037
+ "target": "$FILE_PATH",
1038
+ "type": "agent-operation"
1039
+ }
1040
+ EOF
1041
+ )
1042
+
1043
+ # Try to append to Trellis op log (non-blocking)
1044
+ # Note: This would need a corresponding trellis operation command
1045
+ # For now, we'll create a local log file that can be imported
1046
+ LOG_DIR=".trellis/agent-ops"
1047
+ mkdir -p "$LOG_DIR"
1048
+
1049
+ LOG_FILE="$LOG_DIR/ops-$(date +%Y-%m-%d).jsonl"
1050
+ echo "$OP_ENTRY" >> "$LOG_FILE"
1051
+
1052
+ # Keep only last 7 days of logs
1053
+ find "$LOG_DIR" -name "ops-*.jsonl" -mtime +7 -delete 2>/dev/null || true
1054
+ fi
1055
+
1056
+ exit 0`;
1057
+ }
1058
+ function renderPostToolMemoryCaptureScript() {
1059
+ return `#!/usr/bin/env bash
1060
+ # Post-tool memory and entity capture for Trellis integration
1061
+ # Normalized contract: TRELLIS_ORIGIN, TRELLIS_DESK_ROOT, stdin with tool data
1062
+ set -euo pipefail
1063
+
1064
+ # Import desk root detection
1065
+ source "$(dirname "$0")/../../desk-root.sh"
1066
+
1067
+ # Environment variables from contract
1068
+ ORIGIN="\${TRELLIS_ORIGIN:-unknown}"
1069
+
1070
+ # Read tool data from stdin
1071
+ TOOL_DATA=$(cat)
1072
+
1073
+ # Extract relevant information
1074
+ TOOL_NAME=$(echo "$TOOL_DATA" | jq -r '.tool // "unknown"' 2>/dev/null || echo "unknown")
1075
+ ACTION=$(echo "$TOOL_DATA" | jq -r '.action // "unknown"' 2>/dev/null || echo "unknown")
1076
+ FILE_PATH=$(echo "$TOOL_DATA" | jq -r '.file_path // .filePath // .path // ""' 2>/dev/null || echo "")
1077
+
1078
+ # Only run if we're in a Trellis workspace
1079
+ if ! command -v trellis >/dev/null 2>&1; then
1080
+ exit 0
1081
+ fi
1082
+
1083
+ if [ -f ".trellis/config.json" ]; then
1084
+ # Suggest memory creation for significant operations
1085
+ case "$TOOL_NAME-$ACTION" in
1086
+ "edit-create"|"write-create"|"Edit-create"|"Write-create")
1087
+ # File creation - suggest creating a memory
1088
+ if [ -n "$FILE_PATH" ]; then
1089
+ echo "\uD83E\uDDE0 Consider creating a memory for the new file: $FILE_PATH"
1090
+ echo " Run: trellis memory create -t "Created $FILE_PATH" -c "Created via $ORIGIN agent""
1091
+ fi
1092
+ ;;
1093
+ "edit-update"|"Edit-update")
1094
+ # File update - suggest updating memory if significant
1095
+ if [ -n "$FILE_PATH" ]; then
1096
+ echo "\uD83D\uDD04 Consider updating relevant memories for: $FILE_PATH"
1097
+ echo " Run: trellis memory list -q "$FILE_PATH""
1098
+ fi
1099
+ ;;
1100
+ "issue-create"|"issue-update")
1101
+ # Issue operations - always suggest memory
1102
+ echo "\uD83D\uDCDD Consider creating a memory for this issue operation"
1103
+ echo " Run: trellis memory create -t "Issue $ACTION via $ORIGIN""
1104
+ ;;
1105
+ esac
1106
+
1107
+ # Store potential memory suggestions for later
1108
+ MEMORY_DIR=".trellis/agent-suggestions"
1109
+ mkdir -p "$MEMORY_DIR"
1110
+
1111
+ SUGGESTION=$(cat << EOF
1112
+ {
1113
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)",
1114
+ "origin": "$ORIGIN",
1115
+ "tool": "$TOOL_NAME",
1116
+ "action": "$ACTION",
1117
+ "target": "$FILE_PATH",
1118
+ "suggestion_type": "memory-creation"
1119
+ }
1120
+ EOF
1121
+ )
1122
+
1123
+ SUGGESTIONS_FILE="$MEMORY_DIR/suggestions-$(date +%Y-%m-%d).jsonl"
1124
+ echo "$SUGGESTION" >> "$SUGGESTIONS_FILE"
1125
+
1126
+ # Keep only last 3 days of suggestions
1127
+ find "$MEMORY_DIR" -name "suggestions-*.jsonl" -mtime +3 -delete 2>/dev/null || true
1128
+ fi
1129
+
1130
+ exit 0`;
1131
+ }
1132
+ function renderStopTriageScript() {
1133
+ return `#!/usr/bin/env bash
1134
+ # Session stop issue triage and workflow suggestions for Trellis integration
1135
+ # Normalized contract: TRELLIS_ORIGIN, TRELLIS_DESK_ROOT, TRELLIS_HOOK_OUTPUT
1136
+ set -euo pipefail
1137
+
1138
+ # Import desk root detection
1139
+ source "$(dirname "$0")/../../desk-root.sh"
1140
+
1141
+ # Environment variables from contract
1142
+ ORIGIN="\${TRELLIS_ORIGIN:-unknown}"
1143
+ OUTPUT="\${TRELLIS_HOOK_OUTPUT:-stdout}"
1144
+
1145
+ # Only run if we're in a Trellis workspace
1146
+ if ! command -v trellis >/dev/null 2>&1; then
1147
+ exit 0
1148
+ fi
1149
+
1150
+ if [ -f ".trellis/config.json" ]; then
1151
+ # Check for active issues that need attention
1152
+ ACTIVE_ISSUES=$(trellis issue list --status active --format json 2>/dev/null || echo '{"issues":[]}')
1153
+ ACTIVE_COUNT=$(echo "$ACTIVE_ISSUES" | jq -r '.issues | length' 2>/dev/null || echo "0")
1154
+
1155
+ # Check for pending memory suggestions
1156
+ MEMORY_DIR=".trellis/agent-suggestions"
1157
+ SUGGESTION_COUNT=0
1158
+ if [ -d "$MEMORY_DIR" ]; then
1159
+ SUGGESTION_COUNT=$(find "$MEMORY_DIR" -name "suggestions-*.jsonl" -exec wc -l {} ; 2>/dev/null | awk '{sum+=$1} END {print sum}' || echo "0")
1160
+ fi
1161
+
1162
+ # Check for uncommitted changes
1163
+ UNCOMMITTED=0
1164
+ if git rev-parse --git-dir >/dev/null 2>&1; then
1165
+ UNCOMMITTED=$(git status --porcelain 2>/dev/null | wc -l || echo "0")
1166
+ fi
1167
+
1168
+ # Build recommendations
1169
+ RECOMMENDATIONS=()
1170
+
1171
+ if [ "$ACTIVE_COUNT" -gt 0 ]; then
1172
+ RECOMMENDATIONS+=("\uD83C\uDFAF $ACTIVE_COUNT active issues need attention - run 'trellis issue list'")
1173
+ fi
1174
+
1175
+ if [ "$SUGGESTION_COUNT" -gt 0 ]; then
1176
+ RECOMMENDATIONS+=("\uD83E\uDDE0 $SUGGESTION_COUNT memory suggestions pending - review .trellis/agent-suggestions/")
1177
+ fi
1178
+
1179
+ if [ "$UNCOMMITTED" -gt 0 ]; then
1180
+ RECOMMENDATIONS+=("\uD83D\uDCDD $UNCOMMITTED uncommitted changes - consider committing or creating a checkpoint")
1181
+ fi
1182
+
1183
+ # Format output based on hook type
1184
+ case "$OUTPUT" in
1185
+ "agent-stop")
1186
+ # For Codex/Gemini blocking hooks
1187
+ if [ \${#RECOMMENDATIONS[@]} -gt 0 ]; then
1188
+ MESSAGE=$(printf '%s
1189
+ ' "\${RECOMMENDATIONS[@]}")
1190
+ cat << EOF
1191
+ {
1192
+ "decision": "block",
1193
+ "reason": "Trellis workflow items need attention: $MESSAGE"
1194
+ }
1195
+ EOF
1196
+ else
1197
+ echo "{}"
1198
+ fi
1199
+ ;;
1200
+ "gemini")
1201
+ # For Gemini CLI format
1202
+ if [ \${#RECOMMENDATIONS[@]} -gt 0 ]; then
1203
+ MESSAGE=$(printf '%s\\n' "\${RECOMMENDATIONS[@]}")
1204
+ cat << EOF
1205
+ {
1206
+ "decision": "deny",
1207
+ "reason": "Trellis workflow items need attention: $MESSAGE"
1208
+ }
1209
+ EOF
1210
+ else
1211
+ echo '{"decision": "allow"}'
1212
+ fi
1213
+ ;;
1214
+ *)
1215
+ # Default stdout for Cursor/Windsurf/Claude
1216
+ if [ \${#RECOMMENDATIONS[@]} -gt 0 ]; then
1217
+ echo ""
1218
+ echo "\uD83C\uDF3F Trellis Session Summary ($ORIGIN):"
1219
+ printf ' %s
1220
+ ' "\${RECOMMENDATIONS[@]}"
1221
+ echo ""
1222
+ echo "Run 'trellis status' for full workspace state"
1223
+ else
1224
+ echo "\uD83C\uDF3F Trellis: All clear! No pending items."
1225
+ fi
1226
+ ;;
1227
+ esac
1228
+ fi
1229
+
1230
+ exit 0`;
1231
+ }
1232
+ function renderAdapterScript(ide) {
1233
+ switch (ide) {
1234
+ case "cursor":
1235
+ return `#!/usr/bin/env bash
1236
+ # Cursor adapter for Trellis harness
1237
+ # Translates Cursor events to normalized contract
1238
+ set -euo pipefail
1239
+
1240
+ # Import desk root detection
1241
+ source "$(dirname "$0")/../desk-root.sh"
1242
+
1243
+ # Set origin
1244
+ export TRELLIS_ORIGIN="cursor"
1245
+ export TRELLIS_DESK_ROOT="$PWD"
1246
+
1247
+ # Route based on event type
1248
+ EVENT_TYPE="\${1:-unknown}"
1249
+
1250
+ case "$EVENT_TYPE" in
1251
+ "session-start")
1252
+ # Cursor sessionStart hook
1253
+ exec "$(dirname "$0")/../trellis-harness/pre-prompt-recall.sh"
1254
+ ;;
1255
+ "post-tool")
1256
+ # Cursor afterFileEdit hook - read stdin for tool data
1257
+ TOOL_DATA=$(cat)
1258
+
1259
+ # Transform Cursor format to normalized format
1260
+ NORMALIZED_DATA=$(cat << EOF
1261
+ {
1262
+ "tool": "edit_file",
1263
+ "action": "update",
1264
+ "file_path": $(echo "$TOOL_DATA" | jq -r '.file_path // ""' 2>/dev/null || echo '""'),
1265
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
1266
+ }
1267
+ EOF
1268
+ )
1269
+
1270
+ # Call op logger
1271
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-oplog.sh"
1272
+
1273
+ # Call memory capture
1274
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-memory-capture.sh"
1275
+ ;;
1276
+ "stop")
1277
+ # Cursor stop hook
1278
+ export TRELLIS_HOOK_OUTPUT="stdout"
1279
+ exec "$(dirname "$0")/../trellis-harness/stop-triage.sh"
1280
+ ;;
1281
+ *)
1282
+ echo "Unknown Cursor event: $EVENT_TYPE" >&2
1283
+ exit 1
1284
+ ;;
1285
+ esac`;
1286
+ case "windsurf":
1287
+ return `#!/usr/bin/env bash
1288
+ # Windsurf Cascade adapter for Trellis harness
1289
+ # Translates Cascade events to normalized contract
1290
+ set -euo pipefail
1291
+
1292
+ # Import desk root detection
1293
+ source "$(dirname "$0")/../desk-root.sh"
1294
+
1295
+ # Set origin
1296
+ export TRELLIS_ORIGIN="windsurf"
1297
+ export TRELLIS_DESK_ROOT="$PWD"
1298
+
1299
+ # Route based on event type
1300
+ EVENT_TYPE="\${1:-unknown}"
1301
+
1302
+ case "$EVENT_TYPE" in
1303
+ "pre-prompt")
1304
+ # Cascade pre_user_prompt hook
1305
+ exec "$(dirname "$0")/../trellis-harness/pre-prompt-recall.sh"
1306
+ ;;
1307
+ "post-tool")
1308
+ # Cascade post_write_code hook - read stdin for tool data
1309
+ TOOL_DATA=$(cat)
1310
+
1311
+ # Transform Cascade format to normalized format
1312
+ NORMALIZED_DATA=$(cat << EOF
1313
+ {
1314
+ "tool": "write_file",
1315
+ "action": "create",
1316
+ "file_path": $(echo "$TOOL_DATA" | jq -r '.file_path // ""' 2>/dev/null || echo '""'),
1317
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
1318
+ }
1319
+ EOF
1320
+ )
1321
+
1322
+ # Call op logger
1323
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-oplog.sh"
1324
+
1325
+ # Call memory capture
1326
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-memory-capture.sh"
1327
+ ;;
1328
+ "post-response")
1329
+ # Cascade post_cascade_response hook
1330
+ export TRELLIS_HOOK_OUTPUT="stdout"
1331
+ exec "$(dirname "$0")/../trellis-harness/stop-triage.sh"
1332
+ ;;
1333
+ *)
1334
+ echo "Unknown Cascade event: $EVENT_TYPE" >&2
1335
+ exit 1
1336
+ ;;
1337
+ esac`;
1338
+ case "claude":
1339
+ return `#!/usr/bin/env bash
1340
+ # Claude Code adapter for Trellis harness
1341
+ # Translates Claude Code events to normalized contract
1342
+ set -euo pipefail
1343
+
1344
+ # Import desk root detection
1345
+ source "$(dirname "$0")/../desk-root.sh"
1346
+
1347
+ # Set origin
1348
+ export TRELLIS_ORIGIN="claude"
1349
+ export TRELLIS_DESK_ROOT="$PWD"
1350
+
1351
+ # Route based on event type
1352
+ EVENT_TYPE="\${1:-unknown}"
1353
+
1354
+ case "$EVENT_TYPE" in
1355
+ "pre-tool-use")
1356
+ # Claude Code PreToolUse hook
1357
+ exec "$(dirname "$0")/../trellis-harness/pre-prompt-recall.sh"
1358
+ ;;
1359
+ "post-tool-use")
1360
+ # Claude Code PostToolUse hook - read stdin for tool data
1361
+ TOOL_DATA=$(cat)
1362
+
1363
+ # Transform Claude Code format to normalized format
1364
+ NORMALIZED_DATA=$(cat << EOF
1365
+ {
1366
+ "tool": $(echo "$TOOL_DATA" | jq -r '.tool_name // "unknown"' 2>/dev/null || echo '"unknown"'),
1367
+ "action": $(echo "$TOOL_DATA" | jq -r '.action // "unknown"' 2>/dev/null || echo '"unknown"'),
1368
+ "file_path": $(echo "$TOOL_DATA" | jq -r '.file_path // .filePath // ""' 2>/dev/null || echo '""'),
1369
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
1370
+ }
1371
+ EOF
1372
+ )
1373
+
1374
+ # Call op logger
1375
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-oplog.sh"
1376
+
1377
+ # Call memory capture
1378
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-memory-capture.sh"
1379
+ ;;
1380
+ "post-tool-batch")
1381
+ # Claude Code PostToolBatch hook - process multiple tools
1382
+ TOOL_DATA=$(cat)
1383
+
1384
+ # Process each tool in the batch
1385
+ echo "$TOOL_DATA" | jq -c '.[]' 2>/dev/null | while read -r tool; do
1386
+ NORMALIZED_DATA=$(cat << EOF
1387
+ {
1388
+ "tool": $(echo "$tool" | jq -r '.tool_name // "unknown"' 2>/dev/null || echo '"unknown"'),
1389
+ "action": $(echo "$tool" | jq -r '.action // "unknown"' 2>/dev/null || echo '"unknown"'),
1390
+ "file_path": $(echo "$tool" | jq -r '.file_path // .filePath // ""' 2>/dev/null || echo '""'),
1391
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
1392
+ }
1393
+ EOF
1394
+ )
1395
+
1396
+ # Call op logger
1397
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-oplog.sh"
1398
+
1399
+ # Call memory capture
1400
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-memory-capture.sh"
1401
+ done
1402
+ ;;
1403
+ "permission-denied")
1404
+ # Claude Code PermissionDenied hook
1405
+ NORMALIZED_DATA=$(cat << EOF
1406
+ {
1407
+ "tool": "permission_denied",
1408
+ "action": "blocked",
1409
+ "file_path": "",
1410
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
1411
+ }
1412
+ EOF
1413
+ )
1414
+
1415
+ # Call op logger
1416
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-oplog.sh"
1417
+ ;;
1418
+ *)
1419
+ echo "Unknown Claude Code event: $EVENT_TYPE" >&2
1420
+ exit 1
1421
+ ;;
1422
+ esac`;
1423
+ case "codex":
1424
+ return `#!/usr/bin/env bash
1425
+ # Codex adapter for Trellis harness
1426
+ # Translates Codex events to normalized contract
1427
+ set -euo pipefail
1428
+
1429
+ # Import desk root detection
1430
+ source "$(dirname "$0")/../desk-root.sh"
1431
+
1432
+ # Set origin
1433
+ export TRELLIS_ORIGIN="codex"
1434
+ export TRELLIS_DESK_ROOT="$PWD"
1435
+
1436
+ # Route based on event type
1437
+ EVENT_TYPE="\${1:-unknown}"
1438
+
1439
+ case "$EVENT_TYPE" in
1440
+ "session-start")
1441
+ # Codex SessionStart hook
1442
+ exec "$(dirname "$0")/../trellis-harness/pre-prompt-recall.sh"
1443
+ ;;
1444
+ "post-tool")
1445
+ # Codex PostToolUse hook - read stdin for tool data
1446
+ TOOL_DATA=$(cat)
1447
+
1448
+ # Transform Codex format to normalized format
1449
+ NORMALIZED_DATA=$(cat << EOF
1450
+ {
1451
+ "tool": $(echo "$TOOL_DATA" | jq -r '.tool_name // "unknown"' 2>/dev/null || echo '"unknown"'),
1452
+ "action": $(echo "$TOOL_DATA" | jq -r '.action // "unknown"' 2>/dev/null || echo '"unknown"'),
1453
+ "file_path": $(echo "$TOOL_DATA" | jq -r '.file_path // .filePath // ""' 2>/dev/null || echo '""'),
1454
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
1455
+ }
1456
+ EOF
1457
+ )
1458
+
1459
+ # Call op logger
1460
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-oplog.sh"
1461
+
1462
+ # Call memory capture
1463
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-memory-capture.sh"
1464
+ ;;
1465
+ "stop")
1466
+ # Codex Stop hook
1467
+ export TRELLIS_HOOK_OUTPUT="agent-stop"
1468
+ exec "$(dirname "$0")/../trellis-harness/stop-triage.sh"
1469
+ ;;
1470
+ *)
1471
+ echo "Unknown Codex event: $EVENT_TYPE" >&2
1472
+ exit 1
1473
+ ;;
1474
+ esac`;
1475
+ case "gemini":
1476
+ return `#!/usr/bin/env bash
1477
+ # Gemini CLI adapter for Trellis harness
1478
+ # Translates Gemini events to normalized contract
1479
+ set -euo pipefail
1480
+
1481
+ # Import desk root detection
1482
+ source "$(dirname "$0")/../desk-root.sh"
1483
+
1484
+ # Set origin
1485
+ export TRELLIS_ORIGIN="gemini"
1486
+ export TRELLIS_DESK_ROOT="$PWD"
1487
+
1488
+ # Route based on event type
1489
+ EVENT_TYPE="\${1:-unknown}"
1490
+
1491
+ case "$EVENT_TYPE" in
1492
+ "session-start"|"before-agent")
1493
+ # Gemini SessionStart or BeforeAgent hook
1494
+ exec "$(dirname "$0")/../trellis-harness/pre-prompt-recall.sh"
1495
+ ;;
1496
+ "post-tool")
1497
+ # Gemini AfterTool hook - read stdin for tool data
1498
+ TOOL_DATA=$(cat)
1499
+
1500
+ # Transform Gemini format to normalized format
1501
+ NORMALIZED_DATA=$(cat << EOF
1502
+ {
1503
+ "tool": $(echo "$TOOL_DATA" | jq -r '.tool_name // "unknown"' 2>/dev/null || echo '"unknown"'),
1504
+ "action": $(echo "$TOOL_DATA" | jq -r '.action // "unknown"' 2>/dev/null || echo '"unknown"'),
1505
+ "file_path": $(echo "$TOOL_DATA" | jq -r '.file_path // .filePath // ""' 2>/dev/null || echo '""'),
1506
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
1507
+ }
1508
+ EOF
1509
+ )
1510
+
1511
+ # Call op logger
1512
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-oplog.sh"
1513
+
1514
+ # Call memory capture
1515
+ echo "$NORMALIZED_DATA" | "$(dirname "$0")/../trellis-harness/post-tool-memory-capture.sh"
1516
+ ;;
1517
+ "after-agent")
1518
+ # Gemini AfterAgent hook
1519
+ export TRELLIS_HOOK_OUTPUT="gemini"
1520
+ exec "$(dirname "$0")/../trellis-harness/stop-triage.sh"
1521
+ ;;
1522
+ *)
1523
+ echo "Unknown Gemini event: $EVENT_TYPE" >&2
1524
+ exit 1
1525
+ ;;
1526
+ esac`;
1527
+ default:
1528
+ return `#!/usr/bin/env bash
1529
+ # Default adapter for Trellis harness
1530
+ echo "Unsupported IDE: ${ide}" >&2
1531
+ exit 1`;
1532
+ }
1533
+ }
1534
+ function renderCursorHooksConfig() {
1535
+ return {
1536
+ version: 1,
1537
+ hooks: {
1538
+ sessionStart: [
1539
+ {
1540
+ command: "bash .cursor/hooks/adapters/cursor-adapter.sh session-start",
1541
+ timeout: 15
1542
+ }
1543
+ ],
1544
+ afterFileEdit: [
1545
+ {
1546
+ command: "bash .cursor/hooks/adapters/cursor-adapter.sh post-tool",
1547
+ timeout: 10
1548
+ }
1549
+ ],
1550
+ stop: [
1551
+ {
1552
+ command: "bash .cursor/hooks/adapters/cursor-adapter.sh stop",
1553
+ timeout: 10
1554
+ }
1555
+ ]
1556
+ }
1557
+ };
1558
+ }
1559
+ function renderWindsurfHooksConfig() {
1560
+ return {
1561
+ hooks: {
1562
+ pre_user_prompt: [
1563
+ {
1564
+ command: "bash .cursor/hooks/adapters/cascade-adapter.sh pre-prompt",
1565
+ show_output: true
1566
+ }
1567
+ ],
1568
+ post_write_code: [
1569
+ {
1570
+ command: "bash .cursor/hooks/adapters/cascade-adapter.sh post-tool",
1571
+ show_output: false
1572
+ }
1573
+ ],
1574
+ post_cascade_response: [
1575
+ {
1576
+ command: "bash .cursor/hooks/adapters/cascade-adapter.sh post-response",
1577
+ show_output: true
1578
+ }
1579
+ ]
1580
+ }
1581
+ };
1582
+ }
1583
+ function renderClaudeHooksConfig() {
1584
+ return {
1585
+ hooks: {
1586
+ PreToolUse: [
1587
+ {
1588
+ command: "bash .cursor/hooks/adapters/claude-adapter.sh pre-tool-use",
1589
+ timeout: 15
1590
+ }
1591
+ ],
1592
+ PostToolUse: [
1593
+ {
1594
+ command: "bash .cursor/hooks/adapters/claude-adapter.sh post-tool-use",
1595
+ timeout: 10
1596
+ }
1597
+ ],
1598
+ PostToolBatch: [
1599
+ {
1600
+ command: "bash .cursor/hooks/adapters/claude-adapter.sh post-tool-batch",
1601
+ timeout: 15
1602
+ }
1603
+ ],
1604
+ PermissionDenied: [
1605
+ {
1606
+ command: "bash .cursor/hooks/adapters/claude-adapter.sh permission-denied",
1607
+ timeout: 5
1608
+ }
1609
+ ]
1610
+ },
1611
+ permissions: {
1612
+ allow: [
1613
+ "Bash(trellis --version)",
1614
+ "Bash(trellis issue *)",
1615
+ "Bash(trellis --help)",
1616
+ "Bash(trellis search *)",
1617
+ "Bash(trellis entity *)",
1618
+ "Bash(trellis ontology *)",
1619
+ "Bash(trellis query *)",
1620
+ "Bash(lsof -nP -iTCP -sTCP:LISTEN)",
1621
+ "Bash(awk '{print $9, $1, $2}')",
1622
+ "Bash(curl -s --max-time 1 http://localhost:__TRACKED_VAR__/api/graph/health)",
1623
+ "Bash(curl -s --max-time 1 http://localhost:__TRACKED_VAR__/api/graph/health -H 'Accept: application/json')",
1624
+ 'Bash(grep -ivE "node_modules|dist|.vercel|.git$|.git/|.logs")'
1625
+ ]
1626
+ },
1627
+ spinnerTipsEnabled: true
1628
+ };
1629
+ }
1630
+ function renderCodexHooksConfig() {
1631
+ return {
1632
+ hooks: {
1633
+ SessionStart: [
1634
+ {
1635
+ matcher: "startup|resume|clear",
1636
+ hooks: [
1637
+ {
1638
+ type: "command",
1639
+ command: "bash .cursor/hooks/adapters/codex-adapter.sh session-start",
1640
+ timeout: 15,
1641
+ statusMessage: "Loading Trellis desk context"
1642
+ }
1643
+ ]
1644
+ }
1645
+ ],
1646
+ PostToolUse: [
1647
+ {
1648
+ matcher: "apply_patch|Edit|Write",
1649
+ hooks: [
1650
+ {
1651
+ type: "command",
1652
+ command: "bash .cursor/hooks/adapters/codex-adapter.sh post-tool",
1653
+ timeout: 10,
1654
+ statusMessage: "Recording Trellis operation"
1655
+ }
1656
+ ]
1657
+ }
1658
+ ],
1659
+ Stop: [
1660
+ {
1661
+ hooks: [
1662
+ {
1663
+ type: "command",
1664
+ command: "bash .cursor/hooks/adapters/codex-adapter.sh stop",
1665
+ timeout: 10,
1666
+ statusMessage: "Checking Trellis workflow status"
1667
+ }
1668
+ ]
1669
+ }
1670
+ ]
1671
+ }
1672
+ };
1673
+ }
1674
+ function renderGeminiHooksConfig() {
1675
+ return {
1676
+ hooks: {
1677
+ SessionStart: [
1678
+ {
1679
+ matcher: "startup|resume|clear",
1680
+ hooks: [
1681
+ {
1682
+ name: "trellis-desk-context",
1683
+ type: "command",
1684
+ command: "bash .cursor/hooks/adapters/gemini-adapter.sh session-start",
1685
+ timeout: 15000,
1686
+ description: "Refresh pending Trellis context at session start"
1687
+ }
1688
+ ]
1689
+ }
1690
+ ],
1691
+ BeforeAgent: [
1692
+ {
1693
+ hooks: [
1694
+ {
1695
+ name: "trellis-desk-prompt-context",
1696
+ type: "command",
1697
+ command: "bash .cursor/hooks/adapters/gemini-adapter.sh before-agent",
1698
+ timeout: 15000,
1699
+ description: "Mirror Windsurf pre_user_prompt desk context refresh"
1700
+ }
1701
+ ]
1702
+ }
1703
+ ],
1704
+ AfterTool: [
1705
+ {
1706
+ matcher: "write_file|replace|apply_patch",
1707
+ hooks: [
1708
+ {
1709
+ name: "trellis-op-logging",
1710
+ type: "command",
1711
+ command: "bash .cursor/hooks/adapters/gemini-adapter.sh post-tool",
1712
+ timeout: 1e4,
1713
+ description: "Record Trellis operations and suggest memory creation"
1714
+ }
1715
+ ]
1716
+ }
1717
+ ],
1718
+ AfterAgent: [
1719
+ {
1720
+ hooks: [
1721
+ {
1722
+ name: "trellis-workflow-triage",
1723
+ type: "command",
1724
+ command: "bash .cursor/hooks/adapters/gemini-adapter.sh after-agent",
1725
+ timeout: 1e4,
1726
+ description: "Suggest Trellis workflow actions when items need attention"
1727
+ }
1728
+ ]
1729
+ }
1730
+ ]
1731
+ }
1732
+ };
1733
+ }
902
1734
  function writeIdeScaffold(rootPath, input) {
903
1735
  const { ide, footprint, framework, plugins, context, profile } = input;
904
1736
  const fullInput = {
@@ -917,6 +1749,9 @@ function writeIdeScaffold(rootPath, input) {
917
1749
  mkdirSync2(cursorDir, { recursive: true });
918
1750
  }
919
1751
  writeFileSync2(join4(cursorDir, "rules.md"), renderCursorRules(fullInput), "utf-8");
1752
+ if (footprint === "full") {
1753
+ writeTrellisHooks(rootPath, "cursor");
1754
+ }
920
1755
  break;
921
1756
  }
922
1757
  case "windsurf": {
@@ -925,6 +1760,9 @@ function writeIdeScaffold(rootPath, input) {
925
1760
  mkdirSync2(windsurfDir, { recursive: true });
926
1761
  }
927
1762
  writeFileSync2(join4(windsurfDir, "rules.md"), renderWindsurfRules(fullInput), "utf-8");
1763
+ if (footprint === "full") {
1764
+ writeTrellisHooks(rootPath, "windsurf");
1765
+ }
928
1766
  break;
929
1767
  }
930
1768
  case "claude": {
@@ -933,6 +1771,9 @@ function writeIdeScaffold(rootPath, input) {
933
1771
  mkdirSync2(claudeDir, { recursive: true });
934
1772
  }
935
1773
  writeFileSync2(join4(claudeDir, "settings.md"), renderClaudeMd(fullInput), "utf-8");
1774
+ if (footprint === "full") {
1775
+ writeTrellisHooks(rootPath, "claude");
1776
+ }
936
1777
  break;
937
1778
  }
938
1779
  case "copilot": {
@@ -950,6 +1791,9 @@ function writeIdeScaffold(rootPath, input) {
950
1791
  mkdirSync2(codexDir, { recursive: true });
951
1792
  }
952
1793
  writeFileSync2(join4(codexDir, "config.json"), JSON.stringify(renderCodexConfig(fullInput), null, 2), "utf-8");
1794
+ if (footprint === "full") {
1795
+ writeTrellisHooks(rootPath, "codex");
1796
+ }
953
1797
  break;
954
1798
  }
955
1799
  case "gemini": {
@@ -958,6 +1802,9 @@ function writeIdeScaffold(rootPath, input) {
958
1802
  mkdirSync2(geminiDir, { recursive: true });
959
1803
  }
960
1804
  writeFileSync2(join4(geminiDir, "config.json"), JSON.stringify(renderGeminiConfig(fullInput), null, 2), "utf-8");
1805
+ if (footprint === "full") {
1806
+ writeTrellisHooks(rootPath, "gemini");
1807
+ }
961
1808
  break;
962
1809
  }
963
1810
  case "none":
@@ -980,6 +1827,356 @@ trellis milestone # Create narrative checkpoints
980
1827
  }
981
1828
  }
982
1829
  }
1830
+ function renderBugIntakeScript() {
1831
+ return `#!/usr/bin/env bash
1832
+ # Bug intake and TDD enforcement for Trellis integration
1833
+ # Creates structured issues with acceptance criteria and test requirements
1834
+ set -euo pipefail
1835
+
1836
+ # Import desk root detection
1837
+ source "\\$(dirname "\\$0")/../desk-root.sh"
1838
+
1839
+ # Environment variables from contract
1840
+ ORIGIN="\${TRELLIS_ORIGIN:-unknown}"
1841
+ OUTPUT="\${TRELLIS_HOOK_OUTPUT:-stdout}"
1842
+
1843
+ # Only run if we're in a Trellis workspace
1844
+ if ! command -v trellis >/dev/null 2>&1; then
1845
+ exit 0
1846
+ fi
1847
+
1848
+ # Read bug description from stdin or environment
1849
+ BUG_DATA="\${TRELLIS_BUG_DATA:-}"
1850
+ if [ -z "\\$BUG_DATA" ]; then
1851
+ BUG_DATA=$(cat)
1852
+ fi
1853
+
1854
+ # Extract bug information
1855
+ TITLE=$(echo "\\$BUG_DATA" | jq -r '.title // "Untitled Bug"' 2>/dev/null || echo "Untitled Bug")
1856
+ DESCRIPTION=$(echo "\\$BUG_DATA" | jq -r '.description // ""' 2>/dev/null || echo "")
1857
+ SEVERITY=$(echo "\\$BUG_DATA" | jq -r '.severity // "medium"' 2>/dev/null || echo "medium")
1858
+ COMPONENT=$(echo "\\$BUG_DATA" | jq -r '.component // "unknown"' 2>/dev/null || echo "unknown")
1859
+
1860
+ # Validate required fields
1861
+ if [ -z "\\$DESCRIPTION" ]; then
1862
+ echo "\u274C Bug description is required" >&2
1863
+ exit 1
1864
+ fi
1865
+
1866
+ # Create the Trellis issue with structured content
1867
+ ISSUE_CONTENT="# Bug: \\$TITLE
1868
+
1869
+ **Severity:** \\$SEVERITY
1870
+ **Component:** \\$COMPONENT
1871
+ **Reported by:** \\$ORIGIN
1872
+ **Reported at:** $(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)
1873
+
1874
+ ## Description
1875
+ \\$DESCRIPTION
1876
+
1877
+ ## Acceptance Criteria
1878
+
1879
+ ### Functional Requirements
1880
+ - [ ] **AC-1:** Bug reproduction confirmed in test environment
1881
+ - [ ] **AC-2:** Root cause identified and documented
1882
+ - [ ] **AC-3:** Fix implemented without breaking existing functionality
1883
+ - [ ] **AC-4:** Bug no longer reproducible after fix
1884
+ - [ ] **AC-5:** Edge cases covered and tested
1885
+
1886
+ ### Test Requirements (TDD)
1887
+ - [ ] **TEST-1:** Unit tests written for the failing behavior
1888
+ - [ ] **TEST-2:** Integration tests cover the bug scenario
1889
+ - [ ] **TEST-3:** Regression tests prevent future occurrences
1890
+ - [ ] **TEST-4:** Performance tests if applicable
1891
+ - [ ] **TEST-5:** Manual testing checklist completed
1892
+
1893
+ ### Documentation Requirements
1894
+ - [ ] **DOC-1:** API documentation updated if behavior changed
1895
+ - [ ] **DOC-2:** README updated if user-facing change
1896
+ - [ ] **DOC-3:** Changelog entry prepared
1897
+ - [ ] **DOC-4:** Technical notes added for future reference
1898
+
1899
+ ### Code Quality Requirements
1900
+ - [ ] **QUALITY-1:** Code follows project style guidelines
1901
+ - [ ] **QUALITY-2:** No new linting errors introduced
1902
+ - [ ] **QUALITY-3:** Code coverage maintained or improved
1903
+ - [ ] **QUALITY-4:** Security review completed if applicable
1904
+ - [ ] **QUALITY-5:** Performance impact assessed
1905
+
1906
+ ## Definition of Done
1907
+
1908
+ This bug is considered **DONE** when:
1909
+ 1. \u2705 All acceptance criteria are met
1910
+ 2. \u2705 All tests pass (unit, integration, regression)
1911
+ 3. \u2705 Code review approved
1912
+ 4. \u2705 Documentation updated
1913
+ 5. \u2705 No performance regressions
1914
+ 6. \u2705 Security implications addressed
1915
+ 7. \u2705 Changelog entry created
1916
+ 8. \u2705 Release notes prepared
1917
+
1918
+ ## Labels
1919
+ \`bug\`, \`severity-\\$SEVERITY\`, \`component-\\$COMPONENT\`, \`tdd-required\`, \`needs-investigation\`
1920
+
1921
+ ---
1922
+
1923
+ **Generated by:** Trellis Enhanced Hooks
1924
+ **Origin:** \\$ORIGIN
1925
+ **Template:** bug-intake-v1.0
1926
+ "
1927
+
1928
+ # Create the Trellis issue
1929
+ ISSUE_ID=$(echo "\\$ISSUE_CONTENT" | trellis issue create --title "\\$TITLE" --content-from-stdin --format json 2>/dev/null | jq -r '.id // empty' || echo "")
1930
+
1931
+ if [ -n "\\$ISSUE_ID" ]; then
1932
+ # Add labels to the issue
1933
+ trellis issue label "\\$ISSUE_ID" add "bug" "severity-\\$SEVERITY" "component-\\$COMPONENT" "tdd-required" "needs-investigation" 2>/dev/null || true
1934
+
1935
+ echo ""
1936
+ echo "\uD83D\uDC1B Bug Intake Completed (\\$ORIGIN):"
1937
+ echo " \uD83D\uDCDD Issue: #\\$ISSUE_ID - \\$TITLE"
1938
+ echo " \uD83D\uDD27 Severity: \\$SEVERITY"
1939
+ echo " \uD83D\uDCE6 Component: \\$COMPONENT"
1940
+ echo " \u2705 TDD requirements enforced"
1941
+ echo ""
1942
+ echo "\uD83D\uDCCB Next Steps:"
1943
+ echo " 1. Investigate the bug: trellis issue view \\$ISSUE_ID"
1944
+ echo " 2. Develop tests with TDD approach"
1945
+ echo " 3. Implement fix"
1946
+ echo ""
1947
+ else
1948
+ echo "\u274C Failed to create bug issue" >&2
1949
+ exit 1
1950
+ fi
1951
+ `;
1952
+ }
1953
+ function renderBugInvestigateScript() {
1954
+ return `#!/usr/bin/env bash
1955
+ # Bug investigation and TDD enforcement for Trellis integration
1956
+ # Ensures thorough investigation before fix implementation
1957
+ set -euo pipefail
1958
+
1959
+ # Import desk root detection
1960
+ source "\\$(dirname "\\$0")/../desk-root.sh"
1961
+
1962
+ # Environment variables from contract
1963
+ ORIGIN="\${TRELLIS_ORIGIN:-unknown}"
1964
+ OUTPUT="\${TRELLIS_HOOK_OUTPUT:-stdout}"
1965
+ ISSUE_ID="\${TRELLIS_ISSUE_ID:-}"
1966
+
1967
+ # Only run if we're in a Trellis workspace
1968
+ if ! command -v trellis >/dev/null 2>&1; then
1969
+ exit 0
1970
+ fi
1971
+
1972
+ # Validate issue ID
1973
+ if [ -z "\\$ISSUE_ID" ]; then
1974
+ echo "\u274C Issue ID is required for bug investigation" >&2
1975
+ exit 1
1976
+ fi
1977
+
1978
+ # Get issue details
1979
+ ISSUE_DETAILS=$(trellis issue view "\\$ISSUE_ID" --format json 2>/dev/null || echo "{}")
1980
+ TITLE=$(echo "\\$ISSUE_DETAILS" | jq -r '.title // "Unknown Issue"' 2>/dev/null || echo "Unknown Issue")
1981
+
1982
+ # Create test directory structure
1983
+ TEST_DIR="tests/bugs/\\$ISSUE_ID"
1984
+ mkdir -p "\\$TEST_DIR"
1985
+
1986
+ # Create test template files
1987
+ cat > "\\$TEST_DIR/reproduction.test.ts" << 'EOF'
1988
+ /**
1989
+ * Bug #\\$ISSUE_ID: \\$TITLE
1990
+ * Reproduction test - This test should fail initially
1991
+ */
1992
+
1993
+ import { describe, test, expect } from 'bun:test'
1994
+
1995
+ describe('Bug #\\$ISSUE_ID: \\$TITLE', () => {
1996
+ test('should reproduce the reported issue', async () => {
1997
+ // TODO: Implement reproduction steps based on bug description
1998
+ // This test should fail initially
1999
+ expect(true).toBe(false)
2000
+ })
2001
+ })
2002
+ EOF
2003
+
2004
+ cat > "\\$TEST_DIR/fix.test.ts" << 'EOF'
2005
+ /**
2006
+ * Bug #\\$ISSUE_ID: \\$TITLE
2007
+ * Fix verification test - This test should pass after implementing the fix
2008
+ */
2009
+
2010
+ import { describe, test, expect } from 'bun:test'
2011
+
2012
+ describe('Bug #\\$ISSUE_ID: \\$TITLE - Fix Verification', () => {
2013
+ test('should resolve the issue after fix implementation', async () => {
2014
+ // TODO: Implement test that verifies the fix
2015
+ // This test should pass after fix
2016
+ expect(true).toBe(true)
2017
+ })
2018
+ })
2019
+ EOF
2020
+
2021
+ # Create test runner script
2022
+ cat > "\\$TEST_DIR/run-tests.sh" << 'EOF'
2023
+ #!/usr/bin/env bash
2024
+ # Test runner for bug investigation
2025
+
2026
+ set -euo pipefail
2027
+
2028
+ ISSUE_ID=$(basename "$(pwd)")
2029
+ echo "\uD83E\uDDEA Running tests for Bug #\\$ISSUE_ID"
2030
+
2031
+ # Run reproduction test (should fail initially)
2032
+ echo "\uD83D\uDCCB Running reproduction test..."
2033
+ bun run reproduction.test.ts || echo "\u274C Reproduction test failed (expected)"
2034
+
2035
+ # Run fix test (should pass after fix)
2036
+ echo "\uD83D\uDD27 Running fix verification test..."
2037
+ bun run fix.test.ts
2038
+
2039
+ echo "\u2705 All tests completed"
2040
+ EOF
2041
+
2042
+ chmod +x "\\$TEST_DIR/run-tests.sh"
2043
+
2044
+ # Update issue labels
2045
+ trellis issue label "\\$ISSUE_ID" add "investigating" 2>/dev/null || true
2046
+
2047
+ echo ""
2048
+ echo "\uD83D\uDD0D Bug Investigation Started (\\$ORIGIN):"
2049
+ echo " \uD83D\uDCDD Issue: #\\$ISSUE_ID - \\$TITLE"
2050
+ echo " \uD83D\uDCC1 Test directory: \\$TEST_DIR"
2051
+ echo " \uD83E\uDDEA Test files created"
2052
+ echo ""
2053
+ echo "\uD83D\uDCCB Next Steps:"
2054
+ echo " 1. Complete investigation: trellis issue view \\$ISSUE_ID"
2055
+ echo " 2. Run reproduction test: cd \\$TEST_DIR && ./run-tests.sh"
2056
+ echo " 3. Implement fix with TDD approach"
2057
+ echo ""
2058
+ `;
2059
+ }
2060
+ function renderMilestoneTriageScript() {
2061
+ return `#!/usr/bin/env bash
2062
+ # Milestone and cycle triage for Trellis integration
2063
+ # Integrates bugs with project milestones and development cycles
2064
+ set -euo pipefail
2065
+
2066
+ # Import desk root detection
2067
+ source "\\$(dirname "\\$0")/../desk-root.sh"
2068
+
2069
+ # Environment variables from contract
2070
+ ORIGIN="\${TRELLIS_ORIGIN:-unknown}"
2071
+ OUTPUT="\${TRELLIS_HOOK_OUTPUT:-stdout}"
2072
+ ISSUE_ID="\${TRELLIS_ISSUE_ID:-}"
2073
+
2074
+ # Only run if we're in a Trellis workspace
2075
+ if ! command -v trellis >/dev/null 2>&1; then
2076
+ exit 0
2077
+ fi
2078
+
2079
+ # Validate issue ID
2080
+ if [ -z "\\$ISSUE_ID" ]; then
2081
+ echo "\u274C Issue ID is required for milestone triage" >&2
2082
+ exit 1
2083
+ fi
2084
+
2085
+ # Get issue details and calculate priority
2086
+ ISSUE_DETAILS=$(trellis issue view "\\$ISSUE_ID" --format json 2>/dev/null || echo "{}")
2087
+ TITLE=$(echo "\\$ISSUE_DETAILS" | jq -r '.title // "Unknown Issue"' 2>/dev/null || echo "Unknown Issue")
2088
+ LABELS=$(echo "\\$ISSUE_DETAILS" | jq -r '.labels // []' 2>/dev/null || echo "[]")
2089
+ SEVERITY=$(echo "\\$LABELS" | jq -r '.[] | select(startswith("severity-")) | split("-")[1]' 2>/dev/null || echo "medium")
2090
+
2091
+ # Get current milestone and cycle info
2092
+ CURRENT_MILESTONE=$(trellis milestone current --format json 2>/dev/null || echo "{}")
2093
+ MILESTONE_NAME=$(echo "\\$CURRENT_MILESTONE" | jq -r '.name // "current"' 2>/dev/null || echo "current")
2094
+
2095
+ CURRENT_CYCLE=$(trellis cycle current --format json 2>/dev/null || echo "{}")
2096
+ CYCLE_NAME=$(echo "\\$CURRENT_CYCLE" | jq -r '.name // "current"' 2>/dev/null || echo "current")
2097
+
2098
+ # Calculate priority based on severity
2099
+ calculate_priority() {
2100
+ local severity="\\$1"
2101
+ case "\\$severity" in
2102
+ "critical") echo "urgent" ;;
2103
+ "high") echo "high" ;;
2104
+ "medium") echo "medium" ;;
2105
+ "low") echo "low" ;;
2106
+ *) echo "medium" ;;
2107
+ esac
2108
+ }
2109
+
2110
+ PRIORITY=$(calculate_priority "\\$SEVERITY")
2111
+
2112
+ # Update issue labels and assign to milestone/cycle
2113
+ trellis issue label "\\$ISSUE_ID" add "triaged" "priority-\\$PRIORITY" "milestone-\\$MILESTONE_NAME" "cycle-\\$CYCLE_NAME" 2>/dev/null || true
2114
+
2115
+ # Update issue priority
2116
+ trellis issue priority "\\$ISSUE_ID" set "\\$PRIORITY" 2>/dev/null || true
2117
+
2118
+ echo ""
2119
+ echo "\uD83C\uDFAF Milestone & Cycle Triage Completed (\\$ORIGIN):"
2120
+ echo " \uD83D\uDCDD Issue: #\\$ISSUE_ID - \\$TITLE"
2121
+ echo " \uD83D\uDEA6 Priority: \\$PRIORITY"
2122
+ echo " \uD83C\uDFAF Milestone: \\$MILESTONE_NAME"
2123
+ echo " \uD83D\uDD04 Cycle: \\$CYCLE_NAME"
2124
+ echo ""
2125
+ `;
2126
+ }
2127
+ function renderCyclePlanningScript() {
2128
+ return `#!/usr/bin/env bash
2129
+ # Cycle planning and workload management for Trellis integration
2130
+ # Manages development cycles, sprint planning, and resource allocation
2131
+ set -euo pipefail
2132
+
2133
+ # Import desk root detection
2134
+ source "\\$(dirname "\\$0")/../desk-root.sh"
2135
+
2136
+ # Environment variables from contract
2137
+ ORIGIN="\${TRELLIS_ORIGIN:-unknown}"
2138
+ OUTPUT="\${TRELLIS_HOOK_OUTPUT:-stdout}"
2139
+
2140
+ # Only run if we're in a Trellis workspace
2141
+ if ! command -v trellis >/dev/null 2>&1; then
2142
+ exit 0
2143
+ fi
2144
+
2145
+ # Get current cycle information
2146
+ CURRENT_CYCLE=$(trellis cycle current --format json 2>/dev/null || echo "{}")
2147
+ CYCLE_NAME=$(echo "\\$CURRENT_CYCLE" | jq -r '.name // "current"' 2>/dev/null || echo "current")
2148
+
2149
+ # Get all issues in current cycle
2150
+ CYCLE_ISSUES=$(trellis cycle issues --format json 2>/dev/null || echo "[]")
2151
+
2152
+ # Analyze cycle workload
2153
+ total_issues=$(echo "\\$CYCLE_ISSUES" | jq 'length' 2>/dev/null || echo "0")
2154
+ urgent_issues=$(echo "\\$CYCLE_ISSUES" | jq '[.[] | select(.labels[] | contains("priority-urgent"))] | length' 2>/dev/null || echo "0")
2155
+ high_issues=$(echo "\\$CYCLE_ISSUES" | jq '[.[] | select(.labels[] | contains("priority-high"))] | length' 2>/dev/null || echo "0")
2156
+
2157
+ # Calculate health score
2158
+ health_score=100
2159
+ health_score=$((health_score - (urgent_issues * 20)))
2160
+ health_score=$((health_score - (high_issues * 10)))
2161
+
2162
+ if [ "\\$health_score" -lt 0 ]; then
2163
+ health_score=0
2164
+ fi
2165
+
2166
+ echo ""
2167
+ echo "\uD83D\uDD04 Cycle Planning Completed (\\$ORIGIN):"
2168
+ echo " \uD83D\uDCCA Cycle: \\$CYCLE_NAME"
2169
+ echo " \uD83D\uDCC8 Health Score: \\$health_score/100"
2170
+ echo " \uD83D\uDCDD Total Issues: \\$total_issues"
2171
+ echo " \uD83D\uDEA8 Urgent Issues: \\$urgent_issues"
2172
+ echo " \u26A1 High Priority Issues: \\$high_issues"
2173
+ echo ""
2174
+ echo "\uD83D\uDCCB Recommendations:"
2175
+ echo " \\$([ "\\$health_score" -lt 60 ] && echo "\uD83D\uDEA8 High workload detected - consider rebalancing" || echo "\u2705 Healthy workload - maintain current pace")"
2176
+ echo " \\$([ "\\$urgent_issues" -gt 0 ] && echo "\u26A0\uFE0F Address urgent issues immediately" || echo "\u2705 No urgent issues")"
2177
+ echo ""
2178
+ `;
2179
+ }
983
2180
  var init_write = () => {};
984
2181
 
985
2182
  // src/garden/cluster.ts
@@ -4868,160 +6065,4 @@ var init_engine = __esm(() => {
4868
6065
  init_write();
4869
6066
  });
4870
6067
 
4871
- // src/scaffold/seed.ts
4872
- init_profile();
4873
- init_infer();
4874
- import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
4875
- import { join as join6 } from "path";
4876
- async function seedContext(opts) {
4877
- const { rootPath, ide = "none", force = false } = opts;
4878
- const filesUpdated = [];
4879
- const timestamp = new Date().toISOString();
4880
- const profile = loadProfile();
4881
- const context = await inferProjectContext(rootPath);
4882
- const agentsDir = join6(rootPath, ".trellis", "agents");
4883
- if (existsSync5(agentsDir)) {
4884
- const agentsMdPath = join6(agentsDir, "AGENTS.md");
4885
- if (existsSync5(agentsMdPath) || force) {
4886
- const content = renderSeedAgentsMd(profile, context, timestamp);
4887
- writeFileSync4(agentsMdPath, content, "utf-8");
4888
- filesUpdated.push(".trellis/agents/AGENTS.md");
4889
- }
4890
- const agentContextPath = join6(agentsDir, "agent-context.json");
4891
- if (existsSync5(agentContextPath) || force) {
4892
- const config = {
4893
- domain: context.domain,
4894
- ecosystem: context.ecosystem,
4895
- tools: [],
4896
- ontologies: [],
4897
- generatedAt: timestamp,
4898
- confidence: context.confidence,
4899
- lastSeed: timestamp
4900
- };
4901
- writeFileSync4(agentContextPath, JSON.stringify(config, null, 2), "utf-8");
4902
- filesUpdated.push(".trellis/agents/agent-context.json");
4903
- }
4904
- }
4905
- switch (ide) {
4906
- case "cursor": {
4907
- const cursorRulesPath = join6(rootPath, ".cursor", "rules.md");
4908
- if (existsSync5(cursorRulesPath) || force) {
4909
- const content = renderSeedIdeRules(profile, context, "cursor", timestamp);
4910
- writeFileSync4(cursorRulesPath, content, "utf-8");
4911
- filesUpdated.push(".cursor/rules.md");
4912
- }
4913
- break;
4914
- }
4915
- case "windsurf": {
4916
- const windsurfRulesPath = join6(rootPath, ".windsurf", "rules.md");
4917
- if (existsSync5(windsurfRulesPath) || force) {
4918
- const content = renderSeedIdeRules(profile, context, "windsurf", timestamp);
4919
- writeFileSync4(windsurfRulesPath, content, "utf-8");
4920
- filesUpdated.push(".windsurf/rules.md");
4921
- }
4922
- break;
4923
- }
4924
- case "claude": {
4925
- const claudeSettingsPath = join6(rootPath, ".claude", "settings.md");
4926
- if (existsSync5(claudeSettingsPath) || force) {
4927
- const content = renderSeedIdeRules(profile, context, "claude", timestamp);
4928
- writeFileSync4(claudeSettingsPath, content, "utf-8");
4929
- filesUpdated.push(".claude/settings.md");
4930
- }
4931
- break;
4932
- }
4933
- case "none":
4934
- break;
4935
- }
4936
- return {
4937
- success: true,
4938
- filesUpdated,
4939
- timestamp
4940
- };
4941
- }
4942
- function renderSeedAgentsMd(profile, context, timestamp) {
4943
- const userName = profile?.name ?? "the user";
4944
- const userBio = profile?.bio ?? "(not provided)";
4945
- const userSkills = profile?.skills?.length ? profile.skills.join(", ") : "(not specified)";
4946
- return `# Trellis Agent Context
4947
-
4948
- > This file was seeded by \`trellis seed\` on ${timestamp}
4949
- > Inference confidence: **${context.confidence}**
4950
-
4951
- ---
4952
-
4953
- ## About the User
4954
-
4955
- | Field | Value |
4956
- |-------|-------|
4957
- | **Name** | ${userName} |
4958
- | **Bio** | ${userBio} |
4959
- | **Skills** | ${userSkills} |
4960
-
4961
- ---
4962
-
4963
- ## About This Project
4964
-
4965
- | Field | Value |
4966
- |-------|-------|
4967
- | **Name** | ${context.name ?? "(unnamed)"} |
4968
- | **Domain** | ${context.domain ?? "(not determined)"} |
4969
- | **Description** | ${context.description ?? "(no description found)"} |
4970
- | **Ecosystem** | ${context.ecosystem ?? "unknown"} |
4971
- | **File count** | ~${context.fileCount} |
4972
-
4973
- ---
4974
-
4975
- ## Agent Instructions
4976
-
4977
- You are operating in a Trellis-tracked repository. Follow these guidelines:
4978
-
4979
- 1. **Read \`agent-context.json\`** in this directory for registered tools, ontologies, and domain settings.
4980
- 2. **Check \`skills/\`** for domain-specific operating instructions relevant to this project.
4981
- 3. **Check \`workflows/\`** for repeatable task procedures.
4982
- 4. **Run \`trellis seed\`** to refresh this context file when the project evolves.
4983
-
4984
- ---
4985
-
4986
- ## Quick Reference
4987
-
4988
- \`\`\`bash
4989
- trellis status # Current repo state
4990
- trellis log # Causal operation history
4991
- trellis seed # Refresh this context
4992
- trellis milestone # Create narrative checkpoints
4993
- \`\`\`
4994
- `;
4995
- }
4996
- function renderSeedIdeRules(profile, context, ide, timestamp) {
4997
- const ideName = ide.charAt(0).toUpperCase() + ide.slice(1);
4998
- const projectName = context.name ?? "this project";
4999
- return `# ${ideName} Rules for ${projectName}
5000
-
5001
- > Generated by \`trellis seed\` on ${timestamp}
5002
-
5003
- ## Project Context
5004
- - **Domain**: ${context.domain ?? "(not determined)"}
5005
- - **Ecosystem**: ${context.ecosystem ?? "unknown"}
5006
- - **File count**: ~${context.fileCount}
5007
- - **Confidence**: ${context.confidence}
5008
-
5009
- ## Agent Instructions
5010
- You are working in a Trellis-tracked repository. See \`.trellis/agents/AGENTS.md\` for full context.
5011
-
5012
- ## Commands
5013
- - \`trellis status\` \u2014 Check repo state
5014
- - \`trellis seed\` \u2014 Refresh this context file
5015
- - \`trellis log\` \u2014 View causal history
5016
-
5017
- ---
5018
- *Auto-generated by Trellis. Run \`trellis seed\` to refresh context.*
5019
- `;
5020
- }
5021
-
5022
- // src/scaffold/index.ts
5023
- init_infer();
5024
- init_profile();
5025
- init_write();
5026
-
5027
- export { FileWatcher, init_fs_watcher, Ingestion, init_ingestion, inferProjectContext, loadProfile, saveProfile, hasProfile, promptForProfile, updateProfile, writeAgentScaffold, writeIdeScaffold, TrellisVcsEngine, init_engine, seedContext };
6068
+ export { FileWatcher, init_fs_watcher, Ingestion, init_ingestion, inferProjectContext, init_infer, loadProfile, saveProfile, hasProfile, promptForProfile, updateProfile, init_profile, writeAgentScaffold, writeIdeScaffold, init_write, TrellisVcsEngine, init_engine };