@bigknoxy/hashpilot 4.6.3

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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +777 -0
  3. package/docs/ADAPTER-CONTRACT.md +1260 -0
  4. package/docs/ARCHITECTURE.md +846 -0
  5. package/docs/CLI-QUICKREF.md +827 -0
  6. package/docs/COMPETITIVE-ANALYSIS.md +307 -0
  7. package/docs/INSTALL.md +403 -0
  8. package/docs/INTEGRATION-CLAUDE.md +126 -0
  9. package/docs/INTEGRATION-MCP.md +196 -0
  10. package/docs/INTEGRATION-OPENCODE.md +136 -0
  11. package/docs/INTEGRATION-PI.md +195 -0
  12. package/package.json +77 -0
  13. package/scripts/build-site.sh +39 -0
  14. package/scripts/doctor.sh +218 -0
  15. package/scripts/gen-cli-quickref.ts +232 -0
  16. package/scripts/install-cli.sh +60 -0
  17. package/scripts/install.sh +466 -0
  18. package/scripts/roadmap-lint.ts +200 -0
  19. package/scripts/uninstall.sh +202 -0
  20. package/src/cli-node.cjs +51 -0
  21. package/src/cli.ts +209 -0
  22. package/src/commands/ast.ts +255 -0
  23. package/src/commands/diff.ts +98 -0
  24. package/src/commands/edit.ts +93 -0
  25. package/src/commands/hash.ts +64 -0
  26. package/src/commands/intent.ts +68 -0
  27. package/src/commands/maintenance.ts +191 -0
  28. package/src/commands/mcp.ts +28 -0
  29. package/src/commands/provenance.ts +111 -0
  30. package/src/commands/read.ts +117 -0
  31. package/src/commands/route.ts +42 -0
  32. package/src/commands/shared.ts +65 -0
  33. package/src/commands/telemetry.ts +126 -0
  34. package/src/commands/verify.ts +61 -0
  35. package/src/core/ast-edit.ts +2357 -0
  36. package/src/core/batch-edit.ts +185 -0
  37. package/src/core/config.ts +189 -0
  38. package/src/core/diff-engine.ts +474 -0
  39. package/src/core/doctor.ts +303 -0
  40. package/src/core/encoding.ts +116 -0
  41. package/src/core/envelope.ts +163 -0
  42. package/src/core/exit-codes.ts +198 -0
  43. package/src/core/format.ts +339 -0
  44. package/src/core/grep.ts +180 -0
  45. package/src/core/hash-edit.ts +416 -0
  46. package/src/core/index.ts +155 -0
  47. package/src/core/intent.ts +584 -0
  48. package/src/core/locking.ts +292 -0
  49. package/src/core/module-system.ts +142 -0
  50. package/src/core/operations.ts +557 -0
  51. package/src/core/output.ts +122 -0
  52. package/src/core/path-normalize.ts +61 -0
  53. package/src/core/paths.ts +326 -0
  54. package/src/core/plan-executor.ts +437 -0
  55. package/src/core/platform.ts +132 -0
  56. package/src/core/provenance.ts +214 -0
  57. package/src/core/read.ts +111 -0
  58. package/src/core/redact.ts +98 -0
  59. package/src/core/resolve-content.ts +12 -0
  60. package/src/core/router.ts +463 -0
  61. package/src/core/snapshot.ts +346 -0
  62. package/src/core/telemetry.ts +838 -0
  63. package/src/core/utils.ts +7 -0
  64. package/src/core/verify-baseline.ts +186 -0
  65. package/src/core/verify-scope.ts +282 -0
  66. package/src/core/verify.ts +753 -0
  67. package/src/mcp/server.ts +325 -0
  68. package/templates/claude-section.md +12 -0
  69. package/templates/opencode-agent.md +106 -0
  70. package/templates/opencode-skill.md +241 -0
  71. package/templates/pi-extension.ts +288 -0
  72. package/templates/pi-skill.md +123 -0
  73. package/tsconfig.json +19 -0
@@ -0,0 +1,288 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"
2
+ import { Type } from "@sinclair/typebox"
3
+
4
+ const HASHPILOT_BIN = "hashpilot"
5
+
6
+ const MAX_BYTES = 50 * 1024
7
+
8
+ function truncate(value: string): string {
9
+ if (Buffer.byteLength(value, "utf8") <= MAX_BYTES) return value
10
+ return value.slice(0, MAX_BYTES) + "\n\n[Output truncated to 50KB]"
11
+ }
12
+
13
+ async function runSE(args: string[], pi: ExtensionAPI, signal: AbortSignal, cwd?: string): Promise<{ exitCode: number; output: string }> {
14
+ const result = await pi.exec(HASHPILOT_BIN, args, { cwd, signal })
15
+ const output = result.stdout || result.stderr || ""
16
+ return { exitCode: result.code, output: truncate(output) }
17
+ }
18
+
19
+ export default function (pi: ExtensionAPI) {
20
+ // ── hashpilot_read ──────────────────────────────────
21
+ pi.registerTool({
22
+ name: "hashpilot_read",
23
+ label: "HashPilot Read",
24
+ description: "Read one or more files with content hashes for subsequent hash-anchored edits. Returns JSON array with path, content, hash, and line count.",
25
+ promptSnippet: "Use hashpilot_read to batch-read files with hashes, then use hashpilot_replace_hash for edits.",
26
+ promptGuidelines: [
27
+ "Prefer hashpilot_read over raw file reads when you plan to edit the files afterward.",
28
+ "Use the returned hash to anchor subsequent hashpilot_replace_hash calls.",
29
+ "Batch multiple files in a single hashpilot_read call to minimize round trips.",
30
+ ],
31
+ parameters: Type.Object({
32
+ files: Type.Array(Type.String({ description: "Absolute file paths to read" }), { description: "Files to read" }),
33
+ }),
34
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
35
+ const args = ["read-many", ...params.files]
36
+ const { exitCode, output } = await runSE(args, pi, signal, ctx.cwd)
37
+ return {
38
+ isError: exitCode !== 0,
39
+ content: [{ type: "text", text: output || "(no output)" }],
40
+ details: { exitCode, command: `${HASHPILOT_BIN} ${args.join(" ")}` },
41
+ }
42
+ },
43
+ })
44
+
45
+ // ── hashpilot_search ─────────────────────────────────
46
+ pi.registerTool({
47
+ name: "hashpilot_search",
48
+ label: "HashPilot Search",
49
+ description: "Search a regex pattern across multiple paths. Returns JSON with file, line, column, and content for each match.",
50
+ promptSnippet: "Use hashpilot_search to find symbol definitions, references, or patterns across the codebase.",
51
+ promptGuidelines: [
52
+ "Use hashpilot_search for code search instead of manual grep.",
53
+ "Combine with hashpilot_ast when you need symbol-level operations on TypeScript files.",
54
+ ],
55
+ parameters: Type.Object({
56
+ pattern: Type.String({ description: "Regex pattern to search for" }),
57
+ paths: Type.Array(Type.String(), { description: "Paths to search" }),
58
+ ignoreCase: Type.Optional(Type.Boolean({ description: "Case insensitive search" })),
59
+ filePattern: Type.Optional(Type.String({ description: "Glob pattern to filter files" })),
60
+ maxResults: Type.Optional(Type.Number({ description: "Maximum number of results" })),
61
+ }),
62
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
63
+ const args = ["grep-many", params.pattern, ...params.paths]
64
+ if (params.ignoreCase) args.push("-i")
65
+ if (params.filePattern) args.push("--file-pattern", params.filePattern)
66
+ if (params.maxResults) args.push("--max-results", String(params.maxResults))
67
+ const { exitCode, output } = await runSE(args, pi, signal, ctx.cwd)
68
+ return {
69
+ isError: exitCode !== 0,
70
+ content: [{ type: "text", text: output || "(no output)" }],
71
+ details: { exitCode, command: `${HASHPILOT_BIN} ${args.join(" ")}` },
72
+ }
73
+ },
74
+ })
75
+
76
+ // ── hashpilot_read_hash ──────────────────────────────
77
+ pi.registerTool({
78
+ name: "hashpilot_read_hash",
79
+ label: "HashPilot Read Hash",
80
+ description: "Read a specific line with its hash and surrounding context. Use before hash-anchored edits to get the content hash anchor.",
81
+ promptSnippet: "Use hashpilot_read_hash to get a line-level hash anchor before making hash-anchored edits.",
82
+ promptGuidelines: [
83
+ "Always call hashpilot_read_hash or hashpilot_read before hashpilot_replace_hash to get a current hash.",
84
+ "Never guess hashes — stale hashes are rejected to prevent file corruption.",
85
+ ],
86
+ parameters: Type.Object({
87
+ file: Type.String({ description: "Absolute file path" }),
88
+ line: Type.Number({ description: "Line number (1-indexed)" }),
89
+ context: Type.Optional(Type.Number({ description: "Number of context lines (default 3)", default: 3 })),
90
+ }),
91
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
92
+ const args = ["read-hash", params.file, String(params.line)]
93
+ if (params.context) args.push("-c", String(params.context))
94
+ const { exitCode, output } = await runSE(args, pi, signal, ctx.cwd)
95
+ return {
96
+ isError: exitCode !== 0,
97
+ content: [{ type: "text", text: output || "(no output)" }],
98
+ details: { exitCode },
99
+ }
100
+ },
101
+ })
102
+
103
+ // ── hashpilot_replace_hash ──────────────────────────
104
+ pi.registerTool({
105
+ name: "hashpilot_replace_hash",
106
+ label: "HashPilot Replace Hash",
107
+ description: "Replace file content identified by a hash anchor. The hash must match the current file state — stale hashes are rejected. Use hashpilot_read or hashpilot_read_hash to obtain a current hash first.",
108
+ promptSnippet: "Use hashpilot_replace_hash for reliable file edits. Always obtain the hash from a prior read first.",
109
+ promptGuidelines: [
110
+ "ALWAYS read the file first (hashpilot_read or hashpilot_read_hash) to get a current hash before editing.",
111
+ "Stale hashes are intentionally rejected to prevent overwriting changes — re-read and retry on stale-anchor errors.",
112
+ "Use --range for partial replacements instead of replacing entire files.",
113
+ "Prefer hashpilot_ast for TypeScript symbol-level operations (rename, replace-body, add-import, etc.).",
114
+ ],
115
+ parameters: Type.Object({
116
+ file: Type.String({ description: "Absolute file path" }),
117
+ oldHash: Type.String({ description: "Hash of the content to replace (from hashpilot_read or hashpilot_read_hash)" }),
118
+ newContent: Type.String({ description: "New content to write (or @filepath to read from a file)" }),
119
+ range: Type.Optional(Type.String({ description: "Line range as start:end (1-indexed, inclusive start, exclusive end)" })),
120
+ dryRun: Type.Optional(Type.Boolean({ description: "Preview without writing" })),
121
+ }),
122
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
123
+ const args = ["replace-hash", params.file, params.oldHash, params.newContent]
124
+ if (params.range) args.push("--range", params.range)
125
+ if (params.dryRun) args.push("--dry-run")
126
+ const { exitCode, output } = await runSE(args, pi, signal, ctx.cwd)
127
+ // Envelope (apiVersion 1): the command payload lives under `data`.
128
+ const envelope = JSON.parse(output || "{}")
129
+ const result = envelope.data ?? {}
130
+ const isStale = result.stale === true || envelope.error?.code === "STALE_ANCHOR"
131
+ return {
132
+ isError: exitCode !== 0 || envelope.ok === false,
133
+ content: [{ type: "text", text: output || "(no output)" }],
134
+ details: { exitCode, stale: isStale, fallbackReason: isStale ? "stale-anchor" : undefined },
135
+ }
136
+ },
137
+ })
138
+
139
+ // ── hashpilot_ast ────────────────────────────────────
140
+ pi.registerTool({
141
+ name: "hashpilot_ast",
142
+ label: "HashPilot AST",
143
+ description: "Syntax-aware editing for TypeScript/TSX files via tree-sitter. Supports find-symbols, rename-symbol, replace-body, add-import, remove-import, insert-before, insert-after. Prefer this over hash-based editing for TypeScript files.",
144
+ promptSnippet: "Use hashpilot_ast for TypeScript/TSX symbol-level operations. Prefer over hash edits for these file types.",
145
+ promptGuidelines: [
146
+ "Use hashpilot_ast for all TypeScript/TSX edits involving symbol renaming, function body replacement, or import management.",
147
+ "For find-symbols, use operation='find-symbols' to list symbols in a file.",
148
+ "Always verify changes with hashpilot_verify after AST edits.",
149
+ ],
150
+ parameters: Type.Object({
151
+ operation: Type.Union([
152
+ Type.Literal("find-symbols"),
153
+ Type.Literal("rename-symbol"),
154
+ Type.Literal("replace-body"),
155
+ Type.Literal("add-import"),
156
+ Type.Literal("remove-import"),
157
+ Type.Literal("insert-before"),
158
+ Type.Literal("insert-after"),
159
+ ], { description: "AST operation to perform" }),
160
+ file: Type.String({ description: "Absolute file path (must be .ts or .tsx)" }),
161
+ name: Type.Optional(Type.String({ description: "Symbol name (for rename-symbol, replace-body, insert-before, insert-after)" })),
162
+ newName: Type.Optional(Type.String({ description: "New name (for rename-symbol)" })),
163
+ body: Type.Optional(Type.String({ description: "New body content (for replace-body, insert-before, insert-after)" })),
164
+ importSpec: Type.Optional(Type.String({ description: "Import spec (for add-import, remove-import), e.g. '{ Foo } from ./bar'" })),
165
+ dryRun: Type.Optional(Type.Boolean({ description: "Preview without writing" })),
166
+ }),
167
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
168
+ const subCmd = `ast ${params.operation}`
169
+ const args: string[] = [subCmd, params.file]
170
+
171
+ switch (params.operation) {
172
+ case "find-symbols":
173
+ break
174
+ case "rename-symbol":
175
+ args.push(params.name || "", params.newName || "")
176
+ break
177
+ case "replace-body":
178
+ args.push(params.name || "", params.body || "")
179
+ break
180
+ case "add-import":
181
+ case "remove-import":
182
+ args.push(params.importSpec || "")
183
+ break
184
+ case "insert-before":
185
+ case "insert-after":
186
+ args.push(params.name || "", params.body || "")
187
+ break
188
+ }
189
+
190
+ if (params.dryRun) args.push("--dry-run")
191
+ const { exitCode, output } = await runSE(args, pi, signal, ctx.cwd)
192
+ return {
193
+ isError: exitCode !== 0,
194
+ content: [{ type: "text", text: output || "(no output)" }],
195
+ details: { exitCode, operation: params.operation },
196
+ }
197
+ },
198
+ })
199
+
200
+ // ── hashpilot_verify ─────────────────────────────────
201
+ pi.registerTool({
202
+ name: "hashpilot_verify",
203
+ label: "HashPilot Verify",
204
+ description: "Run formatter, linter, and/or tests on changed files. Bundles verification into a single call.",
205
+ promptSnippet: "Use hashpilot_verify after edits to confirm changes pass formatting, linting, and tests.",
206
+ promptGuidelines: [
207
+ "Always verify after making edits — especially after AST operations.",
208
+ "Pass formatter, linter, or testFilter to enable relevant checks.",
209
+ ],
210
+ parameters: Type.Object({
211
+ files: Type.Array(Type.String(), { description: "Files to verify" }),
212
+ formatter: Type.Optional(Type.String({ description: "Formatter command (e.g., 'prettier')" })),
213
+ linter: Type.Optional(Type.String({ description: "Linter command (e.g., 'eslint')" })),
214
+ testFilter: Type.Optional(Type.String({ description: "Test filter pattern" })),
215
+ }),
216
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
217
+ const args = ["verify-changes", ...params.files]
218
+ if (params.formatter) args.push("--formatter", params.formatter)
219
+ if (params.linter) args.push("--linter", params.linter)
220
+ if (params.testFilter) args.push("--test-filter", params.testFilter)
221
+ const { exitCode, output } = await runSE(args, pi, signal, ctx.cwd)
222
+ return {
223
+ isError: exitCode !== 0,
224
+ content: [{ type: "text", text: output || "(no output)" }],
225
+ details: { exitCode },
226
+ }
227
+ },
228
+ })
229
+
230
+ // ── hashpilot_status ─────────────────────────────────
231
+ pi.registerTool({
232
+ name: "hashpilot_status",
233
+ label: "HashPilot Status",
234
+ description: "Show HashPilot routing info and telemetry summary. Use to check which edit route would be chosen for a file+operation, or to review recent operation telemetry.",
235
+ promptSnippet: "Use hashpilot_status to check edit routing decisions or review telemetry.",
236
+ parameters: Type.Object({
237
+ action: Type.Union([
238
+ Type.Literal("route"),
239
+ Type.Literal("telemetry"),
240
+ ], { description: "Action: 'route' to check edit routing, 'telemetry' to show summary" }),
241
+ file: Type.Optional(Type.String({ description: "File path (for route action)" })),
242
+ operation: Type.Optional(Type.String({ description: "Operation name (for route action)" })),
243
+ }),
244
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
245
+ if (params.action === "route") {
246
+ const args = ["route", params.file || ".", params.operation || "replace-hash"]
247
+ const { exitCode, output } = await runSE(args, pi, signal, ctx.cwd)
248
+ return {
249
+ isError: exitCode !== 0,
250
+ content: [{ type: "text", text: output || "(no output)" }],
251
+ details: { exitCode },
252
+ }
253
+ }
254
+ const args = ["telemetry", "summary"]
255
+ const { exitCode, output } = await runSE(args, pi, signal, ctx.cwd)
256
+ return {
257
+ isError: exitCode !== 0,
258
+ content: [{ type: "text", text: output || "(no output)" }],
259
+ details: { exitCode },
260
+ }
261
+ },
262
+ })
263
+
264
+ // ── /hp slash command ────────────────────────────────
265
+ pi.registerCommand("hp", {
266
+ description: "HashPilot: structured editing status. Usage: /hp [route <file> <op>|telemetry]",
267
+ async handler(args, _ctx) {
268
+ const parts = (args || "").trim().split(/\s+/)
269
+ if (parts[0] === "route" && parts[1] && parts[2]) {
270
+ const proc = await pi.exec(HASHPILOT_BIN, ["route", parts[1], parts[2]], {})
271
+ return proc.stdout || proc.stderr || "(no output)"
272
+ }
273
+ if (parts[0] === "telemetry") {
274
+ const proc = await pi.exec(HASHPILOT_BIN, ["telemetry", "summary"], {})
275
+ return proc.stdout || proc.stderr || "(no output)"
276
+ }
277
+ const proc = await pi.exec(HASHPILOT_BIN, ["--version"], {})
278
+ return `HashPilot v${proc.stdout?.trim() || "unknown"}\n\nCommands: route <file> <op> | telemetry`
279
+ },
280
+ })
281
+
282
+ // ── Enable/disable flag ──────────────────────────────
283
+ pi.registerFlag("hashpilot_enabled", {
284
+ description: "Enable or disable HashPilot structured editing tools",
285
+ type: "boolean",
286
+ default: true,
287
+ })
288
+ }
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: hashpilot
3
+ description: HashPilot structured editing — prefers AST edits for TypeScript, hash-anchored edits otherwise, with stale-anchor safety and batched verification. Use when editing files, renaming symbols, replacing function bodies, managing imports, or verifying changes.
4
+ ---
5
+
6
+ # HashPilot Pi — Structured Editing Skill
7
+
8
+ ## Install HashPilot
9
+
10
+ ```bash
11
+ curl -fsSL https://raw.githubusercontent.com/bigknoxy/HashPilot/main/scripts/install.sh | bash
12
+ ```
13
+
14
+ This installs the `hashpilot` CLI and registers the Pi extension with `/hp` slash command.
15
+
16
+ ---
17
+
18
+ You have access to HashPilot structured editing tools that are more reliable and token-efficient than raw text editing.
19
+
20
+ ### Preferred: use the MCP server
21
+
22
+ If your host speaks MCP, register HashPilot once and call its tools directly
23
+ instead of shelling out:
24
+
25
+ ```json
26
+ { "mcpServers": { "hashpilot": { "command": "hashpilot", "args": ["mcp", "--stdio"] } } }
27
+ ```
28
+
29
+ The MCP tools mirror the CLI one-for-one (`rename_symbol`, `replace_hash`,
30
+ `route_edit`, `verify_changes`, plus the read and search tools), and multi-line
31
+ content with quotes or backticks rides inside JSON rather than through shell
32
+ quoting. Per-host setup lives in `docs/INTEGRATION-MCP.md`.
33
+
34
+ The CLI commands below remain fully supported and are the fallback when MCP is
35
+ not available.
36
+
37
+ ## When to Use
38
+
39
+ - Editing existing files in supported AST languages (TS/JS/Python/Go/Rust)
40
+ - Editing any file where you need precision (hash-anchored avoids line-counting errors)
41
+ - Renaming symbols, replacing function bodies, managing imports
42
+ - Batch reading multiple files
43
+ - Verifying changes after edits
44
+
45
+ ## When NOT to Use
46
+
47
+ - Creating new files → use raw write instead
48
+ - Deleting files/directories → use bash
49
+ - Moving/renaming files → use bash
50
+ - Simple one-off edits → direct edit is cheaper
51
+ - File system operations (cp, mv, rm) → use bash
52
+
53
+ ## Routing Hierarchy
54
+
55
+ Always follow this priority when editing files:
56
+
57
+ 1. **AST route** — For supported languages (TypeScript, TSX, JavaScript, Python, Go, Rust), prefer `hashpilot_ast` for:
58
+ - `find-symbols` — list symbols in a file
59
+ - `rename-symbol` — rename a symbol across all references
60
+ - `replace-body` — replace a function/method body
61
+ - `add-import` — add an import statement
62
+ - `remove-import` — remove an import statement
63
+ - `insert-before` / `insert-after` — insert content around a symbol
64
+
65
+ 2. **Hash route** — For all other files or when AST is not applicable, use:
66
+ - `hashpilot_read` to get file content and hash
67
+ - `hashpilot_replace_hash` to edit with hash anchoring
68
+
69
+ 3. **Fallback** — Only use raw text editing when hash and AST routes fail.
70
+
71
+ ## Workflow
72
+
73
+ ### Editing a supported language file (TypeScript, TSX, JavaScript, Python, Go, Rust)
74
+ ```
75
+ 1. hashpilot_ast operation="find-symbols" file="src/foo.ts"
76
+ 2. hashpilot_ast operation="rename-symbol" file="src/foo.ts" name="oldFunc" newName="newFunc"
77
+ 3. hashpilot_verify files=["src/foo.ts"] formatter="prettier" linter="eslint"
78
+ ```
79
+
80
+ ### Editing an unsupported file type
81
+ ```
82
+ 1. hashpilot_read files=["config.yaml"]
83
+ → get hash from response
84
+ 2. hashpilot_replace_hash file="config.yaml" oldHash="<hash>" newContent="new content"
85
+ 3. hashpilot_verify files=["config.yaml"]
86
+ ```
87
+
88
+ ### Batch reading
89
+ ```
90
+ hashpilot_read files=["src/a.ts", "src/b.ts", "src/c.ts"]
91
+ ```
92
+
93
+ ### Searching
94
+ ```
95
+ hashpilot_search pattern="function\\s+\\w+" paths=["src/"]
96
+ ```
97
+
98
+ ## Stale Anchor Recovery
99
+
100
+ Every command returns the envelope `{ apiVersion, ok, command, data, error, warnings }` —
101
+ read the payload from `data` and branch on `error.code`, never on `error.message`.
102
+
103
+ When `hashpilot_replace_hash` returns `"stale": true` (`error.code: "STALE_ANCHOR"`):
104
+ 1. The file changed since you read it — your hash is outdated
105
+ 2. Re-read the file: `hashpilot_read files=["target.ts"]`
106
+ 3. Retry the edit with the new hash
107
+ 4. Never guess or reuse old hashes
108
+
109
+ ## Verification
110
+
111
+ Always verify after edits:
112
+ - Use `hashpilot_verify` with appropriate formatter and linter
113
+ - Pass `formatter` and `linter` params when available
114
+ - Pass `testFilter` for targeted test runs
115
+
116
+ ## Status and Debugging
117
+
118
+ - `hashpilot_status action="route" file="src/foo.ts" operation="rename-symbol"` — check which route would be used
119
+ - `hashpilot_status action="telemetry"` — review recent operations
120
+
121
+ ## Enable/Disable
122
+
123
+ The `hashpilot_enabled` flag controls whether HashPilot tools are active. Default: enabled.
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "bundler",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "outDir": "dist",
9
+ "rootDir": "src",
10
+ "declaration": true,
11
+ "sourceMap": true,
12
+ "skipLibCheck": true,
13
+ "types": ["bun-types"],
14
+ "lib": ["ES2022"],
15
+ "resolveJsonModule": true
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "dist", "tests"]
19
+ }