@trevonistrevon/pi-loop 0.5.1 → 0.5.2

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.
Files changed (39) hide show
  1. package/dist/goal-store.d.ts +2 -0
  2. package/dist/goal-store.js +16 -4
  3. package/dist/store.d.ts +2 -0
  4. package/dist/store.js +16 -4
  5. package/dist/task-store.d.ts +2 -0
  6. package/dist/task-store.js +16 -4
  7. package/dist/tools/native-task-tools.js +6 -3
  8. package/dist/ui/widget.d.ts +0 -1
  9. package/dist/ui/widget.js +1 -14
  10. package/package.json +1 -1
  11. package/src/goal-store.ts +15 -4
  12. package/src/store.ts +15 -4
  13. package/src/task-store.ts +15 -4
  14. package/src/tools/native-task-tools.ts +6 -3
  15. package/src/ui/widget.ts +1 -16
  16. package/tmp/perf-cap-data/loops.json +455 -0
  17. package/tmp/perf-cap-data/tasks.json +1 -0
  18. package/tmp/perf-data/loops-0.json +1 -0
  19. package/tmp/perf-data/loops-10.json +1 -0
  20. package/tmp/perf-data/loops-100.json +1 -0
  21. package/tmp/perf-data/loops-1000.json +1 -0
  22. package/tmp/perf-data/loops-500.json +1 -0
  23. package/tmp/perf-data/tasks-0.json +1 -0
  24. package/tmp/perf-data/tasks-10.json +1 -0
  25. package/tmp/perf-data/tasks-100.json +1 -0
  26. package/tmp/perf-data/tasks-1000.json +1 -0
  27. package/tmp/perf-data/tasks-500.json +1 -0
  28. package/tmp/perf-data-session/loops-0.json +4 -0
  29. package/tmp/perf-data-session/loops-100.json +1805 -0
  30. package/tmp/perf-data-session/loops-1000.json +18005 -0
  31. package/tmp/perf-data-session/loops-25.json +455 -0
  32. package/tmp/perf-data-session/loops-500.json +9005 -0
  33. package/tmp/perf-data-session/tasks-0.json +1 -0
  34. package/tmp/perf-data-session/tasks-100.json +1 -0
  35. package/tmp/perf-data-session/tasks-1000.json +1 -0
  36. package/tmp/perf-data-session/tasks-25.json +1 -0
  37. package/tmp/perf-data-session/tasks-500.json +1 -0
  38. package/tmp/perf-session-init.js +98 -0
  39. package/tmp/perf-startup-audit.js +80 -0
@@ -2,9 +2,11 @@ import type { GoalCriteria, GoalEntry, GoalProgressSnapshot, GoalScope } from ".
2
2
  export declare class GoalStore {
3
3
  private filePath;
4
4
  private lockPath;
5
+ private lastLoadedSignature;
5
6
  private nextId;
6
7
  private goals;
7
8
  constructor(listIdOrPath?: string);
9
+ private getFileSignature;
8
10
  private load;
9
11
  private save;
10
12
  private withLock;
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, isAbsolute, join } from "node:path";
4
4
  import { reduceGoalState } from "./goal-reducer.js";
@@ -52,6 +52,7 @@ function isProcessRunning(pid) {
52
52
  export class GoalStore {
53
53
  filePath;
54
54
  lockPath;
55
+ lastLoadedSignature;
55
56
  nextId = 1;
56
57
  goals = new Map();
57
58
  constructor(listIdOrPath) {
@@ -64,10 +65,19 @@ export class GoalStore {
64
65
  this.lockPath = filePath + ".lock";
65
66
  this.load();
66
67
  }
67
- load() {
68
+ getFileSignature() {
69
+ if (!this.filePath || !existsSync(this.filePath))
70
+ return undefined;
71
+ const stat = statSync(this.filePath);
72
+ return `${stat.mtimeMs}:${stat.size}`;
73
+ }
74
+ load(force = false) {
68
75
  if (!this.filePath)
69
76
  return;
70
- if (!existsSync(this.filePath))
77
+ const signature = this.getFileSignature();
78
+ if (!signature)
79
+ return;
80
+ if (!force && signature === this.lastLoadedSignature)
71
81
  return;
72
82
  try {
73
83
  const data = JSON.parse(readFileSync(this.filePath, "utf-8"));
@@ -76,6 +86,7 @@ export class GoalStore {
76
86
  for (const goal of data.goals) {
77
87
  this.goals.set(goal.id, goal);
78
88
  }
89
+ this.lastLoadedSignature = signature;
79
90
  }
80
91
  catch { /* corrupt file — start fresh */ }
81
92
  }
@@ -89,13 +100,14 @@ export class GoalStore {
89
100
  const tmpPath = this.filePath + ".tmp";
90
101
  writeFileSync(tmpPath, JSON.stringify(data, null, 2));
91
102
  renameSync(tmpPath, this.filePath);
103
+ this.lastLoadedSignature = this.getFileSignature();
92
104
  }
93
105
  withLock(fn) {
94
106
  if (!this.lockPath)
95
107
  return fn();
96
108
  acquireLock(this.lockPath);
97
109
  try {
98
- this.load();
110
+ this.load(true);
99
111
  const result = fn();
100
112
  this.save();
101
113
  return result;
package/dist/store.d.ts CHANGED
@@ -2,9 +2,11 @@ import type { LoopEntry, Trigger } from "./types.js";
2
2
  export declare class LoopStore {
3
3
  private filePath;
4
4
  private lockPath;
5
+ private lastLoadedSignature;
5
6
  private nextId;
6
7
  private loops;
7
8
  constructor(listIdOrPath?: string);
9
+ private getFileSignature;
8
10
  private load;
9
11
  private save;
10
12
  private withLock;
package/dist/store.js CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, isAbsolute, join } from "node:path";
4
4
  import { reduceLoopState } from "./loop-reducer.js";
@@ -52,6 +52,7 @@ function isProcessRunning(pid) {
52
52
  export class LoopStore {
53
53
  filePath;
54
54
  lockPath;
55
+ lastLoadedSignature;
55
56
  nextId = 1;
56
57
  loops = new Map();
57
58
  constructor(listIdOrPath) {
@@ -64,10 +65,19 @@ export class LoopStore {
64
65
  this.lockPath = filePath + ".lock";
65
66
  this.load();
66
67
  }
67
- load() {
68
+ getFileSignature() {
69
+ if (!this.filePath || !existsSync(this.filePath))
70
+ return undefined;
71
+ const stat = statSync(this.filePath);
72
+ return `${stat.mtimeMs}:${stat.size}`;
73
+ }
74
+ load(force = false) {
68
75
  if (!this.filePath)
69
76
  return;
70
- if (!existsSync(this.filePath))
77
+ const signature = this.getFileSignature();
78
+ if (!signature)
79
+ return;
80
+ if (!force && signature === this.lastLoadedSignature)
71
81
  return;
72
82
  try {
73
83
  const data = JSON.parse(readFileSync(this.filePath, "utf-8"));
@@ -76,6 +86,7 @@ export class LoopStore {
76
86
  for (const loop of data.loops) {
77
87
  this.loops.set(loop.id, loop);
78
88
  }
89
+ this.lastLoadedSignature = signature;
79
90
  }
80
91
  catch { /* corrupt file — start fresh */ }
81
92
  }
@@ -89,13 +100,14 @@ export class LoopStore {
89
100
  const tmpPath = this.filePath + ".tmp";
90
101
  writeFileSync(tmpPath, JSON.stringify(data, null, 2));
91
102
  renameSync(tmpPath, this.filePath);
103
+ this.lastLoadedSignature = this.getFileSignature();
92
104
  }
93
105
  withLock(fn) {
94
106
  if (!this.lockPath)
95
107
  return fn();
96
108
  acquireLock(this.lockPath);
97
109
  try {
98
- this.load();
110
+ this.load(true);
99
111
  const result = fn();
100
112
  this.save();
101
113
  return result;
@@ -2,9 +2,11 @@ import type { TaskEntry } from "./task-types.js";
2
2
  export declare class TaskStore {
3
3
  private filePath;
4
4
  private lockPath;
5
+ private lastLoadedSignature;
5
6
  private nextId;
6
7
  private tasks;
7
8
  constructor(listIdOrPath?: string);
9
+ private getFileSignature;
8
10
  private load;
9
11
  private save;
10
12
  private withLock;
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, isAbsolute, join } from "node:path";
4
4
  import { reduceTaskState } from "./task-reducer.js";
@@ -52,6 +52,7 @@ function isProcessRunning(pid) {
52
52
  export class TaskStore {
53
53
  filePath;
54
54
  lockPath;
55
+ lastLoadedSignature;
55
56
  nextId = 1;
56
57
  tasks = new Map();
57
58
  constructor(listIdOrPath) {
@@ -64,10 +65,19 @@ export class TaskStore {
64
65
  this.lockPath = filePath + ".lock";
65
66
  this.load();
66
67
  }
67
- load() {
68
+ getFileSignature() {
69
+ if (!this.filePath || !existsSync(this.filePath))
70
+ return undefined;
71
+ const stat = statSync(this.filePath);
72
+ return `${stat.mtimeMs}:${stat.size}`;
73
+ }
74
+ load(force = false) {
68
75
  if (!this.filePath)
69
76
  return;
70
- if (!existsSync(this.filePath))
77
+ const signature = this.getFileSignature();
78
+ if (!signature)
79
+ return;
80
+ if (!force && signature === this.lastLoadedSignature)
71
81
  return;
72
82
  try {
73
83
  const data = JSON.parse(readFileSync(this.filePath, "utf-8"));
@@ -76,6 +86,7 @@ export class TaskStore {
76
86
  for (const task of data.tasks) {
77
87
  this.tasks.set(task.id, task);
78
88
  }
89
+ this.lastLoadedSignature = signature;
79
90
  }
80
91
  catch { /* corrupt file — start fresh */ }
81
92
  }
@@ -89,13 +100,14 @@ export class TaskStore {
89
100
  const tmpPath = this.filePath + ".tmp";
90
101
  writeFileSync(tmpPath, JSON.stringify(data, null, 2));
91
102
  renameSync(tmpPath, this.filePath);
103
+ this.lastLoadedSignature = this.getFileSignature();
92
104
  }
93
105
  withLock(fn) {
94
106
  if (!this.lockPath)
95
107
  return fn();
96
108
  acquireLock(this.lockPath);
97
109
  try {
98
- this.load();
110
+ this.load(true);
99
111
  const result = fn();
100
112
  this.save();
101
113
  return result;
@@ -7,14 +7,17 @@ export function registerNativeTaskTools(options) {
7
7
  pi.registerTool({
8
8
  name: "TaskCreate",
9
9
  label: "TaskCreate",
10
- description: `Create a task for tracking work across turns. Use when you need to track progress on complex multi-step tasks.
10
+ description: `Create a task for tracking work across turns. Use when you need to track progress on complex multi-step tasks or turn a broad user goal into a concrete backlog.
11
11
 
12
12
  Fields:
13
13
  - subject: brief actionable title
14
- - description: detailed requirements
15
- - metadata: optional tags/metadata`,
14
+ - description: detailed requirements and done condition`,
16
15
  promptGuidelines: [
17
16
  "Use TaskCreate to track complex multi-step work across turns.",
17
+ "When the user gives a broad goal, use multiple TaskCreate calls to decompose it into a small backlog of concrete tasks rather than one oversized task.",
18
+ "Prefer 2-5 tasks that separate investigation, implementation, validation, and reporting or commit-prep when those phases are distinct.",
19
+ "Make each `subject` a short verb-object action.",
20
+ "Make each `description` include the expected artifact, outcome, or done condition so another turn can pick the task up cleanly.",
18
21
  "Break work into small, independently completable tasks. A task should be finishable in one focused session — if a task would take multiple turns, split it further.",
19
22
  "TaskCreate accepts `subject` and `description` parameters only — do not invent extra fields unless the schema explicitly adds them.",
20
23
  ],
@@ -9,7 +9,6 @@ export declare class LoopWidget {
9
9
  private store;
10
10
  private monitorManager;
11
11
  private uiCtx;
12
- private interval;
13
12
  private taskSummaryProvider;
14
13
  constructor(store: LoopStore, monitorManager: MonitorManager);
15
14
  setUICtx(ctx: ExtensionUIContext): void;
package/dist/ui/widget.js CHANGED
@@ -2,7 +2,6 @@ export class LoopWidget {
2
2
  store;
3
3
  monitorManager;
4
4
  uiCtx;
5
- interval;
6
5
  taskSummaryProvider;
7
6
  constructor(store, monitorManager) {
8
7
  this.store = store;
@@ -20,15 +19,7 @@ export class LoopWidget {
20
19
  update() {
21
20
  if (!this.uiCtx)
22
21
  return;
23
- const status = this.computeStatus();
24
- if (status && !this.interval) {
25
- this.interval = setInterval(() => this.update(), 5000);
26
- }
27
- if (!status && this.interval) {
28
- clearInterval(this.interval);
29
- this.interval = undefined;
30
- }
31
- this.uiCtx.setStatus("loops", status);
22
+ this.uiCtx.setStatus("loops", this.computeStatus());
32
23
  }
33
24
  computeStatus() {
34
25
  const loops = this.store.list().filter(l => l.status === "active");
@@ -50,10 +41,6 @@ export class LoopWidget {
50
41
  return line;
51
42
  }
52
43
  dispose() {
53
- if (this.interval) {
54
- clearInterval(this.interval);
55
- this.interval = undefined;
56
- }
57
44
  this.uiCtx?.setStatus("loops", undefined);
58
45
  }
59
46
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trevonistrevon/pi-loop",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "A pi extension for cron/event-based agent re-wake loops and background process monitoring.",
5
5
  "author": "trevonistrevon",
6
6
  "license": "MIT",
package/src/goal-store.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, isAbsolute, join } from "node:path";
4
4
  import { type GoalReducerEvent, reduceGoalState } from "./goal-reducer.js";
@@ -51,6 +51,7 @@ function isProcessRunning(pid: number): boolean {
51
51
  export class GoalStore {
52
52
  private filePath: string | undefined;
53
53
  private lockPath: string | undefined;
54
+ private lastLoadedSignature: string | undefined;
54
55
 
55
56
  private nextId = 1;
56
57
  private goals = new Map<string, GoalEntry>();
@@ -65,9 +66,17 @@ export class GoalStore {
65
66
  this.load();
66
67
  }
67
68
 
68
- private load(): void {
69
+ private getFileSignature(): string | undefined {
70
+ if (!this.filePath || !existsSync(this.filePath)) return undefined;
71
+ const stat = statSync(this.filePath);
72
+ return `${stat.mtimeMs}:${stat.size}`;
73
+ }
74
+
75
+ private load(force = false): void {
69
76
  if (!this.filePath) return;
70
- if (!existsSync(this.filePath)) return;
77
+ const signature = this.getFileSignature();
78
+ if (!signature) return;
79
+ if (!force && signature === this.lastLoadedSignature) return;
71
80
  try {
72
81
  const data: GoalStoreData = JSON.parse(readFileSync(this.filePath, "utf-8"));
73
82
  this.nextId = data.nextId;
@@ -75,6 +84,7 @@ export class GoalStore {
75
84
  for (const goal of data.goals) {
76
85
  this.goals.set(goal.id, goal);
77
86
  }
87
+ this.lastLoadedSignature = signature;
78
88
  } catch { /* corrupt file — start fresh */ }
79
89
  }
80
90
 
@@ -87,13 +97,14 @@ export class GoalStore {
87
97
  const tmpPath = this.filePath + ".tmp";
88
98
  writeFileSync(tmpPath, JSON.stringify(data, null, 2));
89
99
  renameSync(tmpPath, this.filePath);
100
+ this.lastLoadedSignature = this.getFileSignature();
90
101
  }
91
102
 
92
103
  private withLock<T>(fn: () => T): T {
93
104
  if (!this.lockPath) return fn();
94
105
  acquireLock(this.lockPath);
95
106
  try {
96
- this.load();
107
+ this.load(true);
97
108
  const result = fn();
98
109
  this.save();
99
110
  return result;
package/src/store.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, isAbsolute, join } from "node:path";
4
4
  import { type LoopReducerEvent, type LoopReducerState, reduceLoopState } from "./loop-reducer.js";
@@ -44,6 +44,7 @@ function isProcessRunning(pid: number): boolean {
44
44
  export class LoopStore {
45
45
  private filePath: string | undefined;
46
46
  private lockPath: string | undefined;
47
+ private lastLoadedSignature: string | undefined;
47
48
 
48
49
  private nextId = 1;
49
50
  private loops = new Map<string, LoopEntry>();
@@ -58,9 +59,17 @@ export class LoopStore {
58
59
  this.load();
59
60
  }
60
61
 
61
- private load(): void {
62
+ private getFileSignature(): string | undefined {
63
+ if (!this.filePath || !existsSync(this.filePath)) return undefined;
64
+ const stat = statSync(this.filePath);
65
+ return `${stat.mtimeMs}:${stat.size}`;
66
+ }
67
+
68
+ private load(force = false): void {
62
69
  if (!this.filePath) return;
63
- if (!existsSync(this.filePath)) return;
70
+ const signature = this.getFileSignature();
71
+ if (!signature) return;
72
+ if (!force && signature === this.lastLoadedSignature) return;
64
73
  try {
65
74
  const data: LoopStoreData = JSON.parse(readFileSync(this.filePath, "utf-8"));
66
75
  this.nextId = data.nextId;
@@ -68,6 +77,7 @@ export class LoopStore {
68
77
  for (const loop of data.loops) {
69
78
  this.loops.set(loop.id, loop);
70
79
  }
80
+ this.lastLoadedSignature = signature;
71
81
  } catch { /* corrupt file — start fresh */ }
72
82
  }
73
83
 
@@ -80,13 +90,14 @@ export class LoopStore {
80
90
  const tmpPath = this.filePath + ".tmp";
81
91
  writeFileSync(tmpPath, JSON.stringify(data, null, 2));
82
92
  renameSync(tmpPath, this.filePath);
93
+ this.lastLoadedSignature = this.getFileSignature();
83
94
  }
84
95
 
85
96
  private withLock<T>(fn: () => T): T {
86
97
  if (!this.lockPath) return fn();
87
98
  acquireLock(this.lockPath);
88
99
  try {
89
- this.load();
100
+ this.load(true);
90
101
  const result = fn();
91
102
  this.save();
92
103
  return result;
package/src/task-store.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, isAbsolute, join } from "node:path";
4
4
  import { reduceTaskState, type TaskReducerEvent, type TaskReducerState } from "./task-reducer.js";
@@ -44,6 +44,7 @@ function isProcessRunning(pid: number): boolean {
44
44
  export class TaskStore {
45
45
  private filePath: string | undefined;
46
46
  private lockPath: string | undefined;
47
+ private lastLoadedSignature: string | undefined;
47
48
 
48
49
  private nextId = 1;
49
50
  private tasks = new Map<string, TaskEntry>();
@@ -58,9 +59,17 @@ export class TaskStore {
58
59
  this.load();
59
60
  }
60
61
 
61
- private load(): void {
62
+ private getFileSignature(): string | undefined {
63
+ if (!this.filePath || !existsSync(this.filePath)) return undefined;
64
+ const stat = statSync(this.filePath);
65
+ return `${stat.mtimeMs}:${stat.size}`;
66
+ }
67
+
68
+ private load(force = false): void {
62
69
  if (!this.filePath) return;
63
- if (!existsSync(this.filePath)) return;
70
+ const signature = this.getFileSignature();
71
+ if (!signature) return;
72
+ if (!force && signature === this.lastLoadedSignature) return;
64
73
  try {
65
74
  const data: TaskStoreData = JSON.parse(readFileSync(this.filePath, "utf-8"));
66
75
  this.nextId = data.nextId;
@@ -68,6 +77,7 @@ export class TaskStore {
68
77
  for (const task of data.tasks) {
69
78
  this.tasks.set(task.id, task);
70
79
  }
80
+ this.lastLoadedSignature = signature;
71
81
  } catch { /* corrupt file — start fresh */ }
72
82
  }
73
83
 
@@ -80,13 +90,14 @@ export class TaskStore {
80
90
  const tmpPath = this.filePath + ".tmp";
81
91
  writeFileSync(tmpPath, JSON.stringify(data, null, 2));
82
92
  renameSync(tmpPath, this.filePath);
93
+ this.lastLoadedSignature = this.getFileSignature();
83
94
  }
84
95
 
85
96
  private withLock<T>(fn: () => T): T {
86
97
  if (!this.lockPath) return fn();
87
98
  acquireLock(this.lockPath);
88
99
  try {
89
- this.load();
100
+ this.load(true);
90
101
  const result = fn();
91
102
  this.save();
92
103
  return result;
@@ -24,14 +24,17 @@ export function registerNativeTaskTools(options: NativeTaskToolsOptions): void {
24
24
  pi.registerTool({
25
25
  name: "TaskCreate",
26
26
  label: "TaskCreate",
27
- description: `Create a task for tracking work across turns. Use when you need to track progress on complex multi-step tasks.
27
+ description: `Create a task for tracking work across turns. Use when you need to track progress on complex multi-step tasks or turn a broad user goal into a concrete backlog.
28
28
 
29
29
  Fields:
30
30
  - subject: brief actionable title
31
- - description: detailed requirements
32
- - metadata: optional tags/metadata`,
31
+ - description: detailed requirements and done condition`,
33
32
  promptGuidelines: [
34
33
  "Use TaskCreate to track complex multi-step work across turns.",
34
+ "When the user gives a broad goal, use multiple TaskCreate calls to decompose it into a small backlog of concrete tasks rather than one oversized task.",
35
+ "Prefer 2-5 tasks that separate investigation, implementation, validation, and reporting or commit-prep when those phases are distinct.",
36
+ "Make each `subject` a short verb-object action.",
37
+ "Make each `description` include the expected artifact, outcome, or done condition so another turn can pick the task up cleanly.",
35
38
  "Break work into small, independently completable tasks. A task should be finishable in one focused session — if a task would take multiple turns, split it further.",
36
39
  "TaskCreate accepts `subject` and `description` parameters only — do not invent extra fields unless the schema explicitly adds them.",
37
40
  ],
package/src/ui/widget.ts CHANGED
@@ -9,7 +9,6 @@ interface TaskSummary {
9
9
 
10
10
  export class LoopWidget {
11
11
  private uiCtx: ExtensionUIContext | undefined;
12
- private interval: ReturnType<typeof setInterval> | undefined;
13
12
  private taskSummaryProvider: (() => TaskSummary) | undefined;
14
13
 
15
14
  constructor(
@@ -31,17 +30,7 @@ export class LoopWidget {
31
30
 
32
31
  update() {
33
32
  if (!this.uiCtx) return;
34
-
35
- const status = this.computeStatus();
36
- if (status && !this.interval) {
37
- this.interval = setInterval(() => this.update(), 5000);
38
- }
39
- if (!status && this.interval) {
40
- clearInterval(this.interval);
41
- this.interval = undefined;
42
- }
43
-
44
- this.uiCtx.setStatus("loops", status);
33
+ this.uiCtx.setStatus("loops", this.computeStatus());
45
34
  }
46
35
 
47
36
  private computeStatus(): string | undefined {
@@ -64,10 +53,6 @@ export class LoopWidget {
64
53
  }
65
54
 
66
55
  dispose() {
67
- if (this.interval) {
68
- clearInterval(this.interval);
69
- this.interval = undefined;
70
- }
71
56
  this.uiCtx?.setStatus("loops", undefined);
72
57
  }
73
58
  }