Skip to content

feat: send editor context (visible files, open tabs, shell, timezone) to CLI backend#322

Open
markijbema wants to merge 2 commits intodevfrom
mark/editor-context
Open

feat: send editor context (visible files, open tabs, shell, timezone) to CLI backend#322
markijbema wants to merge 2 commits intodevfrom
mark/editor-context

Conversation

@markijbema
Copy link
Contributor

Summary

Send editor context information (visible files, open tabs, active file, shell, timezone) alongside chat messages to the CLI backend.

Changes

  • EditorContext interface added to src/services/cli-backend/types.ts — defines the shape of editor context data (visible files, open tabs, active file, shell, timezone, cwd)
  • sendMessage() in http-client.ts extended to accept and forward an optional editorContext parameter to the CLI backend
  • gatherEditorContext() in KiloProvider.ts — new method that collects:
    • Visible text editor file paths
    • Open tab file paths
    • Active file path
    • Default shell from VS Code terminal profile
    • User timezone (via Intl.DateTimeFormat)
    • Workspace root as cwd
  • All paths are workspace-relative; files outside the workspace are excluded
  • Existing active editor file:// URI injection into the message body is preserved for backward compatibility
  • Includes docs/vscode-context-plan.md design document

Context

Implements the extension-side pieces of Phase 1 + Phase 2 from docs/vscode-context-plan.md. The CLI backend side (consuming the context) is tracked separately.

return undefined
}
const relative = path.relative(workspaceDir, fsPath)
if (relative.startsWith("..")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: toRelative() may mis-handle files on Windows when paths are on different drive letters

path.relative(workspaceDir, fsPath) can return an absolute path (e.g. D:\...) when workspaceDir is on C: and the file is on D:. In that case relative.startsWith('..') is false, so this code would incorrectly treat an out-of-workspace file as workspace-relative and send an absolute path to the backend. Consider also rejecting path.isAbsolute(relative) (and/or using path.resolve(workspaceDir, relative) + prefix check) before returning the value.


parts.push({ type: "text", text })

const editorContext = this.gatherEditorContext()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Avoid sending an empty editorContext object

gatherEditorContext() can return {} when no fields are available, but options?.editorContext is truthy even when empty so the payload will always include editorContext: {}. If the backend schema expects editorContext to be omitted when empty, this could cause validation/behavior differences. Consider only setting body.editorContext when it has at least one key.

@kiloconnect
Copy link
Contributor

kiloconnect bot commented Feb 16, 2026

Code Review Summary

Status: 1 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/session/system.ts 66 editorContext values should be sanitized/capped before embedding in the system prompt
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
Files Reviewed (5 files)
  • packages/kilo-vscode/docs/vscode-context-plan.md - 0 issues
  • packages/kilo-vscode/src/KiloProvider.ts - 0 issues (existing inline comments already cover main risks)
  • packages/kilo-vscode/src/services/cli-backend/http-client.ts - 0 issues
  • packages/kilo-vscode/src/services/cli-backend/types.ts - 0 issues (existing inline comment re: cwd)
  • packages/opencode/src/session/system.ts - 1 issue

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for sending VS Code “editor context” metadata alongside chat messages so the CLI backend can build more accurate environment/system prompts.

Changes:

  • Introduces an EditorContext type for editor-derived context fields (visible files, open tabs, active file, shell, timezone).
  • Extends the CLI backend HTTP client sendMessage() payload to optionally include editorContext.
  • Implements gatherEditorContext() in KiloProvider to collect and forward editor context; adds a design doc describing phased context expansion.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
packages/kilo-vscode/src/services/cli-backend/types.ts Adds the EditorContext interface for message-adjacent editor context data.
packages/kilo-vscode/src/services/cli-backend/http-client.ts Extends sendMessage() to include an editorContext field in the POST payload.
packages/kilo-vscode/src/KiloProvider.ts Collects editor state (visible files/tabs/active file/shell/timezone) and passes it to the backend on message send.
packages/kilo-vscode/docs/vscode-context-plan.md Adds a phased design plan and proposed protocol shape for VS Code context.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +324 to +336
/** VS Code editor context sent alongside messages to the CLI backend */
export interface EditorContext {
/** Workspace-relative paths of currently visible editors */
visibleFiles?: string[]
/** Workspace-relative paths of open tabs */
openTabs?: string[]
/** Workspace-relative path of the active editor file */
activeFile?: string
/** User's default shell (from vscode.env.shell) */
shell?: string
/** User's timezone (e.g. "Europe/Amsterdam") */
timezone?: string
}
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description says the EditorContext shape includes cwd, but this interface does not define it. Either add a cwd?: string field (and populate it) or update the PR description/docs to reflect that workspace cwd is provided only via the existing x-opencode-directory header.

Copilot uses AI. Check for mistakes.
Comment on lines +318 to +326
// NEW: Editor context from VS Code
editorContext?: {
visibleFiles?: string[] // workspace-relative paths
openTabs?: string[] // workspace-relative paths
activeFile?: string // workspace-relative path (replaces file:// part)
shell?: string // user's default shell
timezone?: string // user's timezone
diagnostics?: DiagnosticInfo[] // workspace diagnostics
}
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The protocol example says activeFile “replaces file:// part”, but this PR explicitly preserves the existing file:// part injection for backward compatibility and also sends editorContext.activeFile. This doc section should be updated to avoid implying the file-part will be removed/absent when activeFile is present (or explicitly mark that as a later migration step).

Copilot uses AI. Check for mistakes.
Comment on lines +1271 to +1273
if (relative.startsWith("..")) {
return undefined
}
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toRelative() uses path.relative(workspaceDir, fsPath) and only checks relative.startsWith(".."). On Windows, if fsPath is on a different drive than workspaceDir, path.relative can return an absolute path (e.g. D:\...), which will not start with .. and will incorrectly be treated as in-workspace. Also, startsWith("..") will incorrectly exclude legitimate in-workspace paths like ..foo. Consider using vscode.workspace.getWorkspaceFolder(uri) / vscode.workspace.asRelativePath(uri, false) to gate inclusion, or at least additionally reject path.isAbsolute(relative) and check relative === ".." || relative.startsWith(..${path.sep}).

Suggested change
if (relative.startsWith("..")) {
return undefined
}
// Exclude paths that are not within the workspace:
// - On Windows, path.relative can return an absolute path for different drives.
// - Only treat ".." or ".." + path.sep + ... as outside the workspace, not names like "..foo".
if (path.isAbsolute(relative)) {
return undefined
}
if (relative === ".." || relative.startsWith(`..${path.sep}`)) {
return undefined
}

Copilot uses AI. Check for mistakes.
}
}
}
const openTabs = [...openTabSet]
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

openTabs is currently unbounded (const openTabs = [...openTabSet]). In large workspaces/users with many tabs, this can noticeably increase the per-message payload size sent to the CLI backend. The design doc in this PR mentions a default cap (e.g. maxOpenTabsContext); consider enforcing a reasonable max (and possibly a stable ordering) before sending.

Suggested change
const openTabs = [...openTabSet]
const maxOpenTabsContext = 50
const openTabs = [...openTabSet].sort().slice(0, maxOpenTabsContext)

Copilot uses AI. Check for mistakes.
envLines.push(` Active file: ${editorContext.activeFile}`)
}
if (editorContext?.visibleFiles?.length) {
envLines.push(` Visible files: ${editorContext.visibleFiles.join(", ")}`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: editorContext values should be sanitized/capped before embedding in the system prompt

visibleFiles/openTabs are joined directly into the <env> block. On POSIX, filenames can contain newlines (and other control chars), which can break the tag structure and create prompt-injection-y formatting. Independently, large tab sets can bloat the system prompt and increase token usage. Consider normalizing these strings (e.g., replace \r/\n) and applying a max count / max character budget before join(", ").

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant