You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TypeScript/QuickJS extension runtime for user-defined tools, commands, and hooks
Summary
Add a first-class extension runtime so users can extend jcode with TypeScript/JavaScript modules without forking the codebase. Extensions register custom tools, slash commands, keybindings, event hooks, providers, status-line widgets, and UI components. The runtime is embedded (QuickJS) — no Node, no Bun, no V8 — so it stays single-binary and starts in <100 ms.
Self-dev is powerful but per-user, not shareable. Extensions are shareable artifacts.
This unblocks: plan mode, custom compaction, permission gates, status lines, sub-agent helpers, MCP-as-extension, custom editors, third-party provider experimentation.
Current state in jcode
No QuickJS / JS / TS runtime is embedded. Grep finds zero matches for QuickJS, quickjs, plugin_runtime, extension_runtime in src/.
src/tool/skill.rs loads Markdown skills only.
MCP exists for tools, but MCP can't register slash commands, hotkeys, or UI hooks — only tools.
Implementation checklist
1. Pick a runtime
Use rquickjs (Rust bindings to QuickJS), feature-gated behind extensions. Rationale: single-file embed, ~700 KB binary cost, no system Node dependency, mature ECMAScript 2020 support. Native AOT is out of scope.
Add to a new crate crates/jcode-extensions/ (lib + runtime + hostcall mux).
Allow default export to be async — host awaits it before the first prompt is accepted.
5. Lifecycle and reactor
Lazy load: extensions are loaded on first use, not at startup. Cold load target: P95 < 100 ms (matches pi_agent_rust).
Warm pool: reuse one isolate per extension across calls. Hot reload on file change in dev mode (gated by JCODE_EXT_DEV=1).
Bounded queue for hostcalls with backpressure (mirror pi_agent_rust's reactor mesh, but simpler v1: single shard).
6. Trust lifecycle (see separate issue)
Track per-extension trust state (pending / acknowledged / trusted / killed). First-run extensions sit in pending and ask the user to acknowledge capabilities. Killed extensions never load.
Audit log under ~/.jcode/logs/extensions.jsonl.
7. Built-in extension examples
Port two small extensions as in-tree examples in examples/extensions/:
Drop the example plan-mode/ extension into ~/.jcode/extensions/; verify /plan works.
Drop a malicious try-to-read-/etc/shadow.js; verify safe profile blocks the read and emits an audit log entry.
Acceptance criteria
cargo build --release --features extensions produces a binary under the existing 22 MiB ceiling reported in AGENTS.md (or document the new size budget in the same file).
Two in-tree example extensions load and register tools/commands without warnings.
jcode ext list shows their state.
Capability denial returns a structured error to the extension and an audit log line.
All new unit and conformance tests pass under cargo test --features extensions.
cargo clippy --all-targets --features extensions -- -D warnings is clean.
No QuickJS/JS runtime in tree. rg -i 'quickjs|rquickjs|extension_runtime' src crates returns no matches.
Closest existing extensibility: src/tool/skill.rs (Markdown skills, semantic-vector-loaded) and src/tool/mcp.rs + src/mcp/ (MCP client for tools).
Slash commands are currently registered manually in src/tui/ui_overlays.rs and dispatched in src/tui/app/commands.rs — the extension API must thread through both.
Provider registry is in src/provider/ and crates/jcode-provider-* — extension-provided providers will need a stream-simple bridge similar to pi_agent_rust's src/providers/mod.rs.
Scope expansion (must-have before merging this issue)
The original checklist covers the runtime + manifest + capability bucket. Devin's review flags four additional must-have-before-shipping items that pi_agent_rust treats as part of the extension runtime, not as separate features:
Secret-aware env filter for pi.env() / jcode.env() — block any key matching *KEY*, *TOKEN*, *SECRET*, *PASSWORD*, *COOKIE* from the extension-visible env by default. Add an --ext-env-allow=NAME flag for opt-in unblocking. Reference: pi_agent_rust Feature Superset Highlights → "Secret-aware extension env filtering".
Hostcall taxonomy with stable error codes — every hostcall must return one of ok | timeout | denied | io | invalid_request | internal. This makes extension errors actionable and lets jcode doctor (Add general-purpose jcode doctor diagnostic command #8) diagnose them. Reference: pi_agent_rust → "Unified hostcall dispatcher with typed taxonomy mapping".
Preflight static analysis — before loading an extension for the first time, scan its entrypoint for forbidden imports (fs, child_process, net, crypto.subtle.importKey) that should be hostcalls instead, and reject obvious red flags (eval, Function(...), postMessage to unknown targets). Reference: pi_agent_rust → "Extension preflight static analysis".
Hostcall compatibility lane forced-compat switch — a single CLI flag and per-extension setting to force the extension into a slower "compat" lane for emergency rollback if a fast-path bug surfaces. Reference: pi_agent_rust → "Hostcall compatibility-lane emergency controls".
If shipping all four in one PR is too large, consider splitting #3 into:
TypeScript/QuickJS extension runtime for user-defined tools, commands, and hooks
Summary
Add a first-class extension runtime so users can extend jcode with TypeScript/JavaScript modules without forking the codebase. Extensions register custom tools, slash commands, keybindings, event hooks, providers, status-line widgets, and UI components. The runtime is embedded (QuickJS) — no Node, no Bun, no V8 — so it stays single-binary and starts in <100 ms.
References:
pi_agent_rust: Extensions, Extension Hostcall ProtocolWhy
src/tool/skill.rs) are good for prompt-style capabilities but cannot register new tools, commands, hotkeys, or replace built-in behavior. PRs like [Upstream #204] 完全无法使用,没法配置第三方模型 #151 and feat(mcp): require explicit trust for project-local configs (#62) #209 keep proposing "another in-tree thing" because there is no extension surface.Current state in jcode
QuickJS,quickjs,plugin_runtime,extension_runtimeinsrc/.src/tool/skill.rsloads Markdown skills only.Implementation checklist
1. Pick a runtime
rquickjs(Rust bindings to QuickJS), feature-gated behindextensions. Rationale: single-file embed, ~700 KB binary cost, no system Node dependency, mature ECMAScript 2020 support. Native AOT is out of scope.crates/jcode-extensions/(lib + runtime + hostcall mux).2. Capability-gated hostcall protocol
tool,exec,http,session,ui,events.pi.json/jcode.json). Missing caps = the host returnsEPERMsynchronously.safe(default),balanced,permissive(mirror pi_agent_rust's--extension-policy).3. Extension manifest + discovery
$JCODE_EXT_PATH(colon-separated).jcode/extensions/(project)~/.jcode/extensions/(user)jcode install npm:.../jcode install git:...packages (see separate issue){ "name": "my-ext", "version": "0.1.0", "main": "./dist/extension.js", "jcode": { "capabilities": ["tool", "session", "ui"], "minJcodeVersion": "0.13" } }mainrelative to the manifest. Reject paths that escape the extension root.4. Public TypeScript API
crates/jcode-extensions/types/jcode.d.tsso users get autocomplete:default exportto be async — host awaits it before the first prompt is accepted.5. Lifecycle and reactor
JCODE_EXT_DEV=1).6. Trust lifecycle (see separate issue)
pending/acknowledged/trusted/killed). First-run extensions sit inpendingand ask the user to acknowledge capabilities. Killed extensions never load.~/.jcode/logs/extensions.jsonl.7. Built-in extension examples
examples/extensions/:plan-mode/: registers/planand writes to aPLAN.mdfile (replaces the in-tree request in [Upstream PR #116] fix: accept stringified ambient cycle counts #96).git-checkpoint/: registers a pre-tool-call hook that snapshots dirty files beforebash/edit.8. CLI surface
jcode ext list,jcode ext info <name>,jcode ext run <name> --help,jcode ext disable <name>,jcode ext enable <name>.jcode doctor(see separate issue) reports loaded extensions, capability profile, and trust state.Testing
Conformance fixtures
tests/extensions/fixtures/*.json) mirroring pi_agent_rust's conformance harness. Each fixture: setup files → load extension → assert tool/command/hook outcomes → assert no capability escapes.Cold/warm load benchmarks
benches/extension_load.rs(criterion). Gate onP95 cold load < 100 ms,P99 warm load < 1 ms.Manual smoke
plan-mode/extension into~/.jcode/extensions/; verify/planworks.try-to-read-/etc/shadow.js; verifysafeprofile blocks the read and emits an audit log entry.Acceptance criteria
cargo build --release --features extensionsproduces a binary under the existing 22 MiB ceiling reported inAGENTS.md(or document the new size budget in the same file).jcode ext listshows their state.cargo test --features extensions.cargo clippy --all-targets --features extensions -- -D warningsis clean.References
pi_agent_rust: Extension Runtime Decision Logic, Hostcall ProtocolTracking: physically split into sub-issues
This issue is now an umbrella. Concrete work lives in:
Land in order. Close this issue when all three sub-issues are closed.
Implementation notes addendum (Devin gap-analysis pass, 2026-05-21)
Verified jcode code paths
rg -i 'quickjs|rquickjs|extension_runtime' src cratesreturns no matches.src/tool/skill.rs(Markdown skills, semantic-vector-loaded) andsrc/tool/mcp.rs+src/mcp/(MCP client for tools).src/tui/ui_overlays.rsand dispatched insrc/tui/app/commands.rs— the extension API must thread through both.src/provider/andcrates/jcode-provider-*— extension-provided providers will need a stream-simple bridge similar to pi_agent_rust'ssrc/providers/mod.rs.Scope expansion (must-have before merging this issue)
The original checklist covers the runtime + manifest + capability bucket. Devin's review flags four additional must-have-before-shipping items that pi_agent_rust treats as part of the extension runtime, not as separate features:
pi.env()/jcode.env()— block any key matching*KEY*,*TOKEN*,*SECRET*,*PASSWORD*,*COOKIE*from the extension-visible env by default. Add an--ext-env-allow=NAMEflag for opt-in unblocking. Reference: pi_agent_rust Feature Superset Highlights → "Secret-aware extension env filtering".ok | timeout | denied | io | invalid_request | internal. This makes extension errors actionable and letsjcode doctor(Add general-purposejcode doctordiagnostic command #8) diagnose them. Reference: pi_agent_rust → "Unified hostcall dispatcher with typed taxonomy mapping".fs,child_process,net,crypto.subtle.importKey) that should be hostcalls instead, and reject obvious red flags (eval,Function(...), postMessage to unknown targets). Reference: pi_agent_rust → "Extension preflight static analysis".If shipping all four in one PR is too large, consider splitting #3 into:
Cross-references
--extension-policy safe|balanced|permissive+ command-level exec mediation #14 Extension capability policy (--extension-policy safe|balanced|permissive) — depends on this issue.jcode install npm:... | git:... | https://...for sharing skills/prompts/themes/extensions #6 Package system — distributes extensions/skills/prompts/themes./name#4 Prompt templates and User themes with hot reload #5 themes — extensions can also register these resources; coordinate the API.ToolSpec/CommandSpectypes./settingsUI for config inspection and edits #7/settingsUI — should list active extensions and let users toggle them.Reference URLs