agileflow 3.2.1 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +6 -6
  3. package/lib/feature-flags.js +32 -4
  4. package/lib/skill-loader.js +0 -1
  5. package/package.json +1 -1
  6. package/scripts/agileflow-statusline.sh +81 -0
  7. package/scripts/babysit-clear-restore.js +154 -0
  8. package/scripts/claude-tmux.sh +120 -24
  9. package/scripts/claude-watchdog.sh +225 -0
  10. package/scripts/generators/agent-registry.js +14 -1
  11. package/scripts/generators/inject-babysit.js +22 -9
  12. package/scripts/generators/inject-help.js +19 -9
  13. package/scripts/lib/README-portable-tasks.md +424 -0
  14. package/scripts/lib/audit-cleanup.js +250 -0
  15. package/scripts/lib/audit-registry.js +248 -0
  16. package/scripts/lib/configure-detect.js +20 -0
  17. package/scripts/lib/feature-catalog.js +13 -2
  18. package/scripts/lib/gate-enforcer.js +295 -0
  19. package/scripts/lib/model-profiles.js +98 -0
  20. package/scripts/lib/signal-detectors.js +1 -1
  21. package/scripts/lib/skill-catalog.js +557 -0
  22. package/scripts/lib/skill-recommender.js +311 -0
  23. package/scripts/lib/tdd-phase-manager.js +455 -0
  24. package/scripts/lib/team-events.js +76 -8
  25. package/scripts/lib/tmux-group-colors.js +113 -0
  26. package/scripts/messaging-bridge.js +209 -1
  27. package/scripts/spawn-audit-sessions.js +549 -0
  28. package/scripts/team-manager.js +37 -16
  29. package/scripts/tmux-close-windows.sh +180 -0
  30. package/scripts/tmux-restore-window.sh +67 -0
  31. package/scripts/tmux-save-closed-window.sh +35 -0
  32. package/src/core/agents/ads-audit-budget.md +181 -0
  33. package/src/core/agents/ads-audit-compliance.md +169 -0
  34. package/src/core/agents/ads-audit-creative.md +164 -0
  35. package/src/core/agents/ads-audit-google.md +226 -0
  36. package/src/core/agents/ads-audit-meta.md +183 -0
  37. package/src/core/agents/ads-audit-tracking.md +197 -0
  38. package/src/core/agents/ads-consensus.md +322 -0
  39. package/src/core/agents/brainstorm-analyzer-features.md +169 -0
  40. package/src/core/agents/brainstorm-analyzer-growth.md +161 -0
  41. package/src/core/agents/brainstorm-analyzer-integration.md +172 -0
  42. package/src/core/agents/brainstorm-analyzer-market.md +147 -0
  43. package/src/core/agents/brainstorm-analyzer-ux.md +167 -0
  44. package/src/core/agents/brainstorm-consensus.md +237 -0
  45. package/src/core/agents/completeness-analyzer-api.md +190 -0
  46. package/src/core/agents/completeness-analyzer-conditional.md +201 -0
  47. package/src/core/agents/completeness-analyzer-handlers.md +159 -0
  48. package/src/core/agents/completeness-analyzer-imports.md +159 -0
  49. package/src/core/agents/completeness-analyzer-routes.md +182 -0
  50. package/src/core/agents/completeness-analyzer-state.md +188 -0
  51. package/src/core/agents/completeness-analyzer-stubs.md +198 -0
  52. package/src/core/agents/completeness-consensus.md +286 -0
  53. package/src/core/agents/perf-consensus.md +2 -2
  54. package/src/core/agents/security-consensus.md +2 -2
  55. package/src/core/agents/seo-analyzer-content.md +167 -0
  56. package/src/core/agents/seo-analyzer-images.md +187 -0
  57. package/src/core/agents/seo-analyzer-performance.md +206 -0
  58. package/src/core/agents/seo-analyzer-schema.md +176 -0
  59. package/src/core/agents/seo-analyzer-sitemap.md +172 -0
  60. package/src/core/agents/seo-analyzer-technical.md +144 -0
  61. package/src/core/agents/seo-consensus.md +289 -0
  62. package/src/core/agents/test-consensus.md +2 -2
  63. package/src/core/commands/ads/audit.md +375 -0
  64. package/src/core/commands/ads/budget.md +97 -0
  65. package/src/core/commands/ads/competitor.md +112 -0
  66. package/src/core/commands/ads/creative.md +85 -0
  67. package/src/core/commands/ads/google.md +112 -0
  68. package/src/core/commands/ads/landing.md +119 -0
  69. package/src/core/commands/ads/linkedin.md +112 -0
  70. package/src/core/commands/ads/meta.md +91 -0
  71. package/src/core/commands/ads/microsoft.md +115 -0
  72. package/src/core/commands/ads/plan.md +321 -0
  73. package/src/core/commands/ads/tiktok.md +129 -0
  74. package/src/core/commands/ads/youtube.md +124 -0
  75. package/src/core/commands/ads.md +128 -0
  76. package/src/core/commands/babysit.md +250 -1344
  77. package/src/core/commands/code/completeness.md +466 -0
  78. package/src/core/commands/{audit → code}/legal.md +26 -16
  79. package/src/core/commands/{audit → code}/logic.md +27 -16
  80. package/src/core/commands/{audit → code}/performance.md +30 -20
  81. package/src/core/commands/{audit → code}/security.md +32 -19
  82. package/src/core/commands/{audit → code}/test.md +30 -20
  83. package/src/core/commands/{discovery → ideate}/brief.md +12 -12
  84. package/src/core/commands/{discovery/new.md → ideate/discover.md} +13 -13
  85. package/src/core/commands/ideate/features.md +435 -0
  86. package/src/core/commands/seo/audit.md +373 -0
  87. package/src/core/commands/seo/competitor.md +174 -0
  88. package/src/core/commands/seo/content.md +107 -0
  89. package/src/core/commands/seo/geo.md +229 -0
  90. package/src/core/commands/seo/hreflang.md +140 -0
  91. package/src/core/commands/seo/images.md +96 -0
  92. package/src/core/commands/seo/page.md +198 -0
  93. package/src/core/commands/seo/plan.md +163 -0
  94. package/src/core/commands/seo/programmatic.md +131 -0
  95. package/src/core/commands/seo/references/cwv-thresholds.md +64 -0
  96. package/src/core/commands/seo/references/eeat-framework.md +110 -0
  97. package/src/core/commands/seo/references/quality-gates.md +91 -0
  98. package/src/core/commands/seo/references/schema-types.md +102 -0
  99. package/src/core/commands/seo/schema.md +183 -0
  100. package/src/core/commands/seo/sitemap.md +97 -0
  101. package/src/core/commands/seo/technical.md +100 -0
  102. package/src/core/commands/seo.md +107 -0
  103. package/src/core/commands/skill/list.md +68 -212
  104. package/src/core/commands/skill/recommend.md +216 -0
  105. package/src/core/commands/tdd-next.md +238 -0
  106. package/src/core/commands/tdd.md +210 -0
  107. package/src/core/experts/_core-expertise.yaml +105 -0
  108. package/src/core/experts/analytics/expertise.yaml +5 -99
  109. package/src/core/experts/codebase-query/expertise.yaml +3 -72
  110. package/src/core/experts/compliance/expertise.yaml +6 -72
  111. package/src/core/experts/database/expertise.yaml +9 -52
  112. package/src/core/experts/documentation/expertise.yaml +7 -140
  113. package/src/core/experts/integrations/expertise.yaml +7 -127
  114. package/src/core/experts/mentor/expertise.yaml +8 -35
  115. package/src/core/experts/monitoring/expertise.yaml +7 -49
  116. package/src/core/experts/performance/expertise.yaml +1 -26
  117. package/src/core/experts/security/expertise.yaml +9 -34
  118. package/src/core/experts/ui/expertise.yaml +6 -36
  119. package/src/core/knowledge/ads/ad-audit-checklist-scoring.md +424 -0
  120. package/src/core/knowledge/ads/ad-optimization-logic.md +590 -0
  121. package/src/core/knowledge/ads/ad-technical-specifications.md +385 -0
  122. package/src/core/knowledge/ads/definitive-advertising-reference-2026.md +506 -0
  123. package/src/core/knowledge/ads/paid-advertising-research-2026.md +445 -0
  124. package/src/core/templates/agileflow-metadata.json +15 -1
  125. package/tools/cli/installers/ide/_base-ide.js +42 -5
  126. package/tools/cli/installers/ide/claude-code.js +13 -4
  127. package/tools/cli/lib/content-injector.js +160 -12
  128. package/tools/cli/lib/docs-setup.js +1 -1
  129. package/src/core/commands/skill/create.md +0 -698
  130. package/src/core/commands/skill/delete.md +0 -316
  131. package/src/core/commands/skill/edit.md +0 -359
  132. package/src/core/commands/skill/test.md +0 -394
  133. package/src/core/commands/skill/upgrade.md +0 -552
  134. package/src/core/templates/skill-template.md +0 -117
package/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.4.0] - 2026-02-28
11
+
12
+ ### Added
13
+ - Ads audit system, SEO analysis, ultradeep concurrency controls, and domain-first command naming
14
+
15
+ ## [3.3.0] - 2026-02-24
16
+
17
+ ### Added
18
+ - Per-agent file tracking, tmux restore, and completeness audit
19
+
10
20
  ## [3.2.1] - 2026-02-21
11
21
 
12
22
  ### Added
package/README.md CHANGED
@@ -3,8 +3,8 @@
3
3
  </p>
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/agileflow?color=brightgreen)](https://www.npmjs.com/package/agileflow)
6
- [![Commands](https://img.shields.io/badge/commands-98-blue)](https://docs.agileflow.projectquestorg.com/docs/commands)
7
- [![Agents/Experts](https://img.shields.io/badge/agents%2Fexperts-83-orange)](https://docs.agileflow.projectquestorg.com/docs/agents)
6
+ [![Commands](https://img.shields.io/badge/commands-124-blue)](https://docs.agileflow.projectquestorg.com/docs/commands)
7
+ [![Agents/Experts](https://img.shields.io/badge/agents%2Fexperts-111-orange)](https://docs.agileflow.projectquestorg.com/docs/agents)
8
8
  [![Skills](https://img.shields.io/badge/skills-dynamic-purple)](https://docs.agileflow.projectquestorg.com/docs/features/skills)
9
9
 
10
10
  **AI-driven agile development for Claude Code, Cursor, Windsurf, OpenAI Codex, and more.** Combining Scrum, Kanban, ADRs, and docs-as-code principles into one framework-agnostic system.
@@ -54,9 +54,9 @@ Traditional project management tools create friction between planning and execut
54
54
 
55
55
  | Component | Count | Description |
56
56
  |-----------|-------|-------------|
57
- | [Commands](https://docs.agileflow.projectquestorg.com/docs/commands) | 98 | Slash commands for agile workflows |
58
- | [Agents/Experts](https://docs.agileflow.projectquestorg.com/docs/agents) | 83 | Specialized agents with self-improving knowledge bases |
59
- | [Skills](https://docs.agileflow.projectquestorg.com/docs/features/skills) | Dynamic | Generated on-demand with `/agileflow:skill:create` |
57
+ | [Commands](https://docs.agileflow.projectquestorg.com/docs/commands) | 124 | Slash commands for agile workflows |
58
+ | [Agents/Experts](https://docs.agileflow.projectquestorg.com/docs/agents) | 111 | Specialized agents with self-improving knowledge bases |
59
+ | [Skills](https://docs.agileflow.projectquestorg.com/docs/features/skills) | Dynamic | Browse and install from skills.sh marketplace via `/agileflow:skill:recommend` |
60
60
 
61
61
  ---
62
62
 
@@ -66,7 +66,7 @@ Traditional project management tools create friction between planning and execut
66
66
  |---------|-------------|------|
67
67
  | Agent Expertise | Self-improving agents that maintain domain knowledge | [Learn more](https://docs.agileflow.projectquestorg.com/docs/features/agent-expertise-system) |
68
68
  | Agent Teams | Multi-domain expert coordination with quality gates | [Learn more](https://docs.agileflow.projectquestorg.com/docs/features/agent-teams) |
69
- | Skills System | Custom AI prompts that learn from your feedback | [Learn more](https://docs.agileflow.projectquestorg.com/docs/features/skills) |
69
+ | Skills System | Browse and install skills from the skills.sh marketplace | [Learn more](https://docs.agileflow.projectquestorg.com/docs/features/skills) |
70
70
  | Parallel Sessions | Isolated workspaces with boundary protection | [Learn more](https://docs.agileflow.projectquestorg.com/docs/features/parallel-sessions) |
71
71
  | Loop Mode | Autonomous story execution until epic completion | [Learn more](https://docs.agileflow.projectquestorg.com/docs/features/loop-mode) |
72
72
  | AI Council | Three-perspective strategic decision analysis | [Learn more](https://docs.agileflow.projectquestorg.com/docs/commands/council) |
@@ -11,6 +11,12 @@
11
11
 
12
12
  const fs = require('fs');
13
13
 
14
+ /**
15
+ * Tools expected to be available when Agent Teams native mode is enabled.
16
+ * Used for feature detection and capability reporting.
17
+ */
18
+ const AGENT_TEAMS_TOOLS = Object.freeze(['TeamCreate', 'SendMessage', 'ListTeams']);
19
+
14
20
  // Lazy-load paths to avoid circular dependency issues
15
21
  let _paths;
16
22
  function getPaths() {
@@ -67,6 +73,24 @@ function getAgentTeamsMode(options = {}) {
67
73
  return 'native';
68
74
  }
69
75
 
76
+ /**
77
+ * Get the list of Agent Teams tools available in the current environment.
78
+ *
79
+ * When Agent Teams is enabled (native mode), returns the expected tool names.
80
+ * When disabled, returns an empty array.
81
+ *
82
+ * @param {object} [options] - Options
83
+ * @param {string} [options.rootDir] - Project root directory
84
+ * @param {object} [options.metadata] - Pre-loaded metadata
85
+ * @returns {string[]} Array of available tool names
86
+ */
87
+ function getAvailableTools(options = {}) {
88
+ if (!isAgentTeamsEnabled(options)) {
89
+ return [];
90
+ }
91
+ return [...AGENT_TEAMS_TOOLS];
92
+ }
93
+
70
94
  /**
71
95
  * Get all feature flags as an object.
72
96
  *
@@ -77,10 +101,12 @@ function getAgentTeamsMode(options = {}) {
77
101
  */
78
102
  function getFeatureFlags(options = {}) {
79
103
  const metadata = options.metadata || loadMetadataSafe(options.rootDir);
104
+ const opts = { ...options, metadata };
80
105
 
81
106
  return {
82
- agentTeams: isAgentTeamsEnabled({ ...options, metadata }),
83
- agentTeamsMode: getAgentTeamsMode({ ...options, metadata }),
107
+ agentTeams: isAgentTeamsEnabled(opts),
108
+ agentTeamsMode: getAgentTeamsMode(opts),
109
+ availableTools: getAvailableTools(opts),
84
110
  };
85
111
  }
86
112
 
@@ -93,12 +119,12 @@ function getFeatureFlags(options = {}) {
93
119
  */
94
120
  function getAgentTeamsDisplayInfo(options = {}) {
95
121
  const enabled = isAgentTeamsEnabled(options);
96
- const mode = getAgentTeamsMode(options);
97
122
 
98
123
  if (enabled) {
124
+ const tools = getAvailableTools(options);
99
125
  return {
100
126
  label: 'Agent Teams',
101
- value: 'ENABLED (native)',
127
+ value: `ENABLED (native, ${tools.length} tools)`,
102
128
  status: 'enabled',
103
129
  };
104
130
  }
@@ -136,8 +162,10 @@ function loadMetadataSafe(rootDir) {
136
162
  }
137
163
 
138
164
  module.exports = {
165
+ AGENT_TEAMS_TOOLS,
139
166
  isAgentTeamsEnabled,
140
167
  getAgentTeamsMode,
168
+ getAvailableTools,
141
169
  getFeatureFlags,
142
170
  getAgentTeamsDisplayInfo,
143
171
  };
@@ -126,7 +126,6 @@ function loadSkill(skillDir) {
126
126
  path: skillDir,
127
127
  hasReferences: fs.existsSync(path.join(skillDir, 'references.md')),
128
128
  hasCookbook: fs.existsSync(path.join(skillDir, 'cookbook')),
129
- hasMcp: fs.existsSync(path.join(skillDir, '.mcp.json')),
130
129
  metadata,
131
130
  };
132
131
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agileflow",
3
- "version": "3.2.1",
3
+ "version": "3.4.0",
4
4
  "description": "AI-driven agile development system for Claude Code, Cursor, Windsurf, and more",
5
5
  "keywords": [
6
6
  "agile",
@@ -88,6 +88,7 @@ SHOW_SESSION_TIME=false
88
88
  SHOW_COST=false
89
89
  SHOW_GIT=true
90
90
  SHOW_SCALE=true
91
+ SHOW_ULTRADEEP=true
91
92
 
92
93
  # Check agileflow-metadata.json for component settings
93
94
  if [ -f "docs/00-meta/agileflow-metadata.json" ]; then
@@ -106,6 +107,7 @@ if [ -f "docs/00-meta/agileflow-metadata.json" ]; then
106
107
  SHOW_COST=$(echo "$COMPONENTS" | jq -r '.cost | if . == null then true else . end')
107
108
  SHOW_GIT=$(echo "$COMPONENTS" | jq -r '.git | if . == null then true else . end')
108
109
  SHOW_SCALE=$(echo "$COMPONENTS" | jq -r '.scale | if . == null then true else . end')
110
+ SHOW_ULTRADEEP=$(echo "$COMPONENTS" | jq -r '.ultradeep | if . == null then true else . end')
109
111
  fi
110
112
  fi
111
113
 
@@ -669,6 +671,79 @@ if [ "$SHOW_SCALE" = "true" ] && [ -f "docs/09-agents/session-state.json" ]; the
669
671
  fi
670
672
  fi
671
673
 
674
+ # ============================================================================
675
+ # Ultradeep Audit Progress Detection
676
+ # ============================================================================
677
+ ULTRADEEP_DISPLAY=""
678
+ if [ "$SHOW_ULTRADEEP" = "true" ]; then
679
+ ULTRADEEP_DIR="docs/09-agents/ultradeep"
680
+ if [ -d "$ULTRADEEP_DIR" ]; then
681
+ # Find most recent trace directory by modification time
682
+ LATEST_TRACE=$(ls -td "$ULTRADEEP_DIR"/*/ 2>/dev/null | head -1)
683
+ if [ -n "$LATEST_TRACE" ]; then
684
+ ULTRA_STATUS_FILE="${LATEST_TRACE}_status.json"
685
+ if [ -f "$ULTRA_STATUS_FILE" ]; then
686
+ ULTRA_STATUS=$(cat "$ULTRA_STATUS_FILE" 2>/dev/null)
687
+ ULTRA_AUDIT_TYPE=$(echo "$ULTRA_STATUS" | jq -r '.audit_type // empty' 2>/dev/null)
688
+ ULTRA_TOTAL=$(echo "$ULTRA_STATUS" | jq -r '.analyzers | length' 2>/dev/null)
689
+ ULTRA_COMPLETED=$(echo "$ULTRA_STATUS" | jq -r '.completed | length' 2>/dev/null)
690
+ ULTRA_STARTED=$(echo "$ULTRA_STATUS" | jq -r '.started_at // empty' 2>/dev/null)
691
+ ULTRA_LAST_CHECKED=$(echo "$ULTRA_STATUS" | jq -r '.last_checked // empty' 2>/dev/null)
692
+
693
+ # Validate we got numeric values
694
+ [[ "$ULTRA_TOTAL" =~ ^[0-9]+$ ]] || ULTRA_TOTAL=0
695
+ [[ "$ULTRA_COMPLETED" =~ ^[0-9]+$ ]] || ULTRA_COMPLETED=0
696
+
697
+ if [ "$ULTRA_TOTAL" -gt 0 ] && [ -n "$ULTRA_AUDIT_TYPE" ]; then
698
+ # Staleness check: skip if started >60 min ago and no recent activity
699
+ ULTRA_STALE=false
700
+ if [ -n "$ULTRA_STARTED" ]; then
701
+ ULTRA_START_EPOCH=$(to_epoch "$ULTRA_STARTED" 2>/dev/null)
702
+ ULTRA_NOW=$(date +%s)
703
+ if [ -n "$ULTRA_START_EPOCH" ] && [ $((ULTRA_NOW - ULTRA_START_EPOCH)) -gt 3600 ]; then
704
+ # Started over 60 min ago - check last_checked
705
+ if [ -n "$ULTRA_LAST_CHECKED" ] && [ "$ULTRA_LAST_CHECKED" != "null" ]; then
706
+ ULTRA_LC_EPOCH=$(to_epoch "$ULTRA_LAST_CHECKED" 2>/dev/null)
707
+ if [ -n "$ULTRA_LC_EPOCH" ] && [ $((ULTRA_NOW - ULTRA_LC_EPOCH)) -gt 120 ]; then
708
+ ULTRA_STALE=true
709
+ fi
710
+ else
711
+ # No last_checked at all and started >60 min ago
712
+ ULTRA_STALE=true
713
+ fi
714
+ fi
715
+ fi
716
+
717
+ if [ "$ULTRA_STALE" = "false" ]; then
718
+ # Map audit type to color and prefix
719
+ case "$ULTRA_AUDIT_TYPE" in
720
+ logic) ULTRA_COLOR="\033[38;2;122;162;247m"; ULTRA_PREFIX="Logic" ;;
721
+ security) ULTRA_COLOR="\033[38;2;247;118;142m"; ULTRA_PREFIX="Sec" ;;
722
+ performance) ULTRA_COLOR="\033[38;2;115;218;202m"; ULTRA_PREFIX="Perf" ;;
723
+ test) ULTRA_COLOR="\033[38;2;224;175;104m"; ULTRA_PREFIX="Test" ;;
724
+ completeness) ULTRA_COLOR="\033[38;2;187;154;247m"; ULTRA_PREFIX="Comp" ;;
725
+ legal) ULTRA_COLOR="\033[38;2;158;206;106m"; ULTRA_PREFIX="Legal" ;;
726
+ *) ULTRA_COLOR="$DIM"; ULTRA_PREFIX="Audit" ;;
727
+ esac
728
+
729
+ # Build display with progress coloring
730
+ if [ "$ULTRA_COMPLETED" -eq "$ULTRA_TOTAL" ]; then
731
+ # All complete - green checkmark
732
+ ULTRADEEP_DISPLAY="${ULTRA_COLOR}${ULTRA_PREFIX}${RESET} ${GREEN}${ULTRA_COMPLETED}/${ULTRA_TOTAL}✓${RESET}"
733
+ elif [ "$ULTRA_COMPLETED" -gt 0 ]; then
734
+ # In progress
735
+ ULTRADEEP_DISPLAY="${ULTRA_COLOR}${ULTRA_PREFIX}${RESET} ${ULTRA_COLOR}${ULTRA_COMPLETED}/${ULTRA_TOTAL}${RESET}"
736
+ else
737
+ # Just started (0 complete) - dim the fraction
738
+ ULTRADEEP_DISPLAY="${ULTRA_COLOR}${ULTRA_PREFIX}${RESET} ${DIM}${ULTRA_COMPLETED}/${ULTRA_TOTAL}${RESET}"
739
+ fi
740
+ fi
741
+ fi
742
+ fi
743
+ fi
744
+ fi
745
+ fi
746
+
672
747
  # ============================================================================
673
748
  # Build Status Line
674
749
  # ============================================================================
@@ -759,6 +834,12 @@ if [ "$SHOW_SCALE" = "true" ] && [ -n "$SCALE_DISPLAY" ]; then
759
834
  OUTPUT="${OUTPUT}${SCALE_DISPLAY}"
760
835
  fi
761
836
 
837
+ # Add ultradeep audit progress (if enabled and active)
838
+ if [ "$SHOW_ULTRADEEP" = "true" ] && [ -n "$ULTRADEEP_DISPLAY" ]; then
839
+ [ -n "$OUTPUT" ] && OUTPUT="${OUTPUT}${SEP}"
840
+ OUTPUT="${OUTPUT}${ULTRADEEP_DISPLAY}"
841
+ fi
842
+
762
843
  # Session health indicator (next to git branch)
763
844
  if [ "$SHOW_SESSION" = "true" ]; then
764
845
  SCRIPTS_DIR="$(dirname "$0")"
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * babysit-clear-restore.js - SessionStart hook for babysit context preservation
4
+ *
5
+ * When context is cleared (e.g., after plan approval), this hook:
6
+ * 1. Detects source="clear" from Claude Code's SessionStart event
7
+ * 2. Checks if /babysit is active in session-state.json
8
+ * 3. If both, outputs the COMPACT_SUMMARY from the babysit command file
9
+ * 4. Sets last_precompact_at so the welcome script preserves active_commands
10
+ *
11
+ * This eliminates the need to manually embed babysit rules in plan files (Rule #6).
12
+ * The hook automatically injects the rules into the fresh context after clear.
13
+ *
14
+ * Exit codes:
15
+ * 0 = Success (always - SessionStart hooks should never block)
16
+ *
17
+ * Input: JSON on stdin with { source: "startup"|"resume"|"clear"|"compact", ... }
18
+ * Output: Babysit compact summary to stdout (appears as system-reminder)
19
+ */
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+
24
+ const STDIN_TIMEOUT_MS = 3000;
25
+
26
+ function findProjectRoot() {
27
+ let dir = process.cwd();
28
+ while (dir !== '/') {
29
+ if (fs.existsSync(path.join(dir, '.agileflow'))) return dir;
30
+ if (fs.existsSync(path.join(dir, 'docs', '09-agents'))) return dir;
31
+ dir = path.dirname(dir);
32
+ }
33
+ return process.cwd();
34
+ }
35
+
36
+ const ROOT = findProjectRoot();
37
+
38
+ function getSessionState() {
39
+ const statePath = path.join(ROOT, 'docs', '09-agents', 'session-state.json');
40
+ try {
41
+ if (fs.existsSync(statePath)) {
42
+ return JSON.parse(fs.readFileSync(statePath, 'utf8'));
43
+ }
44
+ } catch (e) {
45
+ // Silently fail
46
+ }
47
+ return null;
48
+ }
49
+
50
+ function isBabysitActive(state) {
51
+ if (!state) return false;
52
+ const activeCommands = state.active_commands || [];
53
+ return activeCommands.some(cmd => cmd.name === 'babysit');
54
+ }
55
+
56
+ function setPrecompactTimestamp(state) {
57
+ const statePath = path.join(ROOT, 'docs', '09-agents', 'session-state.json');
58
+ try {
59
+ state.last_precompact_at = new Date().toISOString();
60
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + '\n');
61
+ } catch (e) {
62
+ // Silently fail
63
+ }
64
+ }
65
+
66
+ function getBabysitCompactSummary() {
67
+ // Search for the babysit command file in known locations
68
+ const locations = [
69
+ path.join(ROOT, 'packages', 'cli', 'src', 'core', 'commands', 'babysit.md'),
70
+ path.join(ROOT, '.agileflow', 'commands', 'babysit.md'),
71
+ path.join(ROOT, '.claude', 'commands', 'agileflow', 'babysit.md'),
72
+ ];
73
+
74
+ for (const filePath of locations) {
75
+ try {
76
+ if (!fs.existsSync(filePath)) continue;
77
+ const content = fs.readFileSync(filePath, 'utf8');
78
+ const match = content.match(
79
+ /<!-- COMPACT_SUMMARY_START[\s\S]*?-->([\s\S]*?)<!-- COMPACT_SUMMARY_END -->/
80
+ );
81
+ if (match) {
82
+ return match[1].trim();
83
+ }
84
+ } catch (e) {
85
+ // Try next location
86
+ }
87
+ }
88
+ return null;
89
+ }
90
+
91
+ // Read stdin for hook event data
92
+ let inputData = '';
93
+
94
+ process.stdin.setEncoding('utf8');
95
+
96
+ process.stdin.on('data', chunk => {
97
+ inputData += chunk;
98
+ });
99
+
100
+ process.stdin.on('end', () => {
101
+ try {
102
+ const input = JSON.parse(inputData);
103
+ const source = input.source;
104
+
105
+ // Only act on "clear" events (context cleared after plan approval, /clear, etc.)
106
+ if (source !== 'clear') {
107
+ process.exit(0);
108
+ return;
109
+ }
110
+
111
+ const state = getSessionState();
112
+ if (!isBabysitActive(state)) {
113
+ process.exit(0);
114
+ return;
115
+ }
116
+
117
+ // Set last_precompact_at so the welcome script preserves active_commands
118
+ // instead of clearing them (it checks this timestamp)
119
+ setPrecompactTimestamp(state);
120
+
121
+ // Output the babysit compact summary
122
+ const summary = getBabysitCompactSummary();
123
+ if (summary) {
124
+ console.log('## ACTIVE COMMAND: /agileflow:babysit (restored after context clear)');
125
+ console.log('');
126
+ console.log(summary);
127
+ } else {
128
+ // Fallback: output minimal babysit rules if command file not found
129
+ console.log('## /agileflow:babysit IS ACTIVE (restored after context clear)');
130
+ console.log('');
131
+ console.log('MANDATORY RULES:');
132
+ console.log('1. ALWAYS end responses with AskUserQuestion tool (specific options, not text)');
133
+ console.log('2. Use EnterPlanMode for non-trivial tasks');
134
+ console.log('3. Delegate complex work to domain experts via Task tool');
135
+ console.log('4. Track progress with TaskCreate/TaskUpdate for multi-step work');
136
+ console.log(
137
+ '5. ALWAYS suggest logic audit post-implementation (after tests pass, make Recommended)'
138
+ );
139
+ }
140
+ } catch (e) {
141
+ // Parse failed or other error - fail open
142
+ }
143
+ process.exit(0);
144
+ });
145
+
146
+ // Handle no stdin (direct invocation or timeout)
147
+ process.stdin.on('error', () => {
148
+ process.exit(0);
149
+ });
150
+
151
+ // Timeout safety - don't hang
152
+ setTimeout(() => {
153
+ process.exit(0);
154
+ }, STDIN_TIMEOUT_MS);
@@ -112,6 +112,8 @@ WINDOWS:
112
112
  Alt+n/p Next/previous window
113
113
  Alt+r Rename window
114
114
  Alt+w Close window
115
+ Alt+W Batch close windows (picker)
116
+ Alt+t Reopen closed window
115
117
 
116
118
  PANES:
117
119
  Alt+d Split side by side
@@ -120,9 +122,14 @@ PANES:
120
122
  Alt+z Zoom/unzoom pane
121
123
  Alt+x Close pane
122
124
 
125
+ FREEZE RECOVERY:
126
+ Alt+k Send Ctrl+C twice (soft unfreeze)
127
+ Alt+K Force kill Claude process (keeps tab)
128
+ Alt+R Respawn pane (fresh shell)
129
+
123
130
  OTHER:
124
- Alt+[ Scroll mode
125
- Alt+k Send Ctrl+C twice (unfreeze)
131
+ Alt+b Scroll mode (browse history)
132
+ Alt+h Show keybind help panel
126
133
  EOF
127
134
  exit 0
128
135
  fi
@@ -185,19 +192,24 @@ build_tab_format() {
185
192
  # ── Inactive tab: gray text ─────────────────────────────────────────────
186
193
  # " I:" prefix = 4 visible chars (wide); " I:" = 3 chars (narrow)
187
194
  # Width = prefix + pN(name) + 1(space)
188
- local i0='#[fg=#8a8a8a] #I:#{p35:#{=35:window_name}} '
189
- local i1='#[fg=#8a8a8a] #I:#{p22:#{=22:window_name}} '
190
- local i2='#[fg=#8a8a8a] #I:#{p15:#{=15:window_name}} '
191
- local i3='#[fg=#8a8a8a] #I:#{p12:#{=12:window_name}} '
192
- local i4='#[fg=#8a8a8a] #I:#{p9:#{=9:window_name}} '
193
- local i5='#[fg=#8a8a8a] #I:#{p7:#{=7:window_name}} '
194
- local i6='#[fg=#8a8a8a] #I:#{p6:#{=6:window_name}} '
195
- local i7='#[fg=#8a8a8a] #I:#{p5:#{=5:window_name}} '
196
- local i8='#[fg=#8a8a8a] #I:#{p4:#{=4:window_name}} '
197
- local i9='#[fg=#8a8a8a] #I:#{p3:#{=3:window_name}} '
198
- local i10='#[fg=#8a8a8a] #I:#{p2:#{=2:window_name}} '
199
- local i11='#[fg=#8a8a8a] #I:#{p1:#{=1:window_name}} '
200
- local i12='#[fg=#8a8a8a] #I '
195
+ #
196
+ # Group color support: when @group_color is set on a window (e.g. by
197
+ # spawn-audit-sessions.js), show a colored dot prefix to visually
198
+ # identify grouped windows (ULTRADEEP audit tabs in main session).
199
+ local gc='#{?#{@group_color},#[fg=#{@group_color}]•,}'
200
+ local i0="${gc}"'#[fg=#8a8a8a] #I:#{p35:#{=35:window_name}} '
201
+ local i1="${gc}"'#[fg=#8a8a8a] #I:#{p22:#{=22:window_name}} '
202
+ local i2="${gc}"'#[fg=#8a8a8a] #I:#{p15:#{=15:window_name}} '
203
+ local i3="${gc}"'#[fg=#8a8a8a] #I:#{p12:#{=12:window_name}} '
204
+ local i4="${gc}"'#[fg=#8a8a8a] #I:#{p9:#{=9:window_name}} '
205
+ local i5="${gc}"'#[fg=#8a8a8a] #I:#{p7:#{=7:window_name}} '
206
+ local i6="${gc}"'#[fg=#8a8a8a] #I:#{p6:#{=6:window_name}} '
207
+ local i7="${gc}"'#[fg=#8a8a8a] #I:#{p5:#{=5:window_name}} '
208
+ local i8="${gc}"'#[fg=#8a8a8a] #I:#{p4:#{=4:window_name}} '
209
+ local i9="${gc}"'#[fg=#8a8a8a] #I:#{p3:#{=3:window_name}} '
210
+ local i10="${gc}"'#[fg=#8a8a8a] #I:#{p2:#{=2:window_name}} '
211
+ local i11="${gc}"'#[fg=#8a8a8a] #I:#{p1:#{=1:window_name}} '
212
+ local i12="${gc}"'#[fg=#8a8a8a] #I '
201
213
  local i13='#[fg=#565a6e]#I'
202
214
 
203
215
  # ── Tier selection: budget = width / windows ─────────────────────────────
@@ -240,6 +252,14 @@ configure_tmux_session() {
240
252
  # Enable mouse support
241
253
  tmux set-option -t "$target_session" mouse on
242
254
 
255
+ # Reduce escape-time from default 500ms to 10ms.
256
+ # The default causes two problems:
257
+ # 1) Arrow keys (which send \e[A etc.) get misinterpreted as Alt+[ if
258
+ # there's any delivery delay, accidentally triggering copy-mode.
259
+ # 2) Pressing Escape in Claude Code has a 500ms lag before tmux forwards it.
260
+ # 10ms is enough to detect genuine escape sequences on localhost/fast SSH.
261
+ tmux set-option -t "$target_session" escape-time 10
262
+
243
263
  # Automatically renumber windows when one is closed (no gaps)
244
264
  tmux set-option -t "$target_session" renumber-windows on
245
265
 
@@ -308,8 +328,14 @@ configure_tmux_session() {
308
328
  # Alt+x to close current pane (with confirmation)
309
329
  tmux bind-key -n M-x confirm-before -p "Close pane? (y/n)" kill-pane
310
330
 
311
- # Alt+w to close current window (with confirmation)
312
- tmux bind-key -n M-w confirm-before -p "Close window? (y/n)" kill-window
331
+ # Alt+w to close current window (save state for Alt+T restore, then kill)
332
+ tmux bind-key -n M-w confirm-before -p "Close window? (y/n)" "run-shell '\"\$AGILEFLOW_SCRIPTS/tmux-save-closed-window.sh\"' ; kill-window"
333
+
334
+ # Alt+W (uppercase) to batch-close windows via multi-select picker
335
+ tmux bind-key -n M-W display-popup -E -w 60 -h 20 "\"$AGILEFLOW_SCRIPTS/tmux-close-windows.sh\""
336
+
337
+ # Alt+t to restore the most recently closed window (like Ctrl+Shift+T in browsers)
338
+ tmux bind-key -n M-t run-shell '"$AGILEFLOW_SCRIPTS/tmux-restore-window.sh"'
313
339
 
314
340
  # Alt+n/p for next/previous window
315
341
  tmux bind-key -n M-n next-window
@@ -321,8 +347,39 @@ configure_tmux_session() {
321
347
  # Alt+z to zoom/unzoom pane (fullscreen toggle)
322
348
  tmux bind-key -n M-z resize-pane -Z
323
349
 
324
- # Alt+[ to enter copy mode (for scrolling)
325
- tmux bind-key -n M-[ copy-mode
350
+ # Alt+b to enter copy mode (for scrolling / browsing history)
351
+ # NOTE: Do NOT use Alt+[ here — \e[ is the CSI prefix for arrow keys and
352
+ # function keys. Binding M-[ in the root table causes accidental copy-mode
353
+ # entry whenever an escape sequence is split by network latency, making the
354
+ # terminal appear to "lose focus" until Escape is pressed.
355
+ tmux bind-key -n M-b copy-mode
356
+
357
+ # Disable ALL command-prompt bindings in copy mode — they open a text input
358
+ # in the status bar which intercepts keystrokes and confuses the workflow.
359
+ # Navigation (arrows, scroll, mouse, PgUp/PgDn) and exit (q/Escape) still work.
360
+ #
361
+ # Emacs copy-mode: goto-line, search, jump-to-char, repeat-count
362
+ tmux unbind-key -T copy-mode g
363
+ tmux unbind-key -T copy-mode C-r
364
+ tmux unbind-key -T copy-mode C-s
365
+ tmux unbind-key -T copy-mode f
366
+ tmux unbind-key -T copy-mode F
367
+ tmux unbind-key -T copy-mode t
368
+ tmux unbind-key -T copy-mode T
369
+ for i in 1 2 3 4 5 6 7 8 9; do
370
+ tmux unbind-key -T copy-mode "M-$i"
371
+ done
372
+ # Vi copy-mode: goto-line, search, jump-to-char, repeat-count
373
+ tmux unbind-key -T copy-mode-vi :
374
+ tmux unbind-key -T copy-mode-vi /
375
+ tmux unbind-key -T copy-mode-vi ?
376
+ tmux unbind-key -T copy-mode-vi f
377
+ tmux unbind-key -T copy-mode-vi F
378
+ tmux unbind-key -T copy-mode-vi t
379
+ tmux unbind-key -T copy-mode-vi T
380
+ for i in 1 2 3 4 5 6 7 8 9; do
381
+ tmux unbind-key -T copy-mode-vi "$i"
382
+ done
326
383
 
327
384
  # ─── Session Creation Keybindings ──────────────────────────────────────────
328
385
  # Alt+s to create a new Claude window (starts fresh, future re-runs in same pane resume)
@@ -331,11 +388,31 @@ configure_tmux_session() {
331
388
 
332
389
  # ─── Freeze Recovery Keybindings ───────────────────────────────────────────
333
390
  # Alt+k to send Ctrl+C twice (soft interrupt for frozen processes)
334
- tmux bind-key -n M-k run-shell "tmux send-keys C-c; sleep 0.5; tmux send-keys C-c"
391
+ # Uses #{pane_id} to target the correct pane even if focus shifts during the sleep
392
+ tmux bind-key -n M-k run-shell "tmux send-keys -t '#{pane_id}' C-c; sleep 0.5; tmux send-keys -t '#{pane_id}' C-c"
393
+
394
+ # Alt+K (uppercase) to force kill Claude process in pane without closing tab
395
+ # Finds the claude child process under the pane's shell and kills it,
396
+ # leaving the shell prompt intact so the user can restart or continue.
397
+ # Uses ps -o pid=,args= with grep to find claude, then cuts the PID (avoids awk quoting issues in tmux run-shell).
398
+ tmux bind-key -n M-K run-shell '\
399
+ PANE_PID=$(tmux display-message -p "#{pane_pid}"); \
400
+ CLAUDE_PID=$(ps --ppid "$PANE_PID" -o pid=,args= 2>/dev/null | grep -v "watchdog" | grep "claude" | head -1 | sed "s/^[[:space:]]*//" | cut -d" " -f1); \
401
+ if [ -n "$CLAUDE_PID" ]; then \
402
+ kill -TERM "$CLAUDE_PID" 2>/dev/null; \
403
+ sleep 2; \
404
+ kill -0 "$CLAUDE_PID" 2>/dev/null && kill -KILL "$CLAUDE_PID" 2>/dev/null; \
405
+ tmux display-message "Killed Claude process (PID $CLAUDE_PID)"; \
406
+ else \
407
+ tmux display-message "No Claude process found in this pane"; \
408
+ fi'
409
+
410
+ # Alt+R to respawn pane (kills everything in the pane, starts fresh shell)
411
+ tmux bind-key -n M-R respawn-pane -k
335
412
 
336
413
  # ─── Help Panel ──────────────────────────────────────────────────────────
337
414
  # Alt+h to show all Alt keybindings in a popup
338
- tmux bind-key -n M-h display-popup -E -w 52 -h 30 "\
415
+ tmux bind-key -n M-h display-popup -E -w 52 -h 32 "\
339
416
  printf '\\n';\
340
417
  printf ' \\033[1;38;5;208mSESSIONS\\033[0m\\n';\
341
418
  printf ' Alt+s New Claude session\\n';\
@@ -348,6 +425,8 @@ configure_tmux_session() {
348
425
  printf ' Alt+n/p Next / previous window\\n';\
349
426
  printf ' Alt+r Rename window\\n';\
350
427
  printf ' Alt+w Close window\\n';\
428
+ printf ' Alt+W Batch close windows\\n';\
429
+ printf ' Alt+t Reopen closed window\\n';\
351
430
  printf '\\n';\
352
431
  printf ' \\033[1;38;5;208mPANES\\033[0m\\n';\
353
432
  printf ' Alt+d Split side by side\\n';\
@@ -355,11 +434,11 @@ configure_tmux_session() {
355
434
  printf ' Alt+arrows Navigate panes\\n';\
356
435
  printf ' Alt+z Zoom / unzoom\\n';\
357
436
  printf ' Alt+x Close pane (confirm)\\n';\
358
- printf ' Alt+K Kill pane (no confirm)\\n';\
359
- printf ' Alt+R Restart pane\\n';\
437
+ printf ' Alt+K Force kill process\\n';\
438
+ printf ' Alt+R Respawn pane (fresh)\\n';\
360
439
  printf '\\n';\
361
440
  printf ' \\033[1;38;5;208mOTHER\\033[0m\\n';\
362
- printf ' Alt+[ Scroll mode\\n';\
441
+ printf ' Alt+b Scroll mode\\n';\
363
442
  printf ' Alt+k Unfreeze (Ctrl+C x2)\\n';\
364
443
  printf ' Alt+h This help\\n';\
365
444
  printf '\\n';\
@@ -373,6 +452,14 @@ if [ "$REFRESH_CONFIG" = true ]; then
373
452
  configure_tmux_session "$sid"
374
453
  # Ensure AGILEFLOW_SCRIPTS is set (needed by Alt+S keybind)
375
454
  tmux set-environment -t "$sid" AGILEFLOW_SCRIPTS "$SCRIPT_DIR" 2>/dev/null || true
455
+ # (Re)start watchdog if not running for this session
456
+ _EXISTING_WD=$(tmux show-environment -t "$sid" WATCHDOG_PID 2>/dev/null | cut -d= -f2)
457
+ if [ -z "$_EXISTING_WD" ] || ! kill -0 "$_EXISTING_WD" 2>/dev/null; then
458
+ "$SCRIPT_DIR/claude-watchdog.sh" "$sid" &
459
+ _WD_PID=$!
460
+ tmux set-environment -t "$sid" WATCHDOG_PID "$_WD_PID"
461
+ disown "$_WD_PID"
462
+ fi
376
463
  REFRESHED=$((REFRESHED + 1))
377
464
  done
378
465
  if [ "$REFRESHED" -gt 0 ]; then
@@ -645,6 +732,15 @@ if [ -n "$CLAUDE_SESSION_FLAGS" ]; then
645
732
  tmux set-environment -t "$SESSION_NAME" CLAUDE_SESSION_FLAGS "$CLAUDE_SESSION_FLAGS"
646
733
  fi
647
734
 
735
+ # Start watchdog to auto-detect and kill frozen Claude processes
736
+ EXISTING_WD=$(tmux show-environment -t "$SESSION_NAME" WATCHDOG_PID 2>/dev/null | cut -d= -f2)
737
+ if [ -z "$EXISTING_WD" ] || ! kill -0 "$EXISTING_WD" 2>/dev/null; then
738
+ "$SCRIPT_DIR/claude-watchdog.sh" "$SESSION_NAME" &
739
+ _WD_PID=$!
740
+ tmux set-environment -t "$SESSION_NAME" WATCHDOG_PID "$_WD_PID"
741
+ disown "$_WD_PID"
742
+ fi
743
+
648
744
  # Pre-seed @claude_uuid on initial pane if we found a recent conversation
649
745
  if [ "$USE_RESUME" = true ] && [ -n "$RESUME_SESSION_ID" ]; then
650
746
  tmux set-option -p -t "$SESSION_NAME" @claude_uuid "$RESUME_SESSION_ID" 2>/dev/null || true