one-code-extension 0.1.3 → 0.3.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/LICENSE +20 -0
- package/README.md +200 -172
- package/agents/explore.md +9 -6
- package/agents/general-purpose.md +1 -1
- package/agents/plan.md +6 -4
- package/docs/guide/README.md +52 -0
- package/docs/guide/bring-your-claude-code-setup.md +126 -0
- package/docs/guide/configuration.md +171 -0
- package/docs/guide/differences-from-claude-code.md +150 -0
- package/docs/guide/doctor.md +86 -0
- package/docs/guide/hooks.md +109 -0
- package/docs/guide/installation.md +124 -0
- package/docs/guide/permissions-modes-and-auto-mode.md +450 -0
- package/docs/guide/providers-and-models.md +194 -0
- package/docs/guide/reference.md +224 -0
- package/docs/guide/sessions-and-context.md +190 -0
- package/docs/guide/skills-plugins-and-mcp.md +176 -0
- package/docs/guide/subagents-and-workflows.md +193 -0
- package/docs/guide/tasks-and-background-work.md +111 -0
- package/docs/guide/terminal-interface.md +155 -0
- package/docs/guide/tools.md +175 -0
- package/docs/guide/troubleshooting.md +175 -0
- package/extensions/ask-user/index.ts +65 -71
- package/extensions/ask-user/questions.ts +15 -28
- package/extensions/ask-user/widget.ts +533 -0
- package/extensions/auto-mode/actions.ts +9 -26
- package/extensions/auto-mode/classifier-prompt.ts +127 -0
- package/extensions/auto-mode/classifier.ts +305 -227
- package/extensions/auto-mode/config.ts +279 -77
- package/extensions/auto-mode/defaults.ts +17 -60
- package/extensions/auto-mode/instructions.ts +2 -1
- package/extensions/auto-mode/model-picker.ts +53 -31
- package/extensions/auto-mode/model-select.ts +153 -82
- package/extensions/auto-mode/paths.ts +13 -1
- package/extensions/auto-mode/pause.ts +8 -0
- package/extensions/auto-mode/prompt.ts +205 -238
- package/extensions/auto-mode/recoverability.ts +229 -0
- package/extensions/auto-mode/rules.ts +92 -75
- package/extensions/auto-mode/safety-floor.ts +43 -12
- package/extensions/auto-mode/sensitive.ts +11 -0
- package/extensions/auto-mode/setup-run.ts +245 -0
- package/extensions/auto-mode/setup.ts +235 -0
- package/extensions/auto-mode/shell-analysis.ts +332 -26
- package/extensions/auto-mode/transcript.ts +102 -0
- package/extensions/background/index.ts +256 -43
- package/extensions/background/monitor-batch.ts +58 -0
- package/extensions/background/registry.ts +9 -0
- package/extensions/background/wakeup.ts +45 -0
- package/extensions/bash/background.ts +47 -22
- package/extensions/bash/guards.ts +229 -0
- package/extensions/bash/index.ts +81 -19
- package/extensions/branding/index.ts +44 -18
- package/extensions/branding/startup.ts +23 -8
- package/extensions/claude-compat/index.ts +17 -2
- package/extensions/claude-context/index.ts +41 -12
- package/extensions/clear/index.ts +27 -0
- package/extensions/compaction/fit.ts +121 -0
- package/extensions/compaction/index.ts +176 -71
- package/extensions/compaction/prompt.ts +21 -1
- package/extensions/context-budget/budget.ts +61 -0
- package/extensions/context-budget/index.ts +66 -0
- package/extensions/context-management/index.ts +40 -16
- package/extensions/doctor/build.ts +226 -0
- package/extensions/doctor/cli.ts +99 -0
- package/extensions/doctor/compat.ts +532 -0
- package/extensions/doctor/dependencies.ts +186 -0
- package/extensions/doctor/fix-prompt.ts +125 -0
- package/extensions/doctor/index.ts +308 -0
- package/extensions/doctor/models.ts +231 -0
- package/extensions/doctor/presets.ts +208 -0
- package/extensions/doctor/report.ts +189 -0
- package/extensions/doctor/update-lookup.ts +46 -0
- package/extensions/doctor/viewer.ts +124 -0
- package/extensions/effort/index.ts +41 -4
- package/extensions/effort/slider.ts +33 -2
- package/extensions/file-tracker/index.ts +143 -20
- package/extensions/file-tracker/replay.ts +46 -0
- package/extensions/file-tracker/tracker.ts +51 -4
- package/extensions/footer/footer-line.ts +207 -0
- package/extensions/footer/index.ts +152 -0
- package/extensions/footer/pr.ts +53 -0
- package/extensions/hooks/executor.ts +57 -20
- package/extensions/hooks/formatter-notice.ts +76 -0
- package/extensions/hooks/index.ts +314 -73
- package/extensions/hooks/matcher.ts +35 -1
- package/extensions/hooks/plugin-hooks.ts +3 -3
- package/extensions/hooks/protocol.ts +58 -0
- package/extensions/hooks/subagent-bridge.ts +77 -0
- package/extensions/hooks/trust.ts +15 -4
- package/extensions/init/index.ts +26 -0
- package/extensions/init/prompt.ts +40 -0
- package/extensions/interrupted/index.ts +35 -0
- package/extensions/interrupted/line.ts +9 -0
- package/extensions/lib/abort.ts +15 -0
- package/extensions/lib/agent-loader.ts +183 -0
- package/extensions/lib/anthropic-payload.ts +15 -0
- package/extensions/lib/atomic-write.ts +56 -0
- package/extensions/lib/bridge-watch.ts +20 -0
- package/extensions/lib/cache-retention.ts +60 -0
- package/extensions/lib/capability-index.ts +466 -0
- package/extensions/lib/claude-context.ts +318 -27
- package/extensions/lib/claude-settings.ts +64 -0
- package/extensions/lib/consent-preview.ts +30 -0
- package/extensions/lib/deferred.ts +144 -13
- package/extensions/lib/favorites.ts +43 -0
- package/extensions/lib/fetch-timeout.ts +15 -0
- package/extensions/lib/frontmatter.ts +31 -0
- package/extensions/lib/git.ts +44 -2
- package/extensions/lib/hook-gate.ts +90 -0
- package/extensions/lib/interrupt.ts +94 -0
- package/extensions/lib/mcp-overrides.ts +61 -0
- package/extensions/lib/mcp-share.ts +25 -0
- package/extensions/lib/mcp-status.ts +34 -0
- package/extensions/lib/memory.ts +41 -6
- package/extensions/lib/model-facts.generated.json +2480 -0
- package/extensions/lib/model-facts.ts +162 -0
- package/extensions/lib/model-policy.ts +145 -189
- package/extensions/lib/model-tier.ts +480 -64
- package/extensions/lib/model-unusable.ts +37 -0
- package/extensions/lib/notifications.ts +320 -1
- package/extensions/lib/one-code-settings.ts +63 -0
- package/extensions/lib/original-command.ts +71 -0
- package/extensions/lib/package-version.ts +31 -0
- package/extensions/lib/paths.ts +39 -4
- package/extensions/lib/permission-gate.ts +192 -0
- package/extensions/lib/persisted-output.ts +18 -0
- package/extensions/lib/pi-ai-estimate.ts +160 -0
- package/extensions/lib/pi-version.ts +1 -1
- package/extensions/lib/plan-mode-channels.ts +11 -0
- package/extensions/lib/plugin-overrides.ts +24 -0
- package/extensions/lib/plugin-root.ts +44 -0
- package/extensions/lib/plugins.ts +233 -54
- package/extensions/lib/prefix-warm-gate.ts +157 -0
- package/extensions/lib/process-tree.ts +56 -0
- package/extensions/lib/reminders.ts +321 -68
- package/extensions/lib/scratchpad.ts +11 -8
- package/extensions/lib/session-lifecycle.ts +28 -0
- package/extensions/lib/settings-channels.ts +14 -0
- package/extensions/lib/shell-tasks.ts +54 -0
- package/extensions/lib/skill-overrides.ts +86 -0
- package/extensions/lib/skill-scan.ts +110 -0
- package/extensions/lib/text-width.ts +267 -0
- package/extensions/lib/tui-render.ts +281 -54
- package/extensions/lib/usage-bus.ts +75 -0
- package/extensions/lib/usage-tracker.ts +67 -0
- package/extensions/lib/worktree-isolation.ts +170 -0
- package/extensions/lsp/client.ts +69 -12
- package/extensions/lsp/format.ts +12 -2
- package/extensions/lsp/index.ts +273 -84
- package/extensions/lsp/install-hints.ts +43 -0
- package/extensions/lsp/keep-alive.ts +19 -0
- package/extensions/lsp/plugin-servers.ts +331 -0
- package/extensions/lsp/watcher.ts +157 -0
- package/extensions/mcp/announce.ts +99 -0
- package/extensions/mcp/client.ts +181 -22
- package/extensions/mcp/config.ts +55 -24
- package/extensions/mcp/index.ts +586 -86
- package/extensions/mcp/oauth/browser.ts +26 -0
- package/extensions/mcp/oauth/callback.ts +104 -0
- package/extensions/mcp/oauth/flow.ts +68 -0
- package/extensions/mcp/oauth/provider.ts +147 -0
- package/extensions/mcp/oauth/store.ts +86 -0
- package/extensions/mcp/panel/keys.ts +34 -0
- package/extensions/mcp/panel/model.ts +62 -0
- package/extensions/mcp/panel/render.ts +141 -0
- package/extensions/mcp/panel/state.ts +108 -0
- package/extensions/mcp/schema.ts +83 -6
- package/extensions/mcp/trust.ts +340 -0
- package/extensions/memory/entries.ts +163 -0
- package/extensions/memory/index.ts +119 -2
- package/extensions/memory/open-external.ts +97 -0
- package/extensions/memory/panel.ts +124 -0
- package/extensions/model-default/index.ts +74 -0
- package/extensions/notebook/index.ts +36 -18
- package/extensions/notebook/notebook.ts +110 -17
- package/extensions/permissions/index.ts +1012 -247
- package/extensions/permissions/matcher.ts +488 -57
- package/extensions/permissions/modes.ts +55 -7
- package/extensions/permissions/project-trust.ts +81 -0
- package/extensions/permissions/protected-paths.ts +16 -3
- package/extensions/permissions/rule-prose.ts +79 -0
- package/extensions/permissions/session-grant.ts +91 -0
- package/extensions/permissions/settings.ts +112 -33
- package/extensions/permissions/subagent-gate.ts +57 -0
- package/extensions/plan-mode/index.ts +143 -33
- package/extensions/plan-mode/reminder.ts +16 -19
- package/extensions/plan-mode/viewer.ts +40 -31
- package/extensions/plugins/counts.ts +68 -0
- package/extensions/plugins/index.ts +496 -32
- package/extensions/plugins/install/install.ts +167 -0
- package/extensions/plugins/install/paths.ts +23 -0
- package/extensions/plugins/install/registry.ts +72 -0
- package/extensions/plugins/marketplace/git.ts +103 -0
- package/extensions/plugins/marketplace/official.ts +36 -0
- package/extensions/plugins/marketplace/parse.ts +94 -0
- package/extensions/plugins/marketplace/registry.ts +60 -0
- package/extensions/plugins/marketplace/sync.ts +146 -0
- package/extensions/plugins/marketplace/types.ts +128 -0
- package/extensions/plugins/panel/keys.ts +52 -0
- package/extensions/plugins/panel/render.ts +315 -0
- package/extensions/plugins/panel/rows.ts +230 -0
- package/extensions/plugins/panel/state.ts +333 -0
- package/extensions/recap/index.ts +215 -0
- package/extensions/recap/prompt.ts +44 -0
- package/extensions/recap/scheduler.ts +87 -0
- package/extensions/search-tools/index.ts +0 -0
- package/extensions/search-tools/tools.ts +7 -5
- package/extensions/skill/index.ts +360 -34
- package/extensions/skill/invoke.ts +185 -0
- package/extensions/skill/listing.ts +21 -0
- package/extensions/skill/panel/keys.ts +41 -0
- package/extensions/skill/panel/render.ts +91 -0
- package/extensions/skill/panel/state.ts +115 -0
- package/extensions/spinner/index.ts +110 -0
- package/extensions/spinner/line.ts +51 -0
- package/extensions/spinner/verbs.ts +201 -0
- package/extensions/subagents/agents.ts +88 -44
- package/extensions/subagents/default-model.ts +62 -32
- package/extensions/subagents/delegation-steer.ts +23 -0
- package/extensions/subagents/hand-back-review.ts +67 -0
- package/extensions/subagents/index.ts +1545 -346
- package/extensions/subagents/live-runs.ts +293 -0
- package/extensions/subagents/model-select.ts +252 -82
- package/extensions/subagents/outcome.ts +90 -0
- package/extensions/subagents/panel-keys.ts +41 -0
- package/extensions/subagents/panel-render.ts +357 -0
- package/extensions/subagents/panel-widget.ts +330 -0
- package/extensions/subagents/pending-claim.ts +45 -0
- package/extensions/subagents/prose.ts +105 -0
- package/extensions/subagents/runner.ts +674 -0
- package/extensions/subagents/runs.ts +68 -1
- package/extensions/subagents/send-to-main-tool.ts +55 -0
- package/extensions/subagents/session-turns.ts +138 -0
- package/extensions/subagents/shell-panel.ts +198 -0
- package/extensions/subagents/usage.ts +16 -4
- package/extensions/subagents/worktree.ts +13 -0
- package/extensions/system-prompt/environment.ts +5 -3
- package/extensions/system-prompt/git-status.ts +93 -0
- package/extensions/system-prompt/index.ts +23 -1
- package/extensions/system-prompt/template.ts +34 -4
- package/extensions/system-prompt/tiers/common.ts +31 -6
- package/extensions/system-prompt/tiers/low.ts +13 -3
- package/extensions/system-prompt/tiers/mid.ts +21 -8
- package/extensions/system-reminder/index.ts +53 -9
- package/extensions/tasks/index.ts +65 -16
- package/extensions/tasks/store.ts +76 -3
- package/extensions/tool-search/announce.ts +42 -0
- package/extensions/tool-search/index.ts +136 -13
- package/extensions/turn-duration/index.ts +60 -0
- package/extensions/turn-duration/line.ts +22 -0
- package/extensions/turn-duration/span.ts +41 -0
- package/extensions/turn-duration/verbs.ts +24 -0
- package/extensions/web/backends.ts +421 -0
- package/extensions/web/index.ts +178 -21
- package/extensions/web-fetch/index.ts +74 -18
- package/extensions/web-fetch/summarize.ts +1 -1
- package/extensions/workflow/agent-session.ts +182 -112
- package/extensions/workflow/globals.ts +129 -106
- package/extensions/workflow/index.ts +343 -28
- package/extensions/workflow/records.ts +118 -0
- package/extensions/workflow/run-manager.ts +80 -31
- package/extensions/workflow/saved-workflows.ts +2 -1
- package/extensions/workflow/script-worker.mjs +110 -0
- package/extensions/workflow/types.ts +74 -8
- package/extensions/workflow/viewer.ts +562 -0
- package/extensions/workflow/vm-runtime.ts +118 -22
- package/extensions/workflow/widget.ts +126 -18
- package/extensions/worktree/guards.ts +240 -0
- package/extensions/worktree/index.ts +30 -5
- package/extensions/worktree/rewrite.ts +32 -16
- package/package.json +20 -10
- package/skills/code-review/SKILL.md +165 -0
- package/skills/fewer-permission-prompts/SKILL.md +61 -0
- package/skills/security-review/SKILL.md +178 -0
- package/skills/simplify/SKILL.md +70 -0
- package/themes/{one-code-light.json → onecode-light.json} +6 -1
- package/themes/{one-code.json → onecode.json} +6 -1
- package/types/app.d.ts +8 -1
- package/types/community-packages.d.ts +45 -2
- package/docs/decisions/auto-mode.md +0 -526
- package/docs/decisions/branding.md +0 -127
- package/docs/decisions/code-review-remediation.md +0 -106
- package/docs/decisions/compaction.md +0 -140
- package/docs/decisions/distribution.md +0 -168
- package/docs/decisions/hooks.md +0 -64
- package/docs/decisions/lsp.md +0 -58
- package/docs/decisions/mcp.md +0 -104
- package/docs/decisions/memory-state.md +0 -170
- package/docs/decisions/model-policy.md +0 -126
- package/docs/decisions/modes.md +0 -178
- package/docs/decisions/skills-plugins.md +0 -56
- package/docs/decisions/subagents-workflows.md +0 -225
- package/docs/decisions/system-prompt.md +0 -98
- package/docs/decisions/tools.md +0 -407
- package/docs/decisions/tui.md +0 -202
- package/docs/decisions.md +0 -148
- package/extensions/subagents/child.ts +0 -414
- package/extensions/subagents/rpc-turns.ts +0 -123
- package/extensions/todo/index.ts +0 -133
- package/extensions/workflow/permission-gate.ts +0 -71
|
@@ -28,17 +28,51 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
28
28
|
import { Type } from "typebox";
|
|
29
29
|
import {
|
|
30
30
|
DEFER_CHANNEL,
|
|
31
|
-
|
|
31
|
+
deferredAddendumText,
|
|
32
|
+
deferredMissReminderText,
|
|
33
|
+
DeferredRegistry,
|
|
32
34
|
deferredReminderText,
|
|
33
35
|
type DeferRequest,
|
|
36
|
+
resultText,
|
|
34
37
|
searchTools,
|
|
35
38
|
selectedNames,
|
|
39
|
+
supportsToolReferences,
|
|
40
|
+
toolNotFoundName,
|
|
41
|
+
withDeferredToolDefinitions,
|
|
36
42
|
} from "../lib/deferred.ts";
|
|
43
|
+
import { looksLikeAnthropicRequest } from "../lib/anthropic-payload.ts";
|
|
44
|
+
import { MCP_TOOLS_CHANNEL, type McpToolsPayload } from "../lib/mcp-share.ts";
|
|
37
45
|
import { CONTEXT_ORDER, REMINDER_CHANNEL } from "../lib/reminders.ts";
|
|
46
|
+
import { sessionAlive } from "../lib/session-lifecycle.ts";
|
|
38
47
|
import { ccToolRenderers } from "../lib/tui-render.ts";
|
|
48
|
+
import { normalizeToolName } from "../permissions/matcher.ts";
|
|
49
|
+
import { applyAnnouncement, planAnnouncement } from "./announce.ts";
|
|
50
|
+
|
|
51
|
+
/** Coalesces the burst of per-tool defers one server emits into one listing update. */
|
|
52
|
+
const ANNOUNCE_DEBOUNCE_MS = 100;
|
|
39
53
|
|
|
40
54
|
export default function toolSearchExtension(pi: ExtensionAPI) {
|
|
55
|
+
// Owned here (see lib/deferred.ts): the only instance, fed over DEFER_CHANNEL.
|
|
56
|
+
const deferredRegistry = new DeferredRegistry();
|
|
41
57
|
let sessionStarted = false;
|
|
58
|
+
// The deferred-tools listing is a first-prepend reminder on message 1, part of
|
|
59
|
+
// the cached prefix of every later request. Before the first request nothing
|
|
60
|
+
// is cached, so the listing is (re)written freely; after it the listing is
|
|
61
|
+
// FROZEN and later arrivals ride a one-shot addendum where the model reads
|
|
62
|
+
// next (./announce.ts — a rewrite re-cached 16.7k tokens of history on
|
|
63
|
+
// request 2 of every run whose MCP tools registered inside the debounce,
|
|
64
|
+
// measured 2026-09-04). While the initial MCP connect is still settling the
|
|
65
|
+
// addendum waits for settle, so one burst of per-tool defers is one notice.
|
|
66
|
+
let requestSent = false;
|
|
67
|
+
let mcpSettled = false;
|
|
68
|
+
let announcePending = false;
|
|
69
|
+
let announceTimer: NodeJS.Timeout | undefined;
|
|
70
|
+
// After session_shutdown every pi.* call throws (the runner is invalidated,
|
|
71
|
+
// findings §8); a debounced announce landing in a timer callback then would
|
|
72
|
+
// be an uncaught exception and take pi down (LIFECYCLE-REVIEW L1).
|
|
73
|
+
const alive = sessionAlive(pi);
|
|
74
|
+
/** Names the model has been told about: the frozen listing plus every addendum. */
|
|
75
|
+
const announced = new Set<string>();
|
|
42
76
|
|
|
43
77
|
const searchableTools = () =>
|
|
44
78
|
pi
|
|
@@ -50,18 +84,74 @@ export default function toolSearchExtension(pi: ExtensionAPI) {
|
|
|
50
84
|
keywords: deferredRegistry.keywordsFor(tool.name),
|
|
51
85
|
}));
|
|
52
86
|
|
|
53
|
-
const
|
|
87
|
+
const announce = () => {
|
|
88
|
+
if (!alive()) return;
|
|
54
89
|
const available = searchableTools();
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
90
|
+
const plan = planAnnouncement({ requestSent, announced, available: available.map((t) => t.name) });
|
|
91
|
+
if (plan.kind === "rewrite") {
|
|
92
|
+
pi.events.emit(REMINDER_CHANNEL, {
|
|
93
|
+
scope: "every-turn",
|
|
94
|
+
key: "deferred-tools",
|
|
95
|
+
text: deferredReminderText(available),
|
|
96
|
+
placement: "first-prepend",
|
|
97
|
+
order: CONTEXT_ORDER.deferredTools,
|
|
98
|
+
});
|
|
99
|
+
} else if (plan.kind === "addendum") {
|
|
100
|
+
// Unkeyed on purpose: a keyed next-turn reminder replaces its
|
|
101
|
+
// predecessor, and an addendum replaced before delivery would lose names.
|
|
102
|
+
pi.events.emit(REMINDER_CHANNEL, { text: deferredAddendumText(plan.added) });
|
|
103
|
+
}
|
|
104
|
+
applyAnnouncement(announced, plan);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const clearAnnounce = () => {
|
|
108
|
+
if (announceTimer) clearTimeout(announceTimer);
|
|
109
|
+
announceTimer = undefined;
|
|
110
|
+
};
|
|
111
|
+
const scheduleAnnounce = () => {
|
|
112
|
+
clearAnnounce();
|
|
113
|
+
if (!alive()) return;
|
|
114
|
+
announceTimer = setTimeout(() => {
|
|
115
|
+
announceTimer = undefined;
|
|
116
|
+
// Decided at fire time, not schedule time: a request may have gone out
|
|
117
|
+
// during the debounce (the -p race).
|
|
118
|
+
if (requestSent && !mcpSettled) {
|
|
119
|
+
announcePending = true;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
announce();
|
|
123
|
+
}, ANNOUNCE_DEBOUNCE_MS);
|
|
124
|
+
announceTimer.unref?.();
|
|
63
125
|
};
|
|
64
126
|
|
|
127
|
+
pi.events.on(MCP_TOOLS_CHANNEL, (data) => {
|
|
128
|
+
if (!(data as McpToolsPayload | undefined)?.settled) return;
|
|
129
|
+
mcpSettled = true;
|
|
130
|
+
if (announcePending) {
|
|
131
|
+
announcePending = false;
|
|
132
|
+
announce();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
pi.on("context", () => {
|
|
137
|
+
requestSent = true;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// On models that take client-side tool references (first-party Claude >= 4.5,
|
|
141
|
+
// not Haiku) every deferred tool rides every Anthropic request as a
|
|
142
|
+
// `defer_loading: true` definition from request 1, so `tools` never changes
|
|
143
|
+
// when tool_search loads one. Anthropic keeps deferred definitions out of the
|
|
144
|
+
// cached prefix, but adding one mid-session still re-cached the whole message
|
|
145
|
+
// history (lib/deferred.ts withDeferredToolDefinitions has the measurement);
|
|
146
|
+
// unloaded deferred definitions cost no input tokens, so this is free.
|
|
147
|
+
pi.on("before_provider_request", (event, ctx) => {
|
|
148
|
+
if (!supportsToolReferences(ctx.model as { provider?: string; id?: string } | undefined)) return undefined;
|
|
149
|
+
if (!looksLikeAnthropicRequest(event.payload)) return undefined;
|
|
150
|
+
return withDeferredToolDefinitions(event.payload as Record<string, unknown>, pi.getAllTools(), (name) =>
|
|
151
|
+
deferredRegistry.has(name),
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
65
155
|
/** Deactivate every deferred-registry tool and announce the loadable set. */
|
|
66
156
|
const deferAll = () => {
|
|
67
157
|
const deferred = new Set(deferredRegistry.names);
|
|
@@ -69,7 +159,7 @@ export default function toolSearchExtension(pi: ExtensionAPI) {
|
|
|
69
159
|
const active = pi.getActiveTools();
|
|
70
160
|
const next = active.filter((name) => !deferred.has(name));
|
|
71
161
|
if (next.length !== active.length) pi.setActiveTools(next);
|
|
72
|
-
|
|
162
|
+
announce();
|
|
73
163
|
};
|
|
74
164
|
|
|
75
165
|
pi.events.on(DEFER_CHANNEL, (data) => {
|
|
@@ -84,21 +174,54 @@ export default function toolSearchExtension(pi: ExtensionAPI) {
|
|
|
84
174
|
if (active.includes(request.name)) {
|
|
85
175
|
pi.setActiveTools(active.filter((name) => name !== request.name));
|
|
86
176
|
}
|
|
87
|
-
|
|
177
|
+
// The tool is searchable (keyword search reads the registry) from now;
|
|
178
|
+
// only the standing listing waits.
|
|
179
|
+
scheduleAnnounce();
|
|
88
180
|
}
|
|
89
181
|
});
|
|
90
182
|
|
|
91
183
|
pi.on("session_start", () => {
|
|
92
184
|
sessionStarted = true;
|
|
185
|
+
// A pending debounce belongs to the previous session's listing (RPC's
|
|
186
|
+
// new_session emits session_start twice, findings §3); deferAll below
|
|
187
|
+
// announces the fresh one.
|
|
188
|
+
clearAnnounce();
|
|
189
|
+
// A new session (/clear, or a resume in a new process) has no cached prefix
|
|
190
|
+
// yet: its first request gets a freshly written listing.
|
|
191
|
+
requestSent = false;
|
|
192
|
+
announced.clear();
|
|
93
193
|
deferAll();
|
|
94
194
|
});
|
|
95
195
|
|
|
196
|
+
pi.on("session_shutdown", clearAnnounce);
|
|
197
|
+
|
|
198
|
+
// The every-turn deferred-tools list tells the model to load via tool_search,
|
|
199
|
+
// but if it calls a deferred tool directly anyway, pi's core dispatcher fails
|
|
200
|
+
// the call with a bare "Tool <name> not found" — which fires here as an error
|
|
201
|
+
// tool_execution_end. (This path skips beforeToolCall/afterToolCall, so the
|
|
202
|
+
// tool_call/tool_result hooks never see it.) The call can't be salvaged
|
|
203
|
+
// mid-turn, but we steer the model straight back to tool_search on its next
|
|
204
|
+
// step with a one-shot reminder, far more pointed than the standing list.
|
|
205
|
+
pi.on("tool_execution_end", (event) => {
|
|
206
|
+
if (!event.isError) return;
|
|
207
|
+
const raw = toolNotFoundName(resultText(event.result));
|
|
208
|
+
// Map a Claude Code spelling (a direct `NotebookEdit` call) to our name so
|
|
209
|
+
// the miss is recognised and the steer names the loadable tool (M1).
|
|
210
|
+
const name = raw ? normalizeToolName(raw) : undefined;
|
|
211
|
+
if (!name || !deferredRegistry.has(name)) return;
|
|
212
|
+
pi.events.emit(REMINDER_CHANNEL, {
|
|
213
|
+
scope: "next-turn",
|
|
214
|
+
key: `deferred-miss-${name}`,
|
|
215
|
+
text: deferredMissReminderText(name),
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
96
219
|
pi.registerTool({
|
|
97
220
|
name: "tool_search",
|
|
98
221
|
label: "Tool Search",
|
|
99
222
|
...ccToolRenderers("Tool Search"),
|
|
100
223
|
description:
|
|
101
|
-
"
|
|
224
|
+
"Fetches full schema definitions for deferred tools so they can be called.\n\nDeferred tools appear by name in <system-reminder> messages. Until fetched, only the name is known — there is no parameter schema, so the tool cannot be invoked. This tool takes a query, matches it against the deferred tool list, and loads the matched tools' full schemas so they become callable exactly like any tool defined at the top of the prompt.\n\nQuery forms:\n- `select:web_fetch,notebook_edit` — fetch these exact tools by name\n- `notebook jupyter` — keyword search, up to max_results best matches\n- `+slack send` — require \"slack\" in the name, rank by remaining terms",
|
|
102
225
|
promptSnippet: "Load additional tool schemas on demand",
|
|
103
226
|
parameters: Type.Object({
|
|
104
227
|
query: Type.String({ description: "Tool names (`select:a,b`) or keywords describing the capability needed" }),
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turn-duration extension — Claude Code's "✻ Cooked for 5m 12s" line shown in
|
|
3
|
+
* the transcript after each response.
|
|
4
|
+
*
|
|
5
|
+
* The turn's wall-clock is measured across agent_start→agent_settled by span.ts
|
|
6
|
+
* (one turn can hold several runs — `lib/interrupt.ts`) and emitted as a
|
|
7
|
+
* display-only session entry (`appendEntry` — not part of the LLM context, so
|
|
8
|
+
* the model never sees its own timing line), rendered dim and led by the ✻ mark
|
|
9
|
+
* to match CC's TurnDurationMessage. CC shows it after every response with no
|
|
10
|
+
* threshold and defaults it on; CC_TURN_DURATION=0 opts out. The completion verb
|
|
11
|
+
* is sampled once per turn from CC's 8-verb list.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import { trackShellTasks } from "../lib/shell-tasks.ts";
|
|
16
|
+
import { dimMarkedLine } from "../lib/tui-render.ts";
|
|
17
|
+
import { TURN_MARK, turnDurationText } from "./line.ts";
|
|
18
|
+
import { TurnSpan } from "./span.ts";
|
|
19
|
+
import { pickCompletionVerb } from "./verbs.ts";
|
|
20
|
+
|
|
21
|
+
const ENTRY_TYPE = "one-code:turn-duration";
|
|
22
|
+
|
|
23
|
+
interface TurnDurationData {
|
|
24
|
+
verb: string;
|
|
25
|
+
durationMs: number;
|
|
26
|
+
/** Background shells still running when the turn ended (CC's `· 2 shells still running`). */
|
|
27
|
+
runningShells?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default function turnDurationExtension(pi: ExtensionAPI) {
|
|
31
|
+
const shellTasks = trackShellTasks(pi);
|
|
32
|
+
|
|
33
|
+
pi.registerEntryRenderer<TurnDurationData>(ENTRY_TYPE, (entry, _options, theme) => {
|
|
34
|
+
const data = entry.data;
|
|
35
|
+
if (!data) return undefined;
|
|
36
|
+
return dimMarkedLine(theme, TURN_MARK, turnDurationText(data.verb, data.durationMs, data.runningShells ?? 0));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const span = new TurnSpan();
|
|
40
|
+
|
|
41
|
+
pi.on("agent_start", () => {
|
|
42
|
+
span.runStarted(Date.now());
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
pi.on("agent_end", (event, ctx) => {
|
|
46
|
+
span.runEnded(event.messages, ctx.signal?.aborted);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
pi.on("agent_settled", (_event, ctx) => {
|
|
50
|
+
const durationMs = span.settle(Date.now());
|
|
51
|
+
if (durationMs === undefined) return;
|
|
52
|
+
if (process.env.CC_TURN_DURATION === "0") return;
|
|
53
|
+
if (!ctx.hasUI) return;
|
|
54
|
+
pi.appendEntry<TurnDurationData>(ENTRY_TYPE, {
|
|
55
|
+
verb: pickCompletionVerb(),
|
|
56
|
+
durationMs,
|
|
57
|
+
runningShells: shellTasks.running().length,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure pieces of Claude Code's turn-duration line ("✻ Cooked for 5m 12s"),
|
|
3
|
+
* matched to CC's SystemTextMessage/TurnDurationMessage render
|
|
4
|
+
* (`${verb} for ${duration}`, dim, led by the ✻ teardrop asterisk). CC shows
|
|
5
|
+
* it after every response with no minimum-duration threshold; the optional
|
|
6
|
+
* token-budget suffix is CC's separate /budget feature and is not replicated.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { countNoun, formatDuration } from "../lib/tui-render.ts";
|
|
10
|
+
|
|
11
|
+
/** CC's TEARDROP_ASTERISK (figures.ts), the mark leading the duration line. */
|
|
12
|
+
export const TURN_MARK = "✻";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `<Verb> for <duration>` — duration formatted like the spinner's elapsed
|
|
16
|
+
* clock — plus Claude Code's background-shell tail (`· 2 shells still
|
|
17
|
+
* running`) when shells outlive the turn.
|
|
18
|
+
*/
|
|
19
|
+
export function turnDurationText(verb: string, durationMs: number, runningShells = 0): string {
|
|
20
|
+
const shells = runningShells > 0 ? ` · ${countNoun(runningShells, "shell")} still running` : "";
|
|
21
|
+
return `${verb} for ${formatDuration(0, durationMs)}${shells}`;
|
|
22
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The wall-clock span of one user-visible turn.
|
|
3
|
+
*
|
|
4
|
+
* A turn can hold several agent runs (see `lib/interrupt.ts` for why), so the
|
|
5
|
+
* span opens on the FIRST run and closes at settle: a retried run keeps the
|
|
6
|
+
* original start, which is what makes the rendered duration the time the user
|
|
7
|
+
* actually waited rather than the time the last attempt took.
|
|
8
|
+
*
|
|
9
|
+
* A turn that ends aborted or errored renders nothing: the interrupted
|
|
10
|
+
* extension prints its own note for an abort, and pi prints its own retry
|
|
11
|
+
* failure for an error. Claude Code gates its turn line the same way.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { type RunMessage, RunOutcomeLatch } from "../lib/interrupt.ts";
|
|
15
|
+
|
|
16
|
+
export class TurnSpan {
|
|
17
|
+
/** Undefined rather than 0 for "no span open", so a start of 0 is still a start. */
|
|
18
|
+
private startedAt: number | undefined;
|
|
19
|
+
private readonly outcome = new RunOutcomeLatch();
|
|
20
|
+
|
|
21
|
+
/** `agent_start`: opens the span; a retry/continuation is a no-op, keeping the first start. */
|
|
22
|
+
runStarted(now: number): void {
|
|
23
|
+
this.startedAt ??= now;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** `agent_end`: remembers how that run ended (with the run's `signal.aborted`), for settle to read. */
|
|
27
|
+
runEnded(messages: ReadonlyArray<RunMessage> | undefined, signalAborted = false): void {
|
|
28
|
+
this.outcome.record(messages, signalAborted);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* `agent_settled`: closes the span and returns the duration to render, or
|
|
33
|
+
* undefined when this turn should show no line at all.
|
|
34
|
+
*/
|
|
35
|
+
settle(now: number): number | undefined {
|
|
36
|
+
const startedAt = this.startedAt;
|
|
37
|
+
this.startedAt = undefined;
|
|
38
|
+
if (this.outcome.take() !== "ok" || startedAt === undefined) return undefined;
|
|
39
|
+
return Math.max(0, now - startedAt);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code's turn-completion verbs, verbatim from the reconstructed source
|
|
3
|
+
* (src/constants/turnCompletionVerbs.ts) — the past-tense verb shown after a
|
|
4
|
+
* response finishes ("✻ Cooked for 5m 12s"). One is picked at random per turn.
|
|
5
|
+
* These read naturally with "for <duration>", so the gerund spinner list
|
|
6
|
+
* (spinner/verbs.ts) is deliberately not reused.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const TURN_COMPLETION_VERBS: readonly string[] = [
|
|
10
|
+
"Baked",
|
|
11
|
+
"Brewed",
|
|
12
|
+
"Churned",
|
|
13
|
+
"Cogitated",
|
|
14
|
+
"Cooked",
|
|
15
|
+
"Crunched",
|
|
16
|
+
"Sautéed",
|
|
17
|
+
"Worked",
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/** Random completion verb for one turn; `random` is injectable for tests. */
|
|
21
|
+
export function pickCompletionVerb(random: () => number = Math.random): string {
|
|
22
|
+
const index = Math.min(TURN_COMPLETION_VERBS.length - 1, Math.floor(random() * TURN_COMPLETION_VERBS.length));
|
|
23
|
+
return TURN_COMPLETION_VERBS[index];
|
|
24
|
+
}
|