@khalilgharbaoui/opencode-claude-code-plugin 0.4.22 → 0.4.23
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/dist/index.js +174 -14
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -124,6 +124,91 @@ var log = {
|
|
|
124
124
|
}
|
|
125
125
|
};
|
|
126
126
|
|
|
127
|
+
// src/todo-ledger.ts
|
|
128
|
+
var ledgers = /* @__PURE__ */ new Map();
|
|
129
|
+
var PENDING_CREATE_TTL_MS = 6e4;
|
|
130
|
+
var TASK_CREATED_PATTERN = /Task\s*#?\s*(\d+)\s+created/i;
|
|
131
|
+
var VALID_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed"]);
|
|
132
|
+
function getOrCreate(sessionId) {
|
|
133
|
+
let ledger = ledgers.get(sessionId);
|
|
134
|
+
if (!ledger) {
|
|
135
|
+
ledger = { todos: /* @__PURE__ */ new Map(), pendingCreates: /* @__PURE__ */ new Map() };
|
|
136
|
+
ledgers.set(sessionId, ledger);
|
|
137
|
+
}
|
|
138
|
+
return ledger;
|
|
139
|
+
}
|
|
140
|
+
function prunePending(ledger) {
|
|
141
|
+
const cutoff = Date.now() - PENDING_CREATE_TTL_MS;
|
|
142
|
+
for (const [id, pending] of ledger.pendingCreates) {
|
|
143
|
+
if (pending.createdAt < cutoff) ledger.pendingCreates.delete(id);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function materialize(ledger) {
|
|
147
|
+
return Array.from(ledger.todos.values());
|
|
148
|
+
}
|
|
149
|
+
function resolveSubject(input) {
|
|
150
|
+
const subject = typeof input?.subject === "string" ? input.subject.trim() : "";
|
|
151
|
+
if (subject) return subject;
|
|
152
|
+
const description = typeof input?.description === "string" ? input.description.trim() : "";
|
|
153
|
+
if (description) return description;
|
|
154
|
+
return "(no subject)";
|
|
155
|
+
}
|
|
156
|
+
function applyTaskCreateToolUse(sessionId, toolUseId, input) {
|
|
157
|
+
if (!sessionId || !toolUseId) return;
|
|
158
|
+
const ledger = getOrCreate(sessionId);
|
|
159
|
+
prunePending(ledger);
|
|
160
|
+
ledger.pendingCreates.set(toolUseId, {
|
|
161
|
+
subject: resolveSubject(input),
|
|
162
|
+
createdAt: Date.now()
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
function applyTaskCreateToolResult(sessionId, toolUseId, resultText) {
|
|
166
|
+
if (!sessionId || !toolUseId) return null;
|
|
167
|
+
const ledger = ledgers.get(sessionId);
|
|
168
|
+
if (!ledger) return null;
|
|
169
|
+
const pending = ledger.pendingCreates.get(toolUseId);
|
|
170
|
+
if (!pending) return null;
|
|
171
|
+
ledger.pendingCreates.delete(toolUseId);
|
|
172
|
+
const match = typeof resultText === "string" ? resultText.match(TASK_CREATED_PATTERN) : null;
|
|
173
|
+
if (!match) {
|
|
174
|
+
log.debug("TaskCreate result did not match expected format", { sessionId, toolUseId, resultText });
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
const claudeId = match[1];
|
|
178
|
+
if (ledger.todos.has(claudeId)) {
|
|
179
|
+
log.debug("TaskCreate result for already-known claude id; overwriting", { sessionId, claudeId });
|
|
180
|
+
}
|
|
181
|
+
ledger.todos.set(claudeId, { id: claudeId, content: pending.subject, status: "pending" });
|
|
182
|
+
return materialize(ledger);
|
|
183
|
+
}
|
|
184
|
+
function applyTaskUpdate(sessionId, input) {
|
|
185
|
+
if (!sessionId) return null;
|
|
186
|
+
const taskId = typeof input?.taskId === "string" ? input.taskId : null;
|
|
187
|
+
if (!taskId) return null;
|
|
188
|
+
const ledger = ledgers.get(sessionId);
|
|
189
|
+
if (!ledger) return null;
|
|
190
|
+
const entry = ledger.todos.get(taskId);
|
|
191
|
+
if (!entry) {
|
|
192
|
+
log.debug("TaskUpdate for unknown task id", { sessionId, taskId });
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
if (input?.status === "deleted") {
|
|
196
|
+
ledger.todos.delete(taskId);
|
|
197
|
+
return materialize(ledger);
|
|
198
|
+
}
|
|
199
|
+
if (typeof input?.status === "string" && VALID_STATUSES.has(input.status)) {
|
|
200
|
+
entry.status = input.status;
|
|
201
|
+
}
|
|
202
|
+
if (typeof input?.subject === "string" && input.subject.trim().length > 0) {
|
|
203
|
+
entry.content = input.subject.trim();
|
|
204
|
+
}
|
|
205
|
+
return materialize(ledger);
|
|
206
|
+
}
|
|
207
|
+
function clearLedger(sessionId) {
|
|
208
|
+
if (!sessionId) return;
|
|
209
|
+
ledgers.delete(sessionId);
|
|
210
|
+
}
|
|
211
|
+
|
|
127
212
|
// src/tool-mapping.ts
|
|
128
213
|
function mapToolInput(name, input) {
|
|
129
214
|
if (!input) return input;
|
|
@@ -199,17 +284,42 @@ var CLAUDE_INTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
|
199
284
|
"ToolSearch",
|
|
200
285
|
"Agent",
|
|
201
286
|
"AskFollowupQuestion",
|
|
202
|
-
"TaskCreate",
|
|
203
|
-
"TaskUpdate",
|
|
204
287
|
"TaskList",
|
|
205
288
|
"TaskGet",
|
|
206
289
|
"TaskStop"
|
|
207
290
|
]);
|
|
291
|
+
function emitTodoWrite(todos) {
|
|
292
|
+
return {
|
|
293
|
+
name: "todowrite",
|
|
294
|
+
input: {
|
|
295
|
+
todos: todos.map((todo) => ({
|
|
296
|
+
id: todo.id,
|
|
297
|
+
content: todo.content,
|
|
298
|
+
status: todo.status,
|
|
299
|
+
priority: "medium"
|
|
300
|
+
}))
|
|
301
|
+
},
|
|
302
|
+
executed: false
|
|
303
|
+
};
|
|
304
|
+
}
|
|
208
305
|
function mapTool(name, input, opts) {
|
|
209
306
|
if (CLAUDE_INTERNAL_TOOLS.has(name)) {
|
|
210
307
|
log.debug("skipping Claude CLI internal tool", { name });
|
|
211
308
|
return { name, input, executed: true, skip: true };
|
|
212
309
|
}
|
|
310
|
+
if (name === "TaskCreate") {
|
|
311
|
+
if (opts?.sessionId && opts?.toolUseId) {
|
|
312
|
+
applyTaskCreateToolUse(opts.sessionId, opts.toolUseId, input);
|
|
313
|
+
}
|
|
314
|
+
return { name, input, executed: true, skip: true };
|
|
315
|
+
}
|
|
316
|
+
if (name === "TaskUpdate") {
|
|
317
|
+
if (opts?.sessionId) {
|
|
318
|
+
const list = applyTaskUpdate(opts.sessionId, input);
|
|
319
|
+
if (list !== null) return emitTodoWrite(list);
|
|
320
|
+
}
|
|
321
|
+
return { name, input, executed: true, skip: true };
|
|
322
|
+
}
|
|
213
323
|
if (name === "EnterPlanMode") return { name: "plan_enter", input: {}, executed: false };
|
|
214
324
|
if (name === "ExitPlanMode") return { name: "plan_exit", input, executed: false };
|
|
215
325
|
if (name === "TodoWrite") {
|
|
@@ -1181,6 +1291,8 @@ function setClaudeSessionId(key, sessionId) {
|
|
|
1181
1291
|
claudeSessions.set(key, sessionId);
|
|
1182
1292
|
}
|
|
1183
1293
|
function deleteClaudeSessionId(key) {
|
|
1294
|
+
const claudeSessionId = claudeSessions.get(key);
|
|
1295
|
+
if (claudeSessionId) clearLedger(claudeSessionId);
|
|
1184
1296
|
claudeSessions.delete(key);
|
|
1185
1297
|
}
|
|
1186
1298
|
function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcpHash, systemPromptFile) {
|
|
@@ -2703,7 +2815,11 @@ ${plan}
|
|
|
2703
2815
|
input: mappedInput,
|
|
2704
2816
|
executed,
|
|
2705
2817
|
skip
|
|
2706
|
-
} = mapTool(tc.name, tc.args, {
|
|
2818
|
+
} = mapTool(tc.name, tc.args, {
|
|
2819
|
+
webSearch: this.config.webSearch,
|
|
2820
|
+
sessionId: getClaudeSessionId(sk),
|
|
2821
|
+
toolUseId: tc.id
|
|
2822
|
+
});
|
|
2707
2823
|
if (skip) continue;
|
|
2708
2824
|
content.push({
|
|
2709
2825
|
type: "tool-call",
|
|
@@ -3146,7 +3262,11 @@ ${plan}
|
|
|
3146
3262
|
const { name: mappedName, skip, executed } = mapTool(
|
|
3147
3263
|
block.name,
|
|
3148
3264
|
void 0,
|
|
3149
|
-
{
|
|
3265
|
+
{
|
|
3266
|
+
webSearch: self.config.webSearch,
|
|
3267
|
+
sessionId: getClaudeSessionId(sk),
|
|
3268
|
+
toolUseId: block.id
|
|
3269
|
+
}
|
|
3150
3270
|
);
|
|
3151
3271
|
if (!skip) {
|
|
3152
3272
|
controller.enqueue({
|
|
@@ -3280,7 +3400,11 @@ ${plan}
|
|
|
3280
3400
|
input: mappedInput,
|
|
3281
3401
|
executed,
|
|
3282
3402
|
skip
|
|
3283
|
-
} = mapTool(tc.name, parsedInput, {
|
|
3403
|
+
} = mapTool(tc.name, parsedInput, {
|
|
3404
|
+
webSearch: self.config.webSearch,
|
|
3405
|
+
sessionId: getClaudeSessionId(sk),
|
|
3406
|
+
toolUseId: tc.id
|
|
3407
|
+
});
|
|
3284
3408
|
if (!skip) {
|
|
3285
3409
|
toolCallsById.set(tc.id, {
|
|
3286
3410
|
id: tc.id,
|
|
@@ -3446,7 +3570,11 @@ ${plan}
|
|
|
3446
3570
|
input: mappedInput,
|
|
3447
3571
|
executed,
|
|
3448
3572
|
skip
|
|
3449
|
-
} = mapTool(block.name, parsedInput, {
|
|
3573
|
+
} = mapTool(block.name, parsedInput, {
|
|
3574
|
+
webSearch: self.config.webSearch,
|
|
3575
|
+
sessionId: getClaudeSessionId(sk),
|
|
3576
|
+
toolUseId: block.id
|
|
3577
|
+
});
|
|
3450
3578
|
if (!skip) {
|
|
3451
3579
|
if (!executed) skipResultForIds.add(block.id);
|
|
3452
3580
|
controller.enqueue({
|
|
@@ -3487,16 +3615,48 @@ ${plan}
|
|
|
3487
3615
|
});
|
|
3488
3616
|
continue;
|
|
3489
3617
|
}
|
|
3618
|
+
let resultText = "";
|
|
3619
|
+
if (typeof block.content === "string") {
|
|
3620
|
+
resultText = block.content;
|
|
3621
|
+
} else if (Array.isArray(block.content)) {
|
|
3622
|
+
resultText = block.content.filter(
|
|
3623
|
+
(c) => c.type === "text" && typeof c.text === "string"
|
|
3624
|
+
).map((c) => c.text).join("\n");
|
|
3625
|
+
}
|
|
3626
|
+
const claudeSessionId = getClaudeSessionId(sk);
|
|
3627
|
+
if (claudeSessionId) {
|
|
3628
|
+
const list = applyTaskCreateToolResult(
|
|
3629
|
+
claudeSessionId,
|
|
3630
|
+
block.tool_use_id,
|
|
3631
|
+
resultText
|
|
3632
|
+
);
|
|
3633
|
+
if (list) {
|
|
3634
|
+
const synthId = `todowrite_${block.tool_use_id}`;
|
|
3635
|
+
controller.enqueue({
|
|
3636
|
+
type: "tool-input-start",
|
|
3637
|
+
id: synthId,
|
|
3638
|
+
toolName: "todowrite",
|
|
3639
|
+
providerExecuted: false
|
|
3640
|
+
});
|
|
3641
|
+
controller.enqueue({
|
|
3642
|
+
type: "tool-call",
|
|
3643
|
+
toolCallId: synthId,
|
|
3644
|
+
toolName: "todowrite",
|
|
3645
|
+
input: JSON.stringify({
|
|
3646
|
+
todos: list.map((t) => ({
|
|
3647
|
+
id: t.id,
|
|
3648
|
+
content: t.content,
|
|
3649
|
+
status: t.status,
|
|
3650
|
+
priority: "medium"
|
|
3651
|
+
}))
|
|
3652
|
+
}),
|
|
3653
|
+
providerExecuted: false
|
|
3654
|
+
});
|
|
3655
|
+
noteToolActivity();
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3490
3658
|
const toolCall = toolCallsById.get(block.tool_use_id);
|
|
3491
3659
|
if (toolCall) {
|
|
3492
|
-
let resultText = "";
|
|
3493
|
-
if (typeof block.content === "string") {
|
|
3494
|
-
resultText = block.content;
|
|
3495
|
-
} else if (Array.isArray(block.content)) {
|
|
3496
|
-
resultText = block.content.filter(
|
|
3497
|
-
(c) => c.type === "text" && typeof c.text === "string"
|
|
3498
|
-
).map((c) => c.text).join("\n");
|
|
3499
|
-
}
|
|
3500
3660
|
controller.enqueue({
|
|
3501
3661
|
type: "tool-result",
|
|
3502
3662
|
toolCallId: block.tool_use_id,
|