@prjct.app/pi-activity 0.1.3 → 0.2.1

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/index.ts CHANGED
@@ -6,80 +6,179 @@ import {
6
6
  createLsTool,
7
7
  createReadTool,
8
8
  createWriteTool,
9
+ getSettingsListTheme,
10
+ keyHint,
9
11
  type ExtensionAPI,
10
12
  type ExtensionContext,
11
13
  } from "@earendil-works/pi-coding-agent";
12
- import { Container, Text, TruncatedText, truncateToWidth } from "@earendil-works/pi-tui";
13
- import { stripVTControlCharacters } from "node:util";
14
- import { homedir } from "node:os";
15
14
  import type { AgentTool } from "@earendil-works/pi-agent-core";
15
+ import { Container, SettingsList, Text, type SettingItem } from "@earendil-works/pi-tui";
16
16
  import type { TSchema } from "typebox";
17
+ import {
18
+ ActiveToolsWidget,
19
+ ActivityInspectorComponent,
20
+ ActivityRowComponent,
21
+ ActivitySummaryComponent,
22
+ renderExpandedToolResult,
23
+ } from "./src/components.ts";
24
+ import {
25
+ actionTarget,
26
+ activityCategory,
27
+ aggregateFileChanges,
28
+ applyToolResult,
29
+ finishRecord,
30
+ formatDuration,
31
+ resultText,
32
+ snapshotRecord,
33
+ TOOL_VERBS,
34
+ } from "./src/format.ts";
35
+ import type {
36
+ ActivityCategory,
37
+ ActivityDensity,
38
+ ActivityRecord,
39
+ ActivityRecordSnapshot,
40
+ ActivitySummaryData,
41
+ } from "./src/types.ts";
17
42
 
18
- const SPINNER_FRAMES = ["", "", "", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
43
+ const DENSITIES: ActivityDensity[] = ["minimal", "balanced", "forensic"];
44
+ const HISTORY_LIMIT = 200;
19
45
 
20
- function actionTarget(name: string, args: Record<string, unknown>): string {
21
- const value = name === "bash" ? args.command
22
- : name === "grep" || name === "find" ? `${args.pattern ?? ""}${args.path ? ` · ${args.path}` : ""}`
23
- : args.path;
24
- if (typeof value !== "string" || !value) return ".";
25
- const home = homedir();
26
- const target = value.startsWith(`${home}/`) ? `~/${value.slice(home.length + 1)}` : value;
27
- return stripVTControlCharacters(target).replace(/\r?\n/g, " ↵ ").replace(/\t/g, " ").replace(/[\x00-\x1f\x7f]/g, "");
46
+ function isDensity(value: unknown): value is ActivityDensity {
47
+ return typeof value === "string" && DENSITIES.includes(value as ActivityDensity);
28
48
  }
29
49
 
30
- function detailText(result: { content: Array<{ type: string; text?: string }> }): string {
31
- const text = result.content
32
- .filter((item) => item.type === "text" && item.text)
33
- .map((item) => item.text)
34
- .join("\n")
35
- .trim();
36
- return text || "No additional details.";
50
+ function unique(values: string[]): string[] {
51
+ return [...new Set(values)];
37
52
  }
38
53
 
39
54
  export default function activityMode(pi: ExtensionAPI) {
40
- const modifiedFiles = new Set<string>();
41
- const completedActions = new Set<string>();
42
- const failedActions = new Set<string>();
43
- let actionCount = 0;
44
- let errorCount = 0;
45
- const activeTools = new Map<string, string>();
55
+ let density: ActivityDensity = "balanced";
56
+ let runStartedAt: number | undefined;
57
+ let agentRunning = false;
58
+ let waitingForUser = false;
46
59
  let lastWorkingMessage: string | undefined;
60
+ let history: Array<ActivityRecord | ActivityRecordSnapshot> = [];
61
+ let runRecords: ActivityRecord[] = [];
62
+ const recordsById = new Map<string, ActivityRecord>();
63
+ const activeRecords = new Map<string, ActivityRecord>();
47
64
 
48
- function refreshWorkingMessage(ctx: ExtensionContext, settled = false) {
49
- if (ctx.mode !== "tui") return;
50
- const first = activeTools.values().next().value;
51
- const message = settled ? undefined : activeTools.size > 1
52
- ? `${activeTools.size} tools running · ${first}`
53
- : first ?? "Thinking…";
65
+ const completedCount = () => runRecords.filter((record) => record.status !== "running").length;
66
+
67
+ function ensureRecord(
68
+ id: string,
69
+ name: string,
70
+ args: Record<string, unknown> = {},
71
+ startedAt = Date.now(),
72
+ ): ActivityRecord {
73
+ const existing = recordsById.get(id);
74
+ if (existing) {
75
+ if (Object.keys(args).length) {
76
+ existing.args = args;
77
+ existing.target = actionTarget(name, args);
78
+ existing.category = activityCategory(name, args);
79
+ }
80
+ return existing;
81
+ }
82
+ const record: ActivityRecord = {
83
+ id,
84
+ name,
85
+ args,
86
+ target: actionTarget(name, args),
87
+ category: activityCategory(name, args),
88
+ status: "running",
89
+ startedAt,
90
+ };
91
+ recordsById.set(id, record);
92
+ runRecords.push(record);
93
+ runStartedAt ??= startedAt;
94
+ return record;
95
+ }
96
+
97
+ function reconstructSessionState(ctx: ExtensionContext): void {
98
+ density = "balanced";
99
+ history = [];
100
+ const byId = new Map<string, ActivityRecordSnapshot>();
101
+ for (const entry of ctx.sessionManager.getBranch()) {
102
+ if (entry.type !== "custom") continue;
103
+ if (entry.customType === "activity-settings") {
104
+ const savedDensity = (entry.data as { density?: unknown } | undefined)?.density;
105
+ if (isDensity(savedDensity)) density = savedDensity;
106
+ }
107
+ if (entry.customType !== "activity-summary") continue;
108
+ const data = entry.data as Partial<ActivitySummaryData> | undefined;
109
+ if (data?.version !== 2 || !Array.isArray(data.records)) continue;
110
+ for (const record of data.records) byId.set(record.id, record);
111
+ }
112
+ history = [...byId.values()].slice(-HISTORY_LIMIT);
113
+ }
114
+
115
+ function activeList(): ActivityRecord[] {
116
+ return [...activeRecords.values()];
117
+ }
118
+
119
+ function setWorkingMessage(ctx: ExtensionContext, message: string | undefined): void {
54
120
  if (message === lastWorkingMessage) return;
55
121
  lastWorkingMessage = message;
56
122
  ctx.ui.setWorkingMessage(message);
57
123
  }
58
124
 
125
+ function refreshPresence(ctx: ExtensionContext, settled = false): void {
126
+ if (ctx.mode !== "tui") return;
127
+ const records = activeList();
128
+ if (settled) {
129
+ ctx.ui.setWidget("pi-activity", undefined);
130
+ setWorkingMessage(ctx, undefined);
131
+ return;
132
+ }
133
+
134
+ if (waitingForUser || records.length) {
135
+ ctx.ui.setWidget(
136
+ "pi-activity",
137
+ (_tui, theme) => new ActiveToolsWidget(activeList, completedCount, () => waitingForUser, theme),
138
+ { placement: "aboveEditor" },
139
+ );
140
+ } else {
141
+ ctx.ui.setWidget("pi-activity", undefined);
142
+ }
143
+
144
+ if (waitingForUser) {
145
+ setWorkingMessage(ctx, "Waiting for input…");
146
+ ctx.ui.setStatus("pi-activity", ctx.ui.theme.fg("warning", "! waiting for input"));
147
+ return;
148
+ }
149
+ if (records.length) {
150
+ const first = records[0]!;
151
+ const verb = TOOL_VERBS[first.name] ?? first.name.toUpperCase();
152
+ const message = records.length > 1
153
+ ? `${records.length} active · ${verb} ${first.target}`
154
+ : `${verb} ${first.target}`;
155
+ setWorkingMessage(ctx, message);
156
+ ctx.ui.setStatus("pi-activity", ctx.ui.theme.fg("accent", `◆ ${records.length} active · ${completedCount()} done`));
157
+ return;
158
+ }
159
+ if (agentRunning) {
160
+ setWorkingMessage(ctx, "Thinking…");
161
+ ctx.ui.setStatus("pi-activity", ctx.ui.theme.fg("dim", "◆ thinking"));
162
+ } else {
163
+ setWorkingMessage(ctx, undefined);
164
+ ctx.ui.setStatus("pi-activity", ctx.ui.theme.fg("dim", `◇ ready · ${density}`));
165
+ }
166
+ }
167
+
59
168
  pi.registerEntryRenderer("activity-summary", (entry, options, theme) => {
60
- const data = entry.data as {
61
- modifiedFiles?: string[];
62
- failedActions?: string[];
63
- actionCount?: number;
64
- errorCount?: number;
65
- } | undefined;
66
- const files = data?.modifiedFiles ?? [];
67
- const failures = data?.failedActions ?? [];
68
- const count = (n: number, label: string) => `${n} ${label}${n === 1 ? "" : "s"}`;
69
- // Legacy summaries stored categories, not counts; do not invent action totals.
70
- const total = data?.actionCount === undefined ? "previous run" : count(data.actionCount, "action");
71
- const errors = data?.errorCount === undefined ? count(failures.length, "error category") : count(data.errorCount, "error");
72
- const title = `Activity · ${total} · ${count(files.length, "file")} · ${errors}`;
73
- if (!options.expanded) return new TruncatedText(theme.fg(failures.length ? "error" : "muted", title), 0, 0);
74
- const lines = [title, ...files.map((path) => ` • ${actionTarget("read", { path })}`)];
75
- if (failures.length) lines.push(`Errors: ${failures.join(" · ")}`);
76
- return new Text(lines.join("\n"), 0, 0);
169
+ return new ActivitySummaryComponent(
170
+ (entry.data ?? {}) as ActivitySummaryData,
171
+ options.expanded,
172
+ theme,
173
+ );
77
174
  });
78
175
 
176
+ // Settings are persisted as invisible session entries and restored on resume/tree navigation.
177
+ pi.registerEntryRenderer("activity-settings", () => new Container());
178
+
79
179
  function registerCompactTool<T extends TSchema, D>(createTool: (cwd: string) => AgentTool<T, D>) {
80
180
  const initialTool = createTool(process.cwd());
81
181
  const name = initialTool.name;
82
-
83
182
  pi.registerTool({
84
183
  ...initialTool,
85
184
  name,
@@ -88,22 +187,33 @@ export default function activityMode(pi: ExtensionAPI) {
88
187
  return createTool(ctx.cwd).execute(toolCallId, params, signal, onUpdate);
89
188
  },
90
189
  renderCall(args, theme, context) {
91
- const target = actionTarget(name, args as Record<string, unknown>);
92
- const symbol = context.isPartial ? "·" : context.isError ? "✗" : "✓";
93
- const color = context.isPartial ? "muted" : context.isError ? "error" : "success";
94
- const title = `${theme.fg(color, symbol)} ${theme.fg("toolTitle", name)} · ${theme.fg("toolOutput", target)}`;
95
- if (context.expanded) {
96
- const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
97
- text.setText(title);
98
- return text;
99
- }
100
- return new TruncatedText(title, 0, 0);
190
+ const id = context.toolCallId || `${name}:render`;
191
+ const record = ensureRecord(id, name, args as Record<string, unknown>);
192
+ if (context.isError && record.status === "running") record.status = "error";
193
+ const component = context.lastComponent instanceof ActivityRowComponent
194
+ ? context.lastComponent
195
+ : new ActivityRowComponent(record, theme, () => density);
196
+ component.update(record, theme);
197
+ return component;
101
198
  },
102
- renderResult(result, { expanded }, _theme, context) {
103
- if (!expanded) return new Container();
104
- const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
105
- text.setText(detailText(result));
106
- return text;
199
+ renderResult(result, options, theme, context) {
200
+ const id = context.toolCallId || `${name}:render`;
201
+ const record = ensureRecord(id, name, context.args as Record<string, unknown>);
202
+ if (!options.isPartial || context.isError) applyToolResult(record, result, context.isError);
203
+ else {
204
+ const preview = resultText(result);
205
+ if (preview) record.outputPreview = preview.slice(0, 2_000);
206
+ }
207
+ return renderExpandedToolResult(
208
+ name,
209
+ context.args as Record<string, unknown>,
210
+ result,
211
+ options.expanded,
212
+ context.isError,
213
+ context.showImages,
214
+ theme,
215
+ record,
216
+ );
107
217
  },
108
218
  });
109
219
  }
@@ -116,68 +226,225 @@ export default function activityMode(pi: ExtensionAPI) {
116
226
  registerCompactTool(createGrepTool);
117
227
  registerCompactTool(createLsTool);
118
228
 
229
+ pi.registerCommand("activity", {
230
+ description: "Inspect recent tool activity with filters for changes, commands, and issues.",
231
+ handler: async (_args, ctx) => {
232
+ if (ctx.mode !== "tui") {
233
+ ctx.ui.notify("/activity requires interactive TUI mode.", "error");
234
+ return;
235
+ }
236
+ const currentRecords = runRecords.filter((record) => !history.some((saved) => saved.id === record.id));
237
+ const records = [...history, ...currentRecords].slice(-HISTORY_LIMIT);
238
+ if (!records.length) {
239
+ ctx.ui.notify("No activity has been recorded in this session yet.", "info");
240
+ return;
241
+ }
242
+ await ctx.ui.custom<void>((tui, theme, keybindings, done) => {
243
+ return new ActivityInspectorComponent(records, theme, keybindings, () => done(), () => tui.requestRender());
244
+ });
245
+ },
246
+ });
247
+
248
+ pi.registerCommand("activity-settings", {
249
+ description: "Choose minimal, balanced, or forensic activity-row density.",
250
+ handler: async (args, ctx) => {
251
+ const requested = args.trim().toLowerCase();
252
+ if (requested) {
253
+ if (!isDensity(requested)) {
254
+ if (ctx.hasUI) ctx.ui.notify("Usage: /activity-settings [minimal|balanced|forensic]", "error");
255
+ return;
256
+ }
257
+ density = requested;
258
+ pi.appendEntry("activity-settings", { density });
259
+ if (ctx.mode === "tui") {
260
+ ctx.ui.setStatus("pi-activity", ctx.ui.theme.fg("dim", `Activity · ${density}`));
261
+ ctx.ui.notify(`Activity density set to ${density}.`, "info");
262
+ }
263
+ return;
264
+ }
265
+ if (ctx.mode !== "tui") {
266
+ if (ctx.hasUI) ctx.ui.notify("Pass a density: /activity-settings [minimal|balanced|forensic]", "error");
267
+ return;
268
+ }
269
+ const items: SettingItem[] = [{
270
+ id: "density",
271
+ label: "Activity density",
272
+ description: "Minimal hides metadata; balanced shows outcomes; forensic adds category and truncation evidence.",
273
+ currentValue: density,
274
+ values: DENSITIES,
275
+ }];
276
+ await ctx.ui.custom<void>((tui, theme, _keybindings, done) => {
277
+ const container = new Container();
278
+ container.addChild(new Text(theme.fg("accent", theme.bold("Activity settings")), 1, 1));
279
+ const settings = new SettingsList(
280
+ items,
281
+ 3,
282
+ getSettingsListTheme(),
283
+ (id, value) => {
284
+ if (id !== "density" || !isDensity(value)) return;
285
+ density = value;
286
+ settings.updateValue(id, value);
287
+ pi.appendEntry("activity-settings", { density });
288
+ ctx.ui.setStatus("pi-activity", theme.fg("dim", `Activity · ${density}`));
289
+ tui.requestRender();
290
+ },
291
+ () => done(),
292
+ );
293
+ container.addChild(settings);
294
+ container.addChild(new Text(theme.fg("dim", `${keyHint("tui.select.confirm", "change")} · ${keyHint("tui.select.cancel", "close")}`), 1, 1));
295
+ return {
296
+ render: (width) => container.render(width),
297
+ invalidate: () => container.invalidate(),
298
+ handleInput: (data) => {
299
+ settings.handleInput(data);
300
+ tui.requestRender();
301
+ },
302
+ };
303
+ });
304
+ },
305
+ });
306
+
119
307
  pi.on("session_start", (_event, ctx) => {
120
- activeTools.clear();
308
+ runStartedAt = undefined;
309
+ agentRunning = false;
310
+ waitingForUser = false;
121
311
  lastWorkingMessage = undefined;
312
+ runRecords = [];
313
+ recordsById.clear();
314
+ activeRecords.clear();
315
+ reconstructSessionState(ctx);
122
316
  if (ctx.mode !== "tui") return;
123
317
  ctx.ui.setToolsExpanded(false);
124
- ctx.ui.setWorkingIndicator({ frames: SPINNER_FRAMES, intervalMs: 160 });
318
+ ctx.ui.setStatus("pi-activity", ctx.ui.theme.fg("dim", `◇ ready · ${density}`));
319
+ });
320
+
321
+ pi.on("session_tree", (_event, ctx) => {
322
+ activeRecords.clear();
323
+ recordsById.clear();
324
+ runRecords = [];
325
+ runStartedAt = undefined;
326
+ agentRunning = false;
327
+ reconstructSessionState(ctx);
328
+ if (ctx.mode === "tui") {
329
+ ctx.ui.setWidget("pi-activity", undefined);
330
+ setWorkingMessage(ctx, undefined);
331
+ ctx.ui.setStatus("pi-activity", ctx.ui.theme.fg("dim", `◇ ready · ${density}`));
332
+ }
125
333
  });
126
334
 
127
335
  pi.on("session_shutdown", (_event, ctx) => {
128
- activeTools.clear();
129
- refreshWorkingMessage(ctx, true);
336
+ activeRecords.clear();
337
+ agentRunning = false;
338
+ waitingForUser = false;
130
339
  if (ctx.mode !== "tui") return;
131
- ctx.ui.setWorkingIndicator();
340
+ ctx.ui.setWidget("pi-activity", undefined);
341
+ ctx.ui.setStatus("pi-activity", undefined);
342
+ setWorkingMessage(ctx, undefined);
132
343
  });
133
344
 
134
345
  pi.on("agent_start", (_event, ctx) => {
135
- // A retry can emit another agent_start before the overall run settles.
136
- activeTools.clear();
137
- refreshWorkingMessage(ctx);
346
+ activeRecords.clear();
347
+ agentRunning = true;
348
+ waitingForUser = false;
349
+ runStartedAt ??= Date.now();
350
+ refreshPresence(ctx);
138
351
  });
139
352
 
140
- pi.on("tool_execution_start", (event, ctx) => {
141
- const target = actionTarget(event.toolName, event.args ?? {});
142
- activeTools.set(event.toolCallId, truncateToWidth(`${event.toolName} · ${target}`, 72));
143
- refreshWorkingMessage(ctx);
353
+ pi.on("ui_prompt_start", (_event, ctx) => {
354
+ waitingForUser = true;
355
+ refreshPresence(ctx);
144
356
  });
145
357
 
146
- pi.on("tool_execution_end", (event, ctx) => {
147
- activeTools.delete(event.toolCallId);
148
- refreshWorkingMessage(ctx);
149
- actionCount++;
150
- if (event.isError) {
151
- errorCount++;
152
- failedActions.add(event.toolName);
153
- return;
154
- }
155
- completedActions.add(event.toolName);
358
+ pi.on("ui_prompt_end", (_event, ctx) => {
359
+ waitingForUser = false;
360
+ refreshPresence(ctx);
361
+ });
156
362
 
363
+ pi.on("tool_execution_start", (event, ctx) => {
364
+ agentRunning = true;
365
+ const record = ensureRecord(event.toolCallId, event.toolName, event.args ?? {}, Date.now());
366
+ record.status = "running";
367
+ record.startedAt = Date.now();
368
+ record.endedAt = undefined;
369
+ record.durationMs = undefined;
370
+ activeRecords.set(event.toolCallId, record);
371
+ refreshPresence(ctx);
372
+ });
373
+
374
+ pi.on("tool_execution_update", (event) => {
375
+ const record = ensureRecord(event.toolCallId, event.toolName, event.args ?? {});
376
+ const preview = resultText(event.partialResult);
377
+ if (preview) record.outputPreview = preview.slice(0, 2_000);
157
378
  });
158
379
 
159
380
  pi.on("tool_result", (event) => {
160
- if (!event.isError && (event.toolName === "edit" || event.toolName === "write") && typeof event.input.path === "string") {
161
- modifiedFiles.add(event.input.path);
381
+ const record = ensureRecord(event.toolCallId, event.toolName, event.input ?? {});
382
+ applyToolResult(record, { content: event.content, details: event.details }, event.isError);
383
+ });
384
+
385
+ pi.on("tool_execution_end", (event, ctx) => {
386
+ const record = ensureRecord(event.toolCallId, event.toolName);
387
+ if (event.result) {
388
+ finishRecord(record, Date.now(), event.result, event.isError);
389
+ } else {
390
+ record.endedAt = Date.now();
391
+ record.durationMs = Math.max(0, record.endedAt - record.startedAt);
392
+ if (record.status === "running") applyToolResult(record, { content: [] }, event.isError);
162
393
  }
394
+ activeRecords.delete(event.toolCallId);
395
+ refreshPresence(ctx);
163
396
  });
164
397
 
165
- pi.on("turn_start", (_event, ctx) => refreshWorkingMessage(ctx));
398
+ pi.on("turn_start", (_event, ctx) => {
399
+ agentRunning = true;
400
+ refreshPresence(ctx);
401
+ });
166
402
 
167
403
  pi.on("agent_settled", (_event, ctx) => {
168
- activeTools.clear();
169
- refreshWorkingMessage(ctx, true);
170
- if (ctx.mode === "tui" && actionCount > 0) pi.appendEntry("activity-summary", {
171
- actionCount,
172
- errorCount,
173
- modifiedFiles: [...modifiedFiles],
174
- completedActions: [...completedActions],
175
- failedActions: [...failedActions],
176
- });
177
- actionCount = 0;
178
- errorCount = 0;
179
- modifiedFiles.clear();
180
- completedActions.clear();
181
- failedActions.clear();
404
+ const settledAt = Date.now();
405
+ agentRunning = false;
406
+ for (const record of runRecords) {
407
+ if (record.status !== "running") continue;
408
+ record.status = "cancelled";
409
+ record.outcome = "interrupted";
410
+ record.endedAt = settledAt;
411
+ record.durationMs = Math.max(0, settledAt - record.startedAt);
412
+ }
413
+ activeRecords.clear();
414
+ waitingForUser = false;
415
+ refreshPresence(ctx, true);
416
+
417
+ const records = runRecords.map(snapshotRecord);
418
+ if (records.length && ctx.mode === "tui") {
419
+ const categoryCounts: Partial<Record<ActivityCategory, number>> = {};
420
+ for (const record of records) categoryCounts[record.category] = (categoryCounts[record.category] ?? 0) + 1;
421
+ const errors = records.filter((record) => record.status === "error");
422
+ const cancelled = records.filter((record) => record.status === "cancelled");
423
+ const data: ActivitySummaryData = {
424
+ version: 2,
425
+ startedAt: runStartedAt ?? records[0]!.startedAt,
426
+ durationMs: Math.max(0, settledAt - (runStartedAt ?? records[0]!.startedAt)),
427
+ actionCount: records.length,
428
+ errorCount: errors.length,
429
+ cancelledCount: cancelled.length,
430
+ truncatedCount: records.filter((record) => record.truncated).length,
431
+ modifiedFiles: aggregateFileChanges(records),
432
+ completedActions: unique(records.filter((record) => record.status === "success").map((record) => record.name)),
433
+ failedActions: unique([...errors, ...cancelled].map((record) => record.name)),
434
+ categoryCounts,
435
+ records,
436
+ };
437
+ pi.appendEntry("activity-summary", data);
438
+ history = [...history, ...runRecords].slice(-HISTORY_LIMIT);
439
+ const issueCount = data.errorCount + data.cancelledCount;
440
+ const status = issueCount
441
+ ? ctx.ui.theme.fg("warning", `! ${data.actionCount} actions · ${issueCount} issues · ${formatDuration(data.durationMs)}`)
442
+ : ctx.ui.theme.fg("success", `✓ ${data.actionCount} actions · ${data.modifiedFiles.length} files · ${formatDuration(data.durationMs)}`);
443
+ ctx.ui.setStatus("pi-activity", status);
444
+ }
445
+
446
+ runRecords = [];
447
+ recordsById.clear();
448
+ runStartedAt = undefined;
182
449
  });
183
450
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prjct.app/pi-activity",
3
- "version": "0.1.3",
3
+ "version": "0.2.1",
4
4
  "description": "Keep PI Agent tool output readable with compact, expandable activity rows and summaries of actions, errors, and changed files.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -59,10 +59,12 @@
59
59
  },
60
60
  "files": [
61
61
  "index.ts",
62
+ "src",
62
63
  "README.md",
63
64
  "LICENSE",
64
65
  "CONTRIBUTING.md",
65
66
  "CHANGELOG.md",
66
- "docs/package.md"
67
+ "docs/package.md",
68
+ "docs/releases.md"
67
69
  ]
68
70
  }