mirror of
https://github.com/wassname/pi-lgtm.git
synced 2026-06-27 15:31:29 +08:00
fix /new /resume
This commit is contained in:
+2
-2
@@ -16,13 +16,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- `on_task_complete`: each completed task is cleared individually after a few turns
|
||||
- Both auto-clear modes use a turn-based delay (matching `REMINDER_INTERVAL`) for consistent, non-jarring UX — tasks linger briefly so the user sees the completion before they disappear
|
||||
- **`AutoClearManager`** — extracted, testable class (`src/auto-clear.ts`) handling turn-based clearing logic with per-task and batch countdown tracking
|
||||
- **15 new unit tests** — full coverage of all three auto-clear modes, turn delays, dependency cleanup, batch reset, and dynamic mode switching
|
||||
- **20 new unit tests** — full coverage of all three auto-clear modes, turn delays, dependency cleanup, batch reset, dynamic mode switching, session reset, and store swap
|
||||
|
||||
### Changed
|
||||
- **Settings** — `/tasks` → Settings now shows "Auto-clear completed tasks" toggle with `never` / `on_list_complete` / `on_task_complete` values. Also configurable via `.pi/tasks-config.json`.
|
||||
|
||||
### Fixed
|
||||
- **`/new` now correctly switches to a new session task store** — `storeUpgraded` and `persistedTasksShown` flags were never reset on `session_switch`, causing the store to stay pointed at the old session file. All session-scoped state (turn counters, reminder flags, auto-clear tracking) is now reset on `/new`.
|
||||
- **`/new` and `/resume` now correctly switch session state** — `storeUpgraded` and `persistedTasksShown` flags were never reset on `session_switch`, causing the store to stay pointed at the old session file and the widget to not refresh. All session-scoped state (turn counters, reminder flags, auto-clear tracking) is now reset on both `/new` and `/resume`. Memory-mode tasks are explicitly cleared on `/new`.
|
||||
|
||||
## [0.4.0] - 2026-03-22
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ src/
|
||||
```bash
|
||||
npm install
|
||||
npm run typecheck # TypeScript validation
|
||||
npm test # Run unit tests (143 tests)
|
||||
npm test # Run unit tests (145 tests)
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@tintinweb/pi-tasks",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@tintinweb/pi-tasks",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mariozechner/pi-coding-agent": "^0.61.1",
|
||||
|
||||
+5
-5
@@ -19,7 +19,7 @@ export class AutoClearManager {
|
||||
private allCompletedAtTurn: number | null = null;
|
||||
|
||||
constructor(
|
||||
private store: TaskStore,
|
||||
private getStore: () => TaskStore,
|
||||
private getMode: () => AutoClearMode,
|
||||
/** How many turns completed tasks linger before auto-clearing. */
|
||||
private clearDelayTurns = 4,
|
||||
@@ -39,7 +39,7 @@ export class AutoClearManager {
|
||||
|
||||
/** Check if all tasks are completed and start/reset the batch countdown. */
|
||||
private checkAllCompleted(currentTurn: number): void {
|
||||
const tasks = this.store.list();
|
||||
const tasks = this.getStore().list();
|
||||
if (tasks.length > 0 && tasks.every(t => t.status === "completed")) {
|
||||
if (this.allCompletedAtTurn === null) this.allCompletedAtTurn = currentTurn;
|
||||
} else {
|
||||
@@ -68,19 +68,19 @@ export class AutoClearManager {
|
||||
|
||||
if (mode === "on_task_complete") {
|
||||
for (const [taskId, turn] of this.completedAtTurn) {
|
||||
const task = this.store.get(taskId);
|
||||
const task = this.getStore().get(taskId);
|
||||
if (!task || task.status !== "completed") {
|
||||
// Task was deleted or reverted — drop stale tracking entry
|
||||
this.completedAtTurn.delete(taskId);
|
||||
} else if (currentTurn - turn >= this.clearDelayTurns) {
|
||||
this.store.delete(taskId);
|
||||
this.getStore().delete(taskId);
|
||||
this.completedAtTurn.delete(taskId);
|
||||
cleared = true;
|
||||
}
|
||||
}
|
||||
} else if (mode === "on_list_complete" && this.allCompletedAtTurn !== null) {
|
||||
if (currentTurn - this.allCompletedAtTurn >= this.clearDelayTurns) {
|
||||
this.store.clearCompleted();
|
||||
this.getStore().clearCompleted();
|
||||
this.allCompletedAtTurn = null;
|
||||
cleared = true;
|
||||
}
|
||||
|
||||
+13
-8
@@ -167,7 +167,7 @@ export default function (pi: ExtensionAPI) {
|
||||
return prompt;
|
||||
}
|
||||
|
||||
const autoClear = new AutoClearManager(store, () => cfg.autoClearCompleted ?? "on_list_complete", AUTO_CLEAR_DELAY);
|
||||
const autoClear = new AutoClearManager(() => store, () => cfg.autoClearCompleted ?? "on_list_complete", AUTO_CLEAR_DELAY);
|
||||
|
||||
// ── Subagent completion listener ──
|
||||
// Listens for subagent lifecycle events to update task status and optionally cascade.
|
||||
@@ -341,13 +341,18 @@ export default function (pi: ExtensionAPI) {
|
||||
widget.setUICtx(ctx.ui as UICtx);
|
||||
|
||||
const isResume = event?.reason === "resume";
|
||||
if (!isResume) {
|
||||
storeUpgraded = false;
|
||||
persistedTasksShown = false;
|
||||
currentTurn = 0;
|
||||
lastTaskToolUseTurn = 0;
|
||||
reminderInjectedThisCycle = false;
|
||||
autoClear.reset();
|
||||
|
||||
// Reset session-scoped state for both /new and /resume
|
||||
storeUpgraded = false;
|
||||
persistedTasksShown = false;
|
||||
currentTurn = 0;
|
||||
lastTaskToolUseTurn = 0;
|
||||
reminderInjectedThisCycle = false;
|
||||
autoClear.reset();
|
||||
|
||||
// Memory mode has no file-backed store to switch — clear explicitly on /new
|
||||
if (!isResume && taskScope === "memory") {
|
||||
store.clearAll();
|
||||
}
|
||||
|
||||
upgradeStoreIfNeeded(ctx);
|
||||
|
||||
+42
-7
@@ -9,7 +9,7 @@ describe("auto-clear: on_task_complete mode", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
store = new TaskStore();
|
||||
manager = new AutoClearManager(store, () => "on_task_complete");
|
||||
manager = new AutoClearManager(() => store, () => "on_task_complete");
|
||||
});
|
||||
|
||||
it("does not clear completed task before REMINDER_INTERVAL turns", () => {
|
||||
@@ -98,7 +98,7 @@ describe("auto-clear: on_list_complete mode", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
store = new TaskStore();
|
||||
manager = new AutoClearManager(store, () => "on_list_complete");
|
||||
manager = new AutoClearManager(() => store, () => "on_list_complete");
|
||||
});
|
||||
|
||||
it("does not clear when some tasks are still pending", () => {
|
||||
@@ -187,7 +187,7 @@ describe("auto-clear: never mode", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
store = new TaskStore();
|
||||
manager = new AutoClearManager(store, () => "never");
|
||||
manager = new AutoClearManager(() => store, () => "never");
|
||||
});
|
||||
|
||||
it("never clears completed tasks regardless of turns", () => {
|
||||
@@ -218,7 +218,7 @@ describe("auto-clear: dynamic mode switching", () => {
|
||||
it("respects mode changes via getMode callback", () => {
|
||||
const store = new TaskStore();
|
||||
let mode: AutoClearMode = "never";
|
||||
const manager = new AutoClearManager(store, () => mode);
|
||||
const manager = new AutoClearManager(() => store, () => mode);
|
||||
|
||||
store.create("Task", "Desc");
|
||||
store.update("1", { status: "completed" });
|
||||
@@ -236,10 +236,45 @@ describe("auto-clear: dynamic mode switching", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("auto-clear: store getter (session switch)", () => {
|
||||
it("operates on the current store after swap", () => {
|
||||
let store = new TaskStore();
|
||||
const manager = new AutoClearManager(() => store, () => "on_task_complete");
|
||||
|
||||
store.create("Old task", "Desc");
|
||||
store.update("1", { status: "completed" });
|
||||
manager.trackCompletion("1", 1);
|
||||
|
||||
// Simulate session switch — swap store
|
||||
store = new TaskStore();
|
||||
store.create("New task", "Desc");
|
||||
manager.reset();
|
||||
|
||||
// Old task tracking was reset, new store has no completed tasks
|
||||
manager.onTurnStart(5);
|
||||
expect(store.list()).toHaveLength(1);
|
||||
expect(store.get("1")!.subject).toBe("New task");
|
||||
});
|
||||
|
||||
it("clears from new store, not old store", () => {
|
||||
let store = new TaskStore();
|
||||
const manager = new AutoClearManager(() => store, () => "on_task_complete");
|
||||
|
||||
// Swap to new store with a completed task
|
||||
store = new TaskStore();
|
||||
store.create("Task in new store", "Desc");
|
||||
store.update("1", { status: "completed" });
|
||||
manager.trackCompletion("1", 1);
|
||||
|
||||
manager.onTurnStart(5);
|
||||
expect(store.get("1")).toBeUndefined(); // cleared from new store
|
||||
});
|
||||
});
|
||||
|
||||
describe("auto-clear: reset (new session)", () => {
|
||||
it("reset clears per-task tracking so old completions don't fire", () => {
|
||||
const store = new TaskStore();
|
||||
const manager = new AutoClearManager(store, () => "on_task_complete");
|
||||
const manager = new AutoClearManager(() => store, () => "on_task_complete");
|
||||
|
||||
store.create("Task", "Desc");
|
||||
store.update("1", { status: "completed" });
|
||||
@@ -255,7 +290,7 @@ describe("auto-clear: reset (new session)", () => {
|
||||
|
||||
it("reset clears batch countdown so old all-completed state doesn't fire", () => {
|
||||
const store = new TaskStore();
|
||||
const manager = new AutoClearManager(store, () => "on_list_complete");
|
||||
const manager = new AutoClearManager(() => store, () => "on_list_complete");
|
||||
|
||||
store.create("Task", "Desc");
|
||||
store.update("1", { status: "completed" });
|
||||
@@ -271,7 +306,7 @@ describe("auto-clear: reset (new session)", () => {
|
||||
|
||||
it("tracking works normally after reset", () => {
|
||||
const store = new TaskStore();
|
||||
const manager = new AutoClearManager(store, () => "on_task_complete");
|
||||
const manager = new AutoClearManager(() => store, () => "on_task_complete");
|
||||
|
||||
store.create("Task", "Desc");
|
||||
store.update("1", { status: "completed" });
|
||||
|
||||
Reference in New Issue
Block a user