vibe-forge 0.4.0 → 0.8.2

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 (129) hide show
  1. package/.claude/commands/clear-attention.md +63 -63
  2. package/.claude/commands/compact-context.md +52 -0
  3. package/.claude/commands/configure-vcs.md +5 -5
  4. package/.claude/commands/forge.md +50 -3
  5. package/.claude/commands/need-help.md +77 -77
  6. package/.claude/commands/update-status.md +64 -64
  7. package/.claude/commands/worker-loop.md +106 -106
  8. package/.claude/hooks/worker-loop.js +37 -4
  9. package/.claude/scripts/setup-worker-loop.sh +45 -45
  10. package/.claude/settings.json +89 -0
  11. package/LICENSE +21 -21
  12. package/README.md +211 -232
  13. package/agents/aegis/personality.md +35 -1
  14. package/agents/anvil/personality.md +39 -1
  15. package/agents/architect/personality.md +26 -0
  16. package/agents/crucible/personality.md +54 -1
  17. package/agents/crucible-x/personality.md +210 -0
  18. package/agents/ember/personality.md +29 -1
  19. package/agents/flux/personality.md +248 -0
  20. package/agents/furnace/personality.md +52 -1
  21. package/agents/herald/personality.md +3 -1
  22. package/agents/loki/personality.md +108 -0
  23. package/agents/oracle/personality.md +284 -0
  24. package/agents/pixel/personality.md +140 -0
  25. package/agents/planning-hub/personality.md +222 -0
  26. package/agents/scribe/personality.md +3 -1
  27. package/agents/slag/personality.md +268 -0
  28. package/agents/{sentinel → temper}/personality.md +85 -9
  29. package/bin/cli.js +77 -30
  30. package/bin/dashboard/api/agents.js +333 -0
  31. package/bin/dashboard/api/dispatch.js +507 -0
  32. package/bin/dashboard/api/tasks.js +416 -0
  33. package/bin/dashboard/public/assets/index-BpHfsx1r.js +2 -0
  34. package/bin/dashboard/public/assets/index-QODv4Zn9.css +1 -0
  35. package/bin/dashboard/public/index.html +14 -0
  36. package/bin/dashboard/server.js +645 -0
  37. package/bin/forge-daemon.sh +176 -550
  38. package/bin/forge-setup.sh +28 -11
  39. package/bin/forge-spawn.sh +5 -5
  40. package/bin/forge.cmd +83 -83
  41. package/bin/forge.sh +210 -31
  42. package/config/agent-manifest.yaml +237 -243
  43. package/config/agents.json +207 -132
  44. package/config/task-types.yaml +111 -106
  45. package/context/agent-overrides/README.md +41 -0
  46. package/context/architecture.md +42 -0
  47. package/context/modern-conventions.md +129 -129
  48. package/docs/agents.md +473 -409
  49. package/docs/architecture.md +194 -162
  50. package/docs/commands.md +451 -388
  51. package/docs/security.md +195 -144
  52. package/package.json +38 -11
  53. package/src/lib/check-aliases.js +50 -0
  54. package/{bin → src}/lib/colors.sh +2 -1
  55. package/src/lib/config.sh +347 -0
  56. package/{bin → src}/lib/constants.sh +48 -13
  57. package/src/lib/daemon/budgets.sh +107 -0
  58. package/src/lib/daemon/dependencies.sh +146 -0
  59. package/src/lib/daemon/display.sh +128 -0
  60. package/src/lib/daemon/notifications.sh +273 -0
  61. package/src/lib/daemon/routing.sh +93 -0
  62. package/src/lib/daemon/state.sh +163 -0
  63. package/src/lib/daemon/sync.sh +103 -0
  64. package/{bin → src}/lib/database.sh +52 -0
  65. package/src/lib/frontmatter.js +106 -0
  66. package/src/lib/heimdall-setup.js +113 -0
  67. package/src/lib/heimdall.js +265 -0
  68. package/src/lib/index.sh +25 -0
  69. package/{bin → src}/lib/json.sh +7 -1
  70. package/{bin → src}/lib/terminal.js +7 -1
  71. package/.claude/settings.local.json +0 -33
  72. package/agents/forge-master/capabilities.md +0 -144
  73. package/agents/forge-master/context-template.md +0 -128
  74. package/agents/forge-master/personality.md +0 -138
  75. package/bin/lib/config.sh +0 -313
  76. package/config/task-template.md +0 -87
  77. package/context/forge-state.yaml +0 -19
  78. package/docs/TODO.md +0 -150
  79. package/docs/getting-started.md +0 -243
  80. package/docs/npm-publishing.md +0 -95
  81. package/docs/workflows/README.md +0 -32
  82. package/docs/workflows/azure-devops.md +0 -108
  83. package/docs/workflows/bitbucket.md +0 -104
  84. package/docs/workflows/git-only.md +0 -130
  85. package/docs/workflows/gitea.md +0 -168
  86. package/docs/workflows/github.md +0 -103
  87. package/docs/workflows/gitlab.md +0 -105
  88. package/docs/workflows.md +0 -454
  89. package/tasks/completed/ARCH-001-duplicate-agent-config.md +0 -121
  90. package/tasks/completed/ARCH-002-mixed-bash-node-implementation.md +0 -88
  91. package/tasks/completed/ARCH-003-worker-loop-hook-duplication.md +0 -77
  92. package/tasks/completed/ARCH-009-test-organization.md +0 -78
  93. package/tasks/completed/ARCH-011-jq-vs-nodejs-json.md +0 -94
  94. package/tasks/completed/ARCH-012-tmp-files-in-root.md +0 -71
  95. package/tasks/completed/ARCH-013-exit-code-constants.md +0 -65
  96. package/tasks/completed/ARCH-014-sed-incompatibility.md +0 -96
  97. package/tasks/completed/ARCH-015-docs-todo-tracking.md +0 -83
  98. package/tasks/completed/CLEAN-001.md +0 -38
  99. package/tasks/completed/CLEAN-003.md +0 -47
  100. package/tasks/completed/CLEAN-004.md +0 -56
  101. package/tasks/completed/CLEAN-005.md +0 -75
  102. package/tasks/completed/CLEAN-006.md +0 -47
  103. package/tasks/completed/CLEAN-007.md +0 -34
  104. package/tasks/completed/CLEAN-008.md +0 -49
  105. package/tasks/completed/CLEAN-012.md +0 -58
  106. package/tasks/completed/CLEAN-013.md +0 -45
  107. package/tasks/completed/SEC-001-sql-injection-fix.md +0 -58
  108. package/tasks/completed/SEC-002-notification-injection-fix.md +0 -45
  109. package/tasks/completed/SEC-003-eval-injection-fix.md +0 -54
  110. package/tasks/completed/SEC-004-pid-race-condition-fix.md +0 -49
  111. package/tasks/completed/SEC-005-worker-loop-path-fix.md +0 -51
  112. package/tasks/completed/SEC-006-eval-agent-names.md +0 -55
  113. package/tasks/completed/SEC-007-spawn-escaping.md +0 -67
  114. package/tasks/pending/ARCH-004-git-bash-detection-duplication.md +0 -72
  115. package/tasks/pending/ARCH-005-missing-src-directory.md +0 -95
  116. package/tasks/pending/ARCH-006-task-template-location.md +0 -64
  117. package/tasks/pending/ARCH-007-daemon-monolith.md +0 -91
  118. package/tasks/pending/ARCH-008-forge-master-vs-hub.md +0 -81
  119. package/tasks/pending/ARCH-010-missing-index-files.md +0 -84
  120. package/tasks/pending/CLEAN-002.md +0 -29
  121. package/tasks/pending/CLEAN-009.md +0 -31
  122. package/tasks/pending/CLEAN-010.md +0 -30
  123. package/tasks/pending/CLEAN-011.md +0 -30
  124. package/tasks/pending/CLEAN-014.md +0 -32
  125. package/tasks/review/task-001.md +0 -78
  126. /package/{bin → src}/lib/agents.sh +0 -0
  127. /package/{bin → src}/lib/util.sh +0 -0
  128. /package/{bin → src}/lib/vcs.js +0 -0
  129. /package/{context → templates}/project-context-template.md +0 -0
@@ -6,7 +6,7 @@
6
6
  # Usage: ./forge-setup.sh [--non-interactive]
7
7
  #
8
8
 
9
- set -e
9
+ set -euo pipefail
10
10
 
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  FORGE_ROOT="$(dirname "$SCRIPT_DIR")"
@@ -16,17 +16,17 @@ FORGE_ROOT="$(dirname "$SCRIPT_DIR")"
16
16
  # =============================================================================
17
17
 
18
18
  # shellcheck source=lib/colors.sh
19
- source "$SCRIPT_DIR/lib/colors.sh"
19
+ source "$SCRIPT_DIR/../src/lib/colors.sh"
20
20
  # shellcheck source=lib/constants.sh
21
- source "$SCRIPT_DIR/lib/constants.sh"
21
+ source "$SCRIPT_DIR/../src/lib/constants.sh"
22
22
  # shellcheck source=lib/json.sh
23
- source "$SCRIPT_DIR/lib/json.sh"
23
+ source "$SCRIPT_DIR/../src/lib/json.sh"
24
24
  # shellcheck source=lib/util.sh
25
- source "$SCRIPT_DIR/lib/util.sh"
25
+ source "$SCRIPT_DIR/../src/lib/util.sh"
26
26
 
27
27
  # Parse arguments
28
28
  NON_INTERACTIVE=false
29
- if [[ "$1" == "--non-interactive" ]]; then
29
+ if [[ "${1:-}" == "--non-interactive" ]]; then
30
30
  NON_INTERACTIVE=true
31
31
  fi
32
32
 
@@ -197,7 +197,7 @@ configure_vcs() {
197
197
 
198
198
  # Auto-detect VCS
199
199
  local detected_vcs
200
- detected_vcs=$(node "$SCRIPT_DIR/lib/vcs.js" detect 2>/dev/null)
200
+ detected_vcs=$(node "$SCRIPT_DIR/../src/lib/vcs.js" detect 2>/dev/null)
201
201
  local detected_type
202
202
  detected_type=$(echo "$detected_vcs" | node -e "const d=require('fs').readFileSync(0,'utf8');try{console.log(JSON.parse(d).type)}catch{console.log('none')}")
203
203
  local confidence
@@ -256,7 +256,7 @@ configure_vcs() {
256
256
 
257
257
  # Initialize VCS folders and save config
258
258
  local init_result
259
- init_result=$(node "$SCRIPT_DIR/lib/vcs.js" init "$vcs_choice" "$FORGE_ROOT" 2>/dev/null)
259
+ init_result=$(node "$SCRIPT_DIR/../src/lib/vcs.js" init "$vcs_choice" "$FORGE_ROOT" 2>/dev/null)
260
260
 
261
261
  echo ""
262
262
  case "$vcs_choice" in
@@ -411,7 +411,10 @@ configure_daemon() {
411
411
  log_success "Daemon enabled"
412
412
  echo ""
413
413
  echo "Starting daemon..."
414
- "$SCRIPT_DIR/forge-daemon.sh" start
414
+ if ! "$SCRIPT_DIR/forge-daemon.sh" start; then
415
+ log_warn "Daemon failed to start (sqlite3 may be missing)"
416
+ echo " You can install sqlite3 and start it later with: forge daemon start"
417
+ fi
415
418
  else
416
419
  echo ""
417
420
  log_info "Daemon disabled"
@@ -534,9 +537,20 @@ create_project_context() {
534
537
 
535
538
  local context_dir="$PWD/context"
536
539
  local context_file="$context_dir/project-context.md"
537
- local template_file="$FORGE_ROOT/context/project-context-template.md"
540
+ local template_file="$FORGE_ROOT/templates/project-context-template.md"
538
541
 
539
542
  mkdir -p "$context_dir"
543
+ mkdir -p "$context_dir/agent-overrides"
544
+
545
+ # Create architecture.md if missing
546
+ if [[ ! -f "$context_dir/architecture.md" ]] && [[ -f "$FORGE_ROOT/context/architecture.md" ]]; then
547
+ cp "$FORGE_ROOT/context/architecture.md" "$context_dir/architecture.md"
548
+ fi
549
+
550
+ # Create agent-overrides README if missing
551
+ if [[ ! -f "$context_dir/agent-overrides/README.md" ]] && [[ -f "$FORGE_ROOT/context/agent-overrides/README.md" ]]; then
552
+ cp "$FORGE_ROOT/context/agent-overrides/README.md" "$context_dir/agent-overrides/README.md"
553
+ fi
540
554
 
541
555
  # Check if context already exists
542
556
  if [[ -f "$context_file" ]]; then
@@ -552,6 +566,7 @@ create_project_context() {
552
566
  fi
553
567
  echo ""
554
568
 
569
+ local PROJECT_DESC=""
555
570
  if [[ "$NON_INTERACTIVE" == "false" ]]; then
556
571
  # Ask for project description
557
572
  read -p "Brief project description (or Enter to skip): " PROJECT_DESC
@@ -613,7 +628,9 @@ setup_complete() {
613
628
  log_info "/forge spawn anvil Spawn a worker agent"
614
629
  log_info "/forge help See all commands"
615
630
  echo ""
616
- echo "Optional: Edit context/project-context.md to add more details."
631
+ echo "Optional: Edit context/project-context.md to add project details."
632
+ echo " Edit context/architecture.md for architectural guardrails."
633
+ echo " Add files to context/agent-overrides/ for per-agent rules."
617
634
  echo ""
618
635
  }
619
636
 
@@ -10,7 +10,7 @@
10
10
  # - Manual (prints instructions)
11
11
  #
12
12
 
13
- set -e
13
+ set -euo pipefail
14
14
 
15
15
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
16
  FORGE_ROOT="$(dirname "$SCRIPT_DIR")"
@@ -21,13 +21,13 @@ CONFIG_FILE="$FORGE_ROOT/.forge/config.json"
21
21
  # =============================================================================
22
22
 
23
23
  # shellcheck source=lib/colors.sh
24
- source "$SCRIPT_DIR/lib/colors.sh"
24
+ source "$SCRIPT_DIR/../src/lib/colors.sh"
25
25
  # shellcheck source=lib/constants.sh
26
- source "$SCRIPT_DIR/lib/constants.sh"
26
+ source "$SCRIPT_DIR/../src/lib/constants.sh"
27
27
  # shellcheck source=lib/config.sh
28
- source "$SCRIPT_DIR/lib/config.sh"
28
+ source "$SCRIPT_DIR/../src/lib/config.sh"
29
29
  # shellcheck source=lib/agents.sh
30
- source "$SCRIPT_DIR/lib/agents.sh"
30
+ source "$SCRIPT_DIR/../src/lib/agents.sh"
31
31
 
32
32
  # Load agent configuration from JSON if available
33
33
  if [[ -f "$FORGE_ROOT/$AGENTS_CONFIG" ]]; then
package/bin/forge.cmd CHANGED
@@ -1,83 +1,83 @@
1
- @echo off
2
- REM Vibe Forge - Windows Launcher
3
- REM Wraps forge.sh for Windows compatibility
4
-
5
- setlocal EnableDelayedExpansion
6
-
7
- set "SCRIPT_DIR=%~dp0"
8
- set "FORGE_ROOT=%SCRIPT_DIR%.."
9
- set "CONFIG_FILE=%FORGE_ROOT%\.forge\config.json"
10
-
11
- REM Check if this is init or help (doesn't require config)
12
- set "CMD=%~1"
13
- if "%CMD%"=="init" goto :find_bash
14
- if "%CMD%"=="help" goto :find_bash
15
- if "%CMD%"=="--help" goto :find_bash
16
- if "%CMD%"=="-h" goto :find_bash
17
-
18
- REM For other commands, check if initialized
19
- if not exist "%CONFIG_FILE%" (
20
- echo Error: Vibe Forge not initialized.
21
- echo Run 'forge init' first.
22
- exit /b 1
23
- )
24
-
25
- REM Extract git_bash_path from config using PowerShell for reliable JSON parsing
26
- for /f "usebackq delims=" %%a in (`powershell -NoProfile -Command "(Get-Content '%CONFIG_FILE%' | ConvertFrom-Json).git_bash_path"`) do (
27
- set "GIT_BASH_PATH=%%a"
28
- )
29
-
30
- REM Convert forward slashes to backslashes if needed
31
- set "GIT_BASH_PATH=!GIT_BASH_PATH:/=\!"
32
-
33
- REM Verify the path exists
34
- if not exist "!GIT_BASH_PATH!" (
35
- echo Error: Git Bash not found at: !GIT_BASH_PATH!
36
- echo Please run 'forge init' again.
37
- exit /b 1
38
- )
39
-
40
- goto :run_forge
41
-
42
- :find_bash
43
- REM Find Git Bash for init/help commands
44
- set "GIT_BASH_PATH="
45
-
46
- REM Check common paths
47
- if exist "C:\Program Files\Git\bin\bash.exe" (
48
- set "GIT_BASH_PATH=C:\Program Files\Git\bin\bash.exe"
49
- goto :run_forge
50
- )
51
- if exist "C:\Program Files (x86)\Git\bin\bash.exe" (
52
- set "GIT_BASH_PATH=C:\Program Files (x86)\Git\bin\bash.exe"
53
- goto :run_forge
54
- )
55
- if exist "D:\Program Files\Git\bin\bash.exe" (
56
- set "GIT_BASH_PATH=D:\Program Files\Git\bin\bash.exe"
57
- goto :run_forge
58
- )
59
- if exist "D:\applications\git\bin\bash.exe" (
60
- set "GIT_BASH_PATH=D:\applications\git\bin\bash.exe"
61
- goto :run_forge
62
- )
63
-
64
- REM Try to find via where command
65
- for /f "tokens=*" %%i in ('where git 2^>nul') do (
66
- set "GIT_PATH=%%~dpi"
67
- set "GIT_BASH_PATH=!GIT_PATH!..\bin\bash.exe"
68
- if exist "!GIT_BASH_PATH!" goto :run_forge
69
- )
70
-
71
- REM Still not found
72
- echo Error: Git Bash not found.
73
- echo Please install Git from https://git-scm.com/downloads/win
74
- exit /b 1
75
-
76
- :run_forge
77
- REM Set environment variable for Claude Code
78
- set "CLAUDE_CODE_GIT_BASH_PATH=%GIT_BASH_PATH%"
79
-
80
- REM Run the bash script with all arguments
81
- "%GIT_BASH_PATH%" "%SCRIPT_DIR%forge.sh" %*
82
-
83
- endlocal
1
+ @echo off
2
+ REM Vibe Forge - Windows Launcher
3
+ REM Wraps forge.sh for Windows compatibility
4
+
5
+ setlocal EnableDelayedExpansion
6
+
7
+ set "SCRIPT_DIR=%~dp0"
8
+ set "FORGE_ROOT=%SCRIPT_DIR%.."
9
+ set "CONFIG_FILE=%FORGE_ROOT%\.forge\config.json"
10
+
11
+ REM Check if this is init or help (doesn't require config)
12
+ set "CMD=%~1"
13
+ if "%CMD%"=="init" goto :find_bash
14
+ if "%CMD%"=="help" goto :find_bash
15
+ if "%CMD%"=="--help" goto :find_bash
16
+ if "%CMD%"=="-h" goto :find_bash
17
+
18
+ REM For other commands, check if initialized
19
+ if not exist "%CONFIG_FILE%" (
20
+ echo Error: Vibe Forge not initialized.
21
+ echo Run 'forge init' first.
22
+ exit /b 1
23
+ )
24
+
25
+ REM Extract git_bash_path from config using PowerShell for reliable JSON parsing
26
+ for /f "usebackq delims=" %%a in (`powershell -NoProfile -Command "(Get-Content '%CONFIG_FILE%' | ConvertFrom-Json).git_bash_path"`) do (
27
+ set "GIT_BASH_PATH=%%a"
28
+ )
29
+
30
+ REM Convert forward slashes to backslashes if needed
31
+ set "GIT_BASH_PATH=!GIT_BASH_PATH:/=\!"
32
+
33
+ REM Verify the path exists
34
+ if not exist "!GIT_BASH_PATH!" (
35
+ echo Error: Git Bash not found at: !GIT_BASH_PATH!
36
+ echo Please run 'forge init' again.
37
+ exit /b 1
38
+ )
39
+
40
+ goto :run_forge
41
+
42
+ :find_bash
43
+ REM Find Git Bash for init/help commands
44
+ set "GIT_BASH_PATH="
45
+
46
+ REM Check common paths
47
+ if exist "C:\Program Files\Git\bin\bash.exe" (
48
+ set "GIT_BASH_PATH=C:\Program Files\Git\bin\bash.exe"
49
+ goto :run_forge
50
+ )
51
+ if exist "C:\Program Files (x86)\Git\bin\bash.exe" (
52
+ set "GIT_BASH_PATH=C:\Program Files (x86)\Git\bin\bash.exe"
53
+ goto :run_forge
54
+ )
55
+ if exist "D:\Program Files\Git\bin\bash.exe" (
56
+ set "GIT_BASH_PATH=D:\Program Files\Git\bin\bash.exe"
57
+ goto :run_forge
58
+ )
59
+ if exist "D:\applications\git\bin\bash.exe" (
60
+ set "GIT_BASH_PATH=D:\applications\git\bin\bash.exe"
61
+ goto :run_forge
62
+ )
63
+
64
+ REM Try to find via where command
65
+ for /f "tokens=*" %%i in ('where git 2^>nul') do (
66
+ set "GIT_PATH=%%~dpi"
67
+ set "GIT_BASH_PATH=!GIT_PATH!..\bin\bash.exe"
68
+ if exist "!GIT_BASH_PATH!" goto :run_forge
69
+ )
70
+
71
+ REM Still not found
72
+ echo Error: Git Bash not found.
73
+ echo Please install Git from https://git-scm.com/downloads/win
74
+ exit /b 1
75
+
76
+ :run_forge
77
+ REM Set environment variable for Claude Code
78
+ set "CLAUDE_CODE_GIT_BASH_PATH=%GIT_BASH_PATH%"
79
+
80
+ REM Run the bash script with all arguments
81
+ "%GIT_BASH_PATH%" "%SCRIPT_DIR%forge.sh" %*
82
+
83
+ endlocal
package/bin/forge.sh CHANGED
@@ -9,17 +9,17 @@
9
9
  # forge start <agent> - Start a specific worker agent
10
10
  # forge spawn <agent> - Spawn agent in new terminal window/tab
11
11
  # forge status - Show forge status
12
- # forge test - Validate setup
12
+ # forge doctor - Full environment diagnostics (alias: test)
13
13
  # forge daemon - Start/stop the background daemon
14
14
  # forge help - Show help
15
15
  #
16
16
  # Security Note:
17
- # This script uses --dangerously-skip-permissions for Claude Code to enable
18
- # seamless agent startup. This is intentional for the forge workflow.
19
- # See docs/security.md for details.
17
+ # Agents use allowlist-based permissions (.claude/settings.json) plus
18
+ # Heimdall pre-tool hooks instead of --dangerously-skip-permissions.
19
+ # See docs/security.md for the trust model.
20
20
  #
21
21
 
22
- set -e
22
+ set -euo pipefail
23
23
 
24
24
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25
25
  FORGE_ROOT="$(dirname "$SCRIPT_DIR")"
@@ -30,15 +30,15 @@ CONFIG_FILE="$FORGE_ROOT/.forge/config.json"
30
30
  # =============================================================================
31
31
 
32
32
  # shellcheck source=lib/colors.sh
33
- source "$SCRIPT_DIR/lib/colors.sh"
33
+ source "$SCRIPT_DIR/../src/lib/colors.sh"
34
34
  # shellcheck source=lib/constants.sh
35
- source "$SCRIPT_DIR/lib/constants.sh"
35
+ source "$SCRIPT_DIR/../src/lib/constants.sh"
36
36
  # shellcheck source=lib/config.sh
37
- source "$SCRIPT_DIR/lib/config.sh"
37
+ source "$SCRIPT_DIR/../src/lib/config.sh"
38
38
  # shellcheck source=lib/json.sh
39
- source "$SCRIPT_DIR/lib/json.sh"
39
+ source "$SCRIPT_DIR/../src/lib/json.sh"
40
40
  # shellcheck source=lib/agents.sh
41
- source "$SCRIPT_DIR/lib/agents.sh"
41
+ source "$SCRIPT_DIR/../src/lib/agents.sh"
42
42
 
43
43
  # Load agent configuration from JSON if available
44
44
  # This overwrites the fallback values in constants.sh
@@ -101,6 +101,39 @@ cmd_start() {
101
101
  $(cat "$project_context")"
102
102
  fi
103
103
 
104
+ # Add per-project agent overrides if they exist (T2-E3)
105
+ local agent_override="$FORGE_ROOT/$CONTEXT_DIR/agent-overrides/${resolved}.md"
106
+ if [[ -f "$agent_override" ]]; then
107
+ system_prompt="$system_prompt
108
+
109
+ ---
110
+
111
+ # Project-Specific Rules for $agent_name
112
+
113
+ $(cat "$agent_override")"
114
+ fi
115
+
116
+ # Check for handoff files from previous sessions (T2-F2)
117
+ local handoff_dir="$FORGE_ROOT/tasks/handoffs"
118
+ if [[ -d "$handoff_dir" ]]; then
119
+ for handoff in "$handoff_dir"/*-"${resolved}"-*.md "$handoff_dir"/*-handoff.md; do
120
+ if [[ -f "$handoff" ]]; then
121
+ local handoff_agent
122
+ handoff_agent=$(node "$SCRIPT_DIR/../src/lib/frontmatter.js" "$handoff" "agent" 2>/dev/null | sed -n 's/^agent=//p')
123
+ if [[ "$handoff_agent" == "$resolved" ]]; then
124
+ system_prompt="$system_prompt
125
+
126
+ ---
127
+
128
+ # Session Handoff (from previous session)
129
+
130
+ $(cat "$handoff")"
131
+ break
132
+ fi
133
+ fi
134
+ done
135
+ fi
136
+
104
137
  # Add startup instructions based on agent type
105
138
  if [[ "$resolved" == "hub" ]]; then
106
139
  # Planning Hub startup - Party Mode Team
@@ -131,12 +164,12 @@ You are autonomous - when assigned work exists, start it without asking permissi
131
164
  fi
132
165
 
133
166
  # Launch Claude Code with the personality
134
- # --dangerously-skip-permissions avoids repeated prompts when starting agents
135
- # This is documented in docs/security.md
167
+ # Agents use project-level .claude/settings.json for permissions (allowlist + Heimdall hooks)
168
+ # instead of --dangerously-skip-permissions. See docs/security.md.
136
169
  if [[ "$resolved" == "hub" ]]; then
137
- claude --dangerously-skip-permissions --system-prompt "$system_prompt" "begin"
170
+ claude --system-prompt "$system_prompt" "begin"
138
171
  else
139
- claude --dangerously-skip-permissions --system-prompt "$system_prompt" "startup"
172
+ claude --system-prompt "$system_prompt" "startup"
140
173
  fi
141
174
  }
142
175
 
@@ -161,36 +194,182 @@ cmd_status() {
161
194
  }
162
195
 
163
196
  cmd_test() {
164
- require_forge_config "$FORGE_ROOT"
197
+ # Alias for doctor
198
+ cmd_doctor
199
+ }
165
200
 
201
+ cmd_doctor() {
166
202
  echo ""
167
- log_header "🧪 Testing Vibe Forge setup..."
203
+ log_header "🩺 Forge Doctor"
168
204
  echo ""
169
205
 
170
- # Test Claude Code
171
- echo "Testing Claude Code..."
206
+ local issues=0
207
+
208
+ # --- Dependencies ---
209
+ echo "Dependencies"
210
+ echo "────────────────────────────────────────────────"
211
+
172
212
  if claude --version &> /dev/null; then
173
- log_success "Claude Code working"
213
+ local claude_ver
214
+ claude_ver=$(claude --version 2>&1 | head -1)
215
+ log_success "Claude Code: $claude_ver"
216
+ else
217
+ log_error "Claude Code: not found"
218
+ echo " Install from: https://claude.ai/download"
219
+ issues=$((issues + 1))
220
+ fi
221
+
222
+ if command -v node &> /dev/null; then
223
+ local node_ver
224
+ node_ver=$(node --version 2>&1)
225
+ log_success "Node.js: $node_ver"
226
+ # Warn if below Node 18
227
+ local node_major
228
+ node_major=$(node --version | sed 's/v\([0-9]*\).*/\1/')
229
+ if [[ "$node_major" -lt 18 ]]; then
230
+ log_warn "Node.js $node_ver is below recommended v18+"
231
+ fi
232
+ else
233
+ log_error "Node.js: not found"
234
+ issues=$((issues + 1))
235
+ fi
236
+
237
+ if command -v git &> /dev/null; then
238
+ local git_ver
239
+ git_ver=$(git --version 2>&1)
240
+ log_success "Git: $git_ver"
241
+ else
242
+ log_error "Git: not found"
243
+ issues=$((issues + 1))
244
+ fi
245
+
246
+ if command -v sqlite3 &> /dev/null; then
247
+ local sq_ver
248
+ sq_ver=$(sqlite3 --version 2>&1 | head -1)
249
+ log_success "SQLite3: $sq_ver"
174
250
  else
175
- log_error "Claude Code not working"
176
- exit $EXIT_DEPENDENCY_MISSING
251
+ log_warn "SQLite3: not found (daemon metrics disabled)"
177
252
  fi
178
253
 
179
- # Test personality loading
254
+ # --- Forge Config ---
180
255
  echo ""
181
- echo "Testing personality loading..."
182
- local test_output
183
- test_output=$(claude --system-prompt "You are a test. Respond with only: FORGE_TEST_OK" --print "test" 2>&1 | head -1)
256
+ echo "Configuration"
257
+ echo "────────────────────────────────────────────────"
258
+
259
+ local config_file="$FORGE_ROOT/.forge/config.json"
260
+ local local_config_file="$FORGE_ROOT/.forge/config.local.json"
261
+
262
+ if [[ -f "$config_file" ]]; then
263
+ log_success "config.json: found"
264
+ local platform terminal
265
+ platform=$(json_get_string "$config_file" "platform" 2>/dev/null || echo "unknown")
266
+ terminal=$(json_get_string "$config_file" "terminal_type" 2>/dev/null || echo "unknown")
267
+ echo " Platform: $platform | Terminal: $terminal"
268
+ else
269
+ log_error "config.json: not found — run 'forge init'"
270
+ issues=$((issues + 1))
271
+ fi
272
+
273
+ if [[ -f "$local_config_file" ]]; then
274
+ log_success "config.local.json: found (local overrides active)"
275
+ fi
276
+
277
+ # --- agents.json sync check ---
278
+ local agents_file="$FORGE_ROOT/config/agents.json"
279
+ if [[ -f "$agents_file" ]]; then
280
+ log_success "agents.json: found"
281
+ local agent_count
282
+ agent_count=$(node -e "const a=require('$agents_file'); console.log(Object.keys(a.agents||{}).length)" 2>/dev/null || echo "?")
283
+ echo " Agents defined: $agent_count"
284
+ else
285
+ log_error "agents.json: not found"
286
+ issues=$((issues + 1))
287
+ fi
288
+
289
+ # --- Alias collision check ---
290
+ if [[ -f "$agents_file" ]]; then
291
+ local collisions
292
+ collisions=$(node -e "
293
+ const a = require('$agents_file').agents || {};
294
+ const seen = {};
295
+ const dupes = [];
296
+ for (const [name, info] of Object.entries(a)) {
297
+ const aliases = [name, ...(info.aliases || [])];
298
+ for (const alias of aliases) {
299
+ if (seen[alias] && seen[alias] !== name) {
300
+ dupes.push(alias + ' (' + seen[alias] + ' vs ' + name + ')');
301
+ }
302
+ seen[alias] = name;
303
+ }
304
+ }
305
+ if (dupes.length) console.log('COLLISION: ' + dupes.join(', '));
306
+ " 2>/dev/null || true)
307
+ if [[ -n "$collisions" ]]; then
308
+ log_error "Alias collision detected: $collisions"
309
+ issues=$((issues + 1))
310
+ else
311
+ log_success "No alias collisions"
312
+ fi
313
+ fi
184
314
 
315
+ # --- Task directories ---
316
+ echo ""
317
+ echo "Task System"
318
+ echo "────────────────────────────────────────────────"
319
+
320
+ local tasks_dir="$FORGE_ROOT/tasks"
321
+ local required_dirs=("pending" "in-progress" "completed" "review" "approved" "merged" "needs-changes" "attention" "bugs")
322
+ for dir in "${required_dirs[@]}"; do
323
+ if [[ -d "$tasks_dir/$dir" ]]; then
324
+ local count
325
+ count=$(ls "$tasks_dir/$dir/"*.md 2>/dev/null | wc -l | tr -d ' ')
326
+ log_success "$dir/: $count task(s)"
327
+ else
328
+ log_warn "$dir/: missing (will be created by daemon)"
329
+ fi
330
+ done
331
+
332
+ # --- Daemon ---
333
+ echo ""
334
+ echo "Daemon"
335
+ echo "────────────────────────────────────────────────"
336
+
337
+ local pid_file="$FORGE_ROOT/.forge/daemon.pid"
338
+ if [[ -f "$pid_file" ]]; then
339
+ local pid
340
+ pid=$(cat "$pid_file" 2>/dev/null)
341
+ if kill -0 "$pid" 2>/dev/null; then
342
+ log_success "Daemon running (PID $pid)"
343
+ else
344
+ log_warn "Daemon PID file exists but process is dead — run 'forge daemon start'"
345
+ fi
346
+ else
347
+ log_warn "Daemon not running — run 'forge daemon start' for automated task routing"
348
+ fi
349
+
350
+ # --- Claude Code test ---
351
+ echo ""
352
+ echo "Claude Code Integration"
353
+ echo "────────────────────────────────────────────────"
354
+
355
+ local test_output
356
+ test_output=$(claude --system-prompt "You are a test. Reply only: FORGE_TEST_OK" --print "test" 2>&1 | head -1)
185
357
  if [[ "$test_output" == *"FORGE_TEST_OK"* || "$test_output" == *"test"* ]]; then
186
- log_success "Personality loading working"
358
+ log_success "Personality injection: working"
187
359
  else
188
- log_warn "Personality loading may have issues"
360
+ log_warn "Personality injection: response unexpected"
189
361
  echo " Output: $test_output"
190
362
  fi
191
363
 
364
+ # --- Summary ---
365
+ echo ""
366
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
367
+ if [[ $issues -eq 0 ]]; then
368
+ log_success "🔥 Forge looks healthy"
369
+ else
370
+ log_error "Found $issues issue(s) — resolve the errors above before starting"
371
+ fi
192
372
  echo ""
193
- log_success "🔥 Setup validated!"
194
373
  }
195
374
 
196
375
  cmd_spawn() {
@@ -314,7 +493,7 @@ cmd_help() {
314
493
  echo " start <agent> Start a specific worker agent (in current terminal)"
315
494
  echo " spawn <agent> Spawn agent in new terminal window/tab"
316
495
  echo " status Show current forge status"
317
- echo " test Validate setup is working"
496
+ echo " doctor Full environment diagnostics (alias: test)"
318
497
  echo " daemon <action> Manage background daemon (start|stop|status|notifications|clear)"
319
498
  echo " config <setting> View or change configuration settings"
320
499
  echo " help Show this help message"
@@ -358,8 +537,8 @@ main() {
358
537
  "status")
359
538
  cmd_status
360
539
  ;;
361
- "test")
362
- cmd_test
540
+ "test"|"doctor")
541
+ cmd_doctor
363
542
  ;;
364
543
  "daemon")
365
544
  shift