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.
- package/README.md +808 -0
- package/agents/api-documenter.md +32 -0
- package/agents/bash-expert.md +53 -0
- package/agents/code-reviewer-docs.md +145 -0
- package/agents/code-reviewer-guidelines.md +97 -0
- package/agents/code-reviewer-quality.md +142 -0
- package/agents/code-reviewer-security.md +107 -0
- package/agents/code-reviewer-spec.md +164 -0
- package/agents/debugger.md +63 -0
- package/agents/docker-expert.md +40 -0
- package/agents/docs-fetcher.md +48 -0
- package/agents/git-expert.md +63 -0
- package/agents/github-expert.md +83 -0
- package/agents/go-expert.md +62 -0
- package/agents/issue-reviewer-feasibility.md +60 -0
- package/agents/issue-reviewer-scope.md +63 -0
- package/agents/issue-reviewer-spec.md +77 -0
- package/agents/java-expert.md +31 -0
- package/agents/jenkins-expert.md +38 -0
- package/agents/kubernetes-expert.md +40 -0
- package/agents/planner.md +42 -0
- package/agents/python-expert.md +61 -0
- package/agents/reviewer.md +26 -0
- package/agents/scout.md +40 -0
- package/agents/security-auditor.md +254 -0
- package/agents/technical-documentation-writer.md +37 -0
- package/agents/test-automator.md +55 -0
- package/agents/test-runner.md +47 -0
- package/agents/ts-expert.md +23 -0
- package/agents/worker.md +30 -0
- package/extensions/acpx-provider/index.ts +104 -0
- package/extensions/acpx-provider/load-runtime.ts +99 -0
- package/extensions/acpx-provider/runtime-models.ts +40 -0
- package/extensions/cli-provider/agents/claude.ts +128 -0
- package/extensions/cli-provider/agents/cursor.ts +84 -0
- package/extensions/cli-provider/agents/gemini.ts +100 -0
- package/extensions/cli-provider/discover.ts +77 -0
- package/extensions/cli-provider/index.ts +57 -0
- package/extensions/cli-provider/parsers.ts +304 -0
- package/extensions/cli-provider/providers.ts +58 -0
- package/extensions/cli-provider/runner.ts +225 -0
- package/extensions/cli-provider/runtime-models.ts +32 -0
- package/extensions/cli-provider/session-reaper.ts +122 -0
- package/extensions/cli-provider/sessions.ts +533 -0
- package/extensions/cli-provider/shared/discover-cache.ts +235 -0
- package/extensions/cli-provider/types.ts +48 -0
- package/extensions/coms/coms-net-server.ts +1642 -0
- package/extensions/coms/coms-net-wrapper.ts +521 -0
- package/extensions/coms/coms-net.ts +1589 -0
- package/extensions/coms/coms-p2p.ts +1879 -0
- package/extensions/coms/coms-shared.ts +510 -0
- package/extensions/coms/coms-wrapper.ts +138 -0
- package/extensions/coms/index.ts +20 -0
- package/extensions/coms/themeMap.ts +147 -0
- package/extensions/image-gen/image-gen.ts +332 -0
- package/extensions/image-gen/index.ts +14 -0
- package/extensions/orchestrator/agents.ts +134 -0
- package/extensions/orchestrator/ask-user.ts +293 -0
- package/extensions/orchestrator/async-agents.ts +1037 -0
- package/extensions/orchestrator/async-capability.ts +129 -0
- package/extensions/orchestrator/async-runner.ts +324 -0
- package/extensions/orchestrator/async-status-parse.ts +31 -0
- package/extensions/orchestrator/async-status-ui.ts +201 -0
- package/extensions/orchestrator/async-wait.ts +36 -0
- package/extensions/orchestrator/btw.ts +210 -0
- package/extensions/orchestrator/cron-status-format.ts +126 -0
- package/extensions/orchestrator/cron-status-ui.ts +132 -0
- package/extensions/orchestrator/cron.ts +730 -0
- package/extensions/orchestrator/dreaming.ts +355 -0
- package/extensions/orchestrator/enforcement-helpers.ts +530 -0
- package/extensions/orchestrator/enforcement-rules.ts +329 -0
- package/extensions/orchestrator/enforcement.ts +828 -0
- package/extensions/orchestrator/extended-autocomplete.ts +593 -0
- package/extensions/orchestrator/git-helpers.ts +420 -0
- package/extensions/orchestrator/github-autocomplete.ts +250 -0
- package/extensions/orchestrator/icons.ts +17 -0
- package/extensions/orchestrator/index.ts +100 -0
- package/extensions/orchestrator/memory-embeddings.ts +281 -0
- package/extensions/orchestrator/memory-promotion.ts +449 -0
- package/extensions/orchestrator/memory-provenance.ts +80 -0
- package/extensions/orchestrator/memory-query-class.ts +90 -0
- package/extensions/orchestrator/memory-scoring.ts +434 -0
- package/extensions/orchestrator/memory-tools.ts +883 -0
- package/extensions/orchestrator/memory-tree.ts +212 -0
- package/extensions/orchestrator/nvim.ts +169 -0
- package/extensions/orchestrator/overlay-dashboard-utils.ts +25 -0
- package/extensions/orchestrator/overlay-dashboard.ts +512 -0
- package/extensions/orchestrator/pi-config-review-state.ts +303 -0
- package/extensions/orchestrator/preference-extractor.ts +106 -0
- package/extensions/orchestrator/project-settings.ts +468 -0
- package/extensions/orchestrator/promotion-queue.ts +311 -0
- package/extensions/orchestrator/resolve-agent-model.ts +32 -0
- package/extensions/orchestrator/review-ui.ts +204 -0
- package/extensions/orchestrator/rule-placeholders.ts +12 -0
- package/extensions/orchestrator/rules.ts +481 -0
- package/extensions/orchestrator/session-search.ts +243 -0
- package/extensions/orchestrator/session-validation.ts +374 -0
- package/extensions/orchestrator/situation-report.ts +370 -0
- package/extensions/orchestrator/status-bar.ts +31 -0
- package/extensions/orchestrator/status-line.ts +192 -0
- package/extensions/orchestrator/status.ts +110 -0
- package/extensions/orchestrator/subagent-tool.ts +1748 -0
- package/extensions/orchestrator/task-lifecycle.ts +88 -0
- package/extensions/orchestrator/utils.ts +227 -0
- package/extensions/pidash/index.ts +15 -0
- package/extensions/pidash/pidash.ts +941 -0
- package/extensions/pidiff/index.ts +13 -0
- package/extensions/pidiff/pidiff.ts +369 -0
- package/extensions/providers/acpx-driver.ts +479 -0
- package/extensions/providers/built-in-drivers.ts +52 -0
- package/extensions/providers/claude-driver.ts +319 -0
- package/extensions/providers/cursor-acpx-driver.ts +398 -0
- package/extensions/providers/cursor-cli-driver.ts +484 -0
- package/extensions/providers/gemini-driver.ts +302 -0
- package/extensions/providers/index.ts +513 -0
- package/extensions/shared/build-system-prompt.ts +57 -0
- package/extensions/shared/create-runtime-provider.ts +190 -0
- package/extensions/shared/daemon-manager.ts +248 -0
- package/extensions/shared/file-logger.ts +115 -0
- package/extensions/shared/managed-refresh.ts +211 -0
- package/extensions/shared/provider-driver.ts +295 -0
- package/extensions/shared/provider-errors.ts +305 -0
- package/extensions/shared/provider-registry.ts +388 -0
- package/extensions/shared/stream-builder.ts +275 -0
- package/extensions/shared/types.ts +20 -0
- package/extensions/shared/ui/README.md +10 -0
- package/extensions/shared/ui/badge.tsx +52 -0
- package/extensions/shared/ui/button.tsx +58 -0
- package/extensions/shared/ui/collapsible.tsx +21 -0
- package/extensions/shared/ui/dropdown-menu.tsx +266 -0
- package/extensions/shared/ui/input.tsx +20 -0
- package/extensions/shared/ui/popover.tsx +114 -0
- package/extensions/shared/ui/resizable.tsx +48 -0
- package/extensions/shared/ui/scroll-area.tsx +53 -0
- package/extensions/shared/ui/select.tsx +201 -0
- package/extensions/shared/ui/separator.tsx +25 -0
- package/extensions/shared/ui/tooltip.tsx +64 -0
- package/extensions/shared/ws-client.ts +74 -0
- package/package.json +59 -0
- package/prompts/coderabbit-rate-limit.md +95 -0
- package/prompts/create-coms-feature-manager.md +186 -0
- package/prompts/create-skill.md +128 -0
- package/prompts/domain-model.md +109 -0
- package/prompts/external-ai.md +665 -0
- package/prompts/implement-and-review.md +25 -0
- package/prompts/implement.md +26 -0
- package/prompts/issue-review.md +256 -0
- package/prompts/pr-review.md +753 -0
- package/prompts/query-db.md +154 -0
- package/prompts/refine-review.md +208 -0
- package/prompts/release.md +235 -0
- package/prompts/remember.md +33 -0
- package/prompts/review-handler-status.md +27 -0
- package/prompts/review-handler.md +677 -0
- package/prompts/review-local.md +159 -0
- package/prompts/scout-and-plan.md +21 -0
- package/rules/00-orchestrator-core.md +42 -0
- package/rules/05-issue-first-workflow.md +119 -0
- package/rules/10-agent-routing.md +58 -0
- package/rules/15-mcp-launchpad.md +46 -0
- package/rules/20-code-review-loop.md +234 -0
- package/rules/25-documentation-updates.md +15 -0
- package/rules/30-prompt-templates.md +19 -0
- package/rules/35-memory.md +240 -0
- package/rules/40-critical-rules.md +124 -0
- package/rules/45-file-preview.md +29 -0
- package/rules/50-agent-bug-reporting.md +90 -0
- package/rules/55-coms-protocol.md +63 -0
- package/rules/60-task-tracking.md +66 -0
- package/skills/conflict-resolver/SKILL.md +213 -0
- package/skills/skill-creator/SKILL.md +85 -0
- package/skills/systematic-debugging/SKILL.md +353 -0
- package/skills/test-driven-development/SKILL.md +335 -0
- package/skills/update-agents-md/SKILL.md +282 -0
- 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
|
+
};
|