@diegopetrucci/pi-extensions 0.1.74 → 0.1.76
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/LICENSE +2 -0
- package/README.md +9 -4
- package/extensions/agent-workflow-audit/index.ts +1398 -1009
- package/extensions/agent-workflow-audit/package.json +1 -1
- package/extensions/annotate-git-diff/README.md +3 -1
- package/extensions/annotate-git-diff/clipboard.ts +109 -100
- package/extensions/annotate-git-diff/git.ts +853 -767
- package/extensions/annotate-git-diff/glimpseui.d.ts +87 -78
- package/extensions/annotate-git-diff/index.ts +712 -671
- package/extensions/annotate-git-diff/package.json +1 -1
- package/extensions/annotate-git-diff/prompt.ts +45 -45
- package/extensions/annotate-git-diff/quiet-glimpse.ts +161 -155
- package/extensions/annotate-git-diff/review-server.ts +337 -311
- package/extensions/annotate-git-diff/types.ts +132 -132
- package/extensions/annotate-git-diff/ui.ts +27 -27
- package/extensions/annotate-git-diff/watch.ts +74 -74
- package/extensions/annotate-git-diff/web/app.js +1947 -1884
- package/extensions/annotate-git-diff/web/index.html +5 -6
- package/extensions/annotate-last-message/README.md +1 -1
- package/extensions/annotate-last-message/glimpseui.d.ts +87 -78
- package/extensions/annotate-last-message/index.ts +210 -206
- package/extensions/annotate-last-message/package.json +1 -1
- package/extensions/annotate-last-message/prompt.ts +87 -66
- package/extensions/annotate-last-message/quiet-glimpse.ts +161 -155
- package/extensions/annotate-last-message/session.ts +94 -89
- package/extensions/annotate-last-message/theme.ts +199 -193
- package/extensions/annotate-last-message/types.ts +23 -23
- package/extensions/annotate-last-message/ui.ts +27 -25
- package/extensions/annotate-last-message/web/app.js +4 -7
- package/extensions/annotate-last-message/web/md-renderer.js +1 -1
- package/extensions/brrr/README.md +1 -1
- package/extensions/brrr/index.ts +213 -191
- package/extensions/brrr/package.json +1 -1
- package/extensions/claude-fast/README.md +5 -79
- package/extensions/claude-fast/index.ts +31 -289
- package/extensions/claude-fast/package.json +2 -3
- package/extensions/code-reviewer/README.md +1 -1
- package/extensions/code-reviewer/index.ts +1598 -1350
- package/extensions/code-reviewer/package.json +1 -1
- package/extensions/confirm-destructive/README.md +1 -1
- package/extensions/confirm-destructive/index.ts +55 -51
- package/extensions/confirm-destructive/package.json +1 -1
- package/extensions/context-cap/index.ts +312 -304
- package/extensions/context-cap/package.json +1 -1
- package/extensions/context-inspector/index.ts +1211 -1064
- package/extensions/context-inspector/package.json +1 -1
- package/extensions/contrarian/index.ts +1854 -1735
- package/extensions/contrarian/package.json +1 -1
- package/extensions/dirty-repo-guard/index.ts +41 -41
- package/extensions/dynamic-context-pruning/README.md +6 -38
- package/extensions/dynamic-context-pruning/index.ts +2124 -1980
- package/extensions/dynamic-context-pruning/package.json +1 -1
- package/extensions/fast/README.md +50 -12
- package/extensions/fast/index.ts +513 -317
- package/extensions/fast/package.json +1 -1
- package/extensions/git-footer/README.md +1 -1
- package/extensions/git-footer/index.ts +458 -411
- package/extensions/git-footer/package.json +2 -3
- package/extensions/gnosis/index.ts +180 -130
- package/extensions/gnosis/package.json +1 -1
- package/extensions/inline-bash/index.ts +88 -88
- package/extensions/inline-bash/package.json +1 -1
- package/extensions/librarian/index.ts +1669 -1419
- package/extensions/librarian/package.json +1 -1
- package/extensions/minimal-footer/index.ts +994 -1012
- package/extensions/minimal-footer/openai-usage.ts +195 -190
- package/extensions/notify/index.ts +222 -230
- package/extensions/notify/package.json +1 -1
- package/extensions/openai-fast/README.md +5 -73
- package/extensions/openai-fast/index.ts +32 -272
- package/extensions/openai-fast/package.json +2 -3
- package/extensions/oracle/index.ts +1731 -1625
- package/extensions/oracle/package.json +1 -1
- package/extensions/permission-gate/index.ts +658 -608
- package/extensions/permission-gate/package.json +1 -1
- package/extensions/permission-gate/powershell-safety.ts +840 -793
- package/extensions/project-mcp-json/.pi-fleet-tested-version +1 -0
- package/extensions/project-mcp-json/README.md +199 -0
- package/extensions/project-mcp-json/config.ts +564 -0
- package/extensions/project-mcp-json/index.ts +119 -0
- package/extensions/project-mcp-json/package.json +38 -0
- package/extensions/quiet-tools/index.ts +282 -264
- package/extensions/review/README.md +2 -2
- package/extensions/review/index.ts +1745 -1872
- package/extensions/review/package.json +1 -1
- package/extensions/todo/index.ts +284 -265
- package/extensions/triage-comments/README.md +2 -2
- package/extensions/triage-comments/index.ts +2294 -1882
- package/extensions/triage-comments/package.json +1 -1
- package/package.json +6 -3
- package/assets/social-preview.png +0 -0
- package/extensions/claude-fast/claude-fast.example.json +0 -4
- package/extensions/openai-fast/openai-fast.example.json +0 -4
package/extensions/todo/index.ts
CHANGED
|
@@ -16,286 +16,305 @@ import { matchesKey, Text, truncateToWidth } from "@earendil-works/pi-tui";
|
|
|
16
16
|
import { Type } from "typebox";
|
|
17
17
|
|
|
18
18
|
interface Todo {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
id: number;
|
|
20
|
+
text: string;
|
|
21
|
+
done: boolean;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
interface TodoDetails {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
action: "list" | "add" | "toggle" | "clear";
|
|
26
|
+
todos: Todo[];
|
|
27
|
+
nextId: number;
|
|
28
|
+
error?: string;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const cloneTodos = (items: Todo[]): Todo[] => items.map((todo) => ({ ...todo }));
|
|
32
32
|
|
|
33
33
|
const TodoParams = Type.Object({
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
action: StringEnum(["list", "add", "toggle", "clear"] as const),
|
|
35
|
+
text: Type.Optional(Type.String({ description: "Todo text (for add)" })),
|
|
36
|
+
id: Type.Optional(Type.Number({ description: "Todo ID (for toggle)" })),
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* UI component for the /todos command
|
|
41
41
|
*/
|
|
42
42
|
class TodoListComponent {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
43
|
+
private todos: Todo[];
|
|
44
|
+
private theme: Theme;
|
|
45
|
+
private onClose: () => void;
|
|
46
|
+
private cachedWidth?: number;
|
|
47
|
+
private cachedLines?: string[];
|
|
48
|
+
|
|
49
|
+
constructor(todos: Todo[], theme: Theme, onClose: () => void) {
|
|
50
|
+
this.todos = todos;
|
|
51
|
+
this.theme = theme;
|
|
52
|
+
this.onClose = onClose;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
handleInput(data: string): void {
|
|
56
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
57
|
+
this.onClose();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
render(width: number): string[] {
|
|
62
|
+
if (this.cachedLines && this.cachedWidth === width) {
|
|
63
|
+
return this.cachedLines;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const lines: string[] = [];
|
|
67
|
+
const th = this.theme;
|
|
68
|
+
|
|
69
|
+
lines.push("");
|
|
70
|
+
const title = th.fg("accent", " Todos ");
|
|
71
|
+
const headerLine =
|
|
72
|
+
th.fg("borderMuted", "─".repeat(3)) +
|
|
73
|
+
title +
|
|
74
|
+
th.fg("borderMuted", "─".repeat(Math.max(0, width - 10)));
|
|
75
|
+
lines.push(truncateToWidth(headerLine, width));
|
|
76
|
+
lines.push("");
|
|
77
|
+
|
|
78
|
+
if (this.todos.length === 0) {
|
|
79
|
+
lines.push(
|
|
80
|
+
truncateToWidth(` ${th.fg("dim", "No todos yet. Ask the agent to add some!")}`, width),
|
|
81
|
+
);
|
|
82
|
+
} else {
|
|
83
|
+
const done = this.todos.filter((t) => t.done).length;
|
|
84
|
+
const total = this.todos.length;
|
|
85
|
+
lines.push(truncateToWidth(` ${th.fg("muted", `${done}/${total} completed`)}`, width));
|
|
86
|
+
lines.push("");
|
|
87
|
+
|
|
88
|
+
for (const todo of this.todos) {
|
|
89
|
+
const check = todo.done ? th.fg("success", "✓") : th.fg("dim", "○");
|
|
90
|
+
const id = th.fg("accent", `#${todo.id}`);
|
|
91
|
+
const text = todo.done ? th.fg("dim", todo.text) : th.fg("text", todo.text);
|
|
92
|
+
lines.push(truncateToWidth(` ${check} ${id} ${text}`, width));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
lines.push("");
|
|
97
|
+
lines.push(truncateToWidth(` ${th.fg("dim", "Press Escape to close")}`, width));
|
|
98
|
+
lines.push("");
|
|
99
|
+
|
|
100
|
+
this.cachedWidth = width;
|
|
101
|
+
this.cachedLines = lines;
|
|
102
|
+
return lines;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
invalidate(): void {
|
|
106
|
+
this.cachedWidth = undefined;
|
|
107
|
+
this.cachedLines = undefined;
|
|
108
|
+
}
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
export default function (pi: ExtensionAPI) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
112
|
+
// In-memory state (reconstructed from session on load)
|
|
113
|
+
let todos: Todo[] = [];
|
|
114
|
+
let nextId = 1;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Reconstruct state from session entries.
|
|
118
|
+
* Scans tool results for this tool and applies them in order.
|
|
119
|
+
*/
|
|
120
|
+
const reconstructState = (ctx: ExtensionContext) => {
|
|
121
|
+
todos = [];
|
|
122
|
+
nextId = 1;
|
|
123
|
+
|
|
124
|
+
for (const entry of ctx.sessionManager.getBranch()) {
|
|
125
|
+
if (entry.type !== "message") continue;
|
|
126
|
+
const msg = entry.message;
|
|
127
|
+
if (msg.role !== "toolResult" || msg.toolName !== "todo") continue;
|
|
128
|
+
|
|
129
|
+
const details = msg.details as TodoDetails | undefined;
|
|
130
|
+
if (details) {
|
|
131
|
+
todos = cloneTodos(details.todos);
|
|
132
|
+
nextId = details.nextId;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Reconstruct state on session events
|
|
138
|
+
pi.on("session_start", async (_event, ctx) => reconstructState(ctx));
|
|
139
|
+
pi.on("session_tree", async (_event, ctx) => reconstructState(ctx));
|
|
140
|
+
|
|
141
|
+
// Register the todo tool for the LLM
|
|
142
|
+
pi.registerTool({
|
|
143
|
+
name: "todo",
|
|
144
|
+
label: "Todo",
|
|
145
|
+
description: "Manage a todo list. Actions: list, add (text), toggle (id), clear",
|
|
146
|
+
parameters: TodoParams,
|
|
147
|
+
|
|
148
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
149
|
+
switch (params.action) {
|
|
150
|
+
case "list":
|
|
151
|
+
return {
|
|
152
|
+
content: [
|
|
153
|
+
{
|
|
154
|
+
type: "text",
|
|
155
|
+
text: todos.length
|
|
156
|
+
? todos.map((t) => `[${t.done ? "x" : " "}] #${t.id}: ${t.text}`).join("\n")
|
|
157
|
+
: "No todos",
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
details: { action: "list", todos: cloneTodos(todos), nextId } as TodoDetails,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
case "add": {
|
|
164
|
+
if (!params.text) {
|
|
165
|
+
return {
|
|
166
|
+
content: [{ type: "text", text: "Error: text required for add" }],
|
|
167
|
+
details: {
|
|
168
|
+
action: "add",
|
|
169
|
+
todos: cloneTodos(todos),
|
|
170
|
+
nextId,
|
|
171
|
+
error: "text required",
|
|
172
|
+
} as TodoDetails,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const newTodo: Todo = { id: nextId++, text: params.text, done: false };
|
|
176
|
+
todos = [...todos, newTodo];
|
|
177
|
+
return {
|
|
178
|
+
content: [{ type: "text", text: `Added todo #${newTodo.id}: ${newTodo.text}` }],
|
|
179
|
+
details: { action: "add", todos: cloneTodos(todos), nextId } as TodoDetails,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
case "toggle": {
|
|
184
|
+
if (params.id === undefined) {
|
|
185
|
+
return {
|
|
186
|
+
content: [{ type: "text", text: "Error: id required for toggle" }],
|
|
187
|
+
details: {
|
|
188
|
+
action: "toggle",
|
|
189
|
+
todos: cloneTodos(todos),
|
|
190
|
+
nextId,
|
|
191
|
+
error: "id required",
|
|
192
|
+
} as TodoDetails,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const targetId = params.id;
|
|
196
|
+
const todo = todos.find((t) => t.id === targetId);
|
|
197
|
+
if (!todo) {
|
|
198
|
+
return {
|
|
199
|
+
content: [{ type: "text", text: `Todo #${targetId} not found` }],
|
|
200
|
+
details: {
|
|
201
|
+
action: "toggle",
|
|
202
|
+
todos: cloneTodos(todos),
|
|
203
|
+
nextId,
|
|
204
|
+
error: `#${targetId} not found`,
|
|
205
|
+
} as TodoDetails,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
todos = todos.map((t) => (t.id === targetId ? { ...t, done: !t.done } : t));
|
|
209
|
+
const updated = todos.find((t) => t.id === targetId)!;
|
|
210
|
+
return {
|
|
211
|
+
content: [
|
|
212
|
+
{
|
|
213
|
+
type: "text",
|
|
214
|
+
text: `Todo #${updated.id} ${updated.done ? "completed" : "uncompleted"}`,
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
details: { action: "toggle", todos: cloneTodos(todos), nextId } as TodoDetails,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
case "clear": {
|
|
222
|
+
const count = todos.length;
|
|
223
|
+
todos = [];
|
|
224
|
+
nextId = 1;
|
|
225
|
+
return {
|
|
226
|
+
content: [{ type: "text", text: `Cleared ${count} todos` }],
|
|
227
|
+
details: { action: "clear", todos: [], nextId: 1 } as TodoDetails,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
default:
|
|
232
|
+
return {
|
|
233
|
+
content: [{ type: "text", text: `Unknown action: ${params.action}` }],
|
|
234
|
+
details: {
|
|
235
|
+
action: "list",
|
|
236
|
+
todos: cloneTodos(todos),
|
|
237
|
+
nextId,
|
|
238
|
+
error: `unknown action: ${params.action}`,
|
|
239
|
+
} as TodoDetails,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
renderCall(args, theme, _context) {
|
|
245
|
+
let text = theme.fg("toolTitle", theme.bold("todo ")) + theme.fg("muted", args.action);
|
|
246
|
+
if (args.text) text += ` ${theme.fg("dim", `"${args.text}"`)}`;
|
|
247
|
+
if (args.id !== undefined) text += ` ${theme.fg("accent", `#${args.id}`)}`;
|
|
248
|
+
return new Text(text, 0, 0);
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
renderResult(result, { expanded }, theme, _context) {
|
|
252
|
+
const details = result.details as TodoDetails | undefined;
|
|
253
|
+
if (!details) {
|
|
254
|
+
const text = result.content[0];
|
|
255
|
+
return new Text(text?.type === "text" ? text.text : "", 0, 0);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (details.error) {
|
|
259
|
+
return new Text(theme.fg("error", `Error: ${details.error}`), 0, 0);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const todoList = details.todos;
|
|
263
|
+
|
|
264
|
+
switch (details.action) {
|
|
265
|
+
case "list": {
|
|
266
|
+
if (todoList.length === 0) {
|
|
267
|
+
return new Text(theme.fg("dim", "No todos"), 0, 0);
|
|
268
|
+
}
|
|
269
|
+
let listText = theme.fg("muted", `${todoList.length} todo(s):`);
|
|
270
|
+
const display = expanded ? todoList : todoList.slice(0, 5);
|
|
271
|
+
for (const t of display) {
|
|
272
|
+
const check = t.done ? theme.fg("success", "✓") : theme.fg("dim", "○");
|
|
273
|
+
const itemText = t.done ? theme.fg("dim", t.text) : theme.fg("muted", t.text);
|
|
274
|
+
listText += `\n${check} ${theme.fg("accent", `#${t.id}`)} ${itemText}`;
|
|
275
|
+
}
|
|
276
|
+
if (!expanded && todoList.length > 5) {
|
|
277
|
+
listText += `\n${theme.fg("dim", `... ${todoList.length - 5} more`)}`;
|
|
278
|
+
}
|
|
279
|
+
return new Text(listText, 0, 0);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
case "add": {
|
|
283
|
+
const added = todoList[todoList.length - 1];
|
|
284
|
+
return new Text(
|
|
285
|
+
theme.fg("success", "✓ Added ") +
|
|
286
|
+
theme.fg("accent", `#${added.id}`) +
|
|
287
|
+
" " +
|
|
288
|
+
theme.fg("muted", added.text),
|
|
289
|
+
0,
|
|
290
|
+
0,
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
case "toggle": {
|
|
295
|
+
const text = result.content[0];
|
|
296
|
+
const msg = text?.type === "text" ? text.text : "";
|
|
297
|
+
return new Text(theme.fg("success", "✓ ") + theme.fg("muted", msg), 0, 0);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
case "clear":
|
|
301
|
+
return new Text(theme.fg("success", "✓ ") + theme.fg("muted", "Cleared all todos"), 0, 0);
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Register the /todos command for users
|
|
307
|
+
pi.registerCommand("todos", {
|
|
308
|
+
description: "Show all todos on the current branch",
|
|
309
|
+
handler: async (_args, ctx) => {
|
|
310
|
+
if (ctx.mode !== "tui") {
|
|
311
|
+
ctx.ui.notify("/todos requires interactive mode", "error");
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
await ctx.ui.custom<void>((_tui, theme, _kb, done) => {
|
|
316
|
+
return new TodoListComponent(todos, theme, () => done());
|
|
317
|
+
});
|
|
318
|
+
},
|
|
319
|
+
});
|
|
301
320
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Adds `/triage-comments` plus a read-only `triage_comments` subagent tool for evidence-based review-comment triage.
|
|
4
4
|
|
|
5
|
-
Use it when you want
|
|
5
|
+
Use it when you want `/triage-comments` to inspect selected PR feedback, classify whether each comment is valid, cite local evidence, draft review-thread responses, and propose handling options. It does **not** implement changes; after triage, the main agent should ask which handling option to take before editing anything.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -43,7 +43,7 @@ The extension registers `/triage-comments` as an interactive intake flow.
|
|
|
43
43
|
/triage-comments 123
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
- With no arguments,
|
|
46
|
+
- With no arguments, `/triage-comments` asks whether to paste feedback or fetch PR comments.
|
|
47
47
|
- `paste` opens an editor for multiline reviewer feedback, then sends one selected feedback item to the main agent.
|
|
48
48
|
- `pr` with no explicit target first tries to detect an existing PR for the current named non-`main` git branch using read-only `git` and `gh pr view` calls. If the branch is `main`, detached, outside a git repository, `gh` is unavailable or unauthenticated, or no PR is found, it falls back to the PR URL/number prompt.
|
|
49
49
|
- `pr <PR URL or number>` and a bare PR URL/number fetch that explicit PR directly, display PR review comments, PR issue comments, and review bodies with `gh` as numbered items with stable IDs, and ask whether to investigate all displayed comments or an explicit subset such as `1,3-5`.
|