@sugar-crash-studios/vibe-forge 0.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 (201) hide show
  1. package/.claude/commands/clear-attention.md +63 -0
  2. package/.claude/commands/compact-context.md +52 -0
  3. package/.claude/commands/configure-vcs.md +102 -0
  4. package/.claude/commands/forge.md +171 -0
  5. package/.claude/commands/need-help.md +77 -0
  6. package/.claude/commands/update-status.md +64 -0
  7. package/.claude/commands/worker-loop.md +106 -0
  8. package/.claude/hooks/worker-loop.js +198 -0
  9. package/.claude/scripts/setup-worker-loop.sh +45 -0
  10. package/.claude/settings.local.json +46 -0
  11. package/LICENSE +21 -0
  12. package/README.md +238 -0
  13. package/agents/aegis/personality.md +294 -0
  14. package/agents/anvil/personality.md +276 -0
  15. package/agents/architect/personality.md +258 -0
  16. package/agents/crucible/personality.md +360 -0
  17. package/agents/ember/personality.md +291 -0
  18. package/agents/forge-master/capabilities.md +144 -0
  19. package/agents/forge-master/context-template.md +128 -0
  20. package/agents/forge-master/personality.md +138 -0
  21. package/agents/furnace/personality.md +340 -0
  22. package/agents/herald/personality.md +247 -0
  23. package/agents/loki/personality.md +108 -0
  24. package/agents/oracle/personality.md +283 -0
  25. package/agents/pixel/personality.md +113 -0
  26. package/agents/planning-hub/personality.md +320 -0
  27. package/agents/scribe/personality.md +251 -0
  28. package/agents/temper/personality.md +218 -0
  29. package/bin/cli.js +375 -0
  30. package/bin/dashboard/api/agents.js +333 -0
  31. package/bin/dashboard/api/dispatch.js +483 -0
  32. package/bin/dashboard/api/tasks.js +416 -0
  33. package/bin/dashboard/frontend/index.html +13 -0
  34. package/bin/dashboard/frontend/package.json +16 -0
  35. package/bin/dashboard/frontend/src/App.svelte +222 -0
  36. package/bin/dashboard/frontend/src/app.css +1777 -0
  37. package/bin/dashboard/frontend/src/lib/components/AgentCard.svelte +60 -0
  38. package/bin/dashboard/frontend/src/lib/components/AgentsPanel.svelte +57 -0
  39. package/bin/dashboard/frontend/src/lib/components/DispatchModal.svelte +180 -0
  40. package/bin/dashboard/frontend/src/lib/components/Footer.svelte +33 -0
  41. package/bin/dashboard/frontend/src/lib/components/Header.svelte +84 -0
  42. package/bin/dashboard/frontend/src/lib/components/IssueCard.svelte +33 -0
  43. package/bin/dashboard/frontend/src/lib/components/IssuesPanel.svelte +73 -0
  44. package/bin/dashboard/frontend/src/lib/components/KeyboardShortcutsModal.svelte +108 -0
  45. package/bin/dashboard/frontend/src/lib/components/MobileTabs.svelte +52 -0
  46. package/bin/dashboard/frontend/src/lib/components/NotificationCard.svelte +60 -0
  47. package/bin/dashboard/frontend/src/lib/components/NotificationsPanel.svelte +44 -0
  48. package/bin/dashboard/frontend/src/lib/components/TaskCard.svelte +63 -0
  49. package/bin/dashboard/frontend/src/lib/components/TasksPanel.svelte +82 -0
  50. package/bin/dashboard/frontend/src/lib/components/Toast.svelte +45 -0
  51. package/bin/dashboard/frontend/src/lib/stores/agents.js +34 -0
  52. package/bin/dashboard/frontend/src/lib/stores/issues.js +54 -0
  53. package/bin/dashboard/frontend/src/lib/stores/notifications.js +48 -0
  54. package/bin/dashboard/frontend/src/lib/stores/tasks.js +63 -0
  55. package/bin/dashboard/frontend/src/lib/stores/theme.js +33 -0
  56. package/bin/dashboard/frontend/src/lib/stores/toast.js +35 -0
  57. package/bin/dashboard/frontend/src/lib/stores/ui.js +25 -0
  58. package/bin/dashboard/frontend/src/lib/stores/voice.js +275 -0
  59. package/bin/dashboard/frontend/src/lib/stores/websocket.js +295 -0
  60. package/bin/dashboard/frontend/src/lib/utils/api.js +101 -0
  61. package/bin/dashboard/frontend/src/lib/utils/formatters.js +54 -0
  62. package/bin/dashboard/frontend/src/main.js +9 -0
  63. package/bin/dashboard/frontend/svelte.config.js +5 -0
  64. package/bin/dashboard/frontend/vite.config.js +20 -0
  65. package/bin/dashboard/public/assets/index-DnfVj9Ce.css +1 -0
  66. package/bin/dashboard/public/assets/index-Ze5h0kXQ.js +2 -0
  67. package/bin/dashboard/public/index.html +14 -0
  68. package/bin/dashboard/server.js +566 -0
  69. package/bin/forge-daemon.sh +463 -0
  70. package/bin/forge-setup.sh +645 -0
  71. package/bin/forge-spawn.sh +164 -0
  72. package/bin/forge.cmd +83 -0
  73. package/bin/forge.sh +533 -0
  74. package/bin/lib/agents.sh +177 -0
  75. package/bin/lib/colors.sh +44 -0
  76. package/bin/lib/config.sh +347 -0
  77. package/bin/lib/constants.sh +241 -0
  78. package/bin/lib/daemon/display.sh +128 -0
  79. package/bin/lib/daemon/notifications.sh +263 -0
  80. package/bin/lib/daemon/routing.sh +77 -0
  81. package/bin/lib/daemon/state.sh +115 -0
  82. package/bin/lib/daemon/sync.sh +95 -0
  83. package/bin/lib/database.sh +310 -0
  84. package/bin/lib/heimdall-setup.js +113 -0
  85. package/bin/lib/heimdall.js +265 -0
  86. package/bin/lib/json.sh +264 -0
  87. package/bin/lib/terminal.js +451 -0
  88. package/bin/lib/util.sh +126 -0
  89. package/bin/lib/vcs.js +349 -0
  90. package/config/agent-manifest.yaml +203 -0
  91. package/config/agents.json +168 -0
  92. package/config/task-template.md +159 -0
  93. package/config/task-types.yaml +106 -0
  94. package/context/agent-status/aegis.json +7 -0
  95. package/context/agent-status/anvil.json +7 -0
  96. package/context/agent-status/architect.json +7 -0
  97. package/context/agent-status/crucible.json +7 -0
  98. package/context/agent-status/ember.json +7 -0
  99. package/context/agent-status/furnace.json +7 -0
  100. package/context/agent-status/loki.json +7 -0
  101. package/context/agent-status/oracle.json +7 -0
  102. package/context/agent-status/pixel.json +7 -0
  103. package/context/agent-status/planning-hub.json +7 -0
  104. package/context/agent-status/scribe.json +7 -0
  105. package/context/agent-status/temper.json +7 -0
  106. package/context/feature-brainstorm.md +426 -0
  107. package/context/forge-state.yaml +19 -0
  108. package/context/modern-conventions.md +129 -0
  109. package/context/project-context-template.md +122 -0
  110. package/context/project-context.md +122 -0
  111. package/docs/TODO.md +150 -0
  112. package/docs/agents.md +409 -0
  113. package/docs/architecture/decisions/ADR-001-daemon-modularization.md +122 -0
  114. package/docs/architecture/vibe-lab-integration.md +684 -0
  115. package/docs/architecture.md +194 -0
  116. package/docs/bmad-gap-analysis-2026-03-31.md +444 -0
  117. package/docs/cleanup-workflow.md +329 -0
  118. package/docs/commands.md +451 -0
  119. package/docs/dashboard-mockup.html +989 -0
  120. package/docs/getting-started.md +261 -0
  121. package/docs/integration/forge-ownership-policy.md +112 -0
  122. package/docs/npm-publishing.md +132 -0
  123. package/docs/roadmap-2026.md +519 -0
  124. package/docs/security.md +144 -0
  125. package/docs/wireframes/dashboard-mvp.md +1164 -0
  126. package/docs/workflows/README.md +32 -0
  127. package/docs/workflows/azure-devops.md +108 -0
  128. package/docs/workflows/bitbucket.md +104 -0
  129. package/docs/workflows/git-only.md +130 -0
  130. package/docs/workflows/gitea.md +168 -0
  131. package/docs/workflows/github.md +103 -0
  132. package/docs/workflows/gitlab.md +105 -0
  133. package/docs/workflows.md +454 -0
  134. package/package.json +73 -0
  135. package/tasks/completed/ARCH-001-duplicate-agent-config.md +121 -0
  136. package/tasks/completed/ARCH-002-mixed-bash-node-implementation.md +88 -0
  137. package/tasks/completed/ARCH-003-worker-loop-hook-duplication.md +77 -0
  138. package/tasks/completed/ARCH-009-test-organization.md +78 -0
  139. package/tasks/completed/ARCH-011-jq-vs-nodejs-json.md +94 -0
  140. package/tasks/completed/ARCH-012-tmp-files-in-root.md +71 -0
  141. package/tasks/completed/ARCH-013-exit-code-constants.md +65 -0
  142. package/tasks/completed/ARCH-014-sed-incompatibility.md +96 -0
  143. package/tasks/completed/ARCH-015-docs-todo-tracking.md +83 -0
  144. package/tasks/completed/BUG-dash-001-tasks-filter-error.md +31 -0
  145. package/tasks/completed/BUG-dash-002-agents-unknown.md +41 -0
  146. package/tasks/completed/CLEAN-001.md +38 -0
  147. package/tasks/completed/CLEAN-002.md +43 -0
  148. package/tasks/completed/CLEAN-003.md +47 -0
  149. package/tasks/completed/CLEAN-004.md +56 -0
  150. package/tasks/completed/CLEAN-005.md +75 -0
  151. package/tasks/completed/CLEAN-006.md +47 -0
  152. package/tasks/completed/CLEAN-007.md +34 -0
  153. package/tasks/completed/CLEAN-008.md +49 -0
  154. package/tasks/completed/CLEAN-012.md +58 -0
  155. package/tasks/completed/CLEAN-013.md +45 -0
  156. package/tasks/completed/FEATURE-001a-dashboard-wireframes.md +162 -0
  157. package/tasks/completed/IMPL-007a-daemon-notifications-module.md +82 -0
  158. package/tasks/completed/IMPL-007b-daemon-sync-module.md +71 -0
  159. package/tasks/completed/IMPL-007c-daemon-state-module.md +80 -0
  160. package/tasks/completed/IMPL-007d-daemon-routing-module.md +77 -0
  161. package/tasks/completed/IMPL-007e-daemon-display-module.md +77 -0
  162. package/tasks/completed/IMPL-007f-daemon-integration.md +124 -0
  163. package/tasks/completed/PLAT-1-heimdall.md +420 -0
  164. package/tasks/completed/SEC-001-sql-injection-fix.md +58 -0
  165. package/tasks/completed/SEC-002-notification-injection-fix.md +45 -0
  166. package/tasks/completed/SEC-003-eval-injection-fix.md +54 -0
  167. package/tasks/completed/SEC-004-pid-race-condition-fix.md +49 -0
  168. package/tasks/completed/SEC-005-worker-loop-path-fix.md +51 -0
  169. package/tasks/completed/SEC-006-eval-agent-names.md +55 -0
  170. package/tasks/completed/SEC-007-spawn-escaping.md +67 -0
  171. package/tasks/completed/TASK-DASH-001-server-infrastructure.md +185 -0
  172. package/tasks/completed/TASK-anvil-001-dashboard-frontend.md +133 -0
  173. package/tasks/completed/review-bmad-aegis.md +89 -0
  174. package/tasks/completed/review-bmad-anvil.md +80 -0
  175. package/tasks/completed/review-bmad-crucible.md +81 -0
  176. package/tasks/completed/review-bmad-ember.md +90 -0
  177. package/tasks/completed/review-bmad-furnace.md +79 -0
  178. package/tasks/completed/review-bmad-pixel.md +82 -0
  179. package/tasks/completed/review-bmad-scribe.md +92 -0
  180. package/tasks/completed/review-bmad-sentinel.md +83 -0
  181. package/tasks/pending/ARCH-004-git-bash-detection-duplication.md +72 -0
  182. package/tasks/pending/ARCH-005-missing-src-directory.md +95 -0
  183. package/tasks/pending/ARCH-006-task-template-location.md +64 -0
  184. package/tasks/pending/ARCH-008-forge-master-vs-hub.md +81 -0
  185. package/tasks/pending/ARCH-010-missing-index-files.md +84 -0
  186. package/tasks/pending/CLEAN-009.md +31 -0
  187. package/tasks/pending/CLEAN-010.md +30 -0
  188. package/tasks/pending/CLEAN-011.md +30 -0
  189. package/tasks/pending/CLEAN-014.md +32 -0
  190. package/tasks/pending/DESIGN-dash-001-layout-review.md +45 -0
  191. package/tasks/pending/FEATURE-001-dashboard-mvp.md +268 -0
  192. package/tasks/review/ARCH-007-daemon-monolith.md +162 -0
  193. package/tasks/review/bmad-review-aegis.md +349 -0
  194. package/tasks/review/bmad-review-anvil.md +259 -0
  195. package/tasks/review/bmad-review-crucible.md +277 -0
  196. package/tasks/review/bmad-review-ember.md +307 -0
  197. package/tasks/review/bmad-review-furnace.md +285 -0
  198. package/tasks/review/bmad-review-pixel.md +329 -0
  199. package/tasks/review/bmad-review-scribe.md +361 -0
  200. package/tasks/review/bmad-review-sentinel.md +242 -0
  201. package/tasks/review/task-001.md +78 -0
@@ -0,0 +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
+ }
@@ -0,0 +1,347 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Vibe Forge - Configuration Management
4
+ # Source this file in other scripts: source "$SCRIPT_DIR/lib/config.sh"
5
+ #
6
+ # SECURITY: This module provides safe JSON parsing without grep/cut vulnerabilities.
7
+ #
8
+
9
+ # Ensure colors are loaded for error messages
10
+ if ! type log_error &>/dev/null; then
11
+ echo "Error: colors.sh must be sourced before config.sh" >&2
12
+ exit 1
13
+ fi
14
+
15
+ # =============================================================================
16
+ # Agent Configuration Loading
17
+ # =============================================================================
18
+
19
+ # load_agents_from_json AGENTS_JSON_FILE
20
+ # Loads agent configuration from JSON file into shell variables.
21
+ # Sets: VALID_AGENTS array, AGENT_ALIASES associative array, AGENT_DISPLAY_NAMES
22
+ #
23
+ # SECURITY: Uses safe JSON parsing via Node.js
24
+ load_agents_from_json() {
25
+ local agents_file="$1"
26
+
27
+ if [[ ! -f "$agents_file" ]]; then
28
+ return 1
29
+ fi
30
+
31
+ if ! command -v node &>/dev/null; then
32
+ log_error "Node.js required for agent configuration"
33
+ return 1
34
+ fi
35
+
36
+ # Derive forge root from agents file location (agents_file is $FORGE_ROOT/config/agents.json)
37
+ local forge_root
38
+ forge_root="$(cd "$(dirname "$agents_file")/.." 2>/dev/null && pwd)"
39
+ local env_file="$forge_root/.forge/agents.env"
40
+
41
+ # Regenerate the static env file only when agents.json is newer or cache missing
42
+ # SECURITY: Writing to a file and sourcing it is auditable; eval of dynamic strings is not
43
+ if [[ ! -f "$env_file" || "$agents_file" -nt "$env_file" ]]; then
44
+ mkdir -p "$(dirname "$env_file")"
45
+
46
+ # Write header + validated shell assignments to the static cache file
47
+ # SECURITY: File path passed as argument, not interpolated into the script
48
+ # SECURITY: Agent names and aliases are validated to prevent shell injection
49
+ # NOTE: We output direct assignments (not declare -A) since arrays are pre-declared globally
50
+ {
51
+ printf '# AUTO-GENERATED by load_agents_from_json() -- DO NOT EDIT\n'
52
+ printf '# Source: config/agents.json\n'
53
+ printf '# Regenerated automatically when agents.json is newer than this file\n'
54
+ node -e '
55
+ const fs = require("fs");
56
+ const file = process.argv[1];
57
+
58
+ // SECURITY: Validate identifier contains only safe characters
59
+ // Allows: lowercase letters, numbers, underscore, hyphen
60
+ function isValidIdentifier(name) {
61
+ return /^[a-z0-9_-]+$/.test(name);
62
+ }
63
+
64
+ // SECURITY: Escape string for safe use in shell double-quoted string
65
+ // Escapes: $, `, ", \, newlines
66
+ function escapeForShell(str) {
67
+ if (typeof str !== "string") return "";
68
+ return str
69
+ .replace(/\\/g, "\\\\")
70
+ .replace(/"/g, "\\\"")
71
+ .replace(/\$/g, "\\$")
72
+ .replace(/`/g, "\\`")
73
+ .replace(/\n/g, "\\n")
74
+ .replace(/\r/g, "");
75
+ }
76
+
77
+ try {
78
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
79
+ const agents = data.agents || {};
80
+
81
+ // SECURITY: Validate all agent names before processing
82
+ for (const name of Object.keys(agents)) {
83
+ if (!isValidIdentifier(name)) {
84
+ console.error("SECURITY ERROR: Invalid agent name: " + name);
85
+ console.error("Agent names must contain only: a-z, 0-9, underscore, hyphen");
86
+ process.exit(1);
87
+ }
88
+ // Also validate aliases
89
+ const info = agents[name];
90
+ if (info.aliases) {
91
+ for (const alias of info.aliases) {
92
+ if (!isValidIdentifier(alias)) {
93
+ console.error("SECURITY ERROR: Invalid alias: " + alias);
94
+ console.error("Aliases must contain only: a-z, 0-9, underscore, hyphen");
95
+ process.exit(1);
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ // Output VALID_AGENTS array (names validated above)
102
+ const validAgents = Object.keys(agents);
103
+ console.log("VALID_AGENTS=(" + validAgents.map(a => `"${a}"`).join(" ") + ")");
104
+
105
+ // Output AGENT_ALIASES assignments (array already declared globally)
106
+ for (const [canonical, info] of Object.entries(agents)) {
107
+ // Add self-mapping
108
+ console.log(`AGENT_ALIASES["${canonical}"]="${canonical}"`);
109
+ // Add aliases (validated above)
110
+ if (info.aliases) {
111
+ for (const alias of info.aliases) {
112
+ console.log(`AGENT_ALIASES["${alias}"]="${canonical}"`);
113
+ }
114
+ }
115
+ }
116
+
117
+ // Output AGENT_DISPLAY_NAMES assignments
118
+ // SECURITY: Display names are escaped since they come from user input
119
+ for (const [canonical, info] of Object.entries(agents)) {
120
+ const displayName = escapeForShell(info.name || canonical);
121
+ console.log(`AGENT_DISPLAY_NAMES["${canonical}"]="${displayName}"`);
122
+ }
123
+
124
+ // Output AGENT_ROLES assignments
125
+ for (const [canonical, info] of Object.entries(agents)) {
126
+ const role = escapeForShell(info.role || "");
127
+ console.log(`AGENT_ROLES["${canonical}"]="${role}"`);
128
+ }
129
+
130
+ // Output AGENT_PERSONALITY_FILES assignments
131
+ for (const [canonical, info] of Object.entries(agents)) {
132
+ const pfile = escapeForShell(info.personality_file || "");
133
+ console.log(`AGENT_PERSONALITY_FILES["${canonical}"]="${pfile}"`);
134
+ }
135
+
136
+ // Output AGENT_ICONS assignments
137
+ for (const [canonical, info] of Object.entries(agents)) {
138
+ const icon = escapeForShell(info.icon || "");
139
+ console.log(`AGENT_ICONS["${canonical}"]="${icon}"`);
140
+ }
141
+
142
+ // Output AGENT_TAB_COLORS assignments
143
+ for (const [canonical, info] of Object.entries(agents)) {
144
+ const tabColor = escapeForShell(info.tab_color || "");
145
+ console.log(`AGENT_TAB_COLORS["${canonical}"]="${tabColor}"`);
146
+ }
147
+
148
+ } catch (e) {
149
+ console.error("Error parsing agents.json:", e.message);
150
+ process.exit(1);
151
+ }
152
+ ' -- "$agents_file" 2>/dev/null
153
+ } > "$env_file" || { rm -f "$env_file"; return 1; }
154
+ fi
155
+
156
+ # Source the static cache file instead of eval-ing dynamic Node.js output
157
+ # SECURITY: Auditable static file; Node.js only runs when agents.json changes
158
+ # shellcheck source=/dev/null
159
+ if ! source "$env_file"; then
160
+ # Corrupted or invalid cache file — remove it so it regenerates on next call
161
+ rm -f "$env_file"
162
+ return 1
163
+ fi
164
+
165
+ # Mark as loaded
166
+ AGENTS_LOADED="true"
167
+ return 0
168
+ }
169
+
170
+ # json_get_string FILE KEY
171
+ # Safely extracts a string value from a JSON file.
172
+ # Uses node.js for safe parsing (available since we require Node 16+)
173
+ #
174
+ # SECURITY: This avoids grep/cut vulnerabilities by using proper JSON parsing.
175
+ # SECURITY: File and key are passed as command-line arguments, not interpolated.
176
+ json_get_string() {
177
+ local file="$1"
178
+ local key="$2"
179
+
180
+ if [[ ! -f "$file" ]]; then
181
+ return 1
182
+ fi
183
+
184
+ # Use Node.js for safe JSON parsing
185
+ # SECURITY: Pass file and key as arguments to avoid injection
186
+ if command -v node &>/dev/null; then
187
+ node -e '
188
+ const fs = require("fs");
189
+ const file = process.argv[1];
190
+ const key = process.argv[2];
191
+ try {
192
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
193
+ const value = data[key];
194
+ if (value !== undefined && value !== null) {
195
+ console.log(String(value));
196
+ }
197
+ } catch (e) {
198
+ process.exit(1);
199
+ }
200
+ ' -- "$file" "$key" 2>/dev/null
201
+ return $?
202
+ fi
203
+
204
+ # Fallback: Use Python if available
205
+ # SECURITY: Pass file and key as arguments to avoid injection
206
+ if command -v python3 &>/dev/null; then
207
+ python3 -c '
208
+ import json, sys
209
+ try:
210
+ file_path = sys.argv[1]
211
+ key = sys.argv[2]
212
+ with open(file_path) as f:
213
+ data = json.load(f)
214
+ value = data.get(key)
215
+ if value is not None:
216
+ print(str(value))
217
+ except:
218
+ sys.exit(1)
219
+ ' "$file" "$key" 2>/dev/null
220
+ return $?
221
+ fi
222
+
223
+ # No safe parser available - exit with error
224
+ log_error "No JSON parser available. Install Node.js or Python 3."
225
+ return 1
226
+ }
227
+
228
+ # load_forge_config CONFIG_FILE
229
+ # Loads configuration from the forge config file into environment variables.
230
+ # Sets: PLATFORM, GIT_BASH_PATH, TERMINAL_TYPE, FORGE_VALIDATED
231
+ #
232
+ # Returns: 0 on success, 1 on failure
233
+ load_forge_config() {
234
+ local config_file="$1"
235
+
236
+ if [[ ! -f "$config_file" ]]; then
237
+ log_error "Vibe Forge not initialized."
238
+ echo "Run 'forge init' first." >&2
239
+ return 1
240
+ fi
241
+
242
+ # Load config values safely
243
+ PLATFORM=$(json_get_string "$config_file" "platform") || PLATFORM=""
244
+ GIT_BASH_PATH=$(json_get_string "$config_file" "git_bash_path") || GIT_BASH_PATH=""
245
+ TERMINAL_TYPE=$(json_get_string "$config_file" "terminal_type") || TERMINAL_TYPE="manual"
246
+ FORGE_VALIDATED=$(json_get_string "$config_file" "validated") || FORGE_VALIDATED="false"
247
+
248
+ # Validate required fields
249
+ if [[ -z "$PLATFORM" ]]; then
250
+ log_error "Invalid config: missing platform"
251
+ return 1
252
+ fi
253
+
254
+ return 0
255
+ }
256
+
257
+ # setup_windows_env
258
+ # Sets up Windows-specific environment variables and PATH.
259
+ # Call this after load_forge_config on Windows.
260
+ setup_windows_env() {
261
+ if [[ "$PLATFORM" != "windows" ]]; then
262
+ return 0
263
+ fi
264
+
265
+ # Export Git Bash path for Claude Code
266
+ if [[ -n "$GIT_BASH_PATH" ]]; then
267
+ # Convert forward slashes to backslashes for Windows
268
+ local git_bash_win="${GIT_BASH_PATH//\//\\}"
269
+ export CLAUDE_CODE_GIT_BASH_PATH="$git_bash_win"
270
+ fi
271
+
272
+ # Add npm global path if not already in PATH
273
+ local npm_path=""
274
+
275
+ # Try with USER variable
276
+ if [[ -n "$USER" ]]; then
277
+ npm_path="/c/Users/$USER/AppData/Roaming/npm"
278
+ fi
279
+
280
+ # Try with USERPROFILE
281
+ if [[ -z "$npm_path" || ! -d "$npm_path" ]] && [[ -n "$USERPROFILE" ]]; then
282
+ npm_path="${USERPROFILE//\\//}/AppData/Roaming/npm"
283
+ fi
284
+
285
+ # Add to PATH if exists and not already there
286
+ if [[ -n "$npm_path" && -d "$npm_path" && ":$PATH:" != *":$npm_path:"* ]]; then
287
+ export PATH="$npm_path:$PATH"
288
+ fi
289
+ }
290
+
291
+ # require_forge_config FORGE_ROOT
292
+ # Loads config and exits with error if not initialized.
293
+ # Also applies local overrides from .forge/config.local.json if present.
294
+ # config.local.json is gitignored and safe for per-developer settings
295
+ # (e.g. custom terminal path, personal daemon preferences).
296
+ require_forge_config() {
297
+ local forge_root="$1"
298
+ local config_file="$forge_root/.forge/config.json"
299
+ local local_config_file="$forge_root/.forge/config.local.json"
300
+
301
+ load_forge_config "$config_file" || exit 1
302
+
303
+ # Apply local overrides if present (not committed, per-developer)
304
+ if [[ -f "$local_config_file" ]]; then
305
+ local local_terminal local_git_bash local_daemon local_loop
306
+ local_terminal=$(json_get_string "$local_config_file" "terminal_type") && TERMINAL_TYPE="$local_terminal"
307
+ local_git_bash=$(json_get_string "$local_config_file" "git_bash_path") && GIT_BASH_PATH="$local_git_bash"
308
+ local_daemon=$(json_get_string "$local_config_file" "daemon_enabled") && DAEMON_ENABLED="$local_daemon"
309
+ local_loop=$(json_get_string "$local_config_file" "worker_loop_enabled") && WORKER_LOOP_ENABLED="$local_loop"
310
+ fi
311
+
312
+ setup_windows_env
313
+ }
314
+
315
+ # write_json_config FILE KEY VALUE
316
+ # Safely writes/updates a key in a JSON config file.
317
+ # Creates file if it doesn't exist.
318
+ #
319
+ # SECURITY: File, key, and value are passed as command-line arguments, not interpolated.
320
+ write_json_config() {
321
+ local file="$1"
322
+ local key="$2"
323
+ local value="$3"
324
+
325
+ # Use Node.js for safe JSON manipulation
326
+ # SECURITY: Pass all values as arguments to avoid injection
327
+ if command -v node &>/dev/null; then
328
+ node -e '
329
+ const fs = require("fs");
330
+ const file = process.argv[1];
331
+ const key = process.argv[2];
332
+ const value = process.argv[3];
333
+ let data = {};
334
+ try {
335
+ if (fs.existsSync(file)) {
336
+ data = JSON.parse(fs.readFileSync(file, "utf8"));
337
+ }
338
+ } catch (e) {}
339
+ data[key] = value;
340
+ fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
341
+ ' -- "$file" "$key" "$value" 2>/dev/null
342
+ return $?
343
+ fi
344
+
345
+ log_error "Node.js required for config writing"
346
+ return 1
347
+ }
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Vibe Forge - Shared Constants
4
+ # Source this file in other scripts: source "$SCRIPT_DIR/lib/constants.sh"
5
+ #
6
+ # NOTE: Agent configuration should be loaded from config/agents.json via
7
+ # load_agents_from_json() in config.sh. The hardcoded values below are
8
+ # fallback defaults for when JSON loading is not possible (e.g., tests).
9
+ #
10
+
11
+ # =============================================================================
12
+ # Exit Codes
13
+ # =============================================================================
14
+ # Standardized exit codes for consistent error handling across scripts
15
+ # Based on BSD sysexits.h conventions with project-specific additions
16
+
17
+ EXIT_SUCCESS=0 # Successful execution
18
+ EXIT_GENERAL_ERROR=1 # Generic/unspecified error
19
+ EXIT_CONFIG_ERROR=2 # Configuration file missing/invalid
20
+ EXIT_DEPENDENCY_MISSING=3 # Required dependency not found (Claude Code, Git Bash, etc.)
21
+ EXIT_INVALID_ARGUMENT=4 # Invalid argument (unknown agent, unknown command)
22
+ EXIT_RUNTIME_ERROR=5 # Runtime error (daemon conflict, spawn failed, etc.)
23
+
24
+ # Timing
25
+ POLL_INTERVAL=2 # seconds between daemon task checks
26
+ NOTIFICATION_DEBOUNCE=1 # seconds to avoid notification spam
27
+
28
+ # Daemon Configuration
29
+ MAX_LOG_SIZE=1048576 # 1MB - log rotation threshold
30
+ MAX_NOTIFY_ENTRIES=1000 # Maximum notification log entries before trimming
31
+ STALE_STATUS_THRESHOLD=300 # 5 minutes - when worker status is considered stale
32
+ MAINTENANCE_INTERVAL=100 # Daemon iterations between maintenance runs
33
+ STALE_CLEANUP_MINUTES=30 # Remove agent status older than this (minutes)
34
+ HISTORY_PRUNE_DAYS=7 # Remove status history older than this (days)
35
+
36
+ # Directory structure (relative to FORGE_ROOT)
37
+ CONFIG_DIR=".forge"
38
+ TASKS_DIR="tasks"
39
+ TASKS_PENDING="$TASKS_DIR/pending"
40
+ TASKS_IN_PROGRESS="$TASKS_DIR/in-progress"
41
+ TASKS_COMPLETED="$TASKS_DIR/completed"
42
+ TASKS_REVIEW="$TASKS_DIR/review"
43
+ TASKS_APPROVED="$TASKS_DIR/approved"
44
+ TASKS_NEEDS_CHANGES="$TASKS_DIR/needs-changes"
45
+ TASKS_MERGED="$TASKS_DIR/merged"
46
+ TASKS_ATTENTION="$TASKS_DIR/attention"
47
+ CONTEXT_DIR="context"
48
+ AGENT_STATUS_DIR="$CONTEXT_DIR/agent-status"
49
+ AGENTS_DIR="agents"
50
+
51
+ # Config files
52
+ AGENTS_CONFIG="config/agents.json"
53
+
54
+ # =============================================================================
55
+ # Agent Configuration (Fallback Defaults)
56
+ # =============================================================================
57
+ # These values are overwritten when load_agents_from_json() is called.
58
+ # They exist as fallback for contexts where JSON loading isn't available.
59
+
60
+ # Flag to track if agents were loaded from JSON
61
+ AGENTS_LOADED="${AGENTS_LOADED:-false}"
62
+
63
+ # Valid agent names (whitelist for security)
64
+ # These are the ONLY valid canonical agent names
65
+ # NOTE: Must stay in sync with config/agents.json (which is the source of truth)
66
+ # Last sync: 2026-04-03 (added loki, renamed sentinel→temper)
67
+ VALID_AGENTS=(
68
+ "hub"
69
+ "temper"
70
+ "anvil"
71
+ "furnace"
72
+ "crucible"
73
+ "scribe"
74
+ "herald"
75
+ "ember"
76
+ "aegis"
77
+ "architect"
78
+ "pixel"
79
+ "oracle"
80
+ "loki"
81
+ )
82
+
83
+ # Agent aliases map (for resolve_agent)
84
+ # Format: alias=canonical
85
+ # NOTE: Must stay in sync with config/agents.json (which is the source of truth)
86
+ declare -A AGENT_ALIASES=(
87
+ # Hub aliases (Chief Orchestrator)
88
+ ["hub"]="hub"
89
+ ["planning"]="hub"
90
+ ["master"]="hub"
91
+ ["forge-master"]="hub"
92
+ # Temper aliases (Code Reviewer)
93
+ ["temper"]="temper"
94
+ ["review"]="temper"
95
+ ["reviewer"]="temper"
96
+ ["cr"]="temper"
97
+ # Anvil aliases (Frontend)
98
+ ["anvil"]="anvil"
99
+ ["frontend"]="anvil"
100
+ ["ui"]="anvil"
101
+ ["fe"]="anvil"
102
+ # Furnace aliases (Backend)
103
+ ["furnace"]="furnace"
104
+ ["backend"]="furnace"
105
+ ["api"]="furnace"
106
+ ["be"]="furnace"
107
+ # Crucible aliases (Testing)
108
+ ["crucible"]="crucible"
109
+ ["test"]="crucible"
110
+ ["testing"]="crucible"
111
+ ["qa"]="crucible"
112
+ ["tester"]="crucible"
113
+ # Scribe aliases (Documentation)
114
+ ["scribe"]="scribe"
115
+ ["docs"]="scribe"
116
+ ["documentation"]="scribe"
117
+ ["doc"]="scribe"
118
+ # Herald aliases (Release)
119
+ ["herald"]="herald"
120
+ ["release"]="herald"
121
+ ["deploy"]="herald"
122
+ ["deployment"]="herald"
123
+ # Ember aliases (DevOps)
124
+ ["ember"]="ember"
125
+ ["devops"]="ember"
126
+ ["ops"]="ember"
127
+ ["infra"]="ember"
128
+ ["infrastructure"]="ember"
129
+ # Aegis aliases (Security)
130
+ ["aegis"]="aegis"
131
+ ["security"]="aegis"
132
+ ["sec"]="aegis"
133
+ ["appsec"]="aegis"
134
+ # Architect aliases (System Design)
135
+ ["architect"]="architect"
136
+ ["arch"]="architect"
137
+ ["sage"]="architect"
138
+ # Pixel aliases (UX Design)
139
+ # NOTE: "design" intentionally removed — ambiguous with architect's technical design domain
140
+ ["pixel"]="pixel"
141
+ ["ux"]="pixel"
142
+ ["ui-design"]="pixel"
143
+ ["user-experience"]="pixel"
144
+ # Oracle aliases (Product / Requirements)
145
+ ["oracle"]="oracle"
146
+ ["product"]="oracle"
147
+ ["po"]="oracle"
148
+ ["requirements"]="oracle"
149
+ ["req"]="oracle"
150
+ ["analyst"]="oracle"
151
+ # Loki aliases (Lateral Thinker)
152
+ ["loki"]="loki"
153
+ ["trickster"]="loki"
154
+ ["contrarian"]="loki"
155
+ ["brainstorm"]="loki"
156
+ )
157
+
158
+ # Agent display names
159
+ declare -A AGENT_DISPLAY_NAMES=(
160
+ ["hub"]="Planning Hub"
161
+ ["temper"]="Temper"
162
+ ["anvil"]="Anvil"
163
+ ["furnace"]="Furnace"
164
+ ["crucible"]="Crucible"
165
+ ["scribe"]="Scribe"
166
+ ["herald"]="Herald"
167
+ ["ember"]="Ember"
168
+ ["aegis"]="Aegis"
169
+ ["architect"]="Architect"
170
+ ["pixel"]="Pixel"
171
+ ["oracle"]="Oracle"
172
+ ["loki"]="Loki"
173
+ )
174
+
175
+ # Agent roles
176
+ declare -A AGENT_ROLES=(
177
+ ["hub"]="Chief Orchestrator"
178
+ ["temper"]="Code Reviewer"
179
+ ["anvil"]="Frontend Developer"
180
+ ["furnace"]="Backend Developer"
181
+ ["crucible"]="Tester / QA"
182
+ ["scribe"]="Documentation Specialist"
183
+ ["herald"]="Release Manager"
184
+ ["ember"]="DevOps Engineer"
185
+ ["aegis"]="Security Specialist"
186
+ ["architect"]="System Architect"
187
+ ["pixel"]="UX Designer"
188
+ ["oracle"]="Product Owner / Requirements Analyst"
189
+ ["loki"]="Lateral Thinker"
190
+ )
191
+
192
+ # Agent personality files (relative to FORGE_ROOT)
193
+ declare -A AGENT_PERSONALITY_FILES=(
194
+ ["hub"]="agents/planning-hub/personality.md"
195
+ ["temper"]="agents/temper/personality.md"
196
+ ["anvil"]="agents/anvil/personality.md"
197
+ ["furnace"]="agents/furnace/personality.md"
198
+ ["crucible"]="agents/crucible/personality.md"
199
+ ["scribe"]="agents/scribe/personality.md"
200
+ ["herald"]="agents/herald/personality.md"
201
+ ["ember"]="agents/ember/personality.md"
202
+ ["aegis"]="agents/aegis/personality.md"
203
+ ["architect"]="agents/architect/personality.md"
204
+ ["pixel"]="agents/pixel/personality.md"
205
+ ["oracle"]="agents/oracle/personality.md"
206
+ ["loki"]="agents/loki/personality.md"
207
+ )
208
+
209
+ # Agent icons
210
+ declare -A AGENT_ICONS=(
211
+ ["hub"]="⚒️"
212
+ ["temper"]="⚖️"
213
+ ["anvil"]="🔨"
214
+ ["furnace"]="🔥"
215
+ ["crucible"]="🧪"
216
+ ["scribe"]="📜"
217
+ ["herald"]="📯"
218
+ ["ember"]="⚙️"
219
+ ["aegis"]="🔒"
220
+ ["architect"]="🏛️"
221
+ ["pixel"]="🎨"
222
+ ["oracle"]="🔮"
223
+ ["loki"]="🎭"
224
+ )
225
+
226
+ # Agent tab colors for Windows Terminal (hex format)
227
+ declare -A AGENT_TAB_COLORS=(
228
+ ["hub"]="#FF6B35"
229
+ ["temper"]="#8B5CF6"
230
+ ["anvil"]="#3B82F6"
231
+ ["furnace"]="#EF4444"
232
+ ["crucible"]="#10B981"
233
+ ["scribe"]="#F59E0B"
234
+ ["herald"]="#EC4899"
235
+ ["ember"]="#F97316"
236
+ ["aegis"]="#06B6D4"
237
+ ["architect"]="#6366F1"
238
+ ["pixel"]="#D946EF"
239
+ ["oracle"]="#FBBF24"
240
+ ["loki"]="#7C3AED"
241
+ )