promptpilot 0.1.7 → 0.2.1
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/cli.d.ts +1 -0
- package/dist/cli.js +301 -16
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +144 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { readFileSync, realpathSync } from "fs";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
|
-
import { execSync } from "child_process";
|
|
6
|
+
import { execSync, spawn } from "child_process";
|
|
7
7
|
|
|
8
8
|
// src/errors.ts
|
|
9
9
|
var InvalidPromptError = class extends Error {
|
|
@@ -1417,6 +1417,18 @@ Mode: Ultra compression. Minimize tokens aggressively.` : getOptimizationSystemP
|
|
|
1417
1417
|
};
|
|
1418
1418
|
}
|
|
1419
1419
|
if (!this.client.listModels) {
|
|
1420
|
+
if (isClaudeTiersOnlyTargetSet(availableTargets)) {
|
|
1421
|
+
const selected = selectClaudeTierHeuristic(options.input, options.routingPriority, availableTargets);
|
|
1422
|
+
if (selected) {
|
|
1423
|
+
return {
|
|
1424
|
+
selectedTarget: stripInternalTargetFields(selected),
|
|
1425
|
+
rankedTargets: [{ ...stripInternalTargetFields(selected), rank: 1, reason: "Selected by Claude tier heuristic (no local Qwen router available)." }],
|
|
1426
|
+
routingReason: "Selected by Claude tier heuristic (no local Qwen router available).",
|
|
1427
|
+
routingWarnings: [],
|
|
1428
|
+
routingProvider: "heuristic"
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1420
1432
|
return {
|
|
1421
1433
|
selectedTarget: null,
|
|
1422
1434
|
rankedTargets: [],
|
|
@@ -1441,12 +1453,23 @@ Mode: Ultra compression. Minimize tokens aggressively.` : getOptimizationSystemP
|
|
|
1441
1453
|
routingProvider: null
|
|
1442
1454
|
};
|
|
1443
1455
|
}
|
|
1456
|
+
const claudeTiersOnly = isClaudeTiersOnlyTargetSet(availableTargets);
|
|
1457
|
+
const routingCandidates = claudeTiersOnly ? filterClaudeTierCandidates(availableTargets, options.input, options.routingPriority) : availableTargets;
|
|
1458
|
+
if (routingCandidates.length === 1) {
|
|
1459
|
+
return {
|
|
1460
|
+
selectedTarget: stripInternalTargetFields(routingCandidates[0]),
|
|
1461
|
+
rankedTargets: [{ ...stripInternalTargetFields(routingCandidates[0]), rank: 1, reason: "Selected by Claude tier pre-filter based on prompt signals." }],
|
|
1462
|
+
routingReason: "Selected by Claude tier pre-filter based on prompt signals.",
|
|
1463
|
+
routingWarnings: [],
|
|
1464
|
+
routingProvider: "heuristic"
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1444
1467
|
const response = await this.client.generateJson({
|
|
1445
1468
|
model: routerModel,
|
|
1446
1469
|
timeoutMs: options.input.timeoutMs ?? this.config.timeoutMs,
|
|
1447
1470
|
temperature: 0,
|
|
1448
1471
|
format: "json",
|
|
1449
|
-
systemPrompt: buildDownstreamRoutingSystemPrompt(options.routingPriority, options.workloadBias),
|
|
1472
|
+
systemPrompt: buildDownstreamRoutingSystemPrompt(options.routingPriority, options.workloadBias, claudeTiersOnly),
|
|
1450
1473
|
prompt: JSON.stringify(
|
|
1451
1474
|
{
|
|
1452
1475
|
objective: "Rank the caller-supplied downstream targets for this prompt and choose the best top target.",
|
|
@@ -1458,7 +1481,7 @@ Mode: Ultra compression. Minimize tokens aggressively.` : getOptimizationSystemP
|
|
|
1458
1481
|
targetHints: options.input.targetHints ?? [],
|
|
1459
1482
|
workloadBias: options.workloadBias,
|
|
1460
1483
|
routingPriority: options.routingPriority,
|
|
1461
|
-
candidateTargets:
|
|
1484
|
+
candidateTargets: routingCandidates.map((target) => ({
|
|
1462
1485
|
id: target.id,
|
|
1463
1486
|
provider: target.provider,
|
|
1464
1487
|
model: target.model,
|
|
@@ -1477,7 +1500,7 @@ Mode: Ultra compression. Minimize tokens aggressively.` : getOptimizationSystemP
|
|
|
1477
1500
|
new Set((response.rankedTargetIds ?? []).map((value) => value.trim()).filter(Boolean))
|
|
1478
1501
|
).slice(0, Math.max(1, options.routingTopK));
|
|
1479
1502
|
const rankedTargets = rankedTargetIds.map((id, index) => {
|
|
1480
|
-
const target =
|
|
1503
|
+
const target = routingCandidates.find((candidate) => candidate.id === id);
|
|
1481
1504
|
if (!target) {
|
|
1482
1505
|
return null;
|
|
1483
1506
|
}
|
|
@@ -1488,7 +1511,7 @@ Mode: Ultra compression. Minimize tokens aggressively.` : getOptimizationSystemP
|
|
|
1488
1511
|
};
|
|
1489
1512
|
}).filter((value) => value !== null);
|
|
1490
1513
|
const selectedTargetId = response.selectedTargetId?.trim();
|
|
1491
|
-
const selectedTargetCandidate = (selectedTargetId &&
|
|
1514
|
+
const selectedTargetCandidate = (selectedTargetId && routingCandidates.find((candidate) => candidate.id === selectedTargetId)) ?? (rankedTargets[0] ? routingCandidates.find(
|
|
1492
1515
|
(candidate) => candidate.provider === rankedTargets[0].provider && candidate.model === rankedTargets[0].model && candidate.label === rankedTargets[0].label
|
|
1493
1516
|
) ?? null : null);
|
|
1494
1517
|
if (!selectedTargetCandidate || rankedTargets.length === 0) {
|
|
@@ -1510,6 +1533,18 @@ Mode: Ultra compression. Minimize tokens aggressively.` : getOptimizationSystemP
|
|
|
1510
1533
|
routingProvider: routerModel
|
|
1511
1534
|
};
|
|
1512
1535
|
} catch {
|
|
1536
|
+
if (isClaudeTiersOnlyTargetSet(availableTargets)) {
|
|
1537
|
+
const selected = selectClaudeTierHeuristic(options.input, options.routingPriority, availableTargets);
|
|
1538
|
+
if (selected) {
|
|
1539
|
+
return {
|
|
1540
|
+
selectedTarget: stripInternalTargetFields(selected),
|
|
1541
|
+
rankedTargets: [{ ...stripInternalTargetFields(selected), rank: 1, reason: "Selected by Claude tier heuristic (Qwen routing failed)." }],
|
|
1542
|
+
routingReason: "Selected by Claude tier heuristic (Qwen routing failed).",
|
|
1543
|
+
routingWarnings: ["Qwen downstream routing failed; fell back to Claude tier heuristic."],
|
|
1544
|
+
routingProvider: "heuristic"
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1513
1548
|
return {
|
|
1514
1549
|
selectedTarget: null,
|
|
1515
1550
|
rankedTargets: [],
|
|
@@ -1658,8 +1693,8 @@ function stripInternalTargetFields(target) {
|
|
|
1658
1693
|
latencyRank: target.latencyRank
|
|
1659
1694
|
};
|
|
1660
1695
|
}
|
|
1661
|
-
function buildDownstreamRoutingSystemPrompt(priority, workloadBias) {
|
|
1662
|
-
|
|
1696
|
+
function buildDownstreamRoutingSystemPrompt(priority, workloadBias, claudeTiersOnly = false) {
|
|
1697
|
+
const lines = [
|
|
1663
1698
|
"You are a downstream model router for PromptPilot.",
|
|
1664
1699
|
"Return strict JSON only with this shape:",
|
|
1665
1700
|
'{"selectedTargetId":"string","rankedTargetIds":["string"],"reason":"string"}',
|
|
@@ -1670,7 +1705,108 @@ function buildDownstreamRoutingSystemPrompt(priority, workloadBias) {
|
|
|
1670
1705
|
"Code-first means ambiguous prompts should default toward coding-capable or agentic-capable targets.",
|
|
1671
1706
|
"Explicit email, support, chat, and lightweight writing prompts may prefer cheaper lighter targets.",
|
|
1672
1707
|
"Do not invent targets. Do not output prose outside JSON."
|
|
1673
|
-
]
|
|
1708
|
+
];
|
|
1709
|
+
if (claudeTiersOnly) {
|
|
1710
|
+
lines.push(
|
|
1711
|
+
"You are choosing between Claude model tiers (Haiku, Sonnet, Opus).",
|
|
1712
|
+
"Haiku: fastest and cheapest. ONLY suitable for email, chat, support, summarization, and trivial one-sentence rewrites. Do NOT use Haiku for any coding, debugging, refactoring, or technical tasks.",
|
|
1713
|
+
"Sonnet: balanced cost and capability. The DEFAULT for all coding, debugging, refactoring, writing, and general-purpose tasks. If the prompt mentions code, a file, a module, a bug, or any technical work, choose Sonnet at minimum.",
|
|
1714
|
+
"Opus: most capable and most expensive. Use for complex architecture decisions, multi-constraint agentic planning, system design, long-horizon reasoning, or when the prompt explicitly requires the strongest model.",
|
|
1715
|
+
"When routing priority is cheapest_adequate: Haiku for non-technical lightweight tasks only, Sonnet for anything involving code or technical content, Opus only when clearly necessary.",
|
|
1716
|
+
"When routing priority is best_quality: Opus for all code and reasoning tasks, Sonnet for writing and non-technical tasks.",
|
|
1717
|
+
"When routing priority is fastest_adequate: Haiku only for lightweight non-technical tasks, Sonnet otherwise.",
|
|
1718
|
+
"IMPORTANT: refactor, debug, fix, auth, module, CI, test, and TypeScript are all coding signals \u2014 always choose Sonnet or Opus for these, never Haiku."
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
return lines.join("\n");
|
|
1722
|
+
}
|
|
1723
|
+
var CLAUDE_TIER_TARGETS = [
|
|
1724
|
+
{
|
|
1725
|
+
provider: "anthropic",
|
|
1726
|
+
model: "claude-haiku-4-5",
|
|
1727
|
+
label: "anthropic:claude-haiku-4-5",
|
|
1728
|
+
capabilities: ["writing", "email", "support", "chat", "summarization"],
|
|
1729
|
+
costRank: 1,
|
|
1730
|
+
latencyRank: 1
|
|
1731
|
+
},
|
|
1732
|
+
{
|
|
1733
|
+
provider: "anthropic",
|
|
1734
|
+
model: "claude-sonnet-4-6",
|
|
1735
|
+
label: "anthropic:claude-sonnet-4-6",
|
|
1736
|
+
capabilities: ["coding", "writing", "agentic", "tool_use", "refactor", "debugging"],
|
|
1737
|
+
costRank: 2,
|
|
1738
|
+
latencyRank: 2
|
|
1739
|
+
},
|
|
1740
|
+
{
|
|
1741
|
+
provider: "anthropic",
|
|
1742
|
+
model: "claude-opus-4-6",
|
|
1743
|
+
label: "anthropic:claude-opus-4-6",
|
|
1744
|
+
capabilities: ["coding", "agentic", "tool_use", "refactor", "debugging", "architecture", "writing"],
|
|
1745
|
+
costRank: 3,
|
|
1746
|
+
latencyRank: 3
|
|
1747
|
+
}
|
|
1748
|
+
];
|
|
1749
|
+
function isClaudeTiersOnlyTargetSet(targets) {
|
|
1750
|
+
return targets.length >= 2 && targets.every(
|
|
1751
|
+
(t) => t.provider === "anthropic" && /haiku|sonnet|opus/i.test(t.model)
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
function isCodeSignal(input) {
|
|
1755
|
+
const task = (input.task ?? "").toLowerCase();
|
|
1756
|
+
const preset = (input.preset ?? "").toLowerCase();
|
|
1757
|
+
const hints = input.targetHints ?? [];
|
|
1758
|
+
return task === "code" || preset === "code" || hints.some((h) => ["coding", "agentic", "tool_use", "refactor", "debugging", "architecture"].includes(h)) || /\b(refactor|debug|fix|auth|module|ci|test|typescript|javascript|function|class|api|endpoint|build|deploy|lint|migration)\b/i.test(input.prompt);
|
|
1759
|
+
}
|
|
1760
|
+
function isArchitectureSignal(input) {
|
|
1761
|
+
const hints = input.targetHints ?? [];
|
|
1762
|
+
return hints.includes("architecture") || /\b(architect|architecture|design system|migration plan|multi.?step|long.?horizon|agentic.*plan|system design|microservice|monolith)\b/i.test(input.prompt);
|
|
1763
|
+
}
|
|
1764
|
+
function filterClaudeTierCandidates(targets, input, priority) {
|
|
1765
|
+
if (priority === "best_quality") {
|
|
1766
|
+
const filtered = targets.filter((t) => /opus|sonnet/i.test(t.model));
|
|
1767
|
+
return filtered.length > 0 ? filtered : targets;
|
|
1768
|
+
}
|
|
1769
|
+
if (priority === "cheapest_adequate") {
|
|
1770
|
+
if (isArchitectureSignal(input)) {
|
|
1771
|
+
const filtered2 = targets.filter((t) => /opus|sonnet/i.test(t.model));
|
|
1772
|
+
return filtered2.length > 0 ? filtered2 : targets;
|
|
1773
|
+
}
|
|
1774
|
+
if (isCodeSignal(input)) {
|
|
1775
|
+
const sonnet = targets.find((t) => /sonnet/i.test(t.model));
|
|
1776
|
+
return sonnet ? [sonnet] : targets.filter((t) => !/haiku/i.test(t.model));
|
|
1777
|
+
}
|
|
1778
|
+
const filtered = targets.filter((t) => /haiku|sonnet/i.test(t.model));
|
|
1779
|
+
return filtered.length > 0 ? filtered : targets;
|
|
1780
|
+
}
|
|
1781
|
+
if (priority === "fastest_adequate") {
|
|
1782
|
+
const filtered = targets.filter((t) => !/opus/i.test(t.model));
|
|
1783
|
+
return filtered.length > 0 ? filtered : targets;
|
|
1784
|
+
}
|
|
1785
|
+
return targets;
|
|
1786
|
+
}
|
|
1787
|
+
function selectClaudeTierHeuristic(input, priority, targets) {
|
|
1788
|
+
const haiku = targets.find((t) => /haiku/i.test(t.model)) ?? null;
|
|
1789
|
+
const sonnet = targets.find((t) => /sonnet/i.test(t.model)) ?? null;
|
|
1790
|
+
const opus = targets.find((t) => /opus/i.test(t.model)) ?? null;
|
|
1791
|
+
const task = (input.task ?? "").toLowerCase();
|
|
1792
|
+
const preset = (input.preset ?? "").toLowerCase();
|
|
1793
|
+
const hints = input.targetHints ?? [];
|
|
1794
|
+
const prompt = input.prompt;
|
|
1795
|
+
const isLightweight = ["email", "chat", "support", "summarization"].includes(task) || ["email", "chat", "support", "summarization"].includes(preset) || hints.some((h) => ["email", "support", "chat", "summarization"].includes(h));
|
|
1796
|
+
const needsOpus = /\b(architect|architecture|design system|migration plan|multi.?step|complex.*refactor|long.?horizon|agentic.*plan)\b/i.test(prompt) || hints.includes("architecture") || priority === "best_quality";
|
|
1797
|
+
const isCodeTask = ["code"].includes(task) || ["code"].includes(preset) || hints.some((h) => ["coding", "agentic", "tool_use", "refactor", "debugging", "architecture"].includes(h)) || /\b(refactor|debug|fix|auth|module|ci|test|typescript|javascript|function|class|api|endpoint)\b/i.test(prompt);
|
|
1798
|
+
if (priority === "fastest_adequate") {
|
|
1799
|
+
if (needsOpus) return opus ?? sonnet;
|
|
1800
|
+
if (isCodeTask) return sonnet ?? opus;
|
|
1801
|
+
return haiku ?? sonnet;
|
|
1802
|
+
}
|
|
1803
|
+
if (priority === "best_quality") {
|
|
1804
|
+
return opus ?? sonnet ?? haiku;
|
|
1805
|
+
}
|
|
1806
|
+
if (needsOpus) return opus ?? sonnet;
|
|
1807
|
+
if (isCodeTask) return sonnet ?? opus;
|
|
1808
|
+
if (isLightweight) return haiku ?? sonnet;
|
|
1809
|
+
return sonnet ?? haiku ?? opus;
|
|
1674
1810
|
}
|
|
1675
1811
|
function inferCapabilities(target) {
|
|
1676
1812
|
const lower = `${target.provider} ${target.model} ${target.label ?? ""}`.toLowerCase();
|
|
@@ -1819,6 +1955,109 @@ function createOptimizer(config = {}) {
|
|
|
1819
1955
|
return new PromptOptimizer(config);
|
|
1820
1956
|
}
|
|
1821
1957
|
|
|
1958
|
+
// src/cliMenu.ts
|
|
1959
|
+
var ARROW_UP = "\x1B[A";
|
|
1960
|
+
var ARROW_DOWN = "\x1B[B";
|
|
1961
|
+
var ENTER = "\r";
|
|
1962
|
+
var CTRL_C = "";
|
|
1963
|
+
var ESCAPE = "\x1B";
|
|
1964
|
+
var CLAUDE_TIER_OPTIONS = [
|
|
1965
|
+
{
|
|
1966
|
+
key: "auto",
|
|
1967
|
+
label: "Auto",
|
|
1968
|
+
badge: "recommended",
|
|
1969
|
+
description: "PromptPilot picks the best tier for your prompt"
|
|
1970
|
+
},
|
|
1971
|
+
{
|
|
1972
|
+
key: "haiku",
|
|
1973
|
+
label: "Haiku",
|
|
1974
|
+
badge: "fastest \xB7 cheapest",
|
|
1975
|
+
description: "email, chat, summarization, simple rewrites"
|
|
1976
|
+
},
|
|
1977
|
+
{
|
|
1978
|
+
key: "sonnet",
|
|
1979
|
+
label: "Sonnet",
|
|
1980
|
+
badge: "balanced",
|
|
1981
|
+
description: "coding, debugging, writing, general-purpose"
|
|
1982
|
+
},
|
|
1983
|
+
{
|
|
1984
|
+
key: "opus",
|
|
1985
|
+
label: "Opus",
|
|
1986
|
+
badge: "most capable",
|
|
1987
|
+
description: "architecture, complex reasoning, agentic planning"
|
|
1988
|
+
}
|
|
1989
|
+
];
|
|
1990
|
+
function renderClaudeMenu(options, selected) {
|
|
1991
|
+
const lines = ["Select Claude model tier:\n"];
|
|
1992
|
+
for (let index = 0; index < options.length; index++) {
|
|
1993
|
+
const opt = options[index];
|
|
1994
|
+
const isSelected = index === selected;
|
|
1995
|
+
const cursor = isSelected ? "\u276F" : " ";
|
|
1996
|
+
const label = isSelected ? `\x1B[1m${opt.label}\x1B[0m` : opt.label;
|
|
1997
|
+
const badge = `\x1B[2m${opt.badge}\x1B[0m`;
|
|
1998
|
+
const desc = `\x1B[2m${opt.description}\x1B[0m`;
|
|
1999
|
+
lines.push(` ${cursor} ${label.padEnd(isSelected ? 14 : 6)} ${badge}`);
|
|
2000
|
+
lines.push(` ${desc}`);
|
|
2001
|
+
}
|
|
2002
|
+
lines.push("\n \x1B[2m\u2191/\u2193 move Enter confirm q cancel\x1B[0m");
|
|
2003
|
+
return lines.join("\n");
|
|
2004
|
+
}
|
|
2005
|
+
async function promptClaudeTierMenu(stderr, stdin) {
|
|
2006
|
+
return new Promise((resolve) => {
|
|
2007
|
+
let selected = 0;
|
|
2008
|
+
const options = CLAUDE_TIER_OPTIONS;
|
|
2009
|
+
const lineCount = options.length * 2 + 3;
|
|
2010
|
+
const draw = (first) => {
|
|
2011
|
+
if (!first) {
|
|
2012
|
+
stderr.write(`\x1B[${lineCount}A`);
|
|
2013
|
+
}
|
|
2014
|
+
stderr.write(`\x1B[?25l${renderClaudeMenu(options, selected)}
|
|
2015
|
+
`);
|
|
2016
|
+
};
|
|
2017
|
+
const cleanup = () => {
|
|
2018
|
+
stderr.write("\x1B[?25h");
|
|
2019
|
+
stdin.setRawMode(false);
|
|
2020
|
+
stdin.pause();
|
|
2021
|
+
stdin.removeListener("data", onData);
|
|
2022
|
+
};
|
|
2023
|
+
const onData = (chunk) => {
|
|
2024
|
+
if (chunk === CTRL_C) {
|
|
2025
|
+
cleanup();
|
|
2026
|
+
process.exit(0);
|
|
2027
|
+
}
|
|
2028
|
+
if (chunk === "q" || chunk === ESCAPE) {
|
|
2029
|
+
cleanup();
|
|
2030
|
+
stderr.write("\n");
|
|
2031
|
+
resolve(null);
|
|
2032
|
+
return;
|
|
2033
|
+
}
|
|
2034
|
+
if (chunk === ARROW_UP) {
|
|
2035
|
+
selected = (selected - 1 + options.length) % options.length;
|
|
2036
|
+
draw(false);
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
if (chunk === ARROW_DOWN) {
|
|
2040
|
+
selected = (selected + 1) % options.length;
|
|
2041
|
+
draw(false);
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
if (chunk === ENTER) {
|
|
2045
|
+
cleanup();
|
|
2046
|
+
stderr.write(`
|
|
2047
|
+
Selected: \x1B[1m${options[selected].label}\x1B[0m
|
|
2048
|
+
|
|
2049
|
+
`);
|
|
2050
|
+
resolve(options[selected].key);
|
|
2051
|
+
}
|
|
2052
|
+
};
|
|
2053
|
+
stdin.setRawMode(true);
|
|
2054
|
+
stdin.resume();
|
|
2055
|
+
stdin.setEncoding("utf8");
|
|
2056
|
+
stdin.on("data", onData);
|
|
2057
|
+
draw(true);
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
|
|
1822
2061
|
// src/cliWelcome.ts
|
|
1823
2062
|
import { basename } from "path";
|
|
1824
2063
|
var MIN_WIDE_COLUMNS = 76;
|
|
@@ -2036,7 +2275,7 @@ async function runCli(argv, io = { stdout: process.stdout, stderr: process.stder
|
|
|
2036
2275
|
`);
|
|
2037
2276
|
return 0;
|
|
2038
2277
|
}
|
|
2039
|
-
if (command !== "optimize") {
|
|
2278
|
+
if (command !== "optimize" && command !== "claude") {
|
|
2040
2279
|
io.stderr.write(`Unknown command: ${command}
|
|
2041
2280
|
`);
|
|
2042
2281
|
io.stderr.write(`${getHelpText()}
|
|
@@ -2079,6 +2318,20 @@ async function runCli(argv, io = { stdout: process.stdout, stderr: process.stder
|
|
|
2079
2318
|
io.stderr.write("A prompt is required.\n");
|
|
2080
2319
|
return 1;
|
|
2081
2320
|
}
|
|
2321
|
+
const wantTierMenu = command === "claude" || parsed.autoClaudeTiers;
|
|
2322
|
+
let claudeTierChoice = null;
|
|
2323
|
+
if (wantTierMenu && io.stderr.isTTY && io.stdin) {
|
|
2324
|
+
claudeTierChoice = await promptClaudeTierMenu(io.stderr, io.stdin);
|
|
2325
|
+
if (claudeTierChoice === null) {
|
|
2326
|
+
return 0;
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
const resolvedTargets = (() => {
|
|
2330
|
+
if (!wantTierMenu) return parsed.targets;
|
|
2331
|
+
if (!claudeTierChoice || claudeTierChoice === "auto") return [...CLAUDE_TIER_TARGETS, ...parsed.targets];
|
|
2332
|
+
const picked = CLAUDE_TIER_TARGETS.find((t) => t.model.toLowerCase().includes(claudeTierChoice));
|
|
2333
|
+
return picked ? [picked, ...parsed.targets] : [...CLAUDE_TIER_TARGETS, ...parsed.targets];
|
|
2334
|
+
})();
|
|
2082
2335
|
const spinner = createSpinner(io.stderr, io.stderr.isTTY ?? false);
|
|
2083
2336
|
try {
|
|
2084
2337
|
spinner.start("optimizing");
|
|
@@ -2096,7 +2349,7 @@ async function runCli(argv, io = { stdout: process.stdout, stderr: process.stder
|
|
|
2096
2349
|
maxLength: parsed.maxLength,
|
|
2097
2350
|
tags: parsed.tags,
|
|
2098
2351
|
pinnedConstraints: parsed.pinnedConstraints,
|
|
2099
|
-
availableTargets:
|
|
2352
|
+
availableTargets: resolvedTargets,
|
|
2100
2353
|
routingEnabled: parsed.routingEnabled,
|
|
2101
2354
|
routingPriority: parsed.routingPriority,
|
|
2102
2355
|
routingTopK: parsed.routingTopK,
|
|
@@ -2111,6 +2364,11 @@ async function runCli(argv, io = { stdout: process.stdout, stderr: process.stder
|
|
|
2111
2364
|
bypassOptimization: parsed.bypassOptimization
|
|
2112
2365
|
});
|
|
2113
2366
|
spinner.stop();
|
|
2367
|
+
if (command === "claude") {
|
|
2368
|
+
const doSpawn = dependencies.spawnClaude ?? spawnClaudeProcess;
|
|
2369
|
+
const exitCode = await doSpawn(result.finalPrompt);
|
|
2370
|
+
return exitCode;
|
|
2371
|
+
}
|
|
2114
2372
|
if (parsed.json) {
|
|
2115
2373
|
io.stdout.write(`${toPrettyJson(result)}
|
|
2116
2374
|
`);
|
|
@@ -2168,6 +2426,7 @@ function parseOptimizeArgs(args) {
|
|
|
2168
2426
|
clearSession: false,
|
|
2169
2427
|
useContext: true,
|
|
2170
2428
|
bypassOptimization: false,
|
|
2429
|
+
autoClaudeTiers: false,
|
|
2171
2430
|
help: false,
|
|
2172
2431
|
tags: [],
|
|
2173
2432
|
pinnedConstraints: [],
|
|
@@ -2278,6 +2537,9 @@ function parseOptimizeArgs(args) {
|
|
|
2278
2537
|
case "--bypass-optimization":
|
|
2279
2538
|
parsed.bypassOptimization = true;
|
|
2280
2539
|
break;
|
|
2540
|
+
case "--claude":
|
|
2541
|
+
parsed.autoClaudeTiers = true;
|
|
2542
|
+
break;
|
|
2281
2543
|
case "--help":
|
|
2282
2544
|
case "-h":
|
|
2283
2545
|
parsed.help = true;
|
|
@@ -2291,9 +2553,16 @@ function parseOptimizeArgs(args) {
|
|
|
2291
2553
|
}
|
|
2292
2554
|
function getHelpText() {
|
|
2293
2555
|
return [
|
|
2294
|
-
"
|
|
2556
|
+
"Usage:",
|
|
2295
2557
|
"",
|
|
2296
|
-
"
|
|
2558
|
+
" promptpilot claude <prompt> [options]",
|
|
2559
|
+
" Optimize your prompt and pipe it directly to the claude CLI.",
|
|
2560
|
+
" Shows a tier picker (Haiku / Sonnet / Opus / Auto) in interactive mode.",
|
|
2561
|
+
"",
|
|
2562
|
+
" promptpilot optimize <prompt> [options]",
|
|
2563
|
+
" Optimize and print the result to stdout.",
|
|
2564
|
+
"",
|
|
2565
|
+
"Options (shared):",
|
|
2297
2566
|
" --session <id>",
|
|
2298
2567
|
" --model <name> Override auto-selected local Ollama model",
|
|
2299
2568
|
" --mode <mode>",
|
|
@@ -2315,9 +2584,6 @@ function getHelpText() {
|
|
|
2315
2584
|
" --store <local|sqlite>",
|
|
2316
2585
|
" --storage-dir <path>",
|
|
2317
2586
|
" --sqlite-path <path>",
|
|
2318
|
-
" --plain",
|
|
2319
|
-
" --json",
|
|
2320
|
-
" --clipboard Copy optimized prompt to clipboard",
|
|
2321
2587
|
" --debug",
|
|
2322
2588
|
" --save-context",
|
|
2323
2589
|
" --no-context",
|
|
@@ -2326,7 +2592,13 @@ function getHelpText() {
|
|
|
2326
2592
|
" --max-context-tokens <n>",
|
|
2327
2593
|
" --max-input-tokens <n>",
|
|
2328
2594
|
" --timeout <ms>",
|
|
2329
|
-
" --bypass-optimization"
|
|
2595
|
+
" --bypass-optimization",
|
|
2596
|
+
"",
|
|
2597
|
+
"Options (optimize only):",
|
|
2598
|
+
" --plain",
|
|
2599
|
+
" --json",
|
|
2600
|
+
" --clipboard Copy optimized prompt to clipboard",
|
|
2601
|
+
" --claude Route between Haiku, Sonnet, and Opus automatically"
|
|
2330
2602
|
].join("\n");
|
|
2331
2603
|
}
|
|
2332
2604
|
function parseTargetCandidate(raw, index) {
|
|
@@ -2387,6 +2659,19 @@ function readPackageVersion() {
|
|
|
2387
2659
|
return "dev";
|
|
2388
2660
|
}
|
|
2389
2661
|
}
|
|
2662
|
+
function spawnClaudeProcess(prompt) {
|
|
2663
|
+
return new Promise((resolve, reject) => {
|
|
2664
|
+
const child = spawn("claude", [], { stdio: ["pipe", "inherit", "inherit"] });
|
|
2665
|
+
if (!child.stdin) {
|
|
2666
|
+
reject(new Error("Failed to open stdin pipe to claude process"));
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2669
|
+
child.stdin.write(prompt);
|
|
2670
|
+
child.stdin.end();
|
|
2671
|
+
child.on("close", (code) => resolve(code ?? 1));
|
|
2672
|
+
child.on("error", reject);
|
|
2673
|
+
});
|
|
2674
|
+
}
|
|
2390
2675
|
function copyToClipboard(text) {
|
|
2391
2676
|
const commands = [
|
|
2392
2677
|
{ cmd: "xclip", args: ["-selection", "clipboard"], platform: "linux" },
|