@aexol/spectral 0.7.8 → 0.8.2

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 (197) hide show
  1. package/dist/agent/agents.js +4 -4
  2. package/dist/agent/index.js +8 -8
  3. package/dist/cli.js +1 -1
  4. package/dist/commands/serve.js +1 -1
  5. package/dist/extensions/kanban-bridge.js +668 -0
  6. package/dist/extensions/spectral-vision-fallback.js +84 -46
  7. package/dist/mcp/agent-dir.js +1 -1
  8. package/dist/mcp/config.js +3 -3
  9. package/dist/mcp/init.js +1 -9
  10. package/dist/mcp/sampling-handler.js +1 -1
  11. package/dist/mcp/server-manager.js +5 -1
  12. package/dist/memory/commands/status.js +1 -1
  13. package/dist/memory/compaction.js +2 -2
  14. package/dist/memory/config.js +3 -3
  15. package/dist/memory/debug-log.js +1 -1
  16. package/dist/memory/index.js +2 -0
  17. package/dist/memory/observer.js +2 -2
  18. package/dist/memory/tokens.js +1 -1
  19. package/dist/memory/tools/read-project-observations.js +2 -2
  20. package/dist/memory/tools/recall-observation.js +2 -2
  21. package/dist/memory/tools/write-project-observation.js +60 -0
  22. package/dist/relay/auto-research.js +57 -23
  23. package/dist/relay/dispatcher.js +28 -2
  24. package/dist/relay/models-fetch.js +2 -2
  25. package/dist/{pi → sdk}/ai/env-api-keys.js +9 -49
  26. package/dist/{pi → sdk}/ai/utils/oauth/anthropic.js +1 -1
  27. package/dist/{pi → sdk}/ai/utils/oauth/openai-codex.js +1 -1
  28. package/dist/{pi → sdk}/coding-agent/config.js +11 -78
  29. package/dist/{pi → sdk}/coding-agent/core/agent-session.js +2 -0
  30. package/dist/{pi → sdk}/coding-agent/core/compaction/compaction.js +161 -5
  31. package/dist/{pi → sdk}/coding-agent/core/extensions/loader.js +2 -35
  32. package/dist/{pi → sdk}/coding-agent/core/extensions/runner.js +1 -2
  33. package/dist/{pi → sdk}/coding-agent/core/model-registry.js +11 -4
  34. package/dist/sdk/coding-agent/core/model-resolver-utils.js +8 -0
  35. package/dist/{pi → sdk}/coding-agent/core/model-resolver.js +1 -1
  36. package/dist/{pi → sdk}/coding-agent/core/package-manager.js +5 -5
  37. package/dist/{pi → sdk}/coding-agent/core/resource-loader.js +1 -1
  38. package/dist/{pi → sdk}/coding-agent/core/sdk.js +1 -1
  39. package/dist/{pi → sdk}/coding-agent/core/session-manager.js +4 -4
  40. package/dist/{pi → sdk}/coding-agent/core/settings-manager.js +1 -170
  41. package/dist/{pi → sdk}/coding-agent/core/system-prompt.js +3 -1
  42. package/dist/{pi → sdk}/coding-agent/core/telemetry.js +1 -1
  43. package/dist/sdk/coding-agent/core/theme.js +202 -0
  44. package/dist/{pi → sdk}/coding-agent/core/tools/bash.js +17 -18
  45. package/dist/{pi → sdk}/coding-agent/core/tools/edit.js +7 -8
  46. package/dist/{pi → sdk}/coding-agent/core/tools/find.js +9 -13
  47. package/dist/{pi → sdk}/coding-agent/core/tools/grep.js +10 -14
  48. package/dist/{pi → sdk}/coding-agent/core/tools/ls.js +9 -10
  49. package/dist/{pi → sdk}/coding-agent/core/tools/read.js +15 -25
  50. package/dist/{pi/coding-agent/modes/interactive/components/diff.js → sdk/coding-agent/core/tools/render-diff.js} +18 -31
  51. package/dist/{pi → sdk}/coding-agent/core/tools/write.js +10 -11
  52. package/dist/{pi → sdk}/coding-agent/index.js +7 -5
  53. package/dist/{pi → sdk}/coding-agent/migrations.js +3 -3
  54. package/dist/{pi → sdk}/coding-agent/modes/index.js +0 -1
  55. package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-mode.js +2 -2
  56. package/dist/{pi → sdk}/coding-agent/utils/photon.js +2 -10
  57. package/dist/sdk/coding-agent/utils/pi-user-agent.js +3 -0
  58. package/dist/{pi → sdk}/coding-agent/utils/tools-manager.js +1 -1
  59. package/dist/{pi → sdk}/coding-agent/utils/version-check.js +2 -2
  60. package/dist/{pi → sdk}/coding-agent/utils/windows-self-update.js +1 -1
  61. package/dist/server/{pi-bridge.js → agent-bridge.js} +114 -97
  62. package/dist/server/handlers/sessions.js +21 -0
  63. package/dist/server/session-stream.js +5 -5
  64. package/package.json +6 -3
  65. package/dist/pi/coding-agent/bun/cli.js +0 -7
  66. package/dist/pi/coding-agent/bun/restore-sandbox-env.js +0 -31
  67. package/dist/pi/coding-agent/cli/args.js +0 -340
  68. package/dist/pi/coding-agent/cli/file-processor.js +0 -82
  69. package/dist/pi/coding-agent/cli/initial-message.js +0 -21
  70. package/dist/pi/coding-agent/core/footer-data-provider.js +0 -309
  71. package/dist/pi/coding-agent/modes/interactive/components/keybinding-hints.js +0 -35
  72. package/dist/pi/coding-agent/modes/interactive/components/visual-truncate.js +0 -26
  73. package/dist/pi/coding-agent/modes/interactive/interactive-mode.js +0 -3
  74. package/dist/pi/coding-agent/modes/interactive/theme/theme.js +0 -1022
  75. package/dist/pi/coding-agent/utils/pi-user-agent.js +0 -4
  76. /package/dist/{pi → sdk}/agent-core/agent-loop.js +0 -0
  77. /package/dist/{pi → sdk}/agent-core/agent.js +0 -0
  78. /package/dist/{pi → sdk}/agent-core/harness/agent-harness.js +0 -0
  79. /package/dist/{pi → sdk}/agent-core/harness/compaction/branch-summarization.js +0 -0
  80. /package/dist/{pi → sdk}/agent-core/harness/compaction/compaction.js +0 -0
  81. /package/dist/{pi → sdk}/agent-core/harness/compaction/utils.js +0 -0
  82. /package/dist/{pi → sdk}/agent-core/harness/env/nodejs.js +0 -0
  83. /package/dist/{pi → sdk}/agent-core/harness/messages.js +0 -0
  84. /package/dist/{pi → sdk}/agent-core/harness/prompt-templates.js +0 -0
  85. /package/dist/{pi → sdk}/agent-core/harness/session/jsonl-repo.js +0 -0
  86. /package/dist/{pi → sdk}/agent-core/harness/session/jsonl-storage.js +0 -0
  87. /package/dist/{pi → sdk}/agent-core/harness/session/memory-repo.js +0 -0
  88. /package/dist/{pi → sdk}/agent-core/harness/session/memory-storage.js +0 -0
  89. /package/dist/{pi → sdk}/agent-core/harness/session/repo-utils.js +0 -0
  90. /package/dist/{pi → sdk}/agent-core/harness/session/session.js +0 -0
  91. /package/dist/{pi → sdk}/agent-core/harness/session/uuid.js +0 -0
  92. /package/dist/{pi → sdk}/agent-core/harness/skills.js +0 -0
  93. /package/dist/{pi → sdk}/agent-core/harness/system-prompt.js +0 -0
  94. /package/dist/{pi → sdk}/agent-core/harness/types.js +0 -0
  95. /package/dist/{pi → sdk}/agent-core/harness/utils/shell-output.js +0 -0
  96. /package/dist/{pi → sdk}/agent-core/harness/utils/truncate.js +0 -0
  97. /package/dist/{pi → sdk}/agent-core/index.js +0 -0
  98. /package/dist/{pi → sdk}/agent-core/node.js +0 -0
  99. /package/dist/{pi → sdk}/agent-core/proxy.js +0 -0
  100. /package/dist/{pi → sdk}/agent-core/types.js +0 -0
  101. /package/dist/{pi → sdk}/ai/api-registry.js +0 -0
  102. /package/dist/{pi → sdk}/ai/cli.js +0 -0
  103. /package/dist/{pi → sdk}/ai/image-models.generated.js +0 -0
  104. /package/dist/{pi → sdk}/ai/image-models.js +0 -0
  105. /package/dist/{pi → sdk}/ai/images-api-registry.js +0 -0
  106. /package/dist/{pi → sdk}/ai/images.js +0 -0
  107. /package/dist/{pi → sdk}/ai/index.js +0 -0
  108. /package/dist/{pi → sdk}/ai/models.generated.js +0 -0
  109. /package/dist/{pi → sdk}/ai/models.js +0 -0
  110. /package/dist/{pi → sdk}/ai/oauth.js +0 -0
  111. /package/dist/{pi → sdk}/ai/providers/anthropic.js +0 -0
  112. /package/dist/{pi → sdk}/ai/providers/faux.js +0 -0
  113. /package/dist/{pi → sdk}/ai/providers/github-copilot-headers.js +0 -0
  114. /package/dist/{pi → sdk}/ai/providers/openai-completions.js +0 -0
  115. /package/dist/{pi → sdk}/ai/providers/openai-prompt-cache.js +0 -0
  116. /package/dist/{pi → sdk}/ai/providers/register-builtins.js +0 -0
  117. /package/dist/{pi → sdk}/ai/providers/simple-options.js +0 -0
  118. /package/dist/{pi → sdk}/ai/providers/transform-messages.js +0 -0
  119. /package/dist/{pi → sdk}/ai/session-resources.js +0 -0
  120. /package/dist/{pi → sdk}/ai/stream.js +0 -0
  121. /package/dist/{pi → sdk}/ai/types.js +0 -0
  122. /package/dist/{pi → sdk}/ai/utils/diagnostics.js +0 -0
  123. /package/dist/{pi → sdk}/ai/utils/event-stream.js +0 -0
  124. /package/dist/{pi → sdk}/ai/utils/hash.js +0 -0
  125. /package/dist/{pi → sdk}/ai/utils/headers.js +0 -0
  126. /package/dist/{pi → sdk}/ai/utils/json-parse.js +0 -0
  127. /package/dist/{pi → sdk}/ai/utils/node-http-proxy.js +0 -0
  128. /package/dist/{pi → sdk}/ai/utils/oauth/device-code.js +0 -0
  129. /package/dist/{pi → sdk}/ai/utils/oauth/github-copilot.js +0 -0
  130. /package/dist/{pi → sdk}/ai/utils/oauth/index.js +0 -0
  131. /package/dist/{pi → sdk}/ai/utils/oauth/oauth-page.js +0 -0
  132. /package/dist/{pi → sdk}/ai/utils/oauth/pkce.js +0 -0
  133. /package/dist/{pi → sdk}/ai/utils/oauth/types.js +0 -0
  134. /package/dist/{pi → sdk}/ai/utils/overflow.js +0 -0
  135. /package/dist/{pi → sdk}/ai/utils/sanitize-unicode.js +0 -0
  136. /package/dist/{pi → sdk}/ai/utils/typebox-helpers.js +0 -0
  137. /package/dist/{pi → sdk}/ai/utils/validation.js +0 -0
  138. /package/dist/{pi → sdk}/coding-agent/cli.js +0 -0
  139. /package/dist/{pi → sdk}/coding-agent/core/agent-session-runtime.js +0 -0
  140. /package/dist/{pi → sdk}/coding-agent/core/agent-session-services.js +0 -0
  141. /package/dist/{pi → sdk}/coding-agent/core/auth-guidance.js +0 -0
  142. /package/dist/{pi → sdk}/coding-agent/core/auth-storage.js +0 -0
  143. /package/dist/{pi → sdk}/coding-agent/core/bash-executor.js +0 -0
  144. /package/dist/{pi → sdk}/coding-agent/core/compaction/branch-summarization.js +0 -0
  145. /package/dist/{pi → sdk}/coding-agent/core/compaction/index.js +0 -0
  146. /package/dist/{pi → sdk}/coding-agent/core/compaction/utils.js +0 -0
  147. /package/dist/{pi → sdk}/coding-agent/core/defaults.js +0 -0
  148. /package/dist/{pi → sdk}/coding-agent/core/diagnostics.js +0 -0
  149. /package/dist/{pi → sdk}/coding-agent/core/event-bus.js +0 -0
  150. /package/dist/{pi → sdk}/coding-agent/core/exec.js +0 -0
  151. /package/dist/{pi → sdk}/coding-agent/core/extensions/index.js +0 -0
  152. /package/dist/{pi → sdk}/coding-agent/core/extensions/types.js +0 -0
  153. /package/dist/{pi → sdk}/coding-agent/core/extensions/wrapper.js +0 -0
  154. /package/dist/{pi → sdk}/coding-agent/core/http-dispatcher.js +0 -0
  155. /package/dist/{pi → sdk}/coding-agent/core/index.js +0 -0
  156. /package/dist/{pi → sdk}/coding-agent/core/keybindings.js +0 -0
  157. /package/dist/{pi → sdk}/coding-agent/core/messages.js +0 -0
  158. /package/dist/{pi → sdk}/coding-agent/core/output-guard.js +0 -0
  159. /package/dist/{pi → sdk}/coding-agent/core/prompt-templates.js +0 -0
  160. /package/dist/{pi → sdk}/coding-agent/core/provider-display-names.js +0 -0
  161. /package/dist/{pi → sdk}/coding-agent/core/resolve-config-value.js +0 -0
  162. /package/dist/{pi → sdk}/coding-agent/core/session-cwd.js +0 -0
  163. /package/dist/{pi → sdk}/coding-agent/core/skills.js +0 -0
  164. /package/dist/{pi → sdk}/coding-agent/core/slash-commands.js +0 -0
  165. /package/dist/{pi → sdk}/coding-agent/core/source-info.js +0 -0
  166. /package/dist/{pi → sdk}/coding-agent/core/timings.js +0 -0
  167. /package/dist/{pi → sdk}/coding-agent/core/tools/edit-diff.js +0 -0
  168. /package/dist/{pi → sdk}/coding-agent/core/tools/file-mutation-queue.js +0 -0
  169. /package/dist/{pi → sdk}/coding-agent/core/tools/index.js +0 -0
  170. /package/dist/{pi → sdk}/coding-agent/core/tools/output-accumulator.js +0 -0
  171. /package/dist/{pi → sdk}/coding-agent/core/tools/path-utils.js +0 -0
  172. /package/dist/{pi → sdk}/coding-agent/core/tools/render-utils.js +0 -0
  173. /package/dist/{pi → sdk}/coding-agent/core/tools/tool-definition-wrapper.js +0 -0
  174. /package/dist/{pi → sdk}/coding-agent/core/tools/truncate.js +0 -0
  175. /package/dist/{pi → sdk}/coding-agent/main.js +0 -0
  176. /package/dist/{pi → sdk}/coding-agent/modes/print-mode.js +0 -0
  177. /package/dist/{pi → sdk}/coding-agent/modes/rpc/jsonl.js +0 -0
  178. /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-client.js +0 -0
  179. /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-types.js +0 -0
  180. /package/dist/{pi → sdk}/coding-agent/utils/ansi.js +0 -0
  181. /package/dist/{pi → sdk}/coding-agent/utils/changelog.js +0 -0
  182. /package/dist/{pi → sdk}/coding-agent/utils/child-process.js +0 -0
  183. /package/dist/{pi → sdk}/coding-agent/utils/clipboard-image.js +0 -0
  184. /package/dist/{pi → sdk}/coding-agent/utils/clipboard-native.js +0 -0
  185. /package/dist/{pi → sdk}/coding-agent/utils/clipboard.js +0 -0
  186. /package/dist/{pi → sdk}/coding-agent/utils/exif-orientation.js +0 -0
  187. /package/dist/{pi → sdk}/coding-agent/utils/frontmatter.js +0 -0
  188. /package/dist/{pi → sdk}/coding-agent/utils/fs-watch.js +0 -0
  189. /package/dist/{pi → sdk}/coding-agent/utils/git.js +0 -0
  190. /package/dist/{pi → sdk}/coding-agent/utils/html.js +0 -0
  191. /package/dist/{pi → sdk}/coding-agent/utils/image-convert.js +0 -0
  192. /package/dist/{pi → sdk}/coding-agent/utils/image-resize.js +0 -0
  193. /package/dist/{pi → sdk}/coding-agent/utils/mime.js +0 -0
  194. /package/dist/{pi → sdk}/coding-agent/utils/paths.js +0 -0
  195. /package/dist/{pi → sdk}/coding-agent/utils/shell.js +0 -0
  196. /package/dist/{pi → sdk}/coding-agent/utils/sleep.js +0 -0
  197. /package/dist/{pi → sdk}/coding-agent/utils/syntax-highlight.js +0 -0
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-research handler — sends an auto-research task through the existing
3
- * PiBridge (backend proxy) instead of spawning a separate pi process.
3
+ * AgentBridge (backend proxy) instead of spawning a separate pi process.
4
4
  *
5
5
  * This ensures auto-research uses the same model and API keys as the active
6
6
  * session — no separate subprocess, no missing API key errors.
@@ -47,12 +47,12 @@ function makeRelaySubscriber(sessionId, relay) {
47
47
  };
48
48
  }
49
49
  /**
50
- * Scan the project's .pi/extensions/auto-research/ directory for generated
50
+ * Scan the project's .spectral/extensions/auto-research/ directory for generated
51
51
  * extension directories. Each subdirectory with .ts files counts as one
52
52
  * extension.
53
53
  */
54
54
  function scanGeneratedExtensions(projectPath) {
55
- const arDir = path.join(projectPath, ".pi", "extensions", "auto-research");
55
+ const arDir = path.join(projectPath, ".spectral", "extensions", "auto-research");
56
56
  const extensions = [];
57
57
  try {
58
58
  if (!fs.existsSync(arDir))
@@ -105,7 +105,7 @@ function scanGeneratedExtensions(projectPath) {
105
105
  }
106
106
  extensions.push({
107
107
  name: entry.name,
108
- path: `.pi/extensions/auto-research/${entry.name}`,
108
+ path: `.spectral/extensions/auto-research/${entry.name}`,
109
109
  description,
110
110
  usesLLM,
111
111
  fileCount,
@@ -137,7 +137,7 @@ function hasAgentsMdUpdate(projectPath) {
137
137
  */
138
138
  function readManifest(projectPath) {
139
139
  try {
140
- const mPath = path.join(projectPath, ".pi", "extensions", "auto-research", "manifest.json");
140
+ const mPath = path.join(projectPath, ".spectral", "extensions", "auto-research", "manifest.json");
141
141
  if (!fs.existsSync(mPath))
142
142
  return null;
143
143
  const raw = fs.readFileSync(mPath, "utf-8");
@@ -164,7 +164,7 @@ function gatherPreRunContext(projectPath) {
164
164
  const isIncremental = manifest !== null;
165
165
  const existingExtensions = [];
166
166
  try {
167
- const arDir = path.join(projectPath, ".pi", "extensions", "auto-research");
167
+ const arDir = path.join(projectPath, ".spectral", "extensions", "auto-research");
168
168
  if (fs.existsSync(arDir)) {
169
169
  for (const entry of fs.readdirSync(arDir, { withFileTypes: true })) {
170
170
  if (entry.isDirectory() && entry.name !== "node_modules") {
@@ -209,7 +209,7 @@ function buildIncrementalSection(ctx) {
209
209
  if (ctx.existingExtensions.length > 0) {
210
210
  lines.push("### Existing extensions to review:");
211
211
  for (const name of ctx.existingExtensions) {
212
- lines.push(` - \`.pi/extensions/auto-research/${name}/\``);
212
+ lines.push(` - \`.spectral/extensions/auto-research/${name}/\``);
213
213
  }
214
214
  lines.push("");
215
215
  }
@@ -224,7 +224,7 @@ function buildIncrementalSection(ctx) {
224
224
  lines.push(" was removed from the project, delete the extension directory entirely.", "");
225
225
  lines.push("4. **Update AGENTS.md** — The AUTO-RESEARCH section should reflect the CURRENT");
226
226
  lines.push(" set of extensions (remove stale entries, add new ones).", "");
227
- lines.push("5. **Save manifest.json** — Write/update .pi/extensions/auto-research/manifest.json");
227
+ lines.push("5. **Save manifest.json** — Write/update .spectral/extensions/auto-research/manifest.json");
228
228
  lines.push(` with: lastRun (ISO), lastCommit (git HEAD), runCount (${ctx.manifest ? ctx.manifest.runCount + 1 : 1}),`);
229
229
  lines.push(" and extensions array with name/path/category for each generated extension.", "");
230
230
  return lines.join("\n");
@@ -234,7 +234,7 @@ function buildIncrementalSection(ctx) {
234
234
  */
235
235
  function writeManifest(projectPath, extensions) {
236
236
  try {
237
- const arDir = path.join(projectPath, ".pi", "extensions", "auto-research");
237
+ const arDir = path.join(projectPath, ".spectral", "extensions", "auto-research");
238
238
  if (!fs.existsSync(arDir))
239
239
  fs.mkdirSync(arDir, { recursive: true });
240
240
  let currentCommit = "unknown";
@@ -257,7 +257,7 @@ function writeManifest(projectPath, extensions) {
257
257
  }
258
258
  /**
259
259
  * Build the auto-research task prompt. This is sent as a user message
260
- * through the existing PiBridge, so the agent uses the session's model
260
+ * through the existing AgentBridge, so the agent uses the session's model
261
261
  * and backend proxy.
262
262
  *
263
263
  * @param projectPath Absolute path to the project root
@@ -297,8 +297,8 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
297
297
  "}",
298
298
  "```",
299
299
  "",
300
- "Extensions live in: `.pi/extensions/` (project-local) or `~/.pi/agent/extensions/` (user-global).",
301
- "Auto-research generates extensions into `.pi/extensions/auto-research/<name>/`.",
300
+ "Extensions live in: `.spectral/extensions/` (project-local) or `~/.spectral/agent/extensions/` (user-global).",
301
+ "Auto-research generates extensions into `.spectral/extensions/auto-research/<name>/`.",
302
302
  "",
303
303
  "### Tools (pi.registerTool)",
304
304
  "",
@@ -400,10 +400,10 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
400
400
  "",
401
401
  "### Extension File Structure",
402
402
  "",
403
- "Each extension is a directory under `.pi/extensions/auto-research/`:",
403
+ "Each extension is a directory under `.spectral/extensions/auto-research/`:",
404
404
  "",
405
405
  "```",
406
- ".pi/extensions/auto-research/",
406
+ ".spectral/extensions/auto-research/",
407
407
  " <extension-name>/",
408
408
  " index.ts # Entry point — default export activate(pi)",
409
409
  " utils.ts # [optional] Helper functions",
@@ -440,7 +440,7 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
440
440
  "",
441
441
  "1. **Context Collection** — Explore the project structure:",
442
442
  " - Read package.json, tsconfig.json, deno.json (if present)",
443
- " - Check existing extensions under .pi/extensions/",
443
+ " - Check existing extensions under .spectral/extensions/",
444
444
  " - Review key source files to understand architecture",
445
445
  " - Check git log for recent changes and patterns",
446
446
  "",
@@ -454,9 +454,9 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
454
454
  " - Use `pi.registerTool()` for simple tools with TypeBox validation",
455
455
  " - Use `pi.registerCommand()` for custom slash commands",
456
456
  " - For LLM-powered extensions, use `ctx.modelRegistry` to call models",
457
- " - Create files under `.pi/extensions/auto-research/<name>/`",
457
+ " - Create files under `.spectral/extensions/auto-research/<name>/`",
458
458
  " - Each extension needs an `index.ts` that registers its tools/commands",
459
- " - Read `.pi/agents/auto-research-templates.md` for proven extension templates",
459
+ " - Read `.spectral/agents/auto-research-templates.md` for proven extension templates",
460
460
  "",
461
461
  "4. **Validation** — Verify generated extensions:",
462
462
  " - Ensure all imports resolve to available packages (see reference above)",
@@ -478,7 +478,7 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
478
478
  "",
479
479
  "### Important Rules",
480
480
  "",
481
- "- Write each extension as TypeScript files under `.pi/extensions/auto-research/<name>/`",
481
+ "- Write each extension as TypeScript files under `.spectral/extensions/auto-research/<name>/`",
482
482
  "- Every extension directory MUST have an `index.ts` entry point",
483
483
  "- Use proper TypeScript with type annotations and error handling",
484
484
  "- Extensions must handle errors gracefully (never crash the agent)",
@@ -491,7 +491,7 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
491
491
  "agent sessions automatically know about the new capabilities.",
492
492
  "",
493
493
  "**How AGENTS.md works:**",
494
- "- Pi loads AGENTS.md at startup from the project root and parent directories",
494
+ "- Spectral loads AGENTS.md at startup from the project root and parent directories",
495
495
  "- All found AGENTS.md files are concatenated and injected into the system prompt",
496
496
  "- This means documented extensions get discovered by the agent automatically",
497
497
  "",
@@ -502,7 +502,7 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
502
502
  "## Auto-Generated Extensions",
503
503
  "",
504
504
  "These extensions were generated by auto-research. They are available",
505
- "in every session. Pi loads them automatically from `.pi/extensions/`.",
505
+ "in every session. Spectral loads them automatically from `.spectral/extensions/`.",
506
506
  "",
507
507
  "### `<extension-name>`",
508
508
  "",
@@ -532,6 +532,40 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
532
532
  "",
533
533
  "Start by collecting project context, then generate the most impactful extensions",
534
534
  "and update AGENTS.md when done.",
535
+ "",
536
+ "### 6. Persist Key Discoveries as Durable Observations",
537
+ "",
538
+ "As you analyze the project, you will discover conventions, rules, and patterns",
539
+ "that future agent sessions should know. Write these as durable cross-session",
540
+ "observations using the `write_project_observation` tool:",
541
+ "",
542
+ "```",
543
+ "write_project_observation({",
544
+ " content: \"backend/src/models.ts is auto-generated by Axolotl — never edit by hand\",",
545
+ " relevance: \"critical\"",
546
+ "})",
547
+ "```",
548
+ "",
549
+ "**What to capture:**",
550
+ "",
551
+ "- **NEVER-EDIT markers** (relevance: critical): Auto-generated files that must",
552
+ " be regenerated instead of edited. Seen a comment like 'DO NOT EDIT'? Persist it.",
553
+ "- **Import/macro conventions** (relevance: high): Rules like \"all Deno imports",
554
+ " MUST use .ts extension\" or \"use workspace imports, not relative paths\".",
555
+ "- **Code-generation commands** (relevance: high): The exact shell commands to",
556
+ " regenerate generated files (e.g. 'cd backend && deno task axolotl:build').",
557
+ "- **Directory roles** (relevance: medium): What each top-level directory contains",
558
+ " (e.g. 'packages/parser/ is zero-dependency — never add imports here').",
559
+ "- **Runtime boundaries** (relevance: medium): Which packages use Deno vs Node.",
560
+ "- **Database/API patterns** (relevance: medium): How the backend is structured.",
561
+ "",
562
+ "**Guidelines:**",
563
+ "",
564
+ "- Write 5–10 observations covering the most important discoveries.",
565
+ "- Each observation is a single self-contained sentence — no markdown, no lists.",
566
+ "- Focus on facts that would PREVENT MISTAKES or SAVE TIME in future sessions.",
567
+ "- Deduplication is automatic by content hash — writing the same fact twice is harmless.",
568
+ "- Future sessions retrieve these via `read_project_observations(\"query\")`.",
535
569
  ].join("\n");
536
570
  }
537
571
  // ---------------------------------------------------------------------------
@@ -539,7 +573,7 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
539
573
  // ---------------------------------------------------------------------------
540
574
  const AR_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes — generous for LLM-based analysis
541
575
  /**
542
- * Execute auto-research for a project through the existing PiBridge.
576
+ * Execute auto-research for a project through the existing AgentBridge.
543
577
  *
544
578
  * Caller (dispatcher) is fire-and-forget — this function is `void` and
545
579
  * all errors are surfaced as `auto_research_error` events on the wire
@@ -547,7 +581,7 @@ const AR_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes — generous for LLM-based a
547
581
  *
548
582
  * This replaces the old subprocess-spawning approach. Instead of launching
549
583
  * a separate pi process (which lacks backend proxy credentials), we send
550
- * the auto-research task through the session's existing PiBridge. The agent
584
+ * the auto-research task through the session's existing AgentBridge. The agent
551
585
  * uses the session's model, backend proxy, and all available tools.
552
586
  */
553
587
  export function handleAutoResearch(input, deps) {
@@ -700,7 +734,7 @@ export function handleAutoResearch(input, deps) {
700
734
  // then timeout fires and calls finalize() again — but finalize() is
701
735
  // gated by watcherFired, so it's a no-op. The only issue is we don't
702
736
  // clearTimeout on watcher fire — but that's fine, the timer is harmless.
703
- // --- Send the prompt through the existing PiBridge (backend proxy) ---
737
+ // --- Send the prompt through the existing AgentBridge (backend proxy) ---
704
738
  manager.prompt(sessionId, taskContent, storedModelId).catch((err) => {
705
739
  if (watcherFired)
706
740
  return; // already handled by watcher
@@ -41,7 +41,7 @@
41
41
  import { BadRequestError, NotFoundError } from "../server/handlers/errors.js";
42
42
  import { handlePathAutocomplete } from "../server/handlers/paths-autocomplete.js";
43
43
  import { handleBindStudioProject, handleCreateProject, handleDeleteProject, handleListProjects, handleListSessionsByProject, handleUpdateProject, } from "../server/handlers/projects.js";
44
- import { handleCompactSession, handleCreateSession, handleDeleteSession, handleForkSession, handleGetSessionDetail, handleGetSessionMemoryDetails, handleGetSessionMemoryStatus, handleUpdateSession, } from "../server/handlers/sessions.js";
44
+ import { handleCompactSession, handleCreateSession, handleDeleteSession, handleForkSession, handleRememberAndDeleteSession, handleGetSessionDetail, handleGetSessionMemoryDetails, handleGetSessionMemoryStatus, handleUpdateSession, } from "../server/handlers/sessions.js";
45
45
  import { handleClearPromptQueue, handleEnqueuePrompt, handleGetPromptQueue, handleRemovePrompt, } from "../server/handlers/queue.js";
46
46
  import { shutdownState } from "../server/shutdown.js";
47
47
  import { handleAutoResearch } from "./auto-research.js";
@@ -126,6 +126,14 @@ export function matchRoute(method, path) {
126
126
  return { route: "compact_session", id };
127
127
  return null;
128
128
  }
129
+ // /api/sessions/:id/remember-and-delete
130
+ const rememberDeleteMatch = /^\/api\/sessions\/([^/]+)\/remember-and-delete$/.exec(cleanPath);
131
+ if (rememberDeleteMatch) {
132
+ const id = decodeURIComponent(rememberDeleteMatch[1]);
133
+ if (method === "POST")
134
+ return { route: "remember_and_delete_session", id };
135
+ return null;
136
+ }
129
137
  // /api/sessions/:id/fork
130
138
  const forkMatch = /^\/api\/sessions\/([^/]+)\/fork$/.exec(cleanPath);
131
139
  if (forkMatch) {
@@ -341,6 +349,24 @@ async function dispatchRoute(match, body, deps) {
341
349
  }
342
350
  case "compact_session":
343
351
  return await handleCompactSession(store, manager, id);
352
+ case "remember_and_delete_session": {
353
+ // Compact first — the compaction hook's persistProjectObservations
354
+ // runs inside compactSession, writing reflections to cross-session
355
+ // durable memory. On failure (e.g. no API key), we let the error
356
+ // propagate so the session is NOT deleted.
357
+ await handleRememberAndDeleteSession(store, manager, id);
358
+ const detail = store.getSession(id);
359
+ manager.disposeSessionStream(id);
360
+ handleDeleteSession(store, id);
361
+ if (detail) {
362
+ safePublish(publishMetaEvent, logger, {
363
+ type: "session_deleted",
364
+ projectId: detail.projectId,
365
+ sessionId: id,
366
+ });
367
+ }
368
+ return { ok: true };
369
+ }
344
370
  case "fork_session": {
345
371
  const session = handleForkSession(store, id, asObject(body));
346
372
  safePublish(publishMetaEvent, logger, {
@@ -682,7 +708,7 @@ export function detachAllSubscribers(manager, subscribers) {
682
708
  }
683
709
  /**
684
710
  * Dispatch an `auto_research` frame. Sends the auto-research task through
685
- * the existing PiBridge (backend proxy) instead of spawning a separate pi
711
+ * the existing AgentBridge (backend proxy) instead of spawning a separate pi
686
712
  * subprocess. This ensures auto-research uses the same model and API keys
687
713
  * as the active session.
688
714
  *
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Fetch the admin-managed list of allowed base models from the backend.
3
3
  *
4
- * Used by `PiBridge` at startup to register synthetic providers
4
+ * Used by `AgentBridge` at startup to register synthetic providers
5
5
  * (`spectral-proxy-anthropic` / `spectral-proxy-openai`) that route every
6
6
  * inference call through the backend's `/v1/messages` and
7
7
  * `/v1/chat/completions` endpoints. The backend authenticates the call
@@ -32,7 +32,7 @@ const QUERY = `query AvailableBaseModels { availableBaseModels { name provider u
32
32
  /**
33
33
  * Fetch the whitelist of allowed base models. Throws on any failure with a
34
34
  * message tailored for an operator running `spectral serve` — the caller
35
- * (PiBridge.start) lets the throw propagate so the WS subscriber sees a
35
+ * (AgentBridge.start) lets the throw propagate so the WS subscriber sees a
36
36
  * clear error event instead of a silent fall-through to "no models".
37
37
  */
38
38
  export async function fetchAllowedModels(opts) {
@@ -6,8 +6,8 @@ const dynamicImport = (specifier) => import(specifier);
6
6
  const NODE_FS_SPECIFIER = "node:" + "fs";
7
7
  const NODE_OS_SPECIFIER = "node:" + "os";
8
8
  const NODE_PATH_SPECIFIER = "node:" + "path";
9
- // Eagerly load in Node.js/Bun environment only
10
- if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
9
+ // Eagerly load in Node.js environment only
10
+ if (typeof process !== "undefined" && process.versions?.node) {
11
11
  dynamicImport(NODE_FS_SPECIFIER).then((m) => {
12
12
  _existsSync = m.existsSync;
13
13
  });
@@ -18,38 +18,6 @@ if (typeof process !== "undefined" && (process.versions?.node || process.version
18
18
  _join = m.join;
19
19
  });
20
20
  }
21
- let _procEnvCache = null;
22
- /**
23
- * Fallback for https://github.com/oven-sh/bun/issues/27802
24
- * Bun compiled binaries have an empty `process.env` inside sandbox
25
- * environments on Linux. We can recover the env from `/proc/self/environ`.
26
- */
27
- function getProcEnv(key) {
28
- if (!process.versions?.bun)
29
- return undefined;
30
- if (typeof process === "undefined")
31
- return undefined;
32
- // If process.env already has entries, the bug is not triggered.
33
- if (Object.keys(process.env).length > 0)
34
- return undefined;
35
- if (_procEnvCache === null) {
36
- _procEnvCache = new Map();
37
- try {
38
- const { readFileSync } = require("node:fs");
39
- const data = readFileSync("/proc/self/environ", "utf-8");
40
- for (const entry of data.split("\0")) {
41
- const idx = entry.indexOf("=");
42
- if (idx > 0) {
43
- _procEnvCache.set(entry.slice(0, idx), entry.slice(idx + 1));
44
- }
45
- }
46
- }
47
- catch {
48
- // /proc/self/environ may not be readable.
49
- }
50
- }
51
- return _procEnvCache.get(key);
52
- }
53
21
  let cachedVertexAdcCredentialsExists = null;
54
22
  function hasVertexAdcCredentials() {
55
23
  if (cachedVertexAdcCredentialsExists === null) {
@@ -57,7 +25,7 @@ function hasVertexAdcCredentials() {
57
25
  // return false WITHOUT caching so the next call retries once they're ready.
58
26
  // Only cache false permanently in a browser environment where fs is never available.
59
27
  if (!_existsSync || !_homedir || !_join) {
60
- const isNode = typeof process !== "undefined" && (process.versions?.node || process.versions?.bun);
28
+ const isNode = typeof process !== "undefined" && process.versions?.node;
61
29
  if (!isNode) {
62
30
  // Definitively in a browser — safe to cache false permanently
63
31
  cachedVertexAdcCredentialsExists = false;
@@ -65,7 +33,7 @@ function hasVertexAdcCredentials() {
65
33
  return false;
66
34
  }
67
35
  // Check GOOGLE_APPLICATION_CREDENTIALS env var first (standard way)
68
- const gacPath = process.env.GOOGLE_APPLICATION_CREDENTIALS || getProcEnv("GOOGLE_APPLICATION_CREDENTIALS");
36
+ const gacPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
69
37
  if (gacPath) {
70
38
  cachedVertexAdcCredentialsExists = _existsSync(gacPath);
71
39
  }
@@ -121,23 +89,21 @@ export function findEnvKeys(provider) {
121
89
  const envVars = getApiKeyEnvVars(provider);
122
90
  if (!envVars)
123
91
  return undefined;
124
- const found = envVars.filter((envVar) => !!process.env[envVar] || !!getProcEnv(envVar));
92
+ const found = envVars.filter((envVar) => !!process.env[envVar]);
125
93
  return found.length > 0 ? found : undefined;
126
94
  }
127
95
  export function getEnvApiKey(provider) {
128
96
  const envKeys = findEnvKeys(provider);
129
97
  if (envKeys?.[0]) {
130
- return process.env[envKeys[0]] || getProcEnv(envKeys[0]);
98
+ return process.env[envKeys[0]];
131
99
  }
132
100
  // Vertex AI supports either an explicit API key or Application Default Credentials.
133
101
  // Auth is configured via `gcloud auth application-default login`.
134
102
  if (provider === "google-vertex") {
135
103
  const hasCredentials = hasVertexAdcCredentials();
136
104
  const hasProject = !!(process.env.GOOGLE_CLOUD_PROJECT ||
137
- process.env.GCLOUD_PROJECT ||
138
- getProcEnv("GOOGLE_CLOUD_PROJECT") ||
139
- getProcEnv("GCLOUD_PROJECT"));
140
- const hasLocation = !!(process.env.GOOGLE_CLOUD_LOCATION || getProcEnv("GOOGLE_CLOUD_LOCATION"));
105
+ process.env.GCLOUD_PROJECT);
106
+ const hasLocation = !!(process.env.GOOGLE_CLOUD_LOCATION);
141
107
  if (hasCredentials && hasProject && hasLocation) {
142
108
  return "<authenticated>";
143
109
  }
@@ -155,13 +121,7 @@ export function getEnvApiKey(provider) {
155
121
  process.env.AWS_BEARER_TOKEN_BEDROCK ||
156
122
  process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ||
157
123
  process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI ||
158
- process.env.AWS_WEB_IDENTITY_TOKEN_FILE ||
159
- getProcEnv("AWS_PROFILE") ||
160
- (getProcEnv("AWS_ACCESS_KEY_ID") && getProcEnv("AWS_SECRET_ACCESS_KEY")) ||
161
- getProcEnv("AWS_BEARER_TOKEN_BEDROCK") ||
162
- getProcEnv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI") ||
163
- getProcEnv("AWS_CONTAINER_CREDENTIALS_FULL_URI") ||
164
- getProcEnv("AWS_WEB_IDENTITY_TOKEN_FILE")) {
124
+ process.env.AWS_WEB_IDENTITY_TOKEN_FILE) {
165
125
  return "<authenticated>";
166
126
  }
167
127
  }
@@ -21,7 +21,7 @@ async function getNodeApis() {
21
21
  if (nodeApis)
22
22
  return nodeApis;
23
23
  if (!nodeApisPromise) {
24
- if (typeof process === "undefined" || (!process.versions?.node && !process.versions?.bun)) {
24
+ if (typeof process === "undefined" || !process.versions?.node) {
25
25
  throw new Error("Anthropic OAuth is only available in Node.js environments");
26
26
  }
27
27
  nodeApisPromise = import("node:http").then((httpModule) => ({
@@ -7,7 +7,7 @@
7
7
  // NEVER convert to top-level imports - breaks browser/Vite builds
8
8
  let _randomBytes = null;
9
9
  let _http = null;
10
- if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
10
+ if (typeof process !== "undefined" && process.versions?.node) {
11
11
  import("node:crypto").then((m) => {
12
12
  _randomBytes = m.randomBytes;
13
13
  });
@@ -9,13 +9,6 @@ import { normalizePath } from "./utils/paths.js";
9
9
  // =============================================================================
10
10
  const __filename = fileURLToPath(import.meta.url);
11
11
  const __dirname = dirname(__filename);
12
- /**
13
- * Detect if we're running as a Bun compiled binary.
14
- * Bun binaries have import.meta.url containing "$bunfs", "~BUN", or "%7EBUN" (Bun's virtual filesystem path)
15
- */
16
- export const isBunBinary = import.meta.url.includes("$bunfs") || import.meta.url.includes("~BUN") || import.meta.url.includes("%7EBUN");
17
- /** Detect if Bun is the runtime (compiled binary or bun run) */
18
- export const isBunRuntime = !!process.versions.bun;
19
12
  function makeSelfUpdateCommand(installStep, uninstallStep) {
20
13
  if (!uninstallStep)
21
14
  return installStep;
@@ -33,9 +26,6 @@ function makeSelfUpdateCommandStep(command, args) {
33
26
  };
34
27
  }
35
28
  export function detectInstallMethod() {
36
- if (isBunBinary) {
37
- return "bun-binary";
38
- }
39
29
  const resolvedPath = `${__dirname}\0${process.execPath || ""}`.toLowerCase().replace(/\\/g, "/");
40
30
  if (resolvedPath.includes("/pnpm/") || resolvedPath.includes("/.pnpm/")) {
41
31
  return "pnpm";
@@ -43,9 +33,6 @@ export function detectInstallMethod() {
43
33
  if (resolvedPath.includes("/yarn/") || resolvedPath.includes("/.yarn/")) {
44
34
  return "yarn";
45
35
  }
46
- if (isBunRuntime || resolvedPath.includes("/install/global/node_modules/")) {
47
- return "bun";
48
- }
49
36
  if (resolvedPath.includes("/npm/") || resolvedPath.includes("/node_modules/")) {
50
37
  return "npm";
51
38
  }
@@ -74,8 +61,6 @@ function getInferredNpmInstall() {
74
61
  }
75
62
  function getSelfUpdateCommandForMethod(method, installedPackageName, updatePackageName = installedPackageName, npmCommand) {
76
63
  switch (method) {
77
- case "bun-binary":
78
- return undefined;
79
64
  case "pnpm":
80
65
  return makeSelfUpdateCommand(makeSelfUpdateCommandStep("pnpm", ["install", "-g", "--ignore-scripts", updatePackageName]), updatePackageName === installedPackageName
81
66
  ? undefined
@@ -84,10 +69,6 @@ function getSelfUpdateCommandForMethod(method, installedPackageName, updatePacka
84
69
  return makeSelfUpdateCommand(makeSelfUpdateCommandStep("yarn", ["global", "add", "--ignore-scripts", updatePackageName]), updatePackageName === installedPackageName
85
70
  ? undefined
86
71
  : makeSelfUpdateCommandStep("yarn", ["global", "remove", installedPackageName]));
87
- case "bun":
88
- return makeSelfUpdateCommand(makeSelfUpdateCommandStep("bun", ["install", "-g", "--ignore-scripts", updatePackageName]), updatePackageName === installedPackageName
89
- ? undefined
90
- : makeSelfUpdateCommandStep("bun", ["uninstall", "-g", installedPackageName]));
91
72
  case "npm": {
92
73
  const [command = "npm", ...npmArgs] = npmCommand ?? [];
93
74
  const inferred = npmCommand?.length ? undefined : getInferredNpmInstall();
@@ -126,16 +107,6 @@ function getGlobalPackageRoots(method, _packageName, npmCommand) {
126
107
  case "npm": {
127
108
  const configured = !!npmCommand?.length;
128
109
  const [command = "npm", ...npmArgs] = npmCommand ?? [];
129
- if (configured && command === "bun") {
130
- const bunBin = readCommandOutput(command, [...npmArgs, "pm", "bin", "-g"], {
131
- requireSuccess: true,
132
- });
133
- const roots = [join(homedir(), ".bun", "install", "global", "node_modules")];
134
- if (bunBin) {
135
- roots.push(join(dirname(bunBin), "install", "global", "node_modules"));
136
- }
137
- return roots;
138
- }
139
110
  const root = readCommandOutput(command, [...npmArgs, "root", "-g"], {
140
111
  requireSuccess: configured,
141
112
  });
@@ -150,15 +121,6 @@ function getGlobalPackageRoots(method, _packageName, npmCommand) {
150
121
  const dir = readCommandOutput("yarn", ["global", "dir"]);
151
122
  return dir ? [dir, join(dir, "node_modules")] : [];
152
123
  }
153
- case "bun": {
154
- const bunBin = readCommandOutput("bun", ["pm", "bin", "-g"]);
155
- const roots = [join(homedir(), ".bun", "install", "global", "node_modules")];
156
- if (bunBin) {
157
- roots.push(join(dirname(bunBin), "install", "global", "node_modules"));
158
- }
159
- return roots;
160
- }
161
- case "bun-binary":
162
124
  case "unknown":
163
125
  return [];
164
126
  }
@@ -229,9 +191,6 @@ export function getSelfUpdateCommand(packageName, npmCommand, updatePackageName
229
191
  }
230
192
  export function getSelfUpdateUnavailableInstruction(packageName, npmCommand, updatePackageName = packageName) {
231
193
  const method = detectInstallMethod();
232
- if (method === "bun-binary") {
233
- return `Download from: https://github.com/earendil-works/pi-mono/releases/latest`;
234
- }
235
194
  const command = getSelfUpdateCommandForMethod(method, packageName, updatePackageName, npmCommand);
236
195
  if (command) {
237
196
  if (isManagedByGlobalPackageManager(method, packageName, npmCommand) && !isSelfUpdatePathWritable()) {
@@ -254,7 +213,6 @@ export function getUpdateInstruction(packageName) {
254
213
  // =============================================================================
255
214
  /**
256
215
  * Get the base directory for resolving package assets (themes, package.json, README.md, CHANGELOG.md).
257
- * - For Bun binary: returns the directory containing the executable
258
216
  * - For Node.js (dist/): returns __dirname (the dist/ directory)
259
217
  * - For tsx (src/): returns parent directory (the package root)
260
218
  */
@@ -264,10 +222,6 @@ export function getPackageDir() {
264
222
  if (envDir) {
265
223
  return normalizePath(envDir);
266
224
  }
267
- if (isBunBinary) {
268
- // Bun binary: process.execPath points to the compiled executable
269
- return dirname(process.execPath);
270
- }
271
225
  // Node.js: walk up from __dirname until we find package.json
272
226
  let dir = __dirname;
273
227
  while (dir !== dirname(dir)) {
@@ -281,14 +235,10 @@ export function getPackageDir() {
281
235
  }
282
236
  /**
283
237
  * Get path to built-in themes directory (shipped with package)
284
- * - For Bun binary: theme/ next to executable
285
238
  * - For Node.js (dist/): dist/modes/interactive/theme/
286
239
  * - For tsx (src/): src/modes/interactive/theme/
287
240
  */
288
241
  export function getThemesDir() {
289
- if (isBunBinary) {
290
- return join(getPackageDir(), "theme");
291
- }
292
242
  // Theme is in modes/interactive/theme/ relative to src/ or dist/
293
243
  const packageDir = getPackageDir();
294
244
  const srcOrDist = existsSync(join(packageDir, "src")) ? "src" : "dist";
@@ -296,7 +246,8 @@ export function getThemesDir() {
296
246
  }
297
247
  /**
298
248
  * Get path to HTML export template directory (shipped with package)
299
- * - For Bun binary: export-html/ next to executable
249
+ * - For Node.js (dist/): returns __dirname (the dist/ directory)
250
+ * - For tsx (src/): returns parent directory (the package root)
300
251
  /** Get path to package.json */
301
252
  export function getPackageJsonPath() {
302
253
  return join(getPackageDir(), "package.json");
@@ -317,47 +268,29 @@ export function getExamplesPath() {
317
268
  export function getChangelogPath() {
318
269
  return resolve(join(getPackageDir(), "CHANGELOG.md"));
319
270
  }
320
- /**
321
- * Get path to built-in interactive assets directory.
322
- * - For Bun binary: assets/ next to executable
323
- * - For Node.js (dist/): dist/modes/interactive/assets/
324
- * - For tsx (src/): src/modes/interactive/assets/
325
- */
326
- export function getInteractiveAssetsDir() {
327
- if (isBunBinary) {
328
- return join(getPackageDir(), "assets");
329
- }
330
- const packageDir = getPackageDir();
331
- const srcOrDist = existsSync(join(packageDir, "src")) ? "src" : "dist";
332
- return join(packageDir, srcOrDist, "modes", "interactive", "assets");
333
- }
334
- /** Get path to a bundled interactive asset */
335
- export function getBundledInteractiveAssetPath(name) {
336
- return join(getInteractiveAssetsDir(), name);
337
- }
338
271
  const pkg = JSON.parse(readFileSync(getPackageJsonPath(), "utf-8"));
339
- const piConfigName = pkg.piConfig?.name;
272
+ const spectralConfigName = pkg.spectralConfig?.name;
340
273
  export const PACKAGE_NAME = pkg.name || "index.ts";
341
- export const APP_NAME = piConfigName || "pi";
342
- export const APP_TITLE = piConfigName ? APP_NAME : "π";
343
- export const CONFIG_DIR_NAME = pkg.piConfig?.configDir || ".pi";
274
+ export const APP_NAME = spectralConfigName || "spectral";
275
+ export const APP_TITLE = spectralConfigName ? APP_NAME : "spectral";
276
+ export const CONFIG_DIR_NAME = pkg.spectralConfig?.configDir || ".spectral";
344
277
  export const VERSION = pkg.version || "0.0.0";
345
- // e.g., PI_CODING_AGENT_DIR or TAU_CODING_AGENT_DIR
278
+ // e.g., SPECTRAL_CODING_AGENT_DIR
346
279
  export const ENV_AGENT_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_DIR`;
347
280
  export const ENV_SESSION_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_SESSION_DIR`;
348
281
  export function expandTildePath(path) {
349
282
  return normalizePath(path);
350
283
  }
351
- const DEFAULT_SHARE_VIEWER_URL = "https://pi.dev/session/";
284
+ const DEFAULT_SHARE_VIEWER_URL = "https://spectral.dev/session/";
352
285
  /** Get the share viewer URL for a gist ID */
353
286
  export function getShareViewerUrl(gistId) {
354
- const baseUrl = process.env.PI_SHARE_VIEWER_URL || DEFAULT_SHARE_VIEWER_URL;
287
+ const baseUrl = process.env.SPECTRAL_SHARE_VIEWER_URL || DEFAULT_SHARE_VIEWER_URL;
355
288
  return `${baseUrl}#${gistId}`;
356
289
  }
357
290
  // =============================================================================
358
- // User Config Paths (~/.pi/agent/*)
291
+ // User Config Paths (~/.spectral/agent/*)
359
292
  // =============================================================================
360
- /** Get the agent config directory (e.g., ~/.pi/agent/) */
293
+ /** Get the agent config directory (e.g., ~/.spectral/agent/) */
361
294
  export function getAgentDir() {
362
295
  const envDir = process.env[ENV_AGENT_DIR];
363
296
  if (envDir) {
@@ -465,6 +465,8 @@ export class AgentSession {
465
465
  */
466
466
  dispose() {
467
467
  this._extensionRunner.invalidate("This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload().");
468
+ this.abortRetry();
469
+ this.agent.abort();
468
470
  this._disconnectFromAgent();
469
471
  this._eventListeners = [];
470
472
  cleanupSessionResources(this.sessionId);