@cpretzinger/boss-claude 1.0.0 → 1.0.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/README.md +304 -1
- package/bin/boss-claude.js +1138 -0
- package/bin/commands/mode.js +250 -0
- package/bin/onyx-guard.js +259 -0
- package/bin/onyx-guard.sh +251 -0
- package/bin/prompts.js +284 -0
- package/bin/rollback.js +85 -0
- package/bin/setup-wizard.js +492 -0
- package/config/.env.example +17 -0
- package/lib/README.md +83 -0
- package/lib/agent-logger.js +61 -0
- package/lib/agents/memory-engineers/github-memory-engineer.js +251 -0
- package/lib/agents/memory-engineers/postgres-memory-engineer.js +633 -0
- package/lib/agents/memory-engineers/qdrant-memory-engineer.js +358 -0
- package/lib/agents/memory-engineers/redis-memory-engineer.js +383 -0
- package/lib/agents/memory-supervisor.js +526 -0
- package/lib/agents/registry.js +135 -0
- package/lib/auto-monitor.js +131 -0
- package/lib/checkpoint-hook.js +112 -0
- package/lib/checkpoint.js +319 -0
- package/lib/commentator.js +213 -0
- package/lib/context-scribe.js +120 -0
- package/lib/delegation-strategies.js +326 -0
- package/lib/hierarchy-validator.js +643 -0
- package/lib/index.js +15 -0
- package/lib/init-with-mode.js +261 -0
- package/lib/init.js +44 -6
- package/lib/memory-result-aggregator.js +252 -0
- package/lib/memory.js +35 -7
- package/lib/mode-enforcer.js +473 -0
- package/lib/onyx-banner.js +169 -0
- package/lib/onyx-identity.js +214 -0
- package/lib/onyx-monitor.js +381 -0
- package/lib/onyx-reminder.js +188 -0
- package/lib/onyx-tool-interceptor.js +341 -0
- package/lib/onyx-wrapper.js +315 -0
- package/lib/orchestrator-gate.js +334 -0
- package/lib/output-formatter.js +296 -0
- package/lib/postgres.js +1 -1
- package/lib/prompt-injector.js +220 -0
- package/lib/prompts.js +532 -0
- package/lib/session.js +153 -6
- package/lib/setup/README.md +187 -0
- package/lib/setup/env-manager.js +785 -0
- package/lib/setup/error-recovery.js +630 -0
- package/lib/setup/explain-scopes.js +385 -0
- package/lib/setup/github-instructions.js +333 -0
- package/lib/setup/github-repo.js +254 -0
- package/lib/setup/import-credentials.js +498 -0
- package/lib/setup/index.js +62 -0
- package/lib/setup/init-postgres.js +785 -0
- package/lib/setup/init-redis.js +456 -0
- package/lib/setup/integration-test.js +652 -0
- package/lib/setup/progress.js +357 -0
- package/lib/setup/rollback.js +670 -0
- package/lib/setup/rollback.test.js +452 -0
- package/lib/setup/setup-with-rollback.example.js +351 -0
- package/lib/setup/summary.js +400 -0
- package/lib/setup/test-github-setup.js +10 -0
- package/lib/setup/test-postgres-init.js +98 -0
- package/lib/setup/verify-setup.js +102 -0
- package/lib/task-agent-worker.js +235 -0
- package/lib/token-monitor.js +466 -0
- package/lib/tool-wrapper-integration.js +369 -0
- package/lib/tool-wrapper.js +387 -0
- package/lib/validators/README.md +497 -0
- package/lib/validators/config.js +583 -0
- package/lib/validators/config.test.js +175 -0
- package/lib/validators/github.js +310 -0
- package/lib/validators/github.test.js +61 -0
- package/lib/validators/index.js +15 -0
- package/lib/validators/postgres.js +525 -0
- package/package.json +98 -13
- package/scripts/benchmark-memory.js +433 -0
- package/scripts/check-secrets.sh +12 -0
- package/scripts/fetch-todos.mjs +148 -0
- package/scripts/graceful-shutdown.sh +156 -0
- package/scripts/install-onyx-hooks.js +373 -0
- package/scripts/install.js +119 -18
- package/scripts/redis-monitor.js +284 -0
- package/scripts/redis-setup.js +412 -0
- package/scripts/test-memory-retrieval.js +201 -0
- package/scripts/validate-exports.js +68 -0
- package/scripts/validate-package.js +120 -0
- package/scripts/verify-onyx-deployment.js +309 -0
- package/scripts/verify-redis-deployment.js +354 -0
- package/scripts/verify-redis-init.js +219 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# ONYX GUARD - Pre-Hook Tool Interceptor
|
|
4
|
+
#
|
|
5
|
+
# Executable shell script that wraps ONYX agent execution
|
|
6
|
+
# and enforces delegation rules via JavaScript interceptor.
|
|
7
|
+
#
|
|
8
|
+
# USAGE:
|
|
9
|
+
# ./onyx-guard.sh --tool Read --file /path/to/file.js
|
|
10
|
+
# ./onyx-guard.sh --tool Bash --command "npm install"
|
|
11
|
+
# ./onyx-guard.sh --check-tool Read
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
# Colors for terminal output
|
|
17
|
+
RED='\033[0;31m'
|
|
18
|
+
GREEN='\033[0;32m'
|
|
19
|
+
YELLOW='\033[1;33m'
|
|
20
|
+
BLUE='\033[0;34m'
|
|
21
|
+
CYAN='\033[0;36m'
|
|
22
|
+
DIM='\033[2m'
|
|
23
|
+
RESET='\033[0m'
|
|
24
|
+
|
|
25
|
+
# Script directory
|
|
26
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
LIB_DIR="$(dirname "$SCRIPT_DIR")/lib"
|
|
28
|
+
|
|
29
|
+
# FORBIDDEN TOOLS for ONYX
|
|
30
|
+
FORBIDDEN_TOOLS=("Read" "Write" "Edit" "Bash" "Grep" "Glob" "NotebookEdit")
|
|
31
|
+
|
|
32
|
+
# ALLOWED TOOLS for ONYX
|
|
33
|
+
ALLOWED_TOOLS=("Task" "WebFetch" "WebSearch" "TodoWrite" "Skill")
|
|
34
|
+
|
|
35
|
+
# Check if tool is forbidden
|
|
36
|
+
is_forbidden_tool() {
|
|
37
|
+
local tool="$1"
|
|
38
|
+
for forbidden in "${FORBIDDEN_TOOLS[@]}"; do
|
|
39
|
+
if [[ "$tool" == "$forbidden" ]]; then
|
|
40
|
+
return 0
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
return 1
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Check if tool is allowed
|
|
47
|
+
is_allowed_tool() {
|
|
48
|
+
local tool="$1"
|
|
49
|
+
for allowed in "${ALLOWED_TOOLS[@]}"; do
|
|
50
|
+
if [[ "$tool" == "$allowed" ]]; then
|
|
51
|
+
return 0
|
|
52
|
+
fi
|
|
53
|
+
done
|
|
54
|
+
return 1
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Block forbidden tool and suggest delegation
|
|
58
|
+
block_tool() {
|
|
59
|
+
local tool="$1"
|
|
60
|
+
shift
|
|
61
|
+
local params="$*"
|
|
62
|
+
|
|
63
|
+
echo -e "${RED}"
|
|
64
|
+
echo "═══════════════════════════════════════════════════════════════════════════════"
|
|
65
|
+
echo "🚨 ONYX TOOL BLOCK TRIGGERED"
|
|
66
|
+
echo "═══════════════════════════════════════════════════════════════════════════════"
|
|
67
|
+
echo -e "${RESET}"
|
|
68
|
+
|
|
69
|
+
echo -e "${YELLOW}Agent:${RESET} ONYX"
|
|
70
|
+
echo -e "${YELLOW}Forbidden Tool:${RESET} ${RED}${tool}${RESET}"
|
|
71
|
+
echo -e "${YELLOW}Params:${RESET} ${DIM}${params}${RESET}"
|
|
72
|
+
|
|
73
|
+
echo ""
|
|
74
|
+
echo -e "${YELLOW}📋 ONYX DELEGATION RULE:${RESET}"
|
|
75
|
+
echo -e "${DIM} ONYX is META-BOSS and NEVER touches files directly${RESET}"
|
|
76
|
+
echo -e "${DIM} ONYX delegates to Task tool → Task tool uses file operations${RESET}"
|
|
77
|
+
echo -e "${RED} FORBIDDEN: ONYX → ${tool} (direct file access)${RESET}"
|
|
78
|
+
echo -e "${GREEN} ALLOWED: ONYX → Task → ${tool} (delegated access)${RESET}"
|
|
79
|
+
|
|
80
|
+
echo ""
|
|
81
|
+
echo -e "${CYAN}✅ DELEGATION MESSAGE:${RESET}"
|
|
82
|
+
|
|
83
|
+
case "$tool" in
|
|
84
|
+
Read)
|
|
85
|
+
echo -e "${GREEN}Use Task tool: \"Read the file at ${params}\"${RESET}"
|
|
86
|
+
;;
|
|
87
|
+
Write)
|
|
88
|
+
echo -e "${GREEN}Use Task tool: \"Write content to ${params}\"${RESET}"
|
|
89
|
+
;;
|
|
90
|
+
Edit)
|
|
91
|
+
echo -e "${GREEN}Use Task tool: \"Edit the file ${params}\"${RESET}"
|
|
92
|
+
;;
|
|
93
|
+
Bash)
|
|
94
|
+
echo -e "${GREEN}Use Task tool: \"Execute bash command: ${params}\"${RESET}"
|
|
95
|
+
;;
|
|
96
|
+
Grep)
|
|
97
|
+
echo -e "${GREEN}Use Task tool: \"Search for pattern ${params}\"${RESET}"
|
|
98
|
+
;;
|
|
99
|
+
Glob)
|
|
100
|
+
echo -e "${GREEN}Use Task tool: \"Find files matching pattern ${params}\"${RESET}"
|
|
101
|
+
;;
|
|
102
|
+
*)
|
|
103
|
+
echo -e "${GREEN}Use Task tool: \"Execute ${tool} with params ${params}\"${RESET}"
|
|
104
|
+
;;
|
|
105
|
+
esac
|
|
106
|
+
|
|
107
|
+
echo ""
|
|
108
|
+
echo -e "${RED}❌ TOOL CALL BLOCKED${RESET}"
|
|
109
|
+
echo -e "${YELLOW}⚠️ ONYX must delegate to Task tool instead${RESET}"
|
|
110
|
+
echo ""
|
|
111
|
+
|
|
112
|
+
exit 1
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# Allow tool with confirmation
|
|
116
|
+
allow_tool() {
|
|
117
|
+
local tool="$1"
|
|
118
|
+
echo -e "${GREEN}✅ Tool ${tool} is allowed for ONYX${RESET}"
|
|
119
|
+
exit 0
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Main guard logic
|
|
123
|
+
guard_tool() {
|
|
124
|
+
local tool="$1"
|
|
125
|
+
shift
|
|
126
|
+
local params="$*"
|
|
127
|
+
|
|
128
|
+
# Check if forbidden
|
|
129
|
+
if is_forbidden_tool "$tool"; then
|
|
130
|
+
block_tool "$tool" "$params"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# Check if allowed
|
|
134
|
+
if is_allowed_tool "$tool"; then
|
|
135
|
+
allow_tool "$tool"
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# Unknown tool - warn but allow
|
|
139
|
+
echo -e "${YELLOW}⚠️ Unknown tool for ONYX: ${tool}${RESET}"
|
|
140
|
+
echo -e "${DIM}Allowing by default - add to ALLOWED_TOOLS or FORBIDDEN_TOOLS${RESET}"
|
|
141
|
+
exit 0
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Check tool status without blocking
|
|
145
|
+
check_tool() {
|
|
146
|
+
local tool="$1"
|
|
147
|
+
|
|
148
|
+
if is_forbidden_tool "$tool"; then
|
|
149
|
+
echo -e "${RED}FORBIDDEN${RESET}: ${tool} (ONYX must delegate to Task)"
|
|
150
|
+
exit 1
|
|
151
|
+
elif is_allowed_tool "$tool"; then
|
|
152
|
+
echo -e "${GREEN}ALLOWED${RESET}: ${tool}"
|
|
153
|
+
exit 0
|
|
154
|
+
else
|
|
155
|
+
echo -e "${YELLOW}UNKNOWN${RESET}: ${tool}"
|
|
156
|
+
exit 2
|
|
157
|
+
fi
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
# Run JavaScript interceptor
|
|
161
|
+
run_js_interceptor() {
|
|
162
|
+
if [[ -f "$LIB_DIR/onyx-tool-interceptor.js" ]]; then
|
|
163
|
+
node -e "
|
|
164
|
+
import('$LIB_DIR/onyx-tool-interceptor.js').then(module => {
|
|
165
|
+
const interceptor = module.default;
|
|
166
|
+
interceptor.printReport();
|
|
167
|
+
});
|
|
168
|
+
"
|
|
169
|
+
else
|
|
170
|
+
echo -e "${YELLOW}⚠️ JavaScript interceptor not found${RESET}"
|
|
171
|
+
fi
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# Print help
|
|
175
|
+
print_help() {
|
|
176
|
+
cat << EOF
|
|
177
|
+
${CYAN}ONYX GUARD - Tool Interceptor for ONYX Agent${RESET}
|
|
178
|
+
|
|
179
|
+
USAGE:
|
|
180
|
+
onyx-guard.sh --tool <TOOL_NAME> [PARAMS...]
|
|
181
|
+
onyx-guard.sh --check-tool <TOOL_NAME>
|
|
182
|
+
onyx-guard.sh --list-forbidden
|
|
183
|
+
onyx-guard.sh --list-allowed
|
|
184
|
+
onyx-guard.sh --report
|
|
185
|
+
|
|
186
|
+
OPTIONS:
|
|
187
|
+
--tool <NAME> Guard a tool call (blocks forbidden tools)
|
|
188
|
+
--check-tool <NAME> Check if tool is allowed (no blocking)
|
|
189
|
+
--list-forbidden List all forbidden tools for ONYX
|
|
190
|
+
--list-allowed List all allowed tools for ONYX
|
|
191
|
+
--report Show JavaScript interceptor report
|
|
192
|
+
-h, --help Show this help message
|
|
193
|
+
|
|
194
|
+
EXAMPLES:
|
|
195
|
+
# Block Read tool (forbidden for ONYX)
|
|
196
|
+
onyx-guard.sh --tool Read --file /path/to/file.js
|
|
197
|
+
|
|
198
|
+
# Allow Task tool (allowed for ONYX)
|
|
199
|
+
onyx-guard.sh --tool Task --description "Search for files"
|
|
200
|
+
|
|
201
|
+
# Check if tool is allowed
|
|
202
|
+
onyx-guard.sh --check-tool Bash
|
|
203
|
+
|
|
204
|
+
FORBIDDEN TOOLS (ONYX must delegate):
|
|
205
|
+
Read, Write, Edit, Bash, Grep, Glob, NotebookEdit
|
|
206
|
+
|
|
207
|
+
ALLOWED TOOLS (ONYX can use directly):
|
|
208
|
+
Task, WebFetch, WebSearch, TodoWrite, Skill
|
|
209
|
+
|
|
210
|
+
DELEGATION RULE:
|
|
211
|
+
ONYX is META-BOSS and NEVER touches files directly.
|
|
212
|
+
ONYX delegates to Task tool → Task tool uses file operations.
|
|
213
|
+
|
|
214
|
+
EOF
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# Parse command line arguments
|
|
218
|
+
case "${1:-}" in
|
|
219
|
+
--tool)
|
|
220
|
+
shift
|
|
221
|
+
guard_tool "$@"
|
|
222
|
+
;;
|
|
223
|
+
--check-tool)
|
|
224
|
+
shift
|
|
225
|
+
check_tool "$1"
|
|
226
|
+
;;
|
|
227
|
+
--list-forbidden)
|
|
228
|
+
echo -e "${RED}FORBIDDEN TOOLS FOR ONYX:${RESET}"
|
|
229
|
+
for tool in "${FORBIDDEN_TOOLS[@]}"; do
|
|
230
|
+
echo -e " ${RED}✗${RESET} $tool"
|
|
231
|
+
done
|
|
232
|
+
;;
|
|
233
|
+
--list-allowed)
|
|
234
|
+
echo -e "${GREEN}ALLOWED TOOLS FOR ONYX:${RESET}"
|
|
235
|
+
for tool in "${ALLOWED_TOOLS[@]}"; do
|
|
236
|
+
echo -e " ${GREEN}✓${RESET} $tool"
|
|
237
|
+
done
|
|
238
|
+
;;
|
|
239
|
+
--report)
|
|
240
|
+
run_js_interceptor
|
|
241
|
+
;;
|
|
242
|
+
-h|--help)
|
|
243
|
+
print_help
|
|
244
|
+
;;
|
|
245
|
+
*)
|
|
246
|
+
echo -e "${RED}Error: Invalid arguments${RESET}"
|
|
247
|
+
echo ""
|
|
248
|
+
print_help
|
|
249
|
+
exit 1
|
|
250
|
+
;;
|
|
251
|
+
esac
|
package/bin/prompts.js
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive Prompts for Boss Claude Setup Wizard
|
|
3
|
+
* Beautiful CLI prompts using readline and chalk
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import readline from 'readline';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create readline interface
|
|
11
|
+
*/
|
|
12
|
+
function createInterface() {
|
|
13
|
+
return readline.createInterface({
|
|
14
|
+
input: process.stdin,
|
|
15
|
+
output: process.stdout
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Ask a question and get user input
|
|
21
|
+
*/
|
|
22
|
+
export function ask(question, defaultValue = '') {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
const rl = createInterface();
|
|
25
|
+
|
|
26
|
+
const prompt = defaultValue
|
|
27
|
+
? `${question} ${chalk.dim(`(${defaultValue})`)}: `
|
|
28
|
+
: `${question}: `;
|
|
29
|
+
|
|
30
|
+
rl.question(prompt, (answer) => {
|
|
31
|
+
rl.close();
|
|
32
|
+
resolve(answer.trim() || defaultValue);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Ask a yes/no question
|
|
39
|
+
*/
|
|
40
|
+
export function confirm(question, defaultYes = true) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
const rl = createInterface();
|
|
43
|
+
|
|
44
|
+
const hint = defaultYes ? chalk.dim('(Y/n)') : chalk.dim('(y/N)');
|
|
45
|
+
const prompt = `${question} ${hint}: `;
|
|
46
|
+
|
|
47
|
+
rl.question(prompt, (answer) => {
|
|
48
|
+
rl.close();
|
|
49
|
+
|
|
50
|
+
const normalized = answer.trim().toLowerCase();
|
|
51
|
+
|
|
52
|
+
if (!normalized) {
|
|
53
|
+
resolve(defaultYes);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
resolve(normalized === 'y' || normalized === 'yes');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Ask for password/secret (hidden input)
|
|
64
|
+
*/
|
|
65
|
+
export function askSecret(question) {
|
|
66
|
+
return new Promise((resolve) => {
|
|
67
|
+
const rl = createInterface();
|
|
68
|
+
|
|
69
|
+
// Disable echo for password input
|
|
70
|
+
const stdin = process.stdin;
|
|
71
|
+
const setRawMode = stdin.setRawMode;
|
|
72
|
+
|
|
73
|
+
let input = '';
|
|
74
|
+
|
|
75
|
+
rl.question(`${question}: `, () => {
|
|
76
|
+
// This won't be called due to raw mode
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (setRawMode) {
|
|
80
|
+
stdin.setRawMode(true);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
stdin.on('data', (char) => {
|
|
84
|
+
const str = char.toString('utf-8');
|
|
85
|
+
|
|
86
|
+
// Handle Enter key
|
|
87
|
+
if (str === '\n' || str === '\r' || str === '\r\n') {
|
|
88
|
+
stdin.removeAllListeners('data');
|
|
89
|
+
if (setRawMode) {
|
|
90
|
+
stdin.setRawMode(false);
|
|
91
|
+
}
|
|
92
|
+
rl.close();
|
|
93
|
+
process.stdout.write('\n');
|
|
94
|
+
resolve(input);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Handle Ctrl+C
|
|
99
|
+
if (str === '\u0003') {
|
|
100
|
+
process.exit();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Handle backspace
|
|
104
|
+
if (str === '\u007f' || str === '\b') {
|
|
105
|
+
if (input.length > 0) {
|
|
106
|
+
input = input.slice(0, -1);
|
|
107
|
+
process.stdout.write('\b \b');
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Add character to input and show asterisk
|
|
113
|
+
input += str;
|
|
114
|
+
process.stdout.write('*');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Show a spinner while executing async operation
|
|
121
|
+
*/
|
|
122
|
+
export async function spinner(message, asyncFn) {
|
|
123
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
124
|
+
let i = 0;
|
|
125
|
+
|
|
126
|
+
process.stdout.write(`${message} ${frames[0]}`);
|
|
127
|
+
|
|
128
|
+
const intervalId = setInterval(() => {
|
|
129
|
+
i = (i + 1) % frames.length;
|
|
130
|
+
process.stdout.write(`\r${message} ${chalk.cyan(frames[i])}`);
|
|
131
|
+
}, 80);
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const result = await asyncFn();
|
|
135
|
+
clearInterval(intervalId);
|
|
136
|
+
process.stdout.write(`\r${message} ${chalk.green('✓')}\n`);
|
|
137
|
+
return { success: true, result };
|
|
138
|
+
} catch (error) {
|
|
139
|
+
clearInterval(intervalId);
|
|
140
|
+
process.stdout.write(`\r${message} ${chalk.red('✗')}\n`);
|
|
141
|
+
return { success: false, error };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Display a section header
|
|
147
|
+
*/
|
|
148
|
+
export function header(title, description = '') {
|
|
149
|
+
console.log('\n' + chalk.bold.blue('━'.repeat(60)));
|
|
150
|
+
console.log(chalk.bold.white(title));
|
|
151
|
+
if (description) {
|
|
152
|
+
console.log(chalk.dim(description));
|
|
153
|
+
}
|
|
154
|
+
console.log(chalk.bold.blue('━'.repeat(60)) + '\n');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Display a success message
|
|
159
|
+
*/
|
|
160
|
+
export function success(message) {
|
|
161
|
+
console.log(chalk.green('✓') + ' ' + message);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Display an error message
|
|
166
|
+
*/
|
|
167
|
+
export function error(message, details = '') {
|
|
168
|
+
console.log(chalk.red('✗') + ' ' + message);
|
|
169
|
+
if (details) {
|
|
170
|
+
console.log(chalk.dim(' ' + details));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Display a warning message
|
|
176
|
+
*/
|
|
177
|
+
export function warning(message) {
|
|
178
|
+
console.log(chalk.yellow('⚠') + ' ' + message);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Display an info message
|
|
183
|
+
*/
|
|
184
|
+
export function info(message) {
|
|
185
|
+
console.log(chalk.blue('ℹ') + ' ' + message);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Display a list of items
|
|
190
|
+
*/
|
|
191
|
+
export function list(items, style = 'bullet') {
|
|
192
|
+
items.forEach((item, idx) => {
|
|
193
|
+
const prefix = style === 'numbered'
|
|
194
|
+
? chalk.dim(`${idx + 1}.`)
|
|
195
|
+
: chalk.dim('•');
|
|
196
|
+
console.log(` ${prefix} ${item}`);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Display a progress bar
|
|
202
|
+
*/
|
|
203
|
+
export function progress(current, total, label = '') {
|
|
204
|
+
const percentage = Math.round((current / total) * 100);
|
|
205
|
+
const filled = Math.round((current / total) * 30);
|
|
206
|
+
const empty = 30 - filled;
|
|
207
|
+
|
|
208
|
+
const bar = chalk.green('█'.repeat(filled)) + chalk.dim('░'.repeat(empty));
|
|
209
|
+
const text = label ? `${label} ` : '';
|
|
210
|
+
|
|
211
|
+
process.stdout.write(`\r${text}[${bar}] ${percentage}%`);
|
|
212
|
+
|
|
213
|
+
if (current === total) {
|
|
214
|
+
process.stdout.write('\n');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Clear the screen
|
|
220
|
+
*/
|
|
221
|
+
export function clear() {
|
|
222
|
+
process.stdout.write('\x1Bc');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Display a box with content
|
|
227
|
+
*/
|
|
228
|
+
export function box(content, title = '') {
|
|
229
|
+
const lines = content.split('\n');
|
|
230
|
+
const maxLength = Math.max(...lines.map(l => l.length), title.length);
|
|
231
|
+
const width = Math.min(maxLength + 4, 60);
|
|
232
|
+
|
|
233
|
+
console.log('\n' + chalk.dim('┌' + '─'.repeat(width - 2) + '┐'));
|
|
234
|
+
|
|
235
|
+
if (title) {
|
|
236
|
+
const padding = Math.floor((width - title.length - 4) / 2);
|
|
237
|
+
console.log(chalk.dim('│') + ' '.repeat(padding) + chalk.bold(title) + ' '.repeat(width - padding - title.length - 3) + chalk.dim('│'));
|
|
238
|
+
console.log(chalk.dim('├' + '─'.repeat(width - 2) + '┤'));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
lines.forEach(line => {
|
|
242
|
+
const paddedLine = line + ' '.repeat(width - line.length - 3);
|
|
243
|
+
console.log(chalk.dim('│') + ' ' + paddedLine + chalk.dim('│'));
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
console.log(chalk.dim('└' + '─'.repeat(width - 2) + '┘') + '\n');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Choose from a list of options
|
|
251
|
+
*/
|
|
252
|
+
export async function choose(question, options) {
|
|
253
|
+
console.log(chalk.bold(question) + '\n');
|
|
254
|
+
|
|
255
|
+
options.forEach((option, idx) => {
|
|
256
|
+
console.log(chalk.dim(` ${idx + 1}.`) + ` ${option}`);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
console.log();
|
|
260
|
+
|
|
261
|
+
while (true) {
|
|
262
|
+
const answer = await ask('Enter your choice (1-' + options.length + ')');
|
|
263
|
+
const choice = parseInt(answer);
|
|
264
|
+
|
|
265
|
+
if (choice >= 1 && choice <= options.length) {
|
|
266
|
+
return { index: choice - 1, value: options[choice - 1] };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
error('Invalid choice. Please try again.');
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Pause and wait for user to press Enter
|
|
275
|
+
*/
|
|
276
|
+
export function pause(message = 'Press Enter to continue...') {
|
|
277
|
+
return new Promise((resolve) => {
|
|
278
|
+
const rl = createInterface();
|
|
279
|
+
rl.question(chalk.dim(message), () => {
|
|
280
|
+
rl.close();
|
|
281
|
+
resolve();
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
}
|
package/bin/rollback.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* BOSS CLAUDE - Rollback CLI
|
|
4
|
+
* Undo setup changes safely
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { RollbackManager } from '../lib/setup/rollback.js';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
|
|
12
|
+
// Parse command line arguments
|
|
13
|
+
const options = {
|
|
14
|
+
verbose: args.includes('--verbose') || args.includes('-v'),
|
|
15
|
+
dryRun: args.includes('--dry-run') || args.includes('-d'),
|
|
16
|
+
force: args.includes('--force') || args.includes('-f'),
|
|
17
|
+
confirm: args.includes('--confirm') || args.includes('-y'),
|
|
18
|
+
help: args.includes('--help') || args.includes('-h')
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Show help
|
|
22
|
+
if (options.help || args.includes('help')) {
|
|
23
|
+
console.log(chalk.bold.cyan('\n🔄 BOSS CLAUDE ROLLBACK\n'));
|
|
24
|
+
console.log(chalk.white('Safely undo setup changes\n'));
|
|
25
|
+
console.log(chalk.bold('Usage:\n'));
|
|
26
|
+
console.log(chalk.white(' boss-claude rollback [options]\n'));
|
|
27
|
+
console.log(chalk.bold('Options:\n'));
|
|
28
|
+
console.log(chalk.white(' --confirm, -y Confirm rollback without prompts'));
|
|
29
|
+
console.log(chalk.white(' --dry-run, -d Show what would be rolled back (no changes)'));
|
|
30
|
+
console.log(chalk.white(' --force, -f Force rollback even if setup completed'));
|
|
31
|
+
console.log(chalk.white(' --verbose, -v Show detailed progress'));
|
|
32
|
+
console.log(chalk.white(' --help, -h Show this help message\n'));
|
|
33
|
+
console.log(chalk.bold('Examples:\n'));
|
|
34
|
+
console.log(chalk.gray(' # Preview rollback'));
|
|
35
|
+
console.log(chalk.cyan(' boss-claude rollback --dry-run\n'));
|
|
36
|
+
console.log(chalk.gray(' # Execute rollback'));
|
|
37
|
+
console.log(chalk.cyan(' boss-claude rollback --confirm\n'));
|
|
38
|
+
console.log(chalk.gray(' # Force rollback with verbose output'));
|
|
39
|
+
console.log(chalk.cyan(' boss-claude rollback --force --verbose\n'));
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Execute rollback
|
|
44
|
+
async function main() {
|
|
45
|
+
const manager = new RollbackManager(options);
|
|
46
|
+
|
|
47
|
+
if (!options.confirm && !options.dryRun) {
|
|
48
|
+
// Interactive mode
|
|
49
|
+
const result = await manager.rollbackInteractive();
|
|
50
|
+
|
|
51
|
+
if (result.requiresConfirmation) {
|
|
52
|
+
console.log(chalk.yellow('Add --confirm to execute rollback\n'));
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (result.success) {
|
|
57
|
+
process.exit(0);
|
|
58
|
+
} else {
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Execute rollback
|
|
64
|
+
const result = await manager.rollback();
|
|
65
|
+
|
|
66
|
+
if (result.success) {
|
|
67
|
+
console.log(chalk.green.bold('✓ Rollback completed successfully\n'));
|
|
68
|
+
process.exit(0);
|
|
69
|
+
} else {
|
|
70
|
+
console.log(chalk.red.bold('✗ Rollback failed\n'));
|
|
71
|
+
if (result.error) {
|
|
72
|
+
console.log(chalk.red(result.error + '\n'));
|
|
73
|
+
}
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
main().catch(error => {
|
|
79
|
+
console.error(chalk.red.bold('\n✗ Rollback error\n'));
|
|
80
|
+
console.error(chalk.red(error.message));
|
|
81
|
+
if (options.verbose && error.stack) {
|
|
82
|
+
console.error(chalk.gray('\n' + error.stack));
|
|
83
|
+
}
|
|
84
|
+
process.exit(1);
|
|
85
|
+
});
|