@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.
- package/.claude/commands/clear-attention.md +63 -0
- package/.claude/commands/compact-context.md +52 -0
- package/.claude/commands/configure-vcs.md +102 -0
- package/.claude/commands/forge.md +171 -0
- package/.claude/commands/need-help.md +77 -0
- package/.claude/commands/update-status.md +64 -0
- package/.claude/commands/worker-loop.md +106 -0
- package/.claude/hooks/worker-loop.js +198 -0
- package/.claude/scripts/setup-worker-loop.sh +45 -0
- package/.claude/settings.local.json +46 -0
- package/LICENSE +21 -0
- package/README.md +238 -0
- package/agents/aegis/personality.md +294 -0
- package/agents/anvil/personality.md +276 -0
- package/agents/architect/personality.md +258 -0
- package/agents/crucible/personality.md +360 -0
- package/agents/ember/personality.md +291 -0
- package/agents/forge-master/capabilities.md +144 -0
- package/agents/forge-master/context-template.md +128 -0
- package/agents/forge-master/personality.md +138 -0
- package/agents/furnace/personality.md +340 -0
- package/agents/herald/personality.md +247 -0
- package/agents/loki/personality.md +108 -0
- package/agents/oracle/personality.md +283 -0
- package/agents/pixel/personality.md +113 -0
- package/agents/planning-hub/personality.md +320 -0
- package/agents/scribe/personality.md +251 -0
- package/agents/temper/personality.md +218 -0
- package/bin/cli.js +375 -0
- package/bin/dashboard/api/agents.js +333 -0
- package/bin/dashboard/api/dispatch.js +483 -0
- package/bin/dashboard/api/tasks.js +416 -0
- package/bin/dashboard/frontend/index.html +13 -0
- package/bin/dashboard/frontend/package.json +16 -0
- package/bin/dashboard/frontend/src/App.svelte +222 -0
- package/bin/dashboard/frontend/src/app.css +1777 -0
- package/bin/dashboard/frontend/src/lib/components/AgentCard.svelte +60 -0
- package/bin/dashboard/frontend/src/lib/components/AgentsPanel.svelte +57 -0
- package/bin/dashboard/frontend/src/lib/components/DispatchModal.svelte +180 -0
- package/bin/dashboard/frontend/src/lib/components/Footer.svelte +33 -0
- package/bin/dashboard/frontend/src/lib/components/Header.svelte +84 -0
- package/bin/dashboard/frontend/src/lib/components/IssueCard.svelte +33 -0
- package/bin/dashboard/frontend/src/lib/components/IssuesPanel.svelte +73 -0
- package/bin/dashboard/frontend/src/lib/components/KeyboardShortcutsModal.svelte +108 -0
- package/bin/dashboard/frontend/src/lib/components/MobileTabs.svelte +52 -0
- package/bin/dashboard/frontend/src/lib/components/NotificationCard.svelte +60 -0
- package/bin/dashboard/frontend/src/lib/components/NotificationsPanel.svelte +44 -0
- package/bin/dashboard/frontend/src/lib/components/TaskCard.svelte +63 -0
- package/bin/dashboard/frontend/src/lib/components/TasksPanel.svelte +82 -0
- package/bin/dashboard/frontend/src/lib/components/Toast.svelte +45 -0
- package/bin/dashboard/frontend/src/lib/stores/agents.js +34 -0
- package/bin/dashboard/frontend/src/lib/stores/issues.js +54 -0
- package/bin/dashboard/frontend/src/lib/stores/notifications.js +48 -0
- package/bin/dashboard/frontend/src/lib/stores/tasks.js +63 -0
- package/bin/dashboard/frontend/src/lib/stores/theme.js +33 -0
- package/bin/dashboard/frontend/src/lib/stores/toast.js +35 -0
- package/bin/dashboard/frontend/src/lib/stores/ui.js +25 -0
- package/bin/dashboard/frontend/src/lib/stores/voice.js +275 -0
- package/bin/dashboard/frontend/src/lib/stores/websocket.js +295 -0
- package/bin/dashboard/frontend/src/lib/utils/api.js +101 -0
- package/bin/dashboard/frontend/src/lib/utils/formatters.js +54 -0
- package/bin/dashboard/frontend/src/main.js +9 -0
- package/bin/dashboard/frontend/svelte.config.js +5 -0
- package/bin/dashboard/frontend/vite.config.js +20 -0
- package/bin/dashboard/public/assets/index-DnfVj9Ce.css +1 -0
- package/bin/dashboard/public/assets/index-Ze5h0kXQ.js +2 -0
- package/bin/dashboard/public/index.html +14 -0
- package/bin/dashboard/server.js +566 -0
- package/bin/forge-daemon.sh +463 -0
- package/bin/forge-setup.sh +645 -0
- package/bin/forge-spawn.sh +164 -0
- package/bin/forge.cmd +83 -0
- package/bin/forge.sh +533 -0
- package/bin/lib/agents.sh +177 -0
- package/bin/lib/colors.sh +44 -0
- package/bin/lib/config.sh +347 -0
- package/bin/lib/constants.sh +241 -0
- package/bin/lib/daemon/display.sh +128 -0
- package/bin/lib/daemon/notifications.sh +263 -0
- package/bin/lib/daemon/routing.sh +77 -0
- package/bin/lib/daemon/state.sh +115 -0
- package/bin/lib/daemon/sync.sh +95 -0
- package/bin/lib/database.sh +310 -0
- package/bin/lib/heimdall-setup.js +113 -0
- package/bin/lib/heimdall.js +265 -0
- package/bin/lib/json.sh +264 -0
- package/bin/lib/terminal.js +451 -0
- package/bin/lib/util.sh +126 -0
- package/bin/lib/vcs.js +349 -0
- package/config/agent-manifest.yaml +203 -0
- package/config/agents.json +168 -0
- package/config/task-template.md +159 -0
- package/config/task-types.yaml +106 -0
- package/context/agent-status/aegis.json +7 -0
- package/context/agent-status/anvil.json +7 -0
- package/context/agent-status/architect.json +7 -0
- package/context/agent-status/crucible.json +7 -0
- package/context/agent-status/ember.json +7 -0
- package/context/agent-status/furnace.json +7 -0
- package/context/agent-status/loki.json +7 -0
- package/context/agent-status/oracle.json +7 -0
- package/context/agent-status/pixel.json +7 -0
- package/context/agent-status/planning-hub.json +7 -0
- package/context/agent-status/scribe.json +7 -0
- package/context/agent-status/temper.json +7 -0
- package/context/feature-brainstorm.md +426 -0
- package/context/forge-state.yaml +19 -0
- package/context/modern-conventions.md +129 -0
- package/context/project-context-template.md +122 -0
- package/context/project-context.md +122 -0
- package/docs/TODO.md +150 -0
- package/docs/agents.md +409 -0
- package/docs/architecture/decisions/ADR-001-daemon-modularization.md +122 -0
- package/docs/architecture/vibe-lab-integration.md +684 -0
- package/docs/architecture.md +194 -0
- package/docs/bmad-gap-analysis-2026-03-31.md +444 -0
- package/docs/cleanup-workflow.md +329 -0
- package/docs/commands.md +451 -0
- package/docs/dashboard-mockup.html +989 -0
- package/docs/getting-started.md +261 -0
- package/docs/integration/forge-ownership-policy.md +112 -0
- package/docs/npm-publishing.md +132 -0
- package/docs/roadmap-2026.md +519 -0
- package/docs/security.md +144 -0
- package/docs/wireframes/dashboard-mvp.md +1164 -0
- package/docs/workflows/README.md +32 -0
- package/docs/workflows/azure-devops.md +108 -0
- package/docs/workflows/bitbucket.md +104 -0
- package/docs/workflows/git-only.md +130 -0
- package/docs/workflows/gitea.md +168 -0
- package/docs/workflows/github.md +103 -0
- package/docs/workflows/gitlab.md +105 -0
- package/docs/workflows.md +454 -0
- package/package.json +73 -0
- package/tasks/completed/ARCH-001-duplicate-agent-config.md +121 -0
- package/tasks/completed/ARCH-002-mixed-bash-node-implementation.md +88 -0
- package/tasks/completed/ARCH-003-worker-loop-hook-duplication.md +77 -0
- package/tasks/completed/ARCH-009-test-organization.md +78 -0
- package/tasks/completed/ARCH-011-jq-vs-nodejs-json.md +94 -0
- package/tasks/completed/ARCH-012-tmp-files-in-root.md +71 -0
- package/tasks/completed/ARCH-013-exit-code-constants.md +65 -0
- package/tasks/completed/ARCH-014-sed-incompatibility.md +96 -0
- package/tasks/completed/ARCH-015-docs-todo-tracking.md +83 -0
- package/tasks/completed/BUG-dash-001-tasks-filter-error.md +31 -0
- package/tasks/completed/BUG-dash-002-agents-unknown.md +41 -0
- package/tasks/completed/CLEAN-001.md +38 -0
- package/tasks/completed/CLEAN-002.md +43 -0
- package/tasks/completed/CLEAN-003.md +47 -0
- package/tasks/completed/CLEAN-004.md +56 -0
- package/tasks/completed/CLEAN-005.md +75 -0
- package/tasks/completed/CLEAN-006.md +47 -0
- package/tasks/completed/CLEAN-007.md +34 -0
- package/tasks/completed/CLEAN-008.md +49 -0
- package/tasks/completed/CLEAN-012.md +58 -0
- package/tasks/completed/CLEAN-013.md +45 -0
- package/tasks/completed/FEATURE-001a-dashboard-wireframes.md +162 -0
- package/tasks/completed/IMPL-007a-daemon-notifications-module.md +82 -0
- package/tasks/completed/IMPL-007b-daemon-sync-module.md +71 -0
- package/tasks/completed/IMPL-007c-daemon-state-module.md +80 -0
- package/tasks/completed/IMPL-007d-daemon-routing-module.md +77 -0
- package/tasks/completed/IMPL-007e-daemon-display-module.md +77 -0
- package/tasks/completed/IMPL-007f-daemon-integration.md +124 -0
- package/tasks/completed/PLAT-1-heimdall.md +420 -0
- package/tasks/completed/SEC-001-sql-injection-fix.md +58 -0
- package/tasks/completed/SEC-002-notification-injection-fix.md +45 -0
- package/tasks/completed/SEC-003-eval-injection-fix.md +54 -0
- package/tasks/completed/SEC-004-pid-race-condition-fix.md +49 -0
- package/tasks/completed/SEC-005-worker-loop-path-fix.md +51 -0
- package/tasks/completed/SEC-006-eval-agent-names.md +55 -0
- package/tasks/completed/SEC-007-spawn-escaping.md +67 -0
- package/tasks/completed/TASK-DASH-001-server-infrastructure.md +185 -0
- package/tasks/completed/TASK-anvil-001-dashboard-frontend.md +133 -0
- package/tasks/completed/review-bmad-aegis.md +89 -0
- package/tasks/completed/review-bmad-anvil.md +80 -0
- package/tasks/completed/review-bmad-crucible.md +81 -0
- package/tasks/completed/review-bmad-ember.md +90 -0
- package/tasks/completed/review-bmad-furnace.md +79 -0
- package/tasks/completed/review-bmad-pixel.md +82 -0
- package/tasks/completed/review-bmad-scribe.md +92 -0
- package/tasks/completed/review-bmad-sentinel.md +83 -0
- package/tasks/pending/ARCH-004-git-bash-detection-duplication.md +72 -0
- package/tasks/pending/ARCH-005-missing-src-directory.md +95 -0
- package/tasks/pending/ARCH-006-task-template-location.md +64 -0
- package/tasks/pending/ARCH-008-forge-master-vs-hub.md +81 -0
- package/tasks/pending/ARCH-010-missing-index-files.md +84 -0
- package/tasks/pending/CLEAN-009.md +31 -0
- package/tasks/pending/CLEAN-010.md +30 -0
- package/tasks/pending/CLEAN-011.md +30 -0
- package/tasks/pending/CLEAN-014.md +32 -0
- package/tasks/pending/DESIGN-dash-001-layout-review.md +45 -0
- package/tasks/pending/FEATURE-001-dashboard-mvp.md +268 -0
- package/tasks/review/ARCH-007-daemon-monolith.md +162 -0
- package/tasks/review/bmad-review-aegis.md +349 -0
- package/tasks/review/bmad-review-anvil.md +259 -0
- package/tasks/review/bmad-review-crucible.md +277 -0
- package/tasks/review/bmad-review-ember.md +307 -0
- package/tasks/review/bmad-review-furnace.md +285 -0
- package/tasks/review/bmad-review-pixel.md +329 -0
- package/tasks/review/bmad-review-scribe.md +361 -0
- package/tasks/review/bmad-review-sentinel.md +242 -0
- 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
|
+
)
|