vibe-forge 0.4.0 → 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.
- package/.claude/commands/clear-attention.md +63 -63
- package/.claude/commands/compact-context.md +52 -0
- package/.claude/commands/configure-vcs.md +102 -102
- package/.claude/commands/forge.md +218 -171
- package/.claude/commands/need-help.md +77 -77
- package/.claude/commands/update-status.md +64 -64
- package/.claude/commands/worker-loop.md +106 -106
- package/.claude/hooks/worker-loop.js +217 -187
- package/.claude/scripts/setup-worker-loop.sh +45 -45
- package/.claude/settings.json +89 -0
- package/LICENSE +21 -21
- package/README.md +253 -232
- package/agents/aegis/personality.md +303 -269
- package/agents/anvil/personality.md +278 -240
- package/agents/architect/personality.md +260 -234
- package/agents/crucible/personality.md +362 -309
- package/agents/crucible-x/personality.md +210 -0
- package/agents/ember/personality.md +293 -265
- package/agents/flux/personality.md +248 -0
- package/agents/furnace/personality.md +342 -291
- package/agents/herald/personality.md +249 -247
- package/agents/loki/personality.md +108 -0
- package/agents/oracle/personality.md +284 -0
- package/agents/pixel/personality.md +140 -0
- package/agents/planning-hub/personality.md +473 -251
- package/agents/scribe/personality.md +253 -251
- package/agents/slag/personality.md +268 -0
- package/agents/temper/personality.md +270 -0
- package/bin/cli.js +372 -325
- package/bin/dashboard/api/agents.js +333 -0
- package/bin/dashboard/api/dispatch.js +507 -0
- package/bin/dashboard/api/tasks.js +416 -0
- package/bin/dashboard/public/assets/index-BpHfsx1r.js +2 -0
- package/bin/dashboard/public/assets/index-QODv4Zn9.css +1 -0
- package/bin/dashboard/public/index.html +14 -0
- package/bin/dashboard/server.js +645 -0
- package/bin/forge-daemon.sh +477 -851
- package/bin/forge-setup.sh +661 -645
- package/bin/forge-spawn.sh +164 -164
- package/bin/forge.cmd +83 -83
- package/bin/forge.sh +566 -387
- package/bin/lib/agents.sh +177 -177
- package/bin/lib/check-aliases.js +50 -0
- package/bin/lib/colors.sh +44 -44
- package/bin/lib/config.sh +347 -313
- package/bin/lib/constants.sh +241 -206
- package/bin/lib/daemon/budgets.sh +107 -0
- package/bin/lib/daemon/dependencies.sh +146 -0
- package/bin/lib/daemon/display.sh +128 -0
- package/bin/lib/daemon/notifications.sh +273 -0
- package/bin/lib/daemon/routing.sh +93 -0
- package/bin/lib/daemon/state.sh +163 -0
- package/bin/lib/daemon/sync.sh +103 -0
- package/bin/lib/database.sh +357 -305
- package/bin/lib/frontmatter.js +106 -0
- package/bin/lib/heimdall-setup.js +113 -0
- package/bin/lib/heimdall.js +265 -0
- package/bin/lib/json.sh +264 -258
- package/bin/lib/terminal.js +452 -446
- package/bin/lib/util.sh +126 -126
- package/bin/lib/vcs.js +349 -349
- package/config/agent-manifest.yaml +237 -243
- package/config/agents.json +207 -132
- package/config/task-template.md +159 -87
- package/config/task-types.yaml +111 -106
- package/config/templates/handoff-template.md +40 -0
- package/context/agent-overrides/README.md +41 -0
- package/context/architecture.md +42 -0
- package/context/modern-conventions.md +129 -129
- package/context/project-context-template.md +122 -122
- package/docs/agents.md +473 -409
- package/docs/architecture.md +194 -162
- package/docs/commands.md +451 -388
- package/docs/security.md +195 -144
- package/package.json +77 -50
- package/.claude/settings.local.json +0 -33
- package/agents/forge-master/capabilities.md +0 -144
- package/agents/forge-master/context-template.md +0 -128
- package/agents/forge-master/personality.md +0 -138
- package/agents/sentinel/personality.md +0 -194
- package/context/forge-state.yaml +0 -19
- package/docs/TODO.md +0 -150
- package/docs/getting-started.md +0 -243
- package/docs/npm-publishing.md +0 -95
- package/docs/workflows/README.md +0 -32
- package/docs/workflows/azure-devops.md +0 -108
- package/docs/workflows/bitbucket.md +0 -104
- package/docs/workflows/git-only.md +0 -130
- package/docs/workflows/gitea.md +0 -168
- package/docs/workflows/github.md +0 -103
- package/docs/workflows/gitlab.md +0 -105
- package/docs/workflows.md +0 -454
- package/tasks/completed/ARCH-001-duplicate-agent-config.md +0 -121
- package/tasks/completed/ARCH-002-mixed-bash-node-implementation.md +0 -88
- package/tasks/completed/ARCH-003-worker-loop-hook-duplication.md +0 -77
- package/tasks/completed/ARCH-009-test-organization.md +0 -78
- package/tasks/completed/ARCH-011-jq-vs-nodejs-json.md +0 -94
- package/tasks/completed/ARCH-012-tmp-files-in-root.md +0 -71
- package/tasks/completed/ARCH-013-exit-code-constants.md +0 -65
- package/tasks/completed/ARCH-014-sed-incompatibility.md +0 -96
- package/tasks/completed/ARCH-015-docs-todo-tracking.md +0 -83
- package/tasks/completed/CLEAN-001.md +0 -38
- package/tasks/completed/CLEAN-003.md +0 -47
- package/tasks/completed/CLEAN-004.md +0 -56
- package/tasks/completed/CLEAN-005.md +0 -75
- package/tasks/completed/CLEAN-006.md +0 -47
- package/tasks/completed/CLEAN-007.md +0 -34
- package/tasks/completed/CLEAN-008.md +0 -49
- package/tasks/completed/CLEAN-012.md +0 -58
- package/tasks/completed/CLEAN-013.md +0 -45
- package/tasks/completed/SEC-001-sql-injection-fix.md +0 -58
- package/tasks/completed/SEC-002-notification-injection-fix.md +0 -45
- package/tasks/completed/SEC-003-eval-injection-fix.md +0 -54
- package/tasks/completed/SEC-004-pid-race-condition-fix.md +0 -49
- package/tasks/completed/SEC-005-worker-loop-path-fix.md +0 -51
- package/tasks/completed/SEC-006-eval-agent-names.md +0 -55
- package/tasks/completed/SEC-007-spawn-escaping.md +0 -67
- package/tasks/pending/ARCH-004-git-bash-detection-duplication.md +0 -72
- package/tasks/pending/ARCH-005-missing-src-directory.md +0 -95
- package/tasks/pending/ARCH-006-task-template-location.md +0 -64
- package/tasks/pending/ARCH-007-daemon-monolith.md +0 -91
- package/tasks/pending/ARCH-008-forge-master-vs-hub.md +0 -81
- package/tasks/pending/ARCH-010-missing-index-files.md +0 -84
- package/tasks/pending/CLEAN-002.md +0 -29
- package/tasks/pending/CLEAN-009.md +0 -31
- package/tasks/pending/CLEAN-010.md +0 -30
- package/tasks/pending/CLEAN-011.md +0 -30
- package/tasks/pending/CLEAN-014.md +0 -32
- package/tasks/review/task-001.md +0 -78
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# bin/lib/daemon/state.sh
|
|
4
|
+
#
|
|
5
|
+
# Daemon state management - forge-state.yaml updates, attention tracking,
|
|
6
|
+
# adaptive polling
|
|
7
|
+
#
|
|
8
|
+
# Dependencies: database.sh, constants.sh
|
|
9
|
+
# Requires: sync.sh (for build_worker_status)
|
|
10
|
+
# Globals required: FORGE_ROOT, STATE_FILE, AGENT_STATUS_DIR,
|
|
11
|
+
# TASKS_PENDING, TASKS_IN_PROGRESS, TASKS_COMPLETED,
|
|
12
|
+
# TASKS_REVIEW, TASKS_APPROVED, TASKS_NEEDS_CHANGES,
|
|
13
|
+
# TASKS_MERGED, TASKS_ATTENTION
|
|
14
|
+
|
|
15
|
+
# Prevent double-sourcing
|
|
16
|
+
[[ -n "${_DAEMON_STATE_LOADED:-}" ]] && return 0
|
|
17
|
+
_DAEMON_STATE_LOADED=1
|
|
18
|
+
|
|
19
|
+
# Node.js frontmatter helper (RT-20260405-001 MEDIUM-5: replaces grep/cut YAML parsing)
|
|
20
|
+
FRONTMATTER_JS="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/frontmatter.js"
|
|
21
|
+
|
|
22
|
+
# Parse a single frontmatter field from a markdown file.
|
|
23
|
+
# Usage: fm_field <file> <field>
|
|
24
|
+
fm_field() {
|
|
25
|
+
node "$FRONTMATTER_JS" "$1" "$2" 2>/dev/null | sed -n "s/^${2}=//p"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Parse a markdown section's first content line.
|
|
29
|
+
# Usage: fm_section <file> <heading>
|
|
30
|
+
fm_section() {
|
|
31
|
+
node "$FRONTMATTER_JS" --section "$1" "$2" 2>/dev/null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
update_state() {
|
|
35
|
+
# Count tasks in each folder (using find with -maxdepth for safety)
|
|
36
|
+
local pending in_progress completed review approved needs_changes merged attention
|
|
37
|
+
pending=$(find "$FORGE_ROOT/$TASKS_PENDING" -maxdepth 1 -name "*.md" -type f 2>/dev/null | wc -l)
|
|
38
|
+
in_progress=$(find "$FORGE_ROOT/$TASKS_IN_PROGRESS" -maxdepth 1 -name "*.md" -type f 2>/dev/null | wc -l)
|
|
39
|
+
completed=$(find "$FORGE_ROOT/$TASKS_COMPLETED" -maxdepth 1 -name "*.md" -type f 2>/dev/null | wc -l)
|
|
40
|
+
review=$(find "$FORGE_ROOT/$TASKS_REVIEW" -maxdepth 1 -name "*.md" -type f 2>/dev/null | wc -l)
|
|
41
|
+
approved=$(find "$FORGE_ROOT/$TASKS_APPROVED" -maxdepth 1 -name "*.md" -type f 2>/dev/null | wc -l)
|
|
42
|
+
needs_changes=$(find "$FORGE_ROOT/$TASKS_NEEDS_CHANGES" -maxdepth 1 -name "*.md" -type f 2>/dev/null | wc -l)
|
|
43
|
+
merged=$(find "$FORGE_ROOT/$TASKS_MERGED" -maxdepth 1 -name "*.md" -type f 2>/dev/null | wc -l)
|
|
44
|
+
attention=$(find "$FORGE_ROOT/$TASKS_ATTENTION" -maxdepth 1 -name "*.md" -type f 2>/dev/null | wc -l)
|
|
45
|
+
|
|
46
|
+
local blocked=${BLOCKED_TASK_COUNT:-0}
|
|
47
|
+
|
|
48
|
+
# Build active task details for in-progress tasks
|
|
49
|
+
local active_tasks_details=""
|
|
50
|
+
if [[ "$in_progress" -gt 0 ]]; then
|
|
51
|
+
active_tasks_details=$(build_active_tasks)
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Build attention details if any workers need help
|
|
55
|
+
local attention_details=""
|
|
56
|
+
if [[ "$attention" -gt 0 ]]; then
|
|
57
|
+
attention_details=$(build_attention_details)
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Build blocked task details (T2-H2)
|
|
61
|
+
local blocked_details=""
|
|
62
|
+
if [[ "$blocked" -gt 0 ]]; then
|
|
63
|
+
blocked_details=$(build_blocked_tasks)
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# Build worker status from agent-status files
|
|
67
|
+
local worker_status=""
|
|
68
|
+
if [[ -d "$FORGE_ROOT/$AGENT_STATUS_DIR" ]]; then
|
|
69
|
+
worker_status=$(build_worker_status)
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Write state file atomically (write to temp, then move)
|
|
73
|
+
local temp_state="${STATE_FILE}.tmp.$$"
|
|
74
|
+
cat > "$temp_state" << EOF
|
|
75
|
+
# Vibe Forge State
|
|
76
|
+
# Auto-updated by forge-daemon
|
|
77
|
+
# Last updated: $(date -Iseconds)
|
|
78
|
+
|
|
79
|
+
forge:
|
|
80
|
+
status: active
|
|
81
|
+
daemon_pid: $$
|
|
82
|
+
|
|
83
|
+
tasks:
|
|
84
|
+
pending: $pending
|
|
85
|
+
in_progress: $in_progress
|
|
86
|
+
completed: $completed
|
|
87
|
+
in_review: $review
|
|
88
|
+
approved: $approved
|
|
89
|
+
needs_changes: $needs_changes
|
|
90
|
+
merged: $merged
|
|
91
|
+
blocked: $blocked
|
|
92
|
+
attention_needed: $attention
|
|
93
|
+
|
|
94
|
+
$active_tasks_details
|
|
95
|
+
$attention_details
|
|
96
|
+
$blocked_details
|
|
97
|
+
$worker_status
|
|
98
|
+
last_updated: $(date -Iseconds)
|
|
99
|
+
EOF
|
|
100
|
+
mv "$temp_state" "$STATE_FILE"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
build_attention_details() {
|
|
104
|
+
echo "attention:"
|
|
105
|
+
for attention_file in "$FORGE_ROOT/$TASKS_ATTENTION"/*.md; do
|
|
106
|
+
if [[ -f "$attention_file" && ! -L "$attention_file" ]]; then
|
|
107
|
+
local agent created issue
|
|
108
|
+
agent=$(fm_field "$attention_file" "agent")
|
|
109
|
+
created=$(fm_field "$attention_file" "created")
|
|
110
|
+
issue=$(fm_section "$attention_file" "Issue")
|
|
111
|
+
issue="${issue:-Needs attention}"
|
|
112
|
+
|
|
113
|
+
printf ' - agent: %s\n' "$agent"
|
|
114
|
+
printf ' since: %s\n' "$created"
|
|
115
|
+
printf ' issue: "%s"\n' "$issue"
|
|
116
|
+
fi
|
|
117
|
+
done
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
build_active_tasks() {
|
|
121
|
+
echo "active_tasks:"
|
|
122
|
+
for task_file in "$FORGE_ROOT/$TASKS_IN_PROGRESS"/*.md; do
|
|
123
|
+
if [[ -f "$task_file" && ! -L "$task_file" ]]; then
|
|
124
|
+
local task_id title assigned_to
|
|
125
|
+
task_id=$(fm_field "$task_file" "id")
|
|
126
|
+
title=$(fm_field "$task_file" "title")
|
|
127
|
+
assigned_to=$(fm_field "$task_file" "assigned_to")
|
|
128
|
+
|
|
129
|
+
task_id="${task_id:-$(basename "$task_file" .md)}"
|
|
130
|
+
title="${title:-Untitled}"
|
|
131
|
+
assigned_to="${assigned_to:-unassigned}"
|
|
132
|
+
|
|
133
|
+
printf ' - id: %s\n' "$task_id"
|
|
134
|
+
printf ' title: "%s"\n' "$title"
|
|
135
|
+
printf ' assigned_to: %s\n' "$assigned_to"
|
|
136
|
+
fi
|
|
137
|
+
done
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Determine daemon state based on activity (for adaptive polling)
|
|
141
|
+
determine_daemon_state() {
|
|
142
|
+
# Check if there are in-progress tasks
|
|
143
|
+
local in_progress_count
|
|
144
|
+
in_progress_count=$(find "$FORGE_ROOT/$TASKS_IN_PROGRESS" -maxdepth 1 -name "*.md" -type f 2>/dev/null | wc -l)
|
|
145
|
+
|
|
146
|
+
# Check if there are active workers
|
|
147
|
+
local active_workers
|
|
148
|
+
active_workers=$(db_count_active_workers 2>/dev/null || echo "0")
|
|
149
|
+
|
|
150
|
+
if [[ "$in_progress_count" -gt 0 ]] || [[ "$active_workers" -gt 0 ]]; then
|
|
151
|
+
echo "active"
|
|
152
|
+
else
|
|
153
|
+
echo "idle"
|
|
154
|
+
fi
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# Get current poll interval in seconds (from DB, with fallback)
|
|
158
|
+
get_poll_interval() {
|
|
159
|
+
local interval_ms
|
|
160
|
+
interval_ms=$(db_get_poll_interval_ms 2>/dev/null || echo "30000")
|
|
161
|
+
# Convert ms to seconds (bash integer division)
|
|
162
|
+
echo $((interval_ms / 1000))
|
|
163
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# bin/lib/daemon/sync.sh
|
|
4
|
+
#
|
|
5
|
+
# Agent status synchronization functions - JSON files to SQLite
|
|
6
|
+
#
|
|
7
|
+
# Dependencies: json.sh, database.sh, constants.sh
|
|
8
|
+
# Globals required: FORGE_ROOT, FORGE_DB, AGENT_STATUS_DIR, LOG_FILE,
|
|
9
|
+
# STALE_STATUS_THRESHOLD
|
|
10
|
+
|
|
11
|
+
# Prevent double-sourcing
|
|
12
|
+
[[ -n "${_DAEMON_SYNC_LOADED:-}" ]] && return 0
|
|
13
|
+
_DAEMON_SYNC_LOADED=1
|
|
14
|
+
|
|
15
|
+
# Sync agent status from JSON files to SQLite (with mtime filtering)
|
|
16
|
+
sync_agent_status_to_db() {
|
|
17
|
+
local status_dir="$FORGE_ROOT/$AGENT_STATUS_DIR"
|
|
18
|
+
|
|
19
|
+
if [[ ! -d "$status_dir" ]]; then
|
|
20
|
+
return 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
for status_file in "$status_dir"/*.json; do
|
|
24
|
+
if [[ -f "$status_file" && ! -L "$status_file" ]]; then
|
|
25
|
+
# Get file modification time
|
|
26
|
+
local file_mtime
|
|
27
|
+
file_mtime=$(stat -c %Y "$status_file" 2>/dev/null || stat -f %m "$status_file" 2>/dev/null || echo "0")
|
|
28
|
+
|
|
29
|
+
# Get agent name from filename
|
|
30
|
+
local agent_name
|
|
31
|
+
agent_name=$(basename "$status_file" .json)
|
|
32
|
+
|
|
33
|
+
# Check if file has changed since last read
|
|
34
|
+
local stored_mtime
|
|
35
|
+
stored_mtime=$(db_get_agent_mtime "$agent_name")
|
|
36
|
+
|
|
37
|
+
if [[ "$file_mtime" -gt "$stored_mtime" ]]; then
|
|
38
|
+
# File changed - parse and update DB
|
|
39
|
+
local agent status task message updated
|
|
40
|
+
agent=$(json_read "$status_file" "agent" "unknown")
|
|
41
|
+
status=$(json_read "$status_file" "status" "unknown")
|
|
42
|
+
task=$(json_read "$status_file" "task" "")
|
|
43
|
+
message=$(json_read "$status_file" "message" "" | head -c 80)
|
|
44
|
+
updated=$(json_read "$status_file" "updated" "")
|
|
45
|
+
|
|
46
|
+
# Check if status actually changed (T1-D3: record transitions)
|
|
47
|
+
local old_status
|
|
48
|
+
old_status=$(sqlite3 "$FORGE_DB" \
|
|
49
|
+
"SELECT status FROM agent_status WHERE agent = '$(db_escape "$agent")';" 2>/dev/null || echo "")
|
|
50
|
+
if [[ "$old_status" != "$status" && -n "$status" ]]; then
|
|
51
|
+
db_record_status_history "$agent" "$status" "$task"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Upsert to database
|
|
55
|
+
db_upsert_agent_status "$agent" "$status" "$task" "$message" "$updated" "$file_mtime"
|
|
56
|
+
|
|
57
|
+
echo "[$(date -Iseconds)] Synced status for $agent: $status" >> "$LOG_FILE"
|
|
58
|
+
fi
|
|
59
|
+
fi
|
|
60
|
+
done
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Build worker status from SQLite (for YAML output)
|
|
64
|
+
build_worker_status() {
|
|
65
|
+
local now_epoch
|
|
66
|
+
now_epoch=$(date +%s)
|
|
67
|
+
local stale_threshold=$STALE_STATUS_THRESHOLD
|
|
68
|
+
|
|
69
|
+
# Check if we have any agent status in DB
|
|
70
|
+
local agent_count
|
|
71
|
+
agent_count=$(sqlite3 "$FORGE_DB" "SELECT COUNT(*) FROM agent_status;" 2>/dev/null || echo "0")
|
|
72
|
+
|
|
73
|
+
if [[ "$agent_count" -eq 0 ]]; then
|
|
74
|
+
return 0
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
echo "workers:"
|
|
78
|
+
|
|
79
|
+
# Read from database
|
|
80
|
+
while IFS='|' read -r agent status task message updated; do
|
|
81
|
+
local stale_marker=""
|
|
82
|
+
|
|
83
|
+
# Check if stale
|
|
84
|
+
if [[ -n "$updated" ]]; then
|
|
85
|
+
local updated_epoch age
|
|
86
|
+
updated_epoch=$(date -d "$updated" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%S" "${updated%Z}" +%s 2>/dev/null || echo "0")
|
|
87
|
+
age=$((now_epoch - updated_epoch))
|
|
88
|
+
if [[ "$age" -gt "$stale_threshold" ]]; then
|
|
89
|
+
stale_marker=" (stale)"
|
|
90
|
+
fi
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
echo " - agent: $agent"
|
|
94
|
+
echo " status: $status$stale_marker"
|
|
95
|
+
if [[ -n "$task" ]]; then
|
|
96
|
+
echo " task: $task"
|
|
97
|
+
fi
|
|
98
|
+
if [[ -n "$message" ]]; then
|
|
99
|
+
echo " message: \"$message\""
|
|
100
|
+
fi
|
|
101
|
+
echo " updated: $updated"
|
|
102
|
+
done < <(db_get_all_agent_statuses)
|
|
103
|
+
}
|