@pi-unipi/unipi 3.0.0-alpha.2 → 3.0.0-alpha.21
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/CHANGELOG.md +292 -0
- package/README.md +8 -11
- package/docs/prefix-cache-architecture.md +14 -0
- package/package.json +22 -24
- package/packages/ask-user/README.md +55 -107
- package/packages/ask-user/ask-ui.ts +306 -732
- package/packages/ask-user/clipboard.ts +50 -0
- package/packages/ask-user/config.ts +74 -78
- package/packages/ask-user/package.json +2 -2
- package/packages/ask-user/questions.ts +151 -0
- package/packages/ask-user/skills/ask-user/SKILL.md +59 -133
- package/packages/ask-user/tools.ts +198 -432
- package/packages/autocomplete/package.json +1 -1
- package/packages/autocomplete/src/constants.ts +38 -43
- package/packages/autocomplete/src/sorting.ts +8 -0
- package/packages/background-tasks/package.json +2 -2
- package/packages/background-tasks/src/config.ts +0 -73
- package/packages/background-tasks/src/index.ts +20 -16
- package/packages/background-tasks/src/registry.ts +1 -171
- package/packages/background-tasks/src/types.ts +0 -32
- package/packages/btw/README.md +13 -56
- package/packages/btw/extensions/btw.ts +403 -1796
- package/packages/btw/package.json +5 -2
- package/packages/btw/skills/btw/SKILL.md +7 -143
- package/packages/compactor/README.md +56 -287
- package/packages/compactor/package.json +5 -9
- package/packages/compactor/skills/compactor/SKILL.md +13 -20
- package/packages/compactor/src/card.ts +90 -0
- package/packages/compactor/src/commands/index.ts +73 -130
- package/packages/compactor/src/compaction/compact-args.ts +5 -3
- package/packages/compactor/src/compaction/filter-noise.ts +1 -1
- package/packages/compactor/src/compaction/hooks.ts +487 -311
- package/packages/compactor/src/compaction/source.ts +178 -0
- package/packages/compactor/src/compaction/summarize.ts +586 -52
- package/packages/compactor/src/config/manager.ts +130 -127
- package/packages/compactor/src/config/schema.ts +32 -48
- package/packages/compactor/src/index.ts +40 -431
- package/packages/compactor/src/info-screen.ts +44 -190
- package/packages/compactor/src/session/recall-blocks.ts +1 -1
- package/packages/compactor/src/stats.ts +76 -0
- package/packages/compactor/src/tools/context-budget.ts +17 -34
- package/packages/compactor/src/tools/ctx-doctor.ts +45 -36
- package/packages/compactor/src/tools/register.ts +29 -269
- package/packages/compactor/src/types.ts +47 -186
- package/packages/core/README.md +25 -0
- package/packages/core/child-agent.ts +1 -0
- package/packages/core/command-echo.ts +63 -0
- package/packages/core/compaction-context.ts +41 -0
- package/packages/core/constants.ts +8 -38
- package/packages/core/events.ts +0 -53
- package/packages/core/fusion-status.ts +4 -0
- package/packages/core/global.d.ts +10 -0
- package/packages/core/harness-messages.ts +392 -0
- package/packages/core/index.ts +14 -0
- package/packages/core/kanboard-status.ts +49 -0
- package/packages/core/long-horizon-owner-status.ts +46 -0
- package/packages/core/model-cache.ts +81 -7
- package/packages/core/package.json +6 -3
- package/packages/core/src/attach/detect.ts +163 -0
- package/packages/core/src/child-agent/child-args.ts +31 -0
- package/packages/core/src/child-agent/delivery.ts +41 -0
- package/packages/core/src/child-agent/index.ts +5 -0
- package/packages/core/src/child-agent/readers.ts +203 -0
- package/packages/core/src/child-agent/runtime.ts +584 -0
- package/packages/core/src/evidence.ts +78 -0
- package/packages/core/src/hints/browser.ts +278 -0
- package/packages/core/src/hints/crab-data.ts +19 -0
- package/packages/core/src/hints/index.ts +865 -0
- package/packages/core/src/hints/lines.ts +607 -0
- package/packages/core/src/hints/store.ts +120 -0
- package/packages/core/src/jev/chatter.ts +24 -0
- package/packages/core/src/jev/settings.ts +177 -14
- package/packages/core/src/package-colors.ts +2 -0
- package/packages/core/src/settings/catalog.ts +39 -15
- package/packages/core/src/settings/engine.ts +48 -2
- package/packages/core/src/settings/hub.ts +83 -20
- package/packages/core/src/settings/open.ts +49 -0
- package/packages/core/src/settings/schema.ts +27 -7
- package/packages/core/src/tui/hub-kit.ts +18 -5
- package/packages/core/src/tui/kit.ts +198 -0
- package/packages/core/src/tui/progress.ts +73 -0
- package/packages/core/src/turn/arbiter.ts +290 -0
- package/packages/footer/README.md +4 -6
- package/packages/footer/package.json +3 -3
- package/packages/footer/src/glance-editor.ts +19 -11
- package/packages/footer/src/help.ts +1 -1
- package/packages/footer/src/index.ts +36 -3
- package/packages/footer/src/presets.ts +3 -4
- package/packages/footer/src/rendering/icons.ts +0 -20
- package/packages/footer/src/segments/compactor.ts +52 -142
- package/packages/footer/src/segments/status-ext.ts +0 -1
- package/packages/fusion/README.md +14 -4
- package/packages/fusion/package.json +4 -3
- package/packages/fusion/src/child-args.ts +1 -0
- package/packages/fusion/src/index.ts +161 -8
- package/packages/fusion/src/pi-settings.ts +57 -0
- package/packages/fusion/src/picker.ts +186 -51
- package/packages/fusion/src/preset.ts +56 -2
- package/packages/fusion/src/prompts.ts +1 -1
- package/packages/fusion/src/sidekick-runtime.ts +1 -420
- package/packages/fusion/src/tools.ts +107 -128
- package/packages/fusion/src/transcript.ts +27 -85
- package/packages/info-screen/package.json +2 -2
- package/packages/input-shortcuts/README.md +18 -30
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/input-shortcuts/src/burst.ts +88 -0
- package/packages/input-shortcuts/src/chord-overlay.ts +20 -70
- package/packages/input-shortcuts/src/cleared-input.ts +97 -0
- package/packages/input-shortcuts/src/index.ts +99 -193
- package/packages/input-shortcuts/src/last-response.ts +40 -0
- package/packages/input-shortcuts/src/registers.ts +8 -23
- package/packages/input-shortcuts/src/types.ts +4 -16
- package/packages/input-shortcuts/src/undo-redo.ts +59 -47
- package/packages/kanboard/README.md +51 -47
- package/packages/kanboard/index.ts +293 -55
- package/packages/kanboard/package.json +8 -8
- package/packages/kanboard/skills/kanboard/SKILL.md +61 -27
- package/packages/kanboard/src/badges.ts +93 -0
- package/packages/kanboard/src/bin.ts +4 -2
- package/packages/kanboard/src/commands.ts +218 -84
- package/packages/kanboard/src/debug.ts +20 -0
- package/packages/kanboard/src/guard.ts +267 -109
- package/packages/kanboard/src/monitor.ts +330 -0
- package/packages/kanboard/src/notice-buffer.ts +74 -0
- package/packages/kanboard/src/progress.ts +27 -0
- package/packages/kanboard/src/reminders.ts +234 -0
- package/packages/kanboard/src/settings.ts +69 -15
- package/packages/kanboard/src/shapes.ts +5 -0
- package/packages/long-horizon/index.ts +190 -13
- package/packages/long-horizon/package.json +3 -3
- package/packages/long-horizon/src/commands.ts +103 -7
- package/packages/long-horizon/src/compaction-brief.ts +35 -0
- package/packages/long-horizon/src/engine/continuation.ts +84 -15
- package/packages/long-horizon/src/engine/goal-state.ts +64 -13
- package/packages/long-horizon/src/engine/nudge-stash.ts +71 -0
- package/packages/long-horizon/src/engine/ralph.ts +3 -2
- package/packages/long-horizon/src/engine/runaway.ts +68 -11
- package/packages/long-horizon/src/engine/verifier.ts +19 -0
- package/packages/long-horizon/src/gate.ts +37 -12
- package/packages/long-horizon/src/judge/resolve.ts +1 -0
- package/packages/long-horizon/src/owner.ts +35 -2
- package/packages/long-horizon/src/progress.ts +87 -0
- package/packages/long-horizon/src/prompts/goal.ts +1 -1
- package/packages/long-horizon/src/runtime.ts +172 -18
- package/packages/long-horizon/src/settings.ts +54 -39
- package/packages/long-horizon/src/tools/goal.ts +6 -0
- package/packages/long-horizon/src/tools/graph.ts +1 -1
- package/packages/long-horizon/src/tools/swarm.ts +3 -3
- package/packages/mcp/package.json +2 -2
- package/packages/memory/commands.ts +7 -2
- package/packages/memory/index.ts +210 -32
- package/packages/memory/package.json +3 -3
- package/packages/memory/save-session.ts +264 -0
- package/packages/memory/settings.ts +17 -0
- package/packages/memory/tools.ts +253 -203
- package/packages/notify/package.json +2 -2
- package/packages/skill-registry/README.md +53 -16
- package/packages/skill-registry/index.ts +230 -0
- package/packages/skill-registry/package.json +19 -8
- package/packages/skill-registry/src/editor.ts +206 -0
- package/packages/skill-registry/src/judge.ts +289 -0
- package/packages/skill-registry/src/registry.ts +87 -0
- package/packages/skill-registry/src/settings.ts +259 -0
- package/packages/skill-registry/src/vault.ts +80 -0
- package/packages/subagents/README.md +39 -119
- package/packages/subagents/package.json +6 -13
- package/packages/subagents/skills/subagents/SKILL.md +18 -42
- package/packages/subagents/src/agents.ts +213 -0
- package/packages/subagents/src/cards.ts +182 -0
- package/packages/subagents/src/index.ts +609 -1084
- package/packages/subagents/src/manager.ts +429 -0
- package/packages/subagents/src/profiles.ts +130 -0
- package/packages/subagents/src/transcript.ts +154 -0
- package/packages/subagents/src/ui.ts +389 -0
- package/packages/unipi/bundled.js +39756 -45569
- package/packages/unipi/index.ts +55 -10
- package/packages/updater/package.json +2 -2
- package/packages/utility/README.md +76 -69
- package/packages/utility/package.json +2 -2
- package/packages/utility/skills/image/SKILL.md +37 -0
- package/packages/utility/skills/utility/SKILL.md +17 -60
- package/packages/utility/src/answer/extract.ts +128 -0
- package/packages/utility/src/answer/index.ts +213 -0
- package/packages/utility/src/answer/reply.ts +133 -0
- package/packages/utility/src/answer/web.ts +167 -0
- package/packages/utility/src/attach/index.ts +205 -0
- package/packages/utility/src/commands.ts +38 -142
- package/packages/utility/src/diagnostics/engine.ts +50 -5
- package/packages/utility/src/image/index.ts +33 -0
- package/packages/utility/src/image/models.ts +221 -0
- package/packages/{image/src → utility/src/image}/recognize.ts +2 -2
- package/packages/utility/src/image/settings.ts +178 -0
- package/packages/{image/src/image-source.ts → utility/src/image/source.ts} +1 -1
- package/packages/utility/src/image/tools.ts +168 -0
- package/packages/utility/src/image/transports.ts +186 -0
- package/packages/utility/src/index.ts +68 -389
- package/packages/utility/src/lifecycle/cleanup.ts +116 -311
- package/packages/utility/src/rename/gate.ts +102 -0
- package/packages/utility/src/rename/index.ts +191 -0
- package/packages/utility/src/rename/session.ts +170 -0
- package/packages/utility/src/render/delegated.ts +285 -0
- package/packages/utility/src/render/harness.ts +683 -0
- package/packages/utility/src/render/parse.ts +156 -0
- package/packages/utility/src/render/pi-settings.ts +38 -0
- package/packages/utility/src/render/reply-bg.ts +307 -0
- package/packages/utility/src/render/simple.ts +724 -0
- package/packages/utility/src/render/spacing.ts +235 -0
- package/packages/utility/src/render/styled.ts +251 -0
- package/packages/utility/src/render/tools.ts +353 -0
- package/packages/utility/src/settings.ts +166 -233
- package/packages/utility/src/types.ts +1 -222
- package/packages/watchdog/index.ts +17 -6
- package/packages/watchdog/package.json +3 -3
- package/packages/watchdog/src/config.ts +3 -2
- package/packages/web-api/package.json +2 -2
- package/packages/workflow/README.md +3 -0
- package/packages/workflow/index.ts +0 -2
- package/packages/workflow/package.json +2 -2
- package/packages/workflow/src/permission/decide.ts +6 -4
- package/packages/workflow/src/permission/index.ts +17 -7
- package/packages/workflow/src/permission/settings.ts +39 -4
- package/packages/workflow/src/plan/index.ts +19 -4
- package/packages/background-tasks/extensions/delegate-child.ts +0 -1
- package/packages/background-tasks/src/context-parent-snapshot.ts +0 -142
- package/packages/background-tasks/src/context-token-budget.ts +0 -903
- package/packages/background-tasks/src/context-visible-conversation-v2.ts +0 -551
- package/packages/background-tasks/src/delegate/artifacts.ts +0 -487
- package/packages/background-tasks/src/delegate/budget.ts +0 -415
- package/packages/background-tasks/src/delegate/hook-contract-evidence.json +0 -18
- package/packages/background-tasks/src/delegate/hook-contract.ts +0 -154
- package/packages/background-tasks/src/delegate/launch.ts +0 -484
- package/packages/background-tasks/src/delegate/result-package.ts +0 -459
- package/packages/background-tasks/src/delegate/runner.ts +0 -430
- package/packages/background-tasks/src/delegate/seed.ts +0 -423
- package/packages/background-tasks/src/delegate/types.ts +0 -323
- package/packages/background-tasks/src/delegate-child-extension.ts +0 -978
- package/packages/background-tasks/src/delegate-extension.ts +0 -654
- package/packages/background-tasks/src/fixtures/delegate-context-incident.json +0 -17
- package/packages/compactor/skills/compactor-detail/SKILL.md +0 -116
- package/packages/compactor/skills/compactor-doctor/SKILL.md +0 -71
- package/packages/compactor/skills/compactor-stats/SKILL.md +0 -51
- package/packages/compactor/src/compaction/build-sections.ts +0 -76
- package/packages/compactor/src/compaction/extract/goals.ts +0 -36
- package/packages/compactor/src/compaction/extract/preferences.ts +0 -40
- package/packages/compactor/src/compaction/format.ts +0 -53
- package/packages/compactor/src/compaction/merge.ts +0 -143
- package/packages/compactor/src/config/presets.ts +0 -160
- package/packages/compactor/src/executor/executor.ts +0 -233
- package/packages/compactor/src/executor/runtime.ts +0 -115
- package/packages/compactor/src/security/evaluator.ts +0 -89
- package/packages/compactor/src/security/policy.ts +0 -96
- package/packages/compactor/src/security/scanner.ts +0 -65
- package/packages/compactor/src/session/auto-inject.ts +0 -60
- package/packages/compactor/src/session/db.ts +0 -325
- package/packages/compactor/src/session/extract.ts +0 -144
- package/packages/compactor/src/session/resume-inject.ts +0 -64
- package/packages/compactor/src/session/snapshot.ts +0 -327
- package/packages/compactor/src/tools/ctx-batch-execute.ts +0 -41
- package/packages/compactor/src/tools/ctx-execute-file.ts +0 -23
- package/packages/compactor/src/tools/ctx-execute.ts +0 -23
- package/packages/compactor/src/tools/ctx-stats.ts +0 -96
- package/packages/image/README.md +0 -99
- package/packages/image/package.json +0 -61
- package/packages/image/skills/image/SKILL.md +0 -76
- package/packages/image/src/generate.ts +0 -256
- package/packages/image/src/index.ts +0 -135
- package/packages/image/src/models.ts +0 -519
- package/packages/image/src/openai-images-api.ts +0 -282
- package/packages/image/src/register-providers.ts +0 -209
- package/packages/image/src/settings.ts +0 -173
- package/packages/image/src/tools.ts +0 -326
- package/packages/input-shortcuts/src/clipboard.ts +0 -119
- package/packages/kanboard/src/runner.ts +0 -817
- package/packages/skill-registry/skills/auto/SKILL.md +0 -465
- package/packages/skill-registry/skills/brainstorm/SKILL.md +0 -219
- package/packages/skill-registry/skills/chore-create/SKILL.md +0 -315
- package/packages/skill-registry/skills/chore-execute/SKILL.md +0 -314
- package/packages/skill-registry/skills/consolidate/SKILL.md +0 -142
- package/packages/skill-registry/skills/consultant/SKILL.md +0 -97
- package/packages/skill-registry/skills/debug/SKILL.md +0 -228
- package/packages/skill-registry/skills/document/SKILL.md +0 -120
- package/packages/skill-registry/skills/fix/SKILL.md +0 -212
- package/packages/skill-registry/skills/gather-context/SKILL.md +0 -145
- package/packages/skill-registry/skills/plan/SKILL.md +0 -231
- package/packages/skill-registry/skills/quick-fix/SKILL.md +0 -135
- package/packages/skill-registry/skills/quick-work/SKILL.md +0 -110
- package/packages/skill-registry/skills/research/SKILL.md +0 -251
- package/packages/skill-registry/skills/review-work/SKILL.md +0 -187
- package/packages/skill-registry/skills/scan-issues/SKILL.md +0 -195
- package/packages/skill-registry/skills/work/SKILL.md +0 -218
- package/packages/skill-registry/skills/worktree-create/SKILL.md +0 -69
- package/packages/skill-registry/skills/worktree-list/SKILL.md +0 -78
- package/packages/skill-registry/skills/worktree-merge/SKILL.md +0 -89
- package/packages/subagents/agents/delegate.md +0 -14
- package/packages/subagents/agents/oracle.md +0 -78
- package/packages/subagents/agents/researcher.md +0 -52
- package/packages/subagents/agents/reviewer.md +0 -80
- package/packages/subagents/agents/scout.md +0 -50
- package/packages/subagents/agents/worker.md +0 -59
- package/packages/subagents/prompts/council.md +0 -48
- package/packages/subagents/prompts/gather-context-and-clarify.md +0 -13
- package/packages/subagents/prompts/parallel-cleanup.md +0 -59
- package/packages/subagents/prompts/parallel-research.md +0 -50
- package/packages/subagents/prompts/parallel-review.md +0 -54
- package/packages/subagents/prompts/review-loop.md +0 -43
- package/packages/subagents/skills/council-mode/SKILL.md +0 -230
- package/packages/subagents/skills/subagents/references/constraints-and-recipes.md +0 -259
- package/packages/subagents/skills/subagents/references/execution-controls.md +0 -454
- package/packages/subagents/skills/subagents/references/management-authoring-rpc.md +0 -161
- package/packages/subagents/skills/subagents/references/multi-lane-orchestration.md +0 -39
- package/packages/subagents/skills/subagents/references/prompting-and-roles.md +0 -267
- package/packages/subagents/src/acceptance.ts +0 -331
- package/packages/subagents/src/agent-manager.ts +0 -405
- package/packages/subagents/src/agent-memory.ts +0 -300
- package/packages/subagents/src/agent-overrides.ts +0 -183
- package/packages/subagents/src/agent-runner.ts +0 -271
- package/packages/subagents/src/async-runner.ts +0 -422
- package/packages/subagents/src/authority-policy.ts +0 -47
- package/packages/subagents/src/budgets.ts +0 -285
- package/packages/subagents/src/child-safety.ts +0 -140
- package/packages/subagents/src/config.ts +0 -249
- package/packages/subagents/src/conversation-viewer.ts +0 -281
- package/packages/subagents/src/core-compat.ts +0 -94
- package/packages/subagents/src/custom-agents.ts +0 -343
- package/packages/subagents/src/file-system-retry.ts +0 -50
- package/packages/subagents/src/fleet-data.ts +0 -105
- package/packages/subagents/src/fleet-view.ts +0 -322
- package/packages/subagents/src/foreground-detach.ts +0 -59
- package/packages/subagents/src/fork-context.ts +0 -285
- package/packages/subagents/src/global.d.ts +0 -7
- package/packages/subagents/src/guide.ts +0 -129
- package/packages/subagents/src/mission-state.ts +0 -133
- package/packages/subagents/src/mission-store.ts +0 -436
- package/packages/subagents/src/output-limits.ts +0 -142
- package/packages/subagents/src/parity-types.ts +0 -315
- package/packages/subagents/src/pi-args.ts +0 -167
- package/packages/subagents/src/result-files.ts +0 -232
- package/packages/subagents/src/result-watcher.ts +0 -221
- package/packages/subagents/src/retained-children.ts +0 -176
- package/packages/subagents/src/run-fanout-budget.ts +0 -356
- package/packages/subagents/src/scheduled-runs.ts +0 -349
- package/packages/subagents/src/schemas.ts +0 -263
- package/packages/subagents/src/slash-commands.ts +0 -80
- package/packages/subagents/src/supervisor-channel.ts +0 -249
- package/packages/subagents/src/tool-handler.ts +0 -1144
- package/packages/subagents/src/types.ts +0 -238
- package/packages/subagents/src/widget.ts +0 -457
- package/packages/subagents/src/workflow-script.ts +0 -737
- package/packages/subagents/src/workflow-worker.ts +0 -384
- package/packages/subagents/src/worktree.ts +0 -614
- package/packages/utility/src/analytics/collector.ts +0 -293
- package/packages/utility/src/info-screen.ts +0 -80
- package/packages/utility/src/lifecycle/process.ts +0 -162
- package/packages/utility/src/prefix-cache.ts +0 -263
- package/packages/utility/src/skill-discovery.ts +0 -542
- package/packages/utility/src/tools/env.ts +0 -113
- package/packages/utility/src/tui/name-badge-state.ts +0 -315
- package/packages/utility/src/tui/name-badge.ts +0 -109
- /package/packages/{subagents/src → core/src/child-agent}/pi-spawn.ts +0 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/utility — image & file attachments
|
|
3
|
+
*
|
|
4
|
+
* After a paste, drag-and-drop or Ctrl+V, any image/document path that lands
|
|
5
|
+
* in the editor is swapped for a token — [Image #1], [File #2] — and listed
|
|
6
|
+
* in a chip row above the editor (with small inline previews in Kitty,
|
|
7
|
+
* Ghostty, iTerm2 and WezTerm). On submit, images are sent as real image
|
|
8
|
+
* content and file tokens expand to their paths. Deleting a token drops the
|
|
9
|
+
* attachment. After the message is sent, a user-only transcript line shows
|
|
10
|
+
* what was attached (the model never sees it).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
14
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import { getCapabilities, Image, Key, matchesKey, truncateToWidth, type Component } from "@earendil-works/pi-tui";
|
|
16
|
+
import { readUtilSettings } from "../settings.js";
|
|
17
|
+
import { expandForSubmit, formatBytes, stillReferenced, tokenFor, tokenize, type Attachment } from "@pi-unipi/core";
|
|
18
|
+
|
|
19
|
+
export const ATTACHMENTS_ENTRY = "unipi:attachments";
|
|
20
|
+
const WIDGET = "unipi-attachments";
|
|
21
|
+
const SCAN_AFTER_PASTE_MS = [80, 400, 1200];
|
|
22
|
+
const PREVIEW_ROWS = 5;
|
|
23
|
+
const PREVIEW_MAX = 3;
|
|
24
|
+
|
|
25
|
+
type Theme = { fg: (color: string, text: string) => string; bold?: (t: string) => string };
|
|
26
|
+
|
|
27
|
+
function isPaste(data: string): boolean {
|
|
28
|
+
return data.includes("\x1b[200~") || matchesKey(data, Key.ctrl("v")) || matchesKey(data, Key.alt("v"));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function canPreview(): boolean {
|
|
32
|
+
try {
|
|
33
|
+
return Boolean(getCapabilities().images);
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function chip(theme: Theme, a: Pick<Attachment, "kind" | "id" | "name" | "bytes">): string {
|
|
40
|
+
return `${theme.fg("accent", tokenFor(a))} ${a.name}${a.bytes ? theme.fg("muted", ` · ${formatBytes(a.bytes)}`) : ""}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Chip line + optional inline previews, shared by the widget and the transcript line. */
|
|
44
|
+
function attachmentView(items: ReadonlyArray<Pick<Attachment, "kind" | "id" | "name" | "bytes" | "path" | "mimeType">>, theme: Theme, preview: boolean, hint: string): Component {
|
|
45
|
+
const images: Image[] = [];
|
|
46
|
+
if (preview && canPreview()) {
|
|
47
|
+
for (const a of items.filter((i) => i.kind === "image").slice(0, PREVIEW_MAX)) {
|
|
48
|
+
try {
|
|
49
|
+
images.push(new Image(readFileSync(a.path).toString("base64"), a.mimeType ?? "image/png", { fallbackColor: (s) => theme.fg("muted", s) }, { maxHeightCells: PREVIEW_ROWS, maxWidthCells: 32, filename: a.name }));
|
|
50
|
+
} catch {
|
|
51
|
+
// file gone — chip only
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
invalidate() {
|
|
57
|
+
for (const i of images) i.invalidate();
|
|
58
|
+
},
|
|
59
|
+
render(width: number): string[] {
|
|
60
|
+
const bar = theme.fg("accent", "▌ ");
|
|
61
|
+
const chips = items.map((a) => chip(theme, a)).join(theme.fg("muted", " · "));
|
|
62
|
+
const lines = [truncateToWidth(`${bar}${chips}${hint ? theme.fg("muted", ` ${hint}`) : ""}`, width, "…")];
|
|
63
|
+
for (const img of images) lines.push(...img.render(Math.min(width, 34)));
|
|
64
|
+
return lines;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function registerAttachments(pi: ExtensionAPI): void {
|
|
70
|
+
let attachments: Attachment[] = [];
|
|
71
|
+
let sent: Attachment[] = [];
|
|
72
|
+
let ui: ExtensionContext["ui"] | null = null;
|
|
73
|
+
let unsub: (() => void) | undefined;
|
|
74
|
+
const timers = new Set<ReturnType<typeof setTimeout>>();
|
|
75
|
+
|
|
76
|
+
const refreshWidget = () => {
|
|
77
|
+
if (!ui) return;
|
|
78
|
+
const settings = readUtilSettings().attachments;
|
|
79
|
+
if (attachments.length === 0) {
|
|
80
|
+
ui.setWidget(WIDGET, undefined);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const items = [...attachments];
|
|
84
|
+
ui.setWidget(WIDGET, (_tui, theme) => attachmentView(items, theme as unknown as Theme, settings.preview, "attached to your next message"), { placement: "aboveEditor" });
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const scan = () => {
|
|
88
|
+
if (!ui) return;
|
|
89
|
+
try {
|
|
90
|
+
const text = ui.getEditorText();
|
|
91
|
+
const { text: next, added } = tokenize(text, attachments);
|
|
92
|
+
if (next !== text) ui.setEditorText(next);
|
|
93
|
+
if (added.length > 0) {
|
|
94
|
+
attachments = [...attachments, ...added];
|
|
95
|
+
refreshWidget();
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
// editor unavailable — leave the text alone
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const sync = () => {
|
|
103
|
+
if (!ui || attachments.length === 0) return;
|
|
104
|
+
try {
|
|
105
|
+
const kept = stillReferenced(ui.getEditorText(), attachments);
|
|
106
|
+
if (kept.length !== attachments.length) {
|
|
107
|
+
attachments = kept;
|
|
108
|
+
refreshWidget();
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
// ignore
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const later = (fn: () => void, ms: number) => {
|
|
116
|
+
const t = setTimeout(() => {
|
|
117
|
+
timers.delete(t);
|
|
118
|
+
fn();
|
|
119
|
+
}, ms);
|
|
120
|
+
t.unref?.();
|
|
121
|
+
timers.add(t);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
pi.registerEntryRenderer<{ items: Attachment[] }>(ATTACHMENTS_ENTRY, (entry, _opts, theme) => {
|
|
126
|
+
const items = entry.data?.items;
|
|
127
|
+
if (!Array.isArray(items) || items.length === 0) return undefined;
|
|
128
|
+
return attachmentView(items, theme as unknown as Theme, readUtilSettings().attachments.preview, "");
|
|
129
|
+
});
|
|
130
|
+
} catch {
|
|
131
|
+
// UI-dependent
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
pi.on("session_start", (_e, ctx) => {
|
|
135
|
+
unsub?.();
|
|
136
|
+
unsub = undefined;
|
|
137
|
+
attachments = [];
|
|
138
|
+
sent = [];
|
|
139
|
+
ui = ctx.hasUI ? ctx.ui : null;
|
|
140
|
+
if (!ui || !readUtilSettings(ctx.cwd).attachments.enabled) return;
|
|
141
|
+
ui.setWidget(WIDGET, undefined);
|
|
142
|
+
unsub = ui.onTerminalInput((data) => {
|
|
143
|
+
if (data === "\x7f" || data === "\b") {
|
|
144
|
+
// Token-aware backspace: if the cursor is at the end of a token
|
|
145
|
+
// (the common case right after a paste), delete the whole [Image #N]
|
|
146
|
+
// / [File #N] atomically instead of just the closing bracket. The
|
|
147
|
+
// editor API exposes no cursor position, so we only fire when the
|
|
148
|
+
// text ends with a token — mid-text edits fall through untouched.
|
|
149
|
+
const ed = ui;
|
|
150
|
+
if (ed) {
|
|
151
|
+
try {
|
|
152
|
+
const text = ed.getEditorText();
|
|
153
|
+
const m = /\[(?:Image|File) #\d+\] ?$/.exec(text);
|
|
154
|
+
if (m) {
|
|
155
|
+
ed.setEditorText(text.slice(0, text.length - m[0].length));
|
|
156
|
+
later(sync, 0);
|
|
157
|
+
return { consume: true };
|
|
158
|
+
}
|
|
159
|
+
} catch {
|
|
160
|
+
// editor unavailable — fall through
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (isPaste(data)) for (const ms of SCAN_AFTER_PASTE_MS) later(scan, ms);
|
|
165
|
+
else if (attachments.length > 0) later(sync, 60);
|
|
166
|
+
return undefined;
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
pi.on("input", (event) => {
|
|
171
|
+
try {
|
|
172
|
+
if (event.source === "extension" || attachments.length === 0) return undefined;
|
|
173
|
+
const text = event.text ?? "";
|
|
174
|
+
const used = stillReferenced(text, attachments);
|
|
175
|
+
if (used.length === 0 || text.trimStart().startsWith("/")) return undefined;
|
|
176
|
+
const out = expandForSubmit(text, used);
|
|
177
|
+
sent = used;
|
|
178
|
+
attachments = [];
|
|
179
|
+
refreshWidget();
|
|
180
|
+
return { action: "transform" as const, text: out.text, images: [...(event.images ?? []), ...out.images] };
|
|
181
|
+
} catch {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// The user message is in the session by now: show what went with it.
|
|
187
|
+
pi.on("agent_start", () => {
|
|
188
|
+
if (sent.length === 0) return;
|
|
189
|
+
const items = sent.map(({ id, kind, name, path, bytes, mimeType }) => ({ id, kind, name, path, bytes, mimeType }));
|
|
190
|
+
sent = [];
|
|
191
|
+
try {
|
|
192
|
+
pi.appendEntry(ATTACHMENTS_ENTRY, { items });
|
|
193
|
+
} catch {
|
|
194
|
+
// ignore
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
pi.on("session_shutdown", () => {
|
|
199
|
+
unsub?.();
|
|
200
|
+
unsub = undefined;
|
|
201
|
+
for (const t of timers) clearTimeout(t);
|
|
202
|
+
timers.clear();
|
|
203
|
+
ui = null;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
@@ -1,27 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @pi-unipi/utility —
|
|
2
|
+
* @pi-unipi/utility — Commands
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* - /unipi:status — show module status
|
|
8
|
-
* - /unipi:cleanup — clean stale files
|
|
9
|
-
* - /unipi:env — show environment
|
|
10
|
-
* - /unipi:doctor — run diagnostics
|
|
4
|
+
* /unipi:continue (/unipi:retry) — take another turn without adding user text
|
|
5
|
+
* /unipi:cleanup [--dry-run|--yes] — allowlisted stale files, preview + confirm
|
|
6
|
+
* /unipi:doctor — runtime diagnostics
|
|
11
7
|
*/
|
|
12
8
|
|
|
13
9
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
UTILITY_COMMANDS,
|
|
17
|
-
UNIPI_EVENTS,
|
|
18
|
-
emitEvent,
|
|
19
|
-
} from "@pi-unipi/core";
|
|
20
|
-
import { cleanupStale, formatCleanupReport } from "./lifecycle/cleanup.js";
|
|
10
|
+
import { harnessMetadata, UNIPI_PREFIX, UTILITY_COMMANDS } from "@pi-unipi/core";
|
|
11
|
+
import { findCleanupItems, formatBytes, formatCleanupPreview, removeCleanupItems } from "./lifecycle/cleanup.js";
|
|
21
12
|
import { runDiagnostics, formatDiagnosticsReport } from "./diagnostics/engine.js";
|
|
22
|
-
import { getEnvironmentInfo, formatEnvironmentInfo } from "./tools/env.js";
|
|
23
|
-
import type { NameBadgeState } from "./tui/name-badge-state.js";
|
|
24
|
-
import { isSkillDiscoveryEnabled } from "./skill-discovery.js";
|
|
25
13
|
|
|
26
14
|
/** Send a markdown response via pi.sendMessage */
|
|
27
15
|
function sendResponse(pi: ExtensionAPI, markdown: string): void {
|
|
@@ -30,151 +18,59 @@ function sendResponse(pi: ExtensionAPI, markdown: string): void {
|
|
|
30
18
|
customType: "unipi-response",
|
|
31
19
|
content: markdown,
|
|
32
20
|
display: true,
|
|
21
|
+
details: { unipiHarness: harnessMetadata({ source: "Utility", title: "Response", synopsis: "Command response" }, "followUp") },
|
|
33
22
|
},
|
|
34
23
|
{ deliverAs: "followUp" },
|
|
35
24
|
);
|
|
36
25
|
}
|
|
37
26
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
27
|
+
function busy(ctx: ExtensionContext): boolean {
|
|
28
|
+
if (ctx.isIdle()) return false;
|
|
29
|
+
if (ctx.hasUI) ctx.ui.notify("Agent is busy. Press ESC to interrupt, then try again.", "warning");
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
41
33
|
export function registerUtilityCommands(pi: ExtensionAPI): void {
|
|
42
|
-
|
|
34
|
+
const continueHandler = async (_args: string, ctx: ExtensionContext) => {
|
|
35
|
+
if (busy(ctx)) return;
|
|
36
|
+
pi.sendMessage({ customType: "unipi-continue", content: "", display: false }, { triggerTurn: true });
|
|
37
|
+
};
|
|
43
38
|
pi.registerCommand(`${UNIPI_PREFIX}${UTILITY_COMMANDS.CONTINUE}`, {
|
|
44
|
-
description: "Continue the agent from where it left off without adding
|
|
45
|
-
handler:
|
|
46
|
-
if (!ctx.isIdle()) {
|
|
47
|
-
if (ctx.hasUI) {
|
|
48
|
-
ctx.ui.notify(
|
|
49
|
-
"Agent is busy. Press ESC to interrupt, then try again.",
|
|
50
|
-
"warning",
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
pi.sendMessage(
|
|
57
|
-
{
|
|
58
|
-
customType: "unipi-continue",
|
|
59
|
-
content: "",
|
|
60
|
-
display: false,
|
|
61
|
-
},
|
|
62
|
-
{ triggerTurn: true },
|
|
63
|
-
);
|
|
64
|
-
},
|
|
39
|
+
description: "Continue the agent from where it left off, without adding text (/unipi:retry)",
|
|
40
|
+
handler: continueHandler,
|
|
65
41
|
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
description: "Reload all Pi extensions without restarting",
|
|
70
|
-
handler: async (_args: string, ctx: ExtensionContext) => {
|
|
71
|
-
if (!ctx.isIdle()) {
|
|
72
|
-
if (ctx.hasUI) {
|
|
73
|
-
ctx.ui.notify("Agent is busy. Press ESC to interrupt first.", "warning");
|
|
74
|
-
}
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
sendResponse(
|
|
79
|
-
pi,
|
|
80
|
-
"## 🔄 Reload Extensions\n\n" +
|
|
81
|
-
"To reload all extensions:\n" +
|
|
82
|
-
"1. Press **Ctrl+C** to exit Pi\n" +
|
|
83
|
-
"2. Run `pi` again to restart with fresh extensions\n\n" +
|
|
84
|
-
"*Note: Pi does not support hot-reloading of extensions.*",
|
|
85
|
-
);
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// ─── /unipi:status ───────────────────────────────────────────────────────
|
|
90
|
-
pi.registerCommand(`${UNIPI_PREFIX}${UTILITY_COMMANDS.STATUS}`, {
|
|
91
|
-
description: "Show all unipi modules status",
|
|
92
|
-
handler: async (_args: string, ctx: ExtensionContext) => {
|
|
93
|
-
if (!ctx.isIdle()) {
|
|
94
|
-
if (ctx.hasUI) {
|
|
95
|
-
ctx.ui.notify("Agent is busy. Press ESC to interrupt first.", "warning");
|
|
96
|
-
}
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
sendResponse(
|
|
101
|
-
pi,
|
|
102
|
-
"## 📊 Module Status\n\n" +
|
|
103
|
-
"Use `/unipi:info` for the live module and tool dashboard.\n" +
|
|
104
|
-
"Use `/unipi:doctor` to run configuration and runtime diagnostics.",
|
|
105
|
-
);
|
|
106
|
-
},
|
|
42
|
+
pi.registerCommand(`${UNIPI_PREFIX}${UTILITY_COMMANDS.RETRY}`, {
|
|
43
|
+
description: "Retry the last turn — alias of /unipi:continue",
|
|
44
|
+
handler: continueHandler,
|
|
107
45
|
});
|
|
108
46
|
|
|
109
|
-
// ─── /unipi:cleanup ──────────────────────────────────────────────────────
|
|
110
47
|
pi.registerCommand(`${UNIPI_PREFIX}${UTILITY_COMMANDS.CLEANUP}`, {
|
|
111
|
-
description: "
|
|
48
|
+
description: "Remove stale UniPi temp files and leftovers (preview first)",
|
|
112
49
|
handler: async (args: string, ctx: ExtensionContext) => {
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
50
|
+
if (busy(ctx)) return;
|
|
51
|
+
const items = findCleanupItems();
|
|
52
|
+
const preview = formatCleanupPreview(items);
|
|
53
|
+
if (items.length === 0 || args.includes("--dry-run")) {
|
|
54
|
+
sendResponse(pi, `## Cleanup${args.includes("--dry-run") ? " (dry run)" : ""}\n\n\`\`\`\n${preview}\n\`\`\``);
|
|
117
55
|
return;
|
|
118
56
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
emitEvent(pi, UNIPI_EVENTS.UTILITY_CLEANUP_DONE, {
|
|
124
|
-
dryRun,
|
|
125
|
-
categories: ["db", "temp", "session", "cache"],
|
|
126
|
-
results: report.results.map((r) => ({
|
|
127
|
-
category: r.category,
|
|
128
|
-
removed: r.removed,
|
|
129
|
-
bytesFreed: r.bytesFreed,
|
|
130
|
-
})),
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
sendResponse(pi, formatCleanupReport(report));
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
// ─── /unipi:env ──────────────────────────────────────────────────────────
|
|
138
|
-
pi.registerCommand(`${UNIPI_PREFIX}${UTILITY_COMMANDS.ENV}`, {
|
|
139
|
-
description: "Show environment info (versions, paths)",
|
|
140
|
-
handler: async (_args: string, ctx: ExtensionContext) => {
|
|
141
|
-
if (!ctx.isIdle()) {
|
|
142
|
-
if (ctx.hasUI) {
|
|
143
|
-
ctx.ui.notify("Agent is busy. Press ESC to interrupt first.", "warning");
|
|
144
|
-
}
|
|
57
|
+
const confirmed = args.includes("--yes")
|
|
58
|
+
|| (ctx.hasUI && await ctx.ui.confirm("Remove these files?", preview));
|
|
59
|
+
if (!confirmed) {
|
|
60
|
+
sendResponse(pi, `## Cleanup cancelled\n\n\`\`\`\n${preview}\n\`\`\`${ctx.hasUI ? "" : "\n\nRun `/unipi:cleanup --yes` to remove them."}`);
|
|
145
61
|
return;
|
|
146
62
|
}
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
sendResponse(pi,
|
|
63
|
+
const result = removeCleanupItems(items);
|
|
64
|
+
const failed = result.failed.length ? `\n\nCould not remove:\n${result.failed.map((p) => `- \`${p}\``).join("\n")}` : "";
|
|
65
|
+
sendResponse(pi, `## Cleanup\n\nRemoved ${result.removed} item(s), ${formatBytes(result.bytes)}.${failed}`);
|
|
150
66
|
},
|
|
151
67
|
});
|
|
152
68
|
|
|
153
|
-
// ─── /unipi:doctor ───────────────────────────────────────────────────────
|
|
154
69
|
pi.registerCommand(`${UNIPI_PREFIX}${UTILITY_COMMANDS.DOCTOR}`, {
|
|
155
|
-
description: "
|
|
70
|
+
description: "Check UniPi's runtime: folders, config, model cache, Decision Model, skills",
|
|
156
71
|
handler: async (_args: string, ctx: ExtensionContext) => {
|
|
157
|
-
if (
|
|
158
|
-
|
|
159
|
-
ctx.ui.notify("Agent is busy. Press ESC to interrupt first.", "warning");
|
|
160
|
-
}
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
emitEvent(pi, UNIPI_EVENTS.UTILITY_DIAGNOSTICS_START, {
|
|
165
|
-
overall: "unknown",
|
|
166
|
-
checkCount: 0,
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
const report = await runDiagnostics();
|
|
170
|
-
|
|
171
|
-
emitEvent(pi, UNIPI_EVENTS.UTILITY_DIAGNOSTICS_DONE, {
|
|
172
|
-
overall: report.overall,
|
|
173
|
-
checkCount: report.checks.length,
|
|
174
|
-
report,
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
sendResponse(pi, formatDiagnosticsReport(report));
|
|
72
|
+
if (busy(ctx)) return;
|
|
73
|
+
sendResponse(pi, formatDiagnosticsReport(await runDiagnostics()));
|
|
178
74
|
},
|
|
179
75
|
});
|
|
180
76
|
}
|
|
@@ -51,9 +51,7 @@ const coreDirectoriesPlugin: DiagnosticPlugin = {
|
|
|
51
51
|
async run(): Promise<DiagnosticCheck[]> {
|
|
52
52
|
const dirs = [
|
|
53
53
|
{ path: "~/.unipi", required: true },
|
|
54
|
-
{ path: "~/.unipi/
|
|
55
|
-
{ path: "~/.unipi/cache", required: false },
|
|
56
|
-
{ path: "~/.unipi/analytics", required: false },
|
|
54
|
+
{ path: "~/.unipi/config", required: false },
|
|
57
55
|
];
|
|
58
56
|
|
|
59
57
|
const checks: DiagnosticCheck[] = [];
|
|
@@ -196,6 +194,52 @@ const nodeEnvironmentPlugin: DiagnosticPlugin = {
|
|
|
196
194
|
},
|
|
197
195
|
};
|
|
198
196
|
|
|
197
|
+
/** Model cache, Decision Model key and skill exposure — what other modules depend on. */
|
|
198
|
+
const unipiRuntimePlugin: DiagnosticPlugin = {
|
|
199
|
+
name: "unipi_runtime",
|
|
200
|
+
module: "@pi-unipi/utility",
|
|
201
|
+
async run(): Promise<DiagnosticCheck[]> {
|
|
202
|
+
const { readModelCache, resolveDecisionModel, jevApiKey, getSettings } = await import("@pi-unipi/core");
|
|
203
|
+
const checks: DiagnosticCheck[] = [];
|
|
204
|
+
const models = readModelCache();
|
|
205
|
+
const images = models.filter((m) => m.output?.includes("image")).length;
|
|
206
|
+
checks.push({
|
|
207
|
+
name: "model_cache",
|
|
208
|
+
module: "@pi-unipi/core",
|
|
209
|
+
status: models.length > 0 ? "healthy" : "warning",
|
|
210
|
+
message: models.length > 0
|
|
211
|
+
? `Model cache: ${models.length} models (${images} image generators)`
|
|
212
|
+
: "Model cache is empty — pickers fall back to ~/.pi/agent/models.json",
|
|
213
|
+
suggestion: models.length > 0 ? undefined : "Start a pi session once; utility refreshes the cache on session start",
|
|
214
|
+
durationMs: 0,
|
|
215
|
+
});
|
|
216
|
+
const dm = resolveDecisionModel(process.cwd(), "utility");
|
|
217
|
+
const hasKey = Boolean(jevApiKey(dm, process.env));
|
|
218
|
+
checks.push({
|
|
219
|
+
name: "decision_model",
|
|
220
|
+
module: "@pi-unipi/core",
|
|
221
|
+
status: hasKey ? "healthy" : "warning",
|
|
222
|
+
message: hasKey
|
|
223
|
+
? `Decision Model: ${dm.provider}/${dm.model}`
|
|
224
|
+
: `Decision Model ${dm.provider}/${dm.model} has no API key — auto-rename and skill judging fall back`,
|
|
225
|
+
suggestion: hasKey ? undefined : "Set a key in /unipi:settings → Decision Model, or export OPENROUTER_API_KEY",
|
|
226
|
+
durationMs: 0,
|
|
227
|
+
});
|
|
228
|
+
const mode = (getSettings("skills", process.cwd()) as { exposure?: { mode?: string } }).exposure?.mode ?? "judged";
|
|
229
|
+
checks.push({
|
|
230
|
+
name: "skill_exposure",
|
|
231
|
+
module: "@pi-unipi/skill-registry",
|
|
232
|
+
status: mode === "off" ? "warning" : "healthy",
|
|
233
|
+
message: mode === "off"
|
|
234
|
+
? "Skill exposure is off: UniPi's bundled skills are hidden from the model in this project"
|
|
235
|
+
: `Skill exposure: ${mode}`,
|
|
236
|
+
suggestion: mode === "off" ? "Switch Skills → Exposure → Skill exposure to judged or all in /unipi:settings" : undefined,
|
|
237
|
+
durationMs: 0,
|
|
238
|
+
});
|
|
239
|
+
return checks;
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
|
|
199
243
|
// ─── Diagnostics Engine ──────────────────────────────────────────────────────
|
|
200
244
|
|
|
201
245
|
/** Registry of diagnostic plugins */
|
|
@@ -203,6 +247,7 @@ const plugins: DiagnosticPlugin[] = [
|
|
|
203
247
|
coreDirectoriesPlugin,
|
|
204
248
|
configFilesPlugin,
|
|
205
249
|
nodeEnvironmentPlugin,
|
|
250
|
+
unipiRuntimePlugin,
|
|
206
251
|
];
|
|
207
252
|
|
|
208
253
|
/** Register a custom diagnostic plugin */
|
|
@@ -259,7 +304,7 @@ export async function runDiagnostics(): Promise<DiagnosticsReport> {
|
|
|
259
304
|
/** Format a diagnostics report as markdown */
|
|
260
305
|
export function formatDiagnosticsReport(report: DiagnosticsReport): string {
|
|
261
306
|
const lines = [
|
|
262
|
-
"##
|
|
307
|
+
"## Diagnostics",
|
|
263
308
|
"",
|
|
264
309
|
`**Overall:** ${report.overall.toUpperCase()}`,
|
|
265
310
|
`**Checks:** ${report.summary.healthy} healthy, ${report.summary.warning} warning, ${report.summary.error} error, ${report.summary.unknown} unknown`,
|
|
@@ -282,7 +327,7 @@ export function formatDiagnosticsReport(report: DiagnosticsReport): string {
|
|
|
282
327
|
lines.push(`- **${check.name}** (${check.module})`);
|
|
283
328
|
lines.push(` ${check.message}`);
|
|
284
329
|
if (check.suggestion) {
|
|
285
|
-
lines.push(`
|
|
330
|
+
lines.push(` → ${check.suggestion}`);
|
|
286
331
|
}
|
|
287
332
|
lines.push("");
|
|
288
333
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/utility — image: tools + recognize gating
|
|
3
|
+
*
|
|
4
|
+
* image_recognize is hidden while the session model can see images itself
|
|
5
|
+
* (pi's read tool already hands it the pixels) and comes back for text-only
|
|
6
|
+
* models.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import { IMAGE_TOOLS } from "@pi-unipi/core";
|
|
11
|
+
import { applyRecognizeGating } from "./models.js";
|
|
12
|
+
import { loadConfig } from "./settings.js";
|
|
13
|
+
import { registerImageTools } from "./tools.js";
|
|
14
|
+
|
|
15
|
+
export { imageCatalogEntries, refreshImageModelCache } from "./models.js";
|
|
16
|
+
export { loadConfig as loadImageConfig } from "./settings.js";
|
|
17
|
+
|
|
18
|
+
export function registerImage(pi: ExtensionAPI): void {
|
|
19
|
+
registerImageTools(pi);
|
|
20
|
+
|
|
21
|
+
const gate = (model: unknown) => {
|
|
22
|
+
try {
|
|
23
|
+
if (!loadConfig().recognize.enabled) return;
|
|
24
|
+
const active = pi.getActiveTools();
|
|
25
|
+
const next = applyRecognizeGating(active, model, IMAGE_TOOLS.RECOGNIZE);
|
|
26
|
+
if (next !== active) pi.setActiveTools(next);
|
|
27
|
+
} catch {
|
|
28
|
+
// tools not ready — next event retries
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
pi.on("session_start", (_e, ctx) => gate(ctx.model));
|
|
32
|
+
pi.on("model_select", (event) => gate(event.model));
|
|
33
|
+
}
|