claude-overnight 1.17.3 → 1.18.0
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/dist/cursor-models.d.ts +14 -0
- package/dist/cursor-models.js +50 -0
- package/dist/index.js +2 -1
- package/dist/models.d.ts +27 -0
- package/dist/models.js +60 -0
- package/dist/planner-query.d.ts +2 -3
- package/dist/planner-query.js +3 -31
- package/dist/planner.js +6 -4
- package/dist/providers.js +97 -34
- package/dist/state.js +2 -1
- package/package.json +2 -2
- package/plugins/claude-overnight/.claude-plugin/plugin.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const CURSOR_PRIORITY_MODELS: Array<{
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
hint: string;
|
|
5
|
+
}>;
|
|
6
|
+
export declare const CURSOR_KNOWN_MODELS: Array<{
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
hint: string;
|
|
10
|
+
}>;
|
|
11
|
+
/** All known model IDs as a Set for quick membership checks. */
|
|
12
|
+
export declare const KNOWN_CURSOR_MODEL_IDS: Set<string>;
|
|
13
|
+
/** Display hint for a model ID — known ones get a hint, unknowns get a generic label. */
|
|
14
|
+
export declare function cursorModelHint(modelId: string): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// ── Cursor model constants ──
|
|
2
|
+
//
|
|
3
|
+
// Hardcoded model IDs returned by the Cursor API Proxy. These serve as a
|
|
4
|
+
// fallback when `agent --list-models` crashes (the bundled Node.js binary
|
|
5
|
+
// segfaults on some setups — the proxy inherits this bug).
|
|
6
|
+
//
|
|
7
|
+
// Update this list when Cursor adds/removes models. Run:
|
|
8
|
+
// node ~/.local/share/cursor-agent/versions/*/index.js --list-models
|
|
9
|
+
// to get the current list.
|
|
10
|
+
//
|
|
11
|
+
// The `priority` models always appear at the top of the picker in this order.
|
|
12
|
+
// `known` models appear after them. Anything the proxy returns dynamically
|
|
13
|
+
// that isn't in this list goes into a "more..." sub-menu.
|
|
14
|
+
import { CURSOR_MODEL_HINTS } from "./models.js";
|
|
15
|
+
export const CURSOR_PRIORITY_MODELS = [
|
|
16
|
+
{ id: "composer-2", label: "composer-2", hint: "Cursor Composer 2 — latest, strongest Cursor model" },
|
|
17
|
+
{ id: "composer-2-fast", label: "composer-2-fast", hint: "Cursor Composer 2 Fast — faster, cheaper variant" },
|
|
18
|
+
{ id: "auto", label: "auto", hint: "auto-delegates to the best available model" },
|
|
19
|
+
];
|
|
20
|
+
export const CURSOR_KNOWN_MODELS = [
|
|
21
|
+
{ id: "composer", label: "composer", hint: "Cursor Composer — previous generation" },
|
|
22
|
+
];
|
|
23
|
+
/** All known model IDs as a Set for quick membership checks. */
|
|
24
|
+
export const KNOWN_CURSOR_MODEL_IDS = new Set([
|
|
25
|
+
...CURSOR_PRIORITY_MODELS.map(m => m.id),
|
|
26
|
+
...CURSOR_KNOWN_MODELS.map(m => m.id),
|
|
27
|
+
]);
|
|
28
|
+
/** Display hint for a model ID — known ones get a hint, unknowns get a generic label. */
|
|
29
|
+
export function cursorModelHint(modelId) {
|
|
30
|
+
const m = modelId.toLowerCase();
|
|
31
|
+
for (const entry of [...CURSOR_PRIORITY_MODELS, ...CURSOR_KNOWN_MODELS]) {
|
|
32
|
+
if (entry.id === m)
|
|
33
|
+
return entry.hint;
|
|
34
|
+
}
|
|
35
|
+
if (m.startsWith("composer"))
|
|
36
|
+
return "Cursor Composer model";
|
|
37
|
+
if (m.includes("opus"))
|
|
38
|
+
return CURSOR_MODEL_HINTS.opus;
|
|
39
|
+
if (m.includes("sonnet"))
|
|
40
|
+
return CURSOR_MODEL_HINTS.sonnet;
|
|
41
|
+
if (m.includes("haiku"))
|
|
42
|
+
return CURSOR_MODEL_HINTS.haiku;
|
|
43
|
+
if (m.startsWith("gpt-5"))
|
|
44
|
+
return "GPT model via Cursor";
|
|
45
|
+
if (m.startsWith("gemini"))
|
|
46
|
+
return "Gemini model via Cursor";
|
|
47
|
+
if (m.startsWith("grok"))
|
|
48
|
+
return "Grok model via Cursor";
|
|
49
|
+
return "Cursor model";
|
|
50
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
|
10
10
|
import { Swarm } from "./swarm.js";
|
|
11
11
|
import { planTasks, refinePlan, identifyThemes, buildThinkingTasks, orchestrate, salvageFromFile } from "./planner.js";
|
|
12
12
|
import { detectModelTier, setPlannerEnvResolver } from "./planner-query.js";
|
|
13
|
+
import { DEFAULT_MODEL } from "./models.js";
|
|
13
14
|
import { pickModel, loadProviders, preflightProvider, buildEnvResolver, healthCheckCursorProxy, PROXY_DEFAULT_URL, isCursorProxyProvider } from "./providers.js";
|
|
14
15
|
import { RunDisplay } from "./ui.js";
|
|
15
16
|
import { renderSummary } from "./render.js";
|
|
@@ -665,7 +666,7 @@ async function main() {
|
|
|
665
666
|
const defaultModel = activeProvider?.model
|
|
666
667
|
?? models[0]?.value
|
|
667
668
|
?? savedForCLI.find(p => p !== activeProvider)?.model
|
|
668
|
-
??
|
|
669
|
+
?? DEFAULT_MODEL;
|
|
669
670
|
workerModel = cliFlags.model ?? fileCfg?.model ?? defaultModel;
|
|
670
671
|
plannerModel = activeProvider?.model ?? models[0]?.value ?? workerModel;
|
|
671
672
|
// Auto-resolve a saved custom provider if --model matches its id or model id.
|
package/dist/models.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const MODEL_TIER_OPUS = "opus";
|
|
2
|
+
export declare const MODEL_TIER_SONNET = "sonnet";
|
|
3
|
+
export declare const MODEL_TIER_HAIKU = "haiku";
|
|
4
|
+
export declare const MODEL_TIER_UNKNOWN = "unknown";
|
|
5
|
+
export type ModelTier = typeof MODEL_TIER_OPUS | typeof MODEL_TIER_SONNET | typeof MODEL_TIER_HAIKU | typeof MODEL_TIER_UNKNOWN;
|
|
6
|
+
export interface TierDetectionRule {
|
|
7
|
+
match: (model: string) => boolean;
|
|
8
|
+
tier: ModelTier;
|
|
9
|
+
}
|
|
10
|
+
export declare const TIER_DETECTION_RULES: TierDetectionRule[];
|
|
11
|
+
export declare function detectModelTier(model: string): ModelTier;
|
|
12
|
+
export declare const MODEL_CAPABILITY_DESCRIPTIONS: Record<ModelTier, string>;
|
|
13
|
+
export declare const UNKNOWN_MODEL_CAPABILITIES: Record<string, string>;
|
|
14
|
+
export declare function modelCapabilityBlock(model: string): string;
|
|
15
|
+
export declare const DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
16
|
+
export declare const FALLBACK_MODEL = "claude-opus-4-6";
|
|
17
|
+
export declare const PLANNER_THRESHOLDS: {
|
|
18
|
+
opus: {
|
|
19
|
+
small: number;
|
|
20
|
+
medium: number;
|
|
21
|
+
};
|
|
22
|
+
default: {
|
|
23
|
+
small: number;
|
|
24
|
+
medium: number;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export declare const CURSOR_MODEL_HINTS: Record<string, string>;
|
package/dist/models.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// ── Model tier constants ──
|
|
2
|
+
export const MODEL_TIER_OPUS = "opus";
|
|
3
|
+
export const MODEL_TIER_SONNET = "sonnet";
|
|
4
|
+
export const MODEL_TIER_HAIKU = "haiku";
|
|
5
|
+
export const MODEL_TIER_UNKNOWN = "unknown";
|
|
6
|
+
export const TIER_DETECTION_RULES = [
|
|
7
|
+
{ match: m => m === "default" || m.includes("opus"), tier: MODEL_TIER_OPUS },
|
|
8
|
+
{ match: m => m.includes("sonnet"), tier: MODEL_TIER_SONNET },
|
|
9
|
+
{ match: m => m.includes("haiku"), tier: MODEL_TIER_HAIKU },
|
|
10
|
+
{ match: m => m === "auto", tier: MODEL_TIER_UNKNOWN },
|
|
11
|
+
{ match: m => m.startsWith("composer"), tier: MODEL_TIER_SONNET },
|
|
12
|
+
{ match: m => m.startsWith("gpt-5") || m.startsWith("gemini") || m.startsWith("grok"), tier: MODEL_TIER_SONNET },
|
|
13
|
+
];
|
|
14
|
+
export function detectModelTier(model) {
|
|
15
|
+
const m = model.toLowerCase();
|
|
16
|
+
for (const rule of TIER_DETECTION_RULES) {
|
|
17
|
+
if (rule.match(m))
|
|
18
|
+
return rule.tier;
|
|
19
|
+
}
|
|
20
|
+
return MODEL_TIER_UNKNOWN;
|
|
21
|
+
}
|
|
22
|
+
// ── Capability descriptions ──
|
|
23
|
+
export const MODEL_CAPABILITY_DESCRIPTIONS = {
|
|
24
|
+
opus: "Each agent runs Claude Opus with 1M context -- a powerhouse. It can own entire epics, do deep codebase research, make architectural decisions, implement complex multi-file systems end-to-end, use browser tools for analysis, and deliver expert-level work. These agents can work for 30+ minutes on the most complex tasks. Do NOT waste them on trivial edits -- give them ownership and autonomy.",
|
|
25
|
+
sonnet: "Each agent runs Claude Sonnet -- capable of substantial implementation, refactoring, testing, and design work. Can work autonomously for 10-20 minutes on complex tasks. Give agents meaningful scope -- not just single-line edits.",
|
|
26
|
+
haiku: "Each agent runs Claude Haiku -- fast and efficient, best for focused, well-specified tasks. Be explicit about files, functions, and expected changes. Keep tasks scoped to a clear, concrete deliverable.",
|
|
27
|
+
unknown: "", // handled by UNKNOWN_MODEL_CAPABILITIES below
|
|
28
|
+
};
|
|
29
|
+
export const UNKNOWN_MODEL_CAPABILITIES = {
|
|
30
|
+
composer: "Each agent runs a Cursor Composer model with full codebase access. Capable of focused implementation work. Be explicit about files, functions, and expected changes.",
|
|
31
|
+
"gpt-5": "Each agent runs a GPT model via Cursor with full codebase access. Capable of focused implementation work. Be explicit about files, functions, and expected changes.",
|
|
32
|
+
gemini: "Each agent runs a Gemini model via Cursor with full codebase access. Be explicit about files, functions, and expected changes.",
|
|
33
|
+
grok: "Each agent runs a Grok model via Cursor with full codebase access. Be explicit about files, functions, and expected changes.",
|
|
34
|
+
};
|
|
35
|
+
export function modelCapabilityBlock(model) {
|
|
36
|
+
const tier = detectModelTier(model);
|
|
37
|
+
const cap = MODEL_CAPABILITY_DESCRIPTIONS[tier];
|
|
38
|
+
if (cap)
|
|
39
|
+
return cap;
|
|
40
|
+
const m = model.toLowerCase();
|
|
41
|
+
for (const [prefix, desc] of Object.entries(UNKNOWN_MODEL_CAPABILITIES)) {
|
|
42
|
+
if (m.startsWith(prefix))
|
|
43
|
+
return desc;
|
|
44
|
+
}
|
|
45
|
+
return `Each agent has full codebase access and can work autonomously.`;
|
|
46
|
+
}
|
|
47
|
+
// ── Default / fallback models ──
|
|
48
|
+
export const DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
49
|
+
export const FALLBACK_MODEL = "claude-opus-4-6"; // used for planner + worker recovery
|
|
50
|
+
// ── Planner thresholds (opus-tuned vs default) ──
|
|
51
|
+
export const PLANNER_THRESHOLDS = {
|
|
52
|
+
opus: { small: 5, medium: 30 },
|
|
53
|
+
default: { small: 15, medium: 50 },
|
|
54
|
+
};
|
|
55
|
+
// ── Cursor model hints ──
|
|
56
|
+
export const CURSOR_MODEL_HINTS = {
|
|
57
|
+
opus: "Opus-tier model via Cursor",
|
|
58
|
+
sonnet: "Sonnet-tier model via Cursor",
|
|
59
|
+
haiku: "Haiku-tier model via Cursor (fast)",
|
|
60
|
+
};
|
package/dist/planner-query.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Task, PermMode, RateLimitWindow } from "./types.js";
|
|
2
|
+
import { detectModelTier, modelCapabilityBlock } from "./models.js";
|
|
3
|
+
export { detectModelTier, modelCapabilityBlock };
|
|
2
4
|
/**
|
|
3
5
|
* Logging callback used by planner/steering queries.
|
|
4
6
|
* `kind` distinguishes ephemeral status updates (heartbeat ticker) from
|
|
@@ -25,9 +27,6 @@ export interface PlannerOpts {
|
|
|
25
27
|
};
|
|
26
28
|
}
|
|
27
29
|
export declare function setPlannerEnvResolver(fn: ((model?: string) => Record<string, string> | undefined) | undefined): void;
|
|
28
|
-
export type ModelTier = "opus" | "sonnet" | "haiku" | "unknown";
|
|
29
|
-
export declare function detectModelTier(model: string): ModelTier;
|
|
30
|
-
export declare function modelCapabilityBlock(model: string): string;
|
|
31
30
|
export declare function getTotalPlannerCost(): number;
|
|
32
31
|
export declare function getPlannerRateLimitInfo(): PlannerRateLimitInfo;
|
|
33
32
|
export declare function runPlannerQuery(prompt: string, opts: PlannerOpts, onLog: PlannerLog): Promise<string>;
|
package/dist/planner-query.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
2
2
|
import { readFileSync } from "fs";
|
|
3
3
|
import { NudgeError } from "./types.js";
|
|
4
|
+
import { detectModelTier, modelCapabilityBlock } from "./models.js";
|
|
5
|
+
// Re-export for consumers that import from planner-query (steering.ts, index.ts).
|
|
6
|
+
export { detectModelTier, modelCapabilityBlock };
|
|
4
7
|
// ── Shared env resolver (set once at run start, used by every planner query) ──
|
|
5
8
|
//
|
|
6
9
|
// Swarm and planner calls share a model→env map so a custom provider configured
|
|
@@ -10,37 +13,6 @@ let _envResolver;
|
|
|
10
13
|
export function setPlannerEnvResolver(fn) {
|
|
11
14
|
_envResolver = fn;
|
|
12
15
|
}
|
|
13
|
-
export function detectModelTier(model) {
|
|
14
|
-
const m = model.toLowerCase();
|
|
15
|
-
if (m === "default" || m.includes("opus"))
|
|
16
|
-
return "opus";
|
|
17
|
-
if (m.includes("sonnet"))
|
|
18
|
-
return "sonnet";
|
|
19
|
-
if (m.includes("haiku"))
|
|
20
|
-
return "haiku";
|
|
21
|
-
// Cursor API Proxy models
|
|
22
|
-
if (m === "auto")
|
|
23
|
-
return "unknown";
|
|
24
|
-
if (m.startsWith("composer"))
|
|
25
|
-
return "sonnet";
|
|
26
|
-
return "unknown";
|
|
27
|
-
}
|
|
28
|
-
export function modelCapabilityBlock(model) {
|
|
29
|
-
switch (detectModelTier(model)) {
|
|
30
|
-
case "opus":
|
|
31
|
-
return `Each agent runs Claude Opus with 1M context -- a powerhouse. It can own entire epics, do deep codebase research, make architectural decisions, implement complex multi-file systems end-to-end, use browser tools for analysis, and deliver expert-level work. These agents can work for 30+ minutes on the most complex tasks. Do NOT waste them on trivial edits -- give them ownership and autonomy.`;
|
|
32
|
-
case "sonnet":
|
|
33
|
-
return `Each agent runs Claude Sonnet -- capable of substantial implementation, refactoring, testing, and design work. Can work autonomously for 10-20 minutes on complex tasks. Give agents meaningful scope -- not just single-line edits.`;
|
|
34
|
-
case "haiku":
|
|
35
|
-
return `Each agent runs Claude Haiku -- fast and efficient, best for focused, well-specified tasks. Be explicit about files, functions, and expected changes. Keep each task scoped to a clear, concrete deliverable.`;
|
|
36
|
-
default:
|
|
37
|
-
// Cursor API Proxy or unknown model — generic but mention Cursor context
|
|
38
|
-
if (model.toLowerCase().startsWith("composer") || model.toLowerCase() === "auto") {
|
|
39
|
-
return `Each agent runs a Cursor model with full codebase access. Capable of focused implementation work. Be explicit about files, functions, and expected changes.`;
|
|
40
|
-
}
|
|
41
|
-
return `Each agent has full codebase access and can work autonomously.`;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
16
|
// ── Rate limit tracking ──
|
|
45
17
|
const RATE_LIMIT_PATTERNS = ["rate", "limit", "overloaded", "429", "hit your limit", "too many"];
|
|
46
18
|
function isRateLimitError(err) {
|
package/dist/planner.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
|
-
import { runPlannerQuery, extractTaskJson, attemptJsonParse, postProcess
|
|
2
|
+
import { runPlannerQuery, extractTaskJson, attemptJsonParse, postProcess } from "./planner-query.js";
|
|
3
|
+
import { detectModelTier, modelCapabilityBlock, MODEL_TIER_HAIKU, MODEL_TIER_OPUS, PLANNER_THRESHOLDS } from "./models.js";
|
|
3
4
|
// Resilience: if the planner query throws but the agent already wrote valid
|
|
4
5
|
// tasks to `outFile` (via its Write tool), salvage them instead of discarding
|
|
5
6
|
// expensive work. Returns salvaged tasks on success, null if nothing usable on
|
|
@@ -62,7 +63,7 @@ function plannerPrompt(objective, workerModel, budget, concurrency, flexNote) {
|
|
|
62
63
|
? `\n- ${concurrency} agents run in parallel -- tasks that run concurrently must touch DIFFERENT files to avoid merge conflicts`
|
|
63
64
|
: "";
|
|
64
65
|
const flexLine = flexNote ? `\n\n${flexNote}` : "";
|
|
65
|
-
if (tier ===
|
|
66
|
+
if (tier === MODEL_TIER_HAIKU) {
|
|
66
67
|
return `You are a task coordinator for a parallel agent system. Analyze this codebase and break the following objective into independent tasks.
|
|
67
68
|
|
|
68
69
|
Objective: ${objective}
|
|
@@ -84,8 +85,9 @@ Respond with ONLY a JSON object (no markdown fences):
|
|
|
84
85
|
]
|
|
85
86
|
}`;
|
|
86
87
|
}
|
|
87
|
-
const
|
|
88
|
-
const
|
|
88
|
+
const thresholds = tier === MODEL_TIER_OPUS ? PLANNER_THRESHOLDS.opus : PLANNER_THRESHOLDS.default;
|
|
89
|
+
const smallThreshold = thresholds.small;
|
|
90
|
+
const mediumThreshold = thresholds.medium;
|
|
89
91
|
if (b <= smallThreshold) {
|
|
90
92
|
return `You are a task coordinator for a parallel agent system. Analyze this codebase and break the following objective into independent tasks.
|
|
91
93
|
|
package/dist/providers.js
CHANGED
|
@@ -6,6 +6,8 @@ import chalk from "chalk";
|
|
|
6
6
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
7
7
|
import { ask, select, selectKey } from "./cli.js";
|
|
8
8
|
import { getBearerToken, clearTokenCache } from "./auth.js";
|
|
9
|
+
import { DEFAULT_MODEL } from "./models.js";
|
|
10
|
+
import { CURSOR_PRIORITY_MODELS, CURSOR_KNOWN_MODELS, KNOWN_CURSOR_MODEL_IDS, cursorModelHint, } from "./cursor-models.js";
|
|
9
11
|
// ── Store ──
|
|
10
12
|
const STORE_PATH = join(homedir(), ".claude", "claude-overnight", "providers.json");
|
|
11
13
|
export function getStorePath() { return STORE_PATH; }
|
|
@@ -99,8 +101,8 @@ export async function pickModel(label, anthropicModels, currentModelId) {
|
|
|
99
101
|
// entry so the user isn't trapped if they cancel the Other… form.
|
|
100
102
|
if (anthropicModels.length === 0) {
|
|
101
103
|
items.push({
|
|
102
|
-
name:
|
|
103
|
-
value: { kind: "anthropic", model: { value:
|
|
104
|
+
name: DEFAULT_MODEL,
|
|
105
|
+
value: { kind: "anthropic", model: { value: DEFAULT_MODEL, displayName: DEFAULT_MODEL, description: "default (model list unavailable)" } },
|
|
104
106
|
hint: "default -- Anthropic model list unavailable",
|
|
105
107
|
});
|
|
106
108
|
}
|
|
@@ -291,24 +293,48 @@ export async function fetchCursorModels(baseUrl = PROXY_DEFAULT_URL) {
|
|
|
291
293
|
}
|
|
292
294
|
}
|
|
293
295
|
/**
|
|
294
|
-
*
|
|
296
|
+
* Try to fetch live Cursor model IDs. Falls back to an empty array — the
|
|
297
|
+
* caller merges with known constants, so the picker always has content.
|
|
298
|
+
*
|
|
299
|
+
* NOTE: `agent --list-models` segfaults with its bundled Node.js binary
|
|
300
|
+
* (exit 139). We work around this by running with the system `node` instead.
|
|
295
301
|
*/
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
302
|
+
async function fetchLiveCursorModels() {
|
|
303
|
+
// Try the proxy first (works when the bundled node doesn't crash)
|
|
304
|
+
const proxyModels = await fetchCursorModels();
|
|
305
|
+
if (proxyModels.length > 0)
|
|
306
|
+
return proxyModels;
|
|
307
|
+
// Fallback: run the cursor-agent CLI with system node
|
|
308
|
+
// Find the agent binary via command -v (alias-safe), then locate its index.js.
|
|
309
|
+
try {
|
|
310
|
+
// command -v handles symlinks and doesn't expand shell aliases
|
|
311
|
+
const agentPath = execSync("command -v agent 2>/dev/null || command -v cursor-agent 2>/dev/null", {
|
|
312
|
+
timeout: 3_000, encoding: "utf-8", shell: "bash",
|
|
313
|
+
}).trim();
|
|
314
|
+
if (!agentPath)
|
|
315
|
+
return [];
|
|
316
|
+
// Resolve the directory (realpath handles symlinks like agent → cursor-agent)
|
|
317
|
+
const dir = execSync(`dirname "$(realpath "${agentPath}")"`, {
|
|
318
|
+
timeout: 3_000, encoding: "utf-8", shell: "bash",
|
|
319
|
+
}).trim();
|
|
320
|
+
// The bundled index.js lives in the same directory as the agent script
|
|
321
|
+
const indexPath = `${dir}/index.js`;
|
|
322
|
+
const raw = execSync(`node "${indexPath}" --list-models 2>/dev/null`, {
|
|
323
|
+
timeout: 10_000, encoding: "utf-8",
|
|
324
|
+
});
|
|
325
|
+
// Strip ANSI escape codes (cursor uses \x1B[2K\r etc.)
|
|
326
|
+
const out = raw.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
|
|
327
|
+
// Parse lines like "composer-2-fast - Composer 2 Fast"
|
|
328
|
+
const ids = [];
|
|
329
|
+
for (const line of out.split("\n")) {
|
|
330
|
+
const match = line.match(/^([A-Za-z0-9][A-Za-z0-9._:/-]*)\s+-\s+/);
|
|
331
|
+
if (match)
|
|
332
|
+
ids.push(match[1]);
|
|
333
|
+
}
|
|
334
|
+
return ids;
|
|
335
|
+
}
|
|
336
|
+
catch { }
|
|
337
|
+
return [];
|
|
312
338
|
}
|
|
313
339
|
function setupSteps() {
|
|
314
340
|
return [
|
|
@@ -490,7 +516,34 @@ export async function setupCursorProxy() {
|
|
|
490
516
|
console.log(chalk.yellow("\n Proxy not reachable yet. You can start it later and add it via 'Cursor' in the model picker."));
|
|
491
517
|
return false;
|
|
492
518
|
}
|
|
493
|
-
|
|
519
|
+
/**
|
|
520
|
+
* Build the full list of cursor model picker items. Priority models go first,
|
|
521
|
+
* then known models, then any extra live models we fetched. If there are more
|
|
522
|
+
* than a handful extras, they get a "more..." sub-menu.
|
|
523
|
+
*/
|
|
524
|
+
async function buildCursorPicker() {
|
|
525
|
+
const liveIds = await fetchLiveCursorModels();
|
|
526
|
+
const extra = new Set();
|
|
527
|
+
for (const id of liveIds) {
|
|
528
|
+
if (!KNOWN_CURSOR_MODEL_IDS.has(id))
|
|
529
|
+
extra.add(id);
|
|
530
|
+
}
|
|
531
|
+
const top = [
|
|
532
|
+
...CURSOR_PRIORITY_MODELS.map(m => ({ id: m.id, name: m.label, hint: m.hint })),
|
|
533
|
+
...CURSOR_KNOWN_MODELS.map(m => ({ id: m.id, name: m.label, hint: m.hint })),
|
|
534
|
+
];
|
|
535
|
+
// Only a few extras? show them inline. Otherwise defer to "more...".
|
|
536
|
+
const MORE_THRESHOLD = 6;
|
|
537
|
+
const more = [...extra].sort().map(id => ({
|
|
538
|
+
id,
|
|
539
|
+
name: id,
|
|
540
|
+
hint: cursorModelHint(id),
|
|
541
|
+
}));
|
|
542
|
+
if (more.length <= MORE_THRESHOLD) {
|
|
543
|
+
return { top: [...top, ...more], more: [] };
|
|
544
|
+
}
|
|
545
|
+
return { top, more };
|
|
546
|
+
}
|
|
494
547
|
async function pickCursorModel() {
|
|
495
548
|
console.log(chalk.dim("\n Cursor API Proxy Models"));
|
|
496
549
|
console.log(chalk.dim(" " + "─".repeat(40)));
|
|
@@ -522,29 +575,39 @@ async function pickCursorModel() {
|
|
|
522
575
|
return null;
|
|
523
576
|
}
|
|
524
577
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
578
|
+
const { top, more } = await buildCursorPicker();
|
|
579
|
+
// If there are more models available, add a "more…" entry
|
|
580
|
+
const items = top.map(m => ({
|
|
581
|
+
name: m.name,
|
|
582
|
+
value: m.id,
|
|
583
|
+
hint: m.hint,
|
|
584
|
+
}));
|
|
585
|
+
let hasMore = more.length > 0;
|
|
586
|
+
if (hasMore) {
|
|
587
|
+
items.push({ name: chalk.gray("more…"), value: "__more__", hint: `${more.length} additional models` });
|
|
530
588
|
}
|
|
531
|
-
const picked = await select(" Select a Cursor model:",
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
hint:
|
|
535
|
-
|
|
589
|
+
const picked = await select(" Select a Cursor model:", items, 0);
|
|
590
|
+
// Handle "more…" sub-menu
|
|
591
|
+
if (picked === "__more__") {
|
|
592
|
+
const moreItems = more.map(m => ({ name: m.name, value: m.id, hint: m.hint }));
|
|
593
|
+
const morePicked = await select(" More Cursor models:", moreItems, 0);
|
|
594
|
+
return saveCursorPick(morePicked);
|
|
595
|
+
}
|
|
596
|
+
return saveCursorPick(picked);
|
|
597
|
+
}
|
|
598
|
+
function saveCursorPick(modelId) {
|
|
536
599
|
const existingKey = loadProviders().find(p => p.id === CURSOR_KEY_PROVIDER_ID)?.cursorApiKey;
|
|
537
600
|
const provider = {
|
|
538
|
-
id: `cursor-${
|
|
539
|
-
displayName: `Cursor: ${
|
|
601
|
+
id: `cursor-${modelId}`,
|
|
602
|
+
displayName: `Cursor: ${modelId}`,
|
|
540
603
|
baseURL: PROXY_DEFAULT_URL,
|
|
541
|
-
model:
|
|
604
|
+
model: modelId,
|
|
542
605
|
cursorProxy: true,
|
|
543
606
|
...(existingKey ? { cursorApiKey: existingKey } : {}),
|
|
544
607
|
};
|
|
545
608
|
saveProvider(provider);
|
|
546
609
|
console.log(chalk.green(` ✓ Saved as provider: ${provider.displayName}`));
|
|
547
|
-
return { model:
|
|
610
|
+
return { model: modelId, providerId: provider.id, provider };
|
|
548
611
|
}
|
|
549
612
|
/**
|
|
550
613
|
* Build a single resolver that swarm.ts and planner-query.ts share. Maps a
|
package/dist/state.js
CHANGED
|
@@ -3,6 +3,7 @@ import { execSync } from "child_process";
|
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { forceMergeOverlay } from "./merge.js";
|
|
6
|
+
import { FALLBACK_MODEL } from "./models.js";
|
|
6
7
|
// ── File I/O helpers ──
|
|
7
8
|
export function readMdDir(dir) {
|
|
8
9
|
try {
|
|
@@ -267,7 +268,7 @@ export function backfillOrphanedPlans(rootDir, filterCwd) {
|
|
|
267
268
|
id: d,
|
|
268
269
|
objective: `(recovered pre-1.11.7 plan · ${taskCount} tasks)`,
|
|
269
270
|
budget: taskCount, remaining: taskCount,
|
|
270
|
-
workerModel:
|
|
271
|
+
workerModel: FALLBACK_MODEL, plannerModel: FALLBACK_MODEL,
|
|
271
272
|
concurrency: 5, permissionMode: "bypassPermissions",
|
|
272
273
|
flex: false, useWorktrees: true, mergeStrategy: "yolo",
|
|
273
274
|
allowExtraUsage: false,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-overnight",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Background lane for your Claude Max plan. Parallel Claude Agent SDK sessions in git worktrees with a usage cap that reserves headroom for your interactive Claude Code. Crash-safe resume.
|
|
3
|
+
"version": "1.18.0",
|
|
4
|
+
"description": "Background lane for your Claude Max plan. Parallel Claude Agent SDK sessions in git worktrees with a usage cap that reserves headroom for your interactive Claude Code. Crash-safe resume. Provider-agnostic model catalog with capability-based planning.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"claude-overnight": "dist/bin.js"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-overnight",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Claude Code skill for understanding, installing, and inspecting claude-overnight runs -- parallel Claude agents in git worktrees with thinking waves, multi-wave steering, and crash-safe resume. Supports Cursor API Proxy, Qwen, OpenRouter.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Francesco Fornace"
|