@promptcellar/pc 0.3.2 → 0.4.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.
@@ -39,24 +39,28 @@ async function main() {
39
39
  }
40
40
 
41
41
  // Extract the user's input messages
42
+ // Codex passes input-messages as an array of strings
42
43
  const inputMessages = event['input-messages'] || [];
43
44
  if (inputMessages.length === 0) {
44
45
  process.exit(0);
45
46
  }
46
47
 
47
- // Get the first user message as the prompt
48
- const userMessage = inputMessages.find(m => m.role === 'user');
49
- if (!userMessage || !userMessage.content) {
48
+ // Get the first message as the prompt
49
+ const firstMessage = inputMessages[0];
50
+ if (!firstMessage) {
50
51
  process.exit(0);
51
52
  }
52
53
 
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');
54
+ // Handle both string format (current Codex) and object format (future-proofing)
55
+ let content;
56
+ if (typeof firstMessage === 'string') {
57
+ content = firstMessage;
58
+ } else if (firstMessage.content) {
59
+ content = Array.isArray(firstMessage.content)
60
+ ? firstMessage.content.filter(c => c.type === 'text').map(c => c.text).join('\n')
61
+ : firstMessage.content;
62
+ } else {
63
+ process.exit(0);
60
64
  }
61
65
 
62
66
  if (!content.trim()) {
@@ -4,7 +4,12 @@
4
4
  * Claude Code Stop hook for capturing prompts to PromptCellar.
5
5
  *
6
6
  * This script is called by Claude Code when a session ends.
7
- * It parses the transcript file and extracts user prompts to capture.
7
+ * Claude Code Stop hooks receive JSON via stdin with:
8
+ * - transcript_path: path to the session JSONL transcript
9
+ * - session_id: session identifier
10
+ * - cwd: working directory
11
+ *
12
+ * It reads the transcript file and extracts the initial user prompt to capture.
8
13
  */
9
14
 
10
15
  import { readFileSync, existsSync } from 'fs';
@@ -12,25 +17,37 @@ import { capturePrompt } from '../src/lib/api.js';
12
17
  import { getFullContext } from '../src/lib/context.js';
13
18
  import { isLoggedIn } from '../src/lib/config.js';
14
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
+
15
32
  async function main() {
16
- const transcriptPath = process.argv[2];
33
+ try {
34
+ const input = await readStdin();
17
35
 
18
- if (!transcriptPath) {
19
- console.error('Usage: pc-capture <transcript-file>');
20
- process.exit(1);
21
- }
36
+ if (!input.trim()) {
37
+ process.exit(0);
38
+ }
22
39
 
23
- if (!existsSync(transcriptPath)) {
24
- console.error('Transcript file not found:', transcriptPath);
25
- process.exit(1);
26
- }
40
+ if (!isLoggedIn()) {
41
+ process.exit(0);
42
+ }
27
43
 
28
- if (!isLoggedIn()) {
29
- // Silently exit if not logged in
30
- process.exit(0);
31
- }
44
+ const event = JSON.parse(input);
45
+ const transcriptPath = event.transcript_path;
46
+
47
+ if (!transcriptPath || !existsSync(transcriptPath)) {
48
+ process.exit(0);
49
+ }
32
50
 
33
- try {
34
51
  const content = readFileSync(transcriptPath, 'utf8');
35
52
  const lines = content.split('\n').filter(line => line.trim());
36
53
 
@@ -58,18 +75,23 @@ async function main() {
58
75
  const initialPrompt = messages[0];
59
76
  const context = getFullContext('claude-code');
60
77
 
78
+ // Override with event data if available
79
+ if (event.cwd) {
80
+ context.working_directory = event.cwd;
81
+ }
82
+ if (event.session_id) {
83
+ context.session_id = event.session_id;
84
+ }
85
+
61
86
  await capturePrompt({
62
87
  content: initialPrompt.content,
63
88
  ...context,
64
89
  captured_at: initialPrompt.timestamp
65
90
  });
66
91
 
67
- // Optionally capture all prompts if configured
68
- // For now, just capture the initial prompt
69
-
70
- } catch (error) {
71
- console.error('Error capturing prompt:', error.message);
72
- process.exit(1);
92
+ } catch {
93
+ // Fail silently stderr from hooks can cause issues
94
+ process.exit(0);
73
95
  }
74
96
  }
75
97
 
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@promptcellar/pc",
3
- "version": "0.3.2",
3
+ "version": "0.4.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",
10
- "pc-codex-capture": "./hooks/codex-capture.js",
11
- "pc-gemini-capture": "./hooks/gemini-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"
12
12
  },
13
13
  "scripts": {
14
14
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -39,7 +39,6 @@
39
39
  "chalk": "^5.3.0",
40
40
  "ora": "^8.0.0",
41
41
  "inquirer": "^9.2.0",
42
- "socket.io-client": "^4.6.0",
43
- "node-fetch": "^3.3.0"
42
+ "socket.io-client": "^4.6.0"
44
43
  }
45
44
  }
@@ -195,7 +195,7 @@ async function setupClaudeCode() {
195
195
  }
196
196
 
197
197
  config.Stop.push({
198
- command: `pc-capture "$CLAUDE_TRANSCRIPT_FILE"`,
198
+ command: `pc-capture`,
199
199
  description: 'Capture prompts to PromptCellar'
200
200
  });
201
201
 
package/src/lib/api.js CHANGED
@@ -1,4 +1,3 @@
1
- import fetch from 'node-fetch';
2
1
  import { getApiKey, getApiUrl } from './config.js';
3
2
 
4
3
  async function request(endpoint, options = {}) {