@promptcellar/pc 0.1.0 → 0.2.0
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 +6 -3
- package/hooks/codex-capture.js +90 -0
- package/hooks/gemini-capture.js +92 -0
- package/package.json +5 -3
- package/src/commands/setup.js +242 -18
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ npm install -g @promptcellar/pc
|
|
|
14
14
|
# Login with your API key
|
|
15
15
|
pc login
|
|
16
16
|
|
|
17
|
-
# Set up auto-capture for
|
|
17
|
+
# Set up auto-capture for your AI CLI tools
|
|
18
18
|
pc setup
|
|
19
19
|
|
|
20
20
|
# Check status
|
|
@@ -38,12 +38,15 @@ pc setup # Configure auto-capture for CLI tools
|
|
|
38
38
|
pc unsetup # Remove auto-capture hooks
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
- Claude Code
|
|
41
|
+
Supported tools (auto-detected during setup):
|
|
42
|
+
- **Claude Code** - via Stop hooks
|
|
43
|
+
- **Codex CLI** - via notify hook
|
|
44
|
+
- **Gemini CLI** - via AfterAgent hook
|
|
43
45
|
|
|
44
46
|
Coming soon:
|
|
45
47
|
- Cursor
|
|
46
48
|
- Windsurf
|
|
49
|
+
- Aider
|
|
47
50
|
|
|
48
51
|
### Manual Capture
|
|
49
52
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Codex CLI notify hook for capturing prompts to PromptCellar.
|
|
5
|
+
*
|
|
6
|
+
* Codex calls this script with a JSON argument containing:
|
|
7
|
+
* - type: event type (e.g., 'agent-turn-complete')
|
|
8
|
+
* - thread-id: session identifier
|
|
9
|
+
* - turn-id: turn identifier
|
|
10
|
+
* - cwd: working directory
|
|
11
|
+
* - input-messages: array of user messages
|
|
12
|
+
* - last-assistant-message: final assistant response
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { capturePrompt } from '../src/lib/api.js';
|
|
16
|
+
import { getFullContext } from '../src/lib/context.js';
|
|
17
|
+
import { isLoggedIn } from '../src/lib/config.js';
|
|
18
|
+
|
|
19
|
+
async function main() {
|
|
20
|
+
// Codex passes JSON as first argument
|
|
21
|
+
const jsonArg = process.argv[2];
|
|
22
|
+
|
|
23
|
+
if (!jsonArg) {
|
|
24
|
+
// No argument, nothing to do
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!isLoggedIn()) {
|
|
29
|
+
// Silently exit if not logged in
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const event = JSON.parse(jsonArg);
|
|
35
|
+
|
|
36
|
+
// Only capture on agent-turn-complete
|
|
37
|
+
if (event.type !== 'agent-turn-complete') {
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Extract the user's input messages
|
|
42
|
+
const inputMessages = event['input-messages'] || [];
|
|
43
|
+
if (inputMessages.length === 0) {
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Get the first user message as the prompt
|
|
48
|
+
const userMessage = inputMessages.find(m => m.role === 'user');
|
|
49
|
+
if (!userMessage || !userMessage.content) {
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Extract content (handle both string and array formats)
|
|
54
|
+
let content = userMessage.content;
|
|
55
|
+
if (Array.isArray(content)) {
|
|
56
|
+
content = content
|
|
57
|
+
.filter(c => c.type === 'text')
|
|
58
|
+
.map(c => c.text)
|
|
59
|
+
.join('\n');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!content.trim()) {
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Build context
|
|
67
|
+
const context = getFullContext('codex');
|
|
68
|
+
|
|
69
|
+
// Override working directory if provided
|
|
70
|
+
if (event.cwd) {
|
|
71
|
+
context.working_directory = event.cwd;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Add session info
|
|
75
|
+
if (event['thread-id']) {
|
|
76
|
+
context.session_id = event['thread-id'];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
await capturePrompt({
|
|
80
|
+
content: content.trim(),
|
|
81
|
+
...context
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// Fail silently to not disrupt Codex
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
main();
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Gemini CLI AfterAgent hook for capturing prompts to PromptCellar.
|
|
5
|
+
*
|
|
6
|
+
* Gemini hooks receive JSON via stdin with:
|
|
7
|
+
* - event_type: the hook event (e.g., 'AfterAgent')
|
|
8
|
+
* - prompt: the user's prompt
|
|
9
|
+
* - prompt_response: the agent's response (for AfterAgent)
|
|
10
|
+
* - session_id: session identifier
|
|
11
|
+
* - working_directory: current working directory
|
|
12
|
+
*
|
|
13
|
+
* The hook must output valid JSON to stdout.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { capturePrompt } from '../src/lib/api.js';
|
|
17
|
+
import { getFullContext } from '../src/lib/context.js';
|
|
18
|
+
import { isLoggedIn } from '../src/lib/config.js';
|
|
19
|
+
|
|
20
|
+
async function readStdin() {
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
let data = '';
|
|
23
|
+
process.stdin.setEncoding('utf8');
|
|
24
|
+
process.stdin.on('data', chunk => data += chunk);
|
|
25
|
+
process.stdin.on('end', () => resolve(data));
|
|
26
|
+
|
|
27
|
+
// Timeout after 1 second if no input
|
|
28
|
+
setTimeout(() => resolve(data), 1000);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function respond(success = true) {
|
|
33
|
+
// Gemini expects JSON output
|
|
34
|
+
const response = {
|
|
35
|
+
action: 'continue',
|
|
36
|
+
success
|
|
37
|
+
};
|
|
38
|
+
console.log(JSON.stringify(response));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function main() {
|
|
42
|
+
try {
|
|
43
|
+
const input = await readStdin();
|
|
44
|
+
|
|
45
|
+
if (!input.trim()) {
|
|
46
|
+
respond(true);
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!isLoggedIn()) {
|
|
51
|
+
respond(true);
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const event = JSON.parse(input);
|
|
56
|
+
|
|
57
|
+
// Only capture AfterAgent events with a prompt
|
|
58
|
+
if (event.event_type !== 'AfterAgent' || !event.prompt) {
|
|
59
|
+
respond(true);
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const content = event.prompt.trim();
|
|
64
|
+
if (!content) {
|
|
65
|
+
respond(true);
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Build context
|
|
70
|
+
const context = getFullContext('gemini');
|
|
71
|
+
|
|
72
|
+
// Override with event data if available
|
|
73
|
+
if (event.working_directory) {
|
|
74
|
+
context.working_directory = event.working_directory;
|
|
75
|
+
}
|
|
76
|
+
if (event.session_id) {
|
|
77
|
+
context.session_id = event.session_id;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await capturePrompt({
|
|
81
|
+
content,
|
|
82
|
+
...context
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
respond(true);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// Still respond successfully to not block Gemini
|
|
88
|
+
respond(true);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptcellar/pc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI for PromptCellar - sync prompts between your terminal and the cloud",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"pc": "bin/pc.js",
|
|
9
|
-
"pc-capture": "hooks/prompt-capture.js"
|
|
8
|
+
"pc": "./bin/pc.js",
|
|
9
|
+
"pc-capture": "./hooks/prompt-capture.js",
|
|
10
|
+
"pc-codex-capture": "./hooks/codex-capture.js",
|
|
11
|
+
"pc-gemini-capture": "./hooks/gemini-capture.js"
|
|
10
12
|
},
|
|
11
13
|
"scripts": {
|
|
12
14
|
"test": "echo \"Error: no test specified\" && exit 1"
|
package/src/commands/setup.js
CHANGED
|
@@ -3,11 +3,42 @@ import chalk from 'chalk';
|
|
|
3
3
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
4
4
|
import { join, dirname } from 'path';
|
|
5
5
|
import { homedir } from 'os';
|
|
6
|
+
import { execFileSync } from 'child_process';
|
|
6
7
|
|
|
8
|
+
// Config paths
|
|
7
9
|
const CLAUDE_HOOKS_PATH = join(homedir(), '.claude', 'hooks.json');
|
|
10
|
+
const CODEX_CONFIG_PATH = join(homedir(), '.codex', 'config.toml');
|
|
11
|
+
const GEMINI_SETTINGS_PATH = join(homedir(), '.gemini', 'settings.json');
|
|
12
|
+
|
|
8
13
|
const HOOK_SCRIPT_NAME = 'pc-capture';
|
|
9
14
|
|
|
10
|
-
|
|
15
|
+
// Detect which tools are installed
|
|
16
|
+
function detectInstalledTools() {
|
|
17
|
+
const tools = [];
|
|
18
|
+
|
|
19
|
+
// Check for Claude Code
|
|
20
|
+
try {
|
|
21
|
+
execFileSync('which', ['claude'], { stdio: 'pipe' });
|
|
22
|
+
tools.push('claude');
|
|
23
|
+
} catch { /* not installed */ }
|
|
24
|
+
|
|
25
|
+
// Check for Codex CLI
|
|
26
|
+
try {
|
|
27
|
+
execFileSync('which', ['codex'], { stdio: 'pipe' });
|
|
28
|
+
tools.push('codex');
|
|
29
|
+
} catch { /* not installed */ }
|
|
30
|
+
|
|
31
|
+
// Check for Gemini CLI
|
|
32
|
+
try {
|
|
33
|
+
execFileSync('which', ['gemini'], { stdio: 'pipe' });
|
|
34
|
+
tools.push('gemini');
|
|
35
|
+
} catch { /* not installed */ }
|
|
36
|
+
|
|
37
|
+
return tools;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Claude Code hooks
|
|
41
|
+
function getClaudeHooksConfig() {
|
|
11
42
|
if (existsSync(CLAUDE_HOOKS_PATH)) {
|
|
12
43
|
try {
|
|
13
44
|
return JSON.parse(readFileSync(CLAUDE_HOOKS_PATH, 'utf8'));
|
|
@@ -18,7 +49,7 @@ function getHooksConfig() {
|
|
|
18
49
|
return {};
|
|
19
50
|
}
|
|
20
51
|
|
|
21
|
-
function
|
|
52
|
+
function saveClaudeHooksConfig(config) {
|
|
22
53
|
const dir = dirname(CLAUDE_HOOKS_PATH);
|
|
23
54
|
if (!existsSync(dir)) {
|
|
24
55
|
mkdirSync(dir, { recursive: true });
|
|
@@ -26,20 +57,85 @@ function saveHooksConfig(config) {
|
|
|
26
57
|
writeFileSync(CLAUDE_HOOKS_PATH, JSON.stringify(config, null, 2));
|
|
27
58
|
}
|
|
28
59
|
|
|
29
|
-
function
|
|
60
|
+
function isClaudeHookInstalled(config) {
|
|
30
61
|
const stopHooks = config.Stop || [];
|
|
31
62
|
return stopHooks.some(hook =>
|
|
32
63
|
hook.command && hook.command.includes(HOOK_SCRIPT_NAME)
|
|
33
64
|
);
|
|
34
65
|
}
|
|
35
66
|
|
|
67
|
+
// Codex config (TOML)
|
|
68
|
+
function getCodexConfig() {
|
|
69
|
+
if (existsSync(CODEX_CONFIG_PATH)) {
|
|
70
|
+
try {
|
|
71
|
+
return readFileSync(CODEX_CONFIG_PATH, 'utf8');
|
|
72
|
+
} catch {
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function isCodexHookInstalled(config) {
|
|
80
|
+
return config.includes('pc-codex-capture');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function saveCodexConfig(content) {
|
|
84
|
+
const dir = dirname(CODEX_CONFIG_PATH);
|
|
85
|
+
if (!existsSync(dir)) {
|
|
86
|
+
mkdirSync(dir, { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
writeFileSync(CODEX_CONFIG_PATH, content);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Gemini settings (JSON)
|
|
92
|
+
function getGeminiSettings() {
|
|
93
|
+
if (existsSync(GEMINI_SETTINGS_PATH)) {
|
|
94
|
+
try {
|
|
95
|
+
return JSON.parse(readFileSync(GEMINI_SETTINGS_PATH, 'utf8'));
|
|
96
|
+
} catch {
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isGeminiHookInstalled(settings) {
|
|
104
|
+
const hooks = settings.hooks?.AfterAgent || [];
|
|
105
|
+
return hooks.some(h =>
|
|
106
|
+
h.hooks?.some(hook => hook.command?.includes('pc-gemini-capture'))
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function saveGeminiSettings(settings) {
|
|
111
|
+
const dir = dirname(GEMINI_SETTINGS_PATH);
|
|
112
|
+
if (!existsSync(dir)) {
|
|
113
|
+
mkdirSync(dir, { recursive: true });
|
|
114
|
+
}
|
|
115
|
+
writeFileSync(GEMINI_SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
116
|
+
}
|
|
117
|
+
|
|
36
118
|
export async function setup() {
|
|
37
119
|
console.log(chalk.bold('\nPromptCellar CLI Setup\n'));
|
|
38
120
|
|
|
121
|
+
const installedTools = detectInstalledTools();
|
|
122
|
+
|
|
39
123
|
const tools = [
|
|
40
|
-
{
|
|
41
|
-
|
|
42
|
-
|
|
124
|
+
{
|
|
125
|
+
name: `Claude Code${installedTools.includes('claude') ? chalk.green(' (detected)') : ''}`,
|
|
126
|
+
value: 'claude',
|
|
127
|
+
checked: installedTools.includes('claude')
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: `Codex CLI${installedTools.includes('codex') ? chalk.green(' (detected)') : ''}`,
|
|
131
|
+
value: 'codex',
|
|
132
|
+
checked: installedTools.includes('codex')
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: `Gemini CLI${installedTools.includes('gemini') ? chalk.green(' (detected)') : ''}`,
|
|
136
|
+
value: 'gemini',
|
|
137
|
+
checked: installedTools.includes('gemini')
|
|
138
|
+
}
|
|
43
139
|
];
|
|
44
140
|
|
|
45
141
|
const { selectedTools } = await inquirer.prompt([{
|
|
@@ -57,6 +153,10 @@ export async function setup() {
|
|
|
57
153
|
for (const tool of selectedTools) {
|
|
58
154
|
if (tool === 'claude') {
|
|
59
155
|
await setupClaudeCode();
|
|
156
|
+
} else if (tool === 'codex') {
|
|
157
|
+
await setupCodex();
|
|
158
|
+
} else if (tool === 'gemini') {
|
|
159
|
+
await setupGemini();
|
|
60
160
|
}
|
|
61
161
|
}
|
|
62
162
|
|
|
@@ -67,9 +167,9 @@ export async function setup() {
|
|
|
67
167
|
async function setupClaudeCode() {
|
|
68
168
|
console.log(chalk.cyan('\nConfiguring Claude Code...'));
|
|
69
169
|
|
|
70
|
-
const config =
|
|
170
|
+
const config = getClaudeHooksConfig();
|
|
71
171
|
|
|
72
|
-
if (
|
|
172
|
+
if (isClaudeHookInstalled(config)) {
|
|
73
173
|
console.log(chalk.yellow(' Hook already installed.'));
|
|
74
174
|
|
|
75
175
|
const { reinstall } = await inquirer.prompt([{
|
|
@@ -99,26 +199,150 @@ async function setupClaudeCode() {
|
|
|
99
199
|
description: 'Capture prompts to PromptCellar'
|
|
100
200
|
});
|
|
101
201
|
|
|
102
|
-
|
|
202
|
+
saveClaudeHooksConfig(config);
|
|
203
|
+
console.log(chalk.green(' Hook installed successfully.'));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function setupCodex() {
|
|
207
|
+
console.log(chalk.cyan('\nConfiguring Codex CLI...'));
|
|
208
|
+
|
|
209
|
+
let config = getCodexConfig();
|
|
210
|
+
|
|
211
|
+
if (isCodexHookInstalled(config)) {
|
|
212
|
+
console.log(chalk.yellow(' Hook already installed.'));
|
|
213
|
+
|
|
214
|
+
const { reinstall } = await inquirer.prompt([{
|
|
215
|
+
type: 'confirm',
|
|
216
|
+
name: 'reinstall',
|
|
217
|
+
message: 'Reinstall the hook?',
|
|
218
|
+
default: false
|
|
219
|
+
}]);
|
|
220
|
+
|
|
221
|
+
if (!reinstall) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Remove existing notify line
|
|
226
|
+
config = config.split('\n')
|
|
227
|
+
.filter(line => !line.includes('pc-codex-capture'))
|
|
228
|
+
.join('\n');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Add the notify hook
|
|
232
|
+
const notifyLine = 'notify = ["pc-codex-capture"]';
|
|
233
|
+
|
|
234
|
+
if (config.includes('notify')) {
|
|
235
|
+
// Replace existing notify
|
|
236
|
+
config = config.replace(/notify\s*=\s*\[.*\]/, notifyLine);
|
|
237
|
+
} else {
|
|
238
|
+
// Add new notify line
|
|
239
|
+
config = config.trim() + '\n\n# PromptCellar capture hook\n' + notifyLine + '\n';
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
saveCodexConfig(config);
|
|
243
|
+
console.log(chalk.green(' Hook installed successfully.'));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function setupGemini() {
|
|
247
|
+
console.log(chalk.cyan('\nConfiguring Gemini CLI...'));
|
|
248
|
+
|
|
249
|
+
const settings = getGeminiSettings();
|
|
250
|
+
|
|
251
|
+
if (isGeminiHookInstalled(settings)) {
|
|
252
|
+
console.log(chalk.yellow(' Hook already installed.'));
|
|
253
|
+
|
|
254
|
+
const { reinstall } = await inquirer.prompt([{
|
|
255
|
+
type: 'confirm',
|
|
256
|
+
name: 'reinstall',
|
|
257
|
+
message: 'Reinstall the hook?',
|
|
258
|
+
default: false
|
|
259
|
+
}]);
|
|
260
|
+
|
|
261
|
+
if (!reinstall) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Remove existing hook
|
|
266
|
+
if (settings.hooks?.AfterAgent) {
|
|
267
|
+
settings.hooks.AfterAgent = settings.hooks.AfterAgent.filter(h =>
|
|
268
|
+
!h.hooks?.some(hook => hook.command?.includes('pc-gemini-capture'))
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Ensure hooks are enabled
|
|
274
|
+
if (!settings.hooksConfig) {
|
|
275
|
+
settings.hooksConfig = {};
|
|
276
|
+
}
|
|
277
|
+
settings.hooksConfig.enabled = true;
|
|
278
|
+
|
|
279
|
+
// Add the AfterAgent hook
|
|
280
|
+
if (!settings.hooks) {
|
|
281
|
+
settings.hooks = {};
|
|
282
|
+
}
|
|
283
|
+
if (!settings.hooks.AfterAgent) {
|
|
284
|
+
settings.hooks.AfterAgent = [];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
settings.hooks.AfterAgent.push({
|
|
288
|
+
matcher: '*',
|
|
289
|
+
hooks: [{
|
|
290
|
+
name: 'promptcellar-capture',
|
|
291
|
+
type: 'command',
|
|
292
|
+
command: 'pc-gemini-capture',
|
|
293
|
+
timeout: 5000
|
|
294
|
+
}]
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
saveGeminiSettings(settings);
|
|
103
298
|
console.log(chalk.green(' Hook installed successfully.'));
|
|
104
299
|
}
|
|
105
300
|
|
|
106
301
|
export async function unsetup() {
|
|
107
302
|
console.log(chalk.bold('\nRemoving PromptCellar hooks...\n'));
|
|
108
303
|
|
|
109
|
-
|
|
304
|
+
let removed = false;
|
|
110
305
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
306
|
+
// Remove Claude hook
|
|
307
|
+
const claudeConfig = getClaudeHooksConfig();
|
|
308
|
+
if (isClaudeHookInstalled(claudeConfig)) {
|
|
309
|
+
claudeConfig.Stop = (claudeConfig.Stop || []).filter(hook =>
|
|
310
|
+
!hook.command || !hook.command.includes(HOOK_SCRIPT_NAME)
|
|
311
|
+
);
|
|
312
|
+
saveClaudeHooksConfig(claudeConfig);
|
|
313
|
+
console.log(chalk.green(' Removed Claude Code hook.'));
|
|
314
|
+
removed = true;
|
|
114
315
|
}
|
|
115
316
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
)
|
|
317
|
+
// Remove Codex hook
|
|
318
|
+
let codexConfig = getCodexConfig();
|
|
319
|
+
if (isCodexHookInstalled(codexConfig)) {
|
|
320
|
+
codexConfig = codexConfig.split('\n')
|
|
321
|
+
.filter(line => !line.includes('pc-codex-capture') && !line.includes('# PromptCellar'))
|
|
322
|
+
.join('\n');
|
|
323
|
+
saveCodexConfig(codexConfig);
|
|
324
|
+
console.log(chalk.green(' Removed Codex CLI hook.'));
|
|
325
|
+
removed = true;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Remove Gemini hook
|
|
329
|
+
const geminiSettings = getGeminiSettings();
|
|
330
|
+
if (isGeminiHookInstalled(geminiSettings)) {
|
|
331
|
+
if (geminiSettings.hooks?.AfterAgent) {
|
|
332
|
+
geminiSettings.hooks.AfterAgent = geminiSettings.hooks.AfterAgent.filter(h =>
|
|
333
|
+
!h.hooks?.some(hook => hook.command?.includes('pc-gemini-capture'))
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
saveGeminiSettings(geminiSettings);
|
|
337
|
+
console.log(chalk.green(' Removed Gemini CLI hook.'));
|
|
338
|
+
removed = true;
|
|
339
|
+
}
|
|
119
340
|
|
|
120
|
-
|
|
121
|
-
|
|
341
|
+
if (!removed) {
|
|
342
|
+
console.log(chalk.yellow('No hooks installed.'));
|
|
343
|
+
} else {
|
|
344
|
+
console.log(chalk.green('\nHooks removed successfully.'));
|
|
345
|
+
}
|
|
122
346
|
}
|
|
123
347
|
|
|
124
348
|
export default setup;
|