@codedrifters/configulator 0.0.183 → 0.0.185
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/lib/index.d.mts +34 -1
- package/lib/index.d.ts +35 -2
- package/lib/index.js +793 -3
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +792 -3
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.mjs
CHANGED
|
@@ -1245,6 +1245,704 @@ var meetingAnalysisBundle = {
|
|
|
1245
1245
|
subAgents: [meetingAnalystSubAgent]
|
|
1246
1246
|
};
|
|
1247
1247
|
|
|
1248
|
+
// src/agent/bundles/orchestrator.ts
|
|
1249
|
+
var checkBlockedProcedure = {
|
|
1250
|
+
name: "check-blocked.sh",
|
|
1251
|
+
description: "Token-efficient issue triage script with subcommands: eligible, unblock, stale, orphaned, prs",
|
|
1252
|
+
content: [
|
|
1253
|
+
"#!/usr/bin/env bash",
|
|
1254
|
+
"# check-blocked.sh \u2014 Token-efficient issue triage for agent loops.",
|
|
1255
|
+
"# Replaces inline body-parsing with shell pipelines that return only",
|
|
1256
|
+
"# actionable summaries.",
|
|
1257
|
+
"#",
|
|
1258
|
+
"# Usage:",
|
|
1259
|
+
"# .claude/procedures/check-blocked.sh unblock",
|
|
1260
|
+
"# .claude/procedures/check-blocked.sh eligible",
|
|
1261
|
+
"# .claude/procedures/check-blocked.sh stale",
|
|
1262
|
+
"# .claude/procedures/check-blocked.sh orphaned",
|
|
1263
|
+
"# .claude/procedures/check-blocked.sh prs",
|
|
1264
|
+
"",
|
|
1265
|
+
"set -uo pipefail",
|
|
1266
|
+
"",
|
|
1267
|
+
"# \u2500\u2500 constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1268
|
+
"",
|
|
1269
|
+
"STALE_IN_PROGRESS_HOURS=72",
|
|
1270
|
+
"STALE_BLOCKED_HOURS=168",
|
|
1271
|
+
"",
|
|
1272
|
+
"# \u2500\u2500 helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1273
|
+
"",
|
|
1274
|
+
'# Extract issue numbers from a "Depends on:" line.',
|
|
1275
|
+
'# Returns space-separated numbers, or empty string for "(none)".',
|
|
1276
|
+
"parse_deps() {",
|
|
1277
|
+
' local line="$1"',
|
|
1278
|
+
` if echo "$line" | grep -qi '(none)'; then`,
|
|
1279
|
+
' echo ""',
|
|
1280
|
+
" return",
|
|
1281
|
+
" fi",
|
|
1282
|
+
` echo "$line" | grep -oE '#[0-9]+' | tr -d '#' | tr '\\n' ' ' || echo ""`,
|
|
1283
|
+
"}",
|
|
1284
|
+
"",
|
|
1285
|
+
"# Check if a single issue is closed. Returns 0 if closed, 1 if open.",
|
|
1286
|
+
"is_closed() {",
|
|
1287
|
+
" local state",
|
|
1288
|
+
` state=$(gh issue view "$1" --json state --jq '.state' 2>/dev/null || echo "UNKNOWN")`,
|
|
1289
|
+
' [[ "$state" == "CLOSED" ]]',
|
|
1290
|
+
"}",
|
|
1291
|
+
"",
|
|
1292
|
+
"# \u2500\u2500 subcommands \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1293
|
+
"",
|
|
1294
|
+
"cmd_unblock() {",
|
|
1295
|
+
" local issues",
|
|
1296
|
+
' issues=$(gh issue list --label "status:blocked" --state open \\',
|
|
1297
|
+
' --json number,body --limit 50 2>/dev/null || echo "[]")',
|
|
1298
|
+
"",
|
|
1299
|
+
" local count",
|
|
1300
|
+
` count=$(echo "$issues" | jq 'length')`,
|
|
1301
|
+
' if [[ "$count" -eq 0 ]]; then',
|
|
1302
|
+
' echo "NO_BLOCKED_ISSUES"',
|
|
1303
|
+
" return 0",
|
|
1304
|
+
" fi",
|
|
1305
|
+
"",
|
|
1306
|
+
" local issue_data",
|
|
1307
|
+
` issue_data=$(echo "$issues" | jq -r '`,
|
|
1308
|
+
" .[] |",
|
|
1309
|
+
' (.body | split("\\n") | map(select(test("Depends on:"; "i"))) | .[0] // "") as $dep_line |',
|
|
1310
|
+
' "\\(.number)\\t\\($dep_line)"',
|
|
1311
|
+
" ')",
|
|
1312
|
+
"",
|
|
1313
|
+
" while IFS=$'\\t' read -r num dep_line; do",
|
|
1314
|
+
' [[ -z "$num" ]] && continue',
|
|
1315
|
+
"",
|
|
1316
|
+
' if [[ -z "$dep_line" ]]; then',
|
|
1317
|
+
' echo "BLOCKED #${num} \u2014 no Depends on field found"',
|
|
1318
|
+
" continue",
|
|
1319
|
+
" fi",
|
|
1320
|
+
"",
|
|
1321
|
+
" local deps",
|
|
1322
|
+
' deps=$(parse_deps "$dep_line")',
|
|
1323
|
+
' if [[ -z "${deps// /}" ]]; then',
|
|
1324
|
+
' echo "UNBLOCK #${num} \u2014 no dependencies"',
|
|
1325
|
+
" continue",
|
|
1326
|
+
" fi",
|
|
1327
|
+
"",
|
|
1328
|
+
" local all_closed=true",
|
|
1329
|
+
' local open_deps=""',
|
|
1330
|
+
' local closed_deps=""',
|
|
1331
|
+
" for dep in $deps; do",
|
|
1332
|
+
' if is_closed "$dep"; then',
|
|
1333
|
+
' closed_deps="${closed_deps}#${dep} "',
|
|
1334
|
+
" else",
|
|
1335
|
+
" all_closed=false",
|
|
1336
|
+
' open_deps="${open_deps}#${dep} "',
|
|
1337
|
+
" fi",
|
|
1338
|
+
" done",
|
|
1339
|
+
"",
|
|
1340
|
+
" if $all_closed; then",
|
|
1341
|
+
' echo "UNBLOCK #${num} \u2014 all deps closed (${closed_deps% })"',
|
|
1342
|
+
" else",
|
|
1343
|
+
' echo "BLOCKED #${num} \u2014 waiting on ${open_deps% }"',
|
|
1344
|
+
" fi",
|
|
1345
|
+
' done <<< "$issue_data"',
|
|
1346
|
+
"}",
|
|
1347
|
+
"",
|
|
1348
|
+
"cmd_eligible() {",
|
|
1349
|
+
" local issues",
|
|
1350
|
+
' issues=$(gh issue list --label "status:ready" --state open \\',
|
|
1351
|
+
' --search "sort:created-asc" \\',
|
|
1352
|
+
' --json number,title,body,labels --limit 50 2>/dev/null || echo "[]")',
|
|
1353
|
+
"",
|
|
1354
|
+
" local count",
|
|
1355
|
+
` count=$(echo "$issues" | jq 'length')`,
|
|
1356
|
+
' if [[ "$count" -eq 0 ]]; then',
|
|
1357
|
+
' echo "NO_READY_ISSUES"',
|
|
1358
|
+
" return 0",
|
|
1359
|
+
" fi",
|
|
1360
|
+
"",
|
|
1361
|
+
" local issue_data",
|
|
1362
|
+
` issue_data=$(echo "$issues" | jq -r '`,
|
|
1363
|
+
" .[] |",
|
|
1364
|
+
' (.body | split("\\n") | map(select(test("Depends on:"; "i"))) | .[0] // "") as $dep_line |',
|
|
1365
|
+
' (.labels | map(.name) | join(",")) as $label_str |',
|
|
1366
|
+
' (.labels | map(.name) | map(select(startswith("type:"))) | .[0] // "") as $type_label |',
|
|
1367
|
+
' "\\(.number)\\t\\(.title)\\t\\($dep_line)\\t\\($label_str)\\t\\($type_label)"',
|
|
1368
|
+
" ')",
|
|
1369
|
+
"",
|
|
1370
|
+
' local results=""',
|
|
1371
|
+
" while IFS=$'\\t' read -r num title dep_line labels_str type_label; do",
|
|
1372
|
+
' [[ -z "$num" ]] && continue',
|
|
1373
|
+
"",
|
|
1374
|
+
' local deps=""',
|
|
1375
|
+
' if [[ -n "$dep_line" ]]; then',
|
|
1376
|
+
' deps=$(parse_deps "$dep_line")',
|
|
1377
|
+
" fi",
|
|
1378
|
+
"",
|
|
1379
|
+
" # Check if any dep is still open.",
|
|
1380
|
+
" local has_open_dep=false",
|
|
1381
|
+
' local open_deps=""',
|
|
1382
|
+
' if [[ -n "${deps// /}" ]]; then',
|
|
1383
|
+
" for dep in $deps; do",
|
|
1384
|
+
' if ! is_closed "$dep"; then',
|
|
1385
|
+
" has_open_dep=true",
|
|
1386
|
+
' open_deps="${open_deps}#${dep} "',
|
|
1387
|
+
" fi",
|
|
1388
|
+
" done",
|
|
1389
|
+
" fi",
|
|
1390
|
+
"",
|
|
1391
|
+
" if $has_open_dep; then",
|
|
1392
|
+
' echo "SKIP #${num} \u2014 dep ${open_deps% } still open"',
|
|
1393
|
+
" continue",
|
|
1394
|
+
" fi",
|
|
1395
|
+
"",
|
|
1396
|
+
" # 5-level priority sort key.",
|
|
1397
|
+
" local sort_key=2",
|
|
1398
|
+
' local priority="medium"',
|
|
1399
|
+
' case "$labels_str" in',
|
|
1400
|
+
' *priority:critical*) sort_key=0; priority="critical" ;;',
|
|
1401
|
+
' *priority:high*) sort_key=1; priority="high" ;;',
|
|
1402
|
+
' *priority:medium*) sort_key=2; priority="medium" ;;',
|
|
1403
|
+
' *priority:low*) sort_key=3; priority="low" ;;',
|
|
1404
|
+
' *priority:trivial*) sort_key=4; priority="trivial" ;;',
|
|
1405
|
+
" esac",
|
|
1406
|
+
"",
|
|
1407
|
+
' local label_info=""',
|
|
1408
|
+
' [[ -n "$type_label" ]] && label_info=" ${type_label}"',
|
|
1409
|
+
"",
|
|
1410
|
+
' results="${results}${sort_key}\\t${num}\\tPICK #${num} priority:${priority}${label_info} \\"${title}\\"\\n"',
|
|
1411
|
+
' done <<< "$issue_data"',
|
|
1412
|
+
"",
|
|
1413
|
+
" # Sort by priority, then issue number (FIFO).",
|
|
1414
|
+
' if [[ -n "$results" ]]; then',
|
|
1415
|
+
` printf '%b' "$results" | sort -t$'\\t' -k1,1n -k2,2n | cut -f3`,
|
|
1416
|
+
" fi",
|
|
1417
|
+
"}",
|
|
1418
|
+
"",
|
|
1419
|
+
"cmd_stale() {",
|
|
1420
|
+
" # Check in-progress issues",
|
|
1421
|
+
" local ip_issues",
|
|
1422
|
+
' ip_issues=$(gh issue list --label "status:in-progress" --state open \\',
|
|
1423
|
+
' --json number,title,updatedAt --limit 50 2>/dev/null || echo "[]")',
|
|
1424
|
+
"",
|
|
1425
|
+
" local ip_count",
|
|
1426
|
+
` ip_count=$(echo "$ip_issues" | jq 'length')`,
|
|
1427
|
+
"",
|
|
1428
|
+
' if [[ "$ip_count" -gt 0 ]]; then',
|
|
1429
|
+
" local ip_threshold",
|
|
1430
|
+
" ip_threshold=$(date -u -v-${STALE_IN_PROGRESS_HOURS}H +%Y-%m-%dT%H:%M:%S 2>/dev/null \\",
|
|
1431
|
+
' || date -u -d "${STALE_IN_PROGRESS_HOURS} hours ago" +%Y-%m-%dT%H:%M:%S 2>/dev/null)',
|
|
1432
|
+
"",
|
|
1433
|
+
" local ip_data",
|
|
1434
|
+
` ip_data=$(echo "$ip_issues" | jq -r '.[] | "\\(.number)\\t\\(.title)\\t\\(.updatedAt)"')`,
|
|
1435
|
+
"",
|
|
1436
|
+
" while IFS=$'\\t' read -r num title updated; do",
|
|
1437
|
+
' [[ -z "$num" ]] && continue',
|
|
1438
|
+
' local updated_trimmed="${updated%%+*}"',
|
|
1439
|
+
' updated_trimmed="${updated_trimmed%%Z*}"',
|
|
1440
|
+
' if [[ "$updated_trimmed" < "$ip_threshold" ]]; then',
|
|
1441
|
+
' local date_part="${updated_trimmed%%T*}"',
|
|
1442
|
+
' echo "STALE #${num} \u2014 no activity since ${date_part} \u2014 \\"${title}\\""',
|
|
1443
|
+
" fi",
|
|
1444
|
+
' done <<< "$ip_data"',
|
|
1445
|
+
" fi",
|
|
1446
|
+
"",
|
|
1447
|
+
" # Check blocked issues",
|
|
1448
|
+
" local bl_issues",
|
|
1449
|
+
' bl_issues=$(gh issue list --label "status:blocked" --state open \\',
|
|
1450
|
+
' --json number,title,updatedAt --limit 50 2>/dev/null || echo "[]")',
|
|
1451
|
+
"",
|
|
1452
|
+
" local bl_count",
|
|
1453
|
+
` bl_count=$(echo "$bl_issues" | jq 'length')`,
|
|
1454
|
+
"",
|
|
1455
|
+
' if [[ "$bl_count" -gt 0 ]]; then',
|
|
1456
|
+
" local bl_threshold",
|
|
1457
|
+
" bl_threshold=$(date -u -v-${STALE_BLOCKED_HOURS}H +%Y-%m-%dT%H:%M:%S 2>/dev/null \\",
|
|
1458
|
+
' || date -u -d "${STALE_BLOCKED_HOURS} hours ago" +%Y-%m-%dT%H:%M:%S 2>/dev/null)',
|
|
1459
|
+
"",
|
|
1460
|
+
" local bl_data",
|
|
1461
|
+
` bl_data=$(echo "$bl_issues" | jq -r '.[] | "\\(.number)\\t\\(.title)\\t\\(.updatedAt)"')`,
|
|
1462
|
+
"",
|
|
1463
|
+
" while IFS=$'\\t' read -r num title updated; do",
|
|
1464
|
+
' [[ -z "$num" ]] && continue',
|
|
1465
|
+
' local updated_trimmed="${updated%%+*}"',
|
|
1466
|
+
' updated_trimmed="${updated_trimmed%%Z*}"',
|
|
1467
|
+
' if [[ "$updated_trimmed" < "$bl_threshold" ]]; then',
|
|
1468
|
+
' local date_part="${updated_trimmed%%T*}"',
|
|
1469
|
+
' echo "STALE_BLOCKED #${num} \u2014 blocked since ${date_part} \u2014 \\"${title}\\""',
|
|
1470
|
+
" fi",
|
|
1471
|
+
' done <<< "$bl_data"',
|
|
1472
|
+
" fi",
|
|
1473
|
+
"",
|
|
1474
|
+
' if [[ "$ip_count" -eq 0 && "$bl_count" -eq 0 ]]; then',
|
|
1475
|
+
' echo "NO_STALE_ISSUES"',
|
|
1476
|
+
" fi",
|
|
1477
|
+
"}",
|
|
1478
|
+
"",
|
|
1479
|
+
"cmd_orphaned() {",
|
|
1480
|
+
" # Check for remote branches whose issues are closed or missing",
|
|
1481
|
+
" git fetch --prune origin 2>/dev/null",
|
|
1482
|
+
" local branches",
|
|
1483
|
+
' branches=$(git branch -r --format="%(refname:short)" | grep -v HEAD | sed "s|origin/||")',
|
|
1484
|
+
"",
|
|
1485
|
+
" local found_orphan=false",
|
|
1486
|
+
" while IFS= read -r branch; do",
|
|
1487
|
+
' [[ -z "$branch" ]] && continue',
|
|
1488
|
+
' [[ "$branch" == "main" || "$branch" == "master" ]] && continue',
|
|
1489
|
+
"",
|
|
1490
|
+
" # Extract issue number from branch name (e.g., feat/42-add-login \u2192 42)",
|
|
1491
|
+
" local issue_num",
|
|
1492
|
+
` issue_num=$(echo "$branch" | grep -oE '/[0-9]+' | tr -d '/' | head -1)`,
|
|
1493
|
+
' [[ -z "$issue_num" ]] && continue',
|
|
1494
|
+
"",
|
|
1495
|
+
" local state",
|
|
1496
|
+
` state=$(gh issue view "$issue_num" --json state --jq '.state' 2>/dev/null || echo "NOT_FOUND")`,
|
|
1497
|
+
"",
|
|
1498
|
+
' if [[ "$state" == "CLOSED" ]]; then',
|
|
1499
|
+
" found_orphan=true",
|
|
1500
|
+
' echo "ORPHAN_BRANCH ${branch} \u2014 issue #${issue_num} is closed"',
|
|
1501
|
+
' elif [[ "$state" == "NOT_FOUND" ]]; then',
|
|
1502
|
+
" found_orphan=true",
|
|
1503
|
+
' echo "ORPHAN_BRANCH ${branch} \u2014 issue #${issue_num} not found"',
|
|
1504
|
+
" fi",
|
|
1505
|
+
' done <<< "$branches"',
|
|
1506
|
+
"",
|
|
1507
|
+
" # Check for open PRs whose linked issues are closed",
|
|
1508
|
+
" local prs",
|
|
1509
|
+
' prs=$(gh pr list --state open --json number,title,body --limit 50 2>/dev/null || echo "[]")',
|
|
1510
|
+
" local pr_data",
|
|
1511
|
+
` pr_data=$(echo "$prs" | jq -r '`,
|
|
1512
|
+
" .[] |",
|
|
1513
|
+
' (.body | capture("(?:Closes|Fixes|Resolves) #(?<num>[0-9]+)") | .num) as $issue |',
|
|
1514
|
+
' "\\(.number)\\t\\(.title)\\t\\($issue // "")"',
|
|
1515
|
+
" ' 2>/dev/null)",
|
|
1516
|
+
"",
|
|
1517
|
+
" while IFS=$'\\t' read -r pr_num title issue_num; do",
|
|
1518
|
+
' [[ -z "$pr_num" || -z "$issue_num" ]] && continue',
|
|
1519
|
+
' if is_closed "$issue_num"; then',
|
|
1520
|
+
" found_orphan=true",
|
|
1521
|
+
' echo "ORPHAN_PR #${pr_num} \u2014 linked issue #${issue_num} is closed \u2014 \\"${title}\\""',
|
|
1522
|
+
" fi",
|
|
1523
|
+
' done <<< "$pr_data"',
|
|
1524
|
+
"",
|
|
1525
|
+
" if ! $found_orphan; then",
|
|
1526
|
+
' echo "NO_ORPHANED_RESOURCES"',
|
|
1527
|
+
" fi",
|
|
1528
|
+
"}",
|
|
1529
|
+
"",
|
|
1530
|
+
"cmd_prs() {",
|
|
1531
|
+
" local prs",
|
|
1532
|
+
" prs=$(gh pr list --state open --json number,title,isDraft,headRefName,labels,body \\",
|
|
1533
|
+
' --limit 50 2>/dev/null || echo "[]")',
|
|
1534
|
+
"",
|
|
1535
|
+
" local count",
|
|
1536
|
+
` count=$(echo "$prs" | jq 'length')`,
|
|
1537
|
+
' if [[ "$count" -eq 0 ]]; then',
|
|
1538
|
+
' echo "NO_OPEN_PRS"',
|
|
1539
|
+
" return 0",
|
|
1540
|
+
" fi",
|
|
1541
|
+
"",
|
|
1542
|
+
" # Filter: not draft, no needs-attention label.",
|
|
1543
|
+
" local eligible",
|
|
1544
|
+
` eligible=$(echo "$prs" | jq -r '`,
|
|
1545
|
+
" .[] |",
|
|
1546
|
+
" select(.isDraft == false) |",
|
|
1547
|
+
' select(.labels | map(.name) | index("status:needs-attention") | not) |',
|
|
1548
|
+
' (.body | capture("(?:Closes|Fixes|Resolves) #(?<num>[0-9]+)") | .num) as $issue |',
|
|
1549
|
+
' "\\(.number)\\t\\(.title)\\t\\(.headRefName)\\t\\($issue // "")"',
|
|
1550
|
+
" ' 2>/dev/null)",
|
|
1551
|
+
"",
|
|
1552
|
+
' if [[ -z "$eligible" ]]; then',
|
|
1553
|
+
' echo "NO_ELIGIBLE_PRS"',
|
|
1554
|
+
" return 0",
|
|
1555
|
+
" fi",
|
|
1556
|
+
"",
|
|
1557
|
+
" while IFS=$'\\t' read -r pr_num title branch issue_num; do",
|
|
1558
|
+
' [[ -z "$pr_num" ]] && continue',
|
|
1559
|
+
"",
|
|
1560
|
+
' if [[ -z "$issue_num" ]]; then',
|
|
1561
|
+
' echo "SKIP PR #${pr_num} \u2014 no linked issue \u2014 \\"${title}\\""',
|
|
1562
|
+
" continue",
|
|
1563
|
+
" fi",
|
|
1564
|
+
"",
|
|
1565
|
+
" # Check CI status",
|
|
1566
|
+
" local failing_checks",
|
|
1567
|
+
' failing_checks=$(gh pr checks "$pr_num" --json name,state \\',
|
|
1568
|
+
` --jq '[.[] | select(.state != "SUCCESS" and .state != "SKIPPED")] | length' 2>/dev/null || echo "-1")`,
|
|
1569
|
+
"",
|
|
1570
|
+
' if [[ "$failing_checks" == "-1" ]]; then',
|
|
1571
|
+
' echo "SKIP PR #${pr_num} \u2014 could not read CI status \u2014 \\"${title}\\""',
|
|
1572
|
+
" continue",
|
|
1573
|
+
" fi",
|
|
1574
|
+
"",
|
|
1575
|
+
' if [[ "$failing_checks" -gt 0 ]]; then',
|
|
1576
|
+
' echo "SKIP PR #${pr_num} \u2014 ${failing_checks} CI check(s) not passing \u2014 \\"${title}\\""',
|
|
1577
|
+
" continue",
|
|
1578
|
+
" fi",
|
|
1579
|
+
"",
|
|
1580
|
+
" # Check if already approved",
|
|
1581
|
+
" local approved",
|
|
1582
|
+
' approved=$(gh pr view "$pr_num" --json reviews \\',
|
|
1583
|
+
` --jq '[.reviews[] | select(.state == "APPROVED")] | length' 2>/dev/null || echo "0")`,
|
|
1584
|
+
' if [[ "$approved" -gt 0 ]]; then',
|
|
1585
|
+
' echo "SKIP PR #${pr_num} \u2014 already approved \u2014 \\"${title}\\""',
|
|
1586
|
+
" continue",
|
|
1587
|
+
" fi",
|
|
1588
|
+
"",
|
|
1589
|
+
' echo "REVIEW PR #${pr_num} issue:#${issue_num} branch:${branch} \u2014 \\"${title}\\""',
|
|
1590
|
+
' done <<< "$eligible"',
|
|
1591
|
+
"}",
|
|
1592
|
+
"",
|
|
1593
|
+
"# \u2500\u2500 main \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1594
|
+
"",
|
|
1595
|
+
'case "${1:-help}" in',
|
|
1596
|
+
' unblock) shift; cmd_unblock "$@" ;;',
|
|
1597
|
+
' eligible) shift; cmd_eligible "$@" ;;',
|
|
1598
|
+
' stale) shift; cmd_stale "$@" ;;',
|
|
1599
|
+
' orphaned) shift; cmd_orphaned "$@" ;;',
|
|
1600
|
+
' prs) shift; cmd_prs "$@" ;;',
|
|
1601
|
+
" help|*)",
|
|
1602
|
+
' echo "Usage: check-blocked.sh <unblock|eligible|stale|orphaned|prs>"',
|
|
1603
|
+
" exit 1",
|
|
1604
|
+
" ;;",
|
|
1605
|
+
"esac"
|
|
1606
|
+
].join("\n")
|
|
1607
|
+
};
|
|
1608
|
+
var orchestratorSubAgent = {
|
|
1609
|
+
name: "orchestrator",
|
|
1610
|
+
description: "Pipeline manager that reviews PRs, triages issues, and identifies the next work item \u2014 never implements code",
|
|
1611
|
+
model: AGENT_MODEL.POWERFUL,
|
|
1612
|
+
maxTurns: 100,
|
|
1613
|
+
platforms: { cursor: { exclude: true } },
|
|
1614
|
+
prompt: [
|
|
1615
|
+
"# Orchestrator Agent",
|
|
1616
|
+
"",
|
|
1617
|
+
"You are a pipeline manager for the **{{repository.owner}}/{{repository.name}}** repository.",
|
|
1618
|
+
"You review PRs, triage issues, and identify the next work item. You **never**",
|
|
1619
|
+
"implement code, create branches for issues, or claim issues.",
|
|
1620
|
+
"",
|
|
1621
|
+
"Run the same loop every invocation. Execute all phases in order.",
|
|
1622
|
+
"",
|
|
1623
|
+
"---",
|
|
1624
|
+
"",
|
|
1625
|
+
"## Phase A: Startup",
|
|
1626
|
+
"",
|
|
1627
|
+
"```bash",
|
|
1628
|
+
"git checkout main && git pull origin main",
|
|
1629
|
+
"```",
|
|
1630
|
+
"",
|
|
1631
|
+
"## Phase B: Batch PR Review",
|
|
1632
|
+
"",
|
|
1633
|
+
"Find all PRs eligible for review:",
|
|
1634
|
+
"",
|
|
1635
|
+
"```bash",
|
|
1636
|
+
".claude/procedures/check-blocked.sh prs",
|
|
1637
|
+
"```",
|
|
1638
|
+
"",
|
|
1639
|
+
"For each `REVIEW PR #N issue:#M branch:<branch>` line:",
|
|
1640
|
+
"",
|
|
1641
|
+
"1. Check out the PR branch: `gh pr checkout N`",
|
|
1642
|
+
"2. Review the PR:",
|
|
1643
|
+
" - Read the PR description and linked issue: `gh pr view N`",
|
|
1644
|
+
" - Review the diff: `gh pr diff N`",
|
|
1645
|
+
" - Check all changed files for correctness, conventions, and test coverage",
|
|
1646
|
+
" - Verify PR conventions: conventional commit title, closing keyword, summary present",
|
|
1647
|
+
" - Check CI status: `gh pr checks N`",
|
|
1648
|
+
"3. If the PR passes review:",
|
|
1649
|
+
" - Approve: `gh pr review N --approve --body '<summary>'`",
|
|
1650
|
+
" - Enable auto-merge: `gh pr merge N --auto --squash`",
|
|
1651
|
+
"4. If the PR fails review:",
|
|
1652
|
+
" - Request changes: `gh pr review N --request-changes --body '<findings>'`",
|
|
1653
|
+
"5. After each PR (whether merged or not):",
|
|
1654
|
+
" ```bash",
|
|
1655
|
+
" git checkout main && git pull origin main",
|
|
1656
|
+
" ```",
|
|
1657
|
+
"",
|
|
1658
|
+
"Skip lines starting with `SKIP` \u2014 those PRs are not eligible.",
|
|
1659
|
+
"If output is `NO_OPEN_PRS` or `NO_ELIGIBLE_PRS`, skip to Phase C.",
|
|
1660
|
+
"",
|
|
1661
|
+
"## Phase C: Triage \u2014 Unblock",
|
|
1662
|
+
"",
|
|
1663
|
+
"Check for blocked issues whose dependencies have resolved:",
|
|
1664
|
+
"",
|
|
1665
|
+
"```bash",
|
|
1666
|
+
".claude/procedures/check-blocked.sh unblock",
|
|
1667
|
+
"```",
|
|
1668
|
+
"",
|
|
1669
|
+
"For each `UNBLOCK #N` line:",
|
|
1670
|
+
"```bash",
|
|
1671
|
+
'gh issue edit N --remove-label "status:blocked" --add-label "status:ready"',
|
|
1672
|
+
'gh issue comment N --body "Dependencies resolved \u2014 unblocking."',
|
|
1673
|
+
"```",
|
|
1674
|
+
"",
|
|
1675
|
+
"For `BLOCKED #N \u2014 no Depends on field found`: leave as-is (Phase D will",
|
|
1676
|
+
"catch it if it's been blocked too long).",
|
|
1677
|
+
"",
|
|
1678
|
+
"If output is `NO_BLOCKED_ISSUES`, skip to Phase D.",
|
|
1679
|
+
"",
|
|
1680
|
+
"## Phase D: Maintenance",
|
|
1681
|
+
"",
|
|
1682
|
+
"### D1: Stale Detection",
|
|
1683
|
+
"",
|
|
1684
|
+
"```bash",
|
|
1685
|
+
".claude/procedures/check-blocked.sh stale",
|
|
1686
|
+
"```",
|
|
1687
|
+
"",
|
|
1688
|
+
"For each `STALE #N` line (in-progress >72h without activity):",
|
|
1689
|
+
"```bash",
|
|
1690
|
+
'gh issue edit N --add-label "status:needs-attention"',
|
|
1691
|
+
'gh issue comment N --body "Flagged: in-progress for >3 days with no activity."',
|
|
1692
|
+
"```",
|
|
1693
|
+
"",
|
|
1694
|
+
"For each `STALE_BLOCKED #N` line (blocked >168h):",
|
|
1695
|
+
"```bash",
|
|
1696
|
+
'gh issue edit N --add-label "status:needs-attention"',
|
|
1697
|
+
'gh issue comment N --body "Flagged: blocked for >7 days \u2014 may need human intervention."',
|
|
1698
|
+
"```",
|
|
1699
|
+
"",
|
|
1700
|
+
"**Important:** Do NOT auto-reset stale issues to `status:ready` \u2014 partial",
|
|
1701
|
+
"implementation work may exist on a branch.",
|
|
1702
|
+
"",
|
|
1703
|
+
"### D2: Orphaned Detection",
|
|
1704
|
+
"",
|
|
1705
|
+
"```bash",
|
|
1706
|
+
".claude/procedures/check-blocked.sh orphaned",
|
|
1707
|
+
"```",
|
|
1708
|
+
"",
|
|
1709
|
+
"Report any `ORPHAN_BRANCH` or `ORPHAN_PR` lines. These indicate branches",
|
|
1710
|
+
"or PRs whose linked issues are closed or missing. Log them for visibility",
|
|
1711
|
+
"but do not delete branches automatically.",
|
|
1712
|
+
"",
|
|
1713
|
+
"### D3: Needs-Attention Summary",
|
|
1714
|
+
"",
|
|
1715
|
+
"List all issues currently flagged:",
|
|
1716
|
+
"```bash",
|
|
1717
|
+
'gh issue list --label "status:needs-attention" --state open --json number,title',
|
|
1718
|
+
"```",
|
|
1719
|
+
"",
|
|
1720
|
+
"Log the count and titles for operator visibility.",
|
|
1721
|
+
"",
|
|
1722
|
+
"## Phase E: Queue Scan",
|
|
1723
|
+
"",
|
|
1724
|
+
"Find the highest-priority ready issue:",
|
|
1725
|
+
"",
|
|
1726
|
+
"```bash",
|
|
1727
|
+
".claude/procedures/check-blocked.sh eligible",
|
|
1728
|
+
"```",
|
|
1729
|
+
"",
|
|
1730
|
+
"If a `PICK` line is returned, report it as:",
|
|
1731
|
+
"```",
|
|
1732
|
+
'NEXT_WORK_ITEM #<number> priority:<level> type:<label> "<title>"',
|
|
1733
|
+
"```",
|
|
1734
|
+
"",
|
|
1735
|
+
"If output is `NO_READY_ISSUES`, report that the queue is empty.",
|
|
1736
|
+
"",
|
|
1737
|
+
"**Do NOT claim the issue, create a branch, or start implementation.**",
|
|
1738
|
+
"The worker agent handles that.",
|
|
1739
|
+
"",
|
|
1740
|
+
"## Phase F: Cleanup",
|
|
1741
|
+
"",
|
|
1742
|
+
"```bash",
|
|
1743
|
+
"git checkout main && git pull origin main",
|
|
1744
|
+
"git fetch --prune origin",
|
|
1745
|
+
"```",
|
|
1746
|
+
"",
|
|
1747
|
+
"Log completion: phases executed, PRs reviewed, issues unblocked,",
|
|
1748
|
+
"stale issues flagged, and next work item (if any).",
|
|
1749
|
+
"",
|
|
1750
|
+
"---",
|
|
1751
|
+
"",
|
|
1752
|
+
"## Rules",
|
|
1753
|
+
"",
|
|
1754
|
+
"1. **Never implement code.** You triage, review, and report \u2014 you do not code.",
|
|
1755
|
+
"2. **Never claim issues.** Do not add `status:in-progress` or create branches for issues.",
|
|
1756
|
+
"3. **Always use check-blocked.sh.** All triage queries go through the shell script for token efficiency.",
|
|
1757
|
+
"4. **Follow CLAUDE.md conventions** for all git and gh operations.",
|
|
1758
|
+
"5. **Priority order:** critical > high > medium > low > trivial, then FIFO by issue number."
|
|
1759
|
+
].join("\n")
|
|
1760
|
+
};
|
|
1761
|
+
var issueWorkerSubAgent = {
|
|
1762
|
+
name: "issue-worker",
|
|
1763
|
+
description: "Selects the next ready issue from the queue, claims it, and implements the change end-to-end following repository conventions",
|
|
1764
|
+
model: AGENT_MODEL.POWERFUL,
|
|
1765
|
+
maxTurns: 50,
|
|
1766
|
+
canDelegateToAgents: [],
|
|
1767
|
+
platforms: { cursor: { exclude: true } },
|
|
1768
|
+
prompt: [
|
|
1769
|
+
"# Issue Worker",
|
|
1770
|
+
"",
|
|
1771
|
+
"You are the issue worker for **{{repository.owner}}/{{repository.name}}**.",
|
|
1772
|
+
"Your job is to pick the next issue from the queue, claim it, and implement",
|
|
1773
|
+
"the change end-to-end \u2014 from branch creation through to opening a PR.",
|
|
1774
|
+
"",
|
|
1775
|
+
"---",
|
|
1776
|
+
"",
|
|
1777
|
+
"## Phase 1: Select an Issue",
|
|
1778
|
+
"",
|
|
1779
|
+
"If an issue number was provided in your instructions, use that issue.",
|
|
1780
|
+
"Otherwise, find the next issue to work on:",
|
|
1781
|
+
"",
|
|
1782
|
+
"```bash",
|
|
1783
|
+
".claude/procedures/check-blocked.sh eligible",
|
|
1784
|
+
"```",
|
|
1785
|
+
"",
|
|
1786
|
+
"This returns issues sorted by priority (critical > high > medium > low > trivial),",
|
|
1787
|
+
"then by issue number (FIFO). Take the **first** `PICK` line.",
|
|
1788
|
+
"",
|
|
1789
|
+
"If the output is `NO_READY_ISSUES`, report that the queue is empty and stop.",
|
|
1790
|
+
"",
|
|
1791
|
+
"If any `SKIP` lines appear, ignore them \u2014 those issues have unresolved dependencies.",
|
|
1792
|
+
"",
|
|
1793
|
+
"## Phase 2: Claim the Issue",
|
|
1794
|
+
"",
|
|
1795
|
+
"```bash",
|
|
1796
|
+
'gh issue edit <number> --remove-label "status:ready" --add-label "status:in-progress"',
|
|
1797
|
+
"```",
|
|
1798
|
+
"",
|
|
1799
|
+
"Read the full issue details:",
|
|
1800
|
+
"```bash",
|
|
1801
|
+
"gh issue view <number>",
|
|
1802
|
+
"```",
|
|
1803
|
+
"",
|
|
1804
|
+
"## Phase 3: Set Up",
|
|
1805
|
+
"",
|
|
1806
|
+
"```bash",
|
|
1807
|
+
"git checkout {{repository.defaultBranch}} && git pull origin {{repository.defaultBranch}}",
|
|
1808
|
+
"```",
|
|
1809
|
+
"",
|
|
1810
|
+
"Determine the branch type from the issue's `type:*` label or title prefix:",
|
|
1811
|
+
"",
|
|
1812
|
+
"| Label / prefix | Branch type |",
|
|
1813
|
+
"|---------------|-------------|",
|
|
1814
|
+
"| `type:feat` / `feat:` | `feat/` |",
|
|
1815
|
+
"| `type:fix` / `fix:` | `fix/` |",
|
|
1816
|
+
"| `type:chore` / `chore:` | `chore/` |",
|
|
1817
|
+
"| `type:refactor` / `refactor:` | `refactor/` |",
|
|
1818
|
+
"| `type:docs` / `docs:` | `docs/` |",
|
|
1819
|
+
"| `type:release` / `release:` | `release/` |",
|
|
1820
|
+
"| `type:hotfix` / `hotfix:` | `hotfix/` |",
|
|
1821
|
+
"| No type label | `feat/` (default) |",
|
|
1822
|
+
"",
|
|
1823
|
+
"Create a branch following the naming convention:",
|
|
1824
|
+
"```bash",
|
|
1825
|
+
"git checkout -b <type>/<issue-number>-<slug>",
|
|
1826
|
+
"```",
|
|
1827
|
+
"",
|
|
1828
|
+
"The slug should be a short (3-5 word) kebab-case summary derived from the issue title.",
|
|
1829
|
+
"",
|
|
1830
|
+
"Link the branch to the issue:",
|
|
1831
|
+
"```bash",
|
|
1832
|
+
"gh issue comment <number> --body 'Branch: `<branch-name>`'",
|
|
1833
|
+
"```",
|
|
1834
|
+
"",
|
|
1835
|
+
"## Phase 4: Implement",
|
|
1836
|
+
"",
|
|
1837
|
+
"Read the issue body carefully. Understand the acceptance criteria.",
|
|
1838
|
+
"",
|
|
1839
|
+
"Implement the change following these guidelines based on issue type:",
|
|
1840
|
+
"",
|
|
1841
|
+
"- **feat**: Implement the new feature. Add tests. Export public APIs from `index.ts`.",
|
|
1842
|
+
"- **fix**: Reproduce the bug first. Write a failing test. Fix the bug. Verify the test passes.",
|
|
1843
|
+
"- **chore**: Make the maintenance change. Verify no regressions.",
|
|
1844
|
+
"- **refactor**: Restructure code without changing behavior. Existing tests must pass unchanged.",
|
|
1845
|
+
"- **docs**: Update documentation only. Do not change source code.",
|
|
1846
|
+
"",
|
|
1847
|
+
"Follow all conventions from CLAUDE.md and the project's agent rules.",
|
|
1848
|
+
"",
|
|
1849
|
+
"## Phase 5: Verify",
|
|
1850
|
+
"",
|
|
1851
|
+
"Run the appropriate verification commands depending on what changed:",
|
|
1852
|
+
"",
|
|
1853
|
+
"1. If Projen config was changed: `npx projen && pnpm install`",
|
|
1854
|
+
"2. Compile the affected package: `pnpm --filter @codedrifters/<package> compile`",
|
|
1855
|
+
"3. Test the affected package: `pnpm --filter @codedrifters/<package> test`",
|
|
1856
|
+
"4. If changes span multiple packages: `pnpm build:all`",
|
|
1857
|
+
"",
|
|
1858
|
+
"Fix any compilation errors, test failures, or lint errors before proceeding.",
|
|
1859
|
+
"",
|
|
1860
|
+
"## Phase 6: Commit and Push",
|
|
1861
|
+
"",
|
|
1862
|
+
"Use conventional commit messages:",
|
|
1863
|
+
"```bash",
|
|
1864
|
+
"git add <files>",
|
|
1865
|
+
'git commit -m "<type>: <description>"',
|
|
1866
|
+
"git push -u origin <branch-name>",
|
|
1867
|
+
"```",
|
|
1868
|
+
"",
|
|
1869
|
+
"## Phase 7: Open a PR",
|
|
1870
|
+
"",
|
|
1871
|
+
"```bash",
|
|
1872
|
+
'gh pr create --title "<type>(<scope>): <description>" --body "## Summary',
|
|
1873
|
+
"",
|
|
1874
|
+
"<bullet points>",
|
|
1875
|
+
"",
|
|
1876
|
+
'Closes #<issue-number>"',
|
|
1877
|
+
"```",
|
|
1878
|
+
"",
|
|
1879
|
+
"Enable auto-merge:",
|
|
1880
|
+
"```bash",
|
|
1881
|
+
"gh pr merge --auto --squash",
|
|
1882
|
+
"```",
|
|
1883
|
+
"",
|
|
1884
|
+
"## Phase 8: Update Status",
|
|
1885
|
+
"",
|
|
1886
|
+
"After the PR is created:",
|
|
1887
|
+
"```bash",
|
|
1888
|
+
'gh issue edit <number> --add-label "status:done"',
|
|
1889
|
+
"```",
|
|
1890
|
+
"",
|
|
1891
|
+
"---",
|
|
1892
|
+
"",
|
|
1893
|
+
"## Rules",
|
|
1894
|
+
"",
|
|
1895
|
+
"1. **One issue per session.** Process exactly ONE issue, then stop.",
|
|
1896
|
+
"2. **Use check-blocked.sh.** Always use the procedure script for issue selection.",
|
|
1897
|
+
"3. **Follow CLAUDE.md conventions** for branch naming, commits, and PRs.",
|
|
1898
|
+
"4. **Do not assign PRs to a project board** \u2014 this repo has no GitHub project.",
|
|
1899
|
+
"5. **Do not add AI co-author** attribution to commits.",
|
|
1900
|
+
"6. **On failure:** If you cannot complete the issue, update labels and leave a comment:",
|
|
1901
|
+
" ```bash",
|
|
1902
|
+
' gh issue edit <number> --remove-label "status:in-progress" --add-label "status:needs-attention"',
|
|
1903
|
+
' gh issue comment <number> --body "Worker could not complete: <reason>"',
|
|
1904
|
+
" ```"
|
|
1905
|
+
].join("\n")
|
|
1906
|
+
};
|
|
1907
|
+
var orchestratorBundle = {
|
|
1908
|
+
name: "orchestrator",
|
|
1909
|
+
description: "Pipeline orchestrator agent for issue triage, PR review, and queue management",
|
|
1910
|
+
// Always included by default
|
|
1911
|
+
appliesWhen: () => true,
|
|
1912
|
+
rules: [
|
|
1913
|
+
{
|
|
1914
|
+
name: "orchestrator-conventions",
|
|
1915
|
+
description: "Guidelines for orchestrator agent behavior and pipeline management",
|
|
1916
|
+
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
1917
|
+
content: [
|
|
1918
|
+
"# Orchestrator Conventions",
|
|
1919
|
+
"",
|
|
1920
|
+
"When running the orchestrator agent (`.claude/agents/orchestrator.md`):",
|
|
1921
|
+
"",
|
|
1922
|
+
"- The orchestrator **never** implements code or creates branches for issues",
|
|
1923
|
+
"- It reviews PRs, triages issues, and reports the next work item",
|
|
1924
|
+
"- All triage queries use `.claude/procedures/check-blocked.sh` for token efficiency",
|
|
1925
|
+
"- Priority order: critical > high > medium > low > trivial, then FIFO",
|
|
1926
|
+
"- Stale thresholds: 72h for in-progress, 168h for blocked",
|
|
1927
|
+
"- Flagged issues get `status:needs-attention` \u2014 they are not auto-reset"
|
|
1928
|
+
].join("\n"),
|
|
1929
|
+
platforms: {
|
|
1930
|
+
claude: { target: "claude-md" },
|
|
1931
|
+
cursor: { exclude: true }
|
|
1932
|
+
},
|
|
1933
|
+
tags: ["workflow"]
|
|
1934
|
+
}
|
|
1935
|
+
],
|
|
1936
|
+
subAgents: [orchestratorSubAgent, issueWorkerSubAgent],
|
|
1937
|
+
procedures: [checkBlockedProcedure],
|
|
1938
|
+
claudePermissions: {
|
|
1939
|
+
allow: [
|
|
1940
|
+
// Allow executing the check-blocked.sh procedure
|
|
1941
|
+
"Bash(.claude/procedures/*.sh *)"
|
|
1942
|
+
]
|
|
1943
|
+
}
|
|
1944
|
+
};
|
|
1945
|
+
|
|
1248
1946
|
// src/pnpm/pnpm-workspace.ts
|
|
1249
1947
|
import { relative } from "path";
|
|
1250
1948
|
import { Component, YamlFile } from "projen";
|
|
@@ -2262,7 +2960,8 @@ var BUILT_IN_BUNDLES = [
|
|
|
2262
2960
|
projenBundle,
|
|
2263
2961
|
githubWorkflowBundle,
|
|
2264
2962
|
slackBundle,
|
|
2265
|
-
meetingAnalysisBundle
|
|
2963
|
+
meetingAnalysisBundle,
|
|
2964
|
+
orchestratorBundle
|
|
2266
2965
|
];
|
|
2267
2966
|
|
|
2268
2967
|
// src/agent/bundles/scope.ts
|
|
@@ -2385,12 +3084,15 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
2385
3084
|
/**
|
|
2386
3085
|
* Render all Claude Code configuration files.
|
|
2387
3086
|
*/
|
|
2388
|
-
static render(component, rules, skills, subAgents, mcpServers, settings) {
|
|
3087
|
+
static render(component, rules, skills, subAgents, mcpServers, settings, procedures) {
|
|
2389
3088
|
_ClaudeRenderer.renderClaudeMd(component, rules);
|
|
2390
3089
|
_ClaudeRenderer.renderScopedRules(component, rules);
|
|
2391
3090
|
_ClaudeRenderer.renderSettings(component, mcpServers, settings);
|
|
2392
3091
|
_ClaudeRenderer.renderSkills(component, skills);
|
|
2393
3092
|
_ClaudeRenderer.renderSubAgents(component, subAgents);
|
|
3093
|
+
if (procedures && procedures.length > 0) {
|
|
3094
|
+
_ClaudeRenderer.renderProcedures(component, procedures);
|
|
3095
|
+
}
|
|
2394
3096
|
}
|
|
2395
3097
|
static renderClaudeMd(component, rules) {
|
|
2396
3098
|
const claudeMdRules = rules.filter((r) => {
|
|
@@ -2689,6 +3391,12 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
2689
3391
|
if (agent.platforms?.claude?.memory) {
|
|
2690
3392
|
lines.push(`memory: ${agent.platforms.claude.memory}`);
|
|
2691
3393
|
}
|
|
3394
|
+
if (agent.canDelegateToAgents && agent.canDelegateToAgents.length > 0) {
|
|
3395
|
+
lines.push(`canDelegateToAgents:`);
|
|
3396
|
+
for (const delegateName of agent.canDelegateToAgents) {
|
|
3397
|
+
lines.push(` - "${delegateName}"`);
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
2692
3400
|
lines.push("---");
|
|
2693
3401
|
lines.push("");
|
|
2694
3402
|
lines.push(...agent.prompt.split("\n"));
|
|
@@ -2707,6 +3415,14 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
2707
3415
|
if (config.env) server.env = { ...config.env };
|
|
2708
3416
|
return server;
|
|
2709
3417
|
}
|
|
3418
|
+
static renderProcedures(component, procedures) {
|
|
3419
|
+
for (const proc of procedures) {
|
|
3420
|
+
new TextFile2(component, `.claude/procedures/${proc.name}`, {
|
|
3421
|
+
lines: proc.content.split("\n"),
|
|
3422
|
+
executable: true
|
|
3423
|
+
});
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
2710
3426
|
/**
|
|
2711
3427
|
* Determine the default Claude rule target based on rule scope.
|
|
2712
3428
|
* ALWAYS-scoped rules default to CLAUDE_MD; FILE_PATTERN rules default to SCOPED_FILE.
|
|
@@ -3060,6 +3776,7 @@ var AgentConfig = class _AgentConfig extends Component8 {
|
|
|
3060
3776
|
const rules = this.resolveRules();
|
|
3061
3777
|
const skills = this.resolveSkills();
|
|
3062
3778
|
const subAgents = this.resolveSubAgents();
|
|
3779
|
+
const procedures = this.resolveProcedures();
|
|
3063
3780
|
const mcpServers = this.options.mcpServers ?? {};
|
|
3064
3781
|
const projectMetadata = ProjectMetadata.of(this.project);
|
|
3065
3782
|
const metadata = projectMetadata?.metadata;
|
|
@@ -3069,6 +3786,10 @@ var AgentConfig = class _AgentConfig extends Component8 {
|
|
|
3069
3786
|
subAgents,
|
|
3070
3787
|
metadata
|
|
3071
3788
|
);
|
|
3789
|
+
const resolvedProcedures = this.resolveProcedureTemplates(
|
|
3790
|
+
procedures,
|
|
3791
|
+
metadata
|
|
3792
|
+
);
|
|
3072
3793
|
if (platforms.includes(AGENT_PLATFORM.CURSOR)) {
|
|
3073
3794
|
CursorRenderer.render(
|
|
3074
3795
|
this,
|
|
@@ -3090,7 +3811,8 @@ var AgentConfig = class _AgentConfig extends Component8 {
|
|
|
3090
3811
|
_AgentConfig.mergeClaudeDefaults(
|
|
3091
3812
|
this.options.claudeSettings,
|
|
3092
3813
|
bundlePermissions
|
|
3093
|
-
)
|
|
3814
|
+
),
|
|
3815
|
+
resolvedProcedures
|
|
3094
3816
|
);
|
|
3095
3817
|
}
|
|
3096
3818
|
if (platforms.includes(AGENT_PLATFORM.CODEX)) {
|
|
@@ -3203,6 +3925,16 @@ ${extra}`
|
|
|
3203
3925
|
}
|
|
3204
3926
|
}
|
|
3205
3927
|
}
|
|
3928
|
+
if (this.options.includeBundles) {
|
|
3929
|
+
for (const bundleName of this.options.includeBundles) {
|
|
3930
|
+
const bundle = BUILT_IN_BUNDLES.find((b) => b.name === bundleName);
|
|
3931
|
+
if (bundle?.skills) {
|
|
3932
|
+
for (const skill of bundle.skills) {
|
|
3933
|
+
skillMap.set(skill.name, skill);
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3206
3938
|
if (this.options.skills) {
|
|
3207
3939
|
for (const skill of this.options.skills) {
|
|
3208
3940
|
skillMap.set(skill.name, skill);
|
|
@@ -3222,6 +3954,16 @@ ${extra}`
|
|
|
3222
3954
|
}
|
|
3223
3955
|
}
|
|
3224
3956
|
}
|
|
3957
|
+
if (this.options.includeBundles) {
|
|
3958
|
+
for (const bundleName of this.options.includeBundles) {
|
|
3959
|
+
const bundle = BUILT_IN_BUNDLES.find((b) => b.name === bundleName);
|
|
3960
|
+
if (bundle?.subAgents) {
|
|
3961
|
+
for (const agent of bundle.subAgents) {
|
|
3962
|
+
agentMap.set(agent.name, agent);
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3225
3967
|
if (this.options.subAgents) {
|
|
3226
3968
|
for (const agent of this.options.subAgents) {
|
|
3227
3969
|
agentMap.set(agent.name, agent);
|
|
@@ -3229,6 +3971,35 @@ ${extra}`
|
|
|
3229
3971
|
}
|
|
3230
3972
|
return [...agentMap.values()];
|
|
3231
3973
|
}
|
|
3974
|
+
resolveProcedures() {
|
|
3975
|
+
const procMap = /* @__PURE__ */ new Map();
|
|
3976
|
+
if (this.options.autoDetectBundles !== false) {
|
|
3977
|
+
for (const bundle of BUILT_IN_BUNDLES) {
|
|
3978
|
+
if (this.options.excludeBundles?.includes(bundle.name)) continue;
|
|
3979
|
+
if (bundle.appliesWhen(this.project) && bundle.procedures) {
|
|
3980
|
+
for (const proc of bundle.procedures) {
|
|
3981
|
+
procMap.set(proc.name, proc);
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
if (this.options.includeBundles) {
|
|
3987
|
+
for (const bundleName of this.options.includeBundles) {
|
|
3988
|
+
const bundle = BUILT_IN_BUNDLES.find((b) => b.name === bundleName);
|
|
3989
|
+
if (bundle?.procedures) {
|
|
3990
|
+
for (const proc of bundle.procedures) {
|
|
3991
|
+
procMap.set(proc.name, proc);
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3996
|
+
if (this.options.procedures) {
|
|
3997
|
+
for (const proc of this.options.procedures) {
|
|
3998
|
+
procMap.set(proc.name, proc);
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
4001
|
+
return [...procMap.values()];
|
|
4002
|
+
}
|
|
3232
4003
|
/**
|
|
3233
4004
|
* Resolves template variables in rule content using project metadata.
|
|
3234
4005
|
* Emits synthesis warnings for rules with unresolved variables.
|
|
@@ -3281,6 +4052,23 @@ ${extra}`
|
|
|
3281
4052
|
return resolved !== agent.prompt ? { ...agent, prompt: resolved } : agent;
|
|
3282
4053
|
});
|
|
3283
4054
|
}
|
|
4055
|
+
/**
|
|
4056
|
+
* Resolves template variables in procedure content using project metadata.
|
|
4057
|
+
*/
|
|
4058
|
+
resolveProcedureTemplates(procedures, metadata) {
|
|
4059
|
+
return procedures.map((proc) => {
|
|
4060
|
+
const { resolved, unresolvedKeys } = resolveTemplateVariables(
|
|
4061
|
+
proc.content,
|
|
4062
|
+
metadata
|
|
4063
|
+
);
|
|
4064
|
+
if (unresolvedKeys.length > 0) {
|
|
4065
|
+
this.project.logger.warn(
|
|
4066
|
+
`AgentConfig: ProjectMetadata not found; procedure '${proc.name}' using default values`
|
|
4067
|
+
);
|
|
4068
|
+
}
|
|
4069
|
+
return resolved !== proc.content ? { ...proc, content: resolved } : proc;
|
|
4070
|
+
});
|
|
4071
|
+
}
|
|
3284
4072
|
/**
|
|
3285
4073
|
* Collects Claude permission entries from all active bundles.
|
|
3286
4074
|
*/
|
|
@@ -4968,6 +5756,7 @@ export {
|
|
|
4968
5756
|
githubWorkflowBundle,
|
|
4969
5757
|
jestBundle,
|
|
4970
5758
|
meetingAnalysisBundle,
|
|
5759
|
+
orchestratorBundle,
|
|
4971
5760
|
pnpmBundle,
|
|
4972
5761
|
projenBundle,
|
|
4973
5762
|
resolveModelAlias,
|