What: @honeide/api is the public extension API for Hone. It is a pure TypeScript type library — zero runtime code, zero dependencies. Every type, interface, namespace, and event signature that extension authors need lives here.
Why: Decoupling the extension contract from implementation allows extensions to be compiled independently, version the API surface with strict semver, and ensure extensions never import internal IDE code.
Who uses it:
- Extension authors (
import * as hone from '@honeide/api') hone-extensions(all built-in extensions import only this)hone-core(implements the API bridge that fulfills these contracts)hone-ide(wires API implementations to the runtime)
Role in ecosystem: The foundational contract. Layer 0 — no dependencies on any other @honeide/* package.
None. This package has zero @honeide/* dependencies.
None. Zero runtime dependencies. Zero dev dependencies beyond TypeScript itself.
None.
hone-api/
├── src/
│ ├── index.ts # Barrel export with namespaces
│ ├── types.ts # Shared primitives (Disposable, Event, Uri, CancellationToken, etc.)
│ ├── commands.ts # Command registration types
│ ├── editor.ts # TextEditor, TextDocument, Position, Range, Selection
│ ├── workspace.ts # WorkspaceFolder, Configuration, file events
│ ├── ui.ts # TreeDataProvider, WebviewPanel, StatusBarItem, QuickPick, InputBox
│ ├── languages.ts # CompletionProvider, HoverProvider, CodeActionProvider, diagnostics
│ ├── debug.ts # Debug session, breakpoint types
│ ├── terminal.ts # Terminal creation and I/O types
│ └── ai.ts # AIProviderAdapter, AgentToolDefinition, capabilities
│
├── tests/
│ ├── type-checks.ts # Compile-only type assertion tests
│ └── api-surface.test.ts # Ensures all expected exports exist
│
├── package.json # Published as @honeide/api
├── tsconfig.json
├── CHANGELOG.md
└── LICENSE # MIT
/**
* A resource that can be released/cleaned up.
* Extensions return Disposable from register* calls.
*/
export interface Disposable {
dispose(): void;
}
/**
* Typed event emitter pattern. Extensions subscribe via `onDid*` properties.
*/
export interface Event<T> {
(listener: (e: T) => void, thisArgs?: any, disposables?: Disposable[]): Disposable;
}
/**
* Uniform resource identifier for files, untitled docs, etc.
*/
export interface Uri {
readonly scheme: string; // "file", "untitled", "hone-webview", etc.
readonly authority: string;
readonly path: string;
readonly query: string;
readonly fragment: string;
readonly fsPath: string; // Platform-native file path
toString(): string;
}
/** Factory for creating Uri instances */
export namespace Uri {
function file(path: string): Uri;
function parse(value: string): Uri;
function from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): Uri;
}
/**
* Cancellation support for long-running operations.
*/
export interface CancellationToken {
readonly isCancellationRequested: boolean;
readonly onCancellationRequested: Event<void>;
}
export interface CancellationTokenSource {
readonly token: CancellationToken;
cancel(): void;
dispose(): void;
}
/**
* A thenable (promise-like) that the API returns in many places.
*/
export type ProviderResult<T> = T | undefined | null | Thenable<T | undefined | null>;
/**
* Output channel for extension logging.
*/
export interface OutputChannel {
readonly name: string;
append(value: string): void;
appendLine(value: string): void;
clear(): void;
show(preserveFocus?: boolean): void;
hide(): void;
dispose(): void;
}
/**
* Progress reporting.
*/
export interface Progress<T> {
report(value: T): void;
}
export interface ProgressOptions {
location: ProgressLocation;
title?: string;
cancellable?: boolean;
}
export enum ProgressLocation {
SourceControl = 1,
Window = 10,
Notification = 15,
}import { Disposable } from './types';
export namespace commands {
/**
* Register a command handler.
* @param id Unique command identifier (e.g., "myext.doSomething")
* @param handler The function to execute when the command is invoked
* @returns Disposable that unregisters the command
*/
function registerCommand(id: string, handler: (...args: any[]) => any): Disposable;
/**
* Execute a registered command programmatically.
* @param id The command identifier
* @param args Arguments to pass to the command handler
*/
function executeCommand<T = any>(id: string, ...args: any[]): Promise<T>;
/**
* Get all registered command identifiers.
*/
function getCommands(filterInternal?: boolean): Promise<string[]>;
}import { Disposable, Event, Uri, ProviderResult } from './types';
/** Zero-based line and character position in a document */
export interface Position {
readonly line: number;
readonly character: number;
}
export namespace Position {
function create(line: number, character: number): Position;
}
/** A range between two positions */
export interface Range {
readonly start: Position;
readonly end: Position;
readonly isEmpty: boolean;
readonly isSingleLine: boolean;
contains(positionOrRange: Position | Range): boolean;
}
export namespace Range {
function create(start: Position, end: Position): Range;
function create(startLine: number, startChar: number, endLine: number, endChar: number): Range;
}
/** A selection with anchor (where selection started) and active (cursor) positions */
export interface Selection extends Range {
readonly anchor: Position;
readonly active: Position;
readonly isReversed: boolean;
}
/** A text edit (insert, delete, or replace) */
export interface TextEdit {
readonly range: Range;
readonly newText: string;
}
export namespace TextEdit {
function insert(position: Position, newText: string): TextEdit;
function delete_(range: Range): TextEdit;
function replace(range: Range, newText: string): TextEdit;
}
/** Read-only view of a text document */
export interface TextDocument {
readonly uri: Uri;
readonly fileName: string;
readonly languageId: string;
readonly version: number;
readonly isDirty: boolean;
readonly isUntitled: boolean;
readonly lineCount: number;
readonly eol: EndOfLine;
readonly encoding: string;
getText(range?: Range): string;
getWordRangeAtPosition(position: Position, regex?: RegExp): Range | undefined;
lineAt(line: number): TextLine;
lineAt(position: Position): TextLine;
offsetAt(position: Position): number;
positionAt(offset: number): Position;
validatePosition(position: Position): Position;
validateRange(range: Range): Range;
}
export interface TextLine {
readonly lineNumber: number;
readonly text: string;
readonly range: Range;
readonly rangeIncludingLineBreak: Range;
readonly firstNonWhitespaceCharacterIndex: number;
readonly isEmptyOrWhitespace: boolean;
}
export enum EndOfLine {
LF = 1,
CRLF = 2,
}
/** An editor tab showing a document */
export interface TextEditor {
readonly document: TextDocument;
selection: Selection;
selections: Selection[];
readonly visibleRanges: Range[];
options: TextEditorOptions;
edit(callback: (editBuilder: TextEditorEdit) => void): Promise<boolean>;
insertSnippet(snippet: SnippetString, location?: Position | Range | readonly Position[] | readonly Range[]): Promise<boolean>;
setDecorations(decorationType: TextEditorDecorationType, rangesOrOptions: Range[] | DecorationOptions[]): void;
revealRange(range: Range, revealType?: TextEditorRevealType): void;
}
export interface TextEditorEdit {
insert(location: Position, value: string): void;
delete_(location: Range): void;
replace(location: Range | Position | Selection, value: string): void;
}
export interface TextEditorOptions {
tabSize?: number | string;
insertSpaces?: boolean | string;
cursorStyle?: TextEditorCursorStyle;
lineNumbers?: TextEditorLineNumbersStyle;
}
export enum TextEditorCursorStyle {
Line = 1,
Block = 2,
Underline = 3,
LineThin = 4,
BlockOutline = 5,
UnderlineThin = 6,
}
export enum TextEditorLineNumbersStyle {
Off = 0,
On = 1,
Relative = 2,
}
export enum TextEditorRevealType {
Default = 0,
InCenter = 1,
InCenterIfOutsideViewport = 2,
AtTop = 3,
}
export interface SnippetString {
readonly value: string;
}
export interface TextEditorDecorationType {
readonly key: string;
dispose(): void;
}
export interface DecorationOptions {
range: Range;
hoverMessage?: string;
renderOptions?: DecorationRenderOptions;
}
export interface DecorationRenderOptions {
backgroundColor?: string;
border?: string;
color?: string;
fontStyle?: string;
fontWeight?: string;
textDecoration?: string;
opacity?: string;
after?: ThemableDecorationAttachmentRenderOptions;
before?: ThemableDecorationAttachmentRenderOptions;
}
export interface ThemableDecorationAttachmentRenderOptions {
contentText?: string;
color?: string;
backgroundColor?: string;
fontStyle?: string;
fontWeight?: string;
}import { Disposable, Event, Uri } from './types';
import { TextDocument, TextEdit } from './editor';
export interface WorkspaceFolder {
readonly uri: Uri;
readonly name: string;
readonly index: number;
}
export interface Configuration {
get<T>(key: string): T | undefined;
get<T>(key: string, defaultValue: T): T;
has(key: string): boolean;
update(key: string, value: any, global?: boolean): Promise<void>;
}
export interface TextDocumentChangeEvent {
readonly document: TextDocument;
readonly contentChanges: readonly TextDocumentContentChangeEvent[];
readonly reason?: TextDocumentChangeReason;
}
export interface TextDocumentContentChangeEvent {
readonly range: Range;
readonly rangeOffset: number;
readonly rangeLength: number;
readonly text: string;
}
export enum TextDocumentChangeReason {
Undo = 1,
Redo = 2,
}
export interface FileSystemWatcher extends Disposable {
readonly onDidCreate: Event<Uri>;
readonly onDidChange: Event<Uri>;
readonly onDidDelete: Event<Uri>;
}
export interface WorkspaceEdit {
set(uri: Uri, edits: TextEdit[]): void;
has(uri: Uri): boolean;
entries(): [Uri, TextEdit[]][];
readonly size: number;
}
export namespace workspace {
/** Currently open workspace folders */
const workspaceFolders: readonly WorkspaceFolder[] | undefined;
/** Open a text document by URI */
function openTextDocument(uri: Uri | string): Promise<TextDocument>;
/** Find files matching a glob pattern */
function findFiles(include: string, exclude?: string, maxResults?: number): Promise<Uri[]>;
/** Create a file system watcher */
function createFileSystemWatcher(globPattern: string, ignoreCreate?: boolean, ignoreChange?: boolean, ignoreDelete?: boolean): FileSystemWatcher;
/** Get configuration for a section */
function getConfiguration(section?: string): Configuration;
/** Apply a workspace edit (multi-file) */
function applyEdit(edit: WorkspaceEdit): Promise<boolean>;
/** Create an output channel */
function createOutputChannel(name: string): OutputChannel;
// Events
const onDidOpenTextDocument: Event<TextDocument>;
const onDidCloseTextDocument: Event<TextDocument>;
const onDidSaveTextDocument: Event<TextDocument>;
const onDidChangeTextDocument: Event<TextDocumentChangeEvent>;
const onDidChangeConfiguration: Event<ConfigurationChangeEvent>;
const onDidChangeWorkspaceFolders: Event<WorkspaceFoldersChangeEvent>;
}
export interface ConfigurationChangeEvent {
affectsConfiguration(section: string): boolean;
}
export interface WorkspaceFoldersChangeEvent {
readonly added: readonly WorkspaceFolder[];
readonly removed: readonly WorkspaceFolder[];
}import { Disposable, Event, Uri, CancellationToken, ProviderResult } from './types';
// === Status Bar ===
export enum StatusBarAlignment {
Left = 1,
Right = 2,
}
export interface StatusBarItem {
alignment: StatusBarAlignment;
priority?: number;
text: string;
tooltip?: string;
color?: string;
backgroundColor?: string;
command?: string;
show(): void;
hide(): void;
dispose(): void;
}
// === Quick Pick ===
export interface QuickPickItem {
label: string;
description?: string;
detail?: string;
picked?: boolean;
alwaysShow?: boolean;
}
export interface QuickPickOptions {
title?: string;
placeHolder?: string;
canPickMany?: boolean;
matchOnDescription?: boolean;
matchOnDetail?: boolean;
}
// === Input Box ===
export interface InputBoxOptions {
title?: string;
prompt?: string;
placeHolder?: string;
value?: string;
password?: boolean;
validateInput?(value: string): string | undefined | null | Thenable<string | undefined | null>;
}
// === Tree View ===
export interface TreeDataProvider<T> {
getTreeItem(element: T): TreeItem | Thenable<TreeItem>;
getChildren(element?: T): ProviderResult<T[]>;
getParent?(element: T): ProviderResult<T>;
onDidChangeTreeData?: Event<T | undefined | null | void>;
}
export interface TreeItem {
label: string;
description?: string;
tooltip?: string;
iconPath?: Uri | { light: Uri; dark: Uri };
collapsibleState?: TreeItemCollapsibleState;
command?: CommandRef;
contextValue?: string;
}
export enum TreeItemCollapsibleState {
None = 0,
Collapsed = 1,
Expanded = 2,
}
export interface CommandRef {
command: string;
title: string;
arguments?: any[];
}
// === Webview ===
export interface WebviewPanel {
readonly viewType: string;
title: string;
readonly webview: Webview;
readonly visible: boolean;
readonly active: boolean;
readonly onDidDispose: Event<void>;
readonly onDidChangeViewState: Event<WebviewPanelOnDidChangeViewStateEvent>;
reveal(preserveFocus?: boolean): void;
dispose(): void;
}
export interface Webview {
html: string;
readonly onDidReceiveMessage: Event<any>;
postMessage(message: any): Promise<boolean>;
readonly cspSource: string;
}
export interface WebviewPanelOnDidChangeViewStateEvent {
readonly webviewPanel: WebviewPanel;
}
export interface WebviewOptions {
enableScripts?: boolean;
localResourceRoots?: Uri[];
}
// === Messages ===
export interface MessageItem {
title: string;
isCloseAffordance?: boolean;
}
// === Namespace ===
export namespace ui {
function registerTreeDataProvider<T>(viewId: string, provider: TreeDataProvider<T>): Disposable;
function createWebviewPanel(viewType: string, title: string, options?: WebviewOptions): WebviewPanel;
function createStatusBarItem(alignment?: StatusBarAlignment, priority?: number): StatusBarItem;
function showInformationMessage(message: string, ...items: string[]): Promise<string | undefined>;
function showInformationMessage(message: string, options: MessageOptions, ...items: MessageItem[]): Promise<MessageItem | undefined>;
function showWarningMessage(message: string, ...items: string[]): Promise<string | undefined>;
function showErrorMessage(message: string, ...items: string[]): Promise<string | undefined>;
function showInputBox(options?: InputBoxOptions): Promise<string | undefined>;
function showQuickPick(items: string[], options?: QuickPickOptions): Promise<string | undefined>;
function showQuickPick(items: QuickPickItem[], options?: QuickPickOptions): Promise<QuickPickItem | undefined>;
function showQuickPick<T extends QuickPickItem>(items: T[], options?: QuickPickOptions & { canPickMany: true }): Promise<T[] | undefined>;
function showOpenDialog(options?: OpenDialogOptions): Promise<Uri[] | undefined>;
function showSaveDialog(options?: SaveDialogOptions): Promise<Uri | undefined>;
function withProgress<R>(options: ProgressOptions, task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => Thenable<R>): Thenable<R>;
}
export interface MessageOptions {
modal?: boolean;
}
export interface OpenDialogOptions {
canSelectFiles?: boolean;
canSelectFolders?: boolean;
canSelectMany?: boolean;
filters?: { [name: string]: string[] };
title?: string;
}
export interface SaveDialogOptions {
filters?: { [name: string]: string[] };
title?: string;
defaultUri?: Uri;
}import { Disposable, CancellationToken, ProviderResult, Uri, Event } from './types';
import { Position, Range, TextDocument, TextEdit } from './editor';
/** Document selector for language feature registration */
export type DocumentSelector = string | DocumentFilter | (string | DocumentFilter)[];
export interface DocumentFilter {
language?: string;
scheme?: string;
pattern?: string;
}
// === Diagnostics ===
export enum DiagnosticSeverity {
Error = 0,
Warning = 1,
Information = 2,
Hint = 3,
}
export interface Diagnostic {
range: Range;
message: string;
severity: DiagnosticSeverity;
code?: string | number;
source?: string;
relatedInformation?: DiagnosticRelatedInformation[];
}
export interface DiagnosticRelatedInformation {
location: Location;
message: string;
}
export interface Location {
uri: Uri;
range: Range;
}
export interface DiagnosticCollection extends Disposable {
readonly name: string;
set(uri: Uri, diagnostics: Diagnostic[]): void;
delete(uri: Uri): void;
clear(): void;
forEach(callback: (uri: Uri, diagnostics: Diagnostic[], collection: DiagnosticCollection) => any): void;
get(uri: Uri): Diagnostic[] | undefined;
has(uri: Uri): boolean;
}
// === Completion ===
export enum CompletionItemKind {
Text = 0, Method = 1, Function = 2, Constructor = 3, Field = 4,
Variable = 5, Class = 6, Interface = 7, Module = 8, Property = 9,
Unit = 10, Value = 11, Enum = 12, Keyword = 13, Snippet = 14,
Color = 15, File = 16, Reference = 17, Folder = 18, EnumMember = 19,
Constant = 20, Struct = 21, Event = 22, Operator = 23, TypeParameter = 24,
}
export enum CompletionTriggerKind {
Invoke = 0,
TriggerCharacter = 1,
TriggerForIncompleteCompletions = 2,
}
export interface CompletionContext {
triggerKind: CompletionTriggerKind;
triggerCharacter?: string;
}
export interface CompletionItem {
label: string | CompletionItemLabel;
kind?: CompletionItemKind;
detail?: string;
documentation?: string | MarkdownString;
sortText?: string;
filterText?: string;
insertText?: string | SnippetString;
range?: Range;
additionalTextEdits?: TextEdit[];
command?: CommandRef;
preselect?: boolean;
}
export interface CompletionItemLabel {
label: string;
detail?: string;
description?: string;
}
export interface CompletionList {
isIncomplete: boolean;
items: CompletionItem[];
}
export interface CompletionProvider {
provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): ProviderResult<CompletionItem[] | CompletionList>;
resolveCompletionItem?(item: CompletionItem, token: CancellationToken): ProviderResult<CompletionItem>;
}
// === Hover ===
export interface MarkdownString {
readonly value: string;
readonly isTrusted?: boolean;
}
export interface Hover {
contents: MarkdownString[];
range?: Range;
}
export interface HoverProvider {
provideHover(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<Hover>;
}
// === Code Actions ===
export enum CodeActionKind {
QuickFix = 'quickfix',
Refactor = 'refactor',
RefactorExtract = 'refactor.extract',
RefactorInline = 'refactor.inline',
RefactorRewrite = 'refactor.rewrite',
Source = 'source',
SourceOrganizeImports = 'source.organizeImports',
SourceFixAll = 'source.fixAll',
}
export interface CodeAction {
title: string;
kind?: CodeActionKind;
diagnostics?: Diagnostic[];
isPreferred?: boolean;
edit?: WorkspaceEdit;
command?: CommandRef;
}
export interface CodeActionContext {
readonly diagnostics: readonly Diagnostic[];
readonly only?: CodeActionKind;
readonly triggerKind: CodeActionTriggerKind;
}
export enum CodeActionTriggerKind {
Invoke = 1,
Automatic = 2,
}
export interface CodeActionProvider {
provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): ProviderResult<CodeAction[]>;
}
// === Code Lens ===
export interface CodeLens {
range: Range;
command?: CommandRef;
isResolved: boolean;
}
export interface CodeLensProvider {
provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult<CodeLens[]>;
resolveCodeLens?(codeLens: CodeLens, token: CancellationToken): ProviderResult<CodeLens>;
onDidChangeCodeLenses?: Event<void>;
}
// === Definition / References ===
export interface DefinitionProvider {
provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<Location | Location[]>;
}
export interface ReferenceContext {
includeDeclaration: boolean;
}
export interface ReferenceProvider {
provideReferences(document: TextDocument, position: Position, context: ReferenceContext, token: CancellationToken): ProviderResult<Location[]>;
}
// === Rename ===
export interface RenameProvider {
provideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): ProviderResult<WorkspaceEdit>;
prepareRename?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<Range | { range: Range; placeholder: string }>;
}
// === Document Symbols ===
export enum SymbolKind {
File = 0, Module = 1, Namespace = 2, Package = 3, Class = 4,
Method = 5, Property = 6, Field = 7, Constructor = 8, Enum = 9,
Interface = 10, Function = 11, Variable = 12, Constant = 13, String = 14,
Number = 15, Boolean = 16, Array = 17, Object = 18, Key = 19,
Null = 20, EnumMember = 21, Struct = 22, Event = 23, Operator = 24,
TypeParameter = 25,
}
export interface DocumentSymbol {
name: string;
detail: string;
kind: SymbolKind;
range: Range;
selectionRange: Range;
children?: DocumentSymbol[];
}
export interface DocumentSymbolProvider {
provideDocumentSymbols(document: TextDocument, token: CancellationToken): ProviderResult<DocumentSymbol[]>;
}
// === Formatting ===
export interface FormattingOptions {
tabSize: number;
insertSpaces: boolean;
}
export interface DocumentFormattingEditProvider {
provideDocumentFormattingEdits(document: TextDocument, options: FormattingOptions, token: CancellationToken): ProviderResult<TextEdit[]>;
}
export interface DocumentRangeFormattingEditProvider {
provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult<TextEdit[]>;
}
// === Signature Help ===
export interface SignatureHelp {
signatures: SignatureInformation[];
activeSignature: number;
activeParameter: number;
}
export interface SignatureInformation {
label: string;
documentation?: string | MarkdownString;
parameters: ParameterInformation[];
}
export interface ParameterInformation {
label: string | [number, number];
documentation?: string | MarkdownString;
}
export interface SignatureHelpProvider {
provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult<SignatureHelp>;
}
export interface SignatureHelpContext {
triggerKind: SignatureHelpTriggerKind;
triggerCharacter?: string;
isRetrigger: boolean;
activeSignatureHelp?: SignatureHelp;
}
export enum SignatureHelpTriggerKind {
Invoke = 1,
TriggerCharacter = 2,
ContentChange = 3,
}
// === Namespace ===
export namespace languages {
function registerCompletionItemProvider(selector: DocumentSelector, provider: CompletionProvider, ...triggerCharacters: string[]): Disposable;
function registerHoverProvider(selector: DocumentSelector, provider: HoverProvider): Disposable;
function registerCodeActionProvider(selector: DocumentSelector, provider: CodeActionProvider, metadata?: CodeActionProviderMetadata): Disposable;
function registerCodeLensProvider(selector: DocumentSelector, provider: CodeLensProvider): Disposable;
function registerDefinitionProvider(selector: DocumentSelector, provider: DefinitionProvider): Disposable;
function registerReferenceProvider(selector: DocumentSelector, provider: ReferenceProvider): Disposable;
function registerRenameProvider(selector: DocumentSelector, provider: RenameProvider): Disposable;
function registerDocumentSymbolProvider(selector: DocumentSelector, provider: DocumentSymbolProvider): Disposable;
function registerDocumentFormattingEditProvider(selector: DocumentSelector, provider: DocumentFormattingEditProvider): Disposable;
function registerDocumentRangeFormattingEditProvider(selector: DocumentSelector, provider: DocumentRangeFormattingEditProvider): Disposable;
function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): Disposable;
function createDiagnosticCollection(name?: string): DiagnosticCollection;
function getDiagnostics(resource?: Uri): Diagnostic[] | [Uri, Diagnostic[]][];
function setLanguageConfiguration(language: string, configuration: LanguageConfiguration): Disposable;
}
export interface CodeActionProviderMetadata {
readonly providedCodeActionKinds?: readonly CodeActionKind[];
}
export interface LanguageConfiguration {
comments?: CommentRule;
brackets?: [string, string][];
wordPattern?: RegExp;
indentationRules?: IndentationRule;
autoClosingPairs?: AutoClosingPair[];
}
export interface CommentRule {
lineComment?: string;
blockComment?: [string, string];
}
export interface IndentationRule {
increaseIndentPattern: RegExp;
decreaseIndentPattern: RegExp;
}
export interface AutoClosingPair {
open: string;
close: string;
notIn?: string[];
}import { Disposable, Event, Uri } from './types';
export interface DebugSession {
readonly id: string;
readonly type: string;
readonly name: string;
customRequest(command: string, args?: any): Promise<any>;
}
export interface DebugConfiguration {
type: string;
name: string;
request: 'launch' | 'attach';
[key: string]: any;
}
export interface Breakpoint {
readonly id: string;
readonly enabled: boolean;
readonly condition?: string;
readonly hitCondition?: string;
readonly logMessage?: string;
}
export interface SourceBreakpoint extends Breakpoint {
readonly location: Location;
}
export interface FunctionBreakpoint extends Breakpoint {
readonly functionName: string;
}
export namespace debug {
const activeDebugSession: DebugSession | undefined;
const breakpoints: readonly Breakpoint[];
function startDebugging(folder: WorkspaceFolder | undefined, config: DebugConfiguration): Promise<boolean>;
function stopDebugging(session?: DebugSession): Promise<void>;
function addBreakpoints(breakpoints: Breakpoint[]): void;
function removeBreakpoints(breakpoints: Breakpoint[]): void;
const onDidStartDebugSession: Event<DebugSession>;
const onDidTerminateDebugSession: Event<DebugSession>;
const onDidChangeActiveDebugSession: Event<DebugSession | undefined>;
const onDidChangeBreakpoints: Event<BreakpointsChangeEvent>;
}
export interface BreakpointsChangeEvent {
readonly added: readonly Breakpoint[];
readonly removed: readonly Breakpoint[];
readonly changed: readonly Breakpoint[];
}import { Disposable, Event } from './types';
export interface Terminal {
readonly name: string;
readonly processId: Promise<number | undefined>;
readonly exitStatus: TerminalExitStatus | undefined;
sendText(text: string, addNewLine?: boolean): void;
show(preserveFocus?: boolean): void;
hide(): void;
dispose(): void;
}
export interface TerminalExitStatus {
readonly code: number | undefined;
readonly reason: TerminalExitReason;
}
export enum TerminalExitReason {
Unknown = 0,
Shutdown = 1,
Process = 2,
User = 3,
Extension = 4,
}
export interface TerminalOptions {
name?: string;
shellPath?: string;
shellArgs?: string | string[];
cwd?: string;
env?: { [key: string]: string | null | undefined };
hideFromUser?: boolean;
}
export namespace terminal {
const terminals: readonly Terminal[];
const activeTerminal: Terminal | undefined;
function createTerminal(options?: TerminalOptions): Terminal;
function createTerminal(name?: string, shellPath?: string, shellArgs?: string | string[]): Terminal;
const onDidOpenTerminal: Event<Terminal>;
const onDidCloseTerminal: Event<Terminal>;
const onDidChangeActiveTerminal: Event<Terminal | undefined>;
const onDidWriteTerminalData: Event<TerminalDataWriteEvent>;
}
export interface TerminalDataWriteEvent {
readonly terminal: Terminal;
readonly data: string;
}import { Disposable, CancellationToken } from './types';
// === AI Provider Adapter ===
/**
* Interface that AI providers implement to integrate with Hone.
* Extensions can register custom providers (e.g., corporate LLM endpoints).
*/
export interface AIProviderAdapter {
readonly id: string;
readonly name: string;
readonly capabilities: AICapabilities;
/** Stream a completion (for inline ghost text / FIM) */
complete(request: CompletionRequest, token?: CancellationToken): AsyncIterable<CompletionChunk>;
/** Stream a chat response */
chat(request: ChatRequest, token?: CancellationToken): AsyncIterable<ChatChunk>;
/** Stream a chat response with tool use support (for agent mode) */
chatWithTools(request: ToolChatRequest, token?: CancellationToken): AsyncIterable<ToolChatChunk>;
}
export interface AICapabilities {
maxContextTokens: number;
supportsStreaming: boolean;
supportsToolUse: boolean;
supportsVision: boolean;
supportsFIM: boolean;
estimatedLatencyMs: number;
}
// === Completion (Ghost Text / FIM) ===
export interface CompletionRequest {
prompt: string;
suffix?: string;
maxTokens?: number;
temperature?: number;
stop?: string[];
model?: string;
}
export interface CompletionChunk {
text: string;
done: boolean;
}
// === Chat ===
export interface ChatMessage {
role: 'system' | 'user' | 'assistant';
content: string | ChatContentPart[];
}
export interface ChatContentPart {
type: 'text' | 'image';
text?: string;
imageUrl?: string;
mimeType?: string;
}
export interface ChatRequest {
messages: ChatMessage[];
model?: string;
maxTokens?: number;
temperature?: number;
stop?: string[];
}
export interface ChatChunk {
text: string;
done: boolean;
usage?: TokenUsage;
}
export interface TokenUsage {
promptTokens: number;
completionTokens: number;
totalTokens: number;
}
// === Tool Use (Agent Mode) ===
export interface AgentToolDefinition {
name: string;
description: string;
inputSchema: Record<string, any>; // JSON Schema
requiresApproval: boolean;
}
export interface ToolChatRequest extends ChatRequest {
tools: AgentToolDefinition[];
}
export interface ToolCall {
id: string;
name: string;
arguments: Record<string, any>;
}
export interface ToolResult {
toolCallId: string;
content: string;
isError?: boolean;
}
export interface ToolChatChunk {
type: 'text' | 'tool_call' | 'tool_result_request';
text?: string;
toolCall?: ToolCall;
done: boolean;
usage?: TokenUsage;
}
// === AI Namespace ===
export namespace ai {
/** Register a custom AI provider adapter */
function registerAIProvider(provider: AIProviderAdapter): Disposable;
/** Register a custom agent tool */
function registerAgentTool(tool: AgentToolDefinition & { execute: (args: Record<string, any>) => Promise<string> }): Disposable;
/** Get the currently active AI provider (user's configured default) */
function getActiveProvider(): AIProviderAdapter | null;
/** List all registered providers */
function getProviders(): AIProviderAdapter[];
}// Re-export all types and namespaces
export * from './types';
export * from './commands';
export * from './editor';
export * from './workspace';
export * from './ui';
export * from './languages';
export * from './debug';
export * from './terminal';
export * from './ai';
// === Extension Activation ===
/**
* Every extension must export an `activate` function.
* Called when one of the extension's activation events fires.
*/
export interface ExtensionContext {
/** Subscriptions to be disposed when the extension is deactivated */
readonly subscriptions: Disposable[];
/** Absolute path to the extension's install directory */
readonly extensionPath: string;
/** Storage path for extension-specific persistent data */
readonly storagePath: string | undefined;
/** Global storage path shared across workspaces */
readonly globalStoragePath: string;
}
/**
* Extensions export this as their entry point.
*/
export type ActivateFunction = (context: ExtensionContext) => void | Promise<void>;
/**
* Optional — called when the extension is deactivated.
*/
export type DeactivateFunction = () => void | Promise<void>;Purpose: Foundation types used across all other modules.
Disposable— The cleanup pattern. Everyregister*call returns one. Extensions push them intocontext.subscriptionsfor auto-cleanup.Event<T>— A function type that accepts a listener callback. The implementation (in hone-core) manages listener lists, but extensions only see this signature.Uri— Immutable identifier for resources. TheUrinamespace provides factory functions. ThefsPathproperty converts to OS-native paths.CancellationToken— Providers receive this so they can abort long operations when the user moves on.ProviderResult<T>— Convenience type allowing providers to return values synchronously or as promises, with null/undefined for "no result."
Edge cases:
Uri.file()must handle Windows drive letters (C:\) and Unix paths (/usr/).Eventmust support multiple listeners and handle disposal during emission.
Purpose: The command system — how extensions register callable actions.
registerCommandreturns a Disposable. Calling dispose unregisters the command.executeCommandreturns a Promise that resolves to the handler's return value.- Command IDs should be namespaced (e.g.,
myext.doThing).
Purpose: The text editing surface API — what extensions see when manipulating open documents.
PositionandRangeare zero-based (line 0, character 0 is the start of the file).TextDocumentis read-only. Mutations go throughTextEditor.edit()which takes anEditBuilder.- Multi-cursor is exposed through
TextEditor.selections(array of Selections). DecorationRenderOptionssupportsbeforeandafterpseudo-elements for inline decorations.
Edge cases:
validatePositionmust clamp to valid document bounds.getWordRangeAtPositionwith no regex uses default word boundaries.
Purpose: File system access, configuration, and workspace events.
openTextDocumentopens or returns an already-open document by URI.findFilesuses glob patterns and returns matching URIs.Configurationis layered: default < user < workspace < language-specific.WorkspaceEditsupports multi-file atomic edits.
Purpose: All UI contribution points — status bar, quick pick, tree views, webviews, dialogs, messages.
TreeDataProvideris the primary way extensions add sidebar views.WebviewPanelprovides an HTML rendering surface for complex custom UIs.StatusBarItementries appear in the bottom bar.showQuickPickandshowInputBoxare the primary user input methods.
Purpose: Language intelligence registration — completion, hover, diagnostics, definitions, etc.
- Providers are registered with a
DocumentSelectorthat filters by language/scheme/pattern. - Most providers are lazy — only called when the user interacts (hover, trigger completion, etc.).
DiagnosticCollectionis the exception — extensions push diagnostics proactively.CodeActionProvidersupports quick fixes (from diagnostics) and refactoring.
Purpose: Debug session lifecycle and breakpoint management.
startDebugginglaunches a debug session with the given configuration.- Breakpoints are managed as abstract objects with conditions and hit counts.
customRequestallows sending arbitrary DAP messages.
Purpose: Terminal creation and interaction.
createTerminalspawns a shell process.sendTextwrites to stdin.onDidWriteTerminalDataemits stdout/stderr data.- Terminal lifecycle events track open/close/active changes.
Purpose: AI provider registration and agent tool definitions.
AIProviderAdapteris the core interface. Each provider (Anthropic, OpenAI, etc.) implements it.- Extensions can register custom providers for corporate LLMs.
AgentToolDefinitionlets extensions add tools the agent can use.- All AI methods return
AsyncIterablefor streaming. chatWithToolsis the key agent method — returns text and tool calls interleaved.
Purpose: Single barrel export. Extensions do import * as hone from '@honeide/api'.
- Also defines
ExtensionContextand activation/deactivation function types. ExtensionContext.subscriptionsis the primary cleanup mechanism.
perry compile src/index.ts --output-type library --target <platform>Since hone-api is pure types with no runtime code, Perry compiles it as type declarations only. It serves as:
- A compile-time dependency for extensions (type-checking only)
- A type definition source for hone-core to implement against
This package has zero platform-specific code. No native rendering, no system calls.
{
"name": "@honeide/api",
"version": "1.0.0",
"types": "src/index.ts",
"files": ["src/"],
"license": "MIT"
}Compile-only tests that assert type relationships work correctly:
// Verify a CompletionProvider can be created
const provider: CompletionProvider = {
provideCompletionItems(doc, pos, token, ctx) {
return [{ label: 'test', kind: CompletionItemKind.Text }];
}
};
// Verify Event<T> signature
const event: Event<TextDocument> = (listener) => ({ dispose() {} });
// Verify Disposable pattern
const disposable: Disposable = { dispose() {} };Ensures all expected exports exist and are the correct types:
import * as hone from '../src/index';
// Assert namespaces exist
assert(typeof hone.commands !== 'undefined');
assert(typeof hone.workspace !== 'undefined');
assert(typeof hone.editor !== 'undefined');
assert(typeof hone.languages !== 'undefined');
assert(typeof hone.ui !== 'undefined');
assert(typeof hone.debug !== 'undefined');
assert(typeof hone.terminal !== 'undefined');
assert(typeof hone.ai !== 'undefined');
// Assert key enums
assert(hone.DiagnosticSeverity.Error === 0);
assert(hone.CompletionItemKind.Method === 1);types.ts— Disposable, Event, Uri, CancellationTokencommands.ts— Command registrationeditor.ts— Position, Range, Selection, TextDocument, TextEditor- Type check tests passing
workspace.ts— WorkspaceFolder, Configuration, eventsui.ts— StatusBarItem, TreeDataProvider, QuickPick, WebviewPanel- All UI contribution types complete
languages.ts— All provider interfaces (Completion, Hover, CodeAction, etc.)debug.ts— Debug session and breakpoint types
terminal.ts— Terminal creation and lifecycleai.ts— AIProviderAdapter, AgentToolDefinition, chat/completion/tool typesindex.ts— Barrel export with ExtensionContext
- Full API surface review
- Ensure compatibility with hone-core implementation plan
- Publish
@honeide/api@0.1.0on npm - CHANGELOG.md
-
API stability vs. features: The AI namespace (
ai.ts) is the most likely to change as agent capabilities evolve. Consider marking it as@experimentaluntil v1.0. -
VSCode API compatibility: The current design is VSCode-inspired but not identical. Should we add a compatibility shim or accept divergence? Current decision: accept divergence, optimize for Hone's native model.
-
Extension sandboxing: Perry compiles extensions to native code in-process. Without a runtime sandbox, malicious extensions have full access. Mitigation: curated marketplace with code review, plus OS-level sandboxing on platforms that support it (macOS App Sandbox, iOS).
-
Webview security:
WebviewPanelprovides raw HTML rendering. CSP (Content Security Policy) must be enforced. ThecspSourceproperty helps extensions load resources safely. -
Namespace pattern vs. class pattern: Using namespaces (like VSCode's
vscode.commands.registerCommand) vs. class instances. Current decision: namespaces for familiarity. -
Thenable vs. Promise: Using
ProviderResult<T>allows synchronous returns from providers (important for performance). This is VSCode's approach and we follow it.