@oh-my-pi/pi-coding-agent 0.1.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 +1629 -0
- package/README.md +1041 -0
- package/docs/compaction.md +403 -0
- package/docs/config-usage.md +113 -0
- package/docs/custom-tools.md +541 -0
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +867 -0
- package/docs/rpc.md +1040 -0
- package/docs/sdk.md +994 -0
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +240 -0
- package/docs/skills.md +290 -0
- package/docs/theme.md +670 -0
- package/docs/tree.md +197 -0
- package/docs/tui.md +341 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +124 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/question/index.ts +84 -0
- package/examples/custom-tools/subagent/README.md +172 -0
- package/examples/custom-tools/subagent/agents/planner.md +37 -0
- package/examples/custom-tools/subagent/agents/scout.md +50 -0
- package/examples/custom-tools/subagent/agents/worker.md +24 -0
- package/examples/custom-tools/subagent/agents.ts +156 -0
- package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
- package/examples/custom-tools/subagent/commands/implement.md +10 -0
- package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
- package/examples/custom-tools/subagent/index.ts +1002 -0
- package/examples/custom-tools/todo/index.ts +212 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +49 -0
- package/examples/hooks/confirm-destructive.ts +59 -0
- package/examples/hooks/custom-compaction.ts +116 -0
- package/examples/hooks/dirty-repo-guard.ts +52 -0
- package/examples/hooks/file-trigger.ts +41 -0
- package/examples/hooks/git-checkpoint.ts +53 -0
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +34 -0
- package/examples/hooks/protected-paths.ts +30 -0
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +44 -0
- package/examples/sdk/04-skills.ts +44 -0
- package/examples/sdk/05-tools.ts +90 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +36 -0
- package/examples/sdk/08-slash-commands.ts +42 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +48 -0
- package/examples/sdk/12-full-control.ts +95 -0
- package/examples/sdk/README.md +154 -0
- package/package.json +89 -0
- package/src/bun-imports.d.ts +16 -0
- package/src/capability/context-file.ts +40 -0
- package/src/capability/extension.ts +48 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +616 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +52 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule.ts +56 -0
- package/src/capability/settings.ts +35 -0
- package/src/capability/skill.ts +49 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/system-prompt.ts +35 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +166 -0
- package/src/cli/args.ts +259 -0
- package/src/cli/file-processor.ts +121 -0
- package/src/cli/list-models.ts +104 -0
- package/src/cli/plugin-cli.ts +661 -0
- package/src/cli/session-picker.ts +41 -0
- package/src/cli/update-cli.ts +274 -0
- package/src/cli.ts +10 -0
- package/src/config.ts +391 -0
- package/src/core/agent-session.ts +2178 -0
- package/src/core/auth-storage.ts +258 -0
- package/src/core/bash-executor.ts +197 -0
- package/src/core/compaction/branch-summarization.ts +315 -0
- package/src/core/compaction/compaction.ts +664 -0
- package/src/core/compaction/index.ts +7 -0
- package/src/core/compaction/utils.ts +153 -0
- package/src/core/custom-commands/bundled/review/index.ts +156 -0
- package/src/core/custom-commands/index.ts +15 -0
- package/src/core/custom-commands/loader.ts +226 -0
- package/src/core/custom-commands/types.ts +112 -0
- package/src/core/custom-tools/index.ts +22 -0
- package/src/core/custom-tools/loader.ts +248 -0
- package/src/core/custom-tools/types.ts +185 -0
- package/src/core/custom-tools/wrapper.ts +29 -0
- package/src/core/exec.ts +139 -0
- package/src/core/export-html/index.ts +159 -0
- package/src/core/export-html/template.css +774 -0
- package/src/core/export-html/template.generated.ts +2 -0
- package/src/core/export-html/template.html +45 -0
- package/src/core/export-html/template.js +1185 -0
- package/src/core/export-html/template.macro.ts +24 -0
- package/src/core/file-mentions.ts +54 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/loader.ts +288 -0
- package/src/core/hooks/runner.ts +434 -0
- package/src/core/hooks/tool-wrapper.ts +98 -0
- package/src/core/hooks/types.ts +770 -0
- package/src/core/index.ts +53 -0
- package/src/core/logger.ts +112 -0
- package/src/core/mcp/client.ts +185 -0
- package/src/core/mcp/config.ts +248 -0
- package/src/core/mcp/index.ts +45 -0
- package/src/core/mcp/loader.ts +99 -0
- package/src/core/mcp/manager.ts +235 -0
- package/src/core/mcp/tool-bridge.ts +156 -0
- package/src/core/mcp/transports/http.ts +316 -0
- package/src/core/mcp/transports/index.ts +6 -0
- package/src/core/mcp/transports/stdio.ts +252 -0
- package/src/core/mcp/types.ts +228 -0
- package/src/core/messages.ts +211 -0
- package/src/core/model-registry.ts +334 -0
- package/src/core/model-resolver.ts +494 -0
- package/src/core/plugins/doctor.ts +67 -0
- package/src/core/plugins/index.ts +38 -0
- package/src/core/plugins/installer.ts +189 -0
- package/src/core/plugins/loader.ts +339 -0
- package/src/core/plugins/manager.ts +672 -0
- package/src/core/plugins/parser.ts +105 -0
- package/src/core/plugins/paths.ts +37 -0
- package/src/core/plugins/types.ts +190 -0
- package/src/core/sdk.ts +900 -0
- package/src/core/session-manager.ts +1837 -0
- package/src/core/settings-manager.ts +860 -0
- package/src/core/skills.ts +352 -0
- package/src/core/slash-commands.ts +132 -0
- package/src/core/system-prompt.ts +442 -0
- package/src/core/timings.ts +25 -0
- package/src/core/title-generator.ts +110 -0
- package/src/core/tools/ask.ts +193 -0
- package/src/core/tools/bash-interceptor.ts +120 -0
- package/src/core/tools/bash.ts +91 -0
- package/src/core/tools/context.ts +32 -0
- package/src/core/tools/edit-diff.ts +487 -0
- package/src/core/tools/edit.ts +140 -0
- package/src/core/tools/exa/company.ts +59 -0
- package/src/core/tools/exa/index.ts +63 -0
- package/src/core/tools/exa/linkedin.ts +59 -0
- package/src/core/tools/exa/mcp-client.ts +368 -0
- package/src/core/tools/exa/render.ts +200 -0
- package/src/core/tools/exa/researcher.ts +90 -0
- package/src/core/tools/exa/search.ts +338 -0
- package/src/core/tools/exa/types.ts +167 -0
- package/src/core/tools/exa/websets.ts +248 -0
- package/src/core/tools/find.ts +244 -0
- package/src/core/tools/grep.ts +584 -0
- package/src/core/tools/index.ts +283 -0
- package/src/core/tools/ls.ts +142 -0
- package/src/core/tools/lsp/client.ts +767 -0
- package/src/core/tools/lsp/clients/biome-client.ts +207 -0
- package/src/core/tools/lsp/clients/index.ts +49 -0
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
- package/src/core/tools/lsp/config.ts +845 -0
- package/src/core/tools/lsp/edits.ts +110 -0
- package/src/core/tools/lsp/index.ts +1364 -0
- package/src/core/tools/lsp/render.ts +560 -0
- package/src/core/tools/lsp/rust-analyzer.ts +145 -0
- package/src/core/tools/lsp/types.ts +495 -0
- package/src/core/tools/lsp/utils.ts +526 -0
- package/src/core/tools/notebook.ts +182 -0
- package/src/core/tools/output.ts +198 -0
- package/src/core/tools/path-utils.ts +61 -0
- package/src/core/tools/read.ts +507 -0
- package/src/core/tools/renderers.ts +820 -0
- package/src/core/tools/review.ts +275 -0
- package/src/core/tools/rulebook.ts +124 -0
- package/src/core/tools/task/agents.ts +158 -0
- package/src/core/tools/task/artifacts.ts +114 -0
- package/src/core/tools/task/commands.ts +157 -0
- package/src/core/tools/task/discovery.ts +217 -0
- package/src/core/tools/task/executor.ts +531 -0
- package/src/core/tools/task/index.ts +548 -0
- package/src/core/tools/task/model-resolver.ts +176 -0
- package/src/core/tools/task/parallel.ts +38 -0
- package/src/core/tools/task/render.ts +502 -0
- package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
- package/src/core/tools/task/types.ts +142 -0
- package/src/core/tools/truncate.ts +265 -0
- package/src/core/tools/web-fetch.ts +2511 -0
- package/src/core/tools/web-search/auth.ts +199 -0
- package/src/core/tools/web-search/index.ts +583 -0
- package/src/core/tools/web-search/providers/anthropic.ts +198 -0
- package/src/core/tools/web-search/providers/exa.ts +196 -0
- package/src/core/tools/web-search/providers/perplexity.ts +195 -0
- package/src/core/tools/web-search/render.ts +372 -0
- package/src/core/tools/web-search/types.ts +180 -0
- package/src/core/tools/write.ts +63 -0
- package/src/core/ttsr.ts +211 -0
- package/src/core/utils.ts +187 -0
- package/src/discovery/agents-md.ts +75 -0
- package/src/discovery/builtin.ts +647 -0
- package/src/discovery/claude.ts +623 -0
- package/src/discovery/cline.ts +104 -0
- package/src/discovery/codex.ts +571 -0
- package/src/discovery/cursor.ts +266 -0
- package/src/discovery/gemini.ts +368 -0
- package/src/discovery/github.ts +120 -0
- package/src/discovery/helpers.test.ts +127 -0
- package/src/discovery/helpers.ts +249 -0
- package/src/discovery/index.ts +84 -0
- package/src/discovery/mcp-json.ts +127 -0
- package/src/discovery/vscode.ts +99 -0
- package/src/discovery/windsurf.ts +219 -0
- package/src/index.ts +192 -0
- package/src/main.ts +507 -0
- package/src/migrations.ts +156 -0
- package/src/modes/cleanup.ts +23 -0
- package/src/modes/index.ts +48 -0
- package/src/modes/interactive/components/armin.ts +382 -0
- package/src/modes/interactive/components/assistant-message.ts +86 -0
- package/src/modes/interactive/components/bash-execution.ts +199 -0
- package/src/modes/interactive/components/bordered-loader.ts +41 -0
- package/src/modes/interactive/components/branch-summary-message.ts +42 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
- package/src/modes/interactive/components/custom-editor.ts +122 -0
- package/src/modes/interactive/components/diff.ts +147 -0
- package/src/modes/interactive/components/dynamic-border.ts +25 -0
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +296 -0
- package/src/modes/interactive/components/extensions/extension-list.ts +479 -0
- package/src/modes/interactive/components/extensions/index.ts +9 -0
- package/src/modes/interactive/components/extensions/inspector-panel.ts +313 -0
- package/src/modes/interactive/components/extensions/state-manager.ts +558 -0
- package/src/modes/interactive/components/extensions/types.ts +191 -0
- package/src/modes/interactive/components/hook-editor.ts +117 -0
- package/src/modes/interactive/components/hook-input.ts +64 -0
- package/src/modes/interactive/components/hook-message.ts +96 -0
- package/src/modes/interactive/components/hook-selector.ts +91 -0
- package/src/modes/interactive/components/model-selector.ts +560 -0
- package/src/modes/interactive/components/oauth-selector.ts +136 -0
- package/src/modes/interactive/components/plugin-settings.ts +481 -0
- package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
- package/src/modes/interactive/components/session-selector.ts +220 -0
- package/src/modes/interactive/components/settings-defs.ts +597 -0
- package/src/modes/interactive/components/settings-selector.ts +545 -0
- package/src/modes/interactive/components/show-images-selector.ts +45 -0
- package/src/modes/interactive/components/status-line/index.ts +4 -0
- package/src/modes/interactive/components/status-line/presets.ts +94 -0
- package/src/modes/interactive/components/status-line/segments.ts +350 -0
- package/src/modes/interactive/components/status-line/separators.ts +55 -0
- package/src/modes/interactive/components/status-line/types.ts +81 -0
- package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
- package/src/modes/interactive/components/status-line.ts +384 -0
- package/src/modes/interactive/components/theme-selector.ts +62 -0
- package/src/modes/interactive/components/thinking-selector.ts +64 -0
- package/src/modes/interactive/components/tool-execution.ts +946 -0
- package/src/modes/interactive/components/tree-selector.ts +877 -0
- package/src/modes/interactive/components/ttsr-notification.ts +82 -0
- package/src/modes/interactive/components/user-message-selector.ts +159 -0
- package/src/modes/interactive/components/user-message.ts +18 -0
- package/src/modes/interactive/components/visual-truncate.ts +50 -0
- package/src/modes/interactive/components/welcome.ts +228 -0
- package/src/modes/interactive/interactive-mode.ts +2669 -0
- package/src/modes/interactive/theme/dark.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
- package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
- package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
- package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
- package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
- package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
- package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
- package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
- package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
- package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
- package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
- package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
- package/src/modes/interactive/theme/defaults/index.ts +67 -0
- package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
- package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
- package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
- package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
- package/src/modes/interactive/theme/defaults/light-github.json +114 -0
- package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
- package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
- package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
- package/src/modes/interactive/theme/defaults/light-one.json +105 -0
- package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
- package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
- package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
- package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
- package/src/modes/interactive/theme/light.json +99 -0
- package/src/modes/interactive/theme/theme-schema.json +424 -0
- package/src/modes/interactive/theme/theme.ts +2211 -0
- package/src/modes/print-mode.ts +163 -0
- package/src/modes/rpc/rpc-client.ts +527 -0
- package/src/modes/rpc/rpc-mode.ts +494 -0
- package/src/modes/rpc/rpc-types.ts +203 -0
- package/src/prompts/architect-plan.md +10 -0
- package/src/prompts/branch-summary-preamble.md +3 -0
- package/src/prompts/branch-summary.md +28 -0
- package/src/prompts/browser.md +71 -0
- package/src/prompts/compaction-summary.md +34 -0
- package/src/prompts/compaction-turn-prefix.md +16 -0
- package/src/prompts/compaction-update-summary.md +41 -0
- package/src/prompts/explore.md +82 -0
- package/src/prompts/implement-with-critic.md +11 -0
- package/src/prompts/implement.md +11 -0
- package/src/prompts/init.md +30 -0
- package/src/prompts/plan.md +54 -0
- package/src/prompts/reviewer.md +81 -0
- package/src/prompts/summarization-system.md +3 -0
- package/src/prompts/system-prompt.md +27 -0
- package/src/prompts/task.md +56 -0
- package/src/prompts/title-system.md +8 -0
- package/src/prompts/tools/ask.md +24 -0
- package/src/prompts/tools/bash.md +23 -0
- package/src/prompts/tools/edit.md +9 -0
- package/src/prompts/tools/find.md +6 -0
- package/src/prompts/tools/grep.md +12 -0
- package/src/prompts/tools/lsp.md +14 -0
- package/src/prompts/tools/output.md +23 -0
- package/src/prompts/tools/read.md +25 -0
- package/src/prompts/tools/web-fetch.md +8 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +10 -0
- package/src/utils/changelog.ts +99 -0
- package/src/utils/clipboard.ts +265 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/mime.ts +30 -0
- package/src/utils/shell-snapshot.ts +218 -0
- package/src/utils/shell.ts +364 -0
- package/src/utils/tools-manager.ts +265 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Biome CLI-based linter client.
|
|
3
|
+
* Uses Biome's CLI with JSON output instead of LSP (which has stale diagnostics issues).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import type { Diagnostic, DiagnosticSeverity, LinterClient, ServerConfig } from "../types";
|
|
8
|
+
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// Biome JSON Output Types
|
|
11
|
+
// =============================================================================
|
|
12
|
+
|
|
13
|
+
interface BiomeJsonOutput {
|
|
14
|
+
diagnostics: BiomeDiagnostic[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface BiomeDiagnostic {
|
|
18
|
+
category: string; // e.g., "lint/correctness/noUnusedVariables"
|
|
19
|
+
severity: "error" | "warning" | "info" | "hint";
|
|
20
|
+
description: string;
|
|
21
|
+
location?: {
|
|
22
|
+
path?: { file: string };
|
|
23
|
+
span?: [number, number]; // [startOffset, endOffset] in bytes
|
|
24
|
+
sourceCode?: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// =============================================================================
|
|
29
|
+
// Helpers
|
|
30
|
+
// =============================================================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Convert byte offset to line:column using source code.
|
|
34
|
+
*/
|
|
35
|
+
function offsetToPosition(source: string, offset: number): { line: number; column: number } {
|
|
36
|
+
let line = 1;
|
|
37
|
+
let column = 1;
|
|
38
|
+
let byteIndex = 0;
|
|
39
|
+
|
|
40
|
+
for (const ch of source) {
|
|
41
|
+
const byteLen = Buffer.byteLength(ch);
|
|
42
|
+
if (byteIndex + byteLen > offset) {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
if (ch === "\n") {
|
|
46
|
+
line++;
|
|
47
|
+
column = 1;
|
|
48
|
+
} else {
|
|
49
|
+
column++;
|
|
50
|
+
}
|
|
51
|
+
byteIndex += byteLen;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { line, column };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parse Biome severity to LSP DiagnosticSeverity.
|
|
59
|
+
*/
|
|
60
|
+
function parseSeverity(severity: string): DiagnosticSeverity {
|
|
61
|
+
switch (severity) {
|
|
62
|
+
case "error":
|
|
63
|
+
return 1;
|
|
64
|
+
case "warning":
|
|
65
|
+
return 2;
|
|
66
|
+
case "info":
|
|
67
|
+
return 3;
|
|
68
|
+
case "hint":
|
|
69
|
+
return 4;
|
|
70
|
+
default:
|
|
71
|
+
return 2;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Run a Biome CLI command.
|
|
77
|
+
*/
|
|
78
|
+
async function runBiome(
|
|
79
|
+
args: string[],
|
|
80
|
+
cwd: string,
|
|
81
|
+
resolvedCommand?: string,
|
|
82
|
+
): Promise<{ stdout: string; stderr: string; success: boolean }> {
|
|
83
|
+
const command = resolvedCommand ?? "biome";
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const proc = Bun.spawn([command, ...args], {
|
|
87
|
+
cwd,
|
|
88
|
+
stdout: "pipe",
|
|
89
|
+
stderr: "pipe",
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const [stdout, stderr] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text()]);
|
|
93
|
+
const exitCode = await proc.exited;
|
|
94
|
+
|
|
95
|
+
return { stdout, stderr, success: exitCode === 0 };
|
|
96
|
+
} catch (err) {
|
|
97
|
+
return { stdout: "", stderr: String(err), success: false };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// =============================================================================
|
|
102
|
+
// Biome Client
|
|
103
|
+
// =============================================================================
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Biome CLI-based linter client.
|
|
107
|
+
* Parses Biome's --reporter=json output into LSP Diagnostic format.
|
|
108
|
+
*/
|
|
109
|
+
export class BiomeClient implements LinterClient {
|
|
110
|
+
private config: ServerConfig;
|
|
111
|
+
private cwd: string;
|
|
112
|
+
|
|
113
|
+
constructor(config: ServerConfig, cwd: string) {
|
|
114
|
+
this.config = config;
|
|
115
|
+
this.cwd = cwd;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async format(filePath: string, content: string): Promise<string> {
|
|
119
|
+
// Write content to file first
|
|
120
|
+
await Bun.write(filePath, content);
|
|
121
|
+
|
|
122
|
+
// Run biome format --write
|
|
123
|
+
const result = await runBiome(["format", "--write", filePath], this.cwd, this.config.resolvedCommand);
|
|
124
|
+
|
|
125
|
+
if (result.success) {
|
|
126
|
+
// Read back formatted content
|
|
127
|
+
return await Bun.file(filePath).text();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Format failed, return original
|
|
131
|
+
return content;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async lint(filePath: string): Promise<Diagnostic[]> {
|
|
135
|
+
// Run biome lint with JSON reporter
|
|
136
|
+
const result = await runBiome(["lint", "--reporter=json", filePath], this.cwd, this.config.resolvedCommand);
|
|
137
|
+
|
|
138
|
+
return this.parseJsonOutput(result.stdout, filePath);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Parse Biome's JSON output into LSP Diagnostics.
|
|
143
|
+
*/
|
|
144
|
+
private parseJsonOutput(jsonOutput: string, targetFile: string): Diagnostic[] {
|
|
145
|
+
const diagnostics: Diagnostic[] = [];
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const parsed: BiomeJsonOutput = JSON.parse(jsonOutput);
|
|
149
|
+
|
|
150
|
+
for (const diag of parsed.diagnostics) {
|
|
151
|
+
const location = diag.location;
|
|
152
|
+
if (!location?.path?.file) continue;
|
|
153
|
+
|
|
154
|
+
// Resolve file path
|
|
155
|
+
const diagFile = path.isAbsolute(location.path.file)
|
|
156
|
+
? location.path.file
|
|
157
|
+
: path.join(this.cwd, location.path.file);
|
|
158
|
+
|
|
159
|
+
// Only include diagnostics for the target file
|
|
160
|
+
if (path.resolve(diagFile) !== path.resolve(targetFile)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Convert byte offset to line:column
|
|
165
|
+
let startLine = 1;
|
|
166
|
+
let startColumn = 1;
|
|
167
|
+
let endLine = 1;
|
|
168
|
+
let endColumn = 1;
|
|
169
|
+
|
|
170
|
+
if (location.span && location.sourceCode) {
|
|
171
|
+
const startPos = offsetToPosition(location.sourceCode, location.span[0]);
|
|
172
|
+
const endPos = offsetToPosition(location.sourceCode, location.span[1]);
|
|
173
|
+
startLine = startPos.line;
|
|
174
|
+
startColumn = startPos.column;
|
|
175
|
+
endLine = endPos.line;
|
|
176
|
+
endColumn = endPos.column;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
diagnostics.push({
|
|
180
|
+
range: {
|
|
181
|
+
start: { line: startLine - 1, character: startColumn - 1 },
|
|
182
|
+
end: { line: endLine - 1, character: endColumn - 1 },
|
|
183
|
+
},
|
|
184
|
+
severity: parseSeverity(diag.severity),
|
|
185
|
+
message: diag.description,
|
|
186
|
+
source: "biome",
|
|
187
|
+
code: diag.category,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
// JSON parse failed, return empty
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return diagnostics;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
dispose(): void {
|
|
198
|
+
// Nothing to dispose for CLI client
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Factory function to create a Biome client.
|
|
204
|
+
*/
|
|
205
|
+
export function createBiomeClient(config: ServerConfig, cwd: string): LinterClient {
|
|
206
|
+
return new BiomeClient(config, cwd);
|
|
207
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linter client implementations.
|
|
3
|
+
*
|
|
4
|
+
* The LinterClient interface provides a common API for formatters and linters.
|
|
5
|
+
* Different implementations can use LSP protocol, CLI tools, or other mechanisms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { BiomeClient, createBiomeClient } from "./biome-client";
|
|
9
|
+
export { createLspLinterClient, LspLinterClient } from "./lsp-linter-client";
|
|
10
|
+
|
|
11
|
+
import type { LinterClient, ServerConfig } from "../types";
|
|
12
|
+
import { createLspLinterClient } from "./lsp-linter-client";
|
|
13
|
+
|
|
14
|
+
// Cache of linter clients by server name + cwd
|
|
15
|
+
const clientCache = new Map<string, LinterClient>();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get or create a linter client for a server configuration.
|
|
19
|
+
* Uses the server's custom factory if provided, otherwise falls back to LSP.
|
|
20
|
+
*/
|
|
21
|
+
export function getLinterClient(serverName: string, config: ServerConfig, cwd: string): LinterClient {
|
|
22
|
+
const key = `${serverName}:${cwd}`;
|
|
23
|
+
|
|
24
|
+
let client = clientCache.get(key);
|
|
25
|
+
if (client) {
|
|
26
|
+
return client;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Use custom factory if provided
|
|
30
|
+
if (config.createClient) {
|
|
31
|
+
client = config.createClient(config, cwd);
|
|
32
|
+
} else {
|
|
33
|
+
// Default to LSP
|
|
34
|
+
client = createLspLinterClient(config, cwd);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
clientCache.set(key, client);
|
|
38
|
+
return client;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Clear all cached linter clients.
|
|
43
|
+
*/
|
|
44
|
+
export function clearLinterClientCache(): void {
|
|
45
|
+
for (const client of clientCache.values()) {
|
|
46
|
+
client.dispose?.();
|
|
47
|
+
}
|
|
48
|
+
clientCache.clear();
|
|
49
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LSP-based linter client.
|
|
3
|
+
* Uses the Language Server Protocol for formatting and diagnostics.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getOrCreateClient, notifySaved, sendRequest, syncContent } from "../client";
|
|
7
|
+
import { applyTextEditsToString } from "../edits";
|
|
8
|
+
import type { Diagnostic, LinterClient, LspClient, ServerConfig, TextEdit } from "../types";
|
|
9
|
+
import { fileToUri } from "../utils";
|
|
10
|
+
|
|
11
|
+
/** Default formatting options for LSP */
|
|
12
|
+
const DEFAULT_FORMAT_OPTIONS = {
|
|
13
|
+
tabSize: 3,
|
|
14
|
+
insertSpaces: true,
|
|
15
|
+
trimTrailingWhitespace: true,
|
|
16
|
+
insertFinalNewline: true,
|
|
17
|
+
trimFinalNewlines: true,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* LSP-based linter client implementation.
|
|
22
|
+
* Wraps the existing LSP client infrastructure.
|
|
23
|
+
*/
|
|
24
|
+
export class LspLinterClient implements LinterClient {
|
|
25
|
+
private config: ServerConfig;
|
|
26
|
+
private cwd: string;
|
|
27
|
+
private client: LspClient | null = null;
|
|
28
|
+
|
|
29
|
+
constructor(config: ServerConfig, cwd: string) {
|
|
30
|
+
this.config = config;
|
|
31
|
+
this.cwd = cwd;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private async getClient(): Promise<LspClient> {
|
|
35
|
+
if (!this.client) {
|
|
36
|
+
this.client = await getOrCreateClient(this.config, this.cwd);
|
|
37
|
+
}
|
|
38
|
+
return this.client;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async format(filePath: string, content: string): Promise<string> {
|
|
42
|
+
const client = await this.getClient();
|
|
43
|
+
const uri = fileToUri(filePath);
|
|
44
|
+
|
|
45
|
+
// Sync content to LSP
|
|
46
|
+
await syncContent(client, filePath, content);
|
|
47
|
+
|
|
48
|
+
// Check if server supports formatting
|
|
49
|
+
const caps = client.serverCapabilities;
|
|
50
|
+
if (!caps?.documentFormattingProvider) {
|
|
51
|
+
return content;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Request formatting
|
|
55
|
+
const edits = (await sendRequest(client, "textDocument/formatting", {
|
|
56
|
+
textDocument: { uri },
|
|
57
|
+
options: DEFAULT_FORMAT_OPTIONS,
|
|
58
|
+
})) as TextEdit[] | null;
|
|
59
|
+
|
|
60
|
+
if (!edits || edits.length === 0) {
|
|
61
|
+
return content;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return applyTextEditsToString(content, edits);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async lint(filePath: string): Promise<Diagnostic[]> {
|
|
68
|
+
const client = await this.getClient();
|
|
69
|
+
const uri = fileToUri(filePath);
|
|
70
|
+
|
|
71
|
+
// Notify that file was saved to trigger diagnostics
|
|
72
|
+
await notifySaved(client, filePath);
|
|
73
|
+
|
|
74
|
+
// Wait for diagnostics with timeout
|
|
75
|
+
const timeoutMs = 3000;
|
|
76
|
+
const start = Date.now();
|
|
77
|
+
while (Date.now() - start < timeoutMs) {
|
|
78
|
+
const diagnostics = client.diagnostics.get(uri);
|
|
79
|
+
if (diagnostics !== undefined) {
|
|
80
|
+
return diagnostics;
|
|
81
|
+
}
|
|
82
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return client.diagnostics.get(uri) ?? [];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
dispose(): void {
|
|
89
|
+
// Client lifecycle is managed globally, nothing to dispose here
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Factory function to create an LSP linter client.
|
|
95
|
+
*/
|
|
96
|
+
export function createLspLinterClient(config: ServerConfig, cwd: string): LinterClient {
|
|
97
|
+
return new LspLinterClient(config, cwd);
|
|
98
|
+
}
|