pi-jar 0.2.2 → 0.2.3
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/README.md +15 -12
- package/docs/EFFICIENCY_PLAN.md +20 -0
- package/docs/INTEGRATIONS.md +22 -6
- package/docs/SUBAGENT-TOOLS.md +1 -1
- package/docs/WORKFLOWS.md +2 -2
- package/extensions/index.ts +83 -16
- package/package.json +1 -1
- package/src/advisor.ts +21 -9
- package/src/ask-tool.ts +2 -7
- package/src/compact-tools.ts +148 -8
- package/src/context-budget.ts +226 -0
- package/src/context-diet.ts +158 -17
- package/src/{democracy.ts → council.ts} +19 -22
- package/src/delegate.ts +80 -50
- package/src/discussion-hub.ts +9 -6
- package/src/discussion.ts +31 -22
- package/src/footer.ts +5 -1
- package/src/goal-loop.ts +3 -0
- package/src/info-panels.ts +6 -0
- package/src/model-roles.ts +26 -2
- package/src/plan.ts +4 -2
- package/src/profile-ui.ts +21 -6
- package/src/profiles.ts +7 -3
- package/src/quota.ts +71 -49
- package/src/roles-ui.ts +4 -2
- package/src/settings-ui.ts +27 -8
- package/src/settings.ts +21 -1
- package/src/shells.ts +77 -15
- package/src/subagent-tools.ts +1 -1
- package/src/suggest.ts +4 -11
- package/src/task-tool.ts +3 -9
- package/src/tool-activation.ts +14 -0
- package/src/usage-view.ts +8 -4
- package/src/usage.ts +4 -2
package/src/suggest.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
import { Text } from "@earendil-works/pi-tui";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
4
|
import { cleanText } from "./status.ts";
|
|
5
|
+
import { setToolActive } from "./tool-activation.ts";
|
|
5
6
|
|
|
6
7
|
export const SUGGEST_TOOL = "jar_suggest";
|
|
7
8
|
const MAX_SUGGESTION = 160;
|
|
@@ -31,12 +32,8 @@ export function registerSuggestions(pi: ExtensionAPI, state: SuggestionState, op
|
|
|
31
32
|
pi.registerTool?.({
|
|
32
33
|
name: SUGGEST_TOOL,
|
|
33
34
|
label: "suggest",
|
|
34
|
-
description: "Optionally offer one useful next prompt as ghost text in the input box; Tab accepts it for editing.",
|
|
35
|
-
promptSnippet: "Optionally
|
|
36
|
-
promptGuidelines: [
|
|
37
|
-
"jar_suggest is best-effort, not mandatory. Use it only when there is one clear next action worth surfacing; skip it for routine completions.",
|
|
38
|
-
"When used, call it once as the final action with one imperative line under 120 characters. Do not mention jar_suggest in your reply."
|
|
39
|
-
],
|
|
35
|
+
description: "Optionally offer one useful next prompt (one imperative line under 120 characters) as ghost text in the input box; Tab accepts it for editing. Call once as the final action; do not mention it in your reply.",
|
|
36
|
+
promptSnippet: "Optionally end with one jar_suggest call only when one clear next prompt is worth surfacing; skip it for routine completions.",
|
|
40
37
|
parameters: Type.Object({ suggestion: Type.String({ description: "The user's likely next prompt, one line." }) }),
|
|
41
38
|
execute: async (_id, params) => {
|
|
42
39
|
const ok = state.set(params.suggestion ?? "");
|
|
@@ -50,11 +47,7 @@ export function registerSuggestions(pi: ExtensionAPI, state: SuggestionState, op
|
|
|
50
47
|
});
|
|
51
48
|
|
|
52
49
|
const sync = () => {
|
|
53
|
-
|
|
54
|
-
const active = pi.getActiveTools();
|
|
55
|
-
const has = active.includes(SUGGEST_TOOL);
|
|
56
|
-
if (options.enabled() && !has && pi.getAllTools().some((tool) => tool.name === SUGGEST_TOOL)) pi.setActiveTools([...active, SUGGEST_TOOL]);
|
|
57
|
-
else if (!options.enabled() && has) pi.setActiveTools(active.filter((name) => name !== SUGGEST_TOOL));
|
|
50
|
+
setToolActive(pi, SUGGEST_TOOL, options.enabled());
|
|
58
51
|
if (!options.enabled()) state.clear();
|
|
59
52
|
};
|
|
60
53
|
|
package/src/task-tool.ts
CHANGED
|
@@ -87,16 +87,10 @@ export function registerTaskTool(
|
|
|
87
87
|
pi.registerTool({
|
|
88
88
|
name: "jar_todo",
|
|
89
89
|
label: "tasks",
|
|
90
|
-
description: "Maintain session tasks
|
|
91
|
-
promptSnippet: "Track
|
|
90
|
+
description: "Maintain session tasks. write sets the complete current plan (omitted tasks are removed; each item has content, status, optional activeForm and one level of `subtasks`, whose completion completes the parent; progress counts leaves). update changes one task by stable id, append adds genuinely new work mid-run (not a new plan), remove deletes one task tree; start/done/open/edit are concise status operations. Stable IDs are returned for every task.",
|
|
91
|
+
promptSnippet: "Track work with 3+ steps or several requests in jar_todo (skip trivial requests and questions): write the complete plan for a new request, start fresh once the old list is completed.",
|
|
92
92
|
promptGuidelines: [
|
|
93
|
-
"
|
|
94
|
-
"Prefer write when establishing the task plan for a new request. If the existing checklist is fully completed, start the next request with a fresh write rather than appending onto old completed work.",
|
|
95
|
-
"Use update with a stable id to revise one existing task and remove to delete one task tree. Use append mainly when the user steers or adds genuinely new requirements while an active checklist is already in progress; do not use append as the default way to create a new plan.",
|
|
96
|
-
"write with todos is the complete current task plan and removes omitted tasks. Each item has content, status, and optional activeForm/subtasks.",
|
|
97
|
-
"Break a large task into `subtasks` (same shape, one level deep). A parent's status follows its subtasks: it is completed when all of them are. Progress counts leaf tasks (subtasks and tasks without subtasks).",
|
|
98
|
-
"Keep exactly one leaf task in_progress at a time. Mark a task in_progress before you start it and completed immediately after it is verified; do not batch completions.",
|
|
99
|
-
"Only mark a task completed when it is fully done. If tests fail, work is partial or you are blocked, keep it in_progress and add a task for what must be resolved. Remove tasks that are no longer relevant."
|
|
93
|
+
"Keep exactly one leaf in_progress; mark it completed right after verifying it. Never batch completions or complete partial, failing or blocked work: keep it open and add a task for the blocker."
|
|
100
94
|
],
|
|
101
95
|
parameters: Parameters,
|
|
102
96
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Activate or deactivate one registered tool. Inactive tools send neither their schema nor their
|
|
5
|
+
* prompt rules, so mode-specific tools are active only while their mode is. Pi applies a change
|
|
6
|
+
* made in before_agent_start to that run; toggling only at mode boundaries keeps the prompt stable.
|
|
7
|
+
*/
|
|
8
|
+
export function setToolActive(pi: ExtensionAPI, name: string, wanted: boolean): void {
|
|
9
|
+
if (typeof pi.getActiveTools !== "function" || typeof pi.setActiveTools !== "function") return;
|
|
10
|
+
const active = pi.getActiveTools();
|
|
11
|
+
const has = active.includes(name);
|
|
12
|
+
if (wanted && !has && pi.getAllTools().some((tool) => tool.name === name)) pi.setActiveTools([...active, name]);
|
|
13
|
+
else if (!wanted && has) pi.setActiveTools(active.filter((tool) => tool !== name));
|
|
14
|
+
}
|
package/src/usage-view.ts
CHANGED
|
@@ -53,7 +53,7 @@ function resetText(window: QuotaWindow, now: number): string {
|
|
|
53
53
|
return `Resets ${when} (in ${duration(Math.max(0, at - now))})`;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
export interface UsageView { stats: UsageStats; provider?: string; quota?: Quota; quotaEnabled: boolean; now: number }
|
|
56
|
+
export interface UsageView { stats: UsageStats; provider?: string; quota?: Quota; quotaFailure?: { error: string; retryInMs: number }; quotaEnabled: boolean; now: number }
|
|
57
57
|
|
|
58
58
|
/** Claude-style usage page: session totals, per-model breakdown, then plan limit bars. */
|
|
59
59
|
export function usageLines(view: UsageView, width: number, fg: Paint): string[] {
|
|
@@ -79,9 +79,13 @@ export function usageLines(view: UsageView, width: number, fg: Paint): string[]
|
|
|
79
79
|
lines.push("", heading(`Plan usage limits${view.provider ? " · " + view.provider : ""}`));
|
|
80
80
|
const windows: [string, QuotaWindow | undefined][] = [["Current session (5h)", view.quota?.fiveHour], ["Current week", view.quota?.week]];
|
|
81
81
|
if (!windows.some(([, window]) => window)) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
const supported = view.provider === "anthropic" || view.provider === "openai-codex";
|
|
83
|
+
const failure = view.quotaFailure;
|
|
84
|
+
lines.push(fg(supported && view.quotaEnabled && failure ? "warning" : "dim", !supported
|
|
85
|
+
? " Plan limits are available for anthropic and openai-codex subscriptions."
|
|
86
|
+
: !view.quotaEnabled ? " Limit lookup is off; enable it with /jar quota on."
|
|
87
|
+
: failure ? ` Limits unavailable: ${failure.error}. Retrying in ${duration(failure.retryInMs)}.`
|
|
88
|
+
: " Loading limits…"));
|
|
85
89
|
return lines;
|
|
86
90
|
}
|
|
87
91
|
for (const [label, window] of windows) {
|
package/src/usage.ts
CHANGED
|
@@ -12,6 +12,8 @@ export function sessionCost(ctx: Pick<ExtensionContext, "sessionManager">): numb
|
|
|
12
12
|
return total;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/** `cost $1.23`, plus the recent average per provider call when there is one: `cost $1.23 · $0.04/call`. */
|
|
16
|
+
export function formatCost(value: number, perCall?: number): string {
|
|
17
|
+
const money = (amount: number) => `$${amount < 0.01 && amount > 0 ? amount.toFixed(4) : amount.toFixed(2)}`;
|
|
18
|
+
return `cost ${money(value)}` + (perCall !== undefined && perCall > 0 ? ` · ${money(perCall)}/call` : "");
|
|
17
19
|
}
|