@knightcodeai/cli 0.17.4 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1293 -0
- package/README.md +2 -2
- package/dist/bundle/chunks/anthropic-messages-A2IH2IY3.js +6 -0
- package/dist/bundle/chunks/anthropic.js +101 -0
- package/dist/bundle/chunks/azure-openai-responses-XZILQZOG.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +89 -0
- package/dist/bundle/chunks/chunk-4CRI7BMY.js +2 -0
- package/dist/bundle/chunks/chunk-57BPR4HF.js +2 -0
- package/dist/bundle/chunks/chunk-A5LJINMT.js +2 -0
- package/dist/bundle/chunks/chunk-AANBXVWQ.js +2 -0
- package/dist/bundle/chunks/chunk-ACHHEROF.js +7 -0
- package/dist/bundle/chunks/chunk-AFR2OW6S.js +108 -0
- package/dist/bundle/chunks/chunk-AHB7JV5B.js +2 -0
- package/dist/bundle/chunks/chunk-B2FT7RH5.js +2 -0
- package/dist/bundle/chunks/chunk-BRKUE2MD.js +2 -0
- package/dist/bundle/chunks/chunk-DKOURXGU.js +2 -0
- package/dist/bundle/chunks/chunk-DVFCXHT4.js +2 -0
- package/dist/bundle/chunks/chunk-EPYT326Y.js +30 -0
- package/dist/bundle/chunks/chunk-ETOMYYZC.js +2 -0
- package/dist/bundle/chunks/chunk-FA32X2UJ.js +7 -0
- package/dist/bundle/chunks/chunk-FHM6KUY3.js +44 -0
- package/dist/bundle/chunks/chunk-FO7CEQB3.js +11 -0
- package/dist/bundle/chunks/chunk-GHVXUCGU.js +130 -0
- package/dist/bundle/chunks/chunk-HHWP56PR.js +2 -0
- package/dist/bundle/chunks/chunk-HSL72VZI.js +2 -0
- package/dist/bundle/chunks/chunk-HUQB436V.js +2 -0
- package/dist/bundle/chunks/chunk-HWXGL4YB.js +2 -0
- package/dist/bundle/chunks/chunk-KQKUEQJA.js +6 -0
- package/dist/bundle/chunks/chunk-M5GCRGV6.js +1333 -0
- package/dist/bundle/chunks/chunk-MB4QRS7Y.js +9 -0
- package/dist/bundle/chunks/chunk-MXIWLMNT.js +8 -0
- package/dist/bundle/chunks/chunk-N6VDWLND.js +2 -0
- package/dist/bundle/chunks/chunk-NCIBB66G.js +2 -0
- package/dist/bundle/chunks/chunk-NDQ5OW6U.js +2 -0
- package/dist/bundle/chunks/chunk-NNCIIYD6.js +2 -0
- package/dist/bundle/chunks/chunk-O6JSP6YG.js +6 -0
- package/dist/bundle/chunks/chunk-OFST7FDG.js +2 -0
- package/dist/bundle/chunks/chunk-SPAUY3RD.js +12 -0
- package/dist/bundle/chunks/chunk-U5XE4RWR.js +2 -0
- package/dist/bundle/chunks/chunk-UZWP3NHW.js +2 -0
- package/dist/bundle/chunks/chunk-V5ANFNKF.js +11 -0
- package/dist/bundle/chunks/chunk-VJJ6J35N.js +20 -0
- package/dist/bundle/chunks/chunk-XIRAUXOE.js +58 -0
- package/dist/bundle/chunks/chunk-ZW7FVRHT.js +31 -0
- package/dist/bundle/chunks/cli-6ZTWO6GL.js +51 -0
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-6E2UM7IG.js +2 -0
- package/dist/bundle/chunks/codemode-worker.js +478 -0
- package/dist/bundle/chunks/dist-DKI7Q6GY.js +13 -0
- package/dist/bundle/chunks/easter-egg-3d-FDCGDUF3.js +2 -0
- package/dist/bundle/chunks/execute-VSDU5VSN.js +499 -0
- package/dist/bundle/chunks/github-copilot.js +2 -0
- package/dist/bundle/chunks/google-generative-ai-CSWR3ORZ.js +2 -0
- package/dist/bundle/chunks/google-vertex-QIDRFOFB.js +2 -0
- package/dist/bundle/chunks/image-resize-worker.js +2 -0
- package/dist/bundle/chunks/jiti-loader-BXVX3Y2R.js +21 -0
- package/dist/bundle/chunks/jiti-static-loader-NNBCFAO5.js +2 -0
- package/dist/bundle/chunks/kimi-coding.js +2 -0
- package/dist/bundle/chunks/knightcode-messages-3GNOTMSI.js +8 -0
- package/dist/bundle/chunks/lib-IHWMWJDR.js +9 -0
- package/dist/bundle/chunks/llama-cpp-classify-HRPRKOZA.js +22 -0
- package/dist/bundle/chunks/meta.js +2 -0
- package/dist/bundle/chunks/mistral-conversations-46TK5OBA.js +5 -0
- package/dist/bundle/chunks/multipart-parser-VTTMF2EO.js +3 -0
- package/dist/bundle/chunks/node-7S3GFMOZ.js +16 -0
- package/dist/bundle/chunks/node-MAEICQNS.js +2 -0
- package/dist/bundle/chunks/openai-chatgpt.js +101 -0
- package/dist/bundle/chunks/openai-codex-responses-IT7VEKUU.js +10 -0
- package/dist/bundle/chunks/openai-codex.js +101 -0
- package/dist/bundle/chunks/openai-completions-LOKIW33V.js +7 -0
- package/dist/bundle/chunks/openai-decisions-4F3LWHMG.js +5 -0
- package/dist/bundle/chunks/openai-responses-OK36GJXP.js +3 -0
- package/dist/bundle/chunks/openrouter-images-ZI7LLZ4Y.js +2 -0
- package/dist/bundle/chunks/openrouter.js +101 -0
- package/dist/bundle/chunks/radius.js +101 -0
- package/dist/bundle/chunks/runtime-RMPIV5AR.js +2 -0
- package/dist/bundle/chunks/src-ETRMT73D.js +4 -0
- package/dist/bundle/chunks/typesafe-system-one-J2CJBWEH.js +2 -0
- package/dist/bundle/chunks/virtual-modules-EGJP3AJA.js +2 -0
- package/dist/bundle/chunks/xai.js +2 -0
- package/dist/bundle/cli-runtime.js +3 -0
- package/dist/bundle/cli.js +5 -0
- package/dist/bundle/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/dist/bundle/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/dist/bundle/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
- package/dist/bundle/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
- package/dist/bundle/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
- package/dist/bundle/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
- package/dist/core/export-html/template.css +1015 -0
- package/dist/core/export-html/template.html +55 -0
- package/dist/core/export-html/template.js +1929 -0
- package/dist/core/export-html/vendor/highlight.min.js +1213 -0
- package/dist/core/export-html/vendor/marked.min.js +78 -0
- package/dist/modes/interactive/theme/dark.json +74 -0
- package/dist/modes/interactive/theme/light.json +74 -0
- package/dist/modes/interactive/theme/system-theme.ts +579 -0
- package/dist/modes/interactive/theme/theme-controller.ts +253 -0
- package/dist/modes/interactive/theme/theme-schema.ts +145 -0
- package/dist/modes/interactive/theme/theme-tokens.ts +145 -0
- package/dist/modes/interactive/theme/theme.ts +1076 -0
- package/docs/app-server.md +479 -0
- package/docs/app-server.schema.json +8128 -0
- package/docs/cli-integration.md +106 -0
- package/docs/cli.md +393 -0
- package/docs/codemode.md +230 -0
- package/docs/compaction.md +463 -0
- package/docs/configuration.md +48 -0
- package/docs/containerization.md +143 -0
- package/docs/custom-provider.md +173 -0
- package/docs/desktop.md +73 -0
- package/docs/docs.json +236 -0
- package/docs/environment-variables.md +138 -0
- package/docs/extensions.md +314 -0
- package/docs/how-knightcode-works.md +49 -0
- package/docs/images/doom-extension.png +0 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/index.md +39 -0
- package/docs/json.md +226 -0
- package/docs/keybindings.md +197 -0
- package/docs/llama-cpp.md +121 -0
- package/docs/mcp.md +272 -0
- package/docs/message-types.md +279 -0
- package/docs/models-configuration.md +187 -0
- package/docs/models.md +221 -0
- package/docs/nvidia-rtx-llama-cpp.md +96 -0
- package/docs/packages.md +129 -0
- package/docs/plan-mode.md +121 -0
- package/docs/prompt-templates.md +59 -0
- package/docs/providers.md +282 -0
- package/docs/quickstart.md +128 -0
- package/docs/remote.md +87 -0
- package/docs/rpc-commands.md +860 -0
- package/docs/rpc-extension-ui.md +214 -0
- package/docs/rpc.md +193 -0
- package/docs/sdk.md +197 -0
- package/docs/security.md +99 -0
- package/docs/session-format.md +358 -0
- package/docs/sessions.md +135 -0
- package/docs/settings.md +220 -0
- package/docs/shell-aliases.md +93 -0
- package/docs/skills.md +93 -0
- package/docs/slash-commands.md +78 -0
- package/docs/terminal-setup.md +242 -0
- package/docs/termux.md +117 -0
- package/docs/themes.md +140 -0
- package/docs/tmux.md +55 -0
- package/docs/tui.md +125 -0
- package/docs/usage.md +167 -0
- package/docs/virtual-models.md +114 -0
- package/docs/windows.md +67 -0
- package/examples/README.md +36 -0
- package/examples/extensions/README.md +214 -0
- package/examples/extensions/auto-commit-on-exit.ts +55 -0
- package/examples/extensions/bash-spawn-hook.ts +30 -0
- package/examples/extensions/bookmark.ts +50 -0
- package/examples/extensions/border-status-editor.ts +151 -0
- package/examples/extensions/built-in-tool-renderer.ts +225 -0
- package/examples/extensions/claude-rules.ts +89 -0
- package/examples/extensions/commands.ts +72 -0
- package/examples/extensions/confirm-destructive.ts +60 -0
- package/examples/extensions/custom-compaction.ts +118 -0
- package/examples/extensions/custom-footer.ts +66 -0
- package/examples/extensions/custom-header.ts +73 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +617 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
- package/examples/extensions/custom-provider-anthropic/package.json +19 -0
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +399 -0
- package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
- package/examples/extensions/debug-provider.ts +90 -0
- package/examples/extensions/dirty-repo-guard.ts +56 -0
- package/examples/extensions/doom-overlay/README.md +46 -0
- package/examples/extensions/doom-overlay/doom/build.sh +152 -0
- package/examples/extensions/doom-overlay/doom/doomgeneric_knightcode.c +72 -0
- package/examples/extensions/doom-overlay/doom-component.ts +132 -0
- package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
- package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
- package/examples/extensions/doom-overlay/index.ts +74 -0
- package/examples/extensions/doom-overlay/wad-finder.ts +55 -0
- package/examples/extensions/dynamic-resources/SKILL.md +8 -0
- package/examples/extensions/dynamic-resources/dynamic.json +75 -0
- package/examples/extensions/dynamic-resources/dynamic.md +5 -0
- package/examples/extensions/dynamic-resources/index.ts +15 -0
- package/examples/extensions/dynamic-tools.ts +74 -0
- package/examples/extensions/entry-renderer.ts +41 -0
- package/examples/extensions/event-bus.ts +43 -0
- package/examples/extensions/file-trigger.ts +41 -0
- package/examples/extensions/git-checkpoint.ts +53 -0
- package/examples/extensions/git-merge-and-resolve.ts +115 -0
- package/examples/extensions/github-issue-autocomplete.ts +185 -0
- package/examples/extensions/gondolin/index.ts +531 -0
- package/examples/extensions/gondolin/package-lock.json +185 -0
- package/examples/extensions/gondolin/package.json +19 -0
- package/examples/extensions/handoff.ts +190 -0
- package/examples/extensions/hello.ts +26 -0
- package/examples/extensions/hidden-thinking-label.ts +53 -0
- package/examples/extensions/inline-bash.ts +94 -0
- package/examples/extensions/input-transform-streaming.ts +39 -0
- package/examples/extensions/input-transform.ts +43 -0
- package/examples/extensions/interactive-shell.ts +196 -0
- package/examples/extensions/jev-router.ts +113 -0
- package/examples/extensions/mac-system-theme.ts +47 -0
- package/examples/extensions/message-renderer.ts +59 -0
- package/examples/extensions/minimal-mode.ts +353 -0
- package/examples/extensions/modal-editor.ts +85 -0
- package/examples/extensions/model-status.ts +31 -0
- package/examples/extensions/notify.ts +57 -0
- package/examples/extensions/overlay-qa-tests.ts +1450 -0
- package/examples/extensions/overlay-test.ts +153 -0
- package/examples/extensions/permission-gate.ts +34 -0
- package/examples/extensions/pirate.ts +47 -0
- package/examples/extensions/plan-mode/README.md +66 -0
- package/examples/extensions/plan-mode/index.ts +387 -0
- package/examples/extensions/plan-mode/utils.ts +168 -0
- package/examples/extensions/preset.ts +436 -0
- package/examples/extensions/project-trust.ts +64 -0
- package/examples/extensions/prompt-customizer.ts +49 -0
- package/examples/extensions/protected-paths.ts +30 -0
- package/examples/extensions/provider-payload.ts +18 -0
- package/examples/extensions/qna.ts +118 -0
- package/examples/extensions/question.ts +278 -0
- package/examples/extensions/questionnaire.ts +440 -0
- package/examples/extensions/rainbow-editor.ts +88 -0
- package/examples/extensions/reload-runtime.ts +37 -0
- package/examples/extensions/rpc-demo.ts +118 -0
- package/examples/extensions/sandbox/index.ts +321 -0
- package/examples/extensions/sandbox/package-lock.json +92 -0
- package/examples/extensions/sandbox/package.json +19 -0
- package/examples/extensions/send-user-message.ts +97 -0
- package/examples/extensions/session-name.ts +27 -0
- package/examples/extensions/shutdown-command.ts +63 -0
- package/examples/extensions/snake.ts +343 -0
- package/examples/extensions/space-invaders.ts +560 -0
- package/examples/extensions/ssh.ts +220 -0
- package/examples/extensions/status-line.ts +32 -0
- package/examples/extensions/structured-output.ts +65 -0
- package/examples/extensions/subagent/README.md +177 -0
- package/examples/extensions/subagent/agents/planner.md +37 -0
- package/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/examples/extensions/subagent/agents/scout.md +50 -0
- package/examples/extensions/subagent/agents/worker.md +24 -0
- package/examples/extensions/subagent/agents.ts +157 -0
- package/examples/extensions/subagent/index.ts +1010 -0
- package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/examples/extensions/subagent/prompts/implement.md +10 -0
- package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/examples/extensions/summarize.ts +199 -0
- package/examples/extensions/system-prompt-header.ts +17 -0
- package/examples/extensions/tic-tac-toe.ts +1004 -0
- package/examples/extensions/timed-confirm.ts +70 -0
- package/examples/extensions/titlebar-spinner.ts +58 -0
- package/examples/extensions/todo.ts +294 -0
- package/examples/extensions/tool-override.ts +144 -0
- package/examples/extensions/tools.ts +146 -0
- package/examples/extensions/trigger-compact.ts +50 -0
- package/examples/extensions/truncated-tool.ts +195 -0
- package/examples/extensions/widget-placement.ts +9 -0
- package/examples/extensions/with-deps/index.ts +32 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +22 -0
- package/examples/extensions/working-indicator.ts +121 -0
- package/examples/extensions/working-message-test.ts +25 -0
- package/examples/rpc-client.ts +35 -0
- package/examples/rpc-extension-ui.ts +661 -0
- package/examples/sdk/01-minimal.ts +26 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +70 -0
- package/examples/sdk/04-skills.ts +55 -0
- package/examples/sdk/05-tools.ts +48 -0
- package/examples/sdk/06-extensions.ts +94 -0
- package/examples/sdk/07-context-files.ts +42 -0
- package/examples/sdk/08-prompt-templates.ts +51 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +34 -0
- package/examples/sdk/10-settings.ts +53 -0
- package/examples/sdk/11-sessions.ts +52 -0
- package/examples/sdk/12-full-control.ts +74 -0
- package/examples/sdk/13-session-runtime.ts +67 -0
- package/examples/sdk/14-codemode-mcp.ts +61 -0
- package/examples/sdk/README.md +141 -0
- package/package.json +15 -15
- package/bin/knightcode +0 -62
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Extension Examples
|
|
2
|
+
|
|
3
|
+
Example extensions for @knightcodeai/cli.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Load an extension with --extension flag
|
|
9
|
+
knightcode --extension examples/extensions/permission-gate.ts
|
|
10
|
+
|
|
11
|
+
# Or copy to extensions directory for auto-discovery
|
|
12
|
+
cp permission-gate.ts ~/.knightcode/agent/extensions/
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Examples
|
|
16
|
+
|
|
17
|
+
### Lifecycle & Safety
|
|
18
|
+
|
|
19
|
+
| Extension | Description |
|
|
20
|
+
|-----------|-------------|
|
|
21
|
+
| `permission-gate.ts` | Prompts for confirmation before dangerous bash commands (rm -rf, sudo, etc.) |
|
|
22
|
+
| `project-trust.ts` | Demonstrates the `project_trust` event for user/global and CLI extensions |
|
|
23
|
+
| `protected-paths.ts` | Blocks writes to protected paths (.env, .git/, node_modules/) |
|
|
24
|
+
| `confirm-destructive.ts` | Confirms before destructive session actions (clear, switch, fork) |
|
|
25
|
+
| `dirty-repo-guard.ts` | Prevents session changes with uncommitted git changes |
|
|
26
|
+
| `sandbox/` | OS-level sandboxing using `@anthropic-ai/sandbox-runtime` with per-project config |
|
|
27
|
+
| `gondolin/` | Route built-in tools and `!` commands into a Gondolin micro-VM |
|
|
28
|
+
|
|
29
|
+
### Custom Tools
|
|
30
|
+
|
|
31
|
+
| Extension | Description |
|
|
32
|
+
|-----------|-------------|
|
|
33
|
+
| `todo.ts` | Todo list tool + `/todos` command with custom rendering and state persistence |
|
|
34
|
+
| `hello.ts` | Minimal custom tool example |
|
|
35
|
+
| `question.ts` | Demonstrates `ctx.ui.select()` for asking the user questions with custom UI |
|
|
36
|
+
| `questionnaire.ts` | Multi-question input with tab bar navigation between questions |
|
|
37
|
+
| `tool-override.ts` | Override built-in tools (e.g., add logging/access control to `read`) |
|
|
38
|
+
| `dynamic-tools.ts` | Register tools after startup (`session_start`) and at runtime via command, with prompt snippets and tool-specific prompt guidelines |
|
|
39
|
+
| `structured-output.ts` | Final structured-output tool that returns `terminate: true` so the agent can end on the tool call |
|
|
40
|
+
| `built-in-tool-renderer.ts` | Custom compact rendering for built-in tools (read, bash, edit, write) while keeping original behavior |
|
|
41
|
+
| `minimal-mode.ts` | Override built-in tool rendering for minimal display (only tool calls, no output in collapsed mode) |
|
|
42
|
+
| `truncated-tool.ts` | Wraps ripgrep with proper output truncation (50KB/2000 lines) |
|
|
43
|
+
| `ssh.ts` | Delegate all tools to a remote machine via SSH using pluggable operations |
|
|
44
|
+
| `subagent/` | Delegate tasks to specialized subagents with isolated context windows |
|
|
45
|
+
|
|
46
|
+
### Commands & UI
|
|
47
|
+
|
|
48
|
+
| Extension | Description |
|
|
49
|
+
|-----------|-------------|
|
|
50
|
+
| `preset.ts` | Named presets for model, thinking level, tools, and instructions via `--preset` flag and `/preset` command |
|
|
51
|
+
| `plan-mode/` | Claude Code-style plan mode for read-only exploration with `/plan` command and step tracking |
|
|
52
|
+
| `tools.ts` | Interactive `/tools` command to enable/disable tools with session persistence |
|
|
53
|
+
| `handoff.ts` | Transfer context to a new focused session via `/handoff <goal>` |
|
|
54
|
+
| `qna.ts` | Extracts questions from last response into editor via `ctx.ui.setEditorText()` |
|
|
55
|
+
| `status-line.ts` | Shows turn progress in footer via `ctx.ui.setStatus()` with themed colors |
|
|
56
|
+
| `github-issue-autocomplete.ts` | Adds `#1234` issue completions by stacking a custom autocomplete provider that preloads open issues from `gh issue list` |
|
|
57
|
+
| `widget-placement.ts` | Shows widgets above and below the editor via `ctx.ui.setWidget()` placement |
|
|
58
|
+
| `hidden-thinking-label.ts` | Customizes the collapsed thinking label via `ctx.ui.setHiddenThinkingLabel()` |
|
|
59
|
+
| `working-indicator.ts` | Customizes the streaming working indicator via `ctx.ui.setWorkingIndicator()` |
|
|
60
|
+
| `model-status.ts` | Shows model changes in status bar via `model_select` hook |
|
|
61
|
+
| `snake.ts` | Snake game with custom UI, keyboard handling, and session persistence |
|
|
62
|
+
| `tic-tac-toe.ts` | Tic-tac-toe vs the agent with `executionMode: "sequential"` tools to prevent race conditions on shared cursor state |
|
|
63
|
+
| `send-user-message.ts` | Demonstrates `knightcode.sendUserMessage()` for sending user messages from extensions |
|
|
64
|
+
| `timed-confirm.ts` | Demonstrates AbortSignal for auto-dismissing `ctx.ui.confirm()` and `ctx.ui.select()` dialogs |
|
|
65
|
+
| `rpc-demo.ts` | Exercises all RPC-supported extension UI methods; pair with [`examples/rpc-extension-ui.ts`](../rpc-extension-ui.ts) |
|
|
66
|
+
| `modal-editor.ts` | Custom vim-like modal editor via `ctx.ui.setEditorComponent()` |
|
|
67
|
+
| `rainbow-editor.ts` | Animated rainbow text effect via custom editor |
|
|
68
|
+
| `notify.ts` | Desktop notifications via OSC 777 when agent finishes (Ghostty, iTerm2, WezTerm) |
|
|
69
|
+
| `titlebar-spinner.ts` | Braille spinner animation in terminal title while the agent is working |
|
|
70
|
+
| `summarize.ts` | Summarize conversation with GPT-5.2 and show in transient UI |
|
|
71
|
+
| `custom-footer.ts` | Custom footer with git branch and token stats via `ctx.ui.setFooter()` |
|
|
72
|
+
| `custom-header.ts` | Custom header via `ctx.ui.setHeader()` |
|
|
73
|
+
| `overlay-test.ts` | Test overlay compositing with inline text inputs and edge cases |
|
|
74
|
+
| `overlay-qa-tests.ts` | Comprehensive overlay QA tests: anchors, margins, stacking, overflow, animation |
|
|
75
|
+
| `doom-overlay/` | DOOM game running as an overlay at 35 FPS (demonstrates real-time game rendering) |
|
|
76
|
+
| `shutdown-command.ts` | Adds `/quit` command demonstrating `ctx.shutdown()` |
|
|
77
|
+
| `reload-runtime.ts` | Adds `/reload-runtime` and `reload_runtime` tool showing safe reload flow |
|
|
78
|
+
| `interactive-shell.ts` | Run interactive commands (vim, htop) with full terminal via `user_bash` hook |
|
|
79
|
+
| `inline-bash.ts` | Expands `!{command}` patterns in prompts via `input` event transformation |
|
|
80
|
+
| `input-transform-streaming.ts` | Skips expensive input preprocessing for mid-stream steering via `streamingBehavior` |
|
|
81
|
+
|
|
82
|
+
### Git Integration
|
|
83
|
+
|
|
84
|
+
| Extension | Description |
|
|
85
|
+
|-----------|-------------|
|
|
86
|
+
| `git-checkpoint.ts` | Creates git stash checkpoints at each turn for code restoration on fork |
|
|
87
|
+
| `auto-commit-on-exit.ts` | Auto-commits on exit using last assistant message for commit message |
|
|
88
|
+
|
|
89
|
+
### System Prompt & Compaction
|
|
90
|
+
|
|
91
|
+
| Extension | Description |
|
|
92
|
+
|-----------|-------------|
|
|
93
|
+
| `pirate.ts` | Demonstrates `systemPromptAppend` to dynamically modify system prompt |
|
|
94
|
+
| `claude-rules.ts` | Scans `.claude/rules/` folder and lists rules in system prompt |
|
|
95
|
+
| `custom-compaction.ts` | Custom compaction that summarizes entire conversation |
|
|
96
|
+
| `trigger-compact.ts` | Triggers compaction when context usage exceeds 100k tokens and adds `/trigger-compact` command |
|
|
97
|
+
|
|
98
|
+
### System Integration
|
|
99
|
+
|
|
100
|
+
| Extension | Description |
|
|
101
|
+
|-----------|-------------|
|
|
102
|
+
| `mac-system-theme.ts` | Syncs knightcode theme with macOS dark/light mode |
|
|
103
|
+
|
|
104
|
+
### Resources
|
|
105
|
+
|
|
106
|
+
| Extension | Description |
|
|
107
|
+
|-----------|-------------|
|
|
108
|
+
| `dynamic-resources/` | Loads skills, prompts, and themes using `resources_discover` |
|
|
109
|
+
|
|
110
|
+
### Messages & Communication
|
|
111
|
+
|
|
112
|
+
| Extension | Description |
|
|
113
|
+
|-----------|-------------|
|
|
114
|
+
| `message-renderer.ts` | Custom message rendering with colors and expandable details via `registerMessageRenderer` |
|
|
115
|
+
| `entry-renderer.ts` | TUI-only session entry rendering via `appendEntry` and `registerEntryRenderer` |
|
|
116
|
+
| `debug-provider.ts` | Toggle raw provider stream capture with `/debug-provider` and inspect each assistant message in a TUI-only session entry |
|
|
117
|
+
| `event-bus.ts` | Inter-extension communication via `knightcode.events` |
|
|
118
|
+
|
|
119
|
+
### Session Metadata
|
|
120
|
+
|
|
121
|
+
| Extension | Description |
|
|
122
|
+
|-----------|-------------|
|
|
123
|
+
| `session-name.ts` | Name sessions for the session selector via `setSessionName` |
|
|
124
|
+
| `bookmark.ts` | Bookmark entries with labels for `/tree` navigation via `setLabel` |
|
|
125
|
+
|
|
126
|
+
### Custom Providers
|
|
127
|
+
|
|
128
|
+
| Extension | Description |
|
|
129
|
+
|-----------|-------------|
|
|
130
|
+
| `custom-provider-anthropic/` | Custom Anthropic provider with OAuth support and custom streaming implementation |
|
|
131
|
+
| `custom-provider-gitlab-duo/` | GitLab Duo provider using @knightcode/ai's built-in Anthropic/OpenAI streaming via proxy |
|
|
132
|
+
| `jev-router.ts` | Virtual model via `registerVirtualModel` that plans on Codex Sol or Terra (picked by the Jev classifier) and switches to Luna after the first edit |
|
|
133
|
+
|
|
134
|
+
### External Dependencies
|
|
135
|
+
|
|
136
|
+
| Extension | Description |
|
|
137
|
+
|-----------|-------------|
|
|
138
|
+
| `with-deps/` | Extension with its own package.json and dependencies (demonstrates jiti module resolution) |
|
|
139
|
+
| `file-trigger.ts` | Watches a trigger file and injects contents into conversation |
|
|
140
|
+
|
|
141
|
+
## Writing Extensions
|
|
142
|
+
|
|
143
|
+
See [docs/extensions.md](../../docs/extensions.md) for full documentation.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import type { ExtensionAPI } from "@knightcodeai/cli";
|
|
147
|
+
import { Type } from "typebox";
|
|
148
|
+
|
|
149
|
+
export default function (knightcode: ExtensionAPI) {
|
|
150
|
+
// Subscribe to lifecycle events
|
|
151
|
+
knightcode.on("tool_call", async (event, ctx) => {
|
|
152
|
+
if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) {
|
|
153
|
+
const ok = await ctx.ui.confirm("Dangerous!", "Allow rm -rf?");
|
|
154
|
+
if (!ok) return { block: true, reason: "Blocked by user" };
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Register custom tools
|
|
159
|
+
knightcode.registerTool({
|
|
160
|
+
name: "greet",
|
|
161
|
+
label: "Greeting",
|
|
162
|
+
description: "Generate a greeting",
|
|
163
|
+
parameters: Type.Object({
|
|
164
|
+
name: Type.String({ description: "Name to greet" }),
|
|
165
|
+
}),
|
|
166
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
167
|
+
return {
|
|
168
|
+
content: [{ type: "text", text: `Hello, ${params.name}!` }],
|
|
169
|
+
details: {},
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Register commands
|
|
175
|
+
knightcode.registerCommand("hello", {
|
|
176
|
+
description: "Say hello",
|
|
177
|
+
handler: async (args, ctx) => {
|
|
178
|
+
ctx.ui.notify("Hello!", "info");
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Key Patterns
|
|
185
|
+
|
|
186
|
+
**Use StringEnum for string parameters** (required for Google API compatibility):
|
|
187
|
+
```typescript
|
|
188
|
+
import { StringEnum } from "@knightcode/ai";
|
|
189
|
+
|
|
190
|
+
// Good
|
|
191
|
+
action: StringEnum(["list", "add"] as const)
|
|
192
|
+
|
|
193
|
+
// Bad - doesn't work with Google
|
|
194
|
+
action: Type.Union([Type.Literal("list"), Type.Literal("add")])
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**State persistence via details:**
|
|
198
|
+
```typescript
|
|
199
|
+
// Store state in tool result details for proper forking support
|
|
200
|
+
return {
|
|
201
|
+
content: [{ type: "text", text: "Done" }],
|
|
202
|
+
details: { todos: [...todos], nextId }, // Persisted in session
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Reconstruct on session events
|
|
206
|
+
knightcode.on("session_start", async (_event, ctx) => {
|
|
207
|
+
for (const entry of ctx.sessionManager.getBranch()) {
|
|
208
|
+
if (entry.type === "message" && entry.message.toolName === "my_tool") {
|
|
209
|
+
const details = entry.message.details;
|
|
210
|
+
// Reconstruct state from details
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
```
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Commit on Exit Extension
|
|
3
|
+
*
|
|
4
|
+
* Automatically commits changes when the agent exits.
|
|
5
|
+
* Uses the last assistant message to generate a commit message.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ExtensionAPI } from "@knightcodeai/cli";
|
|
9
|
+
|
|
10
|
+
export default function (knightcode: ExtensionAPI) {
|
|
11
|
+
knightcode.on("session_shutdown", async (event, ctx) => {
|
|
12
|
+
// Only commit when the process is really exiting. session_shutdown also fires when the
|
|
13
|
+
// session is replaced (reload/new/resume/fork), and those must not create commits.
|
|
14
|
+
if (event.reason !== "quit") return;
|
|
15
|
+
|
|
16
|
+
// Check for uncommitted changes
|
|
17
|
+
const { stdout: status, code } = await knightcode.exec("git", ["status", "--porcelain"]);
|
|
18
|
+
|
|
19
|
+
if (code !== 0 || status.trim().length === 0) {
|
|
20
|
+
// Not a git repo or no changes
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Find the last assistant message for commit context
|
|
25
|
+
const entries = ctx.sessionManager.getEntries();
|
|
26
|
+
let lastAssistantText = "";
|
|
27
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
28
|
+
const entry = entries[i];
|
|
29
|
+
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
30
|
+
const content = entry.message.content;
|
|
31
|
+
if (Array.isArray(content)) {
|
|
32
|
+
lastAssistantText = content
|
|
33
|
+
.filter((c): c is { type: "text"; text: string } => c.type === "text")
|
|
34
|
+
.map((c) => c.text)
|
|
35
|
+
.join("\n");
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Generate a simple commit message
|
|
42
|
+
const firstLine = lastAssistantText.split("\n")[0] || "Work in progress";
|
|
43
|
+
const commitMessage = `[knightcode] ${firstLine.slice(0, 50)}${firstLine.length > 50 ? "..." : ""}`;
|
|
44
|
+
|
|
45
|
+
// Stage and commit. Bail on a failed stage: committing anyway would capture whatever was
|
|
46
|
+
// already staged rather than the work this shutdown was meant to save.
|
|
47
|
+
const { code: addCode } = await knightcode.exec("git", ["add", "-A"]);
|
|
48
|
+
if (addCode !== 0) return;
|
|
49
|
+
const { code: commitCode } = await knightcode.exec("git", ["commit", "-m", commitMessage]);
|
|
50
|
+
|
|
51
|
+
if (commitCode === 0 && ctx.hasUI) {
|
|
52
|
+
ctx.ui.notify(`Auto-committed: ${commitMessage}`, "info");
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash Spawn Hook Example
|
|
3
|
+
*
|
|
4
|
+
* Adjusts command, cwd, and env before execution.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* knightcode -e ./bash-spawn-hook.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ExtensionAPI } from "@knightcodeai/cli";
|
|
11
|
+
import { createBashTool } from "@knightcodeai/cli";
|
|
12
|
+
|
|
13
|
+
export default function (knightcode: ExtensionAPI) {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
|
|
16
|
+
const bashTool = createBashTool(cwd, {
|
|
17
|
+
spawnHook: ({ command, cwd, env }) => ({
|
|
18
|
+
command: `source ~/.profile\n${command}`,
|
|
19
|
+
cwd,
|
|
20
|
+
env: { ...env, KNIGHTCODE_SPAWN_HOOK: "1" },
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
knightcode.registerTool({
|
|
25
|
+
...bashTool,
|
|
26
|
+
execute: async (id, params, signal, onUpdate, _ctx) => {
|
|
27
|
+
return bashTool.execute(id, params, signal, onUpdate);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry bookmarking example.
|
|
3
|
+
*
|
|
4
|
+
* Shows setLabel to mark entries with labels for easy navigation in /tree.
|
|
5
|
+
* Labels appear in the tree view and help you find important points.
|
|
6
|
+
*
|
|
7
|
+
* Usage: /bookmark [label] - bookmark the last assistant message
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ExtensionAPI } from "@knightcodeai/cli";
|
|
11
|
+
|
|
12
|
+
export default function (knightcode: ExtensionAPI) {
|
|
13
|
+
knightcode.registerCommand("bookmark", {
|
|
14
|
+
description: "Bookmark last message (usage: /bookmark [label])",
|
|
15
|
+
handler: async (args, ctx) => {
|
|
16
|
+
const label = args.trim() || `bookmark-${Date.now()}`;
|
|
17
|
+
|
|
18
|
+
// Find the last assistant message entry
|
|
19
|
+
const entries = ctx.sessionManager.getEntries();
|
|
20
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
21
|
+
const entry = entries[i];
|
|
22
|
+
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
23
|
+
knightcode.setLabel(entry.id, label);
|
|
24
|
+
ctx.ui.notify(`Bookmarked as: ${label}`, "info");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
ctx.ui.notify("No assistant message to bookmark", "warning");
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Remove bookmark
|
|
34
|
+
knightcode.registerCommand("unbookmark", {
|
|
35
|
+
description: "Remove bookmark from last labeled entry",
|
|
36
|
+
handler: async (_args, ctx) => {
|
|
37
|
+
const entries = ctx.sessionManager.getEntries();
|
|
38
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
39
|
+
const entry = entries[i];
|
|
40
|
+
const label = ctx.sessionManager.getLabel(entry.id);
|
|
41
|
+
if (label) {
|
|
42
|
+
knightcode.setLabel(entry.id, undefined);
|
|
43
|
+
ctx.ui.notify(`Removed bookmark: ${label}`, "info");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
ctx.ui.notify("No bookmarked entry found", "warning");
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
2
|
+
import { CustomEditor, type ExtensionAPI, type ExtensionContext, type KeybindingsManager } from "@knightcodeai/cli";
|
|
3
|
+
import type { Component, EditorTheme, TUI } from "@knightcode/tui";
|
|
4
|
+
import { truncateToWidth, visibleWidth } from "@knightcode/tui";
|
|
5
|
+
|
|
6
|
+
function fitBorder(
|
|
7
|
+
left: string,
|
|
8
|
+
right: string,
|
|
9
|
+
width: number,
|
|
10
|
+
border: (text: string) => string,
|
|
11
|
+
fill: (text: string) => string = border,
|
|
12
|
+
): string {
|
|
13
|
+
if (width <= 0) return "";
|
|
14
|
+
if (width === 1) return border("─");
|
|
15
|
+
|
|
16
|
+
let leftText = left;
|
|
17
|
+
let rightText = right;
|
|
18
|
+
const fixedWidth = 2;
|
|
19
|
+
const minimumGap = 3;
|
|
20
|
+
|
|
21
|
+
while (
|
|
22
|
+
fixedWidth + visibleWidth(leftText) + visibleWidth(rightText) + minimumGap > width &&
|
|
23
|
+
visibleWidth(rightText) > 0
|
|
24
|
+
) {
|
|
25
|
+
rightText = truncateToWidth(rightText, Math.max(0, visibleWidth(rightText) - 1), "");
|
|
26
|
+
}
|
|
27
|
+
while (
|
|
28
|
+
fixedWidth + visibleWidth(leftText) + visibleWidth(rightText) + minimumGap > width &&
|
|
29
|
+
visibleWidth(leftText) > 0
|
|
30
|
+
) {
|
|
31
|
+
leftText = truncateToWidth(leftText, Math.max(0, visibleWidth(leftText) - 1), "");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const gapWidth = Math.max(0, width - fixedWidth - visibleWidth(leftText) - visibleWidth(rightText));
|
|
35
|
+
return `${border("─")}${leftText}${fill("─".repeat(gapWidth))}${rightText}${border("─")}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function formatCwd(cwd: string): string {
|
|
39
|
+
// HOME is commonly unset on Windows. A prefix test would also rewrite a sibling directory such
|
|
40
|
+
// as /home/aliceworkspace when HOME is /home/alice, and would miss C:\Users vs c:\users on
|
|
41
|
+
// Windows, where paths are case-insensitive; relative() normalizes both away.
|
|
42
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
43
|
+
if (!home) return cwd;
|
|
44
|
+
const rest = relative(resolve(home), resolve(cwd));
|
|
45
|
+
if (rest === ".." || rest.startsWith(`..${sep}`) || isAbsolute(rest)) return cwd;
|
|
46
|
+
return rest === "" ? "~" : `~${sep}${rest}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function formatContext(ctx: ExtensionContext): string {
|
|
50
|
+
const usage = ctx.getContextUsage();
|
|
51
|
+
const contextWindow = usage?.contextWindow ?? ctx.model?.contextWindow;
|
|
52
|
+
if (!contextWindow || !usage || usage.percent === null) {
|
|
53
|
+
return "ctx ?";
|
|
54
|
+
}
|
|
55
|
+
return `ctx ${Math.round(usage.percent)}%/${(contextWindow / 1000).toFixed(0)}k`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function formatThinking(level: string): string {
|
|
59
|
+
return level === "off" ? "off" : level;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
class EmptyFooter implements Component {
|
|
63
|
+
render(): string[] {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
invalidate(): void {}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default function (knightcode: ExtensionAPI) {
|
|
71
|
+
let isWorking = false;
|
|
72
|
+
let spinnerIndex = 0;
|
|
73
|
+
let spinnerTimer: ReturnType<typeof setInterval> | undefined;
|
|
74
|
+
let activeTui: TUI | undefined;
|
|
75
|
+
const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
76
|
+
|
|
77
|
+
const stopSpinner = () => {
|
|
78
|
+
if (spinnerTimer) {
|
|
79
|
+
clearInterval(spinnerTimer);
|
|
80
|
+
spinnerTimer = undefined;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
knightcode.on("agent_start", () => {
|
|
85
|
+
isWorking = true;
|
|
86
|
+
stopSpinner();
|
|
87
|
+
spinnerTimer = setInterval(() => {
|
|
88
|
+
spinnerIndex = (spinnerIndex + 1) % spinnerFrames.length;
|
|
89
|
+
activeTui?.requestRender();
|
|
90
|
+
}, 80);
|
|
91
|
+
activeTui?.requestRender();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
knightcode.on("agent_settled", () => {
|
|
95
|
+
isWorking = false;
|
|
96
|
+
stopSpinner();
|
|
97
|
+
activeTui?.requestRender();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
knightcode.on("session_shutdown", () => {
|
|
101
|
+
stopSpinner();
|
|
102
|
+
activeTui = undefined;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
knightcode.on("session_start", (_event, ctx) => {
|
|
106
|
+
ctx.ui.setWorkingVisible(false);
|
|
107
|
+
ctx.ui.setFooter(() => new EmptyFooter());
|
|
108
|
+
|
|
109
|
+
let branch: string | undefined;
|
|
110
|
+
|
|
111
|
+
const refreshBranch = async () => {
|
|
112
|
+
const result = await knightcode
|
|
113
|
+
.exec("git", ["branch", "--show-current"], { cwd: ctx.cwd })
|
|
114
|
+
.catch(() => undefined);
|
|
115
|
+
const stdout = result?.stdout.trim();
|
|
116
|
+
branch = stdout && stdout.length > 0 ? stdout : undefined;
|
|
117
|
+
activeTui?.requestRender();
|
|
118
|
+
};
|
|
119
|
+
void refreshBranch();
|
|
120
|
+
|
|
121
|
+
class BorderStatusEditor extends CustomEditor {
|
|
122
|
+
constructor(tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) {
|
|
123
|
+
super(tui, theme, keybindings, { paddingX: 0 });
|
|
124
|
+
activeTui = tui;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
render(width: number): string[] {
|
|
128
|
+
const lines = super.render(width);
|
|
129
|
+
if (lines.length < 2) return lines;
|
|
130
|
+
|
|
131
|
+
const thm = ctx.ui.theme;
|
|
132
|
+
const model = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "no model";
|
|
133
|
+
const thinking = knightcode.getThinkingLevel();
|
|
134
|
+
const topLeft = isWorking ? thm.fg("accent", ` ${spinnerFrames[spinnerIndex]} `) : "";
|
|
135
|
+
const topRight = "";
|
|
136
|
+
const bottomLeft = thm.fg("muted", ` ${model} · ${formatThinking(thinking)} `);
|
|
137
|
+
const bottomRight = thm.fg(
|
|
138
|
+
"muted",
|
|
139
|
+
` ${formatContext(ctx)} · ${formatCwd(ctx.cwd)}${branch ? ` (${branch})` : ""} `,
|
|
140
|
+
);
|
|
141
|
+
const borderColor = (text: string) => this.borderColor(text);
|
|
142
|
+
|
|
143
|
+
lines[0] = fitBorder(topLeft, topRight, width, borderColor);
|
|
144
|
+
lines[lines.length - 1] = fitBorder(bottomLeft, bottomRight, width, borderColor);
|
|
145
|
+
return lines;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
ctx.ui.setEditorComponent((tui, theme, keybindings) => new BorderStatusEditor(tui, theme, keybindings));
|
|
150
|
+
});
|
|
151
|
+
}
|