Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/create-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ on:
- email
- harness
- iii-directory
- iii-lsp
- lsp
- iii-lsp-vscode
- image-resize
- mcp
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
- 'email/v*'
- 'harness/v*'
- 'iii-directory/v*'
- 'iii-lsp/v*'
- 'lsp/v*'
- 'image-resize/v*'
- 'mcp/v*'
- 'shell/v*'
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ npx skills add iii-hq/iii --all
| [`harness`](harness/) | Node | TS port of the iii harness stack — bundles `harness` (provider registry + credentials/settings/permissions via the `configuration` worker), `turn-orchestrator`, `approval-gate`, `session`, `hook-fanout`, `models-catalog`, the `provider-*` workers, `llm-budget`, and `context-compaction` as one pnpm monorepo. See [`harness/README.md`](harness/README.md). |
| [`database`](database/) | Rust | PostgreSQL, MySQL, and SQLite client — query, execute, transactions, prepared statements, and change feeds. |
| [`iii-directory`](iii-directory/) | Rust | Engine introspection (functions / triggers / workers), workers-registry proxy, and filesystem-backed skill + prompt reader. |
| [`iii-lsp`](iii-lsp/) | Rust | Language Server for iii function ids, trigger configs, and worker discovery. Autocomplete / hover across JS/TS, Python, Rust. |
| [`iii-lsp-vscode`](iii-lsp-vscode/) | Node | VS Code extension that embeds `iii-lsp`. |
| [`lsp`](lsp/) | Rust | Language Server for iii function ids, trigger configs, and worker discovery. Autocomplete / hover across JS/TS, Python, Rust. |
| [`iii-lsp-vscode`](iii-lsp-vscode/) | Node | VS Code extension that embeds the `lsp` server. |
| [`image-resize`](image-resize/) | Rust | Image resize via channel I/O — JPEG/PNG/WebP with EXIF auto-orient, scale-to-fit / crop-to-fit. |
| [`llm-budget`](llm-budget/) | Rust | Workspace + agent LLM spend caps with alerts, forecast, and period rollover under `budget::*`. |
| [`mcp`](mcp/) | Rust | MCP 2025-06-18 Streamable HTTP bridge — exposes iii functions tagged `mcp.expose` as MCP tools. |
Expand Down
30 changes: 29 additions & 1 deletion iii-lsp-vscode/extension.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,38 @@
const vscode = require("vscode");
const fs = require("node:fs");
const path = require("node:path");
const { LanguageClient, TransportKind } = require("vscode-languageclient/node");

const { ensureServerBinary } = require("./installer");

let client;

// Resolve the server binary on PATH, preferring the new `lsp` name and
// falling back to the legacy `iii-lsp` name for older installs.
function resolvePathBinary() {
const candidates = ["lsp", "iii-lsp"];
const pathEntries = (process.env.PATH || "").split(path.delimiter).filter(Boolean);
const exts = process.platform === "win32"
? (process.env.PATHEXT || ".EXE;.CMD;.BAT").split(";")
: [""];
for (const candidate of candidates) {
for (const dir of pathEntries) {
for (const ext of exts) {
const full = path.join(dir, candidate + ext);
try {
if (fs.statSync(full).isFile()) {
return full;
}
} catch {
// not here, keep looking
}
}
}
}
// Nothing on PATH; default to the new name and let the OS resolve it.
return candidates[0];
}

async function activate(context) {
const config = vscode.workspace.getConfiguration("iii-lsp");
const engineUrl = config.get("engineUrl") || "ws://127.0.0.1:49134";
Expand All @@ -17,7 +45,7 @@ async function activate(context) {
vscode.window.showWarningMessage(
`Failed to install iii-lsp binary. Falling back to configured path or PATH lookup. ${message}`
);
serverPath = config.get("serverPath") || "iii-lsp";
serverPath = config.get("serverPath") || resolvePathBinary();
}

const serverOptions = {
Expand Down
38 changes: 19 additions & 19 deletions iii-lsp/Cargo.lock → lsp/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions iii-lsp/Cargo.toml → lsp/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
[package]
name = "iii-lsp"
name = "lsp"
version = "0.1.2"
edition = "2021"
publish = false

[lib]
name = "iii_lsp"
name = "lsp"
path = "src/lib.rs"

[[bin]]
name = "iii-lsp"
name = "lsp"
path = "src/main.rs"

[dependencies]
Expand Down
8 changes: 4 additions & 4 deletions iii-lsp/README.md → lsp/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# iii-lsp
# lsp

Language Server Protocol implementation for the [iii engine](https://github.com/iii-hq/iii). Provides autocompletion, hover documentation, and diagnostics for iii function calls and trigger registrations directly inside any LSP-capable editor.

Expand Down Expand Up @@ -26,7 +26,7 @@ When the engine is not reachable on startup, the server stays up and returns emp

```bash
cargo build --release
./target/release/iii-lsp --url ws://127.0.0.1:49134
./target/release/lsp --url ws://127.0.0.1:49134
```

The binary speaks LSP over stdio; spawn it from your editor's LSP client.
Expand All @@ -51,15 +51,15 @@ Use the bundled VS Code extension, which downloads the matching `iii-lsp` binary

```lua
vim.lsp.config.iii = {
cmd = { '/path/to/iii-lsp', '--url', 'ws://127.0.0.1:49134' },
cmd = { '/path/to/lsp', '--url', 'ws://127.0.0.1:49134' },
filetypes = { 'typescript', 'typescriptreact', 'javascript', 'python', 'rust' },
}
vim.lsp.enable('iii')
```

### Any LSP client

Configure the client to launch the `iii-lsp` binary over stdio for the supported filetypes above. No initialization options are required.
Configure the client to launch the `lsp` binary over stdio for the supported filetypes above. No initialization options are required.

## See also

Expand Down
6 changes: 3 additions & 3 deletions iii-lsp/iii.worker.yaml → lsp/iii.worker.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
iii: v1
name: iii-lsp
name: lsp
language: rust
deploy: binary
manifest: Cargo.toml
bin: iii-lsp
bin: lsp
description: III Language Server — autocompletion and hover for III engine functions and triggers
# Opt out of the interface boot smoke. iii-lsp is a stdio language server:
# Opt out of the interface boot smoke. lsp is a stdio language server:
# it exits on stdin EOF (so it cannot stay alive while backgrounded) and it
# only consumes engine discovery — it registers no functions or trigger
# types, so there is no interface to collect.
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
18 changes: 9 additions & 9 deletions iii-lsp/src/diagnostics.rs → lsp/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ fn check_trigger_call(
diagnostics.push(Diagnostic {
range: call.function_id_range,
severity: Some(DiagnosticSeverity::HINT),
source: Some("iii-lsp".to_string()),
source: Some("lsp".to_string()),
message: format!(
"Function ID '{}' should use namespace format 'namespace::name'",
call.function_id
Expand All @@ -530,7 +530,7 @@ fn check_trigger_call(
diagnostics.push(Diagnostic {
range: call.function_id_range,
severity: Some(DiagnosticSeverity::WARNING),
source: Some("iii-lsp".to_string()),
source: Some("lsp".to_string()),
message: format!("Unknown function '{}'", call.function_id),
..Default::default()
});
Expand All @@ -555,7 +555,7 @@ fn check_trigger_call(
diagnostics.push(Diagnostic {
range: call.payload_range,
severity: Some(DiagnosticSeverity::ERROR),
source: Some("iii-lsp".to_string()),
source: Some("lsp".to_string()),
message: format!(
"Missing required property '{}' for '{}'",
name, call.function_id
Expand All @@ -579,7 +579,7 @@ fn check_register_trigger_call(
diagnostics.push(Diagnostic {
range: call.function_id_range,
severity: Some(DiagnosticSeverity::HINT),
source: Some("iii-lsp".to_string()),
source: Some("lsp".to_string()),
message: format!(
"Function ID '{}' should use namespace format 'namespace::name'",
call.function_id
Expand All @@ -592,7 +592,7 @@ fn check_register_trigger_call(
diagnostics.push(Diagnostic {
range: call.function_id_range,
severity: Some(DiagnosticSeverity::WARNING),
source: Some("iii-lsp".to_string()),
source: Some("lsp".to_string()),
message: format!("Unknown function '{}'", call.function_id),
..Default::default()
});
Expand All @@ -602,7 +602,7 @@ fn check_register_trigger_call(
diagnostics.push(Diagnostic {
range: call.trigger_type_range,
severity: Some(DiagnosticSeverity::WARNING),
source: Some("iii-lsp".to_string()),
source: Some("lsp".to_string()),
message: format!("Unknown trigger type '{}'", call.trigger_type),
..Default::default()
});
Expand All @@ -622,7 +622,7 @@ fn check_register_trigger_call(
diagnostics.push(Diagnostic {
range: call.config_range,
severity: Some(DiagnosticSeverity::ERROR),
source: Some("iii-lsp".to_string()),
source: Some("lsp".to_string()),
message: format!(
"Missing required config property '{}' for trigger type '{}'",
name, call.trigger_type
Expand All @@ -642,7 +642,7 @@ fn check_register_trigger_call(
diagnostics.push(Diagnostic {
range: call.config_range,
severity: Some(DiagnosticSeverity::WARNING),
source: Some("iii-lsp".to_string()),
source: Some("lsp".to_string()),
message: format!(
"Invalid HTTP method '{}'. Expected one of: {}",
value,
Expand All @@ -663,7 +663,7 @@ fn check_register_trigger_call(
diagnostics.push(Diagnostic {
range: call.config_range,
severity: Some(DiagnosticSeverity::ERROR),
source: Some("iii-lsp".to_string()),
source: Some("lsp".to_string()),
message: format!(
"Cron expression must have 6 fields (sec min hour day month weekday), got {}",
fields.len()
Expand Down
2 changes: 1 addition & 1 deletion iii-lsp/src/engine_client.rs → lsp/src/engine_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl EngineClient {
metadata: Some(WorkerMetadata {
runtime: "rust".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
name: "iii-lsp".to_string(),
name: "lsp".to_string(),
os: std::env::consts::OS.to_string(),
pid: Some(std::process::id()),
telemetry: None,
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion iii-lsp/src/lib.rs → lsp/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Library surface for `iii-lsp`.
//! Library surface for `lsp`.
//!
//! The binary (`src/main.rs`) keeps its own `mod` declarations; this lib
//! target re-exports the same modules so integration tests under `tests/`
Expand Down
10 changes: 5 additions & 5 deletions iii-lsp/src/main.rs → lsp/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod engine_introspection;
mod hover;

#[derive(ClapParser, Debug)]
#[command(name = "iii-lsp", about = "Language Server for the III engine")]
#[command(name = "lsp", about = "Language Server for the III engine")]
struct Cli {
/// WebSocket URL of the III engine
#[arg(long, env = "III_URL", default_value = "ws://127.0.0.1:49134")]
Expand Down Expand Up @@ -81,7 +81,7 @@ impl LanguageServer for Backend {
..Default::default()
},
server_info: Some(ServerInfo {
name: "iii-lsp".to_string(),
name: "lsp".to_string(),
version: Some(env!("CARGO_PKG_VERSION").to_string()),
}),
..Default::default()
Expand All @@ -96,7 +96,7 @@ impl LanguageServer for Backend {
.log_message(
MessageType::INFO,
format!(
"iii-lsp: connected to engine ({} functions, {} trigger types)",
"lsp: connected to engine ({} functions, {} trigger types)",
self.engine.functions.len(),
self.engine.trigger_types.len()
),
Expand All @@ -106,7 +106,7 @@ impl LanguageServer for Backend {
self.client
.log_message(
MessageType::WARNING,
"iii-lsp: engine not running, completions will be empty until engine starts",
"lsp: engine not running, completions will be empty until engine starts",
)
.await;
}
Expand Down Expand Up @@ -199,7 +199,7 @@ async fn main() {
.init();

let cli = Cli::parse();
tracing::info!("starting iii-lsp, connecting to {}", cli.url);
tracing::info!("starting lsp, connecting to {}", cli.url);

let engine = engine_client::EngineClient::new(&cli.url);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! so a future engine/SDK bump that drifts the payload fails here instead
//! of silently leaving the LSP caches empty.

use iii_lsp::engine_introspection::{TriggerInfo, TriggerTypeInfo};
use lsp::engine_introspection::{TriggerInfo, TriggerTypeInfo};
use serde_json::json;

/// `engine::triggers::list` returns trigger TYPES:
Expand Down