@mrclrchtr/supi-code-intelligence 1.5.0 → 1.7.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.
Files changed (80) hide show
  1. package/README.md +42 -24
  2. package/node_modules/@mrclrchtr/supi-core/package.json +6 -2
  3. package/node_modules/@mrclrchtr/supi-core/src/api.ts +12 -1
  4. package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +1 -1
  5. package/node_modules/@mrclrchtr/supi-core/src/index.ts +12 -1
  6. package/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +116 -0
  7. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +6 -2
  8. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +12 -1
  9. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config.ts +1 -1
  10. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +12 -1
  11. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +116 -0
  12. package/node_modules/@mrclrchtr/supi-lsp/package.json +10 -3
  13. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +8 -5
  14. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +79 -190
  15. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +38 -0
  16. package/node_modules/@mrclrchtr/supi-lsp/src/config/types.ts +61 -387
  17. package/node_modules/@mrclrchtr/supi-lsp/src/format.ts +16 -8
  18. package/node_modules/@mrclrchtr/supi-lsp/src/lsp.ts +2 -2
  19. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +1 -1
  20. package/node_modules/@mrclrchtr/supi-lsp/src/pattern-matcher.ts +11 -184
  21. package/node_modules/@mrclrchtr/supi-lsp/src/session/lsp-state.ts +1 -1
  22. package/node_modules/@mrclrchtr/supi-lsp/src/tool/guidance.ts +1 -1
  23. package/node_modules/@mrclrchtr/supi-lsp/src/tool/tool-specs.ts +1 -1
  24. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +6 -2
  25. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/api.ts +12 -1
  26. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config.ts +1 -1
  27. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +12 -1
  28. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +116 -0
  29. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/LICENSE +21 -0
  30. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/README.md +265 -0
  31. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs +4661 -0
  32. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs.map +7 -0
  33. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js +4605 -0
  34. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js.map +7 -0
  35. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
  36. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +57 -0
  37. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +100 -0
  38. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs +4063 -0
  39. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs.map +7 -0
  40. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts +1025 -0
  41. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts.map +58 -0
  42. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts +1025 -0
  43. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts.map +58 -0
  44. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js +4007 -0
  45. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js.map +7 -0
  46. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
  47. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +55 -0
  48. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +2 -2
  49. package/package.json +4 -4
  50. package/src/actions/affected-action.ts +67 -54
  51. package/src/actions/brief-action.ts +142 -5
  52. package/src/actions/callees-action.ts +1 -1
  53. package/src/actions/callers-action.ts +38 -67
  54. package/src/actions/implementations-action.ts +27 -63
  55. package/src/actions/map-action.ts +206 -0
  56. package/src/actions/pattern-action.ts +1 -1
  57. package/src/api.ts +1 -0
  58. package/src/brief-focused.ts +5 -5
  59. package/src/brief.ts +3 -3
  60. package/src/code-intelligence.ts +6 -75
  61. package/src/index.ts +1 -0
  62. package/src/pattern-structured.ts +1 -1
  63. package/src/prioritization-signals.ts +13 -26
  64. package/src/query-params.ts +15 -0
  65. package/src/resolve-target.ts +2 -2
  66. package/src/search-helpers.ts +2 -2
  67. package/src/target-resolution.ts +27 -102
  68. package/src/tool/execute-affected.ts +25 -0
  69. package/src/tool/execute-brief.ts +25 -0
  70. package/src/tool/execute-map.ts +32 -0
  71. package/src/tool/execute-pattern.ts +26 -0
  72. package/src/tool/execute-relations.ts +48 -0
  73. package/src/tool/guidance.ts +24 -13
  74. package/src/tool/register-tools.ts +32 -0
  75. package/src/tool/tool-specs.ts +184 -0
  76. package/src/tool/validation.ts +43 -0
  77. package/src/types.ts +10 -0
  78. package/src/actions/index-action.ts +0 -187
  79. package/src/tool/action-specs.ts +0 -66
  80. package/src/tool-actions.ts +0 -100
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @mrclrchtr/supi-code-intelligence
2
2
 
3
- Adds a `code_intel` tool to the [pi coding agent](https://github.com/earendil-works/pi) for higher-level codebase analysis.
3
+ Adds a focused code-understanding toolset to the [pi coding agent](https://github.com/earendil-works/pi).
4
4
 
5
5
  ## Install
6
6
 
@@ -20,7 +20,11 @@ After editing the source, run `/reload`.
20
20
 
21
21
  After install, pi gets:
22
22
 
23
- - `code_intel` — one tool with several analysis actions
23
+ - `code_brief` — interpretive orientation for a project, package, directory, file, or symbol
24
+ - `code_map` — factual repo/package/directory inventory
25
+ - `code_relations` — callers, callees, or implementations for a resolved target
26
+ - `code_affected` — blast radius, downstream impact, and risk for a target
27
+ - `code_pattern` — explicit literal, regex, or structured search
24
28
  - a lightweight hidden architecture overview injected near the start of a session when a project model can be built
25
29
  - bundled support from `@mrclrchtr/supi-lsp`, `@mrclrchtr/supi-tree-sitter`, and `@mrclrchtr/supi-core`
26
30
 
@@ -32,53 +36,65 @@ This package is for questions like:
32
36
  - what is the likely blast radius of a change?
33
37
  - where is this pattern defined, imported, or exported?
34
38
 
35
- ## `code_intel` actions
39
+ ## Tool overview
36
40
 
37
- | Action | What it is for |
38
- | --- | --- |
39
- | `brief` | Generate a project, package, directory, file, or symbol-focused brief |
40
- | `callers` | Find call sites for a symbol, or inspect a file's exported surface |
41
- | `callees` | Show outgoing calls from a symbol using structural analysis |
42
- | `implementations` | Find concrete implementations of an interface or abstract type |
43
- | `affected` | Estimate blast radius, affected files/modules, downstream dependents, and risk |
44
- | `pattern` | Run bounded text search with optional regex mode and structured `kind` filters |
45
- | `index` | Build a factual project map: counts, top-level tree, and landmark files |
41
+ ### `code_brief`
42
+ Interpretive orientation. Use for prioritized context, start-here guidance, and project/package/directory/file/symbol overview.
46
43
 
47
- ## Input shape
44
+ ### `code_map`
45
+ Strictly factual inventory. Accepts the repo root, a package root, or **any directory path**. Rejects file paths.
48
46
 
49
- The tool accepts a shared parameter set across actions. The main fields are:
47
+ ### `code_relations`
48
+ Relationship tracing tool with:
49
+ - `kind: "callers"`
50
+ - `kind: "callees"`
51
+ - `kind: "implementations"`
50
52
 
51
- - `action`
53
+ `callers` and `implementations` are semantic-only. `callees` is structural-only.
54
+
55
+ ### `code_affected`
56
+ Semantic blast-radius analysis for a concrete target. This tool no longer falls back to grep-style guesses.
57
+
58
+ ### `code_pattern`
59
+ Explicit search tool for:
60
+ - literal search
61
+ - regex search (`regex: true`)
62
+ - structured search (`kind: "definition" | "export" | "import"`)
63
+
64
+ This is the only tool in the family that intentionally returns heuristic/text-search results.
65
+
66
+ ## Shared input conventions
67
+
68
+ Depending on the tool, inputs may include:
52
69
  - `path`
53
70
  - `file`
54
71
  - `line`
55
72
  - `character`
56
73
  - `symbol`
74
+ - `kind`
57
75
  - `pattern`
58
76
  - `regex`
59
- - `kind`
60
77
  - `exportedOnly`
61
78
  - `maxResults`
62
79
  - `contextLines`
63
80
  - `summary`
64
81
 
65
- Notes from the current implementation:
66
-
82
+ Notes:
67
83
  - line and character positions are **1-based**
68
84
  - `line` and `character` require `file`, not `path`
69
- - `pattern` action `kind` must be `definition`, `export`, or `import`
70
85
  - a leading `@` is stripped from `path` and `file`
86
+ - non-search tools do **not** silently fall back to heuristic grep behavior
71
87
 
72
88
  ## Result style
73
89
 
74
- Depending on the action and what supporting services are available, results report different confidence modes such as:
90
+ Results report confidence such as:
75
91
 
76
92
  - `semantic`
77
93
  - `structural`
78
94
  - `heuristic`
79
95
  - `unavailable`
80
96
 
81
- That lets the model tell the difference between LSP-backed answers, tree-sitter-backed answers, and weaker text-search fallbacks.
97
+ `heuristic` is now primarily a `code_pattern` concern. The other tools prefer explicit unavailable states over silent search fallbacks.
82
98
 
83
99
  ## Package surfaces
84
100
 
@@ -96,6 +112,8 @@ const overview = generateOverview(model);
96
112
 
97
113
  ## Source
98
114
 
99
- - `src/code-intelligence.ts` — tool registration and session overview injection
100
- - `src/tool-actions.ts` — action routing and parameter validation
101
- - `src/actions/*.ts` — per-action implementations
115
+ - `src/code-intelligence.ts` — focused tool registration and session overview injection
116
+ - `src/tool/tool-specs.ts` — single source of truth for the public tool surface
117
+ - `src/tool/register-tools.ts` — focused tool registration wiring
118
+ - `src/tool/guidance.ts` — prompt surfaces derived from tool specs
119
+ - `src/actions/*.ts` — domain implementations
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "SuPi core — shared infrastructure for SuPi extensions (XML context tags, config system)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -20,7 +20,8 @@
20
20
  ],
21
21
  "peerDependencies": {
22
22
  "@earendil-works/pi-coding-agent": "*",
23
- "@earendil-works/pi-tui": "*"
23
+ "@earendil-works/pi-tui": "*",
24
+ "typebox": "*"
24
25
  },
25
26
  "peerDependenciesMeta": {
26
27
  "@earendil-works/pi-coding-agent": {
@@ -28,6 +29,9 @@
28
29
  },
29
30
  "@earendil-works/pi-tui": {
30
31
  "optional": true
32
+ },
33
+ "typebox": {
34
+ "optional": true
31
35
  }
32
36
  },
33
37
  "main": "src/api.ts",
@@ -1,11 +1,12 @@
1
1
  // supi-core — shared infrastructure for SuPi extensions.
2
2
  // Provides XML context tag wrapping, unified config system, context-message utilities,
3
- // and settings registry for supi-wide TUI settings.
3
+ // settings registry for supi-wide TUI settings, and a shared tool-spec/registration framework.
4
4
 
5
5
  export type { SupiConfigLocation, SupiConfigOptions } from "./config/config.ts";
6
6
  export {
7
7
  loadSupiConfig,
8
8
  loadSupiConfigForScope,
9
+ readJsonFile,
9
10
  removeSupiConfigKey,
10
11
  writeSupiConfig,
11
12
  } from "./config/config.ts";
@@ -83,3 +84,13 @@ export {
83
84
  signalWaiting,
84
85
  WAITING_SYMBOL,
85
86
  } from "./terminal.ts";
87
+ export type { SuiPiToolPromptSurface, SuiPiToolSpec, ToolExecuteFn } from "./tool-framework.ts";
88
+ export {
89
+ CharacterParam,
90
+ derivePromptSurface,
91
+ FileParam,
92
+ LineParam,
93
+ MaxResultsParam,
94
+ registerSuiPiTools,
95
+ SymbolParam,
96
+ } from "./tool-framework.ts";
@@ -20,7 +20,7 @@ function getProjectConfigPath(cwd: string): string {
20
20
  return path.join(cwd, PROJECT_CONFIG_DIR, CONFIG_FILE);
21
21
  }
22
22
 
23
- function readJsonFile(filePath: string): Record<string, unknown> | null {
23
+ export function readJsonFile(filePath: string): Record<string, unknown> | null {
24
24
  let content: string;
25
25
  try {
26
26
  content = fs.readFileSync(filePath, "utf-8");
@@ -1,11 +1,12 @@
1
1
  // supi-core — shared infrastructure for SuPi extensions.
2
2
  // Provides XML context tag wrapping, unified config system, context-message utilities,
3
- // and settings registry for supi-wide TUI settings.
3
+ // settings registry for supi-wide TUI settings, and a shared tool-spec/registration framework.
4
4
 
5
5
  export type { SupiConfigLocation, SupiConfigOptions } from "./config/config.ts";
6
6
  export {
7
7
  loadSupiConfig,
8
8
  loadSupiConfigForScope,
9
+ readJsonFile,
9
10
  removeSupiConfigKey,
10
11
  writeSupiConfig,
11
12
  } from "./config/config.ts";
@@ -83,3 +84,13 @@ export {
83
84
  signalWaiting,
84
85
  WAITING_SYMBOL,
85
86
  } from "./terminal.ts";
87
+ export type { SuiPiToolPromptSurface, SuiPiToolSpec, ToolExecuteFn } from "./tool-framework.ts";
88
+ export {
89
+ CharacterParam,
90
+ derivePromptSurface,
91
+ FileParam,
92
+ LineParam,
93
+ MaxResultsParam,
94
+ registerSuiPiTools,
95
+ SymbolParam,
96
+ } from "./tool-framework.ts";
@@ -0,0 +1,116 @@
1
+ // Shared tool framework for SuPi extensions.
2
+ //
3
+ // Provides a standard ToolSpec→PromptSurface→registerTool pipeline so
4
+ // individual packages do not duplicate spec interfaces, guidance derivation,
5
+ // registration loops, or common TypeBox parameter schemas.
6
+
7
+ import type {
8
+ AgentToolResult,
9
+ AgentToolUpdateCallback,
10
+ ExtensionAPI,
11
+ ExtensionContext,
12
+ } from "@earendil-works/pi-coding-agent";
13
+ import { type TSchema, Type } from "typebox";
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Types
17
+ // ---------------------------------------------------------------------------
18
+
19
+ /** Minimum contract for a SuPi tool definition. */
20
+ export interface SuiPiToolSpec {
21
+ name: string;
22
+ label: string;
23
+ description: string;
24
+ promptSnippet: string;
25
+ promptGuidelines: string[];
26
+ parameters: TSchema;
27
+ }
28
+
29
+ /** Derived prompt surface — what pi flattens into the system prompt. */
30
+ export interface SuiPiToolPromptSurface {
31
+ description: string;
32
+ promptSnippet: string;
33
+ promptGuidelines: string[];
34
+ }
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Guidance derivation
38
+ // ---------------------------------------------------------------------------
39
+
40
+ /**
41
+ * Static derivation: copies spec fields into a prompt surface.
42
+ *
43
+ * Packages that need dynamic guidance (e.g. server-coverage injection) should
44
+ * build their own surfaces, optionally starting from the output of this helper.
45
+ */
46
+ export function derivePromptSurface(spec: SuiPiToolSpec): SuiPiToolPromptSurface {
47
+ return {
48
+ description: spec.description,
49
+ promptSnippet: spec.promptSnippet,
50
+ promptGuidelines: [...spec.promptGuidelines],
51
+ };
52
+ }
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // Registration
56
+ // ---------------------------------------------------------------------------
57
+
58
+ // biome-ignore lint/complexity/useMaxParams: matches pi ToolDefinition.execute signature
59
+ export type ToolExecuteFn = (
60
+ toolCallId: string,
61
+ params: unknown,
62
+ signal: AbortSignal | undefined,
63
+ onUpdate: AgentToolUpdateCallback<Record<string, unknown>> | undefined,
64
+ ctx: ExtensionContext,
65
+ ) => Promise<AgentToolResult<Record<string, unknown>>>;
66
+
67
+ /**
68
+ * Register a set of tools from specs + pre-derived surfaces.
69
+ *
70
+ * `createExecute` receives the spec and returns a pi-compatible execute
71
+ * function. This keeps execute-logic package-local while the framework owns
72
+ * the declarative surface and registration boilerplate.
73
+ */
74
+ export function registerSuiPiTools(
75
+ pi: ExtensionAPI,
76
+ specs: readonly SuiPiToolSpec[],
77
+ surfaces: Record<string, SuiPiToolPromptSurface>,
78
+ createExecute: (spec: SuiPiToolSpec) => ToolExecuteFn,
79
+ ): void {
80
+ for (const spec of specs) {
81
+ const surface = surfaces[spec.name];
82
+ pi.registerTool({
83
+ name: spec.name,
84
+ label: spec.label,
85
+ description: surface?.description ?? spec.description,
86
+ promptSnippet: surface?.promptSnippet ?? spec.promptSnippet,
87
+ promptGuidelines: surface?.promptGuidelines ?? [...spec.promptGuidelines],
88
+ parameters: spec.parameters,
89
+ execute: createExecute(spec),
90
+ });
91
+ }
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Shared parameter builders
96
+ // ---------------------------------------------------------------------------
97
+
98
+ /** File path (relative or absolute). */
99
+ export const FileParam = Type.String({ description: "File path (relative or absolute)" });
100
+
101
+ /** 1-based line number. */
102
+ export const LineParam = Type.Number({ description: "1-based line number", minimum: 1 });
103
+
104
+ /** 1-based character column (UTF-16). */
105
+ export const CharacterParam = Type.Number({
106
+ description: "1-based column number (UTF-16)",
107
+ minimum: 1,
108
+ });
109
+
110
+ /** Symbol name for discovery-based resolution. */
111
+ export const SymbolParam = Type.String({
112
+ description: "Symbol name for discovery-based resolution",
113
+ });
114
+
115
+ /** Maximum results to return. */
116
+ export const MaxResultsParam = Type.Number({ description: "Maximum results to return" });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "SuPi core — shared infrastructure for SuPi extensions (XML context tags, config system)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -20,7 +20,8 @@
20
20
  ],
21
21
  "peerDependencies": {
22
22
  "@earendil-works/pi-coding-agent": "*",
23
- "@earendil-works/pi-tui": "*"
23
+ "@earendil-works/pi-tui": "*",
24
+ "typebox": "*"
24
25
  },
25
26
  "peerDependenciesMeta": {
26
27
  "@earendil-works/pi-coding-agent": {
@@ -28,6 +29,9 @@
28
29
  },
29
30
  "@earendil-works/pi-tui": {
30
31
  "optional": true
32
+ },
33
+ "typebox": {
34
+ "optional": true
31
35
  }
32
36
  },
33
37
  "main": "src/api.ts",
@@ -1,11 +1,12 @@
1
1
  // supi-core — shared infrastructure for SuPi extensions.
2
2
  // Provides XML context tag wrapping, unified config system, context-message utilities,
3
- // and settings registry for supi-wide TUI settings.
3
+ // settings registry for supi-wide TUI settings, and a shared tool-spec/registration framework.
4
4
 
5
5
  export type { SupiConfigLocation, SupiConfigOptions } from "./config/config.ts";
6
6
  export {
7
7
  loadSupiConfig,
8
8
  loadSupiConfigForScope,
9
+ readJsonFile,
9
10
  removeSupiConfigKey,
10
11
  writeSupiConfig,
11
12
  } from "./config/config.ts";
@@ -83,3 +84,13 @@ export {
83
84
  signalWaiting,
84
85
  WAITING_SYMBOL,
85
86
  } from "./terminal.ts";
87
+ export type { SuiPiToolPromptSurface, SuiPiToolSpec, ToolExecuteFn } from "./tool-framework.ts";
88
+ export {
89
+ CharacterParam,
90
+ derivePromptSurface,
91
+ FileParam,
92
+ LineParam,
93
+ MaxResultsParam,
94
+ registerSuiPiTools,
95
+ SymbolParam,
96
+ } from "./tool-framework.ts";
@@ -20,7 +20,7 @@ function getProjectConfigPath(cwd: string): string {
20
20
  return path.join(cwd, PROJECT_CONFIG_DIR, CONFIG_FILE);
21
21
  }
22
22
 
23
- function readJsonFile(filePath: string): Record<string, unknown> | null {
23
+ export function readJsonFile(filePath: string): Record<string, unknown> | null {
24
24
  let content: string;
25
25
  try {
26
26
  content = fs.readFileSync(filePath, "utf-8");
@@ -1,11 +1,12 @@
1
1
  // supi-core — shared infrastructure for SuPi extensions.
2
2
  // Provides XML context tag wrapping, unified config system, context-message utilities,
3
- // and settings registry for supi-wide TUI settings.
3
+ // settings registry for supi-wide TUI settings, and a shared tool-spec/registration framework.
4
4
 
5
5
  export type { SupiConfigLocation, SupiConfigOptions } from "./config/config.ts";
6
6
  export {
7
7
  loadSupiConfig,
8
8
  loadSupiConfigForScope,
9
+ readJsonFile,
9
10
  removeSupiConfigKey,
10
11
  writeSupiConfig,
11
12
  } from "./config/config.ts";
@@ -83,3 +84,13 @@ export {
83
84
  signalWaiting,
84
85
  WAITING_SYMBOL,
85
86
  } from "./terminal.ts";
87
+ export type { SuiPiToolPromptSurface, SuiPiToolSpec, ToolExecuteFn } from "./tool-framework.ts";
88
+ export {
89
+ CharacterParam,
90
+ derivePromptSurface,
91
+ FileParam,
92
+ LineParam,
93
+ MaxResultsParam,
94
+ registerSuiPiTools,
95
+ SymbolParam,
96
+ } from "./tool-framework.ts";
@@ -0,0 +1,116 @@
1
+ // Shared tool framework for SuPi extensions.
2
+ //
3
+ // Provides a standard ToolSpec→PromptSurface→registerTool pipeline so
4
+ // individual packages do not duplicate spec interfaces, guidance derivation,
5
+ // registration loops, or common TypeBox parameter schemas.
6
+
7
+ import type {
8
+ AgentToolResult,
9
+ AgentToolUpdateCallback,
10
+ ExtensionAPI,
11
+ ExtensionContext,
12
+ } from "@earendil-works/pi-coding-agent";
13
+ import { type TSchema, Type } from "typebox";
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Types
17
+ // ---------------------------------------------------------------------------
18
+
19
+ /** Minimum contract for a SuPi tool definition. */
20
+ export interface SuiPiToolSpec {
21
+ name: string;
22
+ label: string;
23
+ description: string;
24
+ promptSnippet: string;
25
+ promptGuidelines: string[];
26
+ parameters: TSchema;
27
+ }
28
+
29
+ /** Derived prompt surface — what pi flattens into the system prompt. */
30
+ export interface SuiPiToolPromptSurface {
31
+ description: string;
32
+ promptSnippet: string;
33
+ promptGuidelines: string[];
34
+ }
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Guidance derivation
38
+ // ---------------------------------------------------------------------------
39
+
40
+ /**
41
+ * Static derivation: copies spec fields into a prompt surface.
42
+ *
43
+ * Packages that need dynamic guidance (e.g. server-coverage injection) should
44
+ * build their own surfaces, optionally starting from the output of this helper.
45
+ */
46
+ export function derivePromptSurface(spec: SuiPiToolSpec): SuiPiToolPromptSurface {
47
+ return {
48
+ description: spec.description,
49
+ promptSnippet: spec.promptSnippet,
50
+ promptGuidelines: [...spec.promptGuidelines],
51
+ };
52
+ }
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // Registration
56
+ // ---------------------------------------------------------------------------
57
+
58
+ // biome-ignore lint/complexity/useMaxParams: matches pi ToolDefinition.execute signature
59
+ export type ToolExecuteFn = (
60
+ toolCallId: string,
61
+ params: unknown,
62
+ signal: AbortSignal | undefined,
63
+ onUpdate: AgentToolUpdateCallback<Record<string, unknown>> | undefined,
64
+ ctx: ExtensionContext,
65
+ ) => Promise<AgentToolResult<Record<string, unknown>>>;
66
+
67
+ /**
68
+ * Register a set of tools from specs + pre-derived surfaces.
69
+ *
70
+ * `createExecute` receives the spec and returns a pi-compatible execute
71
+ * function. This keeps execute-logic package-local while the framework owns
72
+ * the declarative surface and registration boilerplate.
73
+ */
74
+ export function registerSuiPiTools(
75
+ pi: ExtensionAPI,
76
+ specs: readonly SuiPiToolSpec[],
77
+ surfaces: Record<string, SuiPiToolPromptSurface>,
78
+ createExecute: (spec: SuiPiToolSpec) => ToolExecuteFn,
79
+ ): void {
80
+ for (const spec of specs) {
81
+ const surface = surfaces[spec.name];
82
+ pi.registerTool({
83
+ name: spec.name,
84
+ label: spec.label,
85
+ description: surface?.description ?? spec.description,
86
+ promptSnippet: surface?.promptSnippet ?? spec.promptSnippet,
87
+ promptGuidelines: surface?.promptGuidelines ?? [...spec.promptGuidelines],
88
+ parameters: spec.parameters,
89
+ execute: createExecute(spec),
90
+ });
91
+ }
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Shared parameter builders
96
+ // ---------------------------------------------------------------------------
97
+
98
+ /** File path (relative or absolute). */
99
+ export const FileParam = Type.String({ description: "File path (relative or absolute)" });
100
+
101
+ /** 1-based line number. */
102
+ export const LineParam = Type.Number({ description: "1-based line number", minimum: 1 });
103
+
104
+ /** 1-based character column (UTF-16). */
105
+ export const CharacterParam = Type.Number({
106
+ description: "1-based column number (UTF-16)",
107
+ minimum: 1,
108
+ });
109
+
110
+ /** Symbol name for discovery-based resolution. */
111
+ export const SymbolParam = Type.String({
112
+ description: "Symbol name for discovery-based resolution",
113
+ });
114
+
115
+ /** Maximum results to return. */
116
+ export const MaxResultsParam = Type.Number({ description: "Maximum results to return" });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-lsp",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "SuPi LSP extension — Language Server Protocol integration for pi",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,11 +21,18 @@
21
21
  "!__tests__"
22
22
  ],
23
23
  "dependencies": {
24
+ "ignore": "^7.0.5",
24
25
  "typescript": "6.0.3",
25
- "@mrclrchtr/supi-core": "1.5.0"
26
+ "vscode-jsonrpc": "^8.2.1",
27
+ "vscode-languageserver-protocol": "^3.17.5",
28
+ "vscode-languageserver-types": "^3.17.5",
29
+ "@mrclrchtr/supi-core": "1.7.0"
26
30
  },
27
31
  "bundledDependencies": [
28
- "@mrclrchtr/supi-core"
32
+ "@mrclrchtr/supi-core",
33
+ "vscode-jsonrpc",
34
+ "vscode-languageserver-protocol",
35
+ "vscode-languageserver-types"
29
36
  ],
30
37
  "peerDependencies": {
31
38
  "@earendil-works/pi-ai": "*",
@@ -173,7 +173,13 @@ export class LspClient {
173
173
  setTimeout(() => reject(new Error("shutdown timeout")), SHUTDOWN_TIMEOUT_MS),
174
174
  ),
175
175
  ]);
176
- this.rpc.sendNotification("exit");
176
+ // Flush the final exit notification before disposing the transport.
177
+ await Promise.race([
178
+ this.rpc.sendNotification("exit"),
179
+ new Promise((_, reject) =>
180
+ setTimeout(() => reject(new Error("exit notification timeout")), SHUTDOWN_TIMEOUT_MS),
181
+ ),
182
+ ]);
177
183
  } catch {
178
184
  // Timeout or error — force kill
179
185
  }
@@ -333,10 +339,7 @@ export class LspClient {
333
339
 
334
340
  /** Check if server supports pull diagnostics. */
335
341
  get hasDiagnosticProvider(): boolean {
336
- return (
337
- this.capabilities?.diagnosticProvider !== undefined &&
338
- this.capabilities.diagnosticProvider !== false
339
- );
342
+ return this.capabilities?.diagnosticProvider !== undefined;
340
343
  }
341
344
 
342
345
  /** Notify the server that watched workspace files changed. */