@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
|
@@ -1,25 +1,62 @@
|
|
|
1
1
|
# @pi-unipi/skill-registry
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
bundled skill set keeps loading through the `@pi-unipi/unipi` umbrella manifest.
|
|
3
|
+
Decides which skills the agent sees: per-project on/off, a vault of extra skills, and jev-judged exposure.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
package was repurposed for **plan mode** and **permission modes**. They are plain
|
|
8
|
-
`SKILL.md` instruction files; the slash commands that used to dispatch them
|
|
9
|
-
(`/unipi:brainstorm`, `/unipi:work`, `/unipi:review-work`, `/unipi:auto`, …) were
|
|
10
|
-
removed — plan mode (`/unipi:plan`, `Alt+P`) and ordinary prompting replace them. Every skill is still
|
|
11
|
-
loadable by name via `/skill:<name>` or by reading its `SKILL.md`, and jev skill
|
|
12
|
-
judging still sees them.
|
|
5
|
+
## Commands
|
|
13
6
|
|
|
14
|
-
|
|
7
|
+
| Command | Description |
|
|
8
|
+
|---------|-------------|
|
|
9
|
+
| `/unipi:skills` | Opens `/unipi:settings` on Skills (proxy, exposure, and the **Skill settings…** row) |
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
skill packs, and the metadata (capability tags, model hints) that jev skill judging and
|
|
18
|
-
the `/unipi:settings` hub will consume. Until then it is a mount point, deliberately
|
|
19
|
-
code-free.
|
|
11
|
+
## Skill settings
|
|
20
12
|
|
|
21
|
-
|
|
13
|
+
`/unipi:settings` → Skills → **Skill settings…** opens a grid of every skill pi loaded, like Fusion's preset editor:
|
|
22
14
|
|
|
23
15
|
```
|
|
24
|
-
skills
|
|
16
|
+
Skill settings · 42 skills · editing global · proxy on
|
|
17
|
+
E M skill source description
|
|
18
|
+
› [x] [ ] api-contract user Check REST/JSON API changes ...
|
|
19
|
+
[ ] [ ] aws-deploy vault Deploy services to AWS with ...
|
|
20
|
+
|
|
21
|
+
E enabled — the model sees it (judged first when exposure is on); off removes it and blocks /skill:name
|
|
22
|
+
M must show — always listed, even when judging would hide it
|
|
23
|
+
bright = set in global · dim = inherited
|
|
25
24
|
```
|
|
25
|
+
|
|
26
|
+
| Key | Action |
|
|
27
|
+
|-----|--------|
|
|
28
|
+
| `↑` `↓` | Move between skills (PgUp/PgDn page) |
|
|
29
|
+
| `←` `→` | Move between the E / M columns |
|
|
30
|
+
| `space` | Toggle the cell |
|
|
31
|
+
| `d` | Clear the cell in the edited scope (inherit again) |
|
|
32
|
+
| `g` | Edit the global or the project scope |
|
|
33
|
+
| `p` | Skill proxy on/off |
|
|
34
|
+
| type | Filter by name, source or description (Esc clears) |
|
|
35
|
+
| `enter` / `esc` | Save / cancel |
|
|
36
|
+
|
|
37
|
+
Cells show the effective value: project over global over the default (vault skills off, everything else on). Only the cells you changed are written, each option on its own, so a project can override just one.
|
|
38
|
+
|
|
39
|
+
## Skill proxy
|
|
40
|
+
|
|
41
|
+
Off by default. While off, pi's skills pass through untouched (exposure judging still applies), vault skills stay hidden, and your choices are saved but not applied. Turn it on and:
|
|
42
|
+
|
|
43
|
+
- Skills turned **off** are removed from the session, including `/skill:name`.
|
|
44
|
+
- The **vault**, `~/.unipi/skill-vault/`, takes part. Keep as many skills there as you like; they stay off until a scope turns them on, so a project only gets the ones it needs. Changes apply on the next prompt, no reload needed. Any folder with a `SKILL.md`, up to three levels deep (`vault/<skill>/` or `vault/<pack>/<skill>/`).
|
|
45
|
+
|
|
46
|
+
## Exposure
|
|
47
|
+
|
|
48
|
+
Settings → Skills → Exposure:
|
|
49
|
+
|
|
50
|
+
- **judged** (default) — when more skills are available than *Max skills listed* (12), the first real request of the session picks the listed set, and it stays fixed for the session so the system prompt never changes (prefix cache intact). Greetings don't count.
|
|
51
|
+
1. **Named skills are always kept.** A skill whose name appears in the request (`agent-browser`, "grill me") or a word only its name contains ("ssh into **coffee**" → `coffee-sandbox`) is listed without asking a model.
|
|
52
|
+
2. **jev scores the rest** against the request; those above the threshold are listed best first, up to *Max skills listed* in total.
|
|
53
|
+
3. **Hidden skills are still named.** The system prompt gets a short section listing every hidden skill by folder, so the agent knows it exists and can read its `SKILL.md`.
|
|
54
|
+
4. **Later requests** that name a hidden skill (or, with *recheck*, that jev finds it relevant to) get a message pointing at it. The system prompt itself never changes.
|
|
55
|
+
- **all** — every skill is listed.
|
|
56
|
+
- **off** — UniPi's bundled skills are removed from the list.
|
|
57
|
+
|
|
58
|
+
jev uses the shared Decision Model (Settings → Skills → Decision model: inherit or custom). If jev is unavailable, every skill is listed.
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/skill-registry — Extension entry
|
|
3
|
+
*
|
|
4
|
+
* Per turn (before_agent_start), on pi's discovered skills:
|
|
5
|
+
* 1. registry (proxy on): drop skills turned off — removed from the session,
|
|
6
|
+
* /skill:name included; the vault is mounted via resources_discover and
|
|
7
|
+
* stays off until a scope turns a skill on.
|
|
8
|
+
* 2. exposure: off (bundled stripped) | all | judged (see src/judge.ts).
|
|
9
|
+
* Plus /unipi:skills (the hub opened on Skills), the "Skill settings…" overlay
|
|
10
|
+
* (per-skill Enabled / Must show, src/editor.ts), and the
|
|
11
|
+
* reveal event kanboard uses to surface a skill mid-session.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
16
|
+
import { emitEvent, getPackageVersion, harnessMetadata, HUB_OVERLAY_OPTIONS, MODULES, openSettingsHub, registerCommandRunner, setSettings, UNIPI_EVENTS } from "@pi-unipi/core";
|
|
17
|
+
import { dirname } from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import { applyRegistry, isBundledSkillLocation, isUnderDir, skillCommandName, skillDir, skillSource, type CatalogSkill } from "./src/registry.js";
|
|
20
|
+
import { readSkillsSettings, readStateLayers, writeStateLayers } from "./src/settings.js";
|
|
21
|
+
import { SkillEditor, type EditorResult } from "./src/editor.js";
|
|
22
|
+
import { listVaultSkills, vaultDir } from "./src/vault.js";
|
|
23
|
+
import {
|
|
24
|
+
decideTurn,
|
|
25
|
+
emptyState,
|
|
26
|
+
HIDDEN_SECTION,
|
|
27
|
+
restoreState,
|
|
28
|
+
revealMessage,
|
|
29
|
+
SKILLS_JUDGED_ENTRY,
|
|
30
|
+
SKILLS_REVEALED_ENTRY,
|
|
31
|
+
toEntry,
|
|
32
|
+
type SessionSkillsState,
|
|
33
|
+
} from "./src/judge.js";
|
|
34
|
+
|
|
35
|
+
export { SKILLS_JUDGED_ENTRY, SKILLS_REVEALED_ENTRY } from "./src/judge.js";
|
|
36
|
+
|
|
37
|
+
const VERSION = getPackageVersion(dirname(fileURLToPath(import.meta.url)));
|
|
38
|
+
|
|
39
|
+
/** Event another module emits to reveal a skill by name (append-only). */
|
|
40
|
+
export const SKILL_REVEAL_EVENT = "unipi:skills:reveal";
|
|
41
|
+
|
|
42
|
+
export default function skillRegistry(pi: ExtensionAPI) {
|
|
43
|
+
let state: SessionSkillsState | null = null;
|
|
44
|
+
let lastCatalog: CatalogSkill[] = [];
|
|
45
|
+
let disabled = new Set<string>();
|
|
46
|
+
|
|
47
|
+
const sessionState = (ctx: ExtensionContext): SessionSkillsState => {
|
|
48
|
+
const id = ctx.sessionManager.getSessionId();
|
|
49
|
+
if (!state || state.sessionId !== id) state = emptyState(id);
|
|
50
|
+
return state;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** Everything the manager can show: last catalog ∪ pi's skill commands ∪ vault. */
|
|
54
|
+
const allSkills = (): CatalogSkill[] => {
|
|
55
|
+
const byName = new Map<string, CatalogSkill>();
|
|
56
|
+
for (const s of lastCatalog) byName.set(s.name, s);
|
|
57
|
+
try {
|
|
58
|
+
for (const c of pi.getCommands()) {
|
|
59
|
+
if (c.source !== "skill") continue;
|
|
60
|
+
const name = c.name.replace(/^skill:/, "");
|
|
61
|
+
if (!byName.has(name)) byName.set(name, { name, description: c.description ?? "", filePath: c.sourceInfo?.path });
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// getCommands unavailable (tests) — catalog + vault only.
|
|
65
|
+
}
|
|
66
|
+
for (const v of listVaultSkills()) if (!byName.has(v.name)) byName.set(v.name, v);
|
|
67
|
+
return [...byName.values()];
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const invalidateJudgement = () => {
|
|
71
|
+
if (state) state.judged = false;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/** "Skill settings…" — the per-skill E/M overlay. */
|
|
75
|
+
const openEditor = async (ctx: ExtensionContext) => {
|
|
76
|
+
if (!ctx.hasUI) return;
|
|
77
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
78
|
+
const vault = vaultDir();
|
|
79
|
+
const skills = allSkills()
|
|
80
|
+
.map((sk) => ({ name: sk.name, description: sk.description, source: skillSource(sk, cwd, vault) }))
|
|
81
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
82
|
+
const before = readStateLayers(cwd);
|
|
83
|
+
const settings = readSkillsSettings(cwd);
|
|
84
|
+
const hasProject = Object.keys(before.project).length > 0;
|
|
85
|
+
const result = await ctx.ui.custom<EditorResult>(
|
|
86
|
+
(tui, theme, _kb, done) =>
|
|
87
|
+
new SkillEditor({
|
|
88
|
+
skills,
|
|
89
|
+
layers: before,
|
|
90
|
+
proxy: settings.proxy,
|
|
91
|
+
initialScope: hasProject ? "project" : "global",
|
|
92
|
+
theme: { fg: (c, text) => theme.fg(c as never, text), bold: (text) => theme.bold(text) },
|
|
93
|
+
onDone: done,
|
|
94
|
+
onRenderRequest: () => tui.requestRender(),
|
|
95
|
+
}),
|
|
96
|
+
HUB_OVERLAY_OPTIONS,
|
|
97
|
+
);
|
|
98
|
+
if (result.type !== "saved") return;
|
|
99
|
+
const writes = writeStateLayers(cwd, before, result.layers);
|
|
100
|
+
if (result.proxy !== settings.proxy) setSettings("skills", { proxy: result.proxy }, "global", cwd);
|
|
101
|
+
if (writes > 0 || result.proxy !== settings.proxy) {
|
|
102
|
+
invalidateJudgement();
|
|
103
|
+
ctx.ui.notify(`Skill settings saved (${writes} change${writes === 1 ? "" : "s"}${result.proxy !== settings.proxy ? `, proxy ${result.proxy ? "on" : "off"}` : ""}) — applies from the next prompt`, "info");
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
registerCommandRunner("unipi:skills-editor", async (raw: unknown) => openEditor(raw as ExtensionContext));
|
|
107
|
+
|
|
108
|
+
pi.registerCommand("unipi:skills", {
|
|
109
|
+
description: "Skill settings — proxy, exposure, and per-skill Enabled / Must show (opens /unipi:settings on Skills)",
|
|
110
|
+
handler: async (_args, ctx) => {
|
|
111
|
+
await openSettingsHub(ctx, {
|
|
112
|
+
filter: "skills",
|
|
113
|
+
onChanged: (ns) => {
|
|
114
|
+
if (ns === "skills") invalidateJudgement();
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// The vault is always mounted so toggles apply without /reload; while the
|
|
121
|
+
// proxy is off, vault skills are filtered out below (and /skill:name blocked).
|
|
122
|
+
pi.on("resources_discover", () => {
|
|
123
|
+
try {
|
|
124
|
+
const vault = vaultDir();
|
|
125
|
+
return existsSync(vault) ? { skillPaths: [vault] } : undefined;
|
|
126
|
+
} catch {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
pi.on("session_start", (_event, ctx) => {
|
|
132
|
+
try {
|
|
133
|
+
state = restoreState(ctx.sessionManager.getSessionId(), ctx.sessionManager.getEntries());
|
|
134
|
+
emitEvent(pi, UNIPI_EVENTS.MODULE_READY, { name: MODULES.SKILL_REGISTRY, version: VERSION, commands: ["unipi:skills"], tools: [] });
|
|
135
|
+
} catch {
|
|
136
|
+
// never block startup
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// A skill turned off is gone for the session — /skill:name included.
|
|
141
|
+
pi.on("input", (event, ctx) => {
|
|
142
|
+
try {
|
|
143
|
+
const name = skillCommandName(event.text ?? "");
|
|
144
|
+
if (!name || !disabled.has(name)) return undefined;
|
|
145
|
+
ctx.ui?.notify?.(`Skill "${name}" is turned off here. Turn it on in /unipi:skills.`, "warning");
|
|
146
|
+
return { action: "handled" as const };
|
|
147
|
+
} catch {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
pi.events?.on?.(SKILL_REVEAL_EVENT, (payload: unknown) => {
|
|
153
|
+
try {
|
|
154
|
+
const data = payload as { names?: unknown } | undefined;
|
|
155
|
+
if (!Array.isArray(data?.names) || !state) return;
|
|
156
|
+
const names = data.names.map(String).filter((n) => !state!.revealed.has(n));
|
|
157
|
+
const entries = state.hidden.filter((h) => names.includes(h.name));
|
|
158
|
+
if (entries.length === 0) return;
|
|
159
|
+
for (const e of entries) state.revealed.add(e.name);
|
|
160
|
+
pi.appendEntry(SKILLS_REVEALED_ENTRY, { names: entries.map((e) => e.name) });
|
|
161
|
+
pi.sendMessage({ customType: "unipi-skills-revealed", content: revealMessage(entries), display: true, details: { unipiHarness: harnessMetadata({ source: "Skills", title: "Skill reveal", synopsis: `${String(entries.length)} relevant skill(s)` }, "direct") } }, { triggerTurn: false });
|
|
162
|
+
} catch {
|
|
163
|
+
// ignore
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
pi.on("before_agent_start", async (event, ctx) => {
|
|
168
|
+
try {
|
|
169
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
170
|
+
const options = event.systemPromptOptions;
|
|
171
|
+
const catalog = (options.skills ?? []) as unknown as CatalogSkill[];
|
|
172
|
+
lastCatalog = catalog;
|
|
173
|
+
const settings = readSkillsSettings(cwd);
|
|
174
|
+
|
|
175
|
+
const vault = vaultDir();
|
|
176
|
+
let pool = catalog;
|
|
177
|
+
let mustShow = new Set<string>();
|
|
178
|
+
disabled = new Set();
|
|
179
|
+
if (settings.proxy) {
|
|
180
|
+
const result = applyRegistry(catalog, settings.states, cwd, vault);
|
|
181
|
+
pool = result.listed;
|
|
182
|
+
disabled = result.disabled;
|
|
183
|
+
mustShow = result.mustShow;
|
|
184
|
+
} else {
|
|
185
|
+
pool = catalog.filter((s) => {
|
|
186
|
+
const inVault = isUnderDir(skillDir(s), vault);
|
|
187
|
+
if (inVault) disabled.add(s.name);
|
|
188
|
+
return !inVault;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const setSkills = (list: CatalogSkill[]) => {
|
|
193
|
+
options.skills = list as unknown as typeof options.skills;
|
|
194
|
+
};
|
|
195
|
+
if (settings.exposure.mode !== "judged") {
|
|
196
|
+
setSkills(settings.exposure.mode === "off" ? pool.filter((s) => mustShow.has(s.name) || !isBundledSkillLocation(skillDir(s))) : pool);
|
|
197
|
+
delete options.sections[HIDDEN_SECTION];
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const out = await decideTurn({
|
|
202
|
+
prompt: event.prompt,
|
|
203
|
+
catalog: pool.map(toEntry),
|
|
204
|
+
settings: settings.exposure,
|
|
205
|
+
cwd,
|
|
206
|
+
state: sessionState(ctx),
|
|
207
|
+
mustShow,
|
|
208
|
+
});
|
|
209
|
+
setSkills(pool.filter((s) => out.listed.has(s.name)));
|
|
210
|
+
if (out.index) options.sections[HIDDEN_SECTION] = out.index;
|
|
211
|
+
else delete options.sections[HIDDEN_SECTION];
|
|
212
|
+
if (out.freeze) pi.appendEntry(SKILLS_JUDGED_ENTRY, out.freeze);
|
|
213
|
+
if (out.status && ctx.hasUI) ctx.ui.setStatus("skills", out.status);
|
|
214
|
+
if (out.reveal.length > 0) {
|
|
215
|
+
pi.appendEntry(SKILLS_REVEALED_ENTRY, { names: out.reveal.map((r) => r.name) });
|
|
216
|
+
return {
|
|
217
|
+
message: {
|
|
218
|
+
customType: "unipi-skills-revealed",
|
|
219
|
+
content: revealMessage(out.reveal),
|
|
220
|
+
display: true,
|
|
221
|
+
details: { unipiHarness: harnessMetadata({ source: "Skills", title: "Skill reveal", synopsis: `${String(out.reveal.length)} relevant skill(s)` }, "before_agent_start") },
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return undefined;
|
|
226
|
+
} catch {
|
|
227
|
+
return undefined; // never abort a turn
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/skill-registry",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.0-alpha.21",
|
|
4
|
+
"description": "Skill registry for pi — per-project skill on/off, a skill vault, and jev-judged skill exposure",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Neuron Mr White",
|
|
@@ -12,23 +12,34 @@
|
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
14
14
|
"pi-package",
|
|
15
|
-
"pi-skills",
|
|
16
15
|
"unipi",
|
|
17
16
|
"skill-registry"
|
|
18
17
|
],
|
|
19
18
|
"files": [
|
|
20
|
-
"
|
|
21
|
-
"
|
|
19
|
+
"index.ts",
|
|
20
|
+
"src/**/*.ts",
|
|
21
|
+
"README.md",
|
|
22
|
+
"!src/**/*.test.ts"
|
|
22
23
|
],
|
|
23
24
|
"publishConfig": {
|
|
24
25
|
"access": "public"
|
|
25
26
|
},
|
|
26
27
|
"pi": {
|
|
27
|
-
"extensions": [
|
|
28
|
-
|
|
29
|
-
"./skills"
|
|
28
|
+
"extensions": [
|
|
29
|
+
"./index.ts"
|
|
30
30
|
],
|
|
31
31
|
"prompts": [],
|
|
32
32
|
"themes": []
|
|
33
|
+
},
|
|
34
|
+
"main": "index.ts",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@pi-unipi/core": "3.0.0-alpha.21"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
40
|
+
"@earendil-works/pi-tui": "^0.87.1"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"test": "npx tsx --test tests/*.test.ts"
|
|
33
44
|
}
|
|
34
45
|
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/skill-registry — "Skill settings…" overlay (like Fusion's preset editor)
|
|
3
|
+
*
|
|
4
|
+
* Skill settings · 47 skills · editing project · proxy on
|
|
5
|
+
* Search: (type to filter)
|
|
6
|
+
* ────────────────────────────────────────────────────────────
|
|
7
|
+
* E M skill source
|
|
8
|
+
* › [x] [ ] sql-review user Review SQL migrations…
|
|
9
|
+
* [ ] [ ] aws-deploy vault Deploy services to AWS…
|
|
10
|
+
*
|
|
11
|
+
* E enabled — the model sees it (judged first when exposure is on); off removes it and blocks /skill:name
|
|
12
|
+
* M must show — always listed, even when judging would hide it
|
|
13
|
+
* ↑↓ skill · ←→ column · space toggle · d inherit · g scope · p proxy · enter save · esc cancel · type to filter
|
|
14
|
+
*
|
|
15
|
+
* Each cell shows the EFFECTIVE value; a value set in the layer being edited
|
|
16
|
+
* is bright, an inherited one dim. Changes are held until Enter.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
20
|
+
import { frameOverlay } from "@pi-unipi/core";
|
|
21
|
+
import type { SkillSource } from "./registry.js";
|
|
22
|
+
import type { SkillState } from "./settings.js";
|
|
23
|
+
|
|
24
|
+
export type Column = "enabled" | "mustShow";
|
|
25
|
+
export const COLUMNS: readonly Column[] = ["enabled", "mustShow"];
|
|
26
|
+
const LETTER: Record<Column, string> = { enabled: "E", mustShow: "M" };
|
|
27
|
+
|
|
28
|
+
export interface EditorSkill {
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
source: SkillSource;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type Scope = "global" | "project";
|
|
35
|
+
export type Layers = Record<Scope, Record<string, SkillState>>;
|
|
36
|
+
|
|
37
|
+
export interface EditorResult {
|
|
38
|
+
type: "saved" | "cancelled";
|
|
39
|
+
/** The layers as edited (only meaningful when saved). */
|
|
40
|
+
layers: Layers;
|
|
41
|
+
proxy: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface EditorOptions {
|
|
45
|
+
skills: readonly EditorSkill[];
|
|
46
|
+
layers: Layers;
|
|
47
|
+
proxy: boolean;
|
|
48
|
+
initialScope: Scope;
|
|
49
|
+
theme: { fg: (color: string, text: string) => string; bold: (text: string) => string };
|
|
50
|
+
onDone: (result: EditorResult) => void;
|
|
51
|
+
onRenderRequest?: () => void;
|
|
52
|
+
visibleRows?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function defaultFor(column: Column, source: SkillSource): boolean {
|
|
56
|
+
if (column === "enabled") return source !== "vault";
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Effective value of one cell: project over global over the source default. */
|
|
61
|
+
export function cellValue(layers: Layers, skill: EditorSkill, column: Column): { value: boolean; from: Scope | "default" } {
|
|
62
|
+
const p = layers.project[skill.name]?.[column];
|
|
63
|
+
if (typeof p === "boolean") return { value: p, from: "project" };
|
|
64
|
+
const g = layers.global[skill.name]?.[column];
|
|
65
|
+
if (typeof g === "boolean") return { value: g, from: "global" };
|
|
66
|
+
return { value: defaultFor(column, skill.source), from: "default" };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function printable(data: string): string | undefined {
|
|
70
|
+
if (data.length !== 1) return undefined;
|
|
71
|
+
const code = data.charCodeAt(0);
|
|
72
|
+
return code < 32 || code === 127 ? undefined : data;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class SkillEditor {
|
|
76
|
+
private readonly opts: EditorOptions;
|
|
77
|
+
private readonly layers: Layers;
|
|
78
|
+
private proxy: boolean;
|
|
79
|
+
private scope: Scope;
|
|
80
|
+
private search = "";
|
|
81
|
+
private selected = 0;
|
|
82
|
+
private column = 0;
|
|
83
|
+
private done = false;
|
|
84
|
+
|
|
85
|
+
constructor(opts: EditorOptions) {
|
|
86
|
+
this.opts = opts;
|
|
87
|
+
this.layers = { global: structuredClone(opts.layers.global), project: structuredClone(opts.layers.project) };
|
|
88
|
+
this.proxy = opts.proxy;
|
|
89
|
+
this.scope = opts.initialScope;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private filtered(): EditorSkill[] {
|
|
93
|
+
const q = this.search.toLowerCase();
|
|
94
|
+
const list = q ? this.opts.skills.filter((s) => `${s.name} ${s.source} ${s.description}`.toLowerCase().includes(q)) : [...this.opts.skills];
|
|
95
|
+
return list;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
handleInput(data: string): void {
|
|
99
|
+
if (this.done) return;
|
|
100
|
+
const list = this.filtered();
|
|
101
|
+
const cur = list[this.selected];
|
|
102
|
+
const col = COLUMNS[this.column]!;
|
|
103
|
+
if (matchesKey(data, Key.escape)) {
|
|
104
|
+
if (this.search) {
|
|
105
|
+
this.search = "";
|
|
106
|
+
this.selected = 0;
|
|
107
|
+
} else {
|
|
108
|
+
this.done = true;
|
|
109
|
+
this.opts.onDone({ type: "cancelled", layers: this.layers, proxy: this.proxy });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
} else if (matchesKey(data, Key.enter) || data === "\r") {
|
|
113
|
+
this.done = true;
|
|
114
|
+
this.opts.onDone({ type: "saved", layers: this.layers, proxy: this.proxy });
|
|
115
|
+
return;
|
|
116
|
+
} else if (matchesKey(data, Key.up)) this.selected = Math.max(0, this.selected - 1);
|
|
117
|
+
else if (matchesKey(data, Key.down)) this.selected = Math.min(Math.max(0, list.length - 1), this.selected + 1);
|
|
118
|
+
else if (matchesKey(data, Key.left) || matchesKey(data, "shift+tab")) this.column = (this.column + COLUMNS.length - 1) % COLUMNS.length;
|
|
119
|
+
else if (matchesKey(data, Key.right) || matchesKey(data, Key.tab)) this.column = (this.column + 1) % COLUMNS.length;
|
|
120
|
+
else if (matchesKey(data, Key.pageUp)) this.selected = Math.max(0, this.selected - this.window());
|
|
121
|
+
else if (matchesKey(data, Key.pageDown)) this.selected = Math.min(Math.max(0, list.length - 1), this.selected + this.window());
|
|
122
|
+
else if ((data === " " || matchesKey(data, Key.space)) && cur) {
|
|
123
|
+
const next = !cellValue(this.layers, cur, col).value;
|
|
124
|
+
const layer = this.layers[this.scope];
|
|
125
|
+
layer[cur.name] = { ...layer[cur.name], [col]: next };
|
|
126
|
+
} else if (!this.search && data === "d" && cur) {
|
|
127
|
+
const entry = this.layers[this.scope][cur.name];
|
|
128
|
+
if (entry) {
|
|
129
|
+
delete entry[col];
|
|
130
|
+
if (Object.keys(entry).length === 0) delete this.layers[this.scope][cur.name];
|
|
131
|
+
}
|
|
132
|
+
} else if (!this.search && data === "g") this.scope = this.scope === "global" ? "project" : "global";
|
|
133
|
+
else if (!this.search && data === "p") this.proxy = !this.proxy;
|
|
134
|
+
else if (matchesKey(data, Key.backspace) || data === "\x7f") {
|
|
135
|
+
this.search = this.search.slice(0, -1);
|
|
136
|
+
this.selected = 0;
|
|
137
|
+
} else {
|
|
138
|
+
const ch = printable(data);
|
|
139
|
+
if (ch === undefined || ch === " ") return;
|
|
140
|
+
this.search += ch;
|
|
141
|
+
this.selected = 0;
|
|
142
|
+
}
|
|
143
|
+
this.opts.onRenderRequest?.();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private window(): number {
|
|
147
|
+
return this.opts.visibleRows ?? Math.max(5, Math.min(18, (process.stdout.rows ?? 40) - 16));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
invalidate(): void {}
|
|
151
|
+
|
|
152
|
+
render(width: number): string[] {
|
|
153
|
+
return frameOverlay(this.body(Math.max(20, width - 2)), width, { title: "Skill settings" });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private body(width: number): string[] {
|
|
157
|
+
const t = this.opts.theme;
|
|
158
|
+
const list = this.filtered();
|
|
159
|
+
if (this.selected >= list.length) this.selected = Math.max(0, list.length - 1);
|
|
160
|
+
const fit = (s: string) => truncateToWidth(s, Math.max(1, width - 1));
|
|
161
|
+
const lines: string[] = [];
|
|
162
|
+
lines.push(fit(`${t.fg("accent", t.bold("Skill settings"))} ${t.fg("dim", `· ${list.length} skills · editing `)}${t.fg("accent", this.scope)}${t.fg("dim", " · proxy ")}${this.proxy ? t.fg("success", "on") : t.fg("warning", "off — saved but not applied (p)")}`));
|
|
163
|
+
lines.push(fit(`${t.fg("dim", "Search:")} ${this.search ? t.fg("text", this.search) : t.fg("dim", "(type to filter)")}`));
|
|
164
|
+
lines.push(t.fg("dim", "─".repeat(Math.max(1, width - 2))));
|
|
165
|
+
|
|
166
|
+
const nameW = Math.min(28, Math.max(10, ...list.map((s) => s.name.length)) + 1);
|
|
167
|
+
const head = COLUMNS.map((c, i) => (i === this.column ? t.fg("accent", t.bold(` ${LETTER[c]} `)) : t.fg("dim", ` ${LETTER[c]} `))).join(" ");
|
|
168
|
+
lines.push(fit(` ${head} ${t.fg("dim", "skill".padEnd(nameW))} ${t.fg("dim", "source ")} ${t.fg("dim", "description")}`));
|
|
169
|
+
|
|
170
|
+
const win = this.window();
|
|
171
|
+
const start = Math.max(0, Math.min(this.selected - Math.floor(win / 2), list.length - win));
|
|
172
|
+
const end = Math.min(list.length, start + win);
|
|
173
|
+
if (list.length === 0) lines.push(t.fg("warning", " No skills match."));
|
|
174
|
+
if (start > 0) lines.push(t.fg("dim", ` ↑ ${start} more`));
|
|
175
|
+
for (let i = start; i < end; i++) {
|
|
176
|
+
const s = list[i]!;
|
|
177
|
+
const hl = i === this.selected;
|
|
178
|
+
const cells = COLUMNS.map((c, ci) => {
|
|
179
|
+
const { value, from } = cellValue(this.layers, s, c);
|
|
180
|
+
const glyph = value ? "[x]" : "[ ]";
|
|
181
|
+
if (hl && ci === this.column) return t.fg("accent", t.bold(glyph));
|
|
182
|
+
if (from === this.scope) return value ? t.fg("success", glyph) : t.fg("text", glyph);
|
|
183
|
+
return t.fg("dim", glyph);
|
|
184
|
+
}).join(" ");
|
|
185
|
+
const name = s.name.length > nameW - 1 ? `${s.name.slice(0, nameW - 2)}…` : s.name;
|
|
186
|
+
const label = hl ? t.fg("accent", name.padEnd(nameW)) : t.fg("text", name.padEnd(nameW));
|
|
187
|
+
const src = t.fg(s.source === "vault" ? "warning" : "dim", s.source.padEnd(7));
|
|
188
|
+
const desc = t.fg("dim", s.description.replace(/\s+/g, " "));
|
|
189
|
+
lines.push(fit(` ${hl ? t.fg("accent", "›") : " "} ${cells} ${label} ${src} ${desc}`));
|
|
190
|
+
}
|
|
191
|
+
if (end < list.length) lines.push(t.fg("dim", ` ↓ ${list.length - end} more`));
|
|
192
|
+
|
|
193
|
+
lines.push("");
|
|
194
|
+
const legend: Array<[Column, string]> = [
|
|
195
|
+
["enabled", "enabled — the model sees it (judged first when exposure is on); off removes it and blocks /skill:name"],
|
|
196
|
+
["mustShow", "must show — always listed, even when judging would hide it"],
|
|
197
|
+
];
|
|
198
|
+
for (const [c, text] of legend) {
|
|
199
|
+
const active = COLUMNS[this.column] === c;
|
|
200
|
+
lines.push(fit(`${active ? t.fg("accent", t.bold(LETTER[c])) : t.fg("dim", LETTER[c])} ${active ? t.fg("text", text) : t.fg("dim", text)}`));
|
|
201
|
+
}
|
|
202
|
+
lines.push(fit(t.fg("dim", `bright = set in ${this.scope} · dim = inherited`)));
|
|
203
|
+
lines.push(fit(t.fg("dim", "↑↓ skill · ←→ column · space toggle · d inherit · g scope · p proxy · enter save · esc cancel · type to filter")));
|
|
204
|
+
return lines.map((l) => (visibleWidth(l) > width ? truncateToWidth(l, width) : l));
|
|
205
|
+
}
|
|
206
|
+
}
|