@herbertgao/pi-extensions 2026.8.7 → 2026.8.9
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 +8 -6
- package/node_modules/@czottmann/pi-automode/CHANGELOG.md +24 -0
- package/node_modules/@czottmann/pi-automode/README.md +78 -112
- package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +18 -14
- package/node_modules/@czottmann/pi-automode/docs/adr/ADR-001-permission-precedence-and-trust-boundaries.md +44 -0
- package/node_modules/@czottmann/pi-automode/docs/adr/INDEX.md +5 -0
- package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +198 -104
- package/node_modules/@czottmann/pi-automode/docs/configuration.md +155 -0
- package/node_modules/@czottmann/pi-automode/docs/defaults.md +55 -14
- package/node_modules/@czottmann/pi-automode/docs/diagnostics.md +90 -0
- package/node_modules/@czottmann/pi-automode/docs/observability-logging.md +61 -26
- package/node_modules/@czottmann/pi-automode/examples/automode.local.json +5 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +172 -18
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +127 -30
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +3 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +273 -31
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/log.ts +60 -5
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +92 -8
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +203 -13
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/state.ts +1 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/types.ts +11 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/utils.ts +9 -1
- package/node_modules/@czottmann/pi-automode/package.json +9 -2
- package/node_modules/@czottmann/pi-automode/skills/automode-diagnostics/SKILL.md +63 -0
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +3 -3
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +3 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +8 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +63 -15
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +56 -29
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +78 -16
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +13 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +30 -17
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-handoff/CHANGELOG.md +29 -0
- package/node_modules/@herbertgao/pi-handoff/README.md +15 -18
- package/node_modules/@herbertgao/pi-handoff/package.json +11 -4
- package/node_modules/@herbertgao/pi-handoff/src/complete-text.ts +33 -0
- package/node_modules/@herbertgao/pi-handoff/src/index.ts +127 -232
- package/node_modules/@herbertgao/pi-handoff/src/session-query.ts +15 -30
- package/node_modules/@herbertgao/pi-mermaid-open/CHANGELOG.md +14 -0
- package/node_modules/@herbertgao/pi-mermaid-open/README.md +21 -4
- package/node_modules/@herbertgao/pi-mermaid-open/herdr-plugin/herdr-plugin.toml +11 -0
- package/node_modules/@herbertgao/pi-mermaid-open/herdr-plugin/viewer.mjs +305 -0
- package/node_modules/@herbertgao/pi-mermaid-open/package.json +15 -4
- package/node_modules/@herbertgao/pi-mermaid-open/src/index.ts +351 -101
- package/node_modules/@herbertgao/pi-preferred-thinking/CHANGELOG.md +12 -0
- package/node_modules/@herbertgao/pi-preferred-thinking/README.md +30 -14
- package/node_modules/@herbertgao/pi-preferred-thinking/package.json +7 -4
- package/node_modules/@herbertgao/pi-preferred-thinking/src/index.ts +140 -85
- package/node_modules/@herbertgao/pi-recap/CHANGELOG.md +12 -0
- package/node_modules/@herbertgao/pi-recap/README.md +5 -3
- package/node_modules/@herbertgao/pi-recap/package.json +9 -3
- package/node_modules/@herbertgao/pi-recap/src/index.ts +3 -7
- package/node_modules/@herbertgao/pi-recap/src/model-picker.ts +6 -19
- package/node_modules/@herbertgao/pi-recap/src/models.ts +9 -21
- package/node_modules/@herbertgao/pi-recap/src/tui.ts +1 -1
- package/node_modules/@herbertgao/pi-rename/CHANGELOG.md +49 -0
- package/node_modules/@herbertgao/pi-rename/README.md +13 -11
- package/node_modules/@herbertgao/pi-rename/package.json +13 -3
- package/node_modules/@herbertgao/pi-rename/src/index.ts +135 -59
- package/node_modules/@herbertgao/pi-rename/src/models.ts +5 -13
- package/node_modules/@herbertgao/pi-rename/src/naming.ts +2 -6
- package/node_modules/@herbertgao/pi-stash/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +16 -3
- package/node_modules/@herbertgao/pi-subagents/package.json +1 -1
- package/node_modules/@herbertgao/pi-subagents/src/cross-extension-rpc.ts +15 -4
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +16 -0
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +5 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +4 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +154 -96
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +3 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +7 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +3 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +137 -96
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +44 -32
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +56 -34
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +50 -4
- package/node_modules/@narumitw/pi-btw/package.json +2 -2
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
- package/node_modules/pi-lens/CHANGELOG.md +90 -0
- package/node_modules/pi-lens/README.md +106 -0
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +97 -6
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +16 -6
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +14 -0
- package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +22 -1
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
- package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +8 -10
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +10 -10
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +10 -15
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +19 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +35 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +8 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +110 -23
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +117 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +16 -14
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +8 -9
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +22 -0
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +5 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +7 -2
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +27 -2
- package/node_modules/pi-lens/dist/clients/lsp/client.js +144 -5
- package/node_modules/pi-lens/dist/clients/lsp/index.js +151 -6
- package/node_modules/pi-lens/dist/clients/module-report.js +14 -1
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +18 -9
- package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +7 -2
- package/node_modules/pi-lens/dist/clients/read-guard.js +88 -19
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
- package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +44 -0
- package/node_modules/pi-lens/dist/clients/runtime-session.js +41 -0
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +47 -2
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
- package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
- package/node_modules/pi-lens/dist/index.js +1224 -393
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +10 -3
- package/node_modules/pi-lens/package.json +18 -5
- package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
- package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
- package/node_modules/pi-web-access/CHANGELOG.md +27 -0
- package/node_modules/pi-web-access/README.md +8 -7
- package/node_modules/pi-web-access/chrome-cookies.ts +96 -20
- package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
- package/node_modules/pi-web-access/extract.ts +12 -2
- package/node_modules/pi-web-access/firecrawl.ts +18 -2
- package/node_modules/pi-web-access/gemini-search.ts +28 -10
- package/node_modules/pi-web-access/github-extract.ts +65 -36
- package/node_modules/pi-web-access/index.ts +43 -14
- package/node_modules/pi-web-access/openai-search.ts +17 -6
- package/node_modules/pi-web-access/package.json +1 -1
- package/node_modules/pi-web-access/pdf-extract.ts +18 -5
- package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
- package/package.json +19 -18
|
@@ -3,31 +3,17 @@ import path from "node:path"
|
|
|
3
3
|
import {
|
|
4
4
|
getAgentDir,
|
|
5
5
|
type ExtensionAPI,
|
|
6
|
+
type ExtensionCommandContext,
|
|
6
7
|
type ExtensionContext,
|
|
7
8
|
} from "@earendil-works/pi-coding-agent"
|
|
8
9
|
|
|
9
10
|
type ThinkingLevel = Parameters<ExtensionAPI["setThinkingLevel"]>[0]
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
preferredThinking?: Record<string, unknown>
|
|
11
|
+
type PiSettings = Record<string, unknown> & {
|
|
12
|
+
enabledModels?: unknown
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
const
|
|
16
|
-
getAgentDir(),
|
|
17
|
-
"extensions",
|
|
18
|
-
"pi-preferred-thinking.json",
|
|
19
|
-
)
|
|
20
|
-
const CONFIG_DIR = path.dirname(CONFIG_PATH)
|
|
15
|
+
const SETTINGS_PATH = path.join(getAgentDir(), "settings.json")
|
|
21
16
|
const UNSET_OPTION = "unset"
|
|
22
|
-
const VALID_THINKING_LEVELS = new Set<ThinkingLevel>([
|
|
23
|
-
"off",
|
|
24
|
-
"minimal",
|
|
25
|
-
"low",
|
|
26
|
-
"medium",
|
|
27
|
-
"high",
|
|
28
|
-
"xhigh",
|
|
29
|
-
"max",
|
|
30
|
-
])
|
|
31
17
|
const THINKING_LEVEL_OPTIONS: readonly ThinkingLevel[] = [
|
|
32
18
|
"off",
|
|
33
19
|
"minimal",
|
|
@@ -37,137 +23,206 @@ const THINKING_LEVEL_OPTIONS: readonly ThinkingLevel[] = [
|
|
|
37
23
|
"xhigh",
|
|
38
24
|
"max",
|
|
39
25
|
]
|
|
26
|
+
const HINT_WIDGET_KEY = "pi-preferred-thinking"
|
|
27
|
+
const HINT_DURATION_MS = 3_000
|
|
28
|
+
|
|
29
|
+
let hintTimer: ReturnType<typeof setTimeout> | undefined
|
|
30
|
+
const nudgedModels = new Set<string>()
|
|
40
31
|
|
|
41
32
|
function getModelKey(provider: string, modelId: string): string {
|
|
42
33
|
return `${provider}/${modelId}`
|
|
43
34
|
}
|
|
44
35
|
|
|
45
|
-
function
|
|
36
|
+
function readSettings(): PiSettings {
|
|
37
|
+
const content = readFileSync(SETTINGS_PATH, "utf8")
|
|
38
|
+
let settings: unknown
|
|
46
39
|
try {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return config && typeof config === "object" && !Array.isArray(config)
|
|
50
|
-
? (config as PreferredThinkingConfig)
|
|
51
|
-
: {}
|
|
52
|
-
} catch (error) {
|
|
53
|
-
if (error instanceof SyntaxError) throw error
|
|
40
|
+
settings = JSON.parse(content)
|
|
41
|
+
} catch {
|
|
54
42
|
return {}
|
|
55
43
|
}
|
|
44
|
+
return settings && typeof settings === "object" && !Array.isArray(settings)
|
|
45
|
+
? (settings as PiSettings)
|
|
46
|
+
: {}
|
|
56
47
|
}
|
|
57
48
|
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
for (const [modelKey, level] of Object.entries(configured)) {
|
|
66
|
-
if (
|
|
67
|
-
typeof level === "string" &&
|
|
68
|
-
VALID_THINKING_LEVELS.has(level as ThinkingLevel)
|
|
69
|
-
) {
|
|
70
|
-
preferredThinking[modelKey] = level as ThinkingLevel
|
|
71
|
-
}
|
|
72
|
-
}
|
|
49
|
+
function getEnabledModels(settings: PiSettings): string[] {
|
|
50
|
+
return Array.isArray(settings.enabledModels)
|
|
51
|
+
? settings.enabledModels.filter(
|
|
52
|
+
(entry): entry is string => typeof entry === "string",
|
|
53
|
+
)
|
|
54
|
+
: []
|
|
55
|
+
}
|
|
73
56
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
57
|
+
function isExactModelEntry(entry: string, modelKey: string): boolean {
|
|
58
|
+
return (
|
|
59
|
+
entry === modelKey ||
|
|
60
|
+
THINKING_LEVEL_OPTIONS.some((level) => entry === `${modelKey}:${level}`)
|
|
61
|
+
)
|
|
78
62
|
}
|
|
79
63
|
|
|
80
|
-
function
|
|
64
|
+
function saveThinkingLevel(
|
|
81
65
|
modelKey: string,
|
|
82
66
|
level: ThinkingLevel | undefined,
|
|
83
67
|
): void {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
68
|
+
const settings = readSettings()
|
|
69
|
+
if (!Array.isArray(settings.enabledModels)) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"Pi enabledModels is not configured. Add the model with /scoped-models first.",
|
|
72
|
+
)
|
|
73
|
+
}
|
|
90
74
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
75
|
+
const entries = getEnabledModels(settings)
|
|
76
|
+
const exactIndex = entries.findIndex((entry) =>
|
|
77
|
+
isExactModelEntry(entry, modelKey),
|
|
78
|
+
)
|
|
79
|
+
const nextEntries = entries.filter(
|
|
80
|
+
(entry) => !isExactModelEntry(entry, modelKey),
|
|
81
|
+
)
|
|
82
|
+
const replacement = level
|
|
83
|
+
? `${modelKey}:${level}`
|
|
84
|
+
: exactIndex >= 0
|
|
85
|
+
? modelKey
|
|
86
|
+
: undefined
|
|
87
|
+
|
|
88
|
+
if (replacement) {
|
|
89
|
+
const insertAt =
|
|
90
|
+
exactIndex < 0 ? 0 : Math.min(exactIndex, nextEntries.length)
|
|
91
|
+
nextEntries.splice(insertAt, 0, replacement)
|
|
95
92
|
}
|
|
96
93
|
|
|
97
|
-
|
|
98
|
-
mkdirSync(
|
|
99
|
-
writeFileSync(
|
|
94
|
+
settings.enabledModels = nextEntries
|
|
95
|
+
mkdirSync(path.dirname(SETTINGS_PATH), { recursive: true })
|
|
96
|
+
writeFileSync(SETTINGS_PATH, `${JSON.stringify(settings, null, 2)}\n`, "utf8")
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getPinnedThinkingLevel(
|
|
100
|
+
ctx: ExtensionContext,
|
|
101
|
+
modelKey: string,
|
|
102
|
+
): ThinkingLevel | undefined {
|
|
103
|
+
const scopedModel = ctx.scopedModels.find(
|
|
104
|
+
(entry) => getModelKey(entry.model.provider, entry.model.id) === modelKey,
|
|
105
|
+
)
|
|
106
|
+
if (scopedModel?.thinkingLevel !== undefined) {
|
|
107
|
+
return scopedModel.thinkingLevel
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const entry = getEnabledModels(readSettings()).find((candidate) =>
|
|
112
|
+
candidate.startsWith(`${modelKey}:`),
|
|
113
|
+
)
|
|
114
|
+
const level = entry?.slice(modelKey.length + 1) as ThinkingLevel | undefined
|
|
115
|
+
return level && THINKING_LEVEL_OPTIONS.includes(level) ? level : undefined
|
|
116
|
+
} catch {
|
|
117
|
+
return undefined
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function clearHint(ctx: ExtensionContext): void {
|
|
122
|
+
if (hintTimer) {
|
|
123
|
+
clearTimeout(hintTimer)
|
|
124
|
+
hintTimer = undefined
|
|
125
|
+
}
|
|
126
|
+
ctx.ui.setWidget(HINT_WIDGET_KEY, undefined)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function showHint(ctx: ExtensionContext, modelKey: string): void {
|
|
130
|
+
if (!ctx.hasUI) return
|
|
131
|
+
clearHint(ctx)
|
|
132
|
+
ctx.ui.setWidget(
|
|
133
|
+
HINT_WIDGET_KEY,
|
|
134
|
+
[
|
|
135
|
+
`No saved thinking level for ${modelKey}.`,
|
|
136
|
+
"Run /preferred-thinking to set one.",
|
|
137
|
+
],
|
|
138
|
+
{ placement: "aboveEditor" },
|
|
139
|
+
)
|
|
140
|
+
hintTimer = setTimeout(() => {
|
|
141
|
+
ctx.ui.setWidget(HINT_WIDGET_KEY, undefined)
|
|
142
|
+
hintTimer = undefined
|
|
143
|
+
}, HINT_DURATION_MS)
|
|
100
144
|
}
|
|
101
145
|
|
|
102
146
|
function applyPreferredThinking(
|
|
103
147
|
pi: ExtensionAPI,
|
|
104
|
-
|
|
105
|
-
|
|
148
|
+
ctx: ExtensionContext,
|
|
149
|
+
model: NonNullable<ExtensionContext["model"]>,
|
|
106
150
|
): void {
|
|
107
|
-
const modelKey = getModelKey(provider,
|
|
108
|
-
const
|
|
109
|
-
|
|
151
|
+
const modelKey = getModelKey(model.provider, model.id)
|
|
152
|
+
const thinkingLevel = getPinnedThinkingLevel(ctx, modelKey)
|
|
153
|
+
|
|
154
|
+
if (thinkingLevel !== undefined) {
|
|
155
|
+
clearHint(ctx)
|
|
156
|
+
if (pi.getThinkingLevel() !== thinkingLevel) {
|
|
157
|
+
pi.setThinkingLevel(thinkingLevel)
|
|
158
|
+
}
|
|
159
|
+
return
|
|
160
|
+
}
|
|
110
161
|
|
|
111
|
-
if (
|
|
112
|
-
|
|
162
|
+
if (model.reasoning && !nudgedModels.has(modelKey)) {
|
|
163
|
+
nudgedModels.add(modelKey)
|
|
164
|
+
showHint(ctx, modelKey)
|
|
113
165
|
}
|
|
114
166
|
}
|
|
115
167
|
|
|
116
168
|
async function configurePreferredThinking(
|
|
117
169
|
pi: ExtensionAPI,
|
|
118
|
-
ctx:
|
|
170
|
+
ctx: ExtensionCommandContext,
|
|
119
171
|
): Promise<void> {
|
|
120
|
-
|
|
172
|
+
const model = ctx.model
|
|
173
|
+
if (!model) {
|
|
121
174
|
ctx.ui.notify("No current model selected.", "error")
|
|
122
175
|
return
|
|
123
176
|
}
|
|
124
177
|
|
|
125
|
-
const modelKey = getModelKey(
|
|
126
|
-
const current =
|
|
178
|
+
const modelKey = getModelKey(model.provider, model.id)
|
|
179
|
+
const current = getPinnedThinkingLevel(ctx, modelKey) ?? UNSET_OPTION
|
|
127
180
|
const selected = await ctx.ui.select(
|
|
128
181
|
`Preferred thinking for ${modelKey} (current: ${current})`,
|
|
129
182
|
[...THINKING_LEVEL_OPTIONS, UNSET_OPTION],
|
|
130
183
|
)
|
|
131
184
|
if (selected === undefined) return
|
|
132
185
|
|
|
133
|
-
const level =
|
|
186
|
+
const level = THINKING_LEVEL_OPTIONS.includes(selected as ThinkingLevel)
|
|
134
187
|
? (selected as ThinkingLevel)
|
|
135
188
|
: undefined
|
|
136
189
|
|
|
137
190
|
try {
|
|
138
|
-
|
|
191
|
+
saveThinkingLevel(modelKey, level)
|
|
139
192
|
} catch (error) {
|
|
140
|
-
const reason =
|
|
141
|
-
|
|
142
|
-
ctx.ui.notify(
|
|
143
|
-
`Could not update preferred thinking config: ${reason}.`,
|
|
144
|
-
"error",
|
|
145
|
-
)
|
|
193
|
+
const reason = error instanceof SyntaxError ? "invalid JSON" : String(error)
|
|
194
|
+
ctx.ui.notify(`Could not update Pi settings: ${reason}`, "error")
|
|
146
195
|
return
|
|
147
196
|
}
|
|
148
197
|
|
|
149
198
|
if (level) {
|
|
150
199
|
pi.setThinkingLevel(level)
|
|
151
|
-
ctx.ui.notify(`
|
|
200
|
+
ctx.ui.notify(`Thinking for ${modelKey} set to ${level}.`, "info")
|
|
152
201
|
} else {
|
|
153
|
-
ctx.ui.notify(`
|
|
202
|
+
ctx.ui.notify(`Thinking pin for ${modelKey} removed.`, "info")
|
|
154
203
|
}
|
|
204
|
+
|
|
205
|
+
await ctx.reload()
|
|
155
206
|
}
|
|
156
207
|
|
|
157
208
|
export default function (pi: ExtensionAPI): void {
|
|
158
209
|
pi.registerCommand("preferred-thinking", {
|
|
159
|
-
description: "set the
|
|
210
|
+
description: "set the native thinking level for the current model",
|
|
160
211
|
handler: async (_args, ctx) => {
|
|
161
212
|
await configurePreferredThinking(pi, ctx)
|
|
162
213
|
},
|
|
163
214
|
})
|
|
164
215
|
|
|
165
216
|
pi.on("session_start", async (_event, ctx) => {
|
|
166
|
-
|
|
167
|
-
applyPreferredThinking(pi, ctx
|
|
217
|
+
nudgedModels.clear()
|
|
218
|
+
if (ctx.model) applyPreferredThinking(pi, ctx, ctx.model)
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
pi.on("model_select", async (event, ctx) => {
|
|
222
|
+
applyPreferredThinking(pi, ctx, event.model)
|
|
168
223
|
})
|
|
169
224
|
|
|
170
|
-
pi.on("
|
|
171
|
-
|
|
225
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
226
|
+
clearHint(ctx)
|
|
172
227
|
})
|
|
173
228
|
}
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @tifan/pi-recap
|
|
2
2
|
|
|
3
|
+
## 0.4.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#116](https://github.com/HerbertGao/pi-extensions/pull/116) [`ecda398`](https://github.com/HerbertGao/pi-extensions/commit/ecda398b9a95212c512d53325c617e485e6ded8d) Thanks [@HerbertGao](https://github.com/HerbertGao)! - Use Pi 0.84.2 model-registry completion and authentication APIs for recap generation and model selection.
|
|
8
|
+
|
|
9
|
+
## 0.4.5
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`12ef66c`](https://github.com/tifandotme/pi-extensions/commit/12ef66c7ec6efe04202c91f70e20cd97e9c167f3) Thanks [@tifandotme](https://github.com/tifandotme)! - Require Pi 0.84.2 or newer for recap generation.
|
|
14
|
+
|
|
3
15
|
## 0.4.4
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -14,6 +14,8 @@ Re-enter a session without rereading the transcript.
|
|
|
14
14
|
pi install npm:@herbertgao/pi-recap
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
This package requires Pi 0.84.2 or newer.
|
|
18
|
+
|
|
17
19
|
## How it works
|
|
18
20
|
|
|
19
21
|
- `/recap` generates a fresh, goal-first recap and shows it above the editor.
|
|
@@ -36,7 +38,7 @@ A good recap should answer "what was I trying to do here?". For example:
|
|
|
36
38
|
|
|
37
39
|
## Configuration
|
|
38
40
|
|
|
39
|
-
Out of the box, `pi-recap` uses this default model: `openai-codex/gpt-5.
|
|
41
|
+
Out of the box, `pi-recap` uses this default model: `openai-codex/gpt-5.6-luna`.
|
|
40
42
|
|
|
41
43
|
Run `/recap config` to choose a different model.
|
|
42
44
|
|
|
@@ -48,13 +50,13 @@ You can also edit `~/.config/pi/extensions/pi-recap.json` manually:
|
|
|
48
50
|
|
|
49
51
|
```json
|
|
50
52
|
{
|
|
51
|
-
"model": "openai-codex/gpt-5.
|
|
53
|
+
"model": "openai-codex/gpt-5.6-luna"
|
|
52
54
|
}
|
|
53
55
|
```
|
|
54
56
|
|
|
55
57
|
## Release notes
|
|
56
58
|
|
|
57
|
-
See [CHANGELOG.md](CHANGELOG.md)
|
|
59
|
+
See [CHANGELOG.md](CHANGELOG.md).
|
|
58
60
|
|
|
59
61
|
## License
|
|
60
62
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herbertgao/pi-recap",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "One-line session recap on demand or after you have been away.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|
|
@@ -32,6 +32,12 @@
|
|
|
32
32
|
"access": "public",
|
|
33
33
|
"provenance": true
|
|
34
34
|
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@earendil-works/pi-agent-core": ">=0.84.2",
|
|
37
|
+
"@earendil-works/pi-ai": ">=0.84.2",
|
|
38
|
+
"@earendil-works/pi-coding-agent": ">=0.84.2",
|
|
39
|
+
"@earendil-works/pi-tui": ">=0.84.2"
|
|
40
|
+
},
|
|
35
41
|
"engines": {
|
|
36
42
|
"node": ">=20"
|
|
37
43
|
},
|
|
@@ -42,8 +48,8 @@
|
|
|
42
48
|
},
|
|
43
49
|
"x-upstream": {
|
|
44
50
|
"package": "@tifan/pi-recap",
|
|
45
|
-
"version": "0.4.
|
|
51
|
+
"version": "0.4.5",
|
|
46
52
|
"repository": "https://github.com/tifandotme/pi-extensions",
|
|
47
|
-
"commit": "
|
|
53
|
+
"commit": "b39d1e6359c78718cbf8fe5c74c82b89065e9a0b"
|
|
48
54
|
}
|
|
49
55
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentMessage } from "@earendil-works/pi-agent-core"
|
|
2
|
-
import {
|
|
2
|
+
import type { Message } from "@earendil-works/pi-ai"
|
|
3
3
|
import type { AutocompleteItem } from "@earendil-works/pi-tui"
|
|
4
4
|
import type {
|
|
5
5
|
ExtensionAPI,
|
|
@@ -276,22 +276,18 @@ async function generateRecap(
|
|
|
276
276
|
return
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
-
const auth = modelAuth.auth
|
|
280
|
-
|
|
281
279
|
const abortController = new AbortController()
|
|
282
280
|
abortPendingGeneration(state)
|
|
283
281
|
state.abortController = abortController
|
|
284
282
|
|
|
285
283
|
try {
|
|
286
|
-
const response = await complete(
|
|
287
|
-
auth
|
|
284
|
+
const response = await ctx.modelRegistry.complete(
|
|
285
|
+
modelAuth.auth,
|
|
288
286
|
{
|
|
289
287
|
systemPrompt: RECAP_SYSTEM_PROMPT,
|
|
290
288
|
messages: [buildPrompt(messages)],
|
|
291
289
|
},
|
|
292
290
|
{
|
|
293
|
-
apiKey: auth.apiKey,
|
|
294
|
-
...(auth.headers ? { headers: auth.headers } : {}),
|
|
295
291
|
maxTokens: RECAP_MAX_TOKENS,
|
|
296
292
|
maxRetries: 0,
|
|
297
293
|
cacheRetention: "none",
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
type RecapModelPreference,
|
|
14
14
|
} from "./models.js"
|
|
15
15
|
|
|
16
|
-
const USE_DEFAULT_VALUE = "__pi-recap-default__"
|
|
17
16
|
const MAX_VISIBLE_ITEMS = 10
|
|
18
17
|
|
|
19
18
|
export type ModelPickerResult =
|
|
@@ -22,7 +21,6 @@ export type ModelPickerResult =
|
|
|
22
21
|
| { action: "cancel" }
|
|
23
22
|
|
|
24
23
|
interface ModelPickerItem {
|
|
25
|
-
readonly value: string
|
|
26
24
|
readonly label: string
|
|
27
25
|
readonly description?: string
|
|
28
26
|
readonly model?: RecapModelPreference
|
|
@@ -43,12 +41,10 @@ class RecapModelPicker implements Component, Focusable {
|
|
|
43
41
|
this.theme = theme
|
|
44
42
|
this.items = [
|
|
45
43
|
{
|
|
46
|
-
value: USE_DEFAULT_VALUE,
|
|
47
44
|
label: "Use default",
|
|
48
45
|
description: formatRecapModelKey(DEFAULT_RECAP_MODEL),
|
|
49
46
|
},
|
|
50
47
|
...models.map((model) => ({
|
|
51
|
-
value: formatRecapModelKey(model),
|
|
52
48
|
label: formatRecapModelKey(model),
|
|
53
49
|
model,
|
|
54
50
|
})),
|
|
@@ -90,11 +86,7 @@ class RecapModelPicker implements Component, Focusable {
|
|
|
90
86
|
} else {
|
|
91
87
|
for (const [index, item] of visibleItems.entries()) {
|
|
92
88
|
lines.push(
|
|
93
|
-
this.renderItem(
|
|
94
|
-
item,
|
|
95
|
-
startIndex + index === this.selectedIndex,
|
|
96
|
-
width,
|
|
97
|
-
),
|
|
89
|
+
this.renderItem(item, startIndex + index === this.selectedIndex),
|
|
98
90
|
)
|
|
99
91
|
}
|
|
100
92
|
}
|
|
@@ -146,11 +138,7 @@ class RecapModelPicker implements Component, Focusable {
|
|
|
146
138
|
}
|
|
147
139
|
}
|
|
148
140
|
|
|
149
|
-
private renderItem(
|
|
150
|
-
item: ModelPickerItem,
|
|
151
|
-
isSelected: boolean,
|
|
152
|
-
width: number,
|
|
153
|
-
): string {
|
|
141
|
+
private renderItem(item: ModelPickerItem, isSelected: boolean): string {
|
|
154
142
|
const prefix = isSelected ? "→ " : " "
|
|
155
143
|
const label = isSelected
|
|
156
144
|
? this.theme.fg("accent", item.label)
|
|
@@ -158,7 +146,7 @@ class RecapModelPicker implements Component, Focusable {
|
|
|
158
146
|
const description = item.description
|
|
159
147
|
? this.theme.fg("dim", ` ${item.description}`)
|
|
160
148
|
: ""
|
|
161
|
-
return
|
|
149
|
+
return `${prefix}${label}${description}`
|
|
162
150
|
}
|
|
163
151
|
|
|
164
152
|
private moveSelection(delta: number): void {
|
|
@@ -171,14 +159,13 @@ class RecapModelPicker implements Component, Focusable {
|
|
|
171
159
|
const item = this.getFilteredItems()[this.selectedIndex]
|
|
172
160
|
if (!item) return
|
|
173
161
|
|
|
174
|
-
|
|
162
|
+
const model = item.model
|
|
163
|
+
if (!model) {
|
|
175
164
|
this.onDone({ action: "default" })
|
|
176
165
|
return
|
|
177
166
|
}
|
|
178
167
|
|
|
179
|
-
|
|
180
|
-
this.onDone({ action: "model", model: item.model })
|
|
181
|
-
}
|
|
168
|
+
this.onDone({ action: "model", model })
|
|
182
169
|
}
|
|
183
170
|
|
|
184
171
|
private getFilter(): string {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
writeFileSync,
|
|
7
7
|
} from "node:fs"
|
|
8
8
|
import path from "node:path"
|
|
9
|
-
import type { Api, Model
|
|
9
|
+
import type { Api, Model } from "@earendil-works/pi-ai"
|
|
10
10
|
import {
|
|
11
11
|
getAgentDir,
|
|
12
12
|
type ExtensionContext,
|
|
@@ -20,12 +20,6 @@ export interface RecapModelPreference {
|
|
|
20
20
|
readonly id: string
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export interface FastModelAuth {
|
|
24
|
-
readonly model: Model<Api>
|
|
25
|
-
readonly apiKey: string
|
|
26
|
-
readonly headers: ProviderHeaders | undefined
|
|
27
|
-
}
|
|
28
|
-
|
|
29
23
|
export type RecapModelConfig =
|
|
30
24
|
| { readonly kind: "missing" }
|
|
31
25
|
| { readonly kind: "invalid" }
|
|
@@ -34,7 +28,7 @@ export type RecapModelConfig =
|
|
|
34
28
|
export type ResolvedRecapModelAuth =
|
|
35
29
|
| {
|
|
36
30
|
readonly status: "ok"
|
|
37
|
-
readonly auth:
|
|
31
|
+
readonly auth: Model<Api>
|
|
38
32
|
readonly source: "configured" | "default"
|
|
39
33
|
}
|
|
40
34
|
| { readonly status: "invalid-config" }
|
|
@@ -66,8 +60,8 @@ export function formatRecapModelKey({
|
|
|
66
60
|
return `${provider}/${id}`
|
|
67
61
|
}
|
|
68
62
|
|
|
69
|
-
export function formatAuthModelKey(auth:
|
|
70
|
-
return `${auth.
|
|
63
|
+
export function formatAuthModelKey(auth: Model<Api>): string {
|
|
64
|
+
return `${auth.provider}/${auth.id}`
|
|
71
65
|
}
|
|
72
66
|
|
|
73
67
|
export function parseModelSpec(
|
|
@@ -127,21 +121,15 @@ export function resolveInitialModelConfig(): RecapModelConfig {
|
|
|
127
121
|
async function getModelAuth(
|
|
128
122
|
ctx: ExtensionContext,
|
|
129
123
|
modelPreference: RecapModelPreference,
|
|
130
|
-
): Promise<
|
|
124
|
+
): Promise<Model<Api> | undefined> {
|
|
131
125
|
const model = ctx.modelRegistry.find(
|
|
132
126
|
modelPreference.provider,
|
|
133
127
|
modelPreference.id,
|
|
134
128
|
)
|
|
135
129
|
if (!model) return undefined
|
|
136
130
|
|
|
137
|
-
const auth = await ctx.modelRegistry.
|
|
138
|
-
return auth.
|
|
139
|
-
? {
|
|
140
|
-
model,
|
|
141
|
-
apiKey: auth.apiKey,
|
|
142
|
-
headers: auth.headers,
|
|
143
|
-
}
|
|
144
|
-
: undefined
|
|
131
|
+
const auth = await ctx.modelRegistry.getProviderAuth(model.provider)
|
|
132
|
+
return auth?.auth.apiKey ? model : undefined
|
|
145
133
|
}
|
|
146
134
|
|
|
147
135
|
export async function getRecapModelAuth(
|
|
@@ -181,8 +169,8 @@ export async function getAuthenticatedTextModelPreferences(
|
|
|
181
169
|
.filter((model) => model.input.includes("text"))
|
|
182
170
|
const authenticatedModels = await Promise.all(
|
|
183
171
|
models.map(async (model) => {
|
|
184
|
-
const auth = await ctx.modelRegistry.
|
|
185
|
-
return auth
|
|
172
|
+
const auth = await ctx.modelRegistry.getProviderAuth(model.provider)
|
|
173
|
+
return auth?.auth.apiKey ? toModelPreference(model) : undefined
|
|
186
174
|
}),
|
|
187
175
|
)
|
|
188
176
|
|
|
@@ -82,7 +82,7 @@ class PiRecapLine implements Component {
|
|
|
82
82
|
const labelWidth = visibleWidth(LABEL)
|
|
83
83
|
const label = this.renderLabel()
|
|
84
84
|
|
|
85
|
-
if (width < MIN_RECAP_WIDTH
|
|
85
|
+
if (width < MIN_RECAP_WIDTH) {
|
|
86
86
|
return [
|
|
87
87
|
truncateToWidth(this.theme.fg("muted", `${LABEL}${this.recap}`), width),
|
|
88
88
|
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @tifan/pi-rename
|
|
2
|
+
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#116](https://github.com/HerbertGao/pi-extensions/pull/116) [`ecda398`](https://github.com/HerbertGao/pi-extensions/commit/ecda398b9a95212c512d53325c617e485e6ded8d) Thanks [@HerbertGao](https://github.com/HerbertGao)! - Use Pi 0.84.2 model-registry APIs, show rename progress, and synchronize generated names across Herdr panes and tabs.
|
|
8
|
+
|
|
9
|
+
## 0.5.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`b2ea184`](https://github.com/tifandotme/pi-extensions/commit/b2ea1841e99243aa4773bd5c33f43c40248859db) Thanks [@tifandotme](https://github.com/tifandotme)! - Update Herdr synchronization to rename the current pane in split tabs and both the pane and tab in single-pane tabs.
|
|
14
|
+
|
|
15
|
+
## 0.4.3
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [`e8709bc`](https://github.com/tifandotme/pi-extensions/commit/e8709bc388aa8e094918ec465cfab511358f18ff) Thanks [@tifandotme](https://github.com/tifandotme)! - Require Pi 0.84.2 or newer and show progress while `/rename` generates a session name.
|
|
20
|
+
|
|
21
|
+
## 0.4.2
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- [`b27ba8a`](https://github.com/tifandotme/pi-extensions/commit/b27ba8aef49ea00879cec87d913840f39e02b19e) Thanks [@tifandotme](https://github.com/tifandotme)! - Use gpt-5.6-luna by default and keep Herdr tab names after quitting Pi.
|
|
26
|
+
|
|
27
|
+
## 0.4.1
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- [`ba259a0`](https://github.com/tifandotme/pi-extensions/commit/ba259a0c990563e4dad3443371cc2ed31d122cd0) Thanks [@tifandotme](https://github.com/tifandotme)! - Keep model-powered handoffs and session names compatible with Pi 0.80.
|
|
32
|
+
|
|
33
|
+
## 0.4.0
|
|
34
|
+
|
|
35
|
+
### Minor Changes
|
|
36
|
+
|
|
37
|
+
- [`416ac2f`](https://github.com/tifandotme/pi-extensions/commit/416ac2fecc6aa731c23ae895220bac87317152e4) Thanks [@tifandotme](https://github.com/tifandotme)! - Expose the rename naming utilities through the package exports for reuse.
|
|
38
|
+
|
|
39
|
+
## 0.3.0
|
|
40
|
+
|
|
41
|
+
### Minor Changes
|
|
42
|
+
|
|
43
|
+
- [`57565bd`](https://github.com/tifandotme/pi-extensions/commit/57565bd14cf86cb51ca5ba5c6d937568fc4e251f) Thanks [@tifandotme](https://github.com/tifandotme)! - Sync Herdr tab labels with saved pi session names on resume, and reset matching tab labels to the tab number when pi quits.
|
|
44
|
+
|
|
45
|
+
## 0.2.0
|
|
46
|
+
|
|
47
|
+
### Minor Changes
|
|
48
|
+
|
|
49
|
+
- [`392f57c`](https://github.com/tifandotme/pi-extensions/commit/392f57c3dd62858d41f462801ef53773596e3f96) Thanks [@tifandotme](https://github.com/tifandotme)! - Add `/rename` to generate hyphen-separated session names, configure the rename model, and mirror names to the current Herdr tab.
|