pi-goal-pro 1.0.0 → 1.1.0

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 (3) hide show
  1. package/README.md +3 -2
  2. package/index.ts +35 -51
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
 
2
2
  <p align="center">
3
3
  <img src="https://img.shields.io/badge/pi-extension-8B5CF6?style=flat-square&logo=pi-hole&logoColor=white" alt="pi extension">
4
+ <img src="https://img.shields.io/npm/v/pi-goal-pro?style=flat-square&color=cb3837" alt="npm">
5
+ <img src="https://img.shields.io/github/actions/workflow/status/izzzzzi/pi-goal-pro/ci.yml?style=flat-square&branch=main" alt="CI">
4
6
  <img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="MIT">
5
- <img src="https://img.shields.io/badge/status-beta-orange?style=flat-square" alt="beta">
6
7
  </p>
7
8
 
8
9
  # pi-goal-pro 🎯
@@ -176,7 +177,7 @@ Set these at the top of `index.ts` if you need to tune behavior:
176
177
 
177
178
  | Setting | Default | Description |
178
179
  |---------|---------|-------------|
179
- | `maxAutoTurns` | `25` | Max auto-continue turns before forced pause |
180
+ | `maxAutoTurns` | `25` | Max auto-continue turns before auto-pause |
180
181
  | `noProgressTokenThreshold` | `50` | Output tokens below this = "no progress" |
181
182
  | `maxNoProgressTurns` | `2` | Consecutive no-progress turns before auto-pause |
182
183
  | `minContinueIntervalMs` | `3000` | Debounce interval between continuations |
package/index.ts CHANGED
@@ -22,7 +22,6 @@
22
22
 
23
23
  import { StringEnum } from '@earendil-works/pi-ai';
24
24
  import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';
25
- import { matchesKey } from '@earendil-works/pi-tui';
26
25
  import { Type } from 'typebox';
27
26
 
28
27
  // ─── Types ───────────────────────────────────────────────────────────────
@@ -119,7 +118,7 @@ function parseTokenBudget(input: string): { objective: string; tokenBudget: numb
119
118
  if (!Number.isFinite(num) || num <= 0) return { objective: input.trim(), tokenBudget: null };
120
119
  const mult = m[2]?.toLowerCase() === 'm' ? 1_000_000 : m[2]?.toLowerCase() === 'k' ? 1_000 : 1;
121
120
  const budget = Math.round(num * mult);
122
- const idx = m.index!;
121
+ const idx = m.index as number;
123
122
  const objective = (input.slice(0, idx) + input.slice(idx + m[0].length)).trim();
124
123
  return { objective, tokenBudget: budget };
125
124
  }
@@ -129,7 +128,7 @@ function parseMaxAutoTurns(input: string): { rest: string; maxAutoTurns: number
129
128
  if (!m) return { rest: input.trim(), maxAutoTurns: null };
130
129
  const turns = Number.parseInt(m[1], 10);
131
130
  if (!Number.isFinite(turns) || turns <= 0) return { rest: input.trim(), maxAutoTurns: null };
132
- const idx = m.index!;
131
+ const idx = m.index as number;
133
132
  const rest = (input.slice(0, idx) + input.slice(idx + m[0].length)).trim();
134
133
  return { rest, maxAutoTurns: turns };
135
134
  }
@@ -692,65 +691,50 @@ Do not call update_goal unless the goal is actually complete.`;
692
691
 
693
692
  // ── Message renderer for goal events ───────────────────────────────
694
693
 
694
+ const GOAL_KIND_LABELS: Record<string, (th: typeof theme) => string> = {
695
+ active: (th) => th.fg('accent', 'active'),
696
+ continuation: (th) => th.fg('muted', 'continuing'),
697
+ paused: (th) => th.fg('warning', 'paused'),
698
+ resumed: (th) => th.fg('accent', 'resumed'),
699
+ cleared: (th) => th.fg('dim', 'cleared'),
700
+ budget_limited: (th) => th.fg('warning', 'budget'),
701
+ complete: (th) => th.fg('success', 'achieved'),
702
+ unmet: (th) => th.fg('error', 'unmet'),
703
+ };
704
+
695
705
  pi.registerMessageRenderer(`${GOAL_STORAGE_TYPE}:event`, (message, options, theme) => {
696
706
  const details = message.details as GoalEvent | undefined;
697
707
  const kind = details?.kind ?? 'continuation';
698
708
  const state = details?.goal ?? null;
699
709
 
700
- const box = new (class {
701
- private children: import('@earendil-works/pi-tui').Component[] = [];
702
-
703
- render(_width: number): string[] {
704
- const lines: string[] = [];
705
- const isExpanded = options.expanded;
706
- const prefix = theme.fg('accent', theme.bold('Goal'));
707
- const kindLabel = this.kindLabel(kind, theme);
708
- const statusText = theme.fg('dim', isExpanded ? '' : '(ctrl+o to expand)');
709
-
710
- lines.push(`${prefix} ${kindLabel} ${!isExpanded ? statusText : ''}`);
711
-
712
- if (isExpanded && state) {
713
- lines.push(`${theme.fg('dim', ' Status: ')}${theme.fg('text', kind)}`);
714
- lines.push(`${theme.fg('dim', ' Goal: ')}${theme.fg('text', state.objective)}`);
715
- if (state.completionEvidence) {
716
- lines.push(`${theme.fg('dim', ' Evidence: ')}${theme.fg('success', state.completionEvidence)}`);
717
- }
718
- if (state.blocker) {
719
- lines.push(`${theme.fg('dim', ' Blocker: ')}${theme.fg('warning', state.blocker)}`);
720
- }
721
- const usage = state.tokenBudget
722
- ? `${formatTokens(state.tokensUsed)}/${formatTokens(state.tokenBudget)}`
723
- : formatDuration(state.timeUsedMs);
724
- lines.push(`${theme.fg('dim', ' Usage: ')}${theme.fg('text', usage)}`);
725
- }
726
-
727
- return lines;
728
- }
710
+ const renderGoalEvent = (_width: number): string[] => {
711
+ const lines: string[] = [];
712
+ const isExpanded = options.expanded;
713
+ const prefix = theme.fg('accent', theme.bold('Goal'));
714
+ const kindLabel = (GOAL_KIND_LABELS[kind] ?? ((th: typeof theme) => th.fg('text', kind)))(theme);
715
+ const statusText = theme.fg('dim', isExpanded ? '' : '(ctrl+o to expand)');
729
716
 
730
- invalidate(): void {}
717
+ lines.push(`${prefix} ${kindLabel} ${!isExpanded ? statusText : ''}`);
731
718
 
732
- handleInput?(data: string): void {
733
- if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {
734
- // no-op, let parent handle
719
+ if (isExpanded && state) {
720
+ lines.push(`${theme.fg('dim', ' Status: ')}${theme.fg('text', kind)}`);
721
+ lines.push(`${theme.fg('dim', ' Goal: ')}${theme.fg('text', state.objective)}`);
722
+ if (state.completionEvidence) {
723
+ lines.push(`${theme.fg('dim', ' Evidence: ')}${theme.fg('success', state.completionEvidence)}`);
735
724
  }
725
+ if (state.blocker) {
726
+ lines.push(`${theme.fg('dim', ' Blocker: ')}${theme.fg('warning', state.blocker)}`);
727
+ }
728
+ const usage = state.tokenBudget
729
+ ? `${formatTokens(state.tokensUsed)}/${formatTokens(state.tokenBudget)}`
730
+ : formatDuration(state.timeUsedMs);
731
+ lines.push(`${theme.fg('dim', ' Usage: ')}${theme.fg('text', usage)}`);
736
732
  }
737
733
 
738
- private kindLabel(k: string, th: typeof theme): string {
739
- const labels: Record<string, string> = {
740
- active: th.fg('accent', 'active'),
741
- continuation: th.fg('muted', 'continuing'),
742
- paused: th.fg('warning', 'paused'),
743
- resumed: th.fg('accent', 'resumed'),
744
- cleared: th.fg('dim', 'cleared'),
745
- budget_limited: th.fg('warning', 'budget'),
746
- complete: th.fg('success', 'achieved'),
747
- unmet: th.fg('error', 'unmet'),
748
- };
749
- return labels[k] ?? k;
750
- }
751
- })();
734
+ return lines;
735
+ };
752
736
 
753
- return box;
737
+ return { render: renderGoalEvent, invalidate: () => {} };
754
738
  });
755
739
 
756
740
  // ── Commands ───────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-pro",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Persistent autonomous goals for Pi — with no-progress detection, evidence-based completion, token budgets, and auto-continuation",
5
5
  "type": "module",
6
6
  "main": "./index.ts",