vibe-forge 0.3.12 → 0.8.1

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 (85) 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 +102 -0
  4. package/.claude/commands/forge.md +218 -171
  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 +217 -0
  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 +253 -230
  13. package/agents/aegis/personality.md +303 -269
  14. package/agents/anvil/personality.md +278 -211
  15. package/agents/architect/personality.md +260 -0
  16. package/agents/crucible/personality.md +362 -285
  17. package/agents/crucible-x/personality.md +210 -0
  18. package/agents/ember/personality.md +293 -245
  19. package/agents/flux/personality.md +248 -0
  20. package/agents/furnace/personality.md +342 -262
  21. package/agents/herald/personality.md +249 -247
  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 +473 -251
  26. package/agents/scribe/personality.md +253 -231
  27. package/agents/slag/personality.md +268 -0
  28. package/agents/temper/personality.md +270 -0
  29. package/bin/cli.js +372 -325
  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 +477 -775
  38. package/bin/forge-setup.sh +661 -532
  39. package/bin/forge-spawn.sh +164 -159
  40. package/bin/forge.cmd +83 -83
  41. package/bin/forge.sh +566 -393
  42. package/bin/lib/agents.sh +177 -177
  43. package/bin/lib/check-aliases.js +50 -0
  44. package/bin/lib/colors.sh +44 -44
  45. package/bin/lib/config.sh +347 -271
  46. package/bin/lib/constants.sh +241 -171
  47. package/bin/lib/daemon/budgets.sh +107 -0
  48. package/bin/lib/daemon/dependencies.sh +146 -0
  49. package/bin/lib/daemon/display.sh +128 -0
  50. package/bin/lib/daemon/notifications.sh +273 -0
  51. package/bin/lib/daemon/routing.sh +93 -0
  52. package/bin/lib/daemon/state.sh +163 -0
  53. package/bin/lib/daemon/sync.sh +103 -0
  54. package/bin/lib/database.sh +357 -224
  55. package/bin/lib/frontmatter.js +106 -0
  56. package/bin/lib/heimdall-setup.js +113 -0
  57. package/bin/lib/heimdall.js +265 -0
  58. package/bin/lib/json.sh +264 -0
  59. package/bin/lib/terminal.js +452 -0
  60. package/bin/lib/util.sh +126 -0
  61. package/bin/lib/vcs.js +349 -0
  62. package/config/agent-manifest.yaml +237 -230
  63. package/config/agents.json +207 -85
  64. package/config/task-template.md +159 -87
  65. package/config/task-types.yaml +111 -106
  66. package/config/templates/handoff-template.md +40 -0
  67. package/context/agent-overrides/README.md +41 -0
  68. package/context/architecture.md +42 -0
  69. package/context/modern-conventions.md +129 -129
  70. package/context/project-context-template.md +122 -122
  71. package/docs/agents.md +473 -0
  72. package/docs/architecture.md +194 -0
  73. package/docs/commands.md +451 -0
  74. package/docs/security.md +195 -144
  75. package/package.json +77 -48
  76. package/.claude/hooks/worker-loop.sh +0 -141
  77. package/.claude/settings.local.json +0 -29
  78. package/agents/forge-master/capabilities.md +0 -144
  79. package/agents/forge-master/context-template.md +0 -128
  80. package/agents/forge-master/personality.md +0 -138
  81. package/agents/sentinel/personality.md +0 -194
  82. package/context/forge-state.yaml +0 -19
  83. package/docs/TODO.md +0 -176
  84. package/docs/npm-publishing.md +0 -95
  85. package/tasks/review/task-001.md +0 -78
package/bin/lib/agents.sh CHANGED
@@ -1,177 +1,177 @@
1
- #!/usr/bin/env bash
2
- #
3
- # Vibe Forge - Agent Resolution and Validation
4
- # Source this file in other scripts: source "$SCRIPT_DIR/lib/agents.sh"
5
- #
6
- # SECURITY: This module provides safe agent name resolution with whitelist validation.
7
- # Never pass user input directly to shell commands - always resolve through these functions.
8
- #
9
-
10
- # Ensure constants are loaded
11
- if [[ -z "${VALID_AGENTS+x}" ]]; then
12
- echo "Error: constants.sh must be sourced before agents.sh" >&2
13
- exit 1
14
- fi
15
-
16
- # resolve_agent ALIAS
17
- # Converts an agent alias to its canonical name.
18
- # Returns: canonical name on stdout, empty string if not found
19
- # Exit code: 0 if found, 1 if not found
20
- #
21
- # SECURITY: This function validates against a whitelist.
22
- # The returned value is safe to use in file paths and commands.
23
- resolve_agent() {
24
- local input="$1"
25
-
26
- # Normalize to lowercase for matching
27
- local normalized
28
- normalized=$(echo "$input" | tr '[:upper:]' '[:lower:]')
29
-
30
- # Look up in alias map
31
- local canonical="${AGENT_ALIASES[$normalized]:-}"
32
-
33
- if [[ -n "$canonical" ]]; then
34
- echo "$canonical"
35
- return 0
36
- fi
37
-
38
- return 1
39
- }
40
-
41
- # is_valid_agent AGENT
42
- # Checks if an agent name is valid (either canonical or alias)
43
- # Returns: 0 if valid, 1 if invalid
44
- is_valid_agent() {
45
- local agent="$1"
46
- resolve_agent "$agent" >/dev/null 2>&1
47
- }
48
-
49
- # get_agent_personality_path FORGE_ROOT AGENT
50
- # Returns the path to an agent's personality file.
51
- # Uses AGENT_PERSONALITY_FILES from constants.sh (or loaded from agents.json)
52
- # SECURITY: Validates agent name before constructing path.
53
- # Returns: full path on stdout
54
- # Exit code: 0 on success, 1 if agent invalid or file missing
55
- get_agent_personality_path() {
56
- local forge_root="$1"
57
- local agent="$2"
58
-
59
- # Resolve and validate agent
60
- local canonical
61
- canonical=$(resolve_agent "$agent") || {
62
- return 1
63
- }
64
-
65
- # Get personality file path from configuration
66
- local relative_path="${AGENT_PERSONALITY_FILES[$canonical]:-}"
67
- if [[ -z "$relative_path" ]]; then
68
- # Fallback: construct path if not in config
69
- relative_path="agents/$canonical/personality.md"
70
- fi
71
-
72
- local personality_path="$forge_root/$relative_path"
73
-
74
- # Validate file exists
75
- if [[ ! -f "$personality_path" ]]; then
76
- return 1
77
- fi
78
-
79
- # SECURITY: Verify the resolved path is within agents directory
80
- local real_path
81
- real_path=$(cd "$(dirname "$personality_path")" 2>/dev/null && pwd)/$(basename "$personality_path")
82
- local agents_dir
83
- agents_dir=$(cd "$forge_root/agents" 2>/dev/null && pwd)
84
-
85
- if [[ "$real_path" != "$agents_dir"/* ]]; then
86
- echo "Security error: Path traversal detected" >&2
87
- return 1
88
- fi
89
-
90
- echo "$personality_path"
91
- return 0
92
- }
93
-
94
- # show_available_agents
95
- # Prints a formatted list of available agents with aliases
96
- # Uses data from AGENT_DISPLAY_NAMES, AGENT_ROLES, and AGENT_ALIASES
97
- show_available_agents() {
98
- echo "Available agents:"
99
-
100
- # Iterate over valid agents (excluding hub for user display)
101
- for agent in "${VALID_AGENTS[@]}"; do
102
- if [[ "$agent" == "hub" ]]; then
103
- continue
104
- fi
105
-
106
- # Get display info
107
- local role="${AGENT_ROLES[$agent]:-}"
108
-
109
- # Collect aliases for this agent
110
- local aliases=()
111
- for alias in "${!AGENT_ALIASES[@]}"; do
112
- if [[ "${AGENT_ALIASES[$alias]}" == "$agent" && "$alias" != "$agent" ]]; then
113
- aliases+=("$alias")
114
- fi
115
- done
116
-
117
- # Format aliases (take first 3)
118
- local alias_str=""
119
- if [[ ${#aliases[@]} -gt 0 ]]; then
120
- # Sort and take first 3
121
- IFS=$'\n' sorted=($(printf '%s\n' "${aliases[@]}" | sort)); unset IFS
122
- local display_aliases=("${sorted[@]:0:3}")
123
- alias_str="($(IFS=", "; echo "${display_aliases[*]}"))"
124
- fi
125
-
126
- # Print formatted line
127
- printf " %-9s %-25s - %s\n" "$agent" "$alias_str" "$role"
128
- done
129
- }
130
-
131
- # get_agent_display_name AGENT
132
- # Returns the display name for an agent (e.g., "Anvil" for "anvil")
133
- # Uses AGENT_DISPLAY_NAMES from constants.sh (or loaded from agents.json)
134
- get_agent_display_name() {
135
- local agent="$1"
136
- local canonical
137
- canonical=$(resolve_agent "$agent") || return 1
138
-
139
- # Look up in AGENT_DISPLAY_NAMES array
140
- local display_name="${AGENT_DISPLAY_NAMES[$canonical]:-}"
141
- if [[ -n "$display_name" ]]; then
142
- echo "$display_name"
143
- else
144
- # Fallback to canonical name with first letter capitalized
145
- echo "${canonical^}"
146
- fi
147
- }
148
-
149
- # get_agent_role AGENT
150
- # Returns the role description for an agent
151
- get_agent_role() {
152
- local agent="$1"
153
- local canonical
154
- canonical=$(resolve_agent "$agent") || return 1
155
-
156
- echo "${AGENT_ROLES[$canonical]:-}"
157
- }
158
-
159
- # get_agent_icon AGENT
160
- # Returns the icon/emoji for an agent
161
- get_agent_icon() {
162
- local agent="$1"
163
- local canonical
164
- canonical=$(resolve_agent "$agent") || return 1
165
-
166
- echo "${AGENT_ICONS[$canonical]:-}"
167
- }
168
-
169
- # get_agent_tab_color AGENT
170
- # Returns the Windows Terminal tab color for an agent (hex format)
171
- get_agent_tab_color() {
172
- local agent="$1"
173
- local canonical
174
- canonical=$(resolve_agent "$agent") || return 1
175
-
176
- echo "${AGENT_TAB_COLORS[$canonical]:-}"
177
- }
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Vibe Forge - Agent Resolution and Validation
4
+ # Source this file in other scripts: source "$SCRIPT_DIR/lib/agents.sh"
5
+ #
6
+ # SECURITY: This module provides safe agent name resolution with whitelist validation.
7
+ # Never pass user input directly to shell commands - always resolve through these functions.
8
+ #
9
+
10
+ # Ensure constants are loaded
11
+ if [[ -z "${VALID_AGENTS+x}" ]]; then
12
+ echo "Error: constants.sh must be sourced before agents.sh" >&2
13
+ exit 1
14
+ fi
15
+
16
+ # resolve_agent ALIAS
17
+ # Converts an agent alias to its canonical name.
18
+ # Returns: canonical name on stdout, empty string if not found
19
+ # Exit code: 0 if found, 1 if not found
20
+ #
21
+ # SECURITY: This function validates against a whitelist.
22
+ # The returned value is safe to use in file paths and commands.
23
+ resolve_agent() {
24
+ local input="$1"
25
+
26
+ # Normalize to lowercase for matching
27
+ local normalized
28
+ normalized=$(echo "$input" | tr '[:upper:]' '[:lower:]')
29
+
30
+ # Look up in alias map
31
+ local canonical="${AGENT_ALIASES[$normalized]:-}"
32
+
33
+ if [[ -n "$canonical" ]]; then
34
+ echo "$canonical"
35
+ return 0
36
+ fi
37
+
38
+ return 1
39
+ }
40
+
41
+ # is_valid_agent AGENT
42
+ # Checks if an agent name is valid (either canonical or alias)
43
+ # Returns: 0 if valid, 1 if invalid
44
+ is_valid_agent() {
45
+ local agent="$1"
46
+ resolve_agent "$agent" >/dev/null 2>&1
47
+ }
48
+
49
+ # get_agent_personality_path FORGE_ROOT AGENT
50
+ # Returns the path to an agent's personality file.
51
+ # Uses AGENT_PERSONALITY_FILES from constants.sh (or loaded from agents.json)
52
+ # SECURITY: Validates agent name before constructing path.
53
+ # Returns: full path on stdout
54
+ # Exit code: 0 on success, 1 if agent invalid or file missing
55
+ get_agent_personality_path() {
56
+ local forge_root="$1"
57
+ local agent="$2"
58
+
59
+ # Resolve and validate agent
60
+ local canonical
61
+ canonical=$(resolve_agent "$agent") || {
62
+ return 1
63
+ }
64
+
65
+ # Get personality file path from configuration
66
+ local relative_path="${AGENT_PERSONALITY_FILES[$canonical]:-}"
67
+ if [[ -z "$relative_path" ]]; then
68
+ # Fallback: construct path if not in config
69
+ relative_path="agents/$canonical/personality.md"
70
+ fi
71
+
72
+ local personality_path="$forge_root/$relative_path"
73
+
74
+ # Validate file exists
75
+ if [[ ! -f "$personality_path" ]]; then
76
+ return 1
77
+ fi
78
+
79
+ # SECURITY: Verify the resolved path is within agents directory
80
+ local real_path
81
+ real_path=$(cd "$(dirname "$personality_path")" 2>/dev/null && pwd)/$(basename "$personality_path")
82
+ local agents_dir
83
+ agents_dir=$(cd "$forge_root/agents" 2>/dev/null && pwd)
84
+
85
+ if [[ "$real_path" != "$agents_dir"/* ]]; then
86
+ echo "Security error: Path traversal detected" >&2
87
+ return 1
88
+ fi
89
+
90
+ echo "$personality_path"
91
+ return 0
92
+ }
93
+
94
+ # show_available_agents
95
+ # Prints a formatted list of available agents with aliases
96
+ # Uses data from AGENT_DISPLAY_NAMES, AGENT_ROLES, and AGENT_ALIASES
97
+ show_available_agents() {
98
+ echo "Available agents:"
99
+
100
+ # Iterate over valid agents (excluding hub for user display)
101
+ for agent in "${VALID_AGENTS[@]}"; do
102
+ if [[ "$agent" == "hub" ]]; then
103
+ continue
104
+ fi
105
+
106
+ # Get display info
107
+ local role="${AGENT_ROLES[$agent]:-}"
108
+
109
+ # Collect aliases for this agent
110
+ local aliases=()
111
+ for alias in "${!AGENT_ALIASES[@]}"; do
112
+ if [[ "${AGENT_ALIASES[$alias]}" == "$agent" && "$alias" != "$agent" ]]; then
113
+ aliases+=("$alias")
114
+ fi
115
+ done
116
+
117
+ # Format aliases (take first 3)
118
+ local alias_str=""
119
+ if [[ ${#aliases[@]} -gt 0 ]]; then
120
+ # Sort and take first 3
121
+ IFS=$'\n' sorted=($(printf '%s\n' "${aliases[@]}" | sort)); unset IFS
122
+ local display_aliases=("${sorted[@]:0:3}")
123
+ alias_str="($(IFS=", "; echo "${display_aliases[*]}"))"
124
+ fi
125
+
126
+ # Print formatted line
127
+ printf " %-9s %-25s - %s\n" "$agent" "$alias_str" "$role"
128
+ done
129
+ }
130
+
131
+ # get_agent_display_name AGENT
132
+ # Returns the display name for an agent (e.g., "Anvil" for "anvil")
133
+ # Uses AGENT_DISPLAY_NAMES from constants.sh (or loaded from agents.json)
134
+ get_agent_display_name() {
135
+ local agent="$1"
136
+ local canonical
137
+ canonical=$(resolve_agent "$agent") || return 1
138
+
139
+ # Look up in AGENT_DISPLAY_NAMES array
140
+ local display_name="${AGENT_DISPLAY_NAMES[$canonical]:-}"
141
+ if [[ -n "$display_name" ]]; then
142
+ echo "$display_name"
143
+ else
144
+ # Fallback to canonical name with first letter capitalized
145
+ echo "${canonical^}"
146
+ fi
147
+ }
148
+
149
+ # get_agent_role AGENT
150
+ # Returns the role description for an agent
151
+ get_agent_role() {
152
+ local agent="$1"
153
+ local canonical
154
+ canonical=$(resolve_agent "$agent") || return 1
155
+
156
+ echo "${AGENT_ROLES[$canonical]:-}"
157
+ }
158
+
159
+ # get_agent_icon AGENT
160
+ # Returns the icon/emoji for an agent
161
+ get_agent_icon() {
162
+ local agent="$1"
163
+ local canonical
164
+ canonical=$(resolve_agent "$agent") || return 1
165
+
166
+ echo "${AGENT_ICONS[$canonical]:-}"
167
+ }
168
+
169
+ # get_agent_tab_color AGENT
170
+ # Returns the Windows Terminal tab color for an agent (hex format)
171
+ get_agent_tab_color() {
172
+ local agent="$1"
173
+ local canonical
174
+ canonical=$(resolve_agent "$agent") || return 1
175
+
176
+ echo "${AGENT_TAB_COLORS[$canonical]:-}"
177
+ }
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agent alias collision detector.
4
+ *
5
+ * Used by:
6
+ * - .husky/pre-commit (pre-commit hook)
7
+ * - .github/workflows/ci.yml (CI lint job)
8
+ * - bin/cli.js validateAgentsConfig() (forge init)
9
+ *
10
+ * Exit code 0: no collisions
11
+ * Exit code 1: collisions found (printed to stderr)
12
+ */
13
+
14
+ const path = require('path');
15
+ const fs = require('fs');
16
+
17
+ const configPath = process.argv[2] || path.join(__dirname, '..', '..', 'config', 'agents.json');
18
+
19
+ if (!fs.existsSync(configPath)) {
20
+ // No agents.json is fine (e.g., fresh clone before setup)
21
+ process.exit(0);
22
+ }
23
+
24
+ let config;
25
+ try {
26
+ config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
27
+ } catch (err) {
28
+ console.error(`ERROR: Failed to parse ${configPath}: ${err.message}`);
29
+ process.exit(1);
30
+ }
31
+
32
+ const agents = config.agents || {};
33
+ const seen = {};
34
+ const dupes = [];
35
+
36
+ for (const [name, info] of Object.entries(agents)) {
37
+ for (const alias of [name, ...(info.aliases || [])]) {
38
+ if (seen[alias] && seen[alias] !== name) {
39
+ dupes.push(`${alias} (${seen[alias]} vs ${name})`);
40
+ }
41
+ seen[alias] = name;
42
+ }
43
+ }
44
+
45
+ if (dupes.length) {
46
+ console.error(`ERROR: Alias collision in agents.json: ${dupes.join(', ')}`);
47
+ process.exit(1);
48
+ }
49
+
50
+ console.log(`No alias collisions found (${Object.keys(agents).length} agents checked)`);
package/bin/lib/colors.sh CHANGED
@@ -1,44 +1,44 @@
1
- #!/usr/bin/env bash
2
- #
3
- # Vibe Forge - Shared Color Definitions and Logging
4
- # Source this file in other scripts: source "$SCRIPT_DIR/lib/colors.sh"
5
- #
6
-
7
- # Colors (only if terminal supports them)
8
- if [[ -t 1 ]]; then
9
- RED='\033[0;31m'
10
- GREEN='\033[0;32m'
11
- YELLOW='\033[1;33m'
12
- BLUE='\033[0;34m'
13
- CYAN='\033[0;36m'
14
- NC='\033[0m'
15
- else
16
- RED=''
17
- GREEN=''
18
- YELLOW=''
19
- BLUE=''
20
- CYAN=''
21
- NC=''
22
- fi
23
-
24
- # Logging functions
25
- log_error() {
26
- echo -e "${RED}Error: $1${NC}" >&2
27
- }
28
-
29
- log_success() {
30
- echo -e "${GREEN}✓ $1${NC}"
31
- }
32
-
33
- log_info() {
34
- echo -e "${BLUE}ℹ $1${NC}"
35
- }
36
-
37
- log_warn() {
38
- echo -e "${YELLOW}⚠ $1${NC}"
39
- }
40
-
41
- log_header() {
42
- echo -e "${YELLOW}$1${NC}"
43
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
44
- }
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Vibe Forge - Shared Color Definitions and Logging
4
+ # Source this file in other scripts: source "$SCRIPT_DIR/lib/colors.sh"
5
+ #
6
+
7
+ # Colors (only if terminal supports them)
8
+ if [[ -t 1 ]]; then
9
+ RED='\033[0;31m'
10
+ GREEN='\033[0;32m'
11
+ YELLOW='\033[1;33m'
12
+ BLUE='\033[0;34m'
13
+ CYAN='\033[0;36m'
14
+ NC='\033[0m'
15
+ else
16
+ RED=''
17
+ GREEN=''
18
+ YELLOW=''
19
+ BLUE=''
20
+ CYAN=''
21
+ NC=''
22
+ fi
23
+
24
+ # Logging functions
25
+ log_error() {
26
+ echo -e "${RED}Error: $1${NC}" >&2
27
+ }
28
+
29
+ log_success() {
30
+ echo -e "${GREEN}✓ $1${NC}"
31
+ }
32
+
33
+ log_info() {
34
+ echo -e "${BLUE}ℹ $1${NC}"
35
+ }
36
+
37
+ log_warn() {
38
+ echo -e "${YELLOW}⚠ $1${NC}"
39
+ }
40
+
41
+ log_header() {
42
+ echo -e "${YELLOW}$1${NC}"
43
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
44
+ }