ultimate-pi 0.1.0 → 0.1.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 (509) hide show
  1. package/.agents/skills/ck-search/SKILL.md +99 -0
  2. package/.agents/skills/defuddle/SKILL.md +90 -0
  3. package/.agents/skills/find-skills/SKILL.md +142 -0
  4. package/.agents/skills/firecrawl/SKILL.md +150 -0
  5. package/.agents/skills/firecrawl/rules/install.md +82 -0
  6. package/.agents/skills/firecrawl/rules/security.md +26 -0
  7. package/.agents/skills/firecrawl-agent/SKILL.md +57 -0
  8. package/.agents/skills/firecrawl-build-interact/SKILL.md +67 -0
  9. package/.agents/skills/firecrawl-build-onboarding/SKILL.md +102 -0
  10. package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +39 -0
  11. package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +20 -0
  12. package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +17 -0
  13. package/.agents/skills/firecrawl-build-scrape/SKILL.md +68 -0
  14. package/.agents/skills/firecrawl-build-search/SKILL.md +68 -0
  15. package/.agents/skills/firecrawl-crawl/SKILL.md +58 -0
  16. package/.agents/skills/firecrawl-download/SKILL.md +69 -0
  17. package/.agents/skills/firecrawl-interact/SKILL.md +83 -0
  18. package/.agents/skills/firecrawl-map/SKILL.md +50 -0
  19. package/.agents/skills/firecrawl-parse/SKILL.md +61 -0
  20. package/.agents/skills/firecrawl-scrape/SKILL.md +68 -0
  21. package/.agents/skills/firecrawl-search/SKILL.md +59 -0
  22. package/.agents/skills/obsidian-bases/SKILL.md +299 -0
  23. package/.agents/skills/obsidian-markdown/SKILL.md +237 -0
  24. package/.agents/skills/posthog-analyst/SKILL.md +306 -0
  25. package/.agents/skills/posthog-analyst/evals/evals.json +23 -0
  26. package/.agents/skills/wiki/SKILL.md +215 -0
  27. package/.agents/skills/wiki/references/css-snippets.md +122 -0
  28. package/.agents/skills/wiki/references/frontmatter.md +107 -0
  29. package/.agents/skills/wiki/references/git-setup.md +58 -0
  30. package/.agents/skills/wiki/references/mcp-setup.md +149 -0
  31. package/.agents/skills/wiki/references/modes.md +259 -0
  32. package/.agents/skills/wiki/references/plugins.md +96 -0
  33. package/.agents/skills/wiki/references/rest-api.md +124 -0
  34. package/.agents/skills/wiki-autoresearch/SKILL.md +211 -0
  35. package/.agents/skills/wiki-autoresearch/references/program.md +75 -0
  36. package/.agents/skills/wiki-fold/SKILL.md +204 -0
  37. package/.agents/skills/wiki-fold/references/fold-template.md +133 -0
  38. package/.agents/skills/wiki-ingest/SKILL.md +288 -0
  39. package/.agents/skills/wiki-lint/SKILL.md +183 -0
  40. package/.agents/skills/wiki-query/SKILL.md +176 -0
  41. package/.agents/skills/wiki-save/SKILL.md +128 -0
  42. package/.ckignore +41 -0
  43. package/.env.example +9 -0
  44. package/.github/banner-v2.png +0 -0
  45. package/.github/workflows/lint.yml +33 -0
  46. package/.github/workflows/publish-github-packages.yml +35 -0
  47. package/.github/workflows/publish-npm.yml +32 -0
  48. package/.pi/SYSTEM.md +107 -40
  49. package/.pi/agents/pi-pi/agent-expert.md +205 -0
  50. package/.pi/agents/pi-pi/cli-expert.md +47 -0
  51. package/.pi/agents/pi-pi/config-expert.md +67 -0
  52. package/.pi/agents/pi-pi/ext-expert.md +53 -0
  53. package/.pi/agents/pi-pi/keybinding-expert.md +123 -0
  54. package/.pi/agents/pi-pi/pi-orchestrator.md +103 -0
  55. package/.pi/agents/pi-pi/prompt-expert.md +83 -0
  56. package/.pi/agents/pi-pi/skill-expert.md +52 -0
  57. package/.pi/agents/pi-pi/theme-expert.md +46 -0
  58. package/.pi/agents/pi-pi/tui-expert.md +100 -0
  59. package/.pi/agents/rethink.md +140 -0
  60. package/.pi/agents/wiki-ingest.md +67 -0
  61. package/.pi/agents/wiki-lint.md +75 -0
  62. package/.pi/auto-commit.json +20 -0
  63. package/.pi/extensions/banner.png +0 -0
  64. package/.pi/extensions/ck-enforce.ts +216 -0
  65. package/.pi/extensions/custom-footer.ts +308 -0
  66. package/.pi/extensions/custom-header.ts +116 -0
  67. package/.pi/extensions/dotenv-loader.ts +170 -0
  68. package/.pi/internal/cursor-sdk-transcript-parser.ts +59 -0
  69. package/.pi/model-router.json +95 -0
  70. package/.pi/npm/.gitignore +2 -0
  71. package/.pi/prompts/git-sync.md +124 -0
  72. package/.pi/prompts/harness-setup.md +509 -0
  73. package/.pi/prompts/save.md +16 -0
  74. package/.pi/prompts/wiki-autoresearch.md +19 -0
  75. package/.pi/prompts/wiki.md +23 -0
  76. package/.pi/providers/cursor-sdk-provider.test.mjs +476 -0
  77. package/.pi/providers/cursor-sdk-provider.ts +1085 -0
  78. package/.pi/settings.json +14 -4
  79. package/.pi/skills/agent-router/SKILL.md +174 -0
  80. package/.pi/sounds/alert/1-kaching-track.mp3 +0 -0
  81. package/.pi/sounds/error/1-ksi-wth-track.mp3 +0 -0
  82. package/.pi/sounds/error/2-smash-track.mp3 +0 -0
  83. package/.pi/sounds/error/3-buzzer-track.mp3 +0 -0
  84. package/.pi/sounds/notification/1-soft-notification-track.mp3 +0 -0
  85. package/.pi/sounds/project-sounds.json +25 -0
  86. package/.pi/sounds/reminder/1-soft-notification-track.mp3 +0 -0
  87. package/.pi/sounds/success/1-tada-track.mp3 +0 -0
  88. package/.pi/sounds/success/2-jobs-done-track.mp3 +0 -0
  89. package/.pi/sounds/success/3-yay-track.mp3 +0 -0
  90. package/CONTRIBUTING.md +116 -0
  91. package/README.md +33 -40
  92. package/biome.json +34 -0
  93. package/firecrawl/.env.template +58 -0
  94. package/firecrawl/README.md +49 -0
  95. package/firecrawl/docker-compose.yaml +201 -0
  96. package/firecrawl/searxng/searxng.env +3 -0
  97. package/firecrawl/searxng/settings.yml +85 -0
  98. package/lefthook.yml +8 -0
  99. package/package.json +55 -16
  100. package/vault/AGENTS.md +37 -0
  101. package/vault/wiki/_templates/comparison.md +39 -0
  102. package/vault/wiki/_templates/concept.md +40 -0
  103. package/vault/wiki/_templates/decision.md +21 -0
  104. package/vault/wiki/_templates/entity.md +32 -0
  105. package/vault/wiki/_templates/flow.md +14 -0
  106. package/vault/wiki/_templates/module.md +18 -0
  107. package/vault/wiki/_templates/question.md +31 -0
  108. package/vault/wiki/_templates/source.md +39 -0
  109. package/vault/wiki/concepts/AST-Aware Code Chunking.md +44 -0
  110. package/vault/wiki/concepts/Build-Time Prompt Compilation.md +107 -0
  111. package/vault/wiki/concepts/Context Engine (AI Coding).md +47 -0
  112. package/vault/wiki/concepts/Context-Aware System Reminders.md +61 -0
  113. package/vault/wiki/concepts/Contextualized Text Embedding.md +42 -0
  114. package/vault/wiki/concepts/Contractor vs Employee AI Model.md +55 -0
  115. package/vault/wiki/concepts/Dual-Model Agent Architecture.md +65 -0
  116. package/vault/wiki/concepts/Late Chunking vs Early Chunking.md +43 -0
  117. package/vault/wiki/concepts/Majority Vote Ensembling.md +68 -0
  118. package/vault/wiki/concepts/Meta-Harness.md +16 -0
  119. package/vault/wiki/concepts/Multi-Agent AI Coding Architecture.md +75 -0
  120. package/vault/wiki/concepts/Prompt Enhancement.md +90 -0
  121. package/vault/wiki/concepts/Prompt Renderer.md +89 -0
  122. package/vault/wiki/concepts/Semantic Codebase Indexing.md +67 -0
  123. package/vault/wiki/concepts/additive-config-hierarchy.md +16 -0
  124. package/vault/wiki/concepts/agent-artifacts-verifiable-deliverables.md +71 -0
  125. package/vault/wiki/concepts/agent-browser-browser-automation.md +99 -0
  126. package/vault/wiki/concepts/agent-codebase-interface.md +43 -0
  127. package/vault/wiki/concepts/agent-harness-architecture.md +67 -0
  128. package/vault/wiki/concepts/agent-loop-detection-patterns.md +133 -0
  129. package/vault/wiki/concepts/agent-search-enforcement.md +126 -0
  130. package/vault/wiki/concepts/agent-skills-ecosystem.md +74 -0
  131. package/vault/wiki/concepts/agent-skills-pattern.md +68 -0
  132. package/vault/wiki/concepts/agentic-harness-context-enforcement.md +91 -0
  133. package/vault/wiki/concepts/agentic-harness.md +34 -0
  134. package/vault/wiki/concepts/agentic-orchestration-pipeline.md +56 -0
  135. package/vault/wiki/concepts/agentic-search-no-embeddings.md +18 -0
  136. package/vault/wiki/concepts/anthropic-context-engineering.md +13 -0
  137. package/vault/wiki/concepts/antigravity-agent-first-architecture.md +61 -0
  138. package/vault/wiki/concepts/ast-compression.md +19 -0
  139. package/vault/wiki/concepts/ast-truncation.md +66 -0
  140. package/vault/wiki/concepts/barrel-files.md +37 -0
  141. package/vault/wiki/concepts/browser-harness-agent.md +41 -0
  142. package/vault/wiki/concepts/browser-subagent-visual-verification.md +82 -0
  143. package/vault/wiki/concepts/codebase-intelligence-ecosystem-comparison.md +192 -0
  144. package/vault/wiki/concepts/codebase-intelligence-harness-integration.md +161 -0
  145. package/vault/wiki/concepts/codebase-to-context-ingestion.md +46 -0
  146. package/vault/wiki/concepts/codex-harness-innovations.md +147 -0
  147. package/vault/wiki/concepts/consensus-debate-flow.md +17 -0
  148. package/vault/wiki/concepts/consensus-debate.md +206 -0
  149. package/vault/wiki/concepts/content-addressed-spec-identity.md +166 -0
  150. package/vault/wiki/concepts/context-anxiety.md +57 -0
  151. package/vault/wiki/concepts/context-compression-techniques.md +19 -0
  152. package/vault/wiki/concepts/context-continuity.md +22 -0
  153. package/vault/wiki/concepts/context-drift-in-agents.md +106 -0
  154. package/vault/wiki/concepts/context-engineering.md +62 -0
  155. package/vault/wiki/concepts/context-folding.md +67 -0
  156. package/vault/wiki/concepts/context-mode.md +38 -0
  157. package/vault/wiki/concepts/cursor-harness-innovations.md +107 -0
  158. package/vault/wiki/concepts/deterministic-session-compaction.md +79 -0
  159. package/vault/wiki/concepts/drift-detection-unified.md +296 -0
  160. package/vault/wiki/concepts/execution-feedback-loop.md +46 -0
  161. package/vault/wiki/concepts/feedforward-feedback-harness.md +60 -0
  162. package/vault/wiki/concepts/five-root-cause-metrics-sentrux.md +40 -0
  163. package/vault/wiki/concepts/fork-safe-spec-storage.md +89 -0
  164. package/vault/wiki/concepts/fts5-sandbox.md +19 -0
  165. package/vault/wiki/concepts/fuzzy-edit-matching.md +71 -0
  166. package/vault/wiki/concepts/gemini-cli-architecture.md +104 -0
  167. package/vault/wiki/concepts/generator-evaluator-architecture.md +64 -0
  168. package/vault/wiki/concepts/guardian-agent-pattern.md +67 -0
  169. package/vault/wiki/concepts/harness-configuration-layers.md +89 -0
  170. package/vault/wiki/concepts/harness-control-frameworks.md +155 -0
  171. package/vault/wiki/concepts/harness-engineering-first-principles.md +90 -0
  172. package/vault/wiki/concepts/harness-h-formalism.md +53 -0
  173. package/vault/wiki/concepts/hybrid-code-search.md +61 -0
  174. package/vault/wiki/concepts/inline-post-edit-validation.md +112 -0
  175. package/vault/wiki/concepts/legendary-engineering-patterns-harness.md +110 -0
  176. package/vault/wiki/concepts/lifecycle-hooks.md +94 -0
  177. package/vault/wiki/concepts/mcp-tool-routing.md +102 -0
  178. package/vault/wiki/concepts/memory-system-of-record-vs-ephemeral-cache.md +47 -0
  179. package/vault/wiki/concepts/meta-agent-context-pruning.md +151 -0
  180. package/vault/wiki/concepts/model-adaptive-harness.md +122 -0
  181. package/vault/wiki/concepts/model-routing-agents.md +101 -0
  182. package/vault/wiki/concepts/monorepo-architecture.md +45 -0
  183. package/vault/wiki/concepts/multi-agent-specialization.md +61 -0
  184. package/vault/wiki/concepts/permission-subsystem.md +16 -0
  185. package/vault/wiki/concepts/pi-messenger-analysis.md +243 -0
  186. package/vault/wiki/concepts/pi-vscode-extension-landscape.md +37 -0
  187. package/vault/wiki/concepts/policy-engine-pattern.md +78 -0
  188. package/vault/wiki/concepts/progressive-disclosure-agents.md +53 -0
  189. package/vault/wiki/concepts/progressive-skill-disclosure.md +17 -0
  190. package/vault/wiki/concepts/provider-native-prompting.md +203 -0
  191. package/vault/wiki/concepts/quality-signal-sentrux.md +37 -0
  192. package/vault/wiki/concepts/repo-map-ranking.md +42 -0
  193. package/vault/wiki/concepts/result-monad-error-handling.md +47 -0
  194. package/vault/wiki/concepts/safety-defense-in-depth.md +83 -0
  195. package/vault/wiki/concepts/sandbox-os-enforcement.md +18 -0
  196. package/vault/wiki/concepts/selective-debate-routing.md +70 -0
  197. package/vault/wiki/concepts/self-evolving-harness.md +60 -0
  198. package/vault/wiki/concepts/sentrux-mcp-integration.md +36 -0
  199. package/vault/wiki/concepts/sentrux-rules-engine.md +49 -0
  200. package/vault/wiki/concepts/shell-pattern-compression.md +24 -0
  201. package/vault/wiki/concepts/skill-first-architecture.md +166 -0
  202. package/vault/wiki/concepts/structured-compaction.md +78 -0
  203. package/vault/wiki/concepts/subagent-orchestration.md +17 -0
  204. package/vault/wiki/concepts/subagent-worktree-isolation.md +68 -0
  205. package/vault/wiki/concepts/superpowers-methodology.md +78 -0
  206. package/vault/wiki/concepts/think-in-code.md +73 -0
  207. package/vault/wiki/concepts/ts-execution-layer.md +100 -0
  208. package/vault/wiki/concepts/typescript-strict-mode.md +37 -0
  209. package/vault/wiki/concepts/vcc-conversation-compaction-for-pi.md +51 -0
  210. package/vault/wiki/concepts/verification-drift-detection.md +19 -0
  211. package/vault/wiki/consensus/consensus-records.md +58 -0
  212. package/vault/wiki/decisions/2026-04-30-pi-lean-ctx-native.md +122 -0
  213. package/vault/wiki/decisions/adr-008.md +40 -0
  214. package/vault/wiki/decisions/adr-009.md +46 -0
  215. package/vault/wiki/decisions/adr-010.md +55 -0
  216. package/vault/wiki/decisions/adr-011.md +165 -0
  217. package/vault/wiki/decisions/adr-012.md +102 -0
  218. package/vault/wiki/decisions/adr-013.md +59 -0
  219. package/vault/wiki/decisions/adr-014.md +73 -0
  220. package/vault/wiki/decisions/adr-015.md +81 -0
  221. package/vault/wiki/decisions/adr-016.md +91 -0
  222. package/vault/wiki/decisions/adr-017.md +79 -0
  223. package/vault/wiki/decisions/adr-018.md +100 -0
  224. package/vault/wiki/decisions/adr-019.md +75 -0
  225. package/vault/wiki/decisions/adr-020.md +106 -0
  226. package/vault/wiki/decisions/adr-021.md +86 -0
  227. package/vault/wiki/decisions/adr-022.md +113 -0
  228. package/vault/wiki/decisions/adr-023.md +113 -0
  229. package/vault/wiki/decisions/adr-024.md +73 -0
  230. package/vault/wiki/decisions/adr-025.md +130 -0
  231. package/vault/wiki/decisions/adr-026.md +56 -0
  232. package/vault/wiki/decisions/colocate-wiki.md +34 -0
  233. package/vault/wiki/entities/Anders Hejlsberg.md +29 -0
  234. package/vault/wiki/entities/Anthropic.md +17 -0
  235. package/vault/wiki/entities/Augment Code.md +49 -0
  236. package/vault/wiki/entities/Bjarne Stroustrup.md +26 -0
  237. package/vault/wiki/entities/Bolt.new (StackBlitz).md +39 -0
  238. package/vault/wiki/entities/Boris Cherny.md +11 -0
  239. package/vault/wiki/entities/Claude Code.md +19 -0
  240. package/vault/wiki/entities/Dennis Ritchie.md +26 -0
  241. package/vault/wiki/entities/Emergent Labs.md +32 -0
  242. package/vault/wiki/entities/Google Cloud.md +16 -0
  243. package/vault/wiki/entities/Guido van Rossum.md +28 -0
  244. package/vault/wiki/entities/Ken Thompson.md +28 -0
  245. package/vault/wiki/entities/Lee et al.md +16 -0
  246. package/vault/wiki/entities/Linus Torvalds.md +28 -0
  247. package/vault/wiki/entities/Lovable (company).md +40 -0
  248. package/vault/wiki/entities/Martin Fowler.md +16 -0
  249. package/vault/wiki/entities/Meng et al.md +16 -0
  250. package/vault/wiki/entities/OpenAI.md +16 -0
  251. package/vault/wiki/entities/Rocket.new.md +38 -0
  252. package/vault/wiki/entities/VILA-Lab.md +15 -0
  253. package/vault/wiki/entities/autodev-codebase.md +18 -0
  254. package/vault/wiki/entities/ck-tool.md +59 -0
  255. package/vault/wiki/entities/codesearch.md +18 -0
  256. package/vault/wiki/entities/disler-indydevdan.md +33 -0
  257. package/vault/wiki/entities/gsd-get-shit-done.md +56 -0
  258. package/vault/wiki/entities/javascript-runtimes.md +48 -0
  259. package/vault/wiki/entities/jesse-vincent.md +38 -0
  260. package/vault/wiki/entities/lean-ctx.md +32 -0
  261. package/vault/wiki/entities/opendev.md +41 -0
  262. package/vault/wiki/entities/ops-codegraph-tool.md +18 -0
  263. package/vault/wiki/entities/pi-coding-agent.md +53 -0
  264. package/vault/wiki/entities/sentrux.md +54 -0
  265. package/vault/wiki/entities/vgrep-tool.md +57 -0
  266. package/vault/wiki/entities/vitest.md +41 -0
  267. package/vault/wiki/flows/harness-wiki-pipeline.md +204 -0
  268. package/vault/wiki/hot.md +932 -0
  269. package/vault/wiki/index.md +437 -0
  270. package/vault/wiki/log.md +418 -0
  271. package/vault/wiki/meta/dashboard.md +30 -0
  272. package/vault/wiki/meta/lint-report-2026-04-30.md +86 -0
  273. package/vault/wiki/meta/lint-report-2026-05-02.md +251 -0
  274. package/vault/wiki/meta/overview.canvas +43 -0
  275. package/vault/wiki/modules/adversarial-verification.md +57 -0
  276. package/vault/wiki/modules/automated-observability.md +54 -0
  277. package/vault/wiki/modules/bench.md +20 -0
  278. package/vault/wiki/modules/extensions.md +23 -0
  279. package/vault/wiki/modules/grounding-checkpoints.md +62 -0
  280. package/vault/wiki/modules/harness-implementation-plan.md +345 -0
  281. package/vault/wiki/modules/harness-wiki-skill-mapping.md +135 -0
  282. package/vault/wiki/modules/harness.md +86 -0
  283. package/vault/wiki/modules/persistent-memory.md +85 -0
  284. package/vault/wiki/modules/schema-orchestration.md +68 -0
  285. package/vault/wiki/modules/skills.md +27 -0
  286. package/vault/wiki/modules/spec-hardening.md +58 -0
  287. package/vault/wiki/modules/structured-planning.md +53 -0
  288. package/vault/wiki/modules/think-in-code-enforcement.md +153 -0
  289. package/vault/wiki/modules/wiki-query-interface.md +64 -0
  290. package/vault/wiki/overview.md +51 -0
  291. package/vault/wiki/questions/Research-pi-vs-claude-code-agentic-orchestration-pipeline.md +87 -0
  292. package/vault/wiki/questions/Research-sentrux-dev.md +123 -0
  293. package/vault/wiki/questions/Research-superpowers-skill-for-agentic-coding-agents.md +164 -0
  294. package/vault/wiki/questions/Research: Augment Code Context Engine.md +244 -0
  295. package/vault/wiki/questions/Research: Automating Software Engineering - Lovable, Bolt, Emergent, Rocket.md +112 -0
  296. package/vault/wiki/questions/Research: Claude Code State-of-the-Art Harness Improvements.md +209 -0
  297. package/vault/wiki/questions/Research: Codex State-of-the-Art Harness Improvements.md +99 -0
  298. package/vault/wiki/questions/Research: Engineering Workflows of Legendary Programmers and AI Harness Mapping.md +107 -0
  299. package/vault/wiki/questions/Research: Fallow Codebase Intelligence Harness Integration.md +72 -0
  300. package/vault/wiki/questions/Research: Gemini CLI SOTA Harness Integration.md +166 -0
  301. package/vault/wiki/questions/Research: GitHub Issues as Harness Spec Storage.md +188 -0
  302. package/vault/wiki/questions/Research: Google Antigravity Harness Integration.md +120 -0
  303. package/vault/wiki/questions/Research: Meta-Agent Context Drift Detection.md +236 -0
  304. package/vault/wiki/questions/Research: Model-Adaptive Agent Harness Design.md +95 -0
  305. package/vault/wiki/questions/Research: Model-Specific Prompting Guides.md +165 -0
  306. package/vault/wiki/questions/Research: Prompt Renderer for Multi-Model Agent Harness.md +216 -0
  307. package/vault/wiki/questions/Research: Skill-First Harness Architecture.md +91 -0
  308. package/vault/wiki/questions/Research: TypeScript Best Practices and Codebase Structure.md +88 -0
  309. package/vault/wiki/questions/Research: TypeScript Execution Layer for Agent Tool Calling.md +81 -0
  310. package/vault/wiki/questions/Research: claude-mem over Obsidian for Harness Layer.md +71 -0
  311. package/vault/wiki/questions/Research: claude-mem over obsidian wiki as the knowledge base for our agentic harness pipeline. think from first principles. does this replace or complement our current setup? no hard feelings about previous decisions. gimme accurate points.md +80 -0
  312. package/vault/wiki/questions/Research: context-mode vs lean-ctx.md +72 -0
  313. package/vault/wiki/questions/Research: cursor.sh Harness Innovations.md +92 -0
  314. package/vault/wiki/questions/Research: executor.sh Harness Integration.md +170 -0
  315. package/vault/wiki/questions/Research: how GSD fits into our coding harness setup.md +97 -0
  316. package/vault/wiki/questions/Research: how claude-mem fits into our workflow. and whether it should replace obsidian in the codebase. no hard feelings about previous actions, rethink from first principles always.md +80 -0
  317. package/vault/wiki/questions/Research: pi-vcc.md +113 -0
  318. package/vault/wiki/questions/Research: semantic code search tools.md +69 -0
  319. package/vault/wiki/questions/Research: vcc extension for pi coding agent.md +73 -0
  320. package/vault/wiki/questions/how-to-enable-semantic-code-search-now.md +111 -0
  321. package/vault/wiki/questions/mvp-implementation-blueprint.md +552 -0
  322. package/vault/wiki/questions/research-agent-first-codebase-exploration.md +199 -0
  323. package/vault/wiki/questions/research-agentic-coding-harness-latest-papers.md +142 -0
  324. package/vault/wiki/questions/research-gitingest-gitreverse-integration.md +100 -0
  325. package/vault/wiki/questions/research-wozcode-token-reduction.md +67 -0
  326. package/vault/wiki/questions/resolved-context-pruning-inplace-vs-restart.md +95 -0
  327. package/vault/wiki/questions/resolved-context-window-economics.md +167 -0
  328. package/vault/wiki/questions/resolved-imad-debate-gating-transfer.md +126 -0
  329. package/vault/wiki/questions/resolved-mcp-tool-preference.md +112 -0
  330. package/vault/wiki/questions/resolved-small-model-meta-agents.md +107 -0
  331. package/vault/wiki/questions/resolved-treesitter-dynamic-languages.md +95 -0
  332. package/vault/wiki/sources/Auggie Context MCP Server.md +63 -0
  333. package/vault/wiki/sources/Augment Code Codacy AI Giants.md +61 -0
  334. package/vault/wiki/sources/Augment Code MCP SiliconAngle.md +49 -0
  335. package/vault/wiki/sources/Augment Code WorkOS ERC 2025.md +55 -0
  336. package/vault/wiki/sources/Augment Context Engine Official.md +71 -0
  337. package/vault/wiki/sources/Augment SWE-bench Agent GitHub.md +74 -0
  338. package/vault/wiki/sources/Augment SWE-bench Pro Blog.md +58 -0
  339. package/vault/wiki/sources/Source: AgentBus Jinja2 Prompt Pipelines.md +75 -0
  340. package/vault/wiki/sources/Source: Arxiv /342/200/224 Don't Break the Cache.md" +85 -0
  341. package/vault/wiki/sources/Source: Augment - Harness Engineering for AI Coding Agents.md +58 -0
  342. package/vault/wiki/sources/Source: Blake Crosley Agent Architecture Guide.md +100 -0
  343. package/vault/wiki/sources/Source: Bolt.new Architecture & Case Study.md +75 -0
  344. package/vault/wiki/sources/Source: Build-Time Prompt Compilation Architecture.md +107 -0
  345. package/vault/wiki/sources/Source: Claude API Agent Skills Overview.md +70 -0
  346. package/vault/wiki/sources/Source: Gemini CLI Changelogs.md +88 -0
  347. package/vault/wiki/sources/Source: Google Blog - Gemini CLI Announcement.md +57 -0
  348. package/vault/wiki/sources/Source: Google Gemini CLI Architecture Docs.md +53 -0
  349. package/vault/wiki/sources/Source: LangChain - Anatomy of Agent Harness.md +65 -0
  350. package/vault/wiki/sources/Source: Lovable Architecture & Clone Analysis.md +83 -0
  351. package/vault/wiki/sources/Source: Martin Fowler - Harness Engineering.md +70 -0
  352. package/vault/wiki/sources/Source: OpenAI Harness Engineering Five Principles.md +58 -0
  353. package/vault/wiki/sources/Source: OpenAI Harness Engineering /342/200/224 0 Lines of Human Code.md" +101 -0
  354. package/vault/wiki/sources/Source: OpenDev /342/200/224 Building AI Coding Agents for the Terminal.md" +100 -0
  355. package/vault/wiki/sources/Source: Render AI Coding Agents Benchmark 2025.md +53 -0
  356. package/vault/wiki/sources/Source: Rocket.new /342/200/224 Vibe Solutioning Platform.md" +70 -0
  357. package/vault/wiki/sources/Source: SwirlAI Agent Skills Progressive Disclosure.md +71 -0
  358. package/vault/wiki/sources/Source: TianPan Prompt Caching Architecture.md +89 -0
  359. package/vault/wiki/sources/Source: Vercel Labs agent-browser.md +155 -0
  360. package/vault/wiki/sources/Source: browser-harness CDP Harness.md +126 -0
  361. package/vault/wiki/sources/agent-drift-academic-paper.md +79 -0
  362. package/vault/wiki/sources/aider-repomap-tree-sitter.md +42 -0
  363. package/vault/wiki/sources/anthropic-compaction-api.md +58 -0
  364. package/vault/wiki/sources/anthropic-effective-harnesses.md +42 -0
  365. package/vault/wiki/sources/anthropic-prompt-best-practices.md +100 -0
  366. package/vault/wiki/sources/anthropic2026-harness-design.md +63 -0
  367. package/vault/wiki/sources/barrel-files-tkdodo.md +38 -0
  368. package/vault/wiki/sources/birth-of-unix-kernighan-interview.md +57 -0
  369. package/vault/wiki/sources/bockeler2026-harness-engineering.md +69 -0
  370. package/vault/wiki/sources/cast-code-chunking-paper.md +50 -0
  371. package/vault/wiki/sources/ck-semantic-search.md +78 -0
  372. package/vault/wiki/sources/claude-code-architecture-karaxai-2026.md +71 -0
  373. package/vault/wiki/sources/claude-code-architecture-qubytes-2026.md +50 -0
  374. package/vault/wiki/sources/claude-code-architecture-vila-lab-2026.md +64 -0
  375. package/vault/wiki/sources/claude-code-security-architecture-penligent-2026.md +70 -0
  376. package/vault/wiki/sources/claude-context-editing-docs.md +13 -0
  377. package/vault/wiki/sources/cloudflare-codemode.md +63 -0
  378. package/vault/wiki/sources/code-chunk-library-supermemory.md +63 -0
  379. package/vault/wiki/sources/codeact-apple-2024.md +62 -0
  380. package/vault/wiki/sources/codex-dsc-rfc-8573.md +41 -0
  381. package/vault/wiki/sources/codex-open-source-agent-2026.md +110 -0
  382. package/vault/wiki/sources/coir-code-retrieval-benchmark.md +51 -0
  383. package/vault/wiki/sources/colinmcnamara-context-optimization-codemode.md +48 -0
  384. package/vault/wiki/sources/context-folding-paper.md +61 -0
  385. package/vault/wiki/sources/context-mode-website.md +63 -0
  386. package/vault/wiki/sources/cursor-agent-best-practices-2026.md +62 -0
  387. package/vault/wiki/sources/cursor-fork-29b-2025.md +50 -0
  388. package/vault/wiki/sources/cursor-harness-april-2026.md +76 -0
  389. package/vault/wiki/sources/cursor-instant-apply-2024.md +45 -0
  390. package/vault/wiki/sources/cursor-shadow-workspace-2024.md +52 -0
  391. package/vault/wiki/sources/cursor-shipped-coding-agent-2026.md +53 -0
  392. package/vault/wiki/sources/cursor-vs-antigravity-2026.md +51 -0
  393. package/vault/wiki/sources/disler-pi-vs-claude-code.md +69 -0
  394. package/vault/wiki/sources/distill-deterministic-context-compression.md +53 -0
  395. package/vault/wiki/sources/embedding-models-benchmark-supermemory-2025.md +48 -0
  396. package/vault/wiki/sources/executor-rhyssullivan.md +122 -0
  397. package/vault/wiki/sources/fallow-rs-codebase-intelligence.md +125 -0
  398. package/vault/wiki/sources/fan2025-imad.md +60 -0
  399. package/vault/wiki/sources/forgecode-gpt5-agent-improvements.md +63 -0
  400. package/vault/wiki/sources/gemini-3-prompting-guide.md +78 -0
  401. package/vault/wiki/sources/gh-cli-sub-issue-rfc.md +50 -0
  402. package/vault/wiki/sources/gh-sub-issue-extension.md +72 -0
  403. package/vault/wiki/sources/github-fork-issues-discussion.md +44 -0
  404. package/vault/wiki/sources/github-issue-dependencies-docs.md +49 -0
  405. package/vault/wiki/sources/github-sub-issues-docs.md +51 -0
  406. package/vault/wiki/sources/gitingest.md +91 -0
  407. package/vault/wiki/sources/gitreverse.md +63 -0
  408. package/vault/wiki/sources/google-antigravity-official-blog.md +47 -0
  409. package/vault/wiki/sources/google-antigravity-wikipedia.md +53 -0
  410. package/vault/wiki/sources/gsd-codecentric-deep-dive.md +57 -0
  411. package/vault/wiki/sources/gsd-github-repo.md +51 -0
  412. package/vault/wiki/sources/gsd-hn-discussion.md +59 -0
  413. package/vault/wiki/sources/guido-python-design-philosophy.md +56 -0
  414. package/vault/wiki/sources/hejlsberg-7-learnings.md +48 -0
  415. package/vault/wiki/sources/ironclaw-drift-monitor.md +80 -0
  416. package/vault/wiki/sources/langsight-loop-detection.md +80 -0
  417. package/vault/wiki/sources/leanctx-website.md +69 -0
  418. package/vault/wiki/sources/lee2026-meta-harness.md +59 -0
  419. package/vault/wiki/sources/linux-kernel-coding-workflow.md +50 -0
  420. package/vault/wiki/sources/lou2026-autoharness.md +53 -0
  421. package/vault/wiki/sources/martin-fowler-harness-engineering.md +73 -0
  422. package/vault/wiki/sources/mcp-architecture-docs.md +13 -0
  423. package/vault/wiki/sources/meng2026-agent-harness-survey.md +79 -0
  424. package/vault/wiki/sources/mindstudio-four-agent-types.md +68 -0
  425. package/vault/wiki/sources/ms-chat-history-management.md +13 -0
  426. package/vault/wiki/sources/openai-prompt-guidance.md +104 -0
  427. package/vault/wiki/sources/openclaw-session-pruning.md +13 -0
  428. package/vault/wiki/sources/opencode-dcp.md +13 -0
  429. package/vault/wiki/sources/opendev-arxiv-2603.05344v1.md +79 -0
  430. package/vault/wiki/sources/openhands-platform.md +39 -0
  431. package/vault/wiki/sources/oss-guide-codebase-exploration.md +53 -0
  432. package/vault/wiki/sources/pi-compaction-extensions-ecosystem.md +102 -0
  433. package/vault/wiki/sources/pi-context-prune-github-repo.md +38 -0
  434. package/vault/wiki/sources/pi-mono-compaction-docs.md +38 -0
  435. package/vault/wiki/sources/pi-omni-compact-github-repo.md +50 -0
  436. package/vault/wiki/sources/pi-rtk-optimizer-github-repo.md +45 -0
  437. package/vault/wiki/sources/pi-vcc-github-repo.md +69 -0
  438. package/vault/wiki/sources/pi-vscode-marketplace.md +41 -0
  439. package/vault/wiki/sources/pi-vscode-model-provider-marketplace.md +39 -0
  440. package/vault/wiki/sources/py-tree-sitter.md +13 -0
  441. package/vault/wiki/sources/sentrux-dev-landing.md +40 -0
  442. package/vault/wiki/sources/sentrux-docs-pro-architecture.md +75 -0
  443. package/vault/wiki/sources/sentrux-docs-quality-signal.md +46 -0
  444. package/vault/wiki/sources/sentrux-docs-root-cause-metrics.md +57 -0
  445. package/vault/wiki/sources/sentrux-docs-rules-engine.md +58 -0
  446. package/vault/wiki/sources/sentrux-github-repo.md +56 -0
  447. package/vault/wiki/sources/superpowers-github-repo.md +56 -0
  448. package/vault/wiki/sources/superpowers-release-blog.md +54 -0
  449. package/vault/wiki/sources/superpowers-termdock-analysis.md +45 -0
  450. package/vault/wiki/sources/swe-agent-aci.md +42 -0
  451. package/vault/wiki/sources/swe-bench.md +45 -0
  452. package/vault/wiki/sources/swe-pruner-context-pruning.md +13 -0
  453. package/vault/wiki/sources/think-in-code-blog.md +48 -0
  454. package/vault/wiki/sources/tree-sitter-docs.md +13 -0
  455. package/vault/wiki/sources/ts-best-practices-2025-devto.md +42 -0
  456. package/vault/wiki/sources/ts-folder-structure-mingyang.md +58 -0
  457. package/vault/wiki/sources/ts-monorepo-koerselman.md +44 -0
  458. package/vault/wiki/sources/ts-result-error-handling-kkalamarski.md +52 -0
  459. package/vault/wiki/sources/ts-runtimes-comparison-betterstack.md +42 -0
  460. package/vault/wiki/sources/ts-strict-mode-rishikc.md +43 -0
  461. package/vault/wiki/sources/unix-philosophy.md +48 -0
  462. package/vault/wiki/sources/vectara-chunking-vs-embedding-naacl2025.md +39 -0
  463. package/vault/wiki/sources/vectara-guardian-agents.md +79 -0
  464. package/vault/wiki/sources/vgrep-semantic-search.md +76 -0
  465. package/vault/wiki/sources/vitest-official.md +41 -0
  466. package/vault/wiki/sources/vscode-pi-community-extension.md +40 -0
  467. package/vault/wiki/sources/wozcode.md +79 -0
  468. package/.agents/skills/compress/SKILL.md +0 -111
  469. package/.agents/skills/compress/scripts/__init__.py +0 -9
  470. package/.agents/skills/compress/scripts/__main__.py +0 -3
  471. package/.agents/skills/compress/scripts/benchmark.py +0 -78
  472. package/.agents/skills/compress/scripts/cli.py +0 -73
  473. package/.agents/skills/compress/scripts/compress.py +0 -227
  474. package/.agents/skills/compress/scripts/detect.py +0 -121
  475. package/.agents/skills/compress/scripts/validate.py +0 -189
  476. package/.agents/skills/emil-design-eng/SKILL.md +0 -679
  477. package/.agents/skills/lean-ctx/SKILL.md +0 -149
  478. package/.agents/skills/lean-ctx/scripts/install.sh +0 -95
  479. package/.agents/skills/scrapling-official/LICENSE.txt +0 -28
  480. package/.agents/skills/scrapling-official/SKILL.md +0 -390
  481. package/.agents/skills/scrapling-official/examples/01_fetcher_session.py +0 -26
  482. package/.agents/skills/scrapling-official/examples/02_dynamic_session.py +0 -26
  483. package/.agents/skills/scrapling-official/examples/03_stealthy_session.py +0 -26
  484. package/.agents/skills/scrapling-official/examples/04_spider.py +0 -58
  485. package/.agents/skills/scrapling-official/examples/README.md +0 -45
  486. package/.agents/skills/scrapling-official/references/fetching/choosing.md +0 -78
  487. package/.agents/skills/scrapling-official/references/fetching/dynamic.md +0 -352
  488. package/.agents/skills/scrapling-official/references/fetching/static.md +0 -432
  489. package/.agents/skills/scrapling-official/references/fetching/stealthy.md +0 -255
  490. package/.agents/skills/scrapling-official/references/mcp-server.md +0 -214
  491. package/.agents/skills/scrapling-official/references/migrating_from_beautifulsoup.md +0 -86
  492. package/.agents/skills/scrapling-official/references/parsing/adaptive.md +0 -212
  493. package/.agents/skills/scrapling-official/references/parsing/main_classes.md +0 -586
  494. package/.agents/skills/scrapling-official/references/parsing/selection.md +0 -494
  495. package/.agents/skills/scrapling-official/references/spiders/advanced.md +0 -344
  496. package/.agents/skills/scrapling-official/references/spiders/architecture.md +0 -94
  497. package/.agents/skills/scrapling-official/references/spiders/getting-started.md +0 -164
  498. package/.agents/skills/scrapling-official/references/spiders/proxy-blocking.md +0 -235
  499. package/.agents/skills/scrapling-official/references/spiders/requests-responses.md +0 -196
  500. package/.agents/skills/scrapling-official/references/spiders/sessions.md +0 -205
  501. package/.github/banner.png +0 -0
  502. package/PLAN.md +0 -11
  503. package/extensions/lean-ctx-enforce.ts +0 -166
  504. package/skills-lock.json +0 -35
  505. package/wiki/README.md +0 -10
  506. package/wiki/decisions/0001-establish-project-wiki-and-decision-record-format.md +0 -25
  507. package/wiki/decisions/0002-add-project-banner-to-readme.md +0 -26
  508. package/wiki/decisions/0003-remove-redundant-readme-title-heading.md +0 -26
  509. package/wiki/decisions/0004-publish-package-to-npm-as-ultimate-pi.md +0 -26
@@ -0,0 +1,1085 @@
1
+ import { Agent, Cursor, type ModelSelection } from "@cursor/sdk";
2
+ import {
3
+ calculateCost,
4
+ createAssistantMessageEventStream,
5
+ type Api,
6
+ type AssistantMessage,
7
+ type AssistantMessageEventStream,
8
+ type Context,
9
+ type ImageContent,
10
+ type Model,
11
+ type SimpleStreamOptions,
12
+ type TextContent,
13
+ type Tool,
14
+ } from "@mariozechner/pi-ai";
15
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
16
+ import { parseCursorTranscriptToolCalls } from "../internal/cursor-sdk-transcript-parser";
17
+
18
+ type ReasoningLevel = "minimal" | "low" | "medium" | "high" | "xhigh";
19
+ type CursorAgentFactory = typeof Agent.create;
20
+ const DEFAULT_CURSOR_AGENT_FACTORY: CursorAgentFactory = Agent.create.bind(Agent);
21
+ let createCursorAgent: CursorAgentFactory = DEFAULT_CURSOR_AGENT_FACTORY;
22
+ const MODEL_LIST_CACHE_TTL_MS = 5 * 60 * 1000;
23
+ let cachedAvailableModelIds: Set<string> | undefined;
24
+ let availableModelIdsFetchedAt = 0;
25
+ let availableModelIdsInFlight: Promise<Set<string>> | undefined;
26
+
27
+ export function __setCursorAgentFactoryForTests(factory?: CursorAgentFactory): void {
28
+ createCursorAgent = factory ?? DEFAULT_CURSOR_AGENT_FACTORY;
29
+ }
30
+
31
+ type ModelVariants = {
32
+ default: string;
33
+ minimal?: string;
34
+ low?: string;
35
+ medium?: string;
36
+ high?: string;
37
+ xhigh?: string;
38
+ };
39
+
40
+ const LEGACY_CURSOR_MODEL_ALIASES: Record<string, { id: string; reasoning?: ReasoningLevel }> = {
41
+ "claude-opus-4-7-xhigh": { id: "claude-opus-4-6", reasoning: "xhigh" },
42
+ "claude-opus-4-7-max": { id: "claude-opus-4-6", reasoning: "xhigh" },
43
+ "claude-opus-4-7-high": { id: "claude-opus-4-6", reasoning: "high" },
44
+ "claude-opus-4-7-medium": { id: "claude-opus-4-6", reasoning: "medium" },
45
+ "claude-opus-4-7-low": { id: "claude-opus-4-6", reasoning: "low" },
46
+ "gpt-5.3-codex-fast": { id: "gpt-5.3-codex", reasoning: "low" },
47
+ "gpt-5.5-high": { id: "gpt-5.2", reasoning: "high" },
48
+ "gpt-5.5-extra-high": { id: "gpt-5.2", reasoning: "xhigh" },
49
+ "claude-4.5-sonnet": { id: "claude-sonnet-4-5" },
50
+ "claude-4.5-sonnet-thinking": { id: "claude-sonnet-4-5", reasoning: "high" },
51
+ "claude-4.5-opus-high": { id: "claude-opus-4-5", reasoning: "high" },
52
+ };
53
+
54
+ const MODEL_MAP: Record<string, ModelVariants> = {
55
+ "claude-sonnet-4-5": {
56
+ default: "sonnet-4.5",
57
+ minimal: "sonnet-4.5-thinking",
58
+ low: "sonnet-4.5-thinking",
59
+ medium: "sonnet-4.5-thinking",
60
+ high: "sonnet-4.5-thinking",
61
+ xhigh: "sonnet-4.5-thinking",
62
+ },
63
+ "claude-sonnet-4-6": {
64
+ default: "sonnet-4.6",
65
+ minimal: "sonnet-4.6-thinking",
66
+ low: "sonnet-4.6-thinking",
67
+ medium: "sonnet-4.6-thinking",
68
+ high: "sonnet-4.6-thinking",
69
+ xhigh: "sonnet-4.6-thinking",
70
+ },
71
+ "claude-opus-4-5": {
72
+ default: "opus-4.5",
73
+ minimal: "opus-4.5-thinking",
74
+ low: "opus-4.5-thinking",
75
+ medium: "opus-4.5-thinking",
76
+ high: "opus-4.5-thinking",
77
+ xhigh: "opus-4.5-thinking",
78
+ },
79
+ "claude-opus-4-6": {
80
+ default: "opus-4.6",
81
+ minimal: "opus-4.6-thinking",
82
+ low: "opus-4.6-thinking",
83
+ medium: "opus-4.6-thinking",
84
+ high: "opus-4.6-thinking",
85
+ xhigh: "opus-4.6-thinking",
86
+ },
87
+ "gpt-5.2": {
88
+ default: "gpt-5.2",
89
+ high: "gpt-5.2-high",
90
+ xhigh: "gpt-5.2-high",
91
+ },
92
+ "gpt-5.2-codex": {
93
+ default: "gpt-5.2-codex",
94
+ minimal: "gpt-5.2-codex-low",
95
+ low: "gpt-5.2-codex-low",
96
+ high: "gpt-5.2-codex-high",
97
+ xhigh: "gpt-5.2-codex-xhigh",
98
+ },
99
+ "gpt-5.3-codex": {
100
+ default: "gpt-5.3-codex",
101
+ minimal: "gpt-5.3-codex-low",
102
+ low: "gpt-5.3-codex-low",
103
+ high: "gpt-5.3-codex-high",
104
+ xhigh: "gpt-5.3-codex-xhigh",
105
+ },
106
+ "gpt-5.1": { default: "gpt-5.1-high" },
107
+ "gpt-5.1-codex-max": {
108
+ default: "gpt-5.1-codex-max",
109
+ high: "gpt-5.1-codex-max-high",
110
+ xhigh: "gpt-5.1-codex-max-high",
111
+ },
112
+ "gemini-3-pro-preview": { default: "gemini-3-pro" },
113
+ "gemini-3-flash-preview": { default: "gemini-3-flash" },
114
+ "grok-code-fast-1": { default: "grok" },
115
+ };
116
+
117
+ const MODEL_ID_ALIASES: Record<string, string[]> = {
118
+ "auto": ["auto", "default", "composer-2"],
119
+ "claude-sonnet-4-5": ["claude-sonnet-4-5", "sonnet-4.5", "claude-sonnet-4"],
120
+ "claude-sonnet-4-6": ["claude-sonnet-4-6", "sonnet-4.6"],
121
+ "claude-opus-4-5": ["claude-opus-4-5", "opus-4.5"],
122
+ "claude-opus-4-6": ["claude-opus-4-6", "opus-4.6"],
123
+ "gpt-5.1": ["gpt-5.1-high", "gpt-5.1"],
124
+ "gpt-5.1-codex-max": ["gpt-5.1-codex-max", "gpt-5.1-codex-mini"],
125
+ "gpt-5.2": ["gpt-5.2-high", "gpt-5.2"],
126
+ "gpt-5.2-codex": ["gpt-5.2-codex"],
127
+ "gpt-5.3-codex": ["gpt-5.3-codex", "gpt-5.3-codex-spark"],
128
+ "gpt-5.3-codex-fast": ["gpt-5.3-codex-fast", "gpt-5.3-codex", "gpt-5.3-codex-low"],
129
+ "gpt-5.5-high": ["gpt-5.5-high", "gpt-5.2-high", "gpt-5.2"],
130
+ "gpt-5.5-extra-high": ["gpt-5.5-extra-high", "gpt-5.2-high", "gpt-5.2"],
131
+ "claude-opus-4-7-low": ["claude-opus-4-7-low", "opus-4.6-thinking", "claude-opus-4-6"],
132
+ "claude-opus-4-7-medium": ["claude-opus-4-7-medium", "opus-4.6-thinking", "claude-opus-4-6"],
133
+ "claude-opus-4-7-high": ["claude-opus-4-7-high", "opus-4.6-thinking", "claude-opus-4-6"],
134
+ "claude-opus-4-7-max": ["claude-opus-4-7-max", "opus-4.6-thinking", "claude-opus-4-6"],
135
+ "claude-opus-4-7-xhigh": ["claude-opus-4-7-xhigh", "opus-4.6-thinking", "claude-opus-4-6"],
136
+ "claude-4.5-sonnet": ["claude-4.5-sonnet", "sonnet-4.5", "claude-sonnet-4-5"],
137
+ "claude-4.5-sonnet-thinking": ["claude-4.5-sonnet-thinking", "sonnet-4.5-thinking", "claude-sonnet-4-5"],
138
+ "claude-4.5-opus-high": ["claude-4.5-opus-high", "opus-4.5-thinking", "claude-opus-4-5"],
139
+ "gemini-3-pro-preview": ["gemini-3-pro-preview", "gemini-3-pro", "gemini-3.1-pro"],
140
+ "gemini-3-flash-preview": ["gemini-3-flash-preview", "gemini-3-flash", "gemini-2.5-flash"],
141
+ "grok-code-fast-1": ["grok-code-fast-1", "grok", "grok-4.3", "grok-4-20"],
142
+ "composer-2": ["composer-2", "default", "composer-1.5"],
143
+ };
144
+
145
+ const PROVIDER_MODELS = [
146
+ { id: "auto", name: "Auto (Cursor)", reasoning: false, contextWindow: 200000, maxTokens: 32768 },
147
+ { id: "claude-sonnet-4-5", name: "Claude 4.5 Sonnet (Cursor)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
148
+ { id: "claude-sonnet-4-6", name: "Claude 4.6 Sonnet (Cursor)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
149
+ { id: "claude-opus-4-5", name: "Claude 4.5 Opus (Cursor)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
150
+ { id: "claude-opus-4-6", name: "Claude 4.6 Opus (Cursor)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
151
+ { id: "gpt-5.1", name: "GPT-5.1 (Cursor)", reasoning: false, contextWindow: 200000, maxTokens: 32768 },
152
+ { id: "gpt-5.1-codex-max", name: "GPT-5.1 Codex Max (Cursor)", reasoning: true, contextWindow: 200000, maxTokens: 32768 },
153
+ { id: "gpt-5.2", name: "GPT-5.2 (Cursor)", reasoning: true, contextWindow: 200000, maxTokens: 32768 },
154
+ { id: "gpt-5.2-codex", name: "GPT-5.2 Codex (Cursor)", reasoning: true, contextWindow: 200000, maxTokens: 32768 },
155
+ { id: "gpt-5.3-codex", name: "GPT-5.3 Codex (Cursor)", reasoning: true, contextWindow: 200000, maxTokens: 32768 },
156
+ { id: "gpt-5.3-codex-fast", name: "GPT-5.3 Codex Fast (Cursor, alias)", reasoning: true, contextWindow: 200000, maxTokens: 32768 },
157
+ { id: "gpt-5.5-high", name: "GPT-5.5 High (Cursor, alias)", reasoning: true, contextWindow: 200000, maxTokens: 32768 },
158
+ { id: "gpt-5.5-extra-high", name: "GPT-5.5 Extra High (Cursor, alias)", reasoning: true, contextWindow: 200000, maxTokens: 32768 },
159
+ { id: "claude-opus-4-7-low", name: "Claude Opus 4.7 Low (Cursor, alias)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
160
+ { id: "claude-opus-4-7-medium", name: "Claude Opus 4.7 Medium (Cursor, alias)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
161
+ { id: "claude-opus-4-7-high", name: "Claude Opus 4.7 High (Cursor, alias)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
162
+ { id: "claude-opus-4-7-max", name: "Claude Opus 4.7 Max (Cursor, alias)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
163
+ { id: "claude-opus-4-7-xhigh", name: "Claude Opus 4.7 XHigh (Cursor, alias)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
164
+ { id: "claude-4.5-sonnet", name: "Claude 4.5 Sonnet (Cursor, alias)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
165
+ { id: "claude-4.5-sonnet-thinking", name: "Claude 4.5 Sonnet Thinking (Cursor, alias)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
166
+ { id: "claude-4.5-opus-high", name: "Claude 4.5 Opus High (Cursor, alias)", reasoning: true, contextWindow: 200000, maxTokens: 32000 },
167
+ { id: "gemini-3-pro-preview", name: "Gemini 3 Pro (Cursor)", reasoning: false, contextWindow: 1000000, maxTokens: 65536 },
168
+ { id: "gemini-3-flash-preview", name: "Gemini 3 Flash (Cursor)", reasoning: false, contextWindow: 1000000, maxTokens: 65536 },
169
+ { id: "grok-code-fast-1", name: "Grok (Cursor)", reasoning: false, contextWindow: 131072, maxTokens: 32768 },
170
+ { id: "composer-2", name: "Composer 2 (Cursor)", reasoning: false, contextWindow: 200000, maxTokens: 32768 },
171
+ ];
172
+
173
+ type ProviderModelEntry = (typeof PROVIDER_MODELS)[number];
174
+
175
+ function getAliasCandidates(id: string): string[] {
176
+ return MODEL_ID_ALIASES[id] ?? [id];
177
+ }
178
+
179
+ function supportsModelId(id: string, available: Set<string>): boolean {
180
+ return getAliasCandidates(id).some((candidate) => available.has(candidate));
181
+ }
182
+
183
+ function resolveSupportedModelId(preferred: string, canonicalId: string, available: Set<string>): string {
184
+ if (available.has(preferred)) {
185
+ return preferred;
186
+ }
187
+
188
+ const candidates = [
189
+ ...getAliasCandidates(preferred),
190
+ ...getAliasCandidates(canonicalId),
191
+ ...getAliasCandidates("auto"),
192
+ ];
193
+ for (const candidate of candidates) {
194
+ if (available.has(candidate)) {
195
+ return candidate;
196
+ }
197
+ }
198
+ return preferred;
199
+ }
200
+
201
+ async function getAvailableCursorModelIds(forceRefresh = false): Promise<Set<string>> {
202
+ const now = Date.now();
203
+ if (!forceRefresh && cachedAvailableModelIds && now - availableModelIdsFetchedAt < MODEL_LIST_CACHE_TTL_MS) {
204
+ return cachedAvailableModelIds;
205
+ }
206
+ if (availableModelIdsInFlight) {
207
+ return availableModelIdsInFlight;
208
+ }
209
+
210
+ availableModelIdsInFlight = (async () => {
211
+ const apiKey = process.env.CURSOR_API_KEY;
212
+ if (!apiKey) {
213
+ return cachedAvailableModelIds ?? new Set<string>();
214
+ }
215
+ try {
216
+ const models = await Cursor.models.list({ apiKey });
217
+ const ids = new Set<string>(models.map((model) => model.id));
218
+ cachedAvailableModelIds = ids;
219
+ availableModelIdsFetchedAt = Date.now();
220
+ return ids;
221
+ } catch {
222
+ return cachedAvailableModelIds ?? new Set<string>();
223
+ }
224
+ })();
225
+
226
+ try {
227
+ return await availableModelIdsInFlight;
228
+ } finally {
229
+ availableModelIdsInFlight = undefined;
230
+ }
231
+ }
232
+
233
+ async function getDynamicProviderModels(forceRefresh = false): Promise<ProviderModelEntry[]> {
234
+ const available = await getAvailableCursorModelIds(forceRefresh);
235
+ if (available.size === 0) {
236
+ return PROVIDER_MODELS;
237
+ }
238
+ return PROVIDER_MODELS.filter((model) => supportsModelId(model.id, available));
239
+ }
240
+
241
+ function toProviderModelConfig(model: ProviderModelEntry) {
242
+ return {
243
+ id: model.id,
244
+ name: model.name,
245
+ reasoning: model.reasoning,
246
+ input: ["text"] as ("text" | "image")[],
247
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
248
+ contextWindow: model.contextWindow,
249
+ maxTokens: model.maxTokens,
250
+ };
251
+ }
252
+
253
+ function registerCursorProviderModels(pi: ExtensionAPI, models: ProviderModelEntry[]): void {
254
+ pi.registerProvider("cursor", {
255
+ baseUrl: "cursor://sdk",
256
+ apiKey: "CURSOR_API_KEY",
257
+ api: "cursor-sdk" as Api,
258
+ models: models.map(toProviderModelConfig),
259
+ streamSimple: streamCursorSdk,
260
+ });
261
+ }
262
+
263
+ async function toCursorModelId(canonicalId: string, reasoning?: string): Promise<string> {
264
+ const normalizedId = canonicalId.startsWith("cursor/") ? canonicalId.slice("cursor/".length) : canonicalId;
265
+ const legacyAlias = LEGACY_CURSOR_MODEL_ALIASES[normalizedId];
266
+ const resolvedCanonicalId = legacyAlias?.id ?? normalizedId;
267
+ const resolvedReasoning = legacyAlias?.reasoning ?? (reasoning as ReasoningLevel | undefined);
268
+ const family = MODEL_MAP[resolvedCanonicalId];
269
+ const preferredModelId = family
270
+ ? (() => {
271
+ const level = resolvedReasoning;
272
+ const variant = level ? family[level] : undefined;
273
+ return variant ?? family.default;
274
+ })()
275
+ : resolvedCanonicalId;
276
+
277
+ const available = await getAvailableCursorModelIds();
278
+ if (available.size === 0) {
279
+ return preferredModelId;
280
+ }
281
+ return resolveSupportedModelId(preferredModelId, resolvedCanonicalId, available);
282
+ }
283
+
284
+ function contentBlockToText(block: TextContent | ImageContent): string {
285
+ if (block.type === "text") {
286
+ return block.text;
287
+ }
288
+ const bytes = Math.round((block.data.length * 3) / 4);
289
+ return `[Image: ${block.mimeType}, ~${bytes} bytes]`;
290
+ }
291
+
292
+ function formatTools(tools: Tool[] | undefined): string {
293
+ if (!tools || tools.length === 0) {
294
+ return "";
295
+ }
296
+
297
+ const lines = ["[Available Pi Tools]"];
298
+ for (const tool of tools) {
299
+ lines.push(`- ${tool.name}: ${tool.description}`);
300
+ lines.push(` schema: ${JSON.stringify(tool.parameters)}`);
301
+ }
302
+ lines.push(
303
+ "When tool needed, reply ONLY with fenced block:",
304
+ "```pi_tool_call",
305
+ '{"name":"tool_name","arguments":{}}',
306
+ "```",
307
+ "No extra text before/after fence when calling tool.",
308
+ "Never use emoji.",
309
+ "Never add markdown decoration unless explicitly requested.",
310
+ );
311
+
312
+ return lines.join("\n");
313
+ }
314
+
315
+ function serializeContext(context: Context): string {
316
+ const lines: string[] = [];
317
+
318
+ if (context.systemPrompt) {
319
+ lines.push(`[System]\n${context.systemPrompt}`);
320
+ }
321
+
322
+ const toolsSection = formatTools(context.tools);
323
+ if (toolsSection) {
324
+ lines.push(toolsSection);
325
+ }
326
+
327
+ for (const msg of context.messages) {
328
+ if (msg.role === "user") {
329
+ const text = typeof msg.content === "string" ? msg.content : msg.content.map(contentBlockToText).join("\n");
330
+ lines.push(`[User]\n${text}`);
331
+ continue;
332
+ }
333
+
334
+ if (msg.role === "assistant") {
335
+ const text = msg.content
336
+ .filter((c): c is TextContent => c.type === "text")
337
+ .map((c) => c.text)
338
+ .join("\n");
339
+ if (text.trim().length > 0) {
340
+ lines.push(`[Assistant]\n${text}`);
341
+ }
342
+ continue;
343
+ }
344
+
345
+ const text = msg.content.map(contentBlockToText).join("\n");
346
+ if (text.trim().length > 0) {
347
+ lines.push(`[Tool result: ${msg.toolName}]\n${text}`);
348
+ }
349
+ }
350
+
351
+ return lines.join("\n\n");
352
+ }
353
+
354
+ function estimateTokens(text: string): number {
355
+ if (!text) return 0;
356
+ return Math.max(1, Math.ceil(text.length / 4));
357
+ }
358
+
359
+ function applyUsage(model: Model<Api>, output: AssistantMessage, promptText: string, finalText: string): void {
360
+ output.usage.input = estimateTokens(promptText);
361
+ output.usage.output = estimateTokens(finalText);
362
+ output.usage.cacheRead = 0;
363
+ output.usage.cacheWrite = 0;
364
+ output.usage.totalTokens = output.usage.input + output.usage.output;
365
+ output.usage.cost = calculateCost(model, output.usage);
366
+ }
367
+
368
+ function parsePiToolCall(text: string, context: Context): { name: string; arguments: Record<string, unknown> } | undefined {
369
+ if (!context.tools || context.tools.length === 0) {
370
+ return undefined;
371
+ }
372
+
373
+ const fenced = text.match(/```pi_tool_call\s*([\s\S]*?)```/i)?.[1]?.trim();
374
+ const candidates = [fenced, text.trim()].filter((v): v is string => Boolean(v));
375
+
376
+ for (const candidate of candidates) {
377
+ try {
378
+ const parsed = JSON.parse(candidate) as { name?: unknown; arguments?: unknown };
379
+ if (typeof parsed.name !== "string") {
380
+ continue;
381
+ }
382
+ if (!context.tools.some((t) => t.name === parsed.name)) {
383
+ continue;
384
+ }
385
+ const args = parsed.arguments;
386
+ if (!args || typeof args !== "object" || Array.isArray(args)) {
387
+ return { name: parsed.name, arguments: {} };
388
+ }
389
+ return { name: parsed.name, arguments: args as Record<string, unknown> };
390
+ } catch {
391
+ // try next candidate
392
+ }
393
+ }
394
+ return undefined;
395
+ }
396
+
397
+ function parseBracketToolCall(text: string, context: Context): { name: string; arguments: Record<string, unknown> } | undefined {
398
+ if (!context.tools || context.tools.length === 0) {
399
+ return undefined;
400
+ }
401
+
402
+ const extractBalancedJson = (input: string): string | undefined => {
403
+ const start = input.indexOf("{");
404
+ if (start === -1) return undefined;
405
+ let depth = 0;
406
+ let inString = false;
407
+ let escaped = false;
408
+ for (let i = start; i < input.length; i++) {
409
+ const ch = input[i];
410
+ if (inString) {
411
+ if (escaped) {
412
+ escaped = false;
413
+ continue;
414
+ }
415
+ if (ch === "\\") {
416
+ escaped = true;
417
+ continue;
418
+ }
419
+ if (ch === "\"") {
420
+ inString = false;
421
+ }
422
+ continue;
423
+ }
424
+ if (ch === "\"") {
425
+ inString = true;
426
+ continue;
427
+ }
428
+ if (ch === "{") {
429
+ depth++;
430
+ continue;
431
+ }
432
+ if (ch === "}") {
433
+ depth--;
434
+ if (depth === 0) {
435
+ return input.slice(start, i + 1);
436
+ }
437
+ }
438
+ }
439
+ return undefined;
440
+ };
441
+
442
+ const extractBestEffortArgs = (input: string): Record<string, unknown> | undefined => {
443
+ const args: Record<string, unknown> = {};
444
+
445
+ const command = input.match(/"command"\s*:\s*"([^"\n\r]*)/);
446
+ if (command?.[1]) args.command = command[1];
447
+
448
+ const workingDirectory = input.match(/"workingDirectory"\s*:\s*"([^"\n\r]*)/);
449
+ if (workingDirectory?.[1] !== undefined) args.workingDirectory = workingDirectory[1];
450
+
451
+ const path = input.match(/"path"\s*:\s*"([^"\n\r]*)/);
452
+ if (path?.[1]) args.path = path[1];
453
+
454
+ const pattern = input.match(/"pattern"\s*:\s*"([^"\n\r]*)/);
455
+ if (pattern?.[1]) args.pattern = pattern[1];
456
+
457
+ const timeout = input.match(/"timeout"\s*:\s*(\d+)/);
458
+ if (timeout?.[1]) args.timeout = Number(timeout[1]);
459
+
460
+ return Object.keys(args).length > 0 ? args : undefined;
461
+ };
462
+
463
+ const lines = text.split("\n");
464
+ for (let i = lines.length - 1; i >= 0; i--) {
465
+ const line = lines[i]?.trim();
466
+ if (!line) continue;
467
+ const match = line.match(/^(?:⏳\s*)?\[([^\]]+)\]\s*(.*)$/);
468
+ if (!match) continue;
469
+ const [, rawName, inlinePayload] = match;
470
+ const toolName = context.tools.find((tool) => tool.name.toLowerCase() === rawName.trim().toLowerCase())?.name;
471
+ if (!toolName) continue;
472
+
473
+ const trailingText = lines.slice(i).join("\n");
474
+ const rawArgs =
475
+ extractBalancedJson(inlinePayload) ??
476
+ extractBalancedJson(trailingText);
477
+ if (!rawArgs) {
478
+ const fallbackArgs = extractBestEffortArgs(trailingText);
479
+ if (fallbackArgs) {
480
+ return { name: toolName, arguments: fallbackArgs };
481
+ }
482
+ continue;
483
+ }
484
+ try {
485
+ const args = JSON.parse(rawArgs);
486
+ if (!args || typeof args !== "object" || Array.isArray(args)) {
487
+ return { name: toolName, arguments: {} };
488
+ }
489
+ return { name: toolName, arguments: args as Record<string, unknown> };
490
+ } catch {
491
+ const fallbackArgs = extractBestEffortArgs(trailingText);
492
+ if (fallbackArgs) {
493
+ return { name: toolName, arguments: fallbackArgs };
494
+ }
495
+ }
496
+ }
497
+ return undefined;
498
+ }
499
+
500
+ function extractThinkingTextFromTranscript(text: string): { thinking: string; remainingText: string } {
501
+ const lines = text.split("\n");
502
+ const thinkingLines: string[] = [];
503
+ const remainingLines: string[] = [];
504
+
505
+ for (const line of lines) {
506
+ const raw = line.trim();
507
+ const stripped = raw.replace(/^[⠋⠙⠸⠴⠦⠇⠏✻•*]+\s*/, "");
508
+ const thinkingMatch = stripped.match(/^Thinking\.\.\.\s*:?\s*(.*)$/i);
509
+ if (thinkingMatch) {
510
+ if (thinkingMatch[1]) thinkingLines.push(thinkingMatch[1]);
511
+ continue;
512
+ }
513
+ remainingLines.push(line);
514
+ }
515
+
516
+ return {
517
+ thinking: thinkingLines.join("\n").trim(),
518
+ remainingText: remainingLines.join("\n"),
519
+ };
520
+ }
521
+
522
+ function stripToolCallMarkup(text: string): string {
523
+ return text
524
+ .replace(/```pi_tool_call\s*[\s\S]*?```/gi, "")
525
+ .replace(/\{\s*"name"\s*:\s*"[^"]+"\s*,\s*"arguments"\s*:\s*\{[\s\S]*?\}\s*\}/g, "")
526
+ .replace(/(?:^|\n)\s*(?:⏳\s*)?\[[^\]]+\]\s*\{[\s\S]*?(?=\n(?:\s*(?:Status|Actions)\s*:|$)|$)/g, "")
527
+ .replace(/(?:^|\n)\s*⏺\s*[a-zA-Z0-9._-]+\s*[\s\S]*?```json\s*[\s\S]*?```\s*[\s\S]*?```[\s\S]*?```/g, "")
528
+ .replace(/^.*?\n(?=\s*⏺\s*[a-zA-Z0-9._-]+\s*$)/s, "")
529
+ .trim();
530
+ }
531
+
532
+ function normalizeParsedToolCallArgs(
533
+ name: string,
534
+ arguments_: Record<string, unknown>,
535
+ ): Record<string, unknown> {
536
+ const tool = name.toLowerCase();
537
+ const args = { ...arguments_ };
538
+ delete args.toolCallId;
539
+ delete args.call_id;
540
+
541
+ if (tool === "shell" || tool === "bash") {
542
+ const command = typeof args.command === "string" ? args.command : "";
543
+ const workingDirectory =
544
+ typeof args.working_directory === "string"
545
+ ? args.working_directory
546
+ : typeof args.workingDirectory === "string"
547
+ ? args.workingDirectory
548
+ : typeof args.cwd === "string"
549
+ ? args.cwd
550
+ : undefined;
551
+ const blockUntilMs =
552
+ typeof args.block_until_ms === "number"
553
+ ? args.block_until_ms
554
+ : typeof args.timeout === "number"
555
+ ? args.timeout
556
+ : undefined;
557
+
558
+ const normalized: Record<string, unknown> = { command };
559
+ if (workingDirectory && workingDirectory.trim().length > 0) {
560
+ normalized.working_directory = workingDirectory.trim();
561
+ }
562
+ if (typeof blockUntilMs === "number" && Number.isFinite(blockUntilMs) && blockUntilMs >= 0) {
563
+ normalized.block_until_ms = Math.floor(blockUntilMs);
564
+ }
565
+ if (typeof args.description === "string" && args.description.trim().length > 0) {
566
+ normalized.description = args.description.trim();
567
+ }
568
+ return normalized;
569
+ }
570
+
571
+ if (tool === "glob") {
572
+ const normalized: Record<string, unknown> = {};
573
+ if (typeof args.globPattern === "string") normalized.glob_pattern = args.globPattern;
574
+ else if (typeof args.pattern === "string") normalized.glob_pattern = args.pattern;
575
+ if (typeof args.targetDirectory === "string") normalized.target_directory = args.targetDirectory;
576
+ return normalized;
577
+ }
578
+
579
+ if (tool === "read") {
580
+ return typeof args.path === "string" ? { path: args.path } : {};
581
+ }
582
+
583
+ if (tool === "edit") {
584
+ const path = typeof args.path === "string" ? args.path : undefined;
585
+ const edits = Array.isArray(args.edits) ? args.edits : undefined;
586
+ if (edits && edits.length > 0) {
587
+ return path ? { path, edits } : { edits };
588
+ }
589
+
590
+ const oldText =
591
+ typeof args.oldText === "string"
592
+ ? args.oldText
593
+ : typeof args.old_string === "string"
594
+ ? args.old_string
595
+ : undefined;
596
+ const newText =
597
+ typeof args.newText === "string"
598
+ ? args.newText
599
+ : typeof args.new_string === "string"
600
+ ? args.new_string
601
+ : undefined;
602
+ if (path && oldText !== undefined && newText !== undefined) {
603
+ return { path, edits: [{ oldText, newText }] };
604
+ }
605
+
606
+ // Incomplete "edit path" pseudo-calls should be remapped upstream.
607
+ return path ? { path } : {};
608
+ }
609
+
610
+ return args;
611
+ }
612
+
613
+ function likelyNeedsTool(text: string): boolean {
614
+ const lower = text.toLowerCase();
615
+ const signals = ["use ", "run ", "read ", "write ", "edit ", "find ", "grep ", "list ", "ls ", "bash ", "command", "file", "directory", "tool"];
616
+ return signals.some((s) => lower.includes(s));
617
+ }
618
+
619
+ const CURSOR_SDK_NOISE_PATTERNS: readonly RegExp[] = [
620
+ /\bmanaged_skills\.(?:removed|added|updated)\b/,
621
+ /\bLocalCursorRulesService load completed\b/,
622
+ /\bAgentSkillsCursorRulesService load completed\b/,
623
+ /\bCursorPluginsAgentSkillsService load completed\b/,
624
+ /^\s*[⠋⠙⠸⠴⠦⠇⠏]\s+Working\.\.\.\s*$/,
625
+ ];
626
+
627
+ function isCursorSdkNoiseChunk(chunk: string): boolean {
628
+ const normalized = chunk.replace(/\u001b\[[0-9;]*m/g, "").trim();
629
+ if (!normalized) return false;
630
+ return CURSOR_SDK_NOISE_PATTERNS.some((pattern) => pattern.test(normalized));
631
+ }
632
+
633
+ function stripCursorSdkNoise(chunk: string): string {
634
+ // Keep carriage-return spinner frames intact while filtering; splitting on '\r'
635
+ // turns transient updates into visible multiple lines in the TUI.
636
+ const lines = chunk.split("\n");
637
+ const kept: string[] = [];
638
+ for (const line of lines) {
639
+ const normalized = line.replace(/\u001b\[[0-9;]*m/g, "").replace(/\r/g, "").trim();
640
+ if (normalized.length > 0 && isCursorSdkNoiseChunk(normalized)) {
641
+ continue;
642
+ }
643
+ kept.push(line);
644
+ }
645
+ return kept.join("\n");
646
+ }
647
+
648
+ async function withCursorSdkNoiseSuppressed<T>(run: () => Promise<T>): Promise<T> {
649
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
650
+ const originalStderrWrite = process.stderr.write.bind(process.stderr);
651
+
652
+ const filteredWrite = (
653
+ originalWrite: (chunk: string | Uint8Array, encoding?: BufferEncoding, cb?: (error?: Error | null) => void) => boolean,
654
+ chunk: string | Uint8Array,
655
+ encoding?: BufferEncoding,
656
+ cb?: (error?: Error | null) => void,
657
+ ): boolean => {
658
+ const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString(encoding ?? "utf8");
659
+ const sanitized = stripCursorSdkNoise(text);
660
+ if (!sanitized) {
661
+ cb?.(null);
662
+ return true;
663
+ }
664
+ return originalWrite(sanitized, encoding, cb);
665
+ };
666
+
667
+ process.stdout.write = ((chunk: string | Uint8Array, encoding?: BufferEncoding, cb?: (error?: Error | null) => void) =>
668
+ filteredWrite(originalStdoutWrite, chunk, encoding, cb)) as typeof process.stdout.write;
669
+ process.stderr.write = ((chunk: string | Uint8Array, encoding?: BufferEncoding, cb?: (error?: Error | null) => void) =>
670
+ filteredWrite(originalStderrWrite, chunk, encoding, cb)) as typeof process.stderr.write;
671
+
672
+ try {
673
+ return await run();
674
+ } finally {
675
+ process.stdout.write = originalStdoutWrite as typeof process.stdout.write;
676
+ process.stderr.write = originalStderrWrite as typeof process.stderr.write;
677
+ }
678
+ }
679
+
680
+ function streamCursorSdk(model: Model<Api>, context: Context, options?: SimpleStreamOptions): AssistantMessageEventStream {
681
+ const stream = createAssistantMessageEventStream();
682
+
683
+ const availableTools = context.tools?.map((tool) => tool.name) ?? [];
684
+ const resolveToolName = (candidates: string[]): string | undefined => {
685
+ for (const candidate of candidates) {
686
+ if (availableTools.includes(candidate)) {
687
+ return candidate;
688
+ }
689
+ }
690
+ for (const candidate of candidates) {
691
+ const matched = availableTools.find((tool) => tool.toLowerCase() === candidate.toLowerCase());
692
+ if (matched) {
693
+ return matched;
694
+ }
695
+ }
696
+ return undefined;
697
+ };
698
+
699
+ const mapCursorTool = (name: string, input: unknown): { name: string; arguments: Record<string, unknown> } | undefined => {
700
+ const n = name.toLowerCase();
701
+ const args = typeof input === "object" && input !== null ? (input as Record<string, unknown>) : {};
702
+ if (n === "read") {
703
+ const mappedName = resolveToolName(["Read", "read", "ReadFile", "readfile"]);
704
+ if (!mappedName) return undefined;
705
+ return { name: mappedName, arguments: { path: args.path ?? "" } };
706
+ }
707
+ if (n === "shell" || n === "bash") {
708
+ const mappedName = resolveToolName(["Shell", "bash", "shell", "Bash"]);
709
+ if (!mappedName) return undefined;
710
+ return { name: mappedName, arguments: { command: args.command ?? "" } };
711
+ }
712
+ if (n === "ls") {
713
+ const mappedName = resolveToolName(["LS", "ls", "List"]);
714
+ if (!mappedName) return undefined;
715
+ return { name: mappedName, arguments: { path: args.path ?? "." } };
716
+ }
717
+ if (n === "grep") {
718
+ const mappedName = resolveToolName(["Grep", "grep", "Search"]);
719
+ if (!mappedName) return undefined;
720
+ return { name: mappedName, arguments: { pattern: args.pattern ?? "", path: args.path ?? "." } };
721
+ }
722
+ if (n === "glob" || n === "find") {
723
+ const mappedName = resolveToolName(["Find", "find"]);
724
+ if (!mappedName) return undefined;
725
+ return { name: mappedName, arguments: { pattern: args.globPattern ?? args.pattern ?? "**/*", path: args.targetDirectory ?? "." } };
726
+ }
727
+ if (n === "write") {
728
+ const mappedName = resolveToolName(["Write", "write"]);
729
+ if (!mappedName) return undefined;
730
+ return { name: mappedName, arguments: { path: args.path ?? "", content: args.fileText ?? "" } };
731
+ }
732
+ if (n === "edit") {
733
+ const editToolName = resolveToolName(["Edit", "edit"]);
734
+ const path = typeof args.path === "string" ? args.path : "";
735
+ const edits = Array.isArray(args.edits) ? args.edits : undefined;
736
+ const oldText =
737
+ typeof args.oldText === "string"
738
+ ? args.oldText
739
+ : typeof args.old_string === "string"
740
+ ? args.old_string
741
+ : undefined;
742
+ const newText =
743
+ typeof args.newText === "string"
744
+ ? args.newText
745
+ : typeof args.new_string === "string"
746
+ ? args.new_string
747
+ : undefined;
748
+
749
+ // Preserve Edit intent even when args are incomplete so the TUI
750
+ // still renders the edit call instead of silently converting it.
751
+ if ((!edits || edits.length === 0) && (oldText === undefined || newText === undefined)) {
752
+ return editToolName ? { name: editToolName, arguments: { path } } : undefined;
753
+ }
754
+
755
+ if (!editToolName) {
756
+ return undefined;
757
+ }
758
+
759
+ if (edits && edits.length > 0) {
760
+ return { name: editToolName, arguments: { path, edits } };
761
+ }
762
+ return {
763
+ name: editToolName,
764
+ arguments: {
765
+ path,
766
+ edits: [{ oldText: oldText ?? "", newText: newText ?? "" }],
767
+ },
768
+ };
769
+ }
770
+ const passthrough = resolveToolName([name, n]);
771
+ if (!passthrough) return undefined;
772
+ return { name: passthrough, arguments: args };
773
+ };
774
+
775
+ const emitToolCallBlock = (output: AssistantMessage, mapped: { name: string; arguments: Record<string, unknown> }, id?: string): void => {
776
+ const contentIndex = output.content.length;
777
+ const toolCallId = id || `call_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
778
+ const piToolCall = {
779
+ type: "toolCall" as const,
780
+ id: toolCallId,
781
+ name: mapped.name,
782
+ arguments: mapped.arguments,
783
+ };
784
+ output.content.push(piToolCall);
785
+ stream.push({ type: "toolcall_start", contentIndex, partial: output });
786
+ stream.push({
787
+ type: "toolcall_delta",
788
+ contentIndex,
789
+ delta: JSON.stringify(mapped.arguments),
790
+ partial: output,
791
+ });
792
+ stream.push({ type: "toolcall_end", contentIndex, toolCall: piToolCall, partial: output });
793
+ };
794
+
795
+ (async () => {
796
+ let runCancel: (() => Promise<void>) | undefined;
797
+ let aborted = false;
798
+ let sawPiToolCall = false;
799
+ let plainText = "";
800
+ let activeTextContentIndex: number | undefined;
801
+ let activeThinkingContentIndex: number | undefined;
802
+ const emittedToolCallIds = new Set<string>();
803
+
804
+ const output: AssistantMessage = {
805
+ role: "assistant",
806
+ content: [],
807
+ api: model.api,
808
+ provider: model.provider,
809
+ model: model.id,
810
+ usage: {
811
+ input: 0,
812
+ output: 0,
813
+ cacheRead: 0,
814
+ cacheWrite: 0,
815
+ totalTokens: 0,
816
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
817
+ },
818
+ stopReason: "stop",
819
+ timestamp: Date.now(),
820
+ };
821
+
822
+ const onAbort = () => {
823
+ aborted = true;
824
+ void runCancel?.();
825
+ };
826
+
827
+ const appendTextDelta = (text: string): void => {
828
+ if (!text) return;
829
+ plainText += text;
830
+ if (activeTextContentIndex === undefined) {
831
+ activeTextContentIndex = output.content.length;
832
+ output.content.push({ type: "text", text: "" });
833
+ stream.push({ type: "text_start", contentIndex: activeTextContentIndex, partial: output });
834
+ }
835
+ const current = output.content[activeTextContentIndex] as TextContent;
836
+ current.text += text;
837
+ stream.push({ type: "text_delta", contentIndex: activeTextContentIndex, delta: text, partial: output });
838
+ };
839
+
840
+ const closeActiveTextBlock = (): void => {
841
+ if (activeTextContentIndex === undefined) return;
842
+ const current = output.content[activeTextContentIndex] as TextContent;
843
+ stream.push({
844
+ type: "text_end",
845
+ contentIndex: activeTextContentIndex,
846
+ content: current.text,
847
+ partial: output,
848
+ });
849
+ activeTextContentIndex = undefined;
850
+ };
851
+
852
+ const appendThinkingDelta = (text: string): void => {
853
+ if (!text) return;
854
+ if (activeThinkingContentIndex === undefined) {
855
+ activeThinkingContentIndex = output.content.length;
856
+ output.content.push({ type: "thinking", thinking: "" });
857
+ stream.push({ type: "thinking_start", contentIndex: activeThinkingContentIndex, partial: output });
858
+ }
859
+ const current = output.content[activeThinkingContentIndex] as { type: "thinking"; thinking: string };
860
+ current.thinking += text;
861
+ stream.push({
862
+ type: "thinking_delta",
863
+ contentIndex: activeThinkingContentIndex,
864
+ delta: text,
865
+ partial: output,
866
+ });
867
+ };
868
+
869
+ const closeActiveThinkingBlock = (): void => {
870
+ if (activeThinkingContentIndex === undefined) return;
871
+ const current = output.content[activeThinkingContentIndex] as { type: "thinking"; thinking: string };
872
+ stream.push({
873
+ type: "thinking_end",
874
+ contentIndex: activeThinkingContentIndex,
875
+ content: current.thinking,
876
+ partial: output,
877
+ });
878
+ activeThinkingContentIndex = undefined;
879
+ };
880
+
881
+ try {
882
+ stream.push({ type: "start", partial: output });
883
+
884
+ await withCursorSdkNoiseSuppressed(async () => {
885
+ const selectedModel = await toCursorModelId(model.id, options?.reasoning);
886
+ const promptText = serializeContext(context);
887
+ const agent = await createCursorAgent({
888
+ apiKey: process.env.CURSOR_API_KEY,
889
+ model: { id: selectedModel } as ModelSelection,
890
+ // YOLO-style local runtime: load all Cursor settings and disable sandbox
891
+ // so the agent can execute tool/shell flows without interactive gating.
892
+ local: {
893
+ cwd: process.cwd(),
894
+ settingSources: ["all"],
895
+ sandboxOptions: { enabled: false },
896
+ },
897
+ });
898
+
899
+ try {
900
+ options?.signal?.addEventListener("abort", onAbort, { once: true });
901
+ // Keep force=true so stale/busy local runs never block autonomous execution.
902
+ const run = await agent.send(promptText, { local: { force: true } });
903
+ runCancel = () => run.cancel();
904
+
905
+ for await (const event of run.stream()) {
906
+ if (aborted) break;
907
+ if (event.type === "tool_call") {
908
+ if (event.status !== "running" && event.status !== "completed" && event.status !== "error") {
909
+ continue;
910
+ }
911
+ const mapped = mapCursorTool(event.name, event.args);
912
+ if (!mapped) continue;
913
+
914
+ if (!emittedToolCallIds.has(event.call_id)) {
915
+ closeActiveThinkingBlock();
916
+ closeActiveTextBlock();
917
+ emittedToolCallIds.add(event.call_id);
918
+ emitToolCallBlock(output, mapped, event.call_id);
919
+ sawPiToolCall = true;
920
+ }
921
+
922
+ continue;
923
+ }
924
+ if (event.type === "thinking") {
925
+ appendThinkingDelta(event.text || "");
926
+ continue;
927
+ }
928
+ if (event.type !== "assistant") continue;
929
+ closeActiveThinkingBlock();
930
+
931
+ for (const block of event.message.content) {
932
+ if (block.type === "thinking" && typeof block.thinking === "string") {
933
+ appendThinkingDelta(block.thinking);
934
+ continue;
935
+ }
936
+
937
+ if (block.type === "reasoning" && typeof block.text === "string") {
938
+ appendThinkingDelta(block.text);
939
+ continue;
940
+ }
941
+
942
+ if (block.type === "text") {
943
+ const text = block.text || "";
944
+ const extracted = extractThinkingTextFromTranscript(text);
945
+ if (extracted.thinking) {
946
+ appendThinkingDelta(extracted.thinking);
947
+ }
948
+ const visibleText = extracted.remainingText;
949
+ if (!visibleText.trim()) {
950
+ continue;
951
+ }
952
+ appendTextDelta(visibleText);
953
+ continue;
954
+ }
955
+
956
+ if (block.type === "tool_use") {
957
+ const mapped = mapCursorTool(block.name, block.input);
958
+ if (!mapped) continue;
959
+ closeActiveThinkingBlock();
960
+ closeActiveTextBlock();
961
+ const toolCallId = block.id || `call_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
962
+ if (emittedToolCallIds.has(toolCallId)) {
963
+ continue;
964
+ }
965
+ emittedToolCallIds.add(toolCallId);
966
+ emitToolCallBlock(output, mapped, toolCallId);
967
+ sawPiToolCall = true;
968
+ }
969
+ }
970
+ }
971
+ closeActiveThinkingBlock();
972
+ closeActiveTextBlock();
973
+
974
+ const result = await run.wait();
975
+ if (aborted || options?.signal?.aborted || result.status === "cancelled") {
976
+ output.stopReason = "aborted";
977
+ output.errorMessage = "Request aborted";
978
+ stream.push({ type: "error", reason: "aborted", error: output });
979
+ stream.end();
980
+ return;
981
+ }
982
+ if (result.status !== "finished") {
983
+ output.stopReason = "error";
984
+ output.errorMessage = result.result || `Cursor SDK run status: ${result.status}`;
985
+ stream.push({ type: "error", reason: "error", error: output });
986
+ stream.end();
987
+ return;
988
+ }
989
+
990
+ applyUsage(model, output, promptText, plainText || result.result || "");
991
+
992
+ // Some models may output fenced `pi_tool_call` JSON as plain text instead of native tool blocks.
993
+ // Convert it into a real toolCall block so Pi UI/tool execution works consistently.
994
+ if (!sawPiToolCall) {
995
+ const parsedPiToolCalls: Array<{ name: string; arguments: Record<string, unknown> }> = [];
996
+ const parsedFenced = parsePiToolCall(plainText, context);
997
+ if (parsedFenced) parsedPiToolCalls.push(parsedFenced);
998
+ const parsedBracket = parseBracketToolCall(plainText, context);
999
+ if (parsedBracket) parsedPiToolCalls.push(parsedBracket);
1000
+ parsedPiToolCalls.push(...parseCursorTranscriptToolCalls(plainText, context));
1001
+
1002
+ if (parsedPiToolCalls.length > 0) {
1003
+ closeActiveTextBlock();
1004
+ closeActiveThinkingBlock();
1005
+ for (const parsedPiToolCall of parsedPiToolCalls) {
1006
+ const contentIndex = output.content.length;
1007
+ const toolCallId = `call_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
1008
+ const normalizedArgs = normalizeParsedToolCallArgs(
1009
+ parsedPiToolCall.name,
1010
+ parsedPiToolCall.arguments,
1011
+ );
1012
+ const piToolCall = {
1013
+ type: "toolCall" as const,
1014
+ id: toolCallId,
1015
+ name: parsedPiToolCall.name,
1016
+ arguments: normalizedArgs,
1017
+ };
1018
+ output.content.push(piToolCall);
1019
+ stream.push({ type: "toolcall_start", contentIndex, partial: output });
1020
+ stream.push({
1021
+ type: "toolcall_delta",
1022
+ contentIndex,
1023
+ delta: JSON.stringify(normalizedArgs),
1024
+ partial: output,
1025
+ });
1026
+ stream.push({ type: "toolcall_end", contentIndex, toolCall: piToolCall, partial: output });
1027
+ sawPiToolCall = true;
1028
+ }
1029
+
1030
+ const cleaned = stripToolCallMarkup(plainText);
1031
+ if (cleaned !== plainText) {
1032
+ for (const content of output.content) {
1033
+ if (content.type === "text") {
1034
+ content.text = stripToolCallMarkup(content.text);
1035
+ }
1036
+ }
1037
+ }
1038
+ }
1039
+ }
1040
+
1041
+ if (sawPiToolCall) {
1042
+ for (const content of output.content) {
1043
+ if (content.type === "text") {
1044
+ content.text = stripToolCallMarkup(content.text);
1045
+ }
1046
+ }
1047
+ output.stopReason = "toolUse";
1048
+ stream.push({ type: "done", reason: "toolUse", message: output });
1049
+ } else {
1050
+ stream.push({ type: "done", reason: "stop", message: output });
1051
+ }
1052
+ stream.end();
1053
+ } finally {
1054
+ options?.signal?.removeEventListener("abort", onAbort);
1055
+ agent.close();
1056
+ }
1057
+ });
1058
+ } catch (error) {
1059
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
1060
+ output.errorMessage = error instanceof Error ? error.message : String(error);
1061
+ stream.push({ type: "error", reason: output.stopReason, error: output });
1062
+ stream.end();
1063
+ }
1064
+ })();
1065
+
1066
+ return stream;
1067
+ }
1068
+
1069
+ export const __streamCursorSdkForTests = streamCursorSdk;
1070
+
1071
+ export default function cursorSdkProvider(pi: ExtensionAPI) {
1072
+ // Synchronous fallback registration; refreshed with account-specific models below.
1073
+ registerCursorProviderModels(pi, PROVIDER_MODELS);
1074
+
1075
+ const refreshProviderModels = async (forceRefresh = false) => {
1076
+ const models = await getDynamicProviderModels(forceRefresh);
1077
+ registerCursorProviderModels(pi, models);
1078
+ };
1079
+
1080
+ void refreshProviderModels();
1081
+
1082
+ pi.on("session_start", () => {
1083
+ void refreshProviderModels(true);
1084
+ });
1085
+ }