pi-orchestrator-config 4.1.5

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 (175) hide show
  1. package/README.md +808 -0
  2. package/agents/api-documenter.md +32 -0
  3. package/agents/bash-expert.md +53 -0
  4. package/agents/code-reviewer-docs.md +145 -0
  5. package/agents/code-reviewer-guidelines.md +97 -0
  6. package/agents/code-reviewer-quality.md +142 -0
  7. package/agents/code-reviewer-security.md +107 -0
  8. package/agents/code-reviewer-spec.md +164 -0
  9. package/agents/debugger.md +63 -0
  10. package/agents/docker-expert.md +40 -0
  11. package/agents/docs-fetcher.md +48 -0
  12. package/agents/git-expert.md +63 -0
  13. package/agents/github-expert.md +83 -0
  14. package/agents/go-expert.md +62 -0
  15. package/agents/issue-reviewer-feasibility.md +60 -0
  16. package/agents/issue-reviewer-scope.md +63 -0
  17. package/agents/issue-reviewer-spec.md +77 -0
  18. package/agents/java-expert.md +31 -0
  19. package/agents/jenkins-expert.md +38 -0
  20. package/agents/kubernetes-expert.md +40 -0
  21. package/agents/planner.md +42 -0
  22. package/agents/python-expert.md +61 -0
  23. package/agents/reviewer.md +26 -0
  24. package/agents/scout.md +40 -0
  25. package/agents/security-auditor.md +254 -0
  26. package/agents/technical-documentation-writer.md +37 -0
  27. package/agents/test-automator.md +55 -0
  28. package/agents/test-runner.md +47 -0
  29. package/agents/ts-expert.md +23 -0
  30. package/agents/worker.md +30 -0
  31. package/extensions/acpx-provider/index.ts +104 -0
  32. package/extensions/acpx-provider/load-runtime.ts +99 -0
  33. package/extensions/acpx-provider/runtime-models.ts +40 -0
  34. package/extensions/cli-provider/agents/claude.ts +128 -0
  35. package/extensions/cli-provider/agents/cursor.ts +84 -0
  36. package/extensions/cli-provider/agents/gemini.ts +100 -0
  37. package/extensions/cli-provider/discover.ts +77 -0
  38. package/extensions/cli-provider/index.ts +57 -0
  39. package/extensions/cli-provider/parsers.ts +304 -0
  40. package/extensions/cli-provider/providers.ts +58 -0
  41. package/extensions/cli-provider/runner.ts +225 -0
  42. package/extensions/cli-provider/runtime-models.ts +32 -0
  43. package/extensions/cli-provider/session-reaper.ts +122 -0
  44. package/extensions/cli-provider/sessions.ts +533 -0
  45. package/extensions/cli-provider/shared/discover-cache.ts +235 -0
  46. package/extensions/cli-provider/types.ts +48 -0
  47. package/extensions/coms/coms-net-server.ts +1642 -0
  48. package/extensions/coms/coms-net-wrapper.ts +521 -0
  49. package/extensions/coms/coms-net.ts +1589 -0
  50. package/extensions/coms/coms-p2p.ts +1879 -0
  51. package/extensions/coms/coms-shared.ts +510 -0
  52. package/extensions/coms/coms-wrapper.ts +138 -0
  53. package/extensions/coms/index.ts +20 -0
  54. package/extensions/coms/themeMap.ts +147 -0
  55. package/extensions/image-gen/image-gen.ts +332 -0
  56. package/extensions/image-gen/index.ts +14 -0
  57. package/extensions/orchestrator/agents.ts +134 -0
  58. package/extensions/orchestrator/ask-user.ts +293 -0
  59. package/extensions/orchestrator/async-agents.ts +1037 -0
  60. package/extensions/orchestrator/async-capability.ts +129 -0
  61. package/extensions/orchestrator/async-runner.ts +324 -0
  62. package/extensions/orchestrator/async-status-parse.ts +31 -0
  63. package/extensions/orchestrator/async-status-ui.ts +201 -0
  64. package/extensions/orchestrator/async-wait.ts +36 -0
  65. package/extensions/orchestrator/btw.ts +210 -0
  66. package/extensions/orchestrator/cron-status-format.ts +126 -0
  67. package/extensions/orchestrator/cron-status-ui.ts +132 -0
  68. package/extensions/orchestrator/cron.ts +730 -0
  69. package/extensions/orchestrator/dreaming.ts +355 -0
  70. package/extensions/orchestrator/enforcement-helpers.ts +530 -0
  71. package/extensions/orchestrator/enforcement-rules.ts +329 -0
  72. package/extensions/orchestrator/enforcement.ts +828 -0
  73. package/extensions/orchestrator/extended-autocomplete.ts +593 -0
  74. package/extensions/orchestrator/git-helpers.ts +420 -0
  75. package/extensions/orchestrator/github-autocomplete.ts +250 -0
  76. package/extensions/orchestrator/icons.ts +17 -0
  77. package/extensions/orchestrator/index.ts +100 -0
  78. package/extensions/orchestrator/memory-embeddings.ts +281 -0
  79. package/extensions/orchestrator/memory-promotion.ts +449 -0
  80. package/extensions/orchestrator/memory-provenance.ts +80 -0
  81. package/extensions/orchestrator/memory-query-class.ts +90 -0
  82. package/extensions/orchestrator/memory-scoring.ts +434 -0
  83. package/extensions/orchestrator/memory-tools.ts +883 -0
  84. package/extensions/orchestrator/memory-tree.ts +212 -0
  85. package/extensions/orchestrator/nvim.ts +169 -0
  86. package/extensions/orchestrator/overlay-dashboard-utils.ts +25 -0
  87. package/extensions/orchestrator/overlay-dashboard.ts +512 -0
  88. package/extensions/orchestrator/pi-config-review-state.ts +303 -0
  89. package/extensions/orchestrator/preference-extractor.ts +106 -0
  90. package/extensions/orchestrator/project-settings.ts +468 -0
  91. package/extensions/orchestrator/promotion-queue.ts +311 -0
  92. package/extensions/orchestrator/resolve-agent-model.ts +32 -0
  93. package/extensions/orchestrator/review-ui.ts +204 -0
  94. package/extensions/orchestrator/rule-placeholders.ts +12 -0
  95. package/extensions/orchestrator/rules.ts +481 -0
  96. package/extensions/orchestrator/session-search.ts +243 -0
  97. package/extensions/orchestrator/session-validation.ts +374 -0
  98. package/extensions/orchestrator/situation-report.ts +370 -0
  99. package/extensions/orchestrator/status-bar.ts +31 -0
  100. package/extensions/orchestrator/status-line.ts +192 -0
  101. package/extensions/orchestrator/status.ts +110 -0
  102. package/extensions/orchestrator/subagent-tool.ts +1748 -0
  103. package/extensions/orchestrator/task-lifecycle.ts +88 -0
  104. package/extensions/orchestrator/utils.ts +227 -0
  105. package/extensions/pidash/index.ts +15 -0
  106. package/extensions/pidash/pidash.ts +941 -0
  107. package/extensions/pidiff/index.ts +13 -0
  108. package/extensions/pidiff/pidiff.ts +369 -0
  109. package/extensions/providers/acpx-driver.ts +479 -0
  110. package/extensions/providers/built-in-drivers.ts +52 -0
  111. package/extensions/providers/claude-driver.ts +319 -0
  112. package/extensions/providers/cursor-acpx-driver.ts +398 -0
  113. package/extensions/providers/cursor-cli-driver.ts +484 -0
  114. package/extensions/providers/gemini-driver.ts +302 -0
  115. package/extensions/providers/index.ts +513 -0
  116. package/extensions/shared/build-system-prompt.ts +57 -0
  117. package/extensions/shared/create-runtime-provider.ts +190 -0
  118. package/extensions/shared/daemon-manager.ts +248 -0
  119. package/extensions/shared/file-logger.ts +115 -0
  120. package/extensions/shared/managed-refresh.ts +211 -0
  121. package/extensions/shared/provider-driver.ts +295 -0
  122. package/extensions/shared/provider-errors.ts +305 -0
  123. package/extensions/shared/provider-registry.ts +388 -0
  124. package/extensions/shared/stream-builder.ts +275 -0
  125. package/extensions/shared/types.ts +20 -0
  126. package/extensions/shared/ui/README.md +10 -0
  127. package/extensions/shared/ui/badge.tsx +52 -0
  128. package/extensions/shared/ui/button.tsx +58 -0
  129. package/extensions/shared/ui/collapsible.tsx +21 -0
  130. package/extensions/shared/ui/dropdown-menu.tsx +266 -0
  131. package/extensions/shared/ui/input.tsx +20 -0
  132. package/extensions/shared/ui/popover.tsx +114 -0
  133. package/extensions/shared/ui/resizable.tsx +48 -0
  134. package/extensions/shared/ui/scroll-area.tsx +53 -0
  135. package/extensions/shared/ui/select.tsx +201 -0
  136. package/extensions/shared/ui/separator.tsx +25 -0
  137. package/extensions/shared/ui/tooltip.tsx +64 -0
  138. package/extensions/shared/ws-client.ts +74 -0
  139. package/package.json +59 -0
  140. package/prompts/coderabbit-rate-limit.md +95 -0
  141. package/prompts/create-coms-feature-manager.md +186 -0
  142. package/prompts/create-skill.md +128 -0
  143. package/prompts/domain-model.md +109 -0
  144. package/prompts/external-ai.md +665 -0
  145. package/prompts/implement-and-review.md +25 -0
  146. package/prompts/implement.md +26 -0
  147. package/prompts/issue-review.md +256 -0
  148. package/prompts/pr-review.md +753 -0
  149. package/prompts/query-db.md +154 -0
  150. package/prompts/refine-review.md +208 -0
  151. package/prompts/release.md +235 -0
  152. package/prompts/remember.md +33 -0
  153. package/prompts/review-handler-status.md +27 -0
  154. package/prompts/review-handler.md +677 -0
  155. package/prompts/review-local.md +159 -0
  156. package/prompts/scout-and-plan.md +21 -0
  157. package/rules/00-orchestrator-core.md +42 -0
  158. package/rules/05-issue-first-workflow.md +119 -0
  159. package/rules/10-agent-routing.md +58 -0
  160. package/rules/15-mcp-launchpad.md +46 -0
  161. package/rules/20-code-review-loop.md +234 -0
  162. package/rules/25-documentation-updates.md +15 -0
  163. package/rules/30-prompt-templates.md +19 -0
  164. package/rules/35-memory.md +240 -0
  165. package/rules/40-critical-rules.md +124 -0
  166. package/rules/45-file-preview.md +29 -0
  167. package/rules/50-agent-bug-reporting.md +90 -0
  168. package/rules/55-coms-protocol.md +63 -0
  169. package/rules/60-task-tracking.md +66 -0
  170. package/skills/conflict-resolver/SKILL.md +213 -0
  171. package/skills/skill-creator/SKILL.md +85 -0
  172. package/skills/systematic-debugging/SKILL.md +353 -0
  173. package/skills/test-driven-development/SKILL.md +335 -0
  174. package/skills/update-agents-md/SKILL.md +282 -0
  175. package/templates/coms-feature-manager-prompt.md +313 -0
@@ -0,0 +1,484 @@
1
+ /**
2
+ * CursorCliDriver — ProviderDriver for the Cursor Agent CLI.
3
+ *
4
+ * Most complex CLI driver — owns session management (markers, resume
5
+ * recovery, history seeding), model discovery via --list-models, and
6
+ * stream-json parsing.
7
+ *
8
+ * Reuses existing modules:
9
+ * - cli-provider/agents/cursor.ts — discovery (parseAgentListModels)
10
+ * - cli-provider/runner.ts — process spawning
11
+ * - cli-provider/sessions.ts — full session marker management
12
+ * - cli-provider/parsers.ts — output parsing
13
+ * - shared/build-system-prompt.ts — system prompt injection
14
+ *
15
+ * @module providers/cursor-cli-driver
16
+ */
17
+
18
+ import type {
19
+ ConfigSchema,
20
+ DiscoveredModel,
21
+ ProviderAdapterShape,
22
+ ProviderDriver,
23
+ ProviderDriverCreateInput,
24
+ ProviderInstance,
25
+ ProviderProbeResult,
26
+ SessionHandle,
27
+ SessionStartOptions,
28
+ TurnOptions,
29
+ TurnResult,
30
+ } from "../shared/provider-driver.js";
31
+ import { makeManagedSnapshot, buildInitialSnapshot } from "../shared/managed-refresh.js";
32
+ import { resolveBinary } from "../cli-provider/shared/discover-cache.js";
33
+ import { discoverCliModelsDetailed } from "../cli-provider/discover.js";
34
+ import { runCliAgent } from "../cli-provider/runner.js";
35
+ import {
36
+ loadCliSessionId,
37
+ saveCliSessionId,
38
+ clearCliSessionId,
39
+ applySystemPromptToCliPrompt,
40
+ shouldRetryWithoutResume,
41
+ createProvisionalPiSessionId,
42
+ isProvisionalPiSessionId,
43
+ migrateCliSessionMarker,
44
+ migrateAllCliSessionMarkers,
45
+ shouldAdoptLegacyCliMarker,
46
+ resolveCliHistorySeed,
47
+ touchCliSession,
48
+ clearCliSessionsForPiSession,
49
+ readPiSessionIdFromManager,
50
+ resolveActivePiSessionIdOnSessionStart,
51
+ decideCliSessionStartReseed,
52
+ type CliSessionKey,
53
+ } from "../cli-provider/sessions.js";
54
+ import { buildExternalSystemPrompt } from "../shared/build-system-prompt.js";
55
+ import { fileLog } from "../shared/file-logger.js";
56
+
57
+ const LOG_DOMAIN = "cursor-cli-driver";
58
+ const DRIVER_KIND = "cursor-cli";
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Config
62
+ // ---------------------------------------------------------------------------
63
+
64
+ export interface CursorCliConfig {
65
+ readonly binary: string;
66
+ readonly enabled: boolean;
67
+ }
68
+
69
+ const cursorCliConfigSchema: ConfigSchema<CursorCliConfig> = {
70
+ parse: (raw: unknown): CursorCliConfig => {
71
+ if (!raw || typeof raw !== "object") {
72
+ return { binary: "agent", enabled: true };
73
+ }
74
+ const obj = raw as Record<string, unknown>;
75
+ return {
76
+ binary: typeof obj.binary === "string" ? obj.binary : "agent",
77
+ enabled: typeof obj.enabled === "boolean" ? obj.enabled : true,
78
+ };
79
+ },
80
+ };
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // History seeding (from original cli-provider)
84
+ // ---------------------------------------------------------------------------
85
+
86
+ function messageText(msg: { role: string; content: unknown }): string {
87
+ if (typeof msg.content === "string") return msg.content;
88
+ if (!Array.isArray(msg.content)) return "";
89
+ const textParts: string[] = [];
90
+ for (const block of msg.content) {
91
+ if (
92
+ block &&
93
+ typeof block === "object" &&
94
+ "text" in block &&
95
+ typeof (block as { text: unknown }).text === "string"
96
+ ) {
97
+ textParts.push((block as { text: string }).text);
98
+ }
99
+ }
100
+ return textParts.join("\n");
101
+ }
102
+
103
+ /**
104
+ * When starting a NEW CLI session (no --resume), inject prior pi turns so
105
+ * switching mid-session does not drop conversation history.
106
+ */
107
+ function buildPromptWithHistory(
108
+ messages: Array<{ role: string; content: unknown }>,
109
+ latest: string,
110
+ hasCliSession: boolean,
111
+ ): string {
112
+ if (hasCliSession) return latest;
113
+
114
+ const prior: string[] = [];
115
+ let lastUser = -1;
116
+ for (let i = messages.length - 1; i >= 0; i--) {
117
+ if (messages[i].role === "user") {
118
+ lastUser = i;
119
+ break;
120
+ }
121
+ }
122
+ for (let i = 0; i < lastUser; i++) {
123
+ const msg = messages[i];
124
+ if (msg.role !== "user" && msg.role !== "assistant") continue;
125
+ const text = messageText(msg).trim();
126
+ if (!text) continue;
127
+ const clipped =
128
+ text.length > 4000 ? `${text.slice(0, 4000)}\n…[truncated]` : text;
129
+ prior.push(`${msg.role === "user" ? "User" : "Assistant"}: ${clipped}`);
130
+ }
131
+
132
+ if (prior.length === 0) return latest;
133
+
134
+ return [
135
+ "Prior conversation in this pi session (for context — continue from here):",
136
+ "",
137
+ ...prior,
138
+ "",
139
+ "---",
140
+ "",
141
+ "Current user message:",
142
+ latest,
143
+ ].join("\n");
144
+ }
145
+
146
+ // ---------------------------------------------------------------------------
147
+ // Adapter
148
+ // ---------------------------------------------------------------------------
149
+
150
+ function createCursorCliAdapter(
151
+ config: CursorCliConfig,
152
+ cwd: string,
153
+ instanceId: string,
154
+ ): ProviderAdapterShape & {
155
+ /** Bind the real pi session UUID (from sessionManager). */
156
+ bindPiSessionId: (sid: string) => void;
157
+ /** Handle session_start event (reseed, marker cleanup). */
158
+ handleSessionStart: (reason: string, sid: string | null) => void;
159
+ /** Force history re-seed on next turn. */
160
+ setForceHistorySeed: (force: boolean) => void;
161
+ } {
162
+ let activePiSessionId: string | null = null;
163
+ const provisionalPiSessionId = createProvisionalPiSessionId();
164
+ let forceHistorySeed = false;
165
+ const systemPromptSent = new Set<string>();
166
+ const storedSystemPrompts = new Map<string, string>();
167
+ const sessionKeys = new Map<string, CliSessionKey>();
168
+
169
+ function resolvedPiSessionId(): string {
170
+ return activePiSessionId || provisionalPiSessionId;
171
+ }
172
+
173
+ function sessionKeyFor(model: string): CliSessionKey {
174
+ return { cwd, agent: "cursor", model, piSessionId: resolvedPiSessionId() };
175
+ }
176
+
177
+ function migrateMarkersToRealPiSessionId(sid: string): void {
178
+ if (!sid || sid === provisionalPiSessionId) return;
179
+ migrateAllCliSessionMarkers(cwd, provisionalPiSessionId, sid);
180
+ for (const [handleKey, prevKey] of sessionKeys) {
181
+ if (
182
+ !prevKey.piSessionId ||
183
+ prevKey.piSessionId === sid ||
184
+ (!isProvisionalPiSessionId(prevKey.piSessionId) &&
185
+ prevKey.piSessionId !== "default")
186
+ ) {
187
+ continue;
188
+ }
189
+ const nextKey: CliSessionKey = { ...prevKey, piSessionId: sid };
190
+ const migrated = migrateCliSessionMarker(nextKey, prevKey.piSessionId);
191
+ if (!migrated && loadCliSessionId(prevKey)) {
192
+ fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
193
+ `CLI marker migration failed; keeping source under ${prevKey.piSessionId}`);
194
+ }
195
+ sessionKeys.set(handleKey, nextKey);
196
+ }
197
+ }
198
+
199
+ return {
200
+ bindPiSessionId: (sid: string) => {
201
+ activePiSessionId = sid;
202
+ migrateMarkersToRealPiSessionId(sid);
203
+ },
204
+
205
+ handleSessionStart: (reason: string, sid: string | null) => {
206
+ const prevSid = activePiSessionId;
207
+ const resolvedSid = sid || activePiSessionId;
208
+ if (sid) {
209
+ activePiSessionId = sid;
210
+ migrateMarkersToRealPiSessionId(sid);
211
+ }
212
+
213
+ const decision = decideCliSessionStartReseed({
214
+ reason,
215
+ prevPiSessionId: prevSid,
216
+ nextPiSessionId: resolvedSid,
217
+ });
218
+ forceHistorySeed = decision.forceHistorySeed;
219
+
220
+ if (decision.action === "keep") {
221
+ return;
222
+ }
223
+ if (decision.action === "reseed" && resolvedSid) {
224
+ try {
225
+ clearCliSessionsForPiSession(cwd, resolvedSid, {
226
+ includeLegacyDefault: prevSid == null || prevSid === "" || prevSid === "default",
227
+ });
228
+ } catch (err) {
229
+ fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
230
+ `session_start marker cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
231
+ }
232
+ systemPromptSent.clear();
233
+ storedSystemPrompts.clear();
234
+ }
235
+ },
236
+
237
+ setForceHistorySeed: (force: boolean) => {
238
+ forceHistorySeed = force;
239
+ },
240
+
241
+ startSession: async (opts: SessionStartOptions): Promise<SessionHandle> => {
242
+ const model = opts.model || "default";
243
+ if (opts.systemPrompt) storedSystemPrompts.set(model, opts.systemPrompt);
244
+ const key = sessionKeyFor(model);
245
+ sessionKeys.set(model, key);
246
+ const existingId = loadCliSessionId(key);
247
+ return {
248
+ sessionId: existingId || `cursor-${instanceId}-${model}`,
249
+ model,
250
+ };
251
+ },
252
+
253
+ sendTurn: async (
254
+ handle: SessionHandle,
255
+ prompt: string,
256
+ opts?: TurnOptions,
257
+ ): Promise<TurnResult> => {
258
+ const handleKey = handle.model || "default";
259
+ const key = sessionKeyFor(handle.model);
260
+ // Read previous key BEFORE updating so legacy-marker adoption can compare categories
261
+ const prevKey = sessionKeys.get(handleKey);
262
+ sessionKeys.set(handleKey, key);
263
+
264
+ let sessionId = loadCliSessionId(key);
265
+ // Mid-session bind: adopt legacy marker
266
+ if (!sessionId && shouldAdoptLegacyCliMarker(prevKey, key) && prevKey?.piSessionId) {
267
+ migrateCliSessionMarker(key, prevKey.piSessionId);
268
+ sessionId = loadCliSessionId(key);
269
+ }
270
+
271
+ const needsSystemPrompt = !systemPromptSent.has(handleKey) || forceHistorySeed;
272
+
273
+ // Prefer the system prompt stored at startSession over rebuilding
274
+ let systemPrompt: string | undefined;
275
+ if (needsSystemPrompt) {
276
+ systemPrompt =
277
+ storedSystemPrompts.get(handleKey) ||
278
+ buildExternalSystemPrompt({ systemPrompt: undefined }, cwd);
279
+ }
280
+
281
+ // Resolve history seed plan
282
+ const seedPlan = resolveCliHistorySeed({
283
+ hasCliSession: !!sessionId,
284
+ forceHistorySeed,
285
+ });
286
+ // Stale marker after /resume: drop it so we open a fresh CLI chat
287
+ if (forceHistorySeed && sessionId) {
288
+ clearCliSessionId(key);
289
+ }
290
+ const effectiveSessionId = seedPlan.useCliSession ? sessionId : null;
291
+
292
+ // Seed prior pi turns when opening a fresh CLI session
293
+ let finalPrompt = seedPlan.seedHistory && opts?.context?.messages
294
+ ? buildPromptWithHistory(opts.context.messages, prompt, false)
295
+ : prompt;
296
+ if (needsSystemPrompt && systemPrompt) {
297
+ finalPrompt = applySystemPromptToCliPrompt(finalPrompt, systemPrompt);
298
+ }
299
+
300
+ // Build event handler
301
+ const onEvent = opts?.onEvent
302
+ ? (ev: { kind: string; text?: string; sessionId?: string }) => {
303
+ if (ev.kind === "session" && ev.sessionId) {
304
+ saveCliSessionId(key, ev.sessionId);
305
+ opts.onEvent!({ kind: "session", sessionId: ev.sessionId });
306
+ } else if (ev.kind === "thinking_delta" && ev.text) {
307
+ opts.onEvent!({ kind: "thinking_delta", text: ev.text });
308
+ } else if (ev.kind === "text_delta" && ev.text) {
309
+ opts.onEvent!({ kind: "text_delta", text: ev.text });
310
+ }
311
+ }
312
+ : undefined;
313
+
314
+ const runOnce = (sid: string | null, p: string) =>
315
+ runCliAgent({
316
+ agent: "cursor",
317
+ model: handle.model === "default" ? "default" : handle.model,
318
+ cwd,
319
+ prompt: p,
320
+ sessionId: sid,
321
+ signal: opts?.signal,
322
+ binary: config.binary,
323
+ onEvent,
324
+ });
325
+
326
+ try {
327
+ const result = await runOnce(effectiveSessionId, finalPrompt);
328
+
329
+ if (result.sessionId) {
330
+ saveCliSessionId(key, result.sessionId);
331
+ } else if (effectiveSessionId) {
332
+ touchCliSession(key);
333
+ }
334
+
335
+ if (needsSystemPrompt) {
336
+ systemPromptSent.add(handleKey);
337
+ }
338
+ if (forceHistorySeed) {
339
+ forceHistorySeed = false;
340
+ }
341
+
342
+ return {
343
+ text: result.text,
344
+ thinking: result.thinking,
345
+ sessionId: result.sessionId,
346
+ stopReason: "stop",
347
+ usage: result.usage,
348
+ };
349
+ } catch (err) {
350
+ const message = err instanceof Error ? err.message : String(err);
351
+ if (effectiveSessionId && shouldRetryWithoutResume(message)) {
352
+ fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
353
+ `resume failed, clearing session and retrying: ${message.slice(0, 200)}`);
354
+ clearCliSessionId(key);
355
+ // Fresh CLI session after failed --resume — re-seed history
356
+ let retryPrompt = opts?.context?.messages
357
+ ? buildPromptWithHistory(opts.context.messages, prompt, false)
358
+ : prompt;
359
+ if (systemPrompt) {
360
+ retryPrompt = applySystemPromptToCliPrompt(retryPrompt, systemPrompt);
361
+ }
362
+ const result = await runOnce(null, retryPrompt);
363
+ if (result.sessionId) {
364
+ saveCliSessionId(key, result.sessionId);
365
+ }
366
+ if (needsSystemPrompt) {
367
+ systemPromptSent.add(handleKey);
368
+ }
369
+ if (forceHistorySeed) {
370
+ forceHistorySeed = false;
371
+ }
372
+ return {
373
+ text: result.text,
374
+ thinking: result.thinking,
375
+ sessionId: result.sessionId,
376
+ stopReason: "stop",
377
+ usage: result.usage,
378
+ };
379
+ }
380
+ throw err;
381
+ }
382
+ },
383
+
384
+ stopSession: async (handle: SessionHandle): Promise<void> => {
385
+ const key = sessionKeyFor(handle.model);
386
+ clearCliSessionId(key);
387
+ const handleKey = handle.model || "default";
388
+ sessionKeys.delete(handleKey);
389
+ systemPromptSent.delete(handleKey);
390
+ storedSystemPrompts.delete(handleKey);
391
+ },
392
+
393
+ stopAll: async (): Promise<void> => {
394
+ sessionKeys.clear();
395
+ systemPromptSent.clear();
396
+ storedSystemPrompts.clear();
397
+ },
398
+
399
+ hasSession: (_sessionId: string): boolean => {
400
+ return false;
401
+ },
402
+ };
403
+ }
404
+
405
+ // ---------------------------------------------------------------------------
406
+ // Driver
407
+ // ---------------------------------------------------------------------------
408
+
409
+ export const CursorCliDriver: ProviderDriver<CursorCliConfig> = {
410
+ driverKind: DRIVER_KIND,
411
+ metadata: {
412
+ displayName: "Cursor CLI",
413
+ supportsMultipleInstances: false,
414
+ },
415
+ configSchema: cursorCliConfigSchema,
416
+ defaultConfig: () => ({ binary: "agent", enabled: true }),
417
+
418
+ probe: async (config: CursorCliConfig): Promise<ProviderProbeResult> => {
419
+ const binary = resolveBinary(config.binary);
420
+ if (!binary) {
421
+ return {
422
+ available: false,
423
+ reason: `Binary '${config.binary}' not found on PATH`,
424
+ };
425
+ }
426
+ return { available: true };
427
+ },
428
+
429
+ create: async (
430
+ input: ProviderDriverCreateInput<CursorCliConfig>,
431
+ ): Promise<ProviderInstance> => {
432
+ const { instanceId, config, cwd } = input;
433
+
434
+ let discoveredModels: DiscoveredModel[] = [];
435
+ try {
436
+ const detailed = await discoverCliModelsDetailed("cursor");
437
+ discoveredModels = detailed.map((m) => ({ id: m.id, name: m.name }));
438
+ } catch (err) {
439
+ fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
440
+ `model discovery failed: ${err instanceof Error ? err.message : String(err)}`);
441
+ }
442
+
443
+ const managed = makeManagedSnapshot({
444
+ skipInitialRefresh: true,
445
+ initialSnapshot: () =>
446
+ buildInitialSnapshot(true, discoveredModels),
447
+ checkProvider: async () => {
448
+ const binary = resolveBinary(config.binary);
449
+ if (!binary) {
450
+ return buildInitialSnapshot(false, [], undefined, `Binary '${config.binary}' not found`);
451
+ }
452
+ try {
453
+ const next = await discoverCliModelsDetailed("cursor");
454
+ const models = next.map((m) => ({ id: m.id, name: m.name }));
455
+ return buildInitialSnapshot(true, models);
456
+ } catch {
457
+ return buildInitialSnapshot(true, discoveredModels);
458
+ }
459
+ },
460
+ getSettings: () => config,
461
+ haveSettingsChanged: (prev, next) =>
462
+ prev.binary !== next.binary || prev.enabled !== next.enabled,
463
+ });
464
+
465
+ const adapter = createCursorCliAdapter(config, cwd, instanceId);
466
+
467
+ return {
468
+ instanceId,
469
+ driverKind: DRIVER_KIND,
470
+ displayName: input.displayName || "Cursor CLI",
471
+ enabled: input.enabled,
472
+ snapshot: {
473
+ getSnapshot: managed.getSnapshot,
474
+ refresh: managed.refresh,
475
+ dispose: managed.dispose,
476
+ },
477
+ adapter,
478
+ dispose: async () => {
479
+ managed.dispose();
480
+ await adapter.stopAll();
481
+ },
482
+ };
483
+ },
484
+ };