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.
- package/.claude/commands/configure-vcs.md +102 -102
- package/.claude/commands/forge.md +218 -218
- package/.claude/hooks/worker-loop.js +220 -217
- package/.claude/settings.json +89 -89
- package/README.md +149 -191
- package/agents/aegis/personality.md +303 -303
- package/agents/anvil/personality.md +278 -278
- package/agents/architect/personality.md +260 -260
- package/agents/crucible/personality.md +362 -362
- package/agents/crucible-x/personality.md +210 -210
- package/agents/ember/personality.md +293 -293
- package/agents/flux/personality.md +248 -248
- package/agents/furnace/personality.md +342 -342
- package/agents/herald/personality.md +249 -249
- package/agents/oracle/personality.md +284 -284
- package/agents/pixel/personality.md +140 -140
- package/agents/planning-hub/personality.md +473 -473
- package/agents/scribe/personality.md +253 -253
- package/agents/slag/personality.md +268 -268
- package/agents/temper/personality.md +270 -270
- package/bin/cli.js +372 -372
- package/bin/forge-daemon.sh +477 -477
- package/bin/forge-setup.sh +662 -661
- package/bin/forge-spawn.sh +164 -164
- package/bin/forge.sh +566 -566
- package/docs/commands.md +8 -8
- package/package.json +77 -77
- package/{bin → src}/lib/agents.sh +177 -177
- package/{bin → src}/lib/check-aliases.js +50 -50
- package/{bin → src}/lib/colors.sh +45 -44
- package/{bin → src}/lib/config.sh +347 -347
- package/{bin → src}/lib/constants.sh +241 -241
- package/{bin → src}/lib/daemon/budgets.sh +107 -107
- package/{bin → src}/lib/daemon/dependencies.sh +146 -146
- package/{bin → src}/lib/daemon/display.sh +128 -128
- package/{bin → src}/lib/daemon/notifications.sh +273 -273
- package/{bin → src}/lib/daemon/routing.sh +93 -93
- package/{bin → src}/lib/daemon/state.sh +163 -163
- package/{bin → src}/lib/daemon/sync.sh +103 -103
- package/{bin → src}/lib/database.sh +357 -357
- package/{bin → src}/lib/frontmatter.js +106 -106
- package/{bin → src}/lib/heimdall-setup.js +113 -113
- package/{bin → src}/lib/heimdall.js +265 -265
- package/src/lib/index.sh +25 -0
- package/{bin → src}/lib/json.sh +264 -264
- package/{bin → src}/lib/terminal.js +452 -452
- package/{bin → src}/lib/util.sh +126 -126
- package/{bin → src}/lib/vcs.js +349 -349
- package/{context → templates}/project-context-template.md +122 -122
- package/config/task-template.md +0 -159
- package/config/templates/handoff-template.md +0 -40
package/{bin → src}/lib/json.sh
RENAMED
|
@@ -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
|
+
}
|