vibe-forge 0.4.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/clear-attention.md +63 -63
- package/.claude/commands/compact-context.md +52 -0
- package/.claude/commands/configure-vcs.md +5 -5
- package/.claude/commands/forge.md +50 -3
- 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 +37 -4
- package/.claude/scripts/setup-worker-loop.sh +45 -45
- package/.claude/settings.json +89 -0
- package/LICENSE +21 -21
- package/README.md +211 -232
- package/agents/aegis/personality.md +35 -1
- package/agents/anvil/personality.md +39 -1
- package/agents/architect/personality.md +26 -0
- package/agents/crucible/personality.md +54 -1
- package/agents/crucible-x/personality.md +210 -0
- package/agents/ember/personality.md +29 -1
- package/agents/flux/personality.md +248 -0
- package/agents/furnace/personality.md +52 -1
- package/agents/herald/personality.md +3 -1
- 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 +222 -0
- package/agents/scribe/personality.md +3 -1
- package/agents/slag/personality.md +268 -0
- package/agents/{sentinel → temper}/personality.md +85 -9
- package/bin/cli.js +77 -30
- 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 +176 -550
- package/bin/forge-setup.sh +28 -11
- package/bin/forge-spawn.sh +5 -5
- package/bin/forge.cmd +83 -83
- package/bin/forge.sh +210 -31
- package/config/agent-manifest.yaml +237 -243
- package/config/agents.json +207 -132
- package/config/task-types.yaml +111 -106
- package/context/agent-overrides/README.md +41 -0
- package/context/architecture.md +42 -0
- package/context/modern-conventions.md +129 -129
- 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 +38 -11
- package/src/lib/check-aliases.js +50 -0
- package/{bin → src}/lib/colors.sh +2 -1
- package/src/lib/config.sh +347 -0
- package/{bin → src}/lib/constants.sh +48 -13
- package/src/lib/daemon/budgets.sh +107 -0
- package/src/lib/daemon/dependencies.sh +146 -0
- package/src/lib/daemon/display.sh +128 -0
- package/src/lib/daemon/notifications.sh +273 -0
- package/src/lib/daemon/routing.sh +93 -0
- package/src/lib/daemon/state.sh +163 -0
- package/src/lib/daemon/sync.sh +103 -0
- package/{bin → src}/lib/database.sh +52 -0
- package/src/lib/frontmatter.js +106 -0
- package/src/lib/heimdall-setup.js +113 -0
- package/src/lib/heimdall.js +265 -0
- package/src/lib/index.sh +25 -0
- package/{bin → src}/lib/json.sh +7 -1
- package/{bin → src}/lib/terminal.js +7 -1
- 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/bin/lib/config.sh +0 -313
- package/config/task-template.md +0 -87
- 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
- /package/{bin → src}/lib/agents.sh +0 -0
- /package/{bin → src}/lib/util.sh +0 -0
- /package/{bin → src}/lib/vcs.js +0 -0
- /package/{context → templates}/project-context-template.md +0 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Heimdall -- Forge worker pre-tool hook interceptor (PLAT-1)
|
|
4
|
+
*
|
|
5
|
+
* Guards the Bifrost: intercepts every tool call before execution,
|
|
6
|
+
* checks it against per-task policy, and blocks or allows.
|
|
7
|
+
*
|
|
8
|
+
* Registered as a PreToolUse hook via .claude/settings.local.json
|
|
9
|
+
* written by the forge daemon at worker startup.
|
|
10
|
+
*
|
|
11
|
+
* Exit codes:
|
|
12
|
+
* 0 -- allow (optionally with audit log entry)
|
|
13
|
+
* 2 -- block (explanation written to stdout, fed back to model)
|
|
14
|
+
*
|
|
15
|
+
* Context file (.context.json in process.cwd()) is written by the forge
|
|
16
|
+
* daemon alongside each inbox task. If absent, Heimdall exits 0 immediately
|
|
17
|
+
* -- forge-native tasks are not restricted.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
'use strict'
|
|
21
|
+
|
|
22
|
+
const fs = require('fs')
|
|
23
|
+
const path = require('path')
|
|
24
|
+
|
|
25
|
+
// ── Input ─────────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
let input
|
|
28
|
+
try {
|
|
29
|
+
input = JSON.parse(fs.readFileSync(0, 'utf8'))
|
|
30
|
+
} catch (e) {
|
|
31
|
+
// Malformed input -- allow and exit rather than blocking the worker
|
|
32
|
+
process.exit(0)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const { tool_name, tool_input } = input
|
|
36
|
+
|
|
37
|
+
// ── Context file ──────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
const contextPath = path.join(process.cwd(), '.context.json')
|
|
40
|
+
|
|
41
|
+
// No context file = forge-native task. Heimdall does not restrict forge-native work.
|
|
42
|
+
if (!fs.existsSync(contextPath)) {
|
|
43
|
+
process.exit(0)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let ctx
|
|
47
|
+
try {
|
|
48
|
+
ctx = JSON.parse(fs.readFileSync(contextPath, 'utf8'))
|
|
49
|
+
} catch (e) {
|
|
50
|
+
// Unreadable context -- fail open to avoid blocking forge-native work
|
|
51
|
+
process.exit(0)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
story_id,
|
|
56
|
+
agent,
|
|
57
|
+
worktree_path,
|
|
58
|
+
has_db_migration,
|
|
59
|
+
allowed_paths,
|
|
60
|
+
escalation_dir,
|
|
61
|
+
audit_log: ctxAuditLog,
|
|
62
|
+
} = ctx
|
|
63
|
+
|
|
64
|
+
// ── Logging ───────────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
const AUDIT_LOG = ctxAuditLog
|
|
67
|
+
|| path.join(process.cwd(), '_vibe-chain-output', 'heimdall-audit.log')
|
|
68
|
+
|
|
69
|
+
const MAX_VIOLATIONS = parseInt(process.env.HEIMDALL_MAX_VIOLATIONS || '3', 10)
|
|
70
|
+
|
|
71
|
+
// Violation tracking lives in the worktree root (process.cwd())
|
|
72
|
+
const VIOLATION_FILE = path.join(process.cwd(), `.${story_id}.violations`)
|
|
73
|
+
|
|
74
|
+
// Escalation signal written to the inbox agent dir so the daemon detects it
|
|
75
|
+
const ESCALATION_DIR = escalation_dir || process.cwd()
|
|
76
|
+
|
|
77
|
+
function timestamp() {
|
|
78
|
+
return new Date().toISOString()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function audit(level, message) {
|
|
82
|
+
const line = `[${timestamp()}] HEIMDALL ${level} ${agent}/${story_id}: ${message}\n`
|
|
83
|
+
try {
|
|
84
|
+
const dir = path.dirname(AUDIT_LOG)
|
|
85
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
|
|
86
|
+
fs.appendFileSync(AUDIT_LOG, line)
|
|
87
|
+
} catch (_) {
|
|
88
|
+
// Audit log write failure must never block or crash the hook
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function block(reason) {
|
|
93
|
+
audit('BLOCKED', reason)
|
|
94
|
+
|
|
95
|
+
// Track violations
|
|
96
|
+
let violations = 0
|
|
97
|
+
try {
|
|
98
|
+
if (fs.existsSync(VIOLATION_FILE)) {
|
|
99
|
+
violations = parseInt(fs.readFileSync(VIOLATION_FILE, 'utf8').trim(), 10) || 0
|
|
100
|
+
}
|
|
101
|
+
} catch (_) {}
|
|
102
|
+
|
|
103
|
+
violations++
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
fs.writeFileSync(VIOLATION_FILE, String(violations))
|
|
107
|
+
} catch (_) {}
|
|
108
|
+
|
|
109
|
+
if (violations >= MAX_VIOLATIONS) {
|
|
110
|
+
// Sound the Gjallarhorn
|
|
111
|
+
const escalationFile = path.join(ESCALATION_DIR, `${story_id}.escalation`)
|
|
112
|
+
try {
|
|
113
|
+
fs.writeFileSync(escalationFile, JSON.stringify({
|
|
114
|
+
story_id,
|
|
115
|
+
agent,
|
|
116
|
+
violations,
|
|
117
|
+
last_reason: reason,
|
|
118
|
+
timestamp: timestamp(),
|
|
119
|
+
}, null, 2))
|
|
120
|
+
} catch (_) {}
|
|
121
|
+
|
|
122
|
+
audit('SOUNDED', `${violations} violations -- escalating to human review`)
|
|
123
|
+
|
|
124
|
+
console.log(
|
|
125
|
+
`[HEIMDALL] Action blocked: ${reason}\n` +
|
|
126
|
+
`[HEIMDALL] GJALLARHORN SOUNDED: ${violations} violations recorded.\n` +
|
|
127
|
+
`[HEIMDALL] Story ${story_id} has been escalated to human review. Stop working on this story.`
|
|
128
|
+
)
|
|
129
|
+
} else {
|
|
130
|
+
console.log(
|
|
131
|
+
`[HEIMDALL] Action blocked: ${reason}\n` +
|
|
132
|
+
`[HEIMDALL] Violation ${violations}/${MAX_VIOLATIONS}. Self-correct and continue.`
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
process.exit(2)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function allow(note) {
|
|
140
|
+
if (note) audit('ALLOWED', note)
|
|
141
|
+
process.exit(0)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── Path helpers ──────────────────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
function isOutsideAllowedPaths(targetPath) {
|
|
147
|
+
if (!targetPath) return false
|
|
148
|
+
try {
|
|
149
|
+
const resolved = path.resolve(targetPath)
|
|
150
|
+
return !allowed_paths.some(p => resolved.startsWith(path.resolve(p)))
|
|
151
|
+
} catch (_) {
|
|
152
|
+
return true // Unresolvable path -- treat as outside
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── Bash tool checks ──────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
if (tool_name === 'Bash') {
|
|
159
|
+
const cmd = (tool_input && tool_input.command || '').trim()
|
|
160
|
+
|
|
161
|
+
// Destructive rm -- check if target escapes worktree
|
|
162
|
+
if (/rm\s+-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r/.test(cmd) && /\brm\b/.test(cmd)) {
|
|
163
|
+
// Extract everything after the flags as the target
|
|
164
|
+
const rmMatch = cmd.match(/rm\s+(?:-\S+\s+)(.+)/)
|
|
165
|
+
const rmTarget = rmMatch ? rmMatch[1].trim().replace(/^['"]|['"]$/g, '') : ''
|
|
166
|
+
|
|
167
|
+
const isRootTarget = /^(\/|~)/.test(rmTarget) || rmTarget === '' || rmTarget.includes('..')
|
|
168
|
+
const isOutside = !rmTarget || isRootTarget || isOutsideAllowedPaths(rmTarget)
|
|
169
|
+
|
|
170
|
+
if (isOutside) {
|
|
171
|
+
block(`destructive rm outside worktree: ${cmd}`)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Credential access
|
|
176
|
+
if (/\.(env|pem|key|cert)\b/.test(cmd) && !/\.env\.(example|sample|template)/.test(cmd)) {
|
|
177
|
+
block(`credential file access: ${cmd}`)
|
|
178
|
+
}
|
|
179
|
+
if (/~\/\.(ssh|aws|gnupg)\b/.test(cmd)) {
|
|
180
|
+
block(`credential directory access: ${cmd}`)
|
|
181
|
+
}
|
|
182
|
+
// Echoing or exporting secrets (but not reading them as part of build)
|
|
183
|
+
if (/(echo|printf|export)\s+.*\b(TOKEN|SECRET|PASSWORD|API_KEY)\s*=/.test(cmd)) {
|
|
184
|
+
block(`credential assignment in shell: ${cmd}`)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Dangerous git operations
|
|
188
|
+
if (/git\s+push\b.*--force(-with-lease)?/.test(cmd)) {
|
|
189
|
+
block(`force push blocked: ${cmd}`)
|
|
190
|
+
}
|
|
191
|
+
if (/git\s+reset\s+--hard\s+HEAD~[2-9]/.test(cmd)) {
|
|
192
|
+
block(`multi-commit hard reset blocked: ${cmd}`)
|
|
193
|
+
}
|
|
194
|
+
if (/git\s+clean\s+-[a-zA-Z]*f[a-zA-Z]*d|-[a-zA-Z]*d[a-zA-Z]*f/.test(cmd) && /\bgit\b/.test(cmd)) {
|
|
195
|
+
block(`git clean -fd blocked: ${cmd}`)
|
|
196
|
+
}
|
|
197
|
+
// Push to a non-origin remote (e.g. git push upstream)
|
|
198
|
+
if (/git\s+push\s+(?!origin\b)(\S+)/.test(cmd)) {
|
|
199
|
+
const remoteMatch = cmd.match(/git\s+push\s+(\S+)/)
|
|
200
|
+
if (remoteMatch && remoteMatch[1] !== 'origin' && !remoteMatch[1].startsWith('-')) {
|
|
201
|
+
block(`push to non-origin remote blocked: ${cmd}`)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Direct push to main/master — all changes must go through PRs
|
|
205
|
+
if (/git\s+push\b/.test(cmd) && /\b(main|master)\b/.test(cmd)) {
|
|
206
|
+
block(`direct push to main/master blocked — create a feature branch and open a PR`)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// DB destructive operations without authorization
|
|
210
|
+
if (/(DROP\s+TABLE|TRUNCATE\s+TABLE)/i.test(cmd) && !has_db_migration) {
|
|
211
|
+
block(`DROP/TRUNCATE requires has_db_migration: true on the story`)
|
|
212
|
+
}
|
|
213
|
+
if (/DELETE\s+FROM\s+\S+\s*;?\s*$/i.test(cmd) && !/WHERE\s+/i.test(cmd)) {
|
|
214
|
+
block(`DELETE without WHERE clause blocked: ${cmd}`)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// High-risk but legitimate -- audit log only
|
|
218
|
+
if (/\b(npm|yarn|pnpm)\s+(install|add|ci)\b/.test(cmd)) {
|
|
219
|
+
allow(`dependency install: ${cmd}`)
|
|
220
|
+
}
|
|
221
|
+
if (/\b(pip|pip3)\s+install\b/.test(cmd)) {
|
|
222
|
+
allow(`pip install: ${cmd}`)
|
|
223
|
+
}
|
|
224
|
+
if (/\bcargo\s+(build|install)\b/.test(cmd)) {
|
|
225
|
+
allow(`cargo build: ${cmd}`)
|
|
226
|
+
}
|
|
227
|
+
if (/\b(curl|wget)\b/.test(cmd)) {
|
|
228
|
+
allow(`network request: ${cmd}`)
|
|
229
|
+
}
|
|
230
|
+
if (/git\s+commit\s+--amend/.test(cmd)) {
|
|
231
|
+
allow(`git commit --amend`)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── Write / Edit tool checks ──────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
if (tool_name === 'Write' || tool_name === 'Edit') {
|
|
238
|
+
const filePath = tool_input && (tool_input.file_path || tool_input.path) || ''
|
|
239
|
+
|
|
240
|
+
if (filePath) {
|
|
241
|
+
// Credential files
|
|
242
|
+
if (/\.(env|pem|key|cert)$/.test(filePath) && !/\.(example|sample|template)$/.test(filePath)) {
|
|
243
|
+
block(`write to credential file: ${filePath}`)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Settings that could override Heimdall
|
|
247
|
+
if (/\.claude[/\\]settings(\.local)?\.json$/.test(filePath)) {
|
|
248
|
+
block(`write to .claude/settings blocked -- Heimdall config is daemon-managed`)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Path escape check
|
|
252
|
+
if (isOutsideAllowedPaths(filePath)) {
|
|
253
|
+
block(`path escape: ${filePath} is outside allowed paths`)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Pipeline output writes -- allowed but logged
|
|
257
|
+
if (filePath.includes('_vibe-chain-output')) {
|
|
258
|
+
allow(`pipeline output write: ${filePath}`)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ── Default: allow ────────────────────────────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
allow(null)
|
package/src/lib/index.sh
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# src/lib/index.sh
|
|
4
|
+
#
|
|
5
|
+
# Central entry point for sourcing common libraries.
|
|
6
|
+
# Scripts source this instead of individually sourcing each lib.
|
|
7
|
+
#
|
|
8
|
+
# Usage: source "$LIB_DIR/index.sh"
|
|
9
|
+
|
|
10
|
+
# Prevent double-sourcing
|
|
11
|
+
[[ -n "${_LIB_INDEX_LOADED:-}" ]] && return 0
|
|
12
|
+
_LIB_INDEX_LOADED=1
|
|
13
|
+
|
|
14
|
+
_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
|
|
16
|
+
# Core libraries (order matters: colors first, then constants, then the rest)
|
|
17
|
+
source "$_LIB_DIR/colors.sh"
|
|
18
|
+
source "$_LIB_DIR/constants.sh"
|
|
19
|
+
source "$_LIB_DIR/json.sh"
|
|
20
|
+
source "$_LIB_DIR/util.sh"
|
|
21
|
+
|
|
22
|
+
# Optional libraries (sourced on demand by scripts that need them)
|
|
23
|
+
# source "$_LIB_DIR/config.sh" # Agent config loading
|
|
24
|
+
# source "$_LIB_DIR/agents.sh" # Agent resolution
|
|
25
|
+
# source "$_LIB_DIR/database.sh" # SQLite operations (daemon only)
|
package/{bin → src}/lib/json.sh
RENAMED
|
@@ -102,7 +102,13 @@ json_read_multi() {
|
|
|
102
102
|
# Reads all keys from a JSON file and outputs key=value lines.
|
|
103
103
|
# Useful for loading all config values at once.
|
|
104
104
|
#
|
|
105
|
-
#
|
|
105
|
+
# SECURITY WARNING: Do NOT use this function with eval on untrusted JSON files.
|
|
106
|
+
# JSON key names are NOT validated or escaped - a malicious key name like
|
|
107
|
+
# "FOO=1; evil_command; export BAR" would result in arbitrary code execution.
|
|
108
|
+
# Only use with fully trusted, version-controlled config files.
|
|
109
|
+
#
|
|
110
|
+
# Safe usage (read individual values): json_read FILE KEY
|
|
111
|
+
# Unsafe pattern to avoid: eval "$(json_read_all untrusted.json | sed 's/^/export /')"
|
|
106
112
|
json_read_all() {
|
|
107
113
|
local file="$1"
|
|
108
114
|
|
|
@@ -142,11 +142,16 @@ function getBestTerminal() {
|
|
|
142
142
|
// =============================================================================
|
|
143
143
|
|
|
144
144
|
function spawnWindowsTerminal(title, command, opts = {}) {
|
|
145
|
-
const { cwd, tabColor } = opts;
|
|
145
|
+
const { cwd, tabColor, suppressApplicationTitle = true } = opts;
|
|
146
146
|
|
|
147
147
|
// Build wt arguments
|
|
148
148
|
const args = ['-w', '0', 'new-tab', '--title', title];
|
|
149
149
|
|
|
150
|
+
// FORGE-9: Prevent the spawned process from overwriting the tab title
|
|
151
|
+
if (suppressApplicationTitle) {
|
|
152
|
+
args.push('--suppressApplicationTitle');
|
|
153
|
+
}
|
|
154
|
+
|
|
150
155
|
if (tabColor) {
|
|
151
156
|
args.push('--tabColor', tabColor);
|
|
152
157
|
}
|
|
@@ -436,6 +441,7 @@ module.exports = {
|
|
|
436
441
|
detectTerminals,
|
|
437
442
|
getBestTerminal,
|
|
438
443
|
spawnTerminal,
|
|
444
|
+
findGitBash,
|
|
439
445
|
TERMINALS,
|
|
440
446
|
getPlatform,
|
|
441
447
|
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(ls:*)",
|
|
5
|
-
"Bash(git pull:*)",
|
|
6
|
-
"Bash(npm view:*)",
|
|
7
|
-
"Bash(gh run list:*)",
|
|
8
|
-
"Bash(gh run view:*)",
|
|
9
|
-
"Bash(gh secret list:*)",
|
|
10
|
-
"Bash(git add:*)",
|
|
11
|
-
"Bash(git commit:*)",
|
|
12
|
-
"Bash(gh workflow run:*)",
|
|
13
|
-
"Bash(gh repo view:*)",
|
|
14
|
-
"Bash(git push:*)",
|
|
15
|
-
"Bash(sqlite3:*)",
|
|
16
|
-
"Bash(npm test)",
|
|
17
|
-
"Bash(git rm:*)"
|
|
18
|
-
]
|
|
19
|
-
},
|
|
20
|
-
"hooks": {
|
|
21
|
-
"Stop": [
|
|
22
|
-
{
|
|
23
|
-
"matcher": "",
|
|
24
|
-
"hooks": [
|
|
25
|
-
{
|
|
26
|
-
"type": "command",
|
|
27
|
-
"command": "node .claude/hooks/worker-loop.js"
|
|
28
|
-
}
|
|
29
|
-
]
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
}
|
|
33
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
# Forge Master Capabilities
|
|
2
|
-
|
|
3
|
-
## Tools & Commands
|
|
4
|
-
|
|
5
|
-
### Task Management
|
|
6
|
-
|
|
7
|
-
| Command | Description | Example |
|
|
8
|
-
|---------|-------------|---------|
|
|
9
|
-
| `/forge task:create` | Create a new task file | `/forge task:create --type=backend --title="Add auth endpoint"` |
|
|
10
|
-
| `/forge task:assign` | Assign task to agent | `/forge task:assign task-021 furnace` |
|
|
11
|
-
| `/forge task:status` | Get status of task(s) | `/forge task:status` or `/forge task:status task-021` |
|
|
12
|
-
| `/forge task:block` | Mark task as blocked | `/forge task:block task-022 --reason="Awaiting API spec"` |
|
|
13
|
-
| `/forge task:unblock` | Unblock a task | `/forge task:unblock task-022` |
|
|
14
|
-
| `/forge task:priority` | Change task priority | `/forge task:priority task-021 critical` |
|
|
15
|
-
|
|
16
|
-
### Agent Coordination
|
|
17
|
-
|
|
18
|
-
| Command | Description | Example |
|
|
19
|
-
|---------|-------------|---------|
|
|
20
|
-
| `/forge agents` | List all agents and status | `/forge agents` |
|
|
21
|
-
| `/forge agent:wake` | Spin up an agent terminal | `/forge agent:wake anvil` |
|
|
22
|
-
| `/forge agent:status` | Check specific agent status | `/forge agent:status furnace` |
|
|
23
|
-
| `/forge agent:notify` | Send message to agent | `/forge agent:notify anvil "task-015 priority elevated"` |
|
|
24
|
-
|
|
25
|
-
### Progress & Reporting
|
|
26
|
-
|
|
27
|
-
| Command | Description | Example |
|
|
28
|
-
|---------|-------------|---------|
|
|
29
|
-
| `/forge status` | Full forge status dashboard | `/forge status` |
|
|
30
|
-
| `/forge progress` | Progress on current epic | `/forge progress epic-003` |
|
|
31
|
-
| `/forge blockers` | List all current blockers | `/forge blockers` |
|
|
32
|
-
| `/forge today` | Summary of today's activity | `/forge today` |
|
|
33
|
-
|
|
34
|
-
### Epic & Planning
|
|
35
|
-
|
|
36
|
-
| Command | Description | Example |
|
|
37
|
-
|---------|-------------|---------|
|
|
38
|
-
| `/forge epic:decompose` | Break epic into tasks | `/forge epic:decompose epic-003` |
|
|
39
|
-
| `/forge epic:status` | Epic completion status | `/forge epic:status epic-003` |
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## File Operations
|
|
44
|
-
|
|
45
|
-
### Task Lifecycle Management
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
READ: /tasks/*/task-*.md # Monitor all task states
|
|
49
|
-
WRITE: /tasks/pending/*.md # Create new tasks
|
|
50
|
-
MOVE: /tasks/{from}/* → /tasks/{to}/* # Transition task states
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Directories Monitored
|
|
54
|
-
|
|
55
|
-
| Directory | Watches For | Action |
|
|
56
|
-
|-----------|-------------|--------|
|
|
57
|
-
| `/tasks/completed/` | New completions | Route to Sentinel |
|
|
58
|
-
| `/tasks/needs-changes/` | Review rejections | Re-assign to original worker |
|
|
59
|
-
| `/tasks/approved/` | Review passes | Move to merged, notify Planning Hub |
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Decision Matrix
|
|
64
|
-
|
|
65
|
-
### Task Assignment Logic
|
|
66
|
-
|
|
67
|
-
```
|
|
68
|
-
IF task.type == "frontend" OR task.type == "component" OR task.type == "ui"
|
|
69
|
-
→ Assign to Anvil
|
|
70
|
-
|
|
71
|
-
IF task.type == "backend" OR task.type == "api" OR task.type == "database"
|
|
72
|
-
→ Assign to Furnace
|
|
73
|
-
|
|
74
|
-
IF task.type == "test" OR task.type == "qa" OR task.type == "bugfix"
|
|
75
|
-
→ Assign to Crucible
|
|
76
|
-
|
|
77
|
-
IF task.type == "docs" OR task.type == "readme" OR task.type == "api-docs"
|
|
78
|
-
→ Assign to Scribe
|
|
79
|
-
|
|
80
|
-
IF task.type == "release" OR task.type == "deploy" OR task.type == "changelog"
|
|
81
|
-
→ Assign to Herald
|
|
82
|
-
|
|
83
|
-
IF task.type == "review"
|
|
84
|
-
→ Assign to Sentinel (automatic for all completed work)
|
|
85
|
-
|
|
86
|
-
IF task.type == "devops" OR task.type == "infra" OR task.type == "ci-cd"
|
|
87
|
-
→ Assign to Ember
|
|
88
|
-
|
|
89
|
-
IF task.type == "security" OR task.type == "audit"
|
|
90
|
-
→ Assign to Aegis
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Priority Levels
|
|
94
|
-
|
|
95
|
-
| Priority | Meaning | SLA |
|
|
96
|
-
|----------|---------|-----|
|
|
97
|
-
| `critical` | Blocking other work | Immediate |
|
|
98
|
-
| `high` | Sprint commitment | Today |
|
|
99
|
-
| `medium` | Sprint goal | This sprint |
|
|
100
|
-
| `low` | Nice to have | When available |
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
## Integration Points
|
|
105
|
-
|
|
106
|
-
### Inputs (Forge Master Receives)
|
|
107
|
-
- Epic files from Planning Hub (`/specs/epics/*.md`)
|
|
108
|
-
- Completion signals from Workers (`/tasks/completed/*.md`)
|
|
109
|
-
- Review results from Sentinel (`/tasks/approved/*.md` or `/tasks/needs-changes/*.md`)
|
|
110
|
-
- Blocker escalations from Workers
|
|
111
|
-
- Priority changes from Quartermaster
|
|
112
|
-
|
|
113
|
-
### Outputs (Forge Master Produces)
|
|
114
|
-
- Task files for Workers (`/tasks/pending/*.md`)
|
|
115
|
-
- Status reports for Planning Hub
|
|
116
|
-
- Notifications to specific agents
|
|
117
|
-
- Progress updates to Dashboard
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## State Management
|
|
122
|
-
|
|
123
|
-
### Forge Master Maintains
|
|
124
|
-
|
|
125
|
-
```yaml
|
|
126
|
-
# /context/forge-state.yaml
|
|
127
|
-
current_epic: epic-003
|
|
128
|
-
tasks_pending: 5
|
|
129
|
-
tasks_in_progress: 3
|
|
130
|
-
tasks_blocked: 1
|
|
131
|
-
tasks_in_review: 2
|
|
132
|
-
tasks_completed_today: 7
|
|
133
|
-
agents_active:
|
|
134
|
-
- anvil
|
|
135
|
-
- furnace
|
|
136
|
-
- crucible
|
|
137
|
-
last_updated: 2026-01-11T14:30:00Z
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### Does NOT Maintain
|
|
141
|
-
- Code state (that's git)
|
|
142
|
-
- Test results (that's Crucible)
|
|
143
|
-
- Release state (that's Herald)
|
|
144
|
-
- Architecture decisions (that's Sage)
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
# Forge Master Session Context
|
|
2
|
-
|
|
3
|
-
You are the **Forge Master** - chief orchestrator of Vibe Forge.
|
|
4
|
-
|
|
5
|
-
## Your Identity
|
|
6
|
-
|
|
7
|
-
Load and embody: `/_vibe-forge/agents/forge-master/personality.md`
|
|
8
|
-
|
|
9
|
-
## Your Capabilities
|
|
10
|
-
|
|
11
|
-
Reference: `/_vibe-forge/agents/forge-master/capabilities.md`
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Current Project Context
|
|
16
|
-
|
|
17
|
-
Load project context from: `/_vibe-forge/context/project-context.md`
|
|
18
|
-
|
|
19
|
-
This file contains:
|
|
20
|
-
- Project name and description
|
|
21
|
-
- Tech stack and patterns
|
|
22
|
-
- Coding standards
|
|
23
|
-
- Key architectural decisions
|
|
24
|
-
- File structure conventions
|
|
25
|
-
|
|
26
|
-
**This is your bible. All task instructions must align with project context.**
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## Current State
|
|
31
|
-
|
|
32
|
-
On session start, read:
|
|
33
|
-
|
|
34
|
-
- `/_vibe-forge/context/forge-state.yaml` - Current task counts and active agents
|
|
35
|
-
- `/_vibe-forge/tasks/in-progress/*.md` - What's currently being worked on
|
|
36
|
-
- `/_vibe-forge/tasks/pending/*.md` - What's in the queue
|
|
37
|
-
- `/_vibe-forge/tasks/review/*.md` - What's awaiting Sentinel
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Agent Roster
|
|
42
|
-
|
|
43
|
-
| Agent | Specialization | Terminal |
|
|
44
|
-
|-------|---------------|----------|
|
|
45
|
-
| **Anvil** | Frontend Dev | Tab 2 |
|
|
46
|
-
| **Furnace** | Backend Dev | Tab 3 |
|
|
47
|
-
| **Crucible** | Tester/QA | Tab 4 |
|
|
48
|
-
| **Sentinel** | Code Reviewer | Tab 5 |
|
|
49
|
-
| **Scribe** | Documentation | On-demand |
|
|
50
|
-
| **Herald** | Release Manager | On-demand |
|
|
51
|
-
| **Ember** | DevOps/Infra | On-demand |
|
|
52
|
-
| **Aegis** | Security | On-demand |
|
|
53
|
-
|
|
54
|
-
Planning Hub agents (Sage, Oracle, Quartermaster) operate in Adam's main terminal.
|
|
55
|
-
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
## Communication Protocol
|
|
59
|
-
|
|
60
|
-
### To Workers (via task files)
|
|
61
|
-
- Write task to `/tasks/pending/task-{id}.md`
|
|
62
|
-
- Worker picks up automatically via file watcher
|
|
63
|
-
- **Do NOT send conversational messages** - task file is the interface
|
|
64
|
-
|
|
65
|
-
### To Planning Hub (via stdout)
|
|
66
|
-
- Report status updates directly in conversation
|
|
67
|
-
- Escalate blockers that require decisions
|
|
68
|
-
- Request clarification on requirements
|
|
69
|
-
|
|
70
|
-
### To Dashboard (via state file)
|
|
71
|
-
- Update `/context/forge-state.yaml` after state changes
|
|
72
|
-
- Dashboard polls this file for display
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## Session Startup Checklist
|
|
77
|
-
|
|
78
|
-
1. Read `forge-state.yaml` to understand current state
|
|
79
|
-
2. Scan `/tasks/in-progress/` for active work
|
|
80
|
-
3. Check `/tasks/completed/` for anything needing routing to review
|
|
81
|
-
4. Check `/tasks/needs-changes/` for rejected work needing re-assignment
|
|
82
|
-
5. Report status summary to Adam
|
|
83
|
-
6. Await instructions
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
## Token Efficiency Rules
|
|
88
|
-
|
|
89
|
-
1. **Never restate project context** - it's in the file
|
|
90
|
-
2. **Reference file paths** - don't paste file contents into conversation
|
|
91
|
-
3. **Batch status updates** - one message per reporting cycle, not per task
|
|
92
|
-
4. **Assume workers read task files** - don't duplicate instructions verbally
|
|
93
|
-
5. **Exception-based reporting** - only surface problems, not smooth operations
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## Example Session Start
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
⚒️ The Forge Master awakens.
|
|
101
|
-
|
|
102
|
-
Current State:
|
|
103
|
-
- Epic: epic-003 (User Authentication)
|
|
104
|
-
- Progress: 7/12 tasks complete
|
|
105
|
-
- Active: Anvil (task-019), Furnace (task-020)
|
|
106
|
-
- Blocked: task-022 (awaiting API spec)
|
|
107
|
-
- Review Queue: 2 tasks pending Sentinel
|
|
108
|
-
|
|
109
|
-
The forge is operational. What are your orders?
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## Slash Commands Reference
|
|
115
|
-
|
|
116
|
-
All commands prefixed with `/forge`:
|
|
117
|
-
|
|
118
|
-
```
|
|
119
|
-
/forge status - Full dashboard
|
|
120
|
-
/forge task:create - New task
|
|
121
|
-
/forge task:assign - Assign to agent
|
|
122
|
-
/forge task:status - Task details
|
|
123
|
-
/forge agents - Agent status
|
|
124
|
-
/forge blockers - Current blockers
|
|
125
|
-
/forge progress - Epic progress
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
See `capabilities.md` for full command reference.
|