Remove flask ASCII art and update harness internals
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,16 +44,7 @@ const FEYNMAN_AGENT_LOGO = [
|
||||
"╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝",
|
||||
];
|
||||
|
||||
const FEYNMAN_MARK_ART = [
|
||||
" .-.",
|
||||
" /___\\\\",
|
||||
" |:::|",
|
||||
" |:::|",
|
||||
" .-'`:::::`'-.",
|
||||
" /:::::::::::::\\\\",
|
||||
" \\\\:::::::::::://",
|
||||
" '-.______.-'",
|
||||
];
|
||||
const FEYNMAN_MARK_ART: string[] = [];
|
||||
|
||||
const FEYNMAN_RESEARCH_TOOLS = [
|
||||
"alpha_search",
|
||||
@@ -754,34 +745,6 @@ Recommended contents:
|
||||
`;
|
||||
}
|
||||
|
||||
type HelpCommand = {
|
||||
usage: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
function buildFeynmanHelpSections(pi: ExtensionAPI): Array<{ title: string; commands: HelpCommand[] }> {
|
||||
const commands = pi.getCommands();
|
||||
const promptCommands = sortCommands(commands.filter((command) => command.source === "prompt")).map((command) => ({
|
||||
usage: `/${command.name}`,
|
||||
description: command.description ?? "Prompt workflow",
|
||||
}));
|
||||
const extensionCommands = sortCommands(commands.filter((command) => command.source === "extension")).map((command) => ({
|
||||
usage: `/${command.name}`,
|
||||
description: command.description ?? "Extension command",
|
||||
}));
|
||||
|
||||
return [
|
||||
{
|
||||
title: "Prompt Workflows",
|
||||
commands: promptCommands,
|
||||
},
|
||||
{
|
||||
title: "Commands",
|
||||
commands: extensionCommands,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export default function researchTools(pi: ExtensionAPI): void {
|
||||
let skillSummaryPromise: Promise<CatalogSummary> | undefined;
|
||||
let agentSummaryPromise: Promise<CatalogSummary> | undefined;
|
||||
@@ -801,186 +764,168 @@ export default function researchTools(pi: ExtensionAPI): void {
|
||||
ctx.ui.setHeader((_tui, theme) => ({
|
||||
render(width: number): string[] {
|
||||
const maxAvailableWidth = Math.max(width - 2, 1);
|
||||
const preferredWidth = Math.min(136, Math.max(72, maxAvailableWidth));
|
||||
const cardWidth = Math.min(maxAvailableWidth, preferredWidth);
|
||||
const innerWidth = cardWidth - 2;
|
||||
const outerPadding = " ".repeat(Math.max(0, Math.floor((width - cardWidth) / 2)));
|
||||
const title = truncateForWidth(` Feynman Research Agent v${FEYNMAN_VERSION} `, innerWidth);
|
||||
const titledBorder = buildTitledBorder(innerWidth, title);
|
||||
const modelLabel = getCurrentModelLabel(ctx);
|
||||
const sessionLabel = ctx.sessionManager.getSessionName()?.trim() || ctx.sessionManager.getSessionId();
|
||||
const directoryLabel = formatHeaderPath(ctx.cwd);
|
||||
const recentActivity = getRecentActivitySummary(ctx);
|
||||
const lines: string[] = [];
|
||||
const preferredWidth = Math.min(136, Math.max(72, maxAvailableWidth));
|
||||
const cardWidth = Math.min(maxAvailableWidth, preferredWidth);
|
||||
const innerWidth = cardWidth - 2;
|
||||
const outerPadding = " ".repeat(Math.max(0, Math.floor((width - cardWidth) / 2)));
|
||||
const title = truncateForWidth(` Feynman Research Agent v${FEYNMAN_VERSION} `, innerWidth);
|
||||
const titledBorder = buildTitledBorder(innerWidth, title);
|
||||
const modelLabel = getCurrentModelLabel(ctx);
|
||||
const sessionLabel = ctx.sessionManager.getSessionName()?.trim() || ctx.sessionManager.getSessionId();
|
||||
const directoryLabel = formatHeaderPath(ctx.cwd);
|
||||
const recentActivity = getRecentActivitySummary(ctx);
|
||||
const lines: string[] = [];
|
||||
|
||||
const push = (line: string): void => {
|
||||
lines.push(`${outerPadding}${line}`);
|
||||
};
|
||||
|
||||
const renderBoxLine = (content: string): string =>
|
||||
`${theme.fg("borderMuted", "│")}${content}${theme.fg("borderMuted", "│")}`;
|
||||
const renderDivider = (): string =>
|
||||
`${theme.fg("borderMuted", "├")}${theme.fg("borderMuted", "─".repeat(innerWidth))}${theme.fg("borderMuted", "┤")}`;
|
||||
const styleAccentCell = (text: string, cellWidth: number): string =>
|
||||
theme.fg("accent", theme.bold(padCell(text, cellWidth)));
|
||||
const styleMutedCell = (text: string, cellWidth: number): string =>
|
||||
theme.fg("muted", padCell(text, cellWidth));
|
||||
const styleSuccessCell = (text: string, cellWidth: number): string =>
|
||||
theme.fg("success", theme.bold(padCell(text, cellWidth)));
|
||||
const styleWarningCell = (text: string, cellWidth: number): string =>
|
||||
theme.fg("warning", theme.bold(padCell(text, cellWidth)));
|
||||
|
||||
push("");
|
||||
for (const logoLine of FEYNMAN_AGENT_LOGO) {
|
||||
push(theme.fg("accent", theme.bold(centerText(logoLine, cardWidth))));
|
||||
}
|
||||
push("");
|
||||
push(
|
||||
theme.fg("borderMuted", `╭${titledBorder.left}`) +
|
||||
theme.fg("accent", theme.bold(title)) +
|
||||
theme.fg("borderMuted", `${titledBorder.right}╮`),
|
||||
);
|
||||
|
||||
if (innerWidth < 72) {
|
||||
const activityLines = wrapForWidth(recentActivity, innerWidth, 2);
|
||||
push(renderBoxLine(padCell("", innerWidth)));
|
||||
push(renderBoxLine(theme.fg("accent", theme.bold(padCell("Research session ready", innerWidth)))));
|
||||
push(renderBoxLine(padCell(`model: ${modelLabel}`, innerWidth)));
|
||||
push(renderBoxLine(padCell(`session: ${sessionLabel}`, innerWidth)));
|
||||
push(renderBoxLine(padCell(`directory: ${directoryLabel}`, innerWidth)));
|
||||
push(renderDivider());
|
||||
push(renderBoxLine(theme.fg("accent", theme.bold(padCell("Available tools", innerWidth)))));
|
||||
for (const toolLine of toolSummary.lines.slice(0, 4)) {
|
||||
push(renderBoxLine(padCell(toolLine, innerWidth)));
|
||||
}
|
||||
push(renderDivider());
|
||||
push(renderBoxLine(theme.fg("accent", theme.bold(padCell("Slash Commands", innerWidth)))));
|
||||
for (const commandLine of commandSummary.lines.slice(0, 4)) {
|
||||
push(renderBoxLine(padCell(commandLine, innerWidth)));
|
||||
}
|
||||
push(renderDivider());
|
||||
push(renderBoxLine(theme.fg("success", theme.bold(padCell("Research Skills", innerWidth)))));
|
||||
for (const skillLine of skillSummary.lines.slice(0, 4)) {
|
||||
push(renderBoxLine(padCell(skillLine, innerWidth)));
|
||||
}
|
||||
if (agentSummary.lines.length > 0) {
|
||||
push(renderDivider());
|
||||
push(renderBoxLine(theme.fg("warning", theme.bold(padCell("Project Agents", innerWidth)))));
|
||||
for (const agentLine of agentSummary.lines.slice(0, 3)) {
|
||||
push(renderBoxLine(padCell(agentLine, innerWidth)));
|
||||
}
|
||||
}
|
||||
push(renderDivider());
|
||||
push(renderBoxLine(theme.fg("accent", theme.bold(padCell("Recent activity", innerWidth)))));
|
||||
for (const activityLine of activityLines.length > 0 ? activityLines : ["No messages yet in this session."]) {
|
||||
push(renderBoxLine(padCell(activityLine, innerWidth)));
|
||||
}
|
||||
push(renderDivider());
|
||||
push(
|
||||
renderBoxLine(
|
||||
padCell(
|
||||
`${toolSummary.count} tools · ${commandSummary.count} commands · ${skillSummary.count} skills · /help`,
|
||||
innerWidth,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
const leftWidth = Math.min(44, Math.max(30, Math.floor(innerWidth * 0.36)));
|
||||
const rightWidth = innerWidth - leftWidth - 3;
|
||||
const activityLines = wrapForWidth(recentActivity, innerWidth, 2);
|
||||
const wrappedToolLines = toolSummary.lines.flatMap((line) => wrapForWidth(line, rightWidth, 3));
|
||||
const wrappedCommandLines = commandSummary.lines.flatMap((line) => wrapForWidth(line, rightWidth, 4));
|
||||
const wrappedSkillLines = skillSummary.lines.flatMap((line) => wrapForWidth(line, rightWidth, 4));
|
||||
const wrappedAgentLines = agentSummary.lines.flatMap((line) => wrapForWidth(line, rightWidth, 4));
|
||||
const wrappedModelLines = wrapForWidth(`model: ${modelLabel}`, leftWidth, 2);
|
||||
const wrappedDirectoryLines = wrapForWidth(`directory: ${directoryLabel}`, leftWidth, 2);
|
||||
const wrappedSessionLines = wrapForWidth(`session: ${sessionLabel}`, leftWidth, 2);
|
||||
const wrappedFooterLines = wrapForWidth(
|
||||
`${toolSummary.count} tools · ${commandSummary.count} commands · ${skillSummary.count} skills · /help`,
|
||||
leftWidth,
|
||||
2,
|
||||
);
|
||||
const leftLines = [
|
||||
...FEYNMAN_MARK_ART.map((line) => centerText(line, leftWidth)),
|
||||
"",
|
||||
centerText("Research shell ready", leftWidth),
|
||||
"",
|
||||
...wrappedModelLines,
|
||||
...wrappedDirectoryLines,
|
||||
...wrappedSessionLines,
|
||||
"",
|
||||
...wrappedFooterLines,
|
||||
];
|
||||
const rightLines = [
|
||||
"Available Tools",
|
||||
...wrappedToolLines,
|
||||
"",
|
||||
"Slash Commands",
|
||||
...wrappedCommandLines,
|
||||
"",
|
||||
"Research Skills",
|
||||
...wrappedSkillLines,
|
||||
...(wrappedAgentLines.length > 0 ? ["", "Project Agents", ...wrappedAgentLines] : []),
|
||||
"",
|
||||
"Recent Activity",
|
||||
...(activityLines.length > 0 ? activityLines : ["No messages yet in this session."]),
|
||||
];
|
||||
const row = (
|
||||
left: string,
|
||||
right: string,
|
||||
options?: { leftAccent?: boolean; rightAccent?: boolean; leftMuted?: boolean; rightMuted?: boolean },
|
||||
): string => {
|
||||
const leftCell = options?.leftAccent
|
||||
? styleAccentCell(left, leftWidth)
|
||||
: options?.leftMuted
|
||||
? styleMutedCell(left, leftWidth)
|
||||
: padCell(left, leftWidth);
|
||||
const rightCell = options?.rightAccent
|
||||
? styleAccentCell(right, rightWidth)
|
||||
: options?.rightMuted
|
||||
? styleMutedCell(right, rightWidth)
|
||||
: padCell(right, rightWidth);
|
||||
return renderBoxLine(`${leftCell}${theme.fg("borderMuted", " │ ")}${rightCell}`);
|
||||
const push = (line: string): void => {
|
||||
lines.push(`${outerPadding}${line}`);
|
||||
};
|
||||
|
||||
push(renderBoxLine(padCell("", innerWidth)));
|
||||
for (let index = 0; index < Math.max(leftLines.length, rightLines.length); index += 1) {
|
||||
const left = leftLines[index] ?? "";
|
||||
const right = rightLines[index] ?? "";
|
||||
const isLogoLine = index < FEYNMAN_MARK_ART.length;
|
||||
const isRightSectionHeading =
|
||||
right === "Available Tools" || right === "Slash Commands" || right === "Research Skills" || right === "Project Agents" ||
|
||||
right === "Recent Activity";
|
||||
const isResearchHeading = right === "Research Skills";
|
||||
const isAgentHeading = right === "Project Agents";
|
||||
const isFooterLine = left.includes("/help");
|
||||
push(
|
||||
(() => {
|
||||
const leftCell = isLogoLine
|
||||
? styleAccentCell(left, leftWidth)
|
||||
: !isFooterLine && index >= FEYNMAN_MARK_ART.length + 2
|
||||
? styleMutedCell(left, leftWidth)
|
||||
: padCell(left, leftWidth);
|
||||
const rightCell = isResearchHeading
|
||||
? styleSuccessCell(right, rightWidth)
|
||||
: isAgentHeading
|
||||
? styleWarningCell(right, rightWidth)
|
||||
: isRightSectionHeading
|
||||
? styleAccentCell(right, rightWidth)
|
||||
: right.length > 0
|
||||
? styleMutedCell(right, rightWidth)
|
||||
: padCell(right, rightWidth);
|
||||
return renderBoxLine(`${leftCell}${theme.fg("borderMuted", " │ ")}${rightCell}`);
|
||||
})(),
|
||||
);
|
||||
}
|
||||
}
|
||||
const renderBoxLine = (content: string): string =>
|
||||
`${theme.fg("borderMuted", "│")}${content}${theme.fg("borderMuted", "│")}`;
|
||||
const renderDivider = (): string =>
|
||||
`${theme.fg("borderMuted", "├")}${theme.fg("borderMuted", "─".repeat(innerWidth))}${theme.fg("borderMuted", "┤")}`;
|
||||
const styleAccentCell = (text: string, cellWidth: number): string =>
|
||||
theme.fg("accent", theme.bold(padCell(text, cellWidth)));
|
||||
const styleMutedCell = (text: string, cellWidth: number): string =>
|
||||
theme.fg("muted", padCell(text, cellWidth));
|
||||
const styleSuccessCell = (text: string, cellWidth: number): string =>
|
||||
theme.fg("success", theme.bold(padCell(text, cellWidth)));
|
||||
const styleWarningCell = (text: string, cellWidth: number): string =>
|
||||
theme.fg("warning", theme.bold(padCell(text, cellWidth)));
|
||||
|
||||
push(theme.fg("borderMuted", `╰${"─".repeat(innerWidth)}╯`));
|
||||
push("");
|
||||
return lines;
|
||||
},
|
||||
invalidate() {},
|
||||
push("");
|
||||
for (const logoLine of FEYNMAN_AGENT_LOGO) {
|
||||
push(theme.fg("accent", theme.bold(centerText(logoLine, cardWidth))));
|
||||
}
|
||||
push("");
|
||||
push(
|
||||
theme.fg("borderMuted", `╭${titledBorder.left}`) +
|
||||
theme.fg("accent", theme.bold(title)) +
|
||||
theme.fg("borderMuted", `${titledBorder.right}╮`),
|
||||
);
|
||||
|
||||
if (innerWidth < 72) {
|
||||
const activityLines = wrapForWidth(recentActivity, innerWidth, 2);
|
||||
push(renderBoxLine(padCell("", innerWidth)));
|
||||
push(renderBoxLine(theme.fg("accent", theme.bold(padCell("Research session ready", innerWidth)))));
|
||||
push(renderBoxLine(padCell(`model: ${modelLabel}`, innerWidth)));
|
||||
push(renderBoxLine(padCell(`session: ${sessionLabel}`, innerWidth)));
|
||||
push(renderBoxLine(padCell(`directory: ${directoryLabel}`, innerWidth)));
|
||||
push(renderDivider());
|
||||
push(renderBoxLine(theme.fg("accent", theme.bold(padCell("Available tools", innerWidth)))));
|
||||
for (const toolLine of toolSummary.lines.slice(0, 4)) {
|
||||
push(renderBoxLine(padCell(toolLine, innerWidth)));
|
||||
}
|
||||
push(renderDivider());
|
||||
push(renderBoxLine(theme.fg("accent", theme.bold(padCell("Slash Commands", innerWidth)))));
|
||||
for (const commandLine of commandSummary.lines.slice(0, 4)) {
|
||||
push(renderBoxLine(padCell(commandLine, innerWidth)));
|
||||
}
|
||||
push(renderDivider());
|
||||
push(renderBoxLine(theme.fg("success", theme.bold(padCell("Research Skills", innerWidth)))));
|
||||
for (const skillLine of skillSummary.lines.slice(0, 4)) {
|
||||
push(renderBoxLine(padCell(skillLine, innerWidth)));
|
||||
}
|
||||
if (agentSummary.lines.length > 0) {
|
||||
push(renderDivider());
|
||||
push(renderBoxLine(theme.fg("warning", theme.bold(padCell("Project Agents", innerWidth)))));
|
||||
for (const agentLine of agentSummary.lines.slice(0, 3)) {
|
||||
push(renderBoxLine(padCell(agentLine, innerWidth)));
|
||||
}
|
||||
}
|
||||
push(renderDivider());
|
||||
push(renderBoxLine(theme.fg("accent", theme.bold(padCell("Recent activity", innerWidth)))));
|
||||
for (const activityLine of activityLines.length > 0 ? activityLines : ["No messages yet in this session."]) {
|
||||
push(renderBoxLine(padCell(activityLine, innerWidth)));
|
||||
}
|
||||
push(renderDivider());
|
||||
push(
|
||||
renderBoxLine(
|
||||
padCell(
|
||||
`${toolSummary.count} tools · ${commandSummary.count} commands · ${skillSummary.count} skills`,
|
||||
innerWidth,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
const leftWidth = Math.min(44, Math.max(30, Math.floor(innerWidth * 0.36)));
|
||||
const rightWidth = innerWidth - leftWidth - 3;
|
||||
const activityLines = wrapForWidth(recentActivity, innerWidth, 2);
|
||||
const wrappedToolLines = toolSummary.lines.flatMap((line) => wrapForWidth(line, rightWidth, 3));
|
||||
const wrappedCommandLines = commandSummary.lines.flatMap((line) => wrapForWidth(line, rightWidth, 4));
|
||||
const wrappedSkillLines = skillSummary.lines.flatMap((line) => wrapForWidth(line, rightWidth, 4));
|
||||
const wrappedAgentLines = agentSummary.lines.flatMap((line) => wrapForWidth(line, rightWidth, 4));
|
||||
const wrappedModelLines = wrapForWidth(`model: ${modelLabel}`, leftWidth, 2);
|
||||
const wrappedDirectoryLines = wrapForWidth(`directory: ${directoryLabel}`, leftWidth, 2);
|
||||
const wrappedSessionLines = wrapForWidth(`session: ${sessionLabel}`, leftWidth, 2);
|
||||
const wrappedFooterLines = wrapForWidth(
|
||||
`${toolSummary.count} tools · ${commandSummary.count} commands · ${skillSummary.count} skills`,
|
||||
leftWidth,
|
||||
2,
|
||||
);
|
||||
const leftLines = [
|
||||
...FEYNMAN_MARK_ART.map((line) => centerText(line, leftWidth)),
|
||||
"",
|
||||
centerText("Research shell ready", leftWidth),
|
||||
"",
|
||||
...wrappedModelLines,
|
||||
...wrappedDirectoryLines,
|
||||
...wrappedSessionLines,
|
||||
"",
|
||||
...wrappedFooterLines,
|
||||
];
|
||||
const rightLines = [
|
||||
"Available Tools",
|
||||
...wrappedToolLines,
|
||||
"",
|
||||
"Slash Commands",
|
||||
...wrappedCommandLines,
|
||||
"",
|
||||
"Research Skills",
|
||||
...wrappedSkillLines,
|
||||
...(wrappedAgentLines.length > 0 ? ["", "Project Agents", ...wrappedAgentLines] : []),
|
||||
"",
|
||||
"Recent Activity",
|
||||
...(activityLines.length > 0 ? activityLines : ["No messages yet in this session."]),
|
||||
];
|
||||
|
||||
push(renderBoxLine(padCell("", innerWidth)));
|
||||
for (let index = 0; index < Math.max(leftLines.length, rightLines.length); index += 1) {
|
||||
const left = leftLines[index] ?? "";
|
||||
const right = rightLines[index] ?? "";
|
||||
const isLogoLine = index < FEYNMAN_MARK_ART.length;
|
||||
const isRightSectionHeading =
|
||||
right === "Available Tools" || right === "Slash Commands" || right === "Research Skills" || right === "Project Agents" ||
|
||||
right === "Recent Activity";
|
||||
const isResearchHeading = right === "Research Skills";
|
||||
const isAgentHeading = right === "Project Agents";
|
||||
push(
|
||||
(() => {
|
||||
const leftCell = isLogoLine
|
||||
? styleAccentCell(left, leftWidth)
|
||||
: index >= FEYNMAN_MARK_ART.length + 2
|
||||
? styleMutedCell(left, leftWidth)
|
||||
: padCell(left, leftWidth);
|
||||
const rightCell = isResearchHeading
|
||||
? styleSuccessCell(right, rightWidth)
|
||||
: isAgentHeading
|
||||
? styleWarningCell(right, rightWidth)
|
||||
: isRightSectionHeading
|
||||
? styleAccentCell(right, rightWidth)
|
||||
: right.length > 0
|
||||
? styleMutedCell(right, rightWidth)
|
||||
: padCell(right, rightWidth);
|
||||
return renderBoxLine(`${leftCell}${theme.fg("borderMuted", " │ ")}${rightCell}`);
|
||||
})(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
push(theme.fg("borderMuted", `╰${"─".repeat(innerWidth)}╯`));
|
||||
push("");
|
||||
return lines;
|
||||
},
|
||||
invalidate() {},
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1028,31 +973,6 @@ export default function researchTools(pi: ExtensionAPI): void {
|
||||
},
|
||||
});
|
||||
|
||||
pi.registerCommand("help", {
|
||||
description: "Show grouped Feynman commands and prefill the editor with a selected command.",
|
||||
handler: async (_args, ctx) => {
|
||||
const sections = buildFeynmanHelpSections(pi);
|
||||
const items = sections.flatMap((section) => [
|
||||
`--- ${section.title} ---`,
|
||||
...section.commands.map((command) => `${command.usage} — ${command.description}`),
|
||||
]).filter((item, index, array) => {
|
||||
if (!item.startsWith("---")) {
|
||||
return true;
|
||||
}
|
||||
return array[index + 1] !== undefined && !array[index + 1].startsWith("---");
|
||||
});
|
||||
|
||||
const selected = await ctx.ui.select("Feynman Help", items);
|
||||
if (!selected || selected.startsWith("---")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const usage = selected.split(" — ")[0];
|
||||
ctx.ui.setEditorText(usage);
|
||||
ctx.ui.notify(`Prefilled ${usage}`, "info");
|
||||
},
|
||||
});
|
||||
|
||||
pi.registerCommand("init", {
|
||||
description: "Initialize AGENTS.md and session-log folders for a research project.",
|
||||
handler: async (_args, ctx) => {
|
||||
|
||||
68
package-lock.json
generated
68
package-lock.json
generated
@@ -10,21 +10,21 @@
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@companion-ai/alpha-hub": "^0.1.2",
|
||||
"@mariozechner/pi-ai": "^0.56.1",
|
||||
"@mariozechner/pi-coding-agent": "^0.56.1",
|
||||
"@sinclair/typebox": "^0.34.41",
|
||||
"dotenv": "^16.4.7"
|
||||
"@mariozechner/pi-ai": "^0.62.0",
|
||||
"@mariozechner/pi-coding-agent": "^0.62.0",
|
||||
"@sinclair/typebox": "^0.34.48",
|
||||
"dotenv": "^17.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"feynman": "bin/feynman.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.3.0",
|
||||
"tsx": "^4.20.5",
|
||||
"typescript": "^5.7.3"
|
||||
"@types/node": "^25.5.0",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.6.0"
|
||||
"node": ">=20.18.1"
|
||||
}
|
||||
},
|
||||
"../alpha-hub/cli": {
|
||||
@@ -1441,21 +1441,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mariozechner/pi-agent-core": {
|
||||
"version": "0.56.3",
|
||||
"resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.56.3.tgz",
|
||||
"integrity": "sha512-TsI1zENf3wqqKPaERnj486Q4i6Y/y6lAZipLNcfDYUDxDrLwNfQ9EW9xukkbJfTZ8zjG3VZ2pBZe3C7wM51dVQ==",
|
||||
"version": "0.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.62.0.tgz",
|
||||
"integrity": "sha512-SBjqgDrgKOaC+IGzFGB3jXQErv9H1QMYnWFvUg6ra6dG0ZgWFBUZb6unidngWLsmaxSDWes6KeKiVFMsr2VSEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mariozechner/pi-ai": "^0.56.3"
|
||||
"@mariozechner/pi-ai": "^0.62.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mariozechner/pi-ai": {
|
||||
"version": "0.56.3",
|
||||
"resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.56.3.tgz",
|
||||
"integrity": "sha512-l4J+cVyVeBLAlGOY/osGDvsbTz0DySCQmR171G6SdbPvIeLGhIi6siZ+zHwq91GJYjv/wtu/08M08ag2mGZKeA==",
|
||||
"version": "0.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.62.0.tgz",
|
||||
"integrity": "sha512-mJgryZ5RgBQG++tiETMtCQQJoH2MAhKetCfqI98NMvGydu7L9x2qC2JekQlRaAgIlTgv4MRH1UXHMEs4UweE/Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.73.0",
|
||||
@@ -1480,15 +1480,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mariozechner/pi-coding-agent": {
|
||||
"version": "0.56.3",
|
||||
"resolved": "https://registry.npmjs.org/@mariozechner/pi-coding-agent/-/pi-coding-agent-0.56.3.tgz",
|
||||
"integrity": "sha512-yHgnadye+TT/4NWKBirZUjw/LWdNWTa7M4HJdX2RxRbwuj4q7RZ0Aqy+lQbOHEPDQYhxK3kZb9hjiAbbGficZQ==",
|
||||
"version": "0.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@mariozechner/pi-coding-agent/-/pi-coding-agent-0.62.0.tgz",
|
||||
"integrity": "sha512-f1NnExqsHuA6w8UVlBtPsvTBhdkMc0h1JD9SzGCdWTLou5GHJr2JIP6DlwV9IKWAnM+sAelaoFez+14wLP2zOQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mariozechner/jiti": "^2.6.2",
|
||||
"@mariozechner/pi-agent-core": "^0.56.3",
|
||||
"@mariozechner/pi-ai": "^0.56.3",
|
||||
"@mariozechner/pi-tui": "^0.56.3",
|
||||
"@mariozechner/pi-agent-core": "^0.62.0",
|
||||
"@mariozechner/pi-ai": "^0.62.0",
|
||||
"@mariozechner/pi-tui": "^0.62.0",
|
||||
"@silvia-odwyer/photon-node": "^0.3.4",
|
||||
"chalk": "^5.5.0",
|
||||
"cli-highlight": "^2.1.11",
|
||||
@@ -1516,9 +1516,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mariozechner/pi-tui": {
|
||||
"version": "0.56.3",
|
||||
"resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.56.3.tgz",
|
||||
"integrity": "sha512-eZ1P9QRKHp78hwx+lITr/mujZqe+eCwL/bOS9vXXkFP070RW4VYum0j7TJ4BrFEH/nNkXRS1tYCXYU05une1bA==",
|
||||
"version": "0.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.62.0.tgz",
|
||||
"integrity": "sha512-/At11PPe8l319MnUoK4wN5L/uVCU6bDdiIUzH8Ez0stOkjSF6isRXScZ+RMM+6iCKsD4muBTX8Cmcif+3/UWHA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mime-types": "^2.1.4",
|
||||
@@ -2307,12 +2307,12 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz",
|
||||
"integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==",
|
||||
"version": "25.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
|
||||
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
"undici-types": "~7.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/retry": {
|
||||
@@ -2647,9 +2647,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||
"version": "17.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz",
|
||||
"integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -3947,9 +3947,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"version": "7.18.2",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
|
||||
16
package.json
16
package.json
@@ -47,18 +47,18 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@companion-ai/alpha-hub": "^0.1.2",
|
||||
"@mariozechner/pi-ai": "^0.56.1",
|
||||
"@mariozechner/pi-coding-agent": "^0.56.1",
|
||||
"@sinclair/typebox": "^0.34.41",
|
||||
"dotenv": "^16.4.7"
|
||||
"@mariozechner/pi-ai": "^0.62.0",
|
||||
"@mariozechner/pi-coding-agent": "^0.62.0",
|
||||
"@sinclair/typebox": "^0.34.48",
|
||||
"dotenv": "^17.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.3.0",
|
||||
"tsx": "^4.20.5",
|
||||
"typescript": "^5.7.3"
|
||||
"@types/node": "^25.5.0",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.6.0"
|
||||
"node": ">=20.18.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -8,6 +8,7 @@ const appRoot = resolve(here, "..");
|
||||
const piPackageRoot = resolve(appRoot, "node_modules", "@mariozechner", "pi-coding-agent");
|
||||
const packageJsonPath = resolve(piPackageRoot, "package.json");
|
||||
const cliPath = resolve(piPackageRoot, "dist", "cli.js");
|
||||
const bunCliPath = resolve(piPackageRoot, "dist", "bun", "cli.js");
|
||||
const interactiveModePath = resolve(piPackageRoot, "dist", "modes", "interactive", "interactive-mode.js");
|
||||
const interactiveThemePath = resolve(piPackageRoot, "dist", "modes", "interactive", "theme", "theme.js");
|
||||
const piTuiRoot = resolve(appRoot, "node_modules", "@mariozechner", "pi-tui");
|
||||
@@ -85,10 +86,14 @@ if (existsSync(packageJsonPath)) {
|
||||
}
|
||||
}
|
||||
|
||||
if (existsSync(cliPath)) {
|
||||
const cliSource = readFileSync(cliPath, "utf8");
|
||||
for (const entryPath of [cliPath, bunCliPath]) {
|
||||
if (!existsSync(entryPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const cliSource = readFileSync(entryPath, "utf8");
|
||||
if (cliSource.includes('process.title = "pi";')) {
|
||||
writeFileSync(cliPath, cliSource.replace('process.title = "pi";', 'process.title = "feynman";'), "utf8");
|
||||
writeFileSync(entryPath, cliSource.replace('process.title = "pi";', 'process.title = "feynman";'), "utf8");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +117,7 @@ if (existsSync(interactiveThemePath)) {
|
||||
" return {",
|
||||
' borderColor: (text) => " ".repeat(text.length),',
|
||||
' bgColor: (text) => theme.bg("userMessageBg", text),',
|
||||
' placeholderText: "Type your message or /help for commands",',
|
||||
' placeholderText: "Type your message",',
|
||||
' placeholder: (text) => theme.fg("dim", text),',
|
||||
" selectList: getSelectListTheme(),",
|
||||
" };",
|
||||
@@ -127,9 +132,11 @@ if (existsSync(interactiveThemePath)) {
|
||||
|
||||
if (existsSync(editorPath)) {
|
||||
let editorSource = readFileSync(editorPath, "utf8");
|
||||
const importOriginal = 'import { getSegmenter, isPunctuationChar, isWhitespaceChar, visibleWidth } from "../utils.js";';
|
||||
const importReplacement = 'import { applyBackgroundToLine, getSegmenter, isPunctuationChar, isWhitespaceChar, visibleWidth } from "../utils.js";';
|
||||
if (!editorSource.includes("applyBackgroundToLine") && editorSource.includes(importOriginal)) {
|
||||
const importOriginal =
|
||||
'import { getSegmenter, isPunctuationChar, isWhitespaceChar, truncateToWidth, visibleWidth } from "../utils.js";';
|
||||
const importReplacement =
|
||||
'import { applyBackgroundToLine, getSegmenter, isPunctuationChar, isWhitespaceChar, truncateToWidth, visibleWidth } from "../utils.js";';
|
||||
if (editorSource.includes(importOriginal)) {
|
||||
editorSource = editorSource.replace(importOriginal, importReplacement);
|
||||
}
|
||||
const desiredRender = [
|
||||
@@ -168,6 +175,13 @@ if (existsSync(editorPath)) {
|
||||
" const result = [];",
|
||||
' const leftPadding = " ".repeat(paddingX);',
|
||||
" const rightPadding = leftPadding;",
|
||||
" const renderBorderLine = (indicator) => {",
|
||||
" const remaining = width - visibleWidth(indicator);",
|
||||
" if (remaining >= 0) {",
|
||||
' return this.borderColor(indicator + "─".repeat(remaining));',
|
||||
" }",
|
||||
" return this.borderColor(truncateToWidth(indicator, width));",
|
||||
" };",
|
||||
" // Render top padding row. When background fill is active, mimic the user-message block",
|
||||
" // instead of the stock editor chrome.",
|
||||
" if (bgColor) {",
|
||||
@@ -181,8 +195,7 @@ if (existsSync(editorPath)) {
|
||||
" }",
|
||||
" else if (this.scrollOffset > 0) {",
|
||||
" const indicator = `─── ↑ ${this.scrollOffset} more `;",
|
||||
" const remaining = width - visibleWidth(indicator);",
|
||||
' result.push(this.borderColor(indicator + "─".repeat(Math.max(0, remaining))));',
|
||||
" result.push(renderBorderLine(indicator));",
|
||||
" }",
|
||||
" else {",
|
||||
" result.push(horizontal.repeat(width));",
|
||||
@@ -205,7 +218,7 @@ if (existsSync(editorPath)) {
|
||||
" if (isPlaceholderLine) {",
|
||||
" const marker = emitCursorMarker ? CURSOR_MARKER : \"\";",
|
||||
" const rawPlaceholder = this.theme.placeholderText;",
|
||||
" const graphemes = [...segmenter.segment(rawPlaceholder)];",
|
||||
" const graphemes = [...this.segment(rawPlaceholder)];",
|
||||
' const firstGrapheme = graphemes[0]?.segment ?? " ";',
|
||||
" const restRaw = rawPlaceholder.slice(firstGrapheme.length);",
|
||||
' const restStyled = typeof this.theme.placeholder === "function"',
|
||||
@@ -222,7 +235,7 @@ if (existsSync(editorPath)) {
|
||||
" if (after.length > 0) {",
|
||||
" // Cursor is on a character (grapheme) - replace it with highlighted version",
|
||||
" // Get the first grapheme from 'after'",
|
||||
" const afterGraphemes = [...segmenter.segment(after)];",
|
||||
" const afterGraphemes = [...this.segment(after)];",
|
||||
' const firstGrapheme = afterGraphemes[0]?.segment || "";',
|
||||
" const restAfter = after.slice(firstGrapheme.length);",
|
||||
' const cursor = `\\x1b[7m${firstGrapheme}\\x1b[27m`;',
|
||||
@@ -260,8 +273,7 @@ if (existsSync(editorPath)) {
|
||||
" }",
|
||||
" else if (linesBelow > 0) {",
|
||||
" const indicator = `─── ↓ ${linesBelow} more `;",
|
||||
" const remaining = width - visibleWidth(indicator);",
|
||||
' const bottomLine = this.borderColor(indicator + "─".repeat(Math.max(0, remaining)));',
|
||||
" const bottomLine = renderBorderLine(indicator);",
|
||||
" result.push(bottomLine);",
|
||||
" }",
|
||||
" else {",
|
||||
|
||||
@@ -105,7 +105,7 @@ function printHelp(): void {
|
||||
printInfo("--alpha-logout Clear alphaXiv auth and exit");
|
||||
printInfo("--alpha-status Show alphaXiv auth status and exit");
|
||||
printInfo("--model provider:model Force a specific model");
|
||||
printInfo("--thinking level off | low | medium | high");
|
||||
printInfo("--thinking level off | minimal | low | medium | high | xhigh");
|
||||
printInfo("--cwd /path/to/workdir Working directory for tools");
|
||||
printInfo("--session-dir /path Session storage directory");
|
||||
printInfo("--doctor Alias for `feynman doctor`");
|
||||
@@ -113,7 +113,6 @@ function printHelp(): void {
|
||||
|
||||
printSection("REPL");
|
||||
printInfo("Inside the REPL, slash workflows come from the live prompt-template and extension command set.");
|
||||
printInfo("Use `/help` in chat to browse the commands actually loaded in this session.");
|
||||
}
|
||||
|
||||
async function handleAlphaCommand(action: string | undefined): Promise<void> {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { dirname } from "node:path";
|
||||
|
||||
import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
export type ThinkingLevel = "off" | "low" | "medium" | "high";
|
||||
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
|
||||
export function parseModelSpec(spec: string, modelRegistry: ModelRegistry) {
|
||||
const trimmed = spec.trim();
|
||||
@@ -27,7 +27,14 @@ export function normalizeThinkingLevel(value: string | undefined): ThinkingLevel
|
||||
}
|
||||
|
||||
const normalized = value.toLowerCase();
|
||||
if (normalized === "off" || normalized === "low" || normalized === "medium" || normalized === "high") {
|
||||
if (
|
||||
normalized === "off" ||
|
||||
normalized === "minimal" ||
|
||||
normalized === "low" ||
|
||||
normalized === "medium" ||
|
||||
normalized === "high" ||
|
||||
normalized === "xhigh"
|
||||
) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
|
||||
18
tests/pi-settings.test.ts
Normal file
18
tests/pi-settings.test.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { normalizeThinkingLevel } from "../src/pi/settings.js";
|
||||
|
||||
test("normalizeThinkingLevel accepts the latest Pi thinking levels", () => {
|
||||
assert.equal(normalizeThinkingLevel("off"), "off");
|
||||
assert.equal(normalizeThinkingLevel("minimal"), "minimal");
|
||||
assert.equal(normalizeThinkingLevel("low"), "low");
|
||||
assert.equal(normalizeThinkingLevel("medium"), "medium");
|
||||
assert.equal(normalizeThinkingLevel("high"), "high");
|
||||
assert.equal(normalizeThinkingLevel("xhigh"), "xhigh");
|
||||
});
|
||||
|
||||
test("normalizeThinkingLevel rejects unknown values", () => {
|
||||
assert.equal(normalizeThinkingLevel("turbo"), undefined);
|
||||
assert.equal(normalizeThinkingLevel(undefined), undefined);
|
||||
});
|
||||
Reference in New Issue
Block a user