opencode-pixel-office 1.0.10 → 1.1.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.
@@ -19,22 +19,22 @@ const postEvent = async (endpoint, event) => {
19
19
  body: JSON.stringify(event),
20
20
  });
21
21
  } catch (error) {
22
- const logPath = path.join(os.homedir(), ".claude", "pixel-office-hook.log");
23
- fs.appendFileSync(logPath, `${new Date().toISOString()} ${String(error)}\n`);
22
+ // Silently ignore - server might not be running
24
23
  }
25
24
  };
26
25
 
27
- const readMode = () => {
28
- try {
29
- const configPath = path.join(os.homedir(), ".opencode", "pixel-office", "config.json");
30
- if (fs.existsSync(configPath)) {
31
- const data = JSON.parse(fs.readFileSync(configPath, "utf8"));
32
- return data.mode || "opencode";
33
- }
34
- } catch (error) {
35
- return "opencode";
36
- }
37
- return "opencode";
26
+ const getProjectName = (cwd) => {
27
+ if (!cwd) return "Claude Code";
28
+ const parts = cwd.split(path.sep).filter(Boolean);
29
+ return parts[parts.length - 1] || "Claude Code";
30
+ };
31
+
32
+ const getModelFromEnv = () => {
33
+ const model = process.env.ANTHROPIC_MODEL || process.env.CLAUDE_MODEL || "";
34
+ if (model.includes("opus")) return "claude-opus";
35
+ if (model.includes("sonnet")) return "claude-sonnet";
36
+ if (model.includes("haiku")) return "claude-haiku";
37
+ return "claude";
38
38
  };
39
39
 
40
40
  const mapHookToEvent = (input) => {
@@ -43,76 +43,169 @@ const mapHookToEvent = (input) => {
43
43
  const cwd = input.cwd || "";
44
44
  const toolName = input.tool_name || "";
45
45
  const toolInput = input.tool_input || {};
46
+ const toolOutput = input.tool_output || {};
46
47
  const prompt = input.prompt || input.user_prompt || "";
47
48
  const permission = input.permission || {};
49
+ const message = input.message || "";
50
+
51
+ const projectName = getProjectName(cwd);
52
+ const agentId = sessionId || `claude-${Date.now()}`;
53
+ const modelId = getModelFromEnv();
48
54
 
49
55
  const info = {
50
- id: sessionId || `claude-${Date.now()}`,
56
+ id: agentId,
51
57
  sessionID: sessionId,
52
58
  agent: "Claude",
53
- title: cwd || "Claude Code",
54
- model: { modelID: "claude", providerID: "anthropic" },
59
+ title: projectName,
60
+ directory: cwd,
61
+ model: {
62
+ modelID: modelId,
63
+ providerID: "anthropic",
64
+ },
55
65
  };
56
66
 
67
+ let event = null;
68
+
57
69
  switch (hook) {
58
70
  case "SessionStart":
59
- return {
71
+ event = {
60
72
  type: "session.created",
61
- properties: { info },
73
+ properties: {
74
+ info: {
75
+ ...info,
76
+ title: projectName,
77
+ slug: projectName.toLowerCase().replace(/[^a-z0-9]/g, "-"),
78
+ },
79
+ },
62
80
  };
81
+ break;
82
+
63
83
  case "SessionEnd":
64
- return {
84
+ event = {
65
85
  type: "session.deleted",
66
- properties: { sessionID: sessionId },
86
+ properties: {
87
+ sessionID: sessionId,
88
+ info,
89
+ },
67
90
  };
91
+ break;
92
+
68
93
  case "UserPromptSubmit":
69
- return {
94
+ event = {
70
95
  type: "message.updated",
71
96
  properties: {
72
97
  info: { ...info, role: "user" },
73
- message: { content: prompt, role: "user" },
98
+ message: {
99
+ id: `msg-${Date.now()}`,
100
+ content: prompt,
101
+ role: "user",
102
+ status: "pending",
103
+ },
74
104
  },
75
105
  };
106
+ break;
107
+
76
108
  case "PreToolUse":
77
- return {
109
+ event = {
78
110
  type: "tool.execute.before",
79
- properties: { info, tool: { name: toolName, input: toolInput } },
111
+ properties: {
112
+ info,
113
+ tool: { name: toolName, input: toolInput },
114
+ },
80
115
  };
116
+ break;
117
+
81
118
  case "PostToolUse":
119
+ event = {
120
+ type: "tool.execute.after",
121
+ properties: {
122
+ info,
123
+ tool: { name: toolName, input: toolInput, output: toolOutput },
124
+ status: "success",
125
+ },
126
+ };
127
+ break;
128
+
82
129
  case "PostToolUseFailure":
83
- return {
130
+ event = {
84
131
  type: "tool.execute.after",
85
- properties: { info, tool: { name: toolName, input: toolInput } },
132
+ properties: {
133
+ info,
134
+ tool: { name: toolName, input: toolInput, output: toolOutput },
135
+ status: "error",
136
+ },
86
137
  };
138
+ break;
139
+
87
140
  case "PermissionRequest":
88
- return {
141
+ event = {
89
142
  type: "permission.asked",
90
- properties: { info, permission },
143
+ properties: {
144
+ info,
145
+ permission: { ...permission, tool: toolName },
146
+ },
91
147
  };
148
+ break;
149
+
92
150
  case "Notification":
93
- return {
151
+ event = {
94
152
  type: "tui.toast.show",
95
- properties: { info, message: input.message || "" },
153
+ properties: { info, message },
96
154
  };
155
+ break;
156
+
97
157
  case "PreCompact":
98
- return {
158
+ event = {
99
159
  type: "session.compacted",
100
160
  properties: { info },
101
161
  };
162
+ break;
163
+
102
164
  case "Stop":
103
- return {
165
+ event = {
104
166
  type: "session.status",
105
- properties: { sessionID: sessionId, status: { type: "idle" } },
167
+ properties: {
168
+ sessionID: sessionId,
169
+ info,
170
+ status: { type: "idle" },
171
+ },
106
172
  };
173
+ break;
174
+
107
175
  case "SubagentStart":
108
- case "SubagentStop":
109
- return {
176
+ event = {
110
177
  type: "session.updated",
111
- properties: { info },
178
+ properties: {
179
+ info: {
180
+ ...info,
181
+ agent: input.subagent_type || "Subagent",
182
+ title: `${projectName} (${input.subagent_type || "Task"})`,
183
+ },
184
+ parentSessionId: sessionId,
185
+ },
112
186
  };
187
+ break;
188
+
189
+ case "SubagentStop":
190
+ event = {
191
+ type: "session.idle",
192
+ properties: {
193
+ sessionID: input.subagent_id || sessionId,
194
+ info,
195
+ },
196
+ };
197
+ break;
198
+
113
199
  default:
114
200
  return null;
115
201
  }
202
+
203
+ // Add source tag to identify this is from Claude Code
204
+ if (event) {
205
+ event.source = "claude";
206
+ }
207
+
208
+ return event;
116
209
  };
117
210
 
118
211
  const main = async () => {
@@ -120,15 +213,21 @@ const main = async () => {
120
213
  if (!raw) {
121
214
  process.exit(0);
122
215
  }
123
- const input = JSON.parse(raw);
124
- const endpoint = process.env.PIXEL_OFFICE_URL || "http://localhost:5100/events";
125
- if (readMode() !== "claude-code") {
216
+
217
+ let input;
218
+ try {
219
+ input = JSON.parse(raw);
220
+ } catch {
126
221
  process.exit(0);
127
222
  }
223
+
224
+ const endpoint = process.env.PIXEL_OFFICE_URL || "http://localhost:5100/events";
128
225
  const event = mapHookToEvent(input);
226
+
129
227
  if (!event) {
130
228
  process.exit(0);
131
229
  }
230
+
132
231
  await postEvent(endpoint, event);
133
232
  process.exit(0);
134
233
  };