vibe-forge 0.8.1 → 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.
Files changed (51) hide show
  1. package/.claude/commands/configure-vcs.md +102 -102
  2. package/.claude/commands/forge.md +218 -218
  3. package/.claude/hooks/worker-loop.js +220 -217
  4. package/.claude/settings.json +89 -89
  5. package/README.md +149 -191
  6. package/agents/aegis/personality.md +303 -303
  7. package/agents/anvil/personality.md +278 -278
  8. package/agents/architect/personality.md +260 -260
  9. package/agents/crucible/personality.md +362 -362
  10. package/agents/crucible-x/personality.md +210 -210
  11. package/agents/ember/personality.md +293 -293
  12. package/agents/flux/personality.md +248 -248
  13. package/agents/furnace/personality.md +342 -342
  14. package/agents/herald/personality.md +249 -249
  15. package/agents/oracle/personality.md +284 -284
  16. package/agents/pixel/personality.md +140 -140
  17. package/agents/planning-hub/personality.md +473 -473
  18. package/agents/scribe/personality.md +253 -253
  19. package/agents/slag/personality.md +268 -268
  20. package/agents/temper/personality.md +270 -270
  21. package/bin/cli.js +372 -372
  22. package/bin/forge-daemon.sh +477 -477
  23. package/bin/forge-setup.sh +662 -661
  24. package/bin/forge-spawn.sh +164 -164
  25. package/bin/forge.sh +566 -566
  26. package/docs/commands.md +8 -8
  27. package/package.json +77 -77
  28. package/{bin → src}/lib/agents.sh +177 -177
  29. package/{bin → src}/lib/check-aliases.js +50 -50
  30. package/{bin → src}/lib/colors.sh +45 -44
  31. package/{bin → src}/lib/config.sh +347 -347
  32. package/{bin → src}/lib/constants.sh +241 -241
  33. package/{bin → src}/lib/daemon/budgets.sh +107 -107
  34. package/{bin → src}/lib/daemon/dependencies.sh +146 -146
  35. package/{bin → src}/lib/daemon/display.sh +128 -128
  36. package/{bin → src}/lib/daemon/notifications.sh +273 -273
  37. package/{bin → src}/lib/daemon/routing.sh +93 -93
  38. package/{bin → src}/lib/daemon/state.sh +163 -163
  39. package/{bin → src}/lib/daemon/sync.sh +103 -103
  40. package/{bin → src}/lib/database.sh +357 -357
  41. package/{bin → src}/lib/frontmatter.js +106 -106
  42. package/{bin → src}/lib/heimdall-setup.js +113 -113
  43. package/{bin → src}/lib/heimdall.js +265 -265
  44. package/src/lib/index.sh +25 -0
  45. package/{bin → src}/lib/json.sh +264 -264
  46. package/{bin → src}/lib/terminal.js +452 -452
  47. package/{bin → src}/lib/util.sh +126 -126
  48. package/{bin → src}/lib/vcs.js +349 -349
  49. package/{context → templates}/project-context-template.md +122 -122
  50. package/config/task-template.md +0 -159
  51. package/config/templates/handoff-template.md +0 -40
@@ -1,264 +1,264 @@
1
- #!/usr/bin/env bash
2
- #
3
- # Vibe Forge - JSON Utilities (Node.js based)
4
- #
5
- # Provides safe JSON operations using Node.js instead of jq.
6
- # This standardizes JSON handling across the codebase and removes
7
- # the implicit jq dependency.
8
- #
9
- # SECURITY: All functions pass values as command-line arguments,
10
- # never interpolated into the Node.js code.
11
- #
12
-
13
- # =============================================================================
14
- # JSON Reading Functions
15
- # =============================================================================
16
-
17
- # json_read FILE KEY [DEFAULT]
18
- # Reads a single key from a JSON file.
19
- # Returns the value or default (empty string if not provided).
20
- #
21
- # Example: value=$(json_read config.json "name" "unknown")
22
- json_read() {
23
- local file="$1"
24
- local key="$2"
25
- local default="${3:-}"
26
-
27
- if [[ ! -f "$file" ]]; then
28
- echo "$default"
29
- return 1
30
- fi
31
-
32
- local result
33
- result=$(node -e '
34
- const fs = require("fs");
35
- const file = process.argv[1];
36
- const key = process.argv[2];
37
- const defaultVal = process.argv[3] || "";
38
- try {
39
- const data = JSON.parse(fs.readFileSync(file, "utf8"));
40
- const value = data[key];
41
- if (value !== undefined && value !== null) {
42
- console.log(String(value));
43
- } else {
44
- console.log(defaultVal);
45
- }
46
- } catch (e) {
47
- console.log(defaultVal);
48
- process.exit(1);
49
- }
50
- ' -- "$file" "$key" "$default" 2>/dev/null)
51
-
52
- echo "$result"
53
- }
54
-
55
- # json_read_multi FILE KEY1 KEY2 ...
56
- # Reads multiple keys from a JSON file efficiently (single Node.js call).
57
- # Outputs tab-separated values in the order of keys provided.
58
- # Missing keys return empty string.
59
- #
60
- # Example: read -r name status task <<< "$(json_read_multi file.json name status task)"
61
- json_read_multi() {
62
- local file="$1"
63
- shift
64
- local keys=("$@")
65
-
66
- if [[ ! -f "$file" ]]; then
67
- # Output empty strings for each key
68
- local empty=""
69
- for ((i=0; i<${#keys[@]}; i++)); do
70
- empty+="\t"
71
- done
72
- echo "${empty:1}" # Remove leading tab
73
- return 1
74
- fi
75
-
76
- # Pass keys as JSON array for clean parsing
77
- local keys_json
78
- keys_json=$(printf '%s\n' "${keys[@]}" | node -e '
79
- const lines = require("fs").readFileSync(0, "utf8").trim().split("\n");
80
- console.log(JSON.stringify(lines));
81
- ')
82
-
83
- node -e '
84
- const fs = require("fs");
85
- const file = process.argv[1];
86
- const keys = JSON.parse(process.argv[2]);
87
- try {
88
- const data = JSON.parse(fs.readFileSync(file, "utf8"));
89
- const values = keys.map(k => {
90
- const v = data[k];
91
- return (v !== undefined && v !== null) ? String(v) : "";
92
- });
93
- console.log(values.join("\t"));
94
- } catch (e) {
95
- console.log(keys.map(() => "").join("\t"));
96
- process.exit(1);
97
- }
98
- ' -- "$file" "$keys_json" 2>/dev/null
99
- }
100
-
101
- # json_read_all FILE
102
- # Reads all keys from a JSON file and outputs key=value lines.
103
- # Useful for loading all config values at once.
104
- #
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 /')"
112
- json_read_all() {
113
- local file="$1"
114
-
115
- if [[ ! -f "$file" ]]; then
116
- return 1
117
- fi
118
-
119
- node -e '
120
- const fs = require("fs");
121
- const file = process.argv[1];
122
- try {
123
- const data = JSON.parse(fs.readFileSync(file, "utf8"));
124
- for (const [key, value] of Object.entries(data)) {
125
- if (typeof value !== "object") {
126
- // Escape for shell: single quotes, backslashes
127
- const escaped = String(value).replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
128
- console.log(`${key}="${escaped}"`);
129
- }
130
- }
131
- } catch (e) {
132
- process.exit(1);
133
- }
134
- ' -- "$file" 2>/dev/null
135
- }
136
-
137
- # =============================================================================
138
- # JSON Writing Functions
139
- # =============================================================================
140
-
141
- # json_write FILE KEY VALUE
142
- # Writes/updates a single key in a JSON file.
143
- # Creates the file if it does not exist.
144
- #
145
- # Example: json_write config.json "enabled" "true"
146
- json_write() {
147
- local file="$1"
148
- local key="$2"
149
- local value="$3"
150
-
151
- node -e '
152
- const fs = require("fs");
153
- const file = process.argv[1];
154
- const key = process.argv[2];
155
- const value = process.argv[3];
156
- let data = {};
157
- try {
158
- if (fs.existsSync(file)) {
159
- data = JSON.parse(fs.readFileSync(file, "utf8"));
160
- }
161
- } catch (e) {}
162
-
163
- // Try to parse value as boolean or number
164
- if (value === "true") {
165
- data[key] = true;
166
- } else if (value === "false") {
167
- data[key] = false;
168
- } else if (value === "null") {
169
- data[key] = null;
170
- } else if (!isNaN(value) && value.trim() !== "") {
171
- data[key] = Number(value);
172
- } else {
173
- data[key] = value;
174
- }
175
-
176
- fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
177
- ' -- "$file" "$key" "$value" 2>/dev/null
178
- }
179
-
180
- # json_write_bool FILE KEY BOOL_VALUE
181
- # Writes a boolean value (ensures proper true/false, not string).
182
- #
183
- # Example: json_write_bool config.json "enabled" true
184
- json_write_bool() {
185
- local file="$1"
186
- local key="$2"
187
- local value="$3"
188
-
189
- # Normalize to "true" or "false"
190
- case "$value" in
191
- true|1|on|yes|enabled) value="true" ;;
192
- *) value="false" ;;
193
- esac
194
-
195
- node -e '
196
- const fs = require("fs");
197
- const file = process.argv[1];
198
- const key = process.argv[2];
199
- const value = process.argv[3] === "true";
200
- let data = {};
201
- try {
202
- if (fs.existsSync(file)) {
203
- data = JSON.parse(fs.readFileSync(file, "utf8"));
204
- }
205
- } catch (e) {}
206
- data[key] = value;
207
- fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
208
- ' -- "$file" "$key" "$value" 2>/dev/null
209
- }
210
-
211
- # =============================================================================
212
- # JSON Display Functions
213
- # =============================================================================
214
-
215
- # json_pretty FILE
216
- # Pretty-prints a JSON file with 2-space indentation.
217
- #
218
- # Example: json_pretty config.json
219
- json_pretty() {
220
- local file="$1"
221
-
222
- if [[ ! -f "$file" ]]; then
223
- echo "{}"
224
- return 1
225
- fi
226
-
227
- node -e '
228
- const fs = require("fs");
229
- const file = process.argv[1];
230
- try {
231
- const data = JSON.parse(fs.readFileSync(file, "utf8"));
232
- console.log(JSON.stringify(data, null, 2));
233
- } catch (e) {
234
- console.error("Invalid JSON:", e.message);
235
- process.exit(1);
236
- }
237
- ' -- "$file" 2>/dev/null
238
- }
239
-
240
- # json_has_key FILE KEY
241
- # Checks if a key exists in a JSON file.
242
- # Returns 0 if key exists, 1 otherwise.
243
- #
244
- # Example: if json_has_key config.json "enabled"; then ...
245
- json_has_key() {
246
- local file="$1"
247
- local key="$2"
248
-
249
- if [[ ! -f "$file" ]]; then
250
- return 1
251
- fi
252
-
253
- node -e '
254
- const fs = require("fs");
255
- const file = process.argv[1];
256
- const key = process.argv[2];
257
- try {
258
- const data = JSON.parse(fs.readFileSync(file, "utf8"));
259
- process.exit(key in data ? 0 : 1);
260
- } catch (e) {
261
- process.exit(1);
262
- }
263
- ' -- "$file" "$key" 2>/dev/null
264
- }
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Vibe Forge - JSON Utilities (Node.js based)
4
+ #
5
+ # Provides safe JSON operations using Node.js instead of jq.
6
+ # This standardizes JSON handling across the codebase and removes
7
+ # the implicit jq dependency.
8
+ #
9
+ # SECURITY: All functions pass values as command-line arguments,
10
+ # never interpolated into the Node.js code.
11
+ #
12
+
13
+ # =============================================================================
14
+ # JSON Reading Functions
15
+ # =============================================================================
16
+
17
+ # json_read FILE KEY [DEFAULT]
18
+ # Reads a single key from a JSON file.
19
+ # Returns the value or default (empty string if not provided).
20
+ #
21
+ # Example: value=$(json_read config.json "name" "unknown")
22
+ json_read() {
23
+ local file="$1"
24
+ local key="$2"
25
+ local default="${3:-}"
26
+
27
+ if [[ ! -f "$file" ]]; then
28
+ echo "$default"
29
+ return 1
30
+ fi
31
+
32
+ local result
33
+ result=$(node -e '
34
+ const fs = require("fs");
35
+ const file = process.argv[1];
36
+ const key = process.argv[2];
37
+ const defaultVal = process.argv[3] || "";
38
+ try {
39
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
40
+ const value = data[key];
41
+ if (value !== undefined && value !== null) {
42
+ console.log(String(value));
43
+ } else {
44
+ console.log(defaultVal);
45
+ }
46
+ } catch (e) {
47
+ console.log(defaultVal);
48
+ process.exit(1);
49
+ }
50
+ ' -- "$file" "$key" "$default" 2>/dev/null)
51
+
52
+ echo "$result"
53
+ }
54
+
55
+ # json_read_multi FILE KEY1 KEY2 ...
56
+ # Reads multiple keys from a JSON file efficiently (single Node.js call).
57
+ # Outputs tab-separated values in the order of keys provided.
58
+ # Missing keys return empty string.
59
+ #
60
+ # Example: read -r name status task <<< "$(json_read_multi file.json name status task)"
61
+ json_read_multi() {
62
+ local file="$1"
63
+ shift
64
+ local keys=("$@")
65
+
66
+ if [[ ! -f "$file" ]]; then
67
+ # Output empty strings for each key
68
+ local empty=""
69
+ for ((i=0; i<${#keys[@]}; i++)); do
70
+ empty+="\t"
71
+ done
72
+ echo "${empty:1}" # Remove leading tab
73
+ return 1
74
+ fi
75
+
76
+ # Pass keys as JSON array for clean parsing
77
+ local keys_json
78
+ keys_json=$(printf '%s\n' "${keys[@]}" | node -e '
79
+ const lines = require("fs").readFileSync(0, "utf8").trim().split("\n");
80
+ console.log(JSON.stringify(lines));
81
+ ')
82
+
83
+ node -e '
84
+ const fs = require("fs");
85
+ const file = process.argv[1];
86
+ const keys = JSON.parse(process.argv[2]);
87
+ try {
88
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
89
+ const values = keys.map(k => {
90
+ const v = data[k];
91
+ return (v !== undefined && v !== null) ? String(v) : "";
92
+ });
93
+ console.log(values.join("\t"));
94
+ } catch (e) {
95
+ console.log(keys.map(() => "").join("\t"));
96
+ process.exit(1);
97
+ }
98
+ ' -- "$file" "$keys_json" 2>/dev/null
99
+ }
100
+
101
+ # json_read_all FILE
102
+ # Reads all keys from a JSON file and outputs key=value lines.
103
+ # Useful for loading all config values at once.
104
+ #
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 /')"
112
+ json_read_all() {
113
+ local file="$1"
114
+
115
+ if [[ ! -f "$file" ]]; then
116
+ return 1
117
+ fi
118
+
119
+ node -e '
120
+ const fs = require("fs");
121
+ const file = process.argv[1];
122
+ try {
123
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
124
+ for (const [key, value] of Object.entries(data)) {
125
+ if (typeof value !== "object") {
126
+ // Escape for shell: single quotes, backslashes
127
+ const escaped = String(value).replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
128
+ console.log(`${key}="${escaped}"`);
129
+ }
130
+ }
131
+ } catch (e) {
132
+ process.exit(1);
133
+ }
134
+ ' -- "$file" 2>/dev/null
135
+ }
136
+
137
+ # =============================================================================
138
+ # JSON Writing Functions
139
+ # =============================================================================
140
+
141
+ # json_write FILE KEY VALUE
142
+ # Writes/updates a single key in a JSON file.
143
+ # Creates the file if it does not exist.
144
+ #
145
+ # Example: json_write config.json "enabled" "true"
146
+ json_write() {
147
+ local file="$1"
148
+ local key="$2"
149
+ local value="$3"
150
+
151
+ node -e '
152
+ const fs = require("fs");
153
+ const file = process.argv[1];
154
+ const key = process.argv[2];
155
+ const value = process.argv[3];
156
+ let data = {};
157
+ try {
158
+ if (fs.existsSync(file)) {
159
+ data = JSON.parse(fs.readFileSync(file, "utf8"));
160
+ }
161
+ } catch (e) {}
162
+
163
+ // Try to parse value as boolean or number
164
+ if (value === "true") {
165
+ data[key] = true;
166
+ } else if (value === "false") {
167
+ data[key] = false;
168
+ } else if (value === "null") {
169
+ data[key] = null;
170
+ } else if (!isNaN(value) && value.trim() !== "") {
171
+ data[key] = Number(value);
172
+ } else {
173
+ data[key] = value;
174
+ }
175
+
176
+ fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
177
+ ' -- "$file" "$key" "$value" 2>/dev/null
178
+ }
179
+
180
+ # json_write_bool FILE KEY BOOL_VALUE
181
+ # Writes a boolean value (ensures proper true/false, not string).
182
+ #
183
+ # Example: json_write_bool config.json "enabled" true
184
+ json_write_bool() {
185
+ local file="$1"
186
+ local key="$2"
187
+ local value="$3"
188
+
189
+ # Normalize to "true" or "false"
190
+ case "$value" in
191
+ true|1|on|yes|enabled) value="true" ;;
192
+ *) value="false" ;;
193
+ esac
194
+
195
+ node -e '
196
+ const fs = require("fs");
197
+ const file = process.argv[1];
198
+ const key = process.argv[2];
199
+ const value = process.argv[3] === "true";
200
+ let data = {};
201
+ try {
202
+ if (fs.existsSync(file)) {
203
+ data = JSON.parse(fs.readFileSync(file, "utf8"));
204
+ }
205
+ } catch (e) {}
206
+ data[key] = value;
207
+ fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
208
+ ' -- "$file" "$key" "$value" 2>/dev/null
209
+ }
210
+
211
+ # =============================================================================
212
+ # JSON Display Functions
213
+ # =============================================================================
214
+
215
+ # json_pretty FILE
216
+ # Pretty-prints a JSON file with 2-space indentation.
217
+ #
218
+ # Example: json_pretty config.json
219
+ json_pretty() {
220
+ local file="$1"
221
+
222
+ if [[ ! -f "$file" ]]; then
223
+ echo "{}"
224
+ return 1
225
+ fi
226
+
227
+ node -e '
228
+ const fs = require("fs");
229
+ const file = process.argv[1];
230
+ try {
231
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
232
+ console.log(JSON.stringify(data, null, 2));
233
+ } catch (e) {
234
+ console.error("Invalid JSON:", e.message);
235
+ process.exit(1);
236
+ }
237
+ ' -- "$file" 2>/dev/null
238
+ }
239
+
240
+ # json_has_key FILE KEY
241
+ # Checks if a key exists in a JSON file.
242
+ # Returns 0 if key exists, 1 otherwise.
243
+ #
244
+ # Example: if json_has_key config.json "enabled"; then ...
245
+ json_has_key() {
246
+ local file="$1"
247
+ local key="$2"
248
+
249
+ if [[ ! -f "$file" ]]; then
250
+ return 1
251
+ fi
252
+
253
+ node -e '
254
+ const fs = require("fs");
255
+ const file = process.argv[1];
256
+ const key = process.argv[2];
257
+ try {
258
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
259
+ process.exit(key in data ? 0 : 1);
260
+ } catch (e) {
261
+ process.exit(1);
262
+ }
263
+ ' -- "$file" "$key" 2>/dev/null
264
+ }