@thurstonsand/pi-librarian 0.3.1 → 0.4.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.
package/RELEASE.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  # Release notes
4
4
 
5
+ ## 0.4.0
6
+
7
+ ### Added
8
+
9
+ - Added compact, expandable transcript entries for `/librarian` attach and detach changes, including the exact repo tools affected.
10
+ - Added support for Pi's `max` thinking level.
11
+
12
+ ### Changed
13
+
14
+ - Changed `librarian.model` to use Pi's native model-pattern resolution.
15
+ - Raised the minimum supported Pi version to 0.80.6.
16
+ - Changed persisted attach entries to require tool snapshots. Older attach entries are now ignored.
17
+
5
18
  ## 0.3.1
6
19
 
7
20
  ### Changed
@@ -0,0 +1,171 @@
1
+ # Pi 0.80.6 attach entries, thinking, and model resolution
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ ## Decision Summary
8
+
9
+ Adopt Pi 0.80.6's custom-entry renderer, CLI model resolver, and `max` thinking level. Attach state changes become durable transcript accordions with historical tool snapshots; configured models follow Pi's native model-pattern semantics and fall back to the current session model only when Pi cannot resolve them.
10
+
11
+ ## Problem Statement / Background
12
+
13
+ Pi-librarian already persists `/librarian` attach state as `pi-librarian:attach` custom entries, but those entries are invisible in the transcript. The command compensates with transient notifications, so a resumed session can restore its state without showing when or how the state changed.
14
+
15
+ Pi 0.80.4 added `registerEntryRenderer`, which can present state records without sending them to the model. The rendered entry must snapshot the affected tool names so historical entries remain accurate if pi-librarian's attachable toolset changes later; older incomplete shapes are deliberately unsupported.
16
+
17
+ Configured librarian models currently use local fuzzy matching over `modelRegistry.getAvailable()`. Pi exports `resolveCliModel`, the canonical resolver for exact references, bare and provider-scoped fuzzy patterns, and custom model IDs. Pi-librarian should use that behavior directly rather than impose a separate availability policy.
18
+
19
+ Pi 0.80.6 extends the public `ThinkingLevel` type with `max`. Pi exposes model-specific `getSupportedThinkingLevels`, but no public runtime list of every accepted setting value, so settings validation must keep one local runtime list aligned with Pi's public type.
20
+
21
+ ## Goals
22
+
23
+ - Make actual attach and detach changes visible as durable, compact transcript history without duplicate notifications.
24
+ - Preserve the exact toolset affected by each new historical entry.
25
+ - Reject attach entries that do not match the current data shape without migration behavior.
26
+ - Use Pi's canonical configured-model resolution and fallback to the current session model only on resolution failure.
27
+ - Make every configured-model fallback visible and actionable.
28
+ - Accept and preserve Pi 0.80.6's `max` thinking level from settings through nested runs.
29
+
30
+ ## Non-Goals
31
+
32
+ - Persist `/librarian status` queries or repeated explicit `on`/`off` no-ops.
33
+ - Send attach state entries to the LLM.
34
+ - Change nested-run extension loading to use `InlineExtension`.
35
+ - Add a separate authentication or availability gate after Pi resolves a configured model.
36
+ - Choose a different fallback model when no current session model exists.
37
+
38
+ ## Exposed Shape
39
+
40
+ ### `/librarian` command feedback
41
+
42
+ An actual state change appends one `pi-librarian:attach` custom entry and does not also show a notification. `/librarian status` remains a transient notification. `/librarian on` while attached and `/librarian off` while detached remain transient no-ops, using symmetrical wording:
43
+
44
+ - `Librarian tools already attached.`
45
+ - `Librarian tools already detached.`
46
+
47
+ ### Attach transcript entry
48
+
49
+ The compact line is stable when expanded:
50
+
51
+ - `Librarian tools attached`
52
+ - `Librarian tools detached`
53
+
54
+ Attached state uses success styling; detached state uses muted styling. Expanding a current entry reveals the exact affected tool names beneath the unchanged compact line. Tool names appear in their registered order.
55
+
56
+ New entries persist:
57
+
58
+ ```ts
59
+ {
60
+ attached: boolean;
61
+ tools: string[];
62
+ }
63
+ ```
64
+
65
+ Both attach and detach entries snapshot the complete affected tool list. Any entry that does not match the complete current shape renders `Librarian attach state unavailable` and does not influence restored state.
66
+
67
+ ### Thinking-level settings
68
+
69
+ `librarian.thinkingLevel` accepts `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. The selected value crosses settings, model resolution, and nested-run boundaries unchanged; provider/model capability handling remains Pi's responsibility. Because Pi has no public runtime constant for the complete setting vocabulary, pi-librarian keeps one local list checked against Pi's public `ThinkingLevel` type.
70
+
71
+ ### Configured model resolution
72
+
73
+ `librarian.model` accepts the same patterns as Pi: exact or fuzzy bare patterns such as `opus`, provider-scoped patterns such as `anthropic/opus`, and custom model IDs supported by `resolveCliModel`. `resolveLibrarianModel` passes the pattern directly to Pi against `modelRegistry.getAll()` and accepts any returned model. Resolver warnings, including custom-ID fallback warnings, are shown without rejecting the model.
74
+
75
+ When configured resolution returns an error or no model, each librarian invocation:
76
+
77
+ 1. falls back to `ctx.model` when present;
78
+ 2. emits a warning naming the configured model, the failure reason, and the current fallback model; and
79
+ 3. runs with the requested librarian thinking level unchanged.
80
+
81
+ If no current model exists, the librarian tool retains its existing execution error. No warning is needed in the successful configured-model or unconfigured current-model paths.
82
+
83
+ ## Design Decisions
84
+
85
+ ### 1. Entries record state changes, not every command response
86
+
87
+ Only actual state changes belong in durable transcript history. Status queries and repeated explicit states do not change session state, so notifications remain the appropriate surface. Removing the transition notification avoids presenting the same event twice.
88
+
89
+ ### 2. New entries snapshot the affected tools
90
+
91
+ Deriving names from the current `ATTACHABLE_TOOL_NAMES` would make old transcript entries change meaning after the toolset evolves. Storing the names adds minor duplication but preserves historical truth. Detach entries retain the list because removal has the same affected scope as attachment.
92
+
93
+ ### 3. Entry rendering has no compatibility shape
94
+
95
+ Attach entries either match the complete current `{ attached, tools }` shape or render the generic unavailable-state line. Carrying a legacy branch would preserve data that cannot satisfy the historical-toolset requirement.
96
+
97
+ ### 4. Expansion is additive
98
+
99
+ Expanded rendering keeps the compact line unchanged and adds details below it. This treats transcript expansion as an accordion rather than substituting one representation for another.
100
+
101
+ ### 5. Pi owns model resolution
102
+
103
+ `resolveCliModel` owns provider normalization, fuzzy matching, priority, and custom model-ID fallback. Pi-librarian accepts its result directly. Authentication and provider failures belong to nested execution rather than a second model-selection policy.
104
+
105
+ ### 6. Fallback warnings occur on every affected invocation
106
+
107
+ Repeated warnings are intentional. Every run that differs from configured intent should say so at the point of use; silently deduplicating could hide a continuing configuration problem from later research runs.
108
+
109
+ ## Edge Cases & Failure Modes
110
+
111
+ - **Old or malformed attach entry:** does not alter restored state and renders a neutral unavailable-state line.
112
+ - **Toolset changes after an entry was written:** new rendering uses the entry's snapshot, preserving historical scope.
113
+ - **Repeated explicit state:** appends no entry and reports that tools are already attached or detached.
114
+ - **Unknown provider:** warns with Pi's resolution reason and falls back to the current model.
115
+ - **Resolved but unauthenticated model:** is selected; nested execution reports any resulting authentication failure.
116
+ - **Resolver-created custom model ID:** is selected and Pi's warning is shown.
117
+ - **No configured model:** uses the current model without warning.
118
+ - **No usable configured or current model:** librarian execution throws the existing no-model error.
119
+
120
+ ## Alternatives
121
+
122
+ ### Keep transient transition notifications alongside entries
123
+
124
+ - **Status:** Rejected
125
+ - **Decision:** It duplicates one event in two UI surfaces. The durable entry is sufficient feedback for an actual state change.
126
+
127
+ ### Derive tool names while rendering
128
+
129
+ - **Status:** Rejected
130
+ - **Decision:** Historical entries would silently change when the attachable toolset changes.
131
+
132
+ ### Version the entry immediately
133
+
134
+ - **Status:** Rejected
135
+ - **Decision:** There is one accepted shape and no migration behavior. A version adds ceremony without distinguishing supported semantics.
136
+
137
+ ### Filter resolved models through `getAvailable()`
138
+
139
+ - **Status:** Rejected
140
+ - **Decision:** It diverges from Pi's native model-pattern behavior and blocks intentional custom or differently authenticated models. Nested execution owns operational model failures.
141
+
142
+ ### Warn once at startup or once per session
143
+
144
+ - **Status:** Rejected
145
+ - **Decision:** Each resolver warning or fallback should remain explicit at its point of use.
146
+
147
+ ## Implementation Plan
148
+
149
+ - [x] Phase 1: Raise the Pi dependency floor
150
+ - Goal: Make the new Pi APIs and thinking level part of pi-librarian's supported runtime contract.
151
+ - Files: `package.json`, `package-lock.json`, `extensions/librarian/settings.ts`, tests.
152
+ - Work: Raise relevant `@earendil-works/pi-*` development and peer dependency minimums to 0.80.6, refresh the npm lockfile, and accept `max` through a local runtime list checked against Pi's public `ThinkingLevel` type.
153
+ - Validation: Inspect root constraints and resolved package versions; include package validation in the full quality gate.
154
+
155
+ - [x] Phase 2: Render durable attach state
156
+ - Goal: Replace duplicate transition notifications with strict-shape transcript accordions.
157
+ - Files: `extensions/librarian/attach.ts`, `extensions/librarian.ts`, `test/attach.test.ts`.
158
+ - Work: Define the current entry shape; snapshot tool names from `ATTACHABLE_TOOL_NAMES`; register the entry renderer; preserve compact content while expanded; render every non-current shape generically; remove actual-transition notifications; make no-op wording symmetrical.
159
+ - Validation: Focused tests for state restoration, persisted snapshots, compact and expanded rendering, generic invalid-shape rendering, and active-tool mutation.
160
+
161
+ - [x] Phase 3: Adopt canonical model resolution
162
+ - Goal: Use `resolveCliModel` with Pi-native model-pattern and fallback behavior.
163
+ - Files: `extensions/librarian/model.ts`, `extensions/librarian.ts`, `test/model.test.ts`.
164
+ - Work: Resolve bare and provider-scoped configured patterns through Pi; accept returned models and warnings directly; return a specific warning when falling back to the current model; preserve no-model behavior.
165
+ - Validation: Focused tests for bare and provider-scoped fuzzy resolution, Pi model priority, custom-ID fallback, resolution errors, warning text, current fallback, missing fallback, and preservation of `max`.
166
+
167
+ - [x] Phase 4: Integration validation
168
+ - Goal: Prove the upgrade works as one coherent change.
169
+ - Files: implementation and test files above; design status only if implementation materially diverges.
170
+ - Work: Run focused tests, typecheck against the upgraded Pi API, and inspect the final staged/unstaged split without altering it.
171
+ - Validation: `npm test -- test/attach.test.ts test/model.test.ts test/settings.test.ts`; `npm run check`.
@@ -1,21 +1,28 @@
1
- import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
1
+ import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
2
+ import { type Component, Text } from "@earendil-works/pi-tui";
2
3
  import { Type } from "typebox";
3
4
  import { safeParseTypeBoxValue } from "../shared/typebox.ts";
4
5
  import { ATTACHABLE_TOOL_NAMES } from "./tools/names.ts";
5
6
 
6
7
  export const ATTACH_ENTRY_TYPE = "pi-librarian:attach";
7
8
 
8
- const ATTACH_STATE_SCHEMA = Type.Object({
9
+ const ATTACH_ENTRY_SCHEMA = Type.Object({
9
10
  attached: Type.Boolean(),
11
+ tools: Type.Array(Type.String()),
10
12
  });
11
13
 
14
+ export interface AttachEntryData {
15
+ attached: boolean;
16
+ tools: string[];
17
+ }
18
+
12
19
  export function readAttachState(ctx: ExtensionContext): boolean {
13
20
  let attached = false;
14
21
  for (const entry of ctx.sessionManager.getEntries()) {
15
22
  if (entry.type !== "custom" || entry.customType !== ATTACH_ENTRY_TYPE) {
16
23
  continue;
17
24
  }
18
- const parsed = safeParseTypeBoxValue(ATTACH_STATE_SCHEMA, entry.data);
25
+ const parsed = safeParseTypeBoxValue(ATTACH_ENTRY_SCHEMA, entry.data);
19
26
  if (parsed) {
20
27
  attached = parsed.attached;
21
28
  }
@@ -37,5 +44,25 @@ export function applyAttachState(pi: ExtensionAPI, attached: boolean): void {
37
44
 
38
45
  export function setAttachState(pi: ExtensionAPI, attached: boolean): void {
39
46
  applyAttachState(pi, attached);
40
- pi.appendEntry(ATTACH_ENTRY_TYPE, { attached });
47
+ pi.appendEntry<AttachEntryData>(ATTACH_ENTRY_TYPE, {
48
+ attached,
49
+ tools: [...ATTACHABLE_TOOL_NAMES],
50
+ });
51
+ }
52
+
53
+ export function renderAttachEntry(data: unknown, expanded: boolean, theme: Theme): Component {
54
+ const entry = safeParseTypeBoxValue(ATTACH_ENTRY_SCHEMA, data);
55
+ if (!entry) {
56
+ return new Text(theme.fg("warning", "Librarian attach state unavailable"), 0, 0);
57
+ }
58
+
59
+ const state = entry.attached ? "attached" : "detached";
60
+ const color = entry.attached ? "success" : "muted";
61
+ let text = theme.fg(color, `Librarian tools ${state}`);
62
+
63
+ if (expanded) {
64
+ text += `\n${entry.tools.map((tool) => theme.fg("dim", ` ${tool}`)).join("\n")}`;
65
+ }
66
+
67
+ return new Text(text, 0, 0);
41
68
  }
@@ -1,6 +1,6 @@
1
1
  import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
2
2
  import type { Api, Model } from "@earendil-works/pi-ai";
3
- import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
+ import { type ExtensionContext, resolveCliModel } from "@earendil-works/pi-coding-agent";
4
4
  import type { ModelReference } from "./settings.ts";
5
5
 
6
6
  export type LibrarianModelSource = "configured" | "current";
@@ -9,31 +9,21 @@ export interface LibrarianModelResolution {
9
9
  model: Model<Api>;
10
10
  thinkingLevel: ThinkingLevel;
11
11
  source: LibrarianModelSource;
12
+ warning?: string;
12
13
  }
13
14
 
14
- function isAlias(modelId: string): boolean {
15
- return modelId.endsWith("-latest") || !/-\d{8}$/.test(modelId);
16
- }
17
-
18
- function bestModelMatch(models: Model<Api>[], pattern: string): Model<Api> | undefined {
19
- const normalizedPattern = pattern.toLowerCase();
20
- const exactMatches = models.filter((model) => model.id.toLowerCase() === normalizedPattern);
21
- if (exactMatches.length === 1) {
22
- return exactMatches[0];
15
+ function configuredModelFailure(
16
+ configuredModel: ModelReference,
17
+ error: string | undefined,
18
+ warning: string | undefined,
19
+ ): string {
20
+ if (error) {
21
+ return error;
23
22
  }
24
-
25
- const partialMatches = models.filter(
26
- (model) =>
27
- model.id.toLowerCase().includes(normalizedPattern) ||
28
- model.name?.toLowerCase().includes(normalizedPattern),
29
- );
30
- if (partialMatches.length === 0) {
31
- return undefined;
23
+ if (warning) {
24
+ return warning;
32
25
  }
33
-
34
- const aliases = partialMatches.filter((model) => isAlias(model.id));
35
- const candidates = aliases.length > 0 ? aliases : partialMatches;
36
- return candidates.toSorted((a, b) => b.id.localeCompare(a.id))[0];
26
+ return `Could not resolve configured model "${configuredModel}".`;
37
27
  }
38
28
 
39
29
  export function resolveLibrarianModel(
@@ -41,19 +31,41 @@ export function resolveLibrarianModel(
41
31
  configuredModel: ModelReference | undefined,
42
32
  thinkingLevel: ThinkingLevel,
43
33
  ): LibrarianModelResolution | undefined {
34
+ let failure: string | undefined;
35
+
44
36
  if (configuredModel) {
45
- const providerModels = ctx.modelRegistry
46
- .getAvailable()
47
- .filter((model) => model.provider === configuredModel.provider);
48
- const match = bestModelMatch(providerModels, configuredModel.modelId);
49
- if (match) {
50
- return { model: match, thinkingLevel, source: "configured" };
37
+ const resolved = resolveCliModel({
38
+ ...(configuredModel.provider ? { cliProvider: configuredModel.provider } : {}),
39
+ cliModel: configuredModel.modelId,
40
+ modelRegistry: ctx.modelRegistry,
41
+ });
42
+ if (resolved.model) {
43
+ const resolution: LibrarianModelResolution = {
44
+ model: resolved.model,
45
+ thinkingLevel,
46
+ source: "configured",
47
+ };
48
+ if (resolved.warning) {
49
+ resolution.warning = resolved.warning;
50
+ }
51
+ return resolution;
51
52
  }
53
+
54
+ failure = configuredModelFailure(configuredModel, resolved.error, resolved.warning);
52
55
  }
53
56
 
54
57
  if (!ctx.model) {
55
58
  return undefined;
56
59
  }
57
60
 
58
- return { model: ctx.model, thinkingLevel, source: "current" };
61
+ if (!configuredModel) {
62
+ return { model: ctx.model, thinkingLevel, source: "current" };
63
+ }
64
+
65
+ return {
66
+ model: ctx.model,
67
+ thinkingLevel,
68
+ source: "current",
69
+ warning: `Configured librarian model "${configuredModel}" is unavailable: ${failure} Using current model "${ctx.model.provider}/${ctx.model.id}".`,
70
+ };
59
71
  }
@@ -5,13 +5,24 @@ import { SettingsManager } from "@earendil-works/pi-coding-agent";
5
5
  import { type Static, Type } from "typebox";
6
6
  import { parseTypeBoxValue } from "../shared/typebox.ts";
7
7
 
8
+ const THINKING_LEVELS = [
9
+ "off",
10
+ "minimal",
11
+ "low",
12
+ "medium",
13
+ "high",
14
+ "xhigh",
15
+ "max",
16
+ ] as const satisfies readonly ThinkingLevel[];
17
+
8
18
  const THINKING_LEVEL_SCHEMA = Type.Union([
9
- Type.Literal("off"),
10
- Type.Literal("minimal"),
11
- Type.Literal("low"),
12
- Type.Literal("medium"),
13
- Type.Literal("high"),
14
- Type.Literal("xhigh"),
19
+ Type.Literal(THINKING_LEVELS[0]),
20
+ Type.Literal(THINKING_LEVELS[1]),
21
+ Type.Literal(THINKING_LEVELS[2]),
22
+ Type.Literal(THINKING_LEVELS[3]),
23
+ Type.Literal(THINKING_LEVELS[4]),
24
+ Type.Literal(THINKING_LEVELS[5]),
25
+ Type.Literal(THINKING_LEVELS[6]),
15
26
  ]);
16
27
 
17
28
  const LIBRARIAN_FILE_SETTINGS_SCHEMA = Type.Object({
@@ -35,12 +46,12 @@ type LibrarianFileSettings = Static<typeof LIBRARIAN_FILE_SETTINGS_SCHEMA>;
35
46
 
36
47
  export class ModelReference {
37
48
  constructor(
38
- readonly provider: string,
49
+ readonly provider: string | undefined,
39
50
  readonly modelId: string,
40
51
  ) {}
41
52
 
42
53
  toString(): string {
43
- return `${this.provider}/${this.modelId}`;
54
+ return this.provider ? `${this.provider}/${this.modelId}` : this.modelId;
44
55
  }
45
56
  }
46
57
 
@@ -92,7 +103,10 @@ function parseModelReference(value: string | undefined): ModelReference | undefi
92
103
  }
93
104
 
94
105
  const slashIndex = trimmed.indexOf("/");
95
- if (slashIndex <= 0 || slashIndex === trimmed.length - 1) {
106
+ if (slashIndex === -1) {
107
+ return new ModelReference(undefined, trimmed);
108
+ }
109
+ if (slashIndex === 0 || slashIndex === trimmed.length - 1) {
96
110
  return undefined;
97
111
  }
98
112
 
@@ -1,7 +1,14 @@
1
1
  import type { ExtensionAPI, SessionStartEvent } from "@earendil-works/pi-coding-agent";
2
2
  import { Text } from "@earendil-works/pi-tui";
3
3
  import { Type } from "typebox";
4
- import { applyAttachState, readAttachState, setAttachState } from "./librarian/attach.ts";
4
+ import {
5
+ ATTACH_ENTRY_TYPE,
6
+ type AttachEntryData,
7
+ applyAttachState,
8
+ readAttachState,
9
+ renderAttachEntry,
10
+ setAttachState,
11
+ } from "./librarian/attach.ts";
5
12
  import { collectExtraToolWarnings, resolveExtraTools } from "./librarian/extra-tools.ts";
6
13
  import { createGitHubClientProvider } from "./librarian/github.ts";
7
14
  import { resolveLibrarianModel } from "./librarian/model.ts";
@@ -50,6 +57,10 @@ export default function librarianExtension(pi: ExtensionAPI): void {
50
57
  const settings = loadSettings();
51
58
  const githubClient = createGitHubClientProvider();
52
59
 
60
+ pi.registerEntryRenderer<AttachEntryData>(ATTACH_ENTRY_TYPE, (entry, { expanded }, theme) =>
61
+ renderAttachEntry(entry.data, expanded, theme),
62
+ );
63
+
53
64
  const attachableTools = [
54
65
  createSearchReposTool(githubClient),
55
66
  searchCodeTool,
@@ -94,6 +105,9 @@ export default function librarianExtension(pi: ExtensionAPI): void {
94
105
  "No model available for the librarian. Configure librarian.model or select a session model.",
95
106
  );
96
107
  }
108
+ if (resolution.warning) {
109
+ ctx.ui.notify(resolution.warning, "warning");
110
+ }
97
111
 
98
112
  const extraTools = resolveExtraTools(pi.getAllTools(), settings);
99
113
 
@@ -167,19 +181,13 @@ export default function librarianExtension(pi: ExtensionAPI): void {
167
181
  argument === "on" ? true : argument === "off" ? false : !currentlyAttached;
168
182
  if (nextAttached === currentlyAttached) {
169
183
  ctx.ui.notify(
170
- nextAttached ? "Librarian tools already attached." : "Librarian tools not attached.",
184
+ nextAttached ? "Librarian tools already attached." : "Librarian tools already detached.",
171
185
  "info",
172
186
  );
173
187
  return;
174
188
  }
175
189
 
176
190
  setAttachState(pi, nextAttached);
177
- ctx.ui.notify(
178
- nextAttached
179
- ? `Attached librarian tools: ${ATTACHABLE_TOOL_NAMES.join(", ")}`
180
- : "Detached librarian tools.",
181
- "info",
182
- );
183
191
  },
184
192
  });
185
193
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thurstonsand/pi-librarian",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "GitHub research subagent for pi: deep-dive specific repos, discover across the ecosystem",
6
6
  "license": "MIT",
@@ -43,10 +43,10 @@
43
43
  },
44
44
  "devDependencies": {
45
45
  "@biomejs/biome": "^2.4.14",
46
- "@earendil-works/pi-agent-core": "^0.80.2",
47
- "@earendil-works/pi-ai": "^0.80.2",
48
- "@earendil-works/pi-coding-agent": "^0.80.2",
49
- "@earendil-works/pi-tui": "^0.80.2",
46
+ "@earendil-works/pi-agent-core": "^0.80.6",
47
+ "@earendil-works/pi-ai": "^0.80.6",
48
+ "@earendil-works/pi-coding-agent": "^0.80.6",
49
+ "@earendil-works/pi-tui": "^0.80.6",
50
50
  "@types/node": "^25.6.2",
51
51
  "husky": "^9.1.7",
52
52
  "lint-staged": "^17.0.3",
@@ -55,10 +55,10 @@
55
55
  "vitest": "^4.1.5"
56
56
  },
57
57
  "peerDependencies": {
58
- "@earendil-works/pi-agent-core": ">=0.80.2",
59
- "@earendil-works/pi-ai": ">=0.80.2",
60
- "@earendil-works/pi-coding-agent": ">=0.80.2",
61
- "@earendil-works/pi-tui": ">=0.80.2",
58
+ "@earendil-works/pi-agent-core": ">=0.80.6",
59
+ "@earendil-works/pi-ai": ">=0.80.6",
60
+ "@earendil-works/pi-coding-agent": ">=0.80.6",
61
+ "@earendil-works/pi-tui": ">=0.80.6",
62
62
  "typebox": ">=1.1.24"
63
63
  },
64
64
  "lint-staged": {