specweave 1.0.256 → 1.0.259

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.
Files changed (113) hide show
  1. package/CLAUDE.md +56 -221
  2. package/README.md +31 -0
  3. package/bin/specweave.js +17 -0
  4. package/dist/src/adapters/README.md +4 -4
  5. package/dist/src/adapters/agents-md-generator.d.ts.map +1 -1
  6. package/dist/src/adapters/agents-md-generator.js +0 -2
  7. package/dist/src/adapters/agents-md-generator.js.map +1 -1
  8. package/dist/src/adapters/claude/README.md +3 -3
  9. package/dist/src/adapters/claude/adapter.js +3 -3
  10. package/dist/src/adapters/claude-md-generator.js +1 -1
  11. package/dist/src/adapters/claude-md-generator.js.map +1 -1
  12. package/dist/src/adapters/registry.yaml +1 -1
  13. package/dist/src/cli/commands/create-increment.d.ts +24 -0
  14. package/dist/src/cli/commands/create-increment.d.ts.map +1 -0
  15. package/dist/src/cli/commands/create-increment.js +53 -0
  16. package/dist/src/cli/commands/create-increment.js.map +1 -0
  17. package/dist/src/cli/commands/init.d.ts.map +1 -1
  18. package/dist/src/cli/commands/init.js +48 -31
  19. package/dist/src/cli/commands/init.js.map +1 -1
  20. package/dist/src/cli/commands/update.d.ts.map +1 -1
  21. package/dist/src/cli/commands/update.js +36 -0
  22. package/dist/src/cli/commands/update.js.map +1 -1
  23. package/dist/src/cli/helpers/init/directory-structure.d.ts.map +1 -1
  24. package/dist/src/cli/helpers/init/directory-structure.js +13 -1
  25. package/dist/src/cli/helpers/init/directory-structure.js.map +1 -1
  26. package/dist/src/cli/helpers/init/summary-banner.d.ts +11 -0
  27. package/dist/src/cli/helpers/init/summary-banner.d.ts.map +1 -1
  28. package/dist/src/cli/helpers/init/summary-banner.js +49 -3
  29. package/dist/src/cli/helpers/init/summary-banner.js.map +1 -1
  30. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  31. package/dist/src/cli/helpers/issue-tracker/index.js +0 -1
  32. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  33. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.d.ts.map +1 -1
  34. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js +6 -2
  35. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js.map +1 -1
  36. package/dist/src/core/ac-progress-sync.d.ts +13 -0
  37. package/dist/src/core/ac-progress-sync.d.ts.map +1 -1
  38. package/dist/src/core/ac-progress-sync.js +28 -0
  39. package/dist/src/core/ac-progress-sync.js.map +1 -1
  40. package/dist/src/core/config/types.d.ts +24 -1
  41. package/dist/src/core/config/types.d.ts.map +1 -1
  42. package/dist/src/core/config/types.js +6 -1
  43. package/dist/src/core/config/types.js.map +1 -1
  44. package/dist/src/core/doctor/checkers/project-structure-checker.d.ts +1 -0
  45. package/dist/src/core/doctor/checkers/project-structure-checker.d.ts.map +1 -1
  46. package/dist/src/core/doctor/checkers/project-structure-checker.js +53 -3
  47. package/dist/src/core/doctor/checkers/project-structure-checker.js.map +1 -1
  48. package/dist/src/core/fabric/security-scanner.d.ts.map +1 -1
  49. package/dist/src/core/fabric/security-scanner.js +70 -9
  50. package/dist/src/core/fabric/security-scanner.js.map +1 -1
  51. package/dist/src/core/increment/increment-utils.d.ts +6 -0
  52. package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
  53. package/dist/src/core/increment/increment-utils.js +5 -0
  54. package/dist/src/core/increment/increment-utils.js.map +1 -1
  55. package/dist/src/core/living-docs/discovery.d.ts +2 -0
  56. package/dist/src/core/living-docs/discovery.d.ts.map +1 -1
  57. package/dist/src/core/living-docs/discovery.js +91 -17
  58. package/dist/src/core/living-docs/discovery.js.map +1 -1
  59. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts +5 -0
  60. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts.map +1 -1
  61. package/dist/src/core/living-docs/intelligent-analyzer/index.js +3 -3
  62. package/dist/src/core/living-docs/intelligent-analyzer/index.js.map +1 -1
  63. package/dist/src/core/living-docs/lsp-bootstrapper.d.ts +64 -0
  64. package/dist/src/core/living-docs/lsp-bootstrapper.d.ts.map +1 -0
  65. package/dist/src/core/living-docs/lsp-bootstrapper.js +118 -0
  66. package/dist/src/core/living-docs/lsp-bootstrapper.js.map +1 -0
  67. package/dist/src/core/project/project-service.d.ts +10 -1
  68. package/dist/src/core/project/project-service.d.ts.map +1 -1
  69. package/dist/src/core/project/project-service.js +37 -2
  70. package/dist/src/core/project/project-service.js.map +1 -1
  71. package/dist/src/core/universal-auto-create.d.ts +64 -0
  72. package/dist/src/core/universal-auto-create.d.ts.map +1 -0
  73. package/dist/src/core/universal-auto-create.js +228 -0
  74. package/dist/src/core/universal-auto-create.js.map +1 -0
  75. package/package.json +1 -1
  76. package/plugins/specweave/PLUGIN.md +0 -3
  77. package/plugins/specweave/commands/living-docs.md +0 -2
  78. package/plugins/specweave/hooks/stop-sync.sh +34 -5
  79. package/plugins/specweave/hooks/user-prompt-submit.sh +115 -326
  80. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +19 -5
  81. package/plugins/specweave/hooks/v2/handlers/ac-sync-dispatcher.sh +14 -4
  82. package/plugins/specweave/hooks/v2/handlers/universal-auto-create-dispatcher.sh +181 -0
  83. package/plugins/specweave/lib/hooks/sync-living-docs.js +4 -2
  84. package/plugins/specweave/scripts/skill-context.sh +160 -0
  85. package/plugins/specweave/skills/architect/SKILL.md +1 -1
  86. package/plugins/specweave/skills/archive-increments/SKILL.md +13 -3
  87. package/plugins/specweave/skills/auto/SKILL.md +92 -1038
  88. package/plugins/specweave/skills/do/SKILL.md +66 -1106
  89. package/plugins/specweave/skills/docs/SKILL.md +124 -56
  90. package/plugins/specweave/skills/done/SKILL.md +76 -1406
  91. package/plugins/specweave/skills/framework/SKILL.md +1 -1
  92. package/plugins/specweave/skills/increment/SKILL.md +1 -1
  93. package/plugins/specweave/skills/increment-planner/SKILL.md +29 -19
  94. package/plugins/specweave/skills/jobs/SKILL.md +52 -0
  95. package/plugins/specweave/skills/multi-project-spec-mapper/SKILL.md +1 -1
  96. package/plugins/specweave/skills/save/SKILL.md +51 -1372
  97. package/plugins/specweave/skills/smart-reopen-detector/SKILL.md +1 -1
  98. package/plugins/specweave/skills/tdd-orchestrator/SKILL.md +1 -1
  99. package/plugins/specweave/skills/validate/SKILL.md +65 -848
  100. package/plugins/specweave-backend/skills/database-optimizer/SKILL.md +1 -1
  101. package/plugins/specweave-backend/skills/graphql/SKILL.md +1 -1
  102. package/plugins/specweave-frontend/skills/design-system-architect/SKILL.md +1 -1
  103. package/plugins/specweave-frontend/skills/frontend/SKILL.md +1 -1
  104. package/plugins/specweave-frontend/skills/frontend-architect/SKILL.md +1 -1
  105. package/plugins/specweave-frontend/skills/frontend-design/SKILL.md +1 -1
  106. package/plugins/specweave-frontend/skills/i18n-expert/SKILL.md +1 -1
  107. package/plugins/specweave-payments/skills/billing-automation/SKILL.md +1 -1
  108. package/plugins/specweave-testing/skills/accessibility-testing/SKILL.md +1 -1
  109. package/src/templates/CLAUDE.md.template +50 -356
  110. package/src/templates/config.json.template +5 -1
  111. package/plugins/specweave/commands/brownfield-analyzer.md +0 -408
  112. package/plugins/specweave/commands/brownfield-onboarder.md +0 -837
  113. package/plugins/specweave/commands/export-skills.md +0 -179
@@ -373,8 +373,8 @@ escape_json_early() {
373
373
 
374
374
  # v1.0.254: Prompt safety limits to prevent "Prompt is too long" errors
375
375
  # These must match the constants in src/core/lazy-loading/llm-plugin-detector.ts
376
- MAX_ADDITIONAL_CONTEXT_LENGTH=8000
377
- MAX_SKILL_FIRST_PROMPT_LENGTH=2000
376
+ MAX_ADDITIONAL_CONTEXT_LENGTH=3000
377
+ MAX_SKILL_FIRST_PROMPT_LENGTH=800
378
378
 
379
379
  # Helper: Output approve response with context (Claude Code hook format v1.0.166)
380
380
  # CRITICAL: systemMessage is NOT a valid field for UserPromptSubmit hooks!
@@ -525,17 +525,7 @@ if [[ -f "$LSP_STATE_FILE" ]] && command -v jq >/dev/null 2>&1; then
525
525
  MISSING_SERVERS=$(jq -r '.missing[] | "- **\(.language)**: `\(.install)`"' "$LSP_STATE_FILE" 2>/dev/null)
526
526
 
527
527
  if [[ -n "$MISSING_SERVERS" ]]; then
528
- LSP_WARNING_MSG=" **LSP: Install language servers for 100x faster code intelligence**
529
-
530
- The following language servers are not installed:
531
- $MISSING_SERVERS
532
-
533
- LSP provides semantic code understanding (findReferences, goToDefinition, diagnostics).
534
- Without it, Claude uses text search which is slower and less accurate.
535
-
536
- 📖 Guide: https://spec-weave.com/docs/guides/lsp-integration
537
-
538
- ---
528
+ LSP_WARNING_MSG="LSP missing: ${MISSING_SERVERS}. Install for semantic code intelligence. Guide: https://spec-weave.com/docs/guides/lsp-integration
539
529
 
540
530
  "
541
531
  # Mark as warned so we don't show again this session
@@ -1370,57 +1360,12 @@ Task({
1370
1360
  # ask targeted questions first. If complete, proceed directly.
1371
1361
  DEEP_INTERVIEW_MSG=""
1372
1362
  if [[ "$DEEP_INTERVIEW_ENABLED" == "true" ]]; then
1373
- DEEP_INTERVIEW_MSG="
1374
-
1375
- 🧠 **SMART INTERVIEW GATE** (Deep Interview Mode active)
1376
-
1377
- BEFORE calling sw:increment-planner, assess this prompt for completeness.
1378
- Consider ALL prior messages in this conversation.
1379
-
1380
- **Assess complexity:**
1381
- - Trivial → need almost nothing, call increment-planner now
1382
- - Small (single component) → need: tech stack + basic flow
1383
- - Medium (multiple components) → need: stack + integrations + user flows + auth
1384
- - Large (platform/multi-service) → need: architecture + security + deployment + edge cases
1385
-
1386
- **Check signals (weight by project type):**
1387
- - Technical: stack, frameworks, DB, auth, API integrations, deployment target
1388
- - Product: target users, core flows, business model, MVP scope
1389
- - Operational: env vars/keys, CI/CD, monitoring
1390
-
1391
- **Decision:**
1392
- - Sufficient detail → call increment-planner immediately with full context
1393
- - Gaps detected → ask 2-5 targeted questions about ONLY what is missing, then call increment-planner after answers received
1394
- - NEVER ask 10+ questions. NEVER repeat what the user already said."
1363
+ DEEP_INTERVIEW_MSG=" Assess prompt completeness first — if gaps exist, ask 2-5 targeted questions before calling."
1395
1364
  fi
1396
1365
 
1397
- MSG="${WIP_WARNING}${AUTOLOAD_PREFIX}╔══════════════════════════════════════════════════════════════════════════════╗
1398
- ║ 🎯 SKILL FIRST - Call Skill tool BEFORE implementation ║
1399
- ╚══════════════════════════════════════════════════════════════════════════════╝
1400
-
1401
- **Your FIRST tool call must be:**
1402
- \`\`\`
1403
- Skill({ skill: \"sw:increment-planner\", args: \"${ESCAPED_PROMPT}\" })
1404
- \`\`\`
1405
-
1406
- **Order matters:**
1407
- 1. ✅ Call Skill tool FIRST (as shown above)
1408
- 2. ✅ THEN proceed with implementation normally
1409
-
1410
- **Detection**: Feature request (confidence: ${INC_CONF})
1411
- **Reason**: ${INC_REASON}${AGENT_DIRECTIVE}${DEEP_INTERVIEW_MSG}
1412
-
1413
- ---
1414
-
1415
- ⚠️ **SKILL CHAINING REQUIRED** - \"SKILL FIRST\" does NOT mean \"only one skill\"!
1416
-
1417
- After sw:increment-planner, ALSO invoke domain skills for your tech stack:
1418
- - React/Vue/Angular → \`sw-frontend:frontend-architect\`
1419
- - .NET/C# → \`sw-backend:dotnet-backend\`
1420
- - Stripe → \`sw-payments:stripe-integration\`
1421
- - After code → LSP works automatically (use findReferences, goToDefinition)
1422
-
1423
- See CLAUDE.md section \"MANDATORY: Skill Chaining\" for full pattern."
1366
+ MSG="${WIP_WARNING}${AUTOLOAD_PREFIX}SKILL FIRST: \`Skill({ skill: \"sw:increment-planner\", args: \"${ESCAPED_PROMPT}\" })\` — call BEFORE implementation.
1367
+ Detection: ${INC_REASON} (confidence: ${INC_CONF}).${AGENT_DIRECTIVE}${DEEP_INTERVIEW_MSG}
1368
+ After increment-planner, chain domain skills per tech stack (see CLAUDE.md Skill Chaining)."
1424
1369
  # Use approve+additionalContext so Claude can read and follow
1425
1370
  # the SKILL FIRST instructions (block erases prompt from context)
1426
1371
  output_approve_with_context "$MSG"
@@ -1428,20 +1373,7 @@ See CLAUDE.md section \"MANDATORY: Skill Chaining\" for full pattern."
1428
1373
  else
1429
1374
  # v1.0.169: Also suggest direct skill call for non-mandatory
1430
1375
  ESCAPED_PROMPT_SUGGEST=$(truncate_and_escape_prompt "$PROMPT")
1431
- MSG="${WIP_WARNING}${AUTOLOAD_PREFIX}💡 **Increment Suggestion**: This looks like new feature work.
1432
-
1433
- Consider creating an increment first:
1434
- \`\`\`
1435
- Skill({ skill: \"sw:increment-planner\", args: \"${ESCAPED_PROMPT_SUGGEST}\" })
1436
- \`\`\`
1437
-
1438
- Or via command: \`$CMD\`
1439
-
1440
- *Reason: $INC_REASON*${AGENT_DIRECTIVE}
1441
-
1442
- ---
1443
-
1444
- *Tip: Disable with \`incrementAssist.enabled: false\` in config.json*"
1376
+ MSG="${WIP_WARNING}${AUTOLOAD_PREFIX}Increment suggested: \`Skill({ skill: \"sw:increment-planner\", args: \"${ESCAPED_PROMPT_SUGGEST}\" })\` or \`$CMD\`. Reason: $INC_REASON${AGENT_DIRECTIVE}"
1445
1377
  output_approve_with_context "$MSG"
1446
1378
  exit 0
1447
1379
  fi
@@ -1450,20 +1382,7 @@ Or via command: \`$CMD\`
1450
1382
  hotfix)
1451
1383
  # v1.0.169: Direct skill call for hotfix too
1452
1384
  ESCAPED_PROMPT_HOTFIX=$(truncate_and_escape_prompt "$PROMPT")
1453
- MSG="${WIP_WARNING}${AUTOLOAD_PREFIX}🚨 **Hotfix Detected**: Urgent production issue.
1454
-
1455
- Create a hotfix increment:
1456
- \`\`\`
1457
- Skill({ skill: \"sw:increment-planner\", args: \"--type=hotfix ${ESCAPED_PROMPT_HOTFIX}\" })
1458
- \`\`\`
1459
-
1460
- Or via command: \`/sw:increment --type=hotfix \"${INC_NAME:-urgent-fix}\"\`
1461
-
1462
- *Reason: $INC_REASON*
1463
-
1464
- ---
1465
-
1466
- *Tip: Disable with \`incrementAssist.enabled: false\` in config.json*"
1385
+ MSG="${WIP_WARNING}${AUTOLOAD_PREFIX}Hotfix detected: \`Skill({ skill: \"sw:increment-planner\", args: \"--type=hotfix ${ESCAPED_PROMPT_HOTFIX}\" })\`. Reason: $INC_REASON"
1467
1386
  output_approve_with_context "$MSG"
1468
1387
  exit 0
1469
1388
  ;;
@@ -1471,19 +1390,7 @@ Or via command: \`/sw:increment --type=hotfix \"${INC_NAME:-urgent-fix}\"\`
1471
1390
  reopen)
1472
1391
  HINT=""
1473
1392
  [[ -n "$INC_KEYWORD" ]] && HINT=" (look for: *$INC_KEYWORD*)"
1474
- MSG="${AUTOLOAD_PREFIX}💡 **Increment Suggestion**: This looks related to previous work$HINT.
1475
-
1476
- Consider reopening the existing increment:
1477
- \`\`\`
1478
- /sw:status # Find the related increment
1479
- specweave resume <id> # Reopen it
1480
- \`\`\`
1481
-
1482
- *Reason: $INC_REASON*
1483
-
1484
- ---
1485
-
1486
- *Tip: Disable with \`incrementAssist.enabled: false\` in config.json*"
1393
+ MSG="${AUTOLOAD_PREFIX}Related to previous work$HINT. Consider: \`/sw:status\` then \`specweave resume <id>\`. Reason: $INC_REASON"
1487
1394
  output_approve_with_context "$MSG"
1488
1395
  exit 0
1489
1396
  ;;
@@ -1495,20 +1402,7 @@ specweave resume <id> # Reopen it
1495
1402
  CMD_SMALLFIX="/sw:increment"
1496
1403
  [[ -n "$INC_NAME" ]] && CMD_SMALLFIX="/sw:increment \"$INC_NAME\""
1497
1404
 
1498
- MSG="${WIP_WARNING}${AUTOLOAD_PREFIX}💡 **Increment Suggestion**: This looks like a small change worth tracking.
1499
-
1500
- Consider creating an increment:
1501
- \`\`\`
1502
- Skill({ skill: \"sw:increment-planner\", args: \"${ESCAPED_PROMPT_SMALLFIX}\" })
1503
- \`\`\`
1504
-
1505
- Or via command: \`$CMD_SMALLFIX\`
1506
-
1507
- *Reason: $INC_REASON*${AGENT_DIRECTIVE}
1508
-
1509
- ---
1510
-
1511
- *Tip: Disable with \`incrementAssist.enabled: false\` in config.json*"
1405
+ MSG="${WIP_WARNING}${AUTOLOAD_PREFIX}Small change consider tracking: \`Skill({ skill: \"sw:increment-planner\", args: \"${ESCAPED_PROMPT_SMALLFIX}\" })\` or \`$CMD_SMALLFIX\`. Reason: $INC_REASON${AGENT_DIRECTIVE}"
1512
1406
  output_approve_with_context "$MSG"
1513
1407
  exit 0
1514
1408
  ;;
@@ -1527,37 +1421,11 @@ Or via command: \`$CMD_SMALLFIX\`
1527
1421
  [[ -n "$LSP_EXPLICIT_REQUEST_MSG" ]] && SKILL_ONLY_PREFIX="${SKILL_ONLY_PREFIX}${LSP_EXPLICIT_REQUEST_MSG}"
1528
1422
 
1529
1423
  if [[ "$SKILL_MANDATORY" == "true" ]]; then
1530
- MSG="${SKILL_ONLY_PREFIX}╔══════════════════════════════════════════════════════════════════════════════╗
1531
- ║ 🎯 SKILL REQUIRED - This task needs specialized skill support ║
1532
- ╚══════════════════════════════════════════════════════════════════════════════╝
1533
-
1534
- <skill_invocation_required>
1535
- ### 🎯 MANDATORY: Use ${SKILL_INVOCATION} Skill
1536
-
1537
- You MUST use this skill for this task. Do NOT proceed without loading it first.
1538
-
1539
- **Invoke NOW using Skill tool:**
1540
- \`\`\`typescript
1541
- Skill({ skill: \"${SKILL_INVOCATION}\" })
1542
- \`\`\`
1543
-
1544
- **Why this skill is required:**
1545
- ${SKILL_REASON:-This skill provides specialized support for your task.}
1546
-
1547
- ⚠️ **Do NOT skip this** - the skill has domain expertise needed for this operation.
1548
- </skill_invocation_required>"
1424
+ MSG="${SKILL_ONLY_PREFIX}SKILL REQUIRED: \`Skill({ skill: \"${SKILL_INVOCATION}\" })\` — call before proceeding. ${SKILL_REASON:-Specialized support needed.}"
1549
1425
  output_approve_with_context "$MSG"
1550
1426
  exit 0
1551
1427
  else
1552
- # Non-mandatory skill recommendation
1553
- MSG="${SKILL_ONLY_PREFIX}💡 **Skill Recommended**: Consider using a specialized skill.
1554
-
1555
- Use \`${SKILL_INVOCATION}\` for better results:
1556
- \`\`\`typescript
1557
- Skill({ skill: \"${SKILL_INVOCATION}\" })
1558
- \`\`\`
1559
-
1560
- *${SKILL_REASON:-This skill provides specialized support for your task.}*"
1428
+ MSG="${SKILL_ONLY_PREFIX}Skill recommended: \`Skill({ skill: \"${SKILL_INVOCATION}\" })\`. ${SKILL_REASON:-Specialized support for this task.}"
1561
1429
  output_approve_with_context "$MSG"
1562
1430
  exit 0
1563
1431
  fi
@@ -1595,126 +1463,29 @@ Skill({ skill: \"${SKILL_INVOCATION}\" })
1595
1463
  if [[ "$ROUTING_SKILLS_COUNT" -gt 0 || -n "$AUTOLOAD_PLUGINS_MSG" ]]; then
1596
1464
  BRAIN_MSG=""
1597
1465
 
1598
- # Header
1599
- BRAIN_MSG="# 🧠 Router Brain Active\\n\\n"
1466
+ # Compact router output — one line per decision
1467
+ [[ -n "$PLUGINS_INSTALLED" ]] && BRAIN_MSG+="Plugins loaded: ${PLUGINS_INSTALLED}. "
1468
+ [[ -n "$PLUGINS_ALREADY" ]] && BRAIN_MSG+="Plugins active: ${PLUGINS_ALREADY}. "
1600
1469
 
1601
- # Analysis summary table
1602
- BRAIN_MSG+="## Analysis\\n"
1603
- BRAIN_MSG+="| Aspect | Decision |\\n"
1604
- BRAIN_MSG+="|--------|----------|\\n"
1605
-
1606
- # Plugins row
1607
- if [[ -n "$PLUGINS_INSTALLED" ]]; then
1608
- BRAIN_MSG+="| Plugins | ✅ Loaded: ${PLUGINS_INSTALLED} |\\n"
1609
- elif [[ -n "$PLUGINS_ALREADY" ]]; then
1610
- BRAIN_MSG+="| Plugins | ✅ Using: ${PLUGINS_ALREADY} |\\n"
1611
- fi
1612
-
1613
- # Increment row
1614
1470
  if [[ "$INC_ACTION" == "new" || "$INC_ACTION" == "hotfix" ]]; then
1615
- BRAIN_MSG+="| Increment | 📋 Create: \\\"${INC_NAME:-new-feature}\\\" |\\n"
1471
+ BRAIN_MSG+="Increment: create \\\"${INC_NAME:-new-feature}\\\" (${INC_ACTION}). "
1616
1472
  elif [[ "$INC_ACTION" == "reopen" ]]; then
1617
- BRAIN_MSG+="| Increment | 🔄 Reopen existing |\\n"
1473
+ BRAIN_MSG+="Increment: reopen existing"
1474
+ [[ -n "$INC_KEYWORD" ]] && BRAIN_MSG+=" (${INC_KEYWORD})"
1475
+ BRAIN_MSG+=". "
1618
1476
  fi
1619
1477
 
1620
- # Primary skill row
1621
1478
  if [[ -n "$PRIMARY_SKILL" ]]; then
1622
- BRAIN_MSG+="| Primary Skill | \`${PRIMARY_SKILL}\` |\\n"
1623
- fi
1624
-
1625
- # Secondary skills row
1626
- if [[ -n "$SECONDARY_SKILLS" ]]; then
1627
- BRAIN_MSG+="| Supporting | ${SECONDARY_SKILLS} |\\n"
1628
- fi
1629
-
1630
- BRAIN_MSG+="\\n"
1631
-
1632
- # Workflow section
1633
- BRAIN_MSG+="## Workflow\\n\\n"
1634
-
1635
- STEP_NUM=1
1636
-
1637
- # Step: Create increment (if needed)
1638
- if [[ "$INC_ACTION" == "new" ]]; then
1639
- CMD="/sw:increment"
1640
- [[ -n "$INC_NAME" ]] && CMD="/sw:increment \\\"$INC_NAME\\\""
1641
- BRAIN_MSG+="### Step ${STEP_NUM}: Create Increment\\n"
1642
- BRAIN_MSG+="\\\`\\\`\\\`\\n${CMD}\\n\\\`\\\`\\\`\\n"
1643
- BRAIN_MSG+="*${INC_REASON:-Tracks this feature work with specs}*\\n\\n"
1644
- STEP_NUM=$((STEP_NUM + 1))
1645
- elif [[ "$INC_ACTION" == "hotfix" ]]; then
1646
- CMD="/sw:increment --type=hotfix \\\"${INC_NAME:-urgent-fix}\\\""
1647
- BRAIN_MSG+="### Step ${STEP_NUM}: Create Hotfix Increment\\n"
1648
- BRAIN_MSG+="\\\`\\\`\\\`\\n${CMD}\\n\\\`\\\`\\\`\\n"
1649
- BRAIN_MSG+="*${INC_REASON:-Urgent production issue}*\\n\\n"
1650
- STEP_NUM=$((STEP_NUM + 1))
1651
- elif [[ "$INC_ACTION" == "reopen" ]]; then
1652
- BRAIN_MSG+="### Step ${STEP_NUM}: Find & Reopen Increment\\n"
1653
- BRAIN_MSG+="\\\`\\\`\\\`\\n/sw:status # Find related increment\\nspecweave resume <id>\\n\\\`\\\`\\\`\\n"
1654
- [[ -n "$INC_KEYWORD" ]] && BRAIN_MSG+="*Look for: ${INC_KEYWORD}*\\n"
1655
- BRAIN_MSG+="\\n"
1656
- STEP_NUM=$((STEP_NUM + 1))
1657
- fi
1658
-
1659
- # Step: SPAWN AGENTS (v1.0.155 - Task tool directive)
1660
- if [[ -n "$PRIMARY_SKILL" ]]; then
1661
- BRAIN_MSG+="### Step ${STEP_NUM}: 🚀 SPAWN SPECIALIZED AGENTS\\n\\n"
1662
-
1663
- # Build agent subagent_type from skill info
1664
- # Format: plugin:skill:skill (e.g., sw-frontend:frontend-architect:frontend-architect)
1479
+ # Extract agent type for Task tool
1665
1480
  PRIMARY_PLUGIN=$(echo "$JSON_OUTPUT" | jq -r '.routing.skills[] | select(.priority == "primary") | .plugin // empty' 2>/dev/null | head -1)
1666
1481
  PRIMARY_SKILL_NAME=$(echo "$JSON_OUTPUT" | jq -r '.routing.skills[] | select(.priority == "primary") | .name // empty' 2>/dev/null | head -1)
1667
-
1668
- if [[ -n "$PRIMARY_PLUGIN" && -n "$PRIMARY_SKILL_NAME" ]]; then
1669
- AGENT_TYPE="${PRIMARY_PLUGIN}:${PRIMARY_SKILL_NAME}:${PRIMARY_SKILL_NAME}"
1670
-
1671
- BRAIN_MSG+="**⚠️ MANDATORY: Use Task tool to spawn agent for implementation**\\n\\n"
1672
- BRAIN_MSG+="\\\`\\\`\\\`typescript\\n"
1673
- BRAIN_MSG+="Task({\\n"
1674
- BRAIN_MSG+=" subagent_type: \\\"${AGENT_TYPE}\\\",\\n"
1675
- BRAIN_MSG+=" prompt: \\\"Implement [describe task]...\\\",\\n"
1676
- BRAIN_MSG+=" description: \\\"[short description]\\\"\\n"
1677
- BRAIN_MSG+="})\\n"
1678
- BRAIN_MSG+="\\\`\\\`\\\`\\n\\n"
1679
-
1680
- BRAIN_MSG+="**Why**: Specialized agents have deep domain knowledge and produce better code than direct implementation.\\n\\n"
1681
- [[ -n "$PRIMARY_REASON" ]] && BRAIN_MSG+="**Agent expertise**: *${PRIMARY_REASON}*\\n\\n"
1682
- fi
1683
-
1684
- # Add secondary agents if present
1685
- if [[ -n "$SECONDARY_SKILLS" ]]; then
1686
- BRAIN_MSG+="**Additional agents available**:\\n"
1687
- # Parse each secondary skill
1688
- echo "$JSON_OUTPUT" | jq -r '.routing.skills[] | select(.priority == "secondary") | "\(.plugin):\(.name):\(.name)"' 2>/dev/null | while read -r sec_agent; do
1689
- [[ -n "$sec_agent" ]] && BRAIN_MSG+="- \`${sec_agent}\`\\n"
1690
- done
1691
- BRAIN_MSG+="\\n"
1692
- fi
1693
-
1694
- # Add invoke timing hint
1695
- case "$PRIMARY_INVOKE" in
1696
- immediate)
1697
- BRAIN_MSG+="**Timing**: Spawn agent NOW - task is self-contained\\n\\n"
1698
- ;;
1699
- after_increment)
1700
- BRAIN_MSG+="**Timing**: Spawn agent AFTER creating increment\\n\\n"
1701
- ;;
1702
- after_planning)
1703
- BRAIN_MSG+="**Timing**: Enter plan mode first, THEN spawn agent\\n\\n"
1704
- ;;
1705
- esac
1706
-
1707
- STEP_NUM=$((STEP_NUM + 1))
1708
- fi
1709
-
1710
- # Add plan mode suggestion if recommended
1711
- if [[ "$SUGGEST_PLAN" == "true" ]]; then
1712
- BRAIN_MSG+="### 💡 Suggestion: Use Plan Mode\\n"
1713
- BRAIN_MSG+="This task appears complex. Consider entering plan mode first.\\n\\n"
1482
+ BRAIN_MSG+="Primary skill: ${PRIMARY_SKILL}"
1483
+ [[ -n "$PRIMARY_PLUGIN" && -n "$PRIMARY_SKILL_NAME" ]] && BRAIN_MSG+=" (agent: ${PRIMARY_PLUGIN}:${PRIMARY_SKILL_NAME}:${PRIMARY_SKILL_NAME})"
1484
+ BRAIN_MSG+=", invoke: ${PRIMARY_INVOKE:-after_increment}. "
1485
+ [[ -n "$SECONDARY_SKILLS" ]] && BRAIN_MSG+="Also: ${SECONDARY_SKILLS}. "
1714
1486
  fi
1715
1487
 
1716
- # Footer
1717
- BRAIN_MSG+="---\\n*Router Brain v1.0.150*"
1488
+ [[ "$SUGGEST_PLAN" == "true" ]] && BRAIN_MSG+="Suggest plan mode. "
1718
1489
 
1719
1490
  output_approve_with_context "$BRAIN_MSG"
1720
1491
  exit 0
@@ -1737,14 +1508,34 @@ Skill({ skill: \"${SKILL_INVOCATION}\" })
1737
1508
  fi
1738
1509
 
1739
1510
  # ==================================================================
1740
- # KEYWORD FALLBACK REMOVED (v1.0.159)
1511
+ # KEYWORD FALLBACK FOR INCREMENT DISCIPLINE (v1.0.257)
1741
1512
  # ==================================================================
1742
- # Keyword fallback was too aggressive - it matched "test" for simple
1743
- # test runs, "database" for any database mention, etc.
1513
+ # Plugin keyword fallback was removed in v1.0.159 (too aggressive for
1514
+ # auto-installing plugins). But INCREMENT DISCIPLINE still needs a
1515
+ # fallback when LLM detection fails/times out. Without this, prompts
1516
+ # like "big test react component" silently bypass spec-first discipline.
1744
1517
  #
1745
- # Now we ONLY use LLM-based detection. If LLM returns empty,
1746
- # no plugins are installed. This is intentional - user can
1747
- # manually install with: claude plugin install sw-frontend@specweave
1518
+ # This fallback ONLY handles increment suggestions (not plugin installs).
1519
+ if [[ "$LLM_DETECTION_FAILED" == "true" && "$INCREMENT_ASSIST_ENABLED" == "true" ]]; then
1520
+ # Check for implementation-intent keywords
1521
+ if echo "$PROMPT" | grep -qiE "(test|component|feature|fix|refactor|setup|configure|integrate|migrate|upgrade|write|style|design|add|create|implement|build|develop|deploy|scaffold|generate)"; then
1522
+ # Exclude questions (starting with question words or ending with ?)
1523
+ if ! echo "$PROMPT" | grep -qiE "^[[:space:]]*(what|how|why|explain|tell me|can you|does|should|is there|where|when|which)" && \
1524
+ ! echo "$PROMPT" | grep -qE "\?[[:space:]]*$"; then
1525
+ FALLBACK_ESCAPED=$(truncate_and_escape_prompt "$PROMPT")
1526
+ if [[ "$INCREMENT_MANDATORY_CONFIG" == "true" ]]; then
1527
+ FALLBACK_MSG="SKILL FIRST: \`Skill({ skill: \"sw:increment-planner\", args: \"${FALLBACK_ESCAPED}\" })\` — call BEFORE implementation.
1528
+ Detection: Implementation keywords detected (LLM unavailable, keyword fallback).
1529
+ After increment-planner, chain domain skills per tech stack (see CLAUDE.md Skill Chaining)."
1530
+ else
1531
+ FALLBACK_MSG="Increment suggested: \`Skill({ skill: \"sw:increment-planner\", args: \"${FALLBACK_ESCAPED}\" })\`. Reason: Implementation keywords detected (LLM unavailable, keyword fallback)."
1532
+ fi
1533
+ echo "[$(date -Iseconds)] keyword-fallback | prompt_keywords_matched=true | mandatory=$INCREMENT_MANDATORY_CONFIG" >> "$LAZY_LOAD_LOG"
1534
+ output_approve_with_context "$FALLBACK_MSG"
1535
+ exit 0
1536
+ fi
1537
+ fi
1538
+ fi
1748
1539
  fi
1749
1540
  fi
1750
1541
  fi
@@ -1823,35 +1614,7 @@ if [[ -n "$SW_PROJECT_ROOT" ]] && [[ -d "$SW_PROJECT_ROOT/.specweave" ]]; then
1823
1614
 
1824
1615
  # v1.0.160: STRICT TDD adds mandatory blocking directive
1825
1616
  if [[ "$TDD_ENFORCEMENT" == "strict" ]]; then
1826
- TDD_MSG="🚫 **STRICT TDD MODE - MANDATORY ENFORCEMENT**
1827
-
1828
- ⚠️ **YOU MUST FOLLOW RED→GREEN→REFACTOR OR YOUR CHANGES WILL BE REJECTED**
1829
-
1830
- **MANDATORY WORKFLOW (NO EXCEPTIONS):**
1831
- 1. **[RED]** Write failing test FIRST → Run test → Verify it FAILS
1832
- 2. **[GREEN]** Write MINIMAL code to pass → Run test → Verify it PASSES
1833
- 3. **[REFACTOR]** Improve code quality → Tests must stay green
1834
-
1835
- **STRICT RULES - VIOLATIONS ARE BLOCKED:**
1836
- - ❌ CANNOT write implementation before test exists
1837
- - ❌ CANNOT mark [GREEN] task complete before [RED] is done
1838
- - ❌ CANNOT skip the test-failure verification step
1839
- - ❌ CANNOT implement features without corresponding tests
1840
-
1841
- **BEFORE ANY IMPLEMENTATION:**
1842
- 1. Find or create the [RED] test task
1843
- 2. Write the test
1844
- 3. Run the test and confirm it FAILS
1845
- 4. Only THEN proceed to [GREEN] implementation
1846
-
1847
- **COMMANDS:** Use \`/sw:tdd-cycle\` for guided workflow
1848
-
1849
- ---
1850
-
1851
- **COMMANDS:** \`/sw:tdd-cycle\` for guided workflow | \`/sw:tdd-red\`, \`/sw:tdd-green\`, \`/sw:tdd-refactor\` for phases
1852
-
1853
- ---
1854
- "
1617
+ TDD_MSG="STRICT TDD ACTIVE (source: ${TDD_SOURCE}). RED->GREEN->REFACTOR enforced. No implementation before failing test. Use /sw:tdd-cycle."
1855
1618
  fi
1856
1619
 
1857
1620
  # v1.0.201: Include LSP instructions BEFORE TDD message
@@ -1882,7 +1645,8 @@ fi
1882
1645
  # This covers 90%+ of prompts with <5ms overhead
1883
1646
  # v1.0.144: Still show plugin autoload message if plugins are being loaded
1884
1647
  # v1.0.155: AUTOLOAD_PLUGINS_MSG now includes new plugin warnings from helper function
1885
- if ! echo "$PROMPT" | grep -qE "(specweave|/sw:|increment|add|create|implement|build|develop)"; then
1648
+ # v1.0.257: Expanded keywords to catch implementation prompts that bypass LLM detection
1649
+ if ! echo "$PROMPT" | grep -qiE "(specweave|/sw:|increment|add|create|implement|build|develop|test|component|feature|fix|refactor|write|style|setup|configure|migrate|deploy|scaffold)"; then
1886
1650
  if [[ -n "$AUTOLOAD_PLUGINS_MSG" ]]; then
1887
1651
  # Show plugin loading feedback even for non-SpecWeave prompts
1888
1652
  output_approve_with_context "$AUTOLOAD_PLUGINS_MSG"
@@ -2169,6 +1933,59 @@ if [[ "$ACTIVE_COUNT" -gt 0 ]]; then
2169
1933
  fi
2170
1934
  fi
2171
1935
 
1936
+ # ==============================================================================
1937
+ # ARCHIVE SUGGESTION (v1.0.257 - Auto-archive when too many increments)
1938
+ # ==============================================================================
1939
+ # When total numbered increment directories exceed threshold, suggest archiving.
1940
+ # In auto mode: archive silently. Interactive: inject suggestion for LLM.
1941
+ # Rate-limited: once per day via marker file.
1942
+ ARCHIVE_SUGGESTION_MSG=""
1943
+ if [[ -d "$SPECWEAVE_DIR/increments" ]]; then
1944
+ TOTAL_INCREMENT_DIRS=$(ls -d "$SPECWEAVE_DIR/increments"/[0-9]* 2>/dev/null | wc -l | tr -d ' ')
1945
+
1946
+ # Read threshold from config (default: 10)
1947
+ _ARCHIVE_THRESHOLD=10
1948
+ if [[ -f "$CONFIG_PATH" ]] && command -v jq >/dev/null 2>&1; then
1949
+ _ARCHIVE_THRESHOLD=$(jq -r '.archiving.autoArchiveThreshold // 10' "$CONFIG_PATH" 2>/dev/null || echo "10")
1950
+ fi
1951
+ [[ ! "$_ARCHIVE_THRESHOLD" =~ ^[0-9]+$ ]] && _ARCHIVE_THRESHOLD=10
1952
+
1953
+ if [[ "$TOTAL_INCREMENT_DIRS" -ge "$_ARCHIVE_THRESHOLD" ]]; then
1954
+ # Rate limit: once per day via marker file
1955
+ ARCHIVE_MARKER="$SPECWEAVE_DIR/state/archive-suggestion.marker"
1956
+ SHOULD_SUGGEST=true
1957
+ if [[ -f "$ARCHIVE_MARKER" ]]; then
1958
+ MARKER_DATE=$(cat "$ARCHIVE_MARKER" 2>/dev/null | head -1)
1959
+ TODAY=$(date +%Y-%m-%d)
1960
+ [[ "$MARKER_DATE" == "$TODAY" ]] && SHOULD_SUGGEST=false
1961
+ fi
1962
+
1963
+ if [[ "$SHOULD_SUGGEST" == "true" ]]; then
1964
+ # Check if in auto mode
1965
+ AUTO_STATE_FILE="$SPECWEAVE_DIR/state/auto.json"
1966
+ IN_AUTO_MODE=false
1967
+ if [[ -f "$AUTO_STATE_FILE" ]] && command -v jq >/dev/null 2>&1; then
1968
+ AUTO_STATUS=$(jq -r '.status // "idle"' "$AUTO_STATE_FILE" 2>/dev/null)
1969
+ [[ "$AUTO_STATUS" == "running" ]] && IN_AUTO_MODE=true
1970
+ fi
1971
+
1972
+ if [[ "$IN_AUTO_MODE" == "true" ]]; then
1973
+ # Auto mode: archive silently in background
1974
+ if command -v specweave >/dev/null 2>&1; then
1975
+ specweave archive --keep-last 10 2>/dev/null &
1976
+ fi
1977
+ else
1978
+ # Interactive: suggest to LLM
1979
+ ARCHIVE_SUGGESTION_MSG="⚠️ **Archive suggested**: ${TOTAL_INCREMENT_DIRS} increments in workspace (threshold: ${_ARCHIVE_THRESHOLD}). Run: \`specweave archive --keep-last 10\`\n\n"
1980
+ fi
1981
+
1982
+ # Write today's date as marker
1983
+ mkdir -p "$(dirname "$ARCHIVE_MARKER")" 2>/dev/null
1984
+ echo "$(date +%Y-%m-%d)" > "$ARCHIVE_MARKER" 2>/dev/null || true
1985
+ fi
1986
+ fi
1987
+ fi
1988
+
2172
1989
  # ==============================================================================
2173
1990
  # SMART INTERVIEW GATE (v1.0.243 - LLM-Driven Prompt Assessment)
2174
1991
  # ==============================================================================
@@ -2189,31 +2006,7 @@ if [[ "$DEEP_INTERVIEW_ENABLED" == "true" ]] && [[ -z "$ACTIVE_INCREMENT" ]]; th
2189
2006
  fi
2190
2007
 
2191
2008
  if [[ "$HAVE_ACTIVE_STATE" != "true" ]]; then
2192
- SMART_INTERVIEW_GATE_MSG="
2193
- 🧠 **SMART INTERVIEW GATE** (Deep Interview Mode active, no increment yet)
2194
-
2195
- Before proceeding, assess this conversation for completeness. Consider ALL prior messages.
2196
-
2197
- **Assess complexity first:**
2198
- - Trivial (config change, typo) → need almost nothing, proceed immediately
2199
- - Small (single component) → need: tech stack + basic flow
2200
- - Medium (multiple components) → need: stack + integrations + user flows + auth approach
2201
- - Large (platform/multi-service) → need: architecture + security + deployment + edge cases + monitoring
2202
-
2203
- **Check for these signals (weight by project type):**
2204
- - Technical: tech stack, frameworks, database, auth method, API integrations, deployment target
2205
- - Product: target users, core flows, business model, MVP scope, timeline
2206
- - Operational: env vars/keys, CI/CD, monitoring needs
2207
-
2208
- **Your decision:**
2209
- - If sufficient detail for the detected complexity → call \`Skill({ skill: \\\"sw:increment-planner\\\", args: \\\"<summarize all gathered context>\\\" })\`
2210
- - If gaps exist → ask 2-5 targeted questions about ONLY what is missing. Do NOT repeat questions already answered in prior messages. Do NOT run a full category-by-category interview.
2211
-
2212
- **Rules:**
2213
- - NEVER overwhelm with 10+ questions at once
2214
- - NEVER ask about things the user already explained
2215
- - Simple projects need fewer signals than complex platforms
2216
- - When in doubt about one detail, ask — but don't block on nice-to-haves"
2009
+ SMART_INTERVIEW_GATE_MSG="No active increment. Assess prompt completeness for complexity — if gaps, ask 2-5 targeted questions. If sufficient, call sw:increment-planner."
2217
2010
  fi
2218
2011
  fi
2219
2012
 
@@ -2412,16 +2205,7 @@ fi
2412
2205
  # COMMAND SUGGESTIONS: Guide users to structured workflow
2413
2206
  # ==============================================================================
2414
2207
 
2415
- if echo "$PROMPT" | grep -qiE "(add|create|implement|build|develop)" && ! echo "$PROMPT" | grep -q "/sw:"; then
2416
- if [[ -n "$CONTEXT" ]]; then
2417
- CONTEXT="$CONTEXT
2418
-
2419
- 💡 TIP: Consider using SpecWeave commands for structured development:
2420
- - /sw:increment \"feature name\" # Plan new increment
2421
- - /sw:do # Execute current tasks
2422
- - /sw:progress # Check progress"
2423
- fi
2424
- fi
2208
+ # Command suggestions removed (v1.0.257) already in CLAUDE.md, reduces per-turn context
2425
2209
 
2426
2210
  # ==============================================================================
2427
2211
  # STATUS LINE REFRESH (v0.26.13 - CONDITIONAL + ASYNC)
@@ -2480,6 +2264,11 @@ if [[ -n "$LSP_EXPLICIT_REQUEST_MSG" ]]; then
2480
2264
  FINAL_MESSAGE="${FINAL_MESSAGE}${LSP_EXPLICIT_REQUEST_MSG}"
2481
2265
  fi
2482
2266
 
2267
+ # v1.0.257: Archive suggestion when too many increments
2268
+ if [[ -n "$ARCHIVE_SUGGESTION_MSG" ]]; then
2269
+ FINAL_MESSAGE="${FINAL_MESSAGE}${ARCHIVE_SUGGESTION_MSG}"
2270
+ fi
2271
+
2483
2272
  # v1.0.243: Smart Interview Gate for non-incrementAssist paths
2484
2273
  # When deep interview is enabled but incrementAssist didn't trigger SKILL FIRST,
2485
2274
  # still inject the gate so LLM can gather context across conversational prompts.
@@ -292,6 +292,20 @@ case "$FILE_PATH" in
292
292
  )
293
293
  log_debug "IMMEDIATE SYNC completed for $INC_ID"
294
294
  fi
295
+
296
+ # ========================================================================
297
+ # EXPLICIT CLOSURE (v1.0.257+): Close external issues on completion
298
+ # ========================================================================
299
+ # Only on completed/done - triggers provider-agnostic closure for ALL
300
+ # user stories, ensuring external issues are closed even if AC sync
301
+ # missed them. NOT called on reopened or on every AC change.
302
+ if [[ "$CURRENT_STATUS" == "completed" ]] || [[ "$CURRENT_STATUS" == "done" ]]; then
303
+ AC_CLOSE_DISPATCHER="${HOOK_DIR}/../handlers/ac-sync-dispatcher.sh"
304
+ if [[ -f "$AC_CLOSE_DISPATCHER" ]]; then
305
+ log_debug "EXPLICIT CLOSURE: Triggering provider-agnostic closure for $INC_ID"
306
+ SPECWEAVE_CLOSE_ALL=1 safe_run_background "$AC_CLOSE_DISPATCHER" "ac-close" "$INC_ID"
307
+ fi
308
+ fi
295
309
  fi
296
310
  fi
297
311
  ;;
@@ -358,14 +372,14 @@ case "$FILE_PATH" in
358
372
  fi
359
373
 
360
374
  # ========================================================================
361
- # GITHUB AUTO-CREATE (v1.0.237+): Create issues when spec.md is written
375
+ # UNIVERSAL AUTO-CREATE (v1.0.256+): Create items in ALL enabled providers
362
376
  # ========================================================================
363
377
  # When spec.md is created/updated AND has user stories, auto-create
364
- # GitHub issues if autoSync or auto_create_github_issue is enabled.
378
+ # items in GitHub/JIRA/ADO if autoSync or auto_create is enabled.
365
379
  if [[ "$FILE_PATH" == *spec.md ]]; then
366
- GITHUB_AUTO_CREATE="${HOOK_DIR}/../../../specweave-github/hooks/github-auto-create-handler.sh"
367
- if [[ -f "$GITHUB_AUTO_CREATE" ]]; then
368
- safe_run_background "$GITHUB_AUTO_CREATE" "github-auto-create" "$INC_ID"
380
+ UNIVERSAL_AUTO_CREATE="${HOOK_DIR}/../handlers/universal-auto-create-dispatcher.sh"
381
+ if [[ -f "$UNIVERSAL_AUTO_CREATE" ]]; then
382
+ safe_run_background "$UNIVERSAL_AUTO_CREATE" "universal-auto-create" "$INC_ID"
369
383
  fi
370
384
  fi
371
385
 
@@ -56,6 +56,11 @@ if [[ -z "$INC_ID" ]]; then
56
56
  exit 0
57
57
  fi
58
58
 
59
+ # Validate INC_ID format (defense-in-depth against injection)
60
+ if [[ ! "$INC_ID" =~ ^[0-9]{4}[A-Za-z0-9_-]*$ ]]; then
61
+ exit 0
62
+ fi
63
+
59
64
  SPEC_PATH="$PROJECT_ROOT/.specweave/increments/$INC_ID/spec.md"
60
65
  METADATA_PATH="$PROJECT_ROOT/.specweave/increments/$INC_ID/metadata.json"
61
66
  CONFIG_PATH="$PROJECT_ROOT/.specweave/config.json"
@@ -114,7 +119,7 @@ for i in {1..10}; do
114
119
 
115
120
  # Check for stale lock
116
121
  if [[ -d "$LOCK_FILE" ]]; then
117
- LOCK_AGE=$(($(date +%s) - $(stat -f "%m" "$LOCK_FILE" 2>/dev/null || echo 0)))
122
+ LOCK_AGE=$(($(date +%s) - $(stat -f%m "$LOCK_FILE" 2>/dev/null || stat -c%Y "$LOCK_FILE" 2>/dev/null || echo 0)))
118
123
  if (( LOCK_AGE > LOCK_TIMEOUT )); then
119
124
  rmdir "$LOCK_FILE" 2>/dev/null || true
120
125
  continue
@@ -136,7 +141,7 @@ fi
136
141
  SIGNAL_FILE="$STATE_DIR/.ac-sync-pending-$INC_ID"
137
142
 
138
143
  if [[ -f "$SIGNAL_FILE" ]]; then
139
- SIGNAL_AGE=$(($(date +%s) - $(stat -f "%m" "$SIGNAL_FILE" 2>/dev/null || echo 0)))
144
+ SIGNAL_AGE=$(($(date +%s) - $(stat -f%m "$SIGNAL_FILE" 2>/dev/null || stat -c%Y "$SIGNAL_FILE" 2>/dev/null || echo 0)))
140
145
  if (( SIGNAL_AGE < 5 )); then
141
146
  log "Debounce: signal file age ${SIGNAL_AGE}s < 5s. Deferring."
142
147
  exit 0
@@ -191,6 +196,12 @@ if [[ "$AFFECTED_US_IDS" == "[]" || -z "$AFFECTED_US_IDS" ]]; then
191
196
  exit 0
192
197
  fi
193
198
 
199
+ # Validate AFFECTED_US_IDS is valid JSON (defense-in-depth against injection)
200
+ if ! echo "$AFFECTED_US_IDS" | jq empty 2>/dev/null; then
201
+ log "Invalid US IDs JSON. Skipping."
202
+ exit 0
203
+ fi
204
+
194
205
  # ============================================================================
195
206
  # BUILD CONFIG FROM SPECWEAVE CONFIG + METADATA
196
207
  # ============================================================================
@@ -246,8 +257,7 @@ try {
246
257
  console.error('AC sync dispatcher error:', err.message || err);
247
258
  process.exit(1);
248
259
  }
249
- " 2>&1) || true
250
-
260
+ " 2>&1)
251
261
  EXIT_CODE=$?
252
262
 
253
263
  if [[ $EXIT_CODE -ne 0 ]]; then