@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
@@ -1,20 +1,15 @@
1
+ import ignore from "ignore";
2
+
1
3
  /** Gitignore-style glob pattern matching for path exclusion.
2
4
  *
3
- * Supports:
4
- * - Literal names at any depth: __tests__, build
5
- * - Trailing slash directory-only: __tests__ + "/"
6
- * - Leading slash root-anchored: "/" + build
7
- * - Stars-star-slash recursive: e.g. `**` + "/" + fixtures
8
- * - Asterisk single-segment wildcard: *.generated.ts
9
- * - Literal paths: packages + "/" + legacy
10
- */
11
-
12
- /**
13
- * Normalize path separators to forward slashes and trim.
5
+ * Delegates to the {@link https://github.com/kaelzhang/node-ignore | ignore} package,
6
+ * which provides battle-tested .gitignore semantics used by ESLint, Prettier, and others.
7
+ *
8
+ * Supports full gitignore syntax: literal names, `*` / `?` wildcards, `**` recursive globs,
9
+ * leading `/` anchored patterns, trailing `/` directory-only patterns, and `!` negation.
10
+ *
11
+ * **Note:** Patterns that start with `#` are treated as comments unless escaped with `\#`.
14
12
  */
15
- function normalize(p: string): string {
16
- return p.replaceAll("\\", "/").trim();
17
- }
18
13
 
19
14
  /**
20
15
  * Check whether a project-relative file path matches a gitignore-style glob pattern.
@@ -24,174 +19,6 @@ function normalize(p: string): string {
24
19
  * @returns `true` if the file path matches the pattern
25
20
  */
26
21
  export function isGlobMatch(filePath: string, pattern: string): boolean {
27
- const fp = normalize(filePath);
28
- const pat = normalize(pattern);
29
- if (!fp || !pat) return false;
30
-
31
- // Leading / → anchored to root
32
- const anchored = pat.startsWith("/");
33
- const noLeadingSlash = anchored ? pat.slice(1) : pat;
34
-
35
- // Trailing / → directory-only
36
- const dirOnly = noLeadingSlash.endsWith("/");
37
- const cleanPat = dirOnly ? noLeadingSlash.slice(0, -1) : noLeadingSlash;
38
-
39
- if (!cleanPat) return false;
40
-
41
- return matchGlob(fp, cleanPat, { anchored, dirOnly });
42
- }
43
-
44
- interface MatchOptions {
45
- anchored: boolean;
46
- dirOnly: boolean;
47
- }
48
-
49
- /**
50
- * Core recursive pattern matching against a multi-segment path.
51
- */
52
- function matchGlob(filePath: string, pattern: string, opts: MatchOptions): boolean {
53
- // Direct match
54
- if (!opts.anchored && !opts.dirOnly && filePath === pattern) return true;
55
-
56
- // Split into segments
57
- const pathSegments = filePath.split("/");
58
- const patternSegments = pattern.split("/");
59
-
60
- // ** recursive glob
61
- if (pattern.startsWith("**/")) {
62
- const suffix = pattern.slice(3);
63
- return (
64
- matchGlob(filePath, suffix, { ...opts, anchored: false }) ||
65
- starStarMatch(pathSegments, suffix)
66
- );
67
- }
68
-
69
- // prefix/**/suffix bounded recursive glob
70
- const dstarIdx = pattern.indexOf("/**/");
71
- if (dstarIdx !== -1) {
72
- const prefix = pattern.slice(0, dstarIdx);
73
- const suffix = pattern.slice(dstarIdx + 4);
74
- return matchBoundedStar(pathSegments, prefix, suffix);
75
- }
76
-
77
- // Single-segment patterns
78
- if (patternSegments.length === 1 && !opts.anchored) {
79
- return matchSingleSegment(pathSegments, patternSegments[0], opts.dirOnly);
80
- }
81
-
82
- // Multi-segment: anchored or unanchored
83
- if (opts.anchored) {
84
- return matchSegments(pathSegments, patternSegments, opts.dirOnly);
85
- }
86
-
87
- // Unanchored multi-segment: try at each starting position
88
- return matchUnanchoredSegments(pathSegments, patternSegments, opts.dirOnly);
89
- }
90
-
91
- /**
92
- * Match a ** recursive suffix across directory levels.
93
- */
94
- function starStarMatch(segments: string[], suffix: string): boolean {
95
- for (let i = 0; i < segments.length; i++) {
96
- const remaining = segments.slice(i).join("/");
97
- if (matchGlob(remaining, suffix, { anchored: false, dirOnly: false })) return true;
98
- }
99
- return false;
100
- }
101
-
102
- /** Match prefix plus star-star-slash plus suffix bounded recursive pattern. */
103
- function matchBoundedStar(segments: string[], prefix: string, suffix: string): boolean {
104
- // Try to find a split point where left matches prefix and right matches suffix
105
- for (let i = 1; i < segments.length; i++) {
106
- const left = segments.slice(0, i).join("/");
107
- const right = segments.slice(i).join("/");
108
- if (
109
- matchGlob(left, prefix, { anchored: false, dirOnly: false }) &&
110
- matchGlob(right, suffix, { anchored: false, dirOnly: false })
111
- ) {
112
- return true;
113
- }
114
- }
115
- return false;
116
- }
117
-
118
- /**
119
- * Match a single-segment pattern against path segments.
120
- */
121
- function matchSingleSegment(segments: string[], patternSeg: string, dirOnly: boolean): boolean {
122
- const hasGlob = patternSeg.includes("*") || patternSeg.includes("?");
123
-
124
- if (hasGlob) {
125
- // Glob pattern matches any segment
126
- return segments.some((seg) => simpleMatch(seg, patternSeg));
127
- }
128
-
129
- // Literal segment name
130
- if (dirOnly) {
131
- // Match as directory: any segment except the last (file) one
132
- return segments.slice(0, -1).some((seg) => seg === patternSeg);
133
- }
134
-
135
- // Match any segment (file or directory)
136
- return segments.some((seg) => seg === patternSeg);
137
- }
138
-
139
- /**
140
- * Try to match multi-segment pattern at each start position.
141
- */
142
- function matchUnanchoredSegments(segments: string[], pattern: string[], dirOnly: boolean): boolean {
143
- for (let i = 0; i < segments.length; i++) {
144
- if (matchSegments(segments.slice(i), pattern, dirOnly)) return true;
145
- }
146
- return false;
147
- }
148
-
149
- /** Match segments from start. Returns true if all pattern segments match contiguously. */
150
- function matchSegments(segments: string[], pattern: string[], dirOnly: boolean): boolean {
151
- if (pattern.length > segments.length) return false;
152
-
153
- for (let i = 0; i < pattern.length; i++) {
154
- if (!matchSegmentAtIndex(segments, pattern, i)) return false;
155
- }
156
-
157
- // dirOnly: last matched segment must not be the last path segment
158
- if (dirOnly && pattern.length === segments.length) return false;
159
-
160
- return true;
161
- }
162
-
163
- /** Match a single pattern segment against the corresponding path segment. */
164
- function matchSegmentAtIndex(segments: string[], pattern: string[], index: number): boolean {
165
- const patSeg = pattern[index];
166
- const pathSeg = segments[index];
167
-
168
- if (patSeg === "**") {
169
- // ** at end matches all remaining segments
170
- if (index === pattern.length - 1) return true;
171
- // Try rest of pattern from various positions
172
- for (let j = index; j < segments.length; j++) {
173
- if (matchSegments(segments.slice(j), pattern.slice(index + 1), false)) return true;
174
- }
175
- return false;
176
- }
177
-
178
- return simpleMatch(pathSeg, patSeg);
179
- }
180
-
181
- /**
182
- * Match a single path segment against a single pattern segment.
183
- * Supports `*` (any chars except `/`) and `?` (single char).
184
- */
185
- function simpleMatch(segment: string, pattern: string): boolean {
186
- if (pattern === "*") return true;
187
- if (pattern === segment) return true;
188
- if (!pattern.includes("*") && !pattern.includes("?")) return false;
189
-
190
- // Convert pattern to simple regex
191
- const regexStr = pattern
192
- .replace(/[.+^${}()|[\]\\]/g, "\\$&")
193
- .replace(/\*/g, "[^/]*")
194
- .replace(/\?/g, "[^/]");
195
-
196
- return new RegExp(`^${regexStr}$`).test(segment);
22
+ if (!filePath || !pattern) return false;
23
+ return ignore().add(pattern).ignores(filePath);
197
24
  }
@@ -2,7 +2,7 @@
2
2
  // Extracted from lsp.ts to keep file sizes within Biome limits.
3
3
 
4
4
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
5
- import type { DetectedProjectServer, ProjectServerInfo } from "../config/types.ts";
5
+ import type { DetectedProjectServer, ProjectServerInfo } from "../config/server-config.ts";
6
6
  import type { LspManager } from "../manager/manager.ts";
7
7
  import { LSP_TOOL_NAMES } from "../tool/names.ts";
8
8
  import type { LspInspectorState } from "../ui/ui.ts";
@@ -1,7 +1,7 @@
1
1
  // Prompt guidance and tool descriptions for the expert LSP toolset.
2
2
 
3
3
  import * as path from "node:path";
4
- import type { ProjectServerInfo } from "../config/types.ts";
4
+ import type { ProjectServerInfo } from "../config/server-config.ts";
5
5
  import { LSP_LOOKUP_TOOL, type LspToolName } from "./names.ts";
6
6
  import { LSP_TOOL_DEFINITION_SPECS } from "./tool-specs.ts";
7
7
 
@@ -103,7 +103,7 @@ export const LSP_TOOL_DEFINITION_SPECS = [
103
103
  basePromptGuidelines: [
104
104
  'Use lsp_lookup with `kind: "hover"` for semantic type or symbol information at a known `file`, `line`, and `character`.',
105
105
  'Use lsp_lookup with `kind: "definition"`, `"references"`, or `"implementation"` for semantic navigation at a known position.',
106
- "Use lsp_lookup after code_intel or tree_sitter has already narrowed the target file and position.",
106
+ "Use lsp_lookup after code_brief, code_map, or tree_sitter has already narrowed the target file and position.",
107
107
  ],
108
108
  parameters: LookupParameters,
109
109
  run: (service, cwd, params) =>
@@ -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" });
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Max Brunsfeld
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.