@supatest/cli 0.0.18 → 0.0.20
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/claude-code-cli.js +1823 -1639
- package/dist/index.js +443 -134
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3899,18 +3899,187 @@ var init_setup = __esm({
|
|
|
3899
3899
|
}
|
|
3900
3900
|
});
|
|
3901
3901
|
|
|
3902
|
+
// src/utils/command-discovery.ts
|
|
3903
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
3904
|
+
import { join, relative } from "path";
|
|
3905
|
+
function parseMarkdownFrontmatter(content) {
|
|
3906
|
+
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
3907
|
+
const match = content.match(frontmatterRegex);
|
|
3908
|
+
if (!match) {
|
|
3909
|
+
return { frontmatter: {}, body: content };
|
|
3910
|
+
}
|
|
3911
|
+
const [, frontmatterStr, body] = match;
|
|
3912
|
+
const frontmatter = {};
|
|
3913
|
+
for (const line of frontmatterStr.split("\n")) {
|
|
3914
|
+
const colonIndex = line.indexOf(":");
|
|
3915
|
+
if (colonIndex > 0) {
|
|
3916
|
+
const key = line.slice(0, colonIndex).trim();
|
|
3917
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
3918
|
+
frontmatter[key] = value;
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
return { frontmatter, body };
|
|
3922
|
+
}
|
|
3923
|
+
function discoverMarkdownFiles(dir, baseDir, files = []) {
|
|
3924
|
+
if (!existsSync(dir)) {
|
|
3925
|
+
return files;
|
|
3926
|
+
}
|
|
3927
|
+
const entries = readdirSync(dir);
|
|
3928
|
+
for (const entry of entries) {
|
|
3929
|
+
const fullPath = join(dir, entry);
|
|
3930
|
+
const stat = statSync(fullPath);
|
|
3931
|
+
if (stat.isDirectory()) {
|
|
3932
|
+
discoverMarkdownFiles(fullPath, baseDir, files);
|
|
3933
|
+
} else if (entry.endsWith(".md")) {
|
|
3934
|
+
files.push(fullPath);
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
return files;
|
|
3938
|
+
}
|
|
3939
|
+
function discoverCommands(cwd) {
|
|
3940
|
+
const commandsDir = join(cwd, ".supatest", "commands");
|
|
3941
|
+
if (!existsSync(commandsDir)) {
|
|
3942
|
+
return [];
|
|
3943
|
+
}
|
|
3944
|
+
const files = discoverMarkdownFiles(commandsDir, commandsDir);
|
|
3945
|
+
const commands = [];
|
|
3946
|
+
for (const filePath of files) {
|
|
3947
|
+
try {
|
|
3948
|
+
const content = readFileSync(filePath, "utf-8");
|
|
3949
|
+
const { frontmatter } = parseMarkdownFrontmatter(content);
|
|
3950
|
+
const relativePath = relative(commandsDir, filePath);
|
|
3951
|
+
const name = relativePath.replace(/\.md$/, "").replace(/\//g, ".").replace(/\\/g, ".");
|
|
3952
|
+
commands.push({
|
|
3953
|
+
name,
|
|
3954
|
+
description: frontmatter.description,
|
|
3955
|
+
filePath
|
|
3956
|
+
});
|
|
3957
|
+
} catch {
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
return commands.sort((a, b2) => a.name.localeCompare(b2.name));
|
|
3961
|
+
}
|
|
3962
|
+
function expandCommand(cwd, commandName, args) {
|
|
3963
|
+
const commandsDir = join(cwd, ".supatest", "commands");
|
|
3964
|
+
const relativePath = commandName.replace(/\./g, "/") + ".md";
|
|
3965
|
+
const filePath = join(commandsDir, relativePath);
|
|
3966
|
+
if (!existsSync(filePath)) {
|
|
3967
|
+
return null;
|
|
3968
|
+
}
|
|
3969
|
+
try {
|
|
3970
|
+
const content = readFileSync(filePath, "utf-8");
|
|
3971
|
+
const { body } = parseMarkdownFrontmatter(content);
|
|
3972
|
+
let expanded = body;
|
|
3973
|
+
if (args) {
|
|
3974
|
+
expanded = expanded.replace(/\$ARGUMENTS/g, args);
|
|
3975
|
+
const argParts = args.split(/\s+/);
|
|
3976
|
+
for (let i = 0; i < argParts.length; i++) {
|
|
3977
|
+
expanded = expanded.replace(new RegExp(`\\$${i + 1}`, "g"), argParts[i]);
|
|
3978
|
+
}
|
|
3979
|
+
} else {
|
|
3980
|
+
expanded = expanded.replace(/\$ARGUMENTS/g, "");
|
|
3981
|
+
}
|
|
3982
|
+
return expanded.trim();
|
|
3983
|
+
} catch {
|
|
3984
|
+
return null;
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
function discoverAgents(cwd) {
|
|
3988
|
+
const agentsDir = join(cwd, ".supatest", "agents");
|
|
3989
|
+
if (!existsSync(agentsDir)) {
|
|
3990
|
+
return [];
|
|
3991
|
+
}
|
|
3992
|
+
const files = discoverMarkdownFiles(agentsDir, agentsDir);
|
|
3993
|
+
const agents = [];
|
|
3994
|
+
for (const filePath of files) {
|
|
3995
|
+
try {
|
|
3996
|
+
const content = readFileSync(filePath, "utf-8");
|
|
3997
|
+
const { frontmatter } = parseMarkdownFrontmatter(content);
|
|
3998
|
+
const relativePath = relative(agentsDir, filePath);
|
|
3999
|
+
const defaultName = relativePath.replace(/\.md$/, "").replace(/\//g, "-").replace(/\\/g, "-");
|
|
4000
|
+
agents.push({
|
|
4001
|
+
name: frontmatter.name || defaultName,
|
|
4002
|
+
description: frontmatter.description,
|
|
4003
|
+
model: frontmatter.model,
|
|
4004
|
+
filePath
|
|
4005
|
+
});
|
|
4006
|
+
} catch {
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
4009
|
+
return agents.sort((a, b2) => a.name.localeCompare(b2.name));
|
|
4010
|
+
}
|
|
4011
|
+
var init_command_discovery = __esm({
|
|
4012
|
+
"src/utils/command-discovery.ts"() {
|
|
4013
|
+
"use strict";
|
|
4014
|
+
}
|
|
4015
|
+
});
|
|
4016
|
+
|
|
4017
|
+
// src/utils/mcp-loader.ts
|
|
4018
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
4019
|
+
import { join as join2 } from "path";
|
|
4020
|
+
function expandEnvVar(value) {
|
|
4021
|
+
return value.replace(/\$\{([^}]+)\}/g, (_2, expr) => {
|
|
4022
|
+
const [varName, defaultValue] = expr.split(":-");
|
|
4023
|
+
return process.env[varName] ?? defaultValue ?? "";
|
|
4024
|
+
});
|
|
4025
|
+
}
|
|
4026
|
+
function expandServerConfig(config2) {
|
|
4027
|
+
const expanded = {
|
|
4028
|
+
command: expandEnvVar(config2.command)
|
|
4029
|
+
};
|
|
4030
|
+
if (config2.args) {
|
|
4031
|
+
expanded.args = config2.args.map(expandEnvVar);
|
|
4032
|
+
}
|
|
4033
|
+
if (config2.env) {
|
|
4034
|
+
expanded.env = {};
|
|
4035
|
+
for (const [key, value] of Object.entries(config2.env)) {
|
|
4036
|
+
expanded.env[key] = expandEnvVar(value);
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
return expanded;
|
|
4040
|
+
}
|
|
4041
|
+
function loadMcpServers(cwd) {
|
|
4042
|
+
const mcpPath = join2(cwd, ".supatest", "mcp.json");
|
|
4043
|
+
if (!existsSync2(mcpPath)) {
|
|
4044
|
+
return {};
|
|
4045
|
+
}
|
|
4046
|
+
try {
|
|
4047
|
+
const content = readFileSync2(mcpPath, "utf-8");
|
|
4048
|
+
const config2 = JSON.parse(content);
|
|
4049
|
+
if (!config2.mcpServers) {
|
|
4050
|
+
return {};
|
|
4051
|
+
}
|
|
4052
|
+
const expanded = {};
|
|
4053
|
+
for (const [name, serverConfig] of Object.entries(config2.mcpServers)) {
|
|
4054
|
+
expanded[name] = expandServerConfig(serverConfig);
|
|
4055
|
+
}
|
|
4056
|
+
return expanded;
|
|
4057
|
+
} catch (error) {
|
|
4058
|
+
console.warn(
|
|
4059
|
+
`Warning: Failed to load MCP servers from ${mcpPath}:`,
|
|
4060
|
+
error instanceof Error ? error.message : String(error)
|
|
4061
|
+
);
|
|
4062
|
+
return {};
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
var init_mcp_loader = __esm({
|
|
4066
|
+
"src/utils/mcp-loader.ts"() {
|
|
4067
|
+
"use strict";
|
|
4068
|
+
}
|
|
4069
|
+
});
|
|
4070
|
+
|
|
3902
4071
|
// src/utils/project-instructions.ts
|
|
3903
|
-
import { existsSync, readFileSync } from "fs";
|
|
3904
|
-
import { join } from "path";
|
|
4072
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
4073
|
+
import { join as join3 } from "path";
|
|
3905
4074
|
function loadProjectInstructions(cwd) {
|
|
3906
4075
|
const paths = [
|
|
3907
|
-
|
|
3908
|
-
|
|
4076
|
+
join3(cwd, "SUPATEST.md"),
|
|
4077
|
+
join3(cwd, ".supatest", "SUPATEST.md")
|
|
3909
4078
|
];
|
|
3910
4079
|
for (const path5 of paths) {
|
|
3911
|
-
if (
|
|
4080
|
+
if (existsSync3(path5)) {
|
|
3912
4081
|
try {
|
|
3913
|
-
return
|
|
4082
|
+
return readFileSync3(path5, "utf-8");
|
|
3914
4083
|
} catch {
|
|
3915
4084
|
}
|
|
3916
4085
|
}
|
|
@@ -3926,13 +4095,15 @@ var init_project_instructions = __esm({
|
|
|
3926
4095
|
// src/core/agent.ts
|
|
3927
4096
|
import { createRequire } from "module";
|
|
3928
4097
|
import { homedir } from "os";
|
|
3929
|
-
import { dirname, join as
|
|
4098
|
+
import { dirname, join as join4 } from "path";
|
|
3930
4099
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
3931
4100
|
var CoreAgent;
|
|
3932
4101
|
var init_agent = __esm({
|
|
3933
4102
|
"src/core/agent.ts"() {
|
|
3934
4103
|
"use strict";
|
|
3935
4104
|
init_config();
|
|
4105
|
+
init_command_discovery();
|
|
4106
|
+
init_mcp_loader();
|
|
3936
4107
|
init_project_instructions();
|
|
3937
4108
|
CoreAgent = class {
|
|
3938
4109
|
presenter;
|
|
@@ -3965,13 +4136,29 @@ ${config2.logs}
|
|
|
3965
4136
|
const isPlanMode = config2.mode === "plan";
|
|
3966
4137
|
const cwd = config2.cwd || process.cwd();
|
|
3967
4138
|
const projectInstructions = loadProjectInstructions(cwd);
|
|
4139
|
+
const customAgents = discoverAgents(cwd);
|
|
4140
|
+
let customAgentsPrompt;
|
|
4141
|
+
if (customAgents.length > 0) {
|
|
4142
|
+
const agentList = customAgents.map((agent) => {
|
|
4143
|
+
const modelInfo = agent.model ? ` (model: ${agent.model})` : "";
|
|
4144
|
+
return `- **${agent.name}**${modelInfo}: ${agent.description || "No description"}`;
|
|
4145
|
+
}).join("\n");
|
|
4146
|
+
customAgentsPrompt = `
|
|
4147
|
+
|
|
4148
|
+
# Custom Sub-Agents (from .supatest/agents/)
|
|
4149
|
+
|
|
4150
|
+
The following custom sub-agents are available via the Task tool. Use them by setting subagent_type to the agent name:
|
|
4151
|
+
|
|
4152
|
+
${agentList}`;
|
|
4153
|
+
}
|
|
3968
4154
|
const systemPromptAppend = [
|
|
3969
4155
|
config2.systemPromptAppend,
|
|
3970
4156
|
projectInstructions && `
|
|
3971
4157
|
|
|
3972
4158
|
# Project Instructions (from SUPATEST.md)
|
|
3973
4159
|
|
|
3974
|
-
${projectInstructions}
|
|
4160
|
+
${projectInstructions}`,
|
|
4161
|
+
customAgentsPrompt
|
|
3975
4162
|
].filter(Boolean).join("\n") || void 0;
|
|
3976
4163
|
const cleanEnv = {};
|
|
3977
4164
|
const excludeKeys = /* @__PURE__ */ new Set([
|
|
@@ -3986,8 +4173,8 @@ ${projectInstructions}`
|
|
|
3986
4173
|
cleanEnv[key] = value;
|
|
3987
4174
|
}
|
|
3988
4175
|
}
|
|
3989
|
-
const
|
|
3990
|
-
cleanEnv.CLAUDE_CONFIG_DIR =
|
|
4176
|
+
const internalConfigDir = join4(homedir(), ".supatest", "claude-internal");
|
|
4177
|
+
cleanEnv.CLAUDE_CONFIG_DIR = internalConfigDir;
|
|
3991
4178
|
cleanEnv.ANTHROPIC_API_KEY = config2.supatestApiKey || "";
|
|
3992
4179
|
cleanEnv.ANTHROPIC_BASE_URL = process.env.ANTHROPIC_BASE_URL || "";
|
|
3993
4180
|
cleanEnv.ANTHROPIC_AUTH_TOKEN = "";
|
|
@@ -4005,12 +4192,21 @@ ${projectInstructions}`
|
|
|
4005
4192
|
includePartialMessages: true,
|
|
4006
4193
|
executable: "node",
|
|
4007
4194
|
// MCP servers for enhanced capabilities
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4195
|
+
// User-defined servers from .supatest/mcp.json can override defaults
|
|
4196
|
+
mcpServers: (() => {
|
|
4197
|
+
const userServers = loadMcpServers(cwd);
|
|
4198
|
+
const allServers = {
|
|
4199
|
+
// Default Playwright MCP server for browser automation
|
|
4200
|
+
playwright: {
|
|
4201
|
+
command: "npx",
|
|
4202
|
+
args: ["-y", "@playwright/mcp@latest"]
|
|
4203
|
+
},
|
|
4204
|
+
// User-defined servers override defaults (spread after)
|
|
4205
|
+
...userServers
|
|
4206
|
+
};
|
|
4207
|
+
this.presenter.onLog(`MCP servers loaded: ${Object.keys(allServers).join(", ")}`);
|
|
4208
|
+
return allServers;
|
|
4209
|
+
})(),
|
|
4014
4210
|
// Resume from previous session if providerSessionId is provided
|
|
4015
4211
|
// This allows the agent to continue conversations with full context
|
|
4016
4212
|
// Note: Sessions expire after ~30 days due to Anthropic's data retention policy
|
|
@@ -4198,7 +4394,7 @@ ${projectInstructions}`
|
|
|
4198
4394
|
async resolveClaudeCodePath() {
|
|
4199
4395
|
const fs4 = await import("fs/promises");
|
|
4200
4396
|
let claudeCodePath;
|
|
4201
|
-
const bundledPath =
|
|
4397
|
+
const bundledPath = join4(dirname(import.meta.url.replace("file://", "")), "claude-code-cli.js");
|
|
4202
4398
|
try {
|
|
4203
4399
|
await fs4.access(bundledPath);
|
|
4204
4400
|
claudeCodePath = bundledPath;
|
|
@@ -4206,7 +4402,7 @@ ${projectInstructions}`
|
|
|
4206
4402
|
} catch {
|
|
4207
4403
|
const require2 = createRequire(import.meta.url);
|
|
4208
4404
|
const sdkPath = require2.resolve("@anthropic-ai/claude-agent-sdk/sdk.mjs");
|
|
4209
|
-
claudeCodePath =
|
|
4405
|
+
claudeCodePath = join4(dirname(sdkPath), "cli.js");
|
|
4210
4406
|
this.presenter.onLog(`Development mode: ${claudeCodePath}`);
|
|
4211
4407
|
}
|
|
4212
4408
|
if (config.claudeCodeExecutablePath) {
|
|
@@ -4745,7 +4941,7 @@ var CLI_VERSION;
|
|
|
4745
4941
|
var init_version = __esm({
|
|
4746
4942
|
"src/version.ts"() {
|
|
4747
4943
|
"use strict";
|
|
4748
|
-
CLI_VERSION = "0.0.
|
|
4944
|
+
CLI_VERSION = "0.0.20";
|
|
4749
4945
|
}
|
|
4750
4946
|
});
|
|
4751
4947
|
|
|
@@ -4819,21 +5015,21 @@ var init_encryption = __esm({
|
|
|
4819
5015
|
});
|
|
4820
5016
|
|
|
4821
5017
|
// src/utils/token-storage.ts
|
|
4822
|
-
import { existsSync as
|
|
5018
|
+
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync4, unlinkSync, writeFileSync } from "fs";
|
|
4823
5019
|
import { homedir as homedir2 } from "os";
|
|
4824
|
-
import { join as
|
|
5020
|
+
import { join as join6 } from "path";
|
|
4825
5021
|
function getTokenFilePath() {
|
|
4826
5022
|
const apiUrl = process.env.SUPATEST_API_URL || PRODUCTION_API_URL;
|
|
4827
5023
|
if (apiUrl === PRODUCTION_API_URL) {
|
|
4828
|
-
return
|
|
5024
|
+
return join6(CONFIG_DIR, "token.json");
|
|
4829
5025
|
}
|
|
4830
|
-
return
|
|
5026
|
+
return join6(CONFIG_DIR, "token.local.json");
|
|
4831
5027
|
}
|
|
4832
5028
|
function isV2Format(stored) {
|
|
4833
5029
|
return "version" in stored && stored.version === 2;
|
|
4834
5030
|
}
|
|
4835
5031
|
function ensureConfigDir() {
|
|
4836
|
-
if (!
|
|
5032
|
+
if (!existsSync4(CONFIG_DIR)) {
|
|
4837
5033
|
mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
4838
5034
|
}
|
|
4839
5035
|
}
|
|
@@ -4853,11 +5049,11 @@ function saveToken(token, expiresAt) {
|
|
|
4853
5049
|
}
|
|
4854
5050
|
function loadToken() {
|
|
4855
5051
|
const tokenFile = getTokenFilePath();
|
|
4856
|
-
if (!
|
|
5052
|
+
if (!existsSync4(tokenFile)) {
|
|
4857
5053
|
return null;
|
|
4858
5054
|
}
|
|
4859
5055
|
try {
|
|
4860
|
-
const data =
|
|
5056
|
+
const data = readFileSync4(tokenFile, "utf8");
|
|
4861
5057
|
const stored = JSON.parse(data);
|
|
4862
5058
|
let payload;
|
|
4863
5059
|
if (isV2Format(stored)) {
|
|
@@ -4886,7 +5082,7 @@ function loadToken() {
|
|
|
4886
5082
|
}
|
|
4887
5083
|
function removeToken() {
|
|
4888
5084
|
const tokenFile = getTokenFilePath();
|
|
4889
|
-
if (
|
|
5085
|
+
if (existsSync4(tokenFile)) {
|
|
4890
5086
|
unlinkSync(tokenFile);
|
|
4891
5087
|
}
|
|
4892
5088
|
}
|
|
@@ -4895,10 +5091,10 @@ var init_token_storage = __esm({
|
|
|
4895
5091
|
"src/utils/token-storage.ts"() {
|
|
4896
5092
|
"use strict";
|
|
4897
5093
|
init_encryption();
|
|
4898
|
-
CONFIG_DIR =
|
|
5094
|
+
CONFIG_DIR = join6(homedir2(), ".supatest");
|
|
4899
5095
|
PRODUCTION_API_URL = "https://code-api.supatest.ai";
|
|
4900
5096
|
STORAGE_VERSION = 2;
|
|
4901
|
-
TOKEN_FILE =
|
|
5097
|
+
TOKEN_FILE = join6(CONFIG_DIR, "token.json");
|
|
4902
5098
|
}
|
|
4903
5099
|
});
|
|
4904
5100
|
|
|
@@ -5056,6 +5252,10 @@ var init_react = __esm({
|
|
|
5056
5252
|
todos
|
|
5057
5253
|
});
|
|
5058
5254
|
}
|
|
5255
|
+
} else if (tool === "ExitPlanMode") {
|
|
5256
|
+
this.callbacks.onExitPlanMode?.();
|
|
5257
|
+
} else if (tool === "EnterPlanMode") {
|
|
5258
|
+
this.callbacks.onEnterPlanMode?.();
|
|
5059
5259
|
}
|
|
5060
5260
|
const toolUseEvent = {
|
|
5061
5261
|
type: "tool_use",
|
|
@@ -6562,13 +6762,20 @@ var init_FeedbackDialog = __esm({
|
|
|
6562
6762
|
|
|
6563
6763
|
// src/ui/components/HelpMenu.tsx
|
|
6564
6764
|
import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
|
|
6565
|
-
import React5 from "react";
|
|
6765
|
+
import React5, { useEffect as useEffect4, useState as useState3 } from "react";
|
|
6566
6766
|
var HelpMenu;
|
|
6567
6767
|
var init_HelpMenu = __esm({
|
|
6568
6768
|
"src/ui/components/HelpMenu.tsx"() {
|
|
6569
6769
|
"use strict";
|
|
6770
|
+
init_command_discovery();
|
|
6570
6771
|
init_theme();
|
|
6571
|
-
HelpMenu = ({ isAuthenticated, onClose }) => {
|
|
6772
|
+
HelpMenu = ({ isAuthenticated, onClose, cwd }) => {
|
|
6773
|
+
const [customCommands, setCustomCommands] = useState3([]);
|
|
6774
|
+
useEffect4(() => {
|
|
6775
|
+
const projectDir = cwd || process.cwd();
|
|
6776
|
+
const commands = discoverCommands(projectDir);
|
|
6777
|
+
setCustomCommands(commands);
|
|
6778
|
+
}, [cwd]);
|
|
6572
6779
|
useInput2((input, key) => {
|
|
6573
6780
|
if (key.escape || input === "q" || input === "?" || key.ctrl && input === "h") {
|
|
6574
6781
|
onClose();
|
|
@@ -6587,15 +6794,16 @@ var init_HelpMenu = __esm({
|
|
|
6587
6794
|
/* @__PURE__ */ React5.createElement(Box4, { marginTop: 1 }),
|
|
6588
6795
|
/* @__PURE__ */ React5.createElement(Text4, { bold: true, color: theme.text.secondary }, "Slash Commands:"),
|
|
6589
6796
|
/* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "/help"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " or "), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "/?"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Toggle this help menu")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "/resume"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Resume a previous session")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "/clear"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Clear message history")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "/model"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Cycle through available models")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "/setup"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Initial setup for Supatest CLI")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "/feedback"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Report an issue or request a feature")), isAuthenticated ? /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "/logout"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Log out of Supatest")) : /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "/login"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Authenticate with Supatest")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "/exit"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Exit the CLI"))),
|
|
6797
|
+
customCommands.length > 0 && /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Box4, { marginTop: 1 }), /* @__PURE__ */ React5.createElement(Text4, { bold: true, color: theme.text.secondary }, "Project Commands:"), /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, customCommands.slice(0, 5).map((cmd) => /* @__PURE__ */ React5.createElement(Text4, { key: cmd.name }, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "/", cmd.name), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, cmd.description ? ` - ${cmd.description}` : ""))), customCommands.length > 5 && /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, "...and ", customCommands.length - 5, " more (use Tab to autocomplete)"))),
|
|
6590
6798
|
/* @__PURE__ */ React5.createElement(Box4, { marginTop: 1 }),
|
|
6591
6799
|
/* @__PURE__ */ React5.createElement(Text4, { bold: true, color: theme.text.secondary }, "Keyboard Shortcuts:"),
|
|
6592
|
-
/* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "?"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " ", "- Toggle help (when input is empty)")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Ctrl+H"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Toggle help")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Ctrl+C"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " ", "- Exit (or clear input if not empty)")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Ctrl+D"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Exit immediately")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Ctrl+L"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Clear terminal screen")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Ctrl+U"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Clear current input line")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "ESC"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Interrupt running agent")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Shift+
|
|
6800
|
+
/* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "?"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " ", "- Toggle help (when input is empty)")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Ctrl+H"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Toggle help")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Ctrl+C"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " ", "- Exit (or clear input if not empty)")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Ctrl+D"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Exit immediately")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Ctrl+L"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Clear terminal screen")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Ctrl+U"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Clear current input line")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "ESC"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Interrupt running agent")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Shift+Enter"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Add new line in input")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "ctrl+o"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Toggle tool outputs")), /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "Ctrl+M"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " - Cycle through models"))),
|
|
6593
6801
|
/* @__PURE__ */ React5.createElement(Box4, { marginTop: 1 }),
|
|
6594
6802
|
/* @__PURE__ */ React5.createElement(Text4, { bold: true, color: theme.text.secondary }, "File References:"),
|
|
6595
6803
|
/* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.accent }, "@filename"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, " ", "- Reference a file (autocomplete with Tab)")), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, 'Example: "Fix the bug in @src/app.ts"')),
|
|
6596
6804
|
/* @__PURE__ */ React5.createElement(Box4, { marginTop: 1 }),
|
|
6597
6805
|
/* @__PURE__ */ React5.createElement(Text4, { bold: true, color: theme.text.secondary }, "Tips:"),
|
|
6598
|
-
/* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, "\u2022 Press Enter to submit your task"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, "\u2022 Use Shift+Enter to write multi-line prompts"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, "\u2022 Drag and drop files into the terminal to add file paths"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, "\u2022 The agent will automatically run tools and fix issues")),
|
|
6806
|
+
/* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, "\u2022 Press Enter to submit your task"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, "\u2022 Use Shift+Enter to write multi-line prompts"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, "\u2022 Drag and drop files into the terminal to add file paths"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, "\u2022 The agent will automatically run tools and fix issues"), /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, "\u2022 Use Ctrl+L to clear the terminal screen without clearing the messages history")),
|
|
6599
6807
|
/* @__PURE__ */ React5.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text4, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React5.createElement(Text4, { bold: true }, "ESC"), " or ", /* @__PURE__ */ React5.createElement(Text4, { bold: true }, "?"), " to close"))
|
|
6600
6808
|
);
|
|
6601
6809
|
};
|
|
@@ -6603,7 +6811,7 @@ var init_HelpMenu = __esm({
|
|
|
6603
6811
|
});
|
|
6604
6812
|
|
|
6605
6813
|
// src/ui/contexts/SessionContext.tsx
|
|
6606
|
-
import React6, { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useState as
|
|
6814
|
+
import React6, { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useState as useState4 } from "react";
|
|
6607
6815
|
var SessionContext, SessionProvider, useSession;
|
|
6608
6816
|
var init_SessionContext = __esm({
|
|
6609
6817
|
"src/ui/contexts/SessionContext.tsx"() {
|
|
@@ -6614,24 +6822,24 @@ var init_SessionContext = __esm({
|
|
|
6614
6822
|
children,
|
|
6615
6823
|
initialModel
|
|
6616
6824
|
}) => {
|
|
6617
|
-
const [messages, setMessages] =
|
|
6618
|
-
const [todos, setTodos] =
|
|
6619
|
-
const [stats, setStats] =
|
|
6825
|
+
const [messages, setMessages] = useState4([]);
|
|
6826
|
+
const [todos, setTodos] = useState4([]);
|
|
6827
|
+
const [stats, setStats] = useState4({
|
|
6620
6828
|
filesModified: /* @__PURE__ */ new Set(),
|
|
6621
6829
|
commandsRun: [],
|
|
6622
6830
|
iterations: 0,
|
|
6623
6831
|
startTime: Date.now()
|
|
6624
6832
|
});
|
|
6625
|
-
const [isAgentRunning, setIsAgentRunning] =
|
|
6626
|
-
const [shouldInterruptAgent, setShouldInterruptAgent] =
|
|
6627
|
-
const [usageStats, setUsageStats] =
|
|
6628
|
-
const [sessionId, setSessionId] =
|
|
6629
|
-
const [webUrl, setWebUrl] =
|
|
6630
|
-
const [agentMode, setAgentMode] =
|
|
6631
|
-
const [planFilePath, setPlanFilePath] =
|
|
6632
|
-
const [selectedModel, setSelectedModel] =
|
|
6633
|
-
const [allToolsExpanded, setAllToolsExpanded] =
|
|
6634
|
-
const [staticRemountKey, setStaticRemountKey] =
|
|
6833
|
+
const [isAgentRunning, setIsAgentRunning] = useState4(false);
|
|
6834
|
+
const [shouldInterruptAgent, setShouldInterruptAgent] = useState4(false);
|
|
6835
|
+
const [usageStats, setUsageStats] = useState4(null);
|
|
6836
|
+
const [sessionId, setSessionId] = useState4();
|
|
6837
|
+
const [webUrl, setWebUrl] = useState4();
|
|
6838
|
+
const [agentMode, setAgentMode] = useState4("build");
|
|
6839
|
+
const [planFilePath, setPlanFilePath] = useState4();
|
|
6840
|
+
const [selectedModel, setSelectedModel] = useState4(initialModel || Mt);
|
|
6841
|
+
const [allToolsExpanded, setAllToolsExpanded] = useState4(true);
|
|
6842
|
+
const [staticRemountKey, setStaticRemountKey] = useState4(0);
|
|
6635
6843
|
const addMessage = useCallback2(
|
|
6636
6844
|
(message) => {
|
|
6637
6845
|
const expandableTools = ["Bash", "BashOutput", "Command Output"];
|
|
@@ -6838,7 +7046,7 @@ var init_file_completion = __esm({
|
|
|
6838
7046
|
|
|
6839
7047
|
// src/ui/components/ModelSelector.tsx
|
|
6840
7048
|
import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
|
|
6841
|
-
import React7, { useState as
|
|
7049
|
+
import React7, { useState as useState5 } from "react";
|
|
6842
7050
|
function getNextModel(currentModel) {
|
|
6843
7051
|
const currentIndex = ke.findIndex((m2) => m2.id === currentModel);
|
|
6844
7052
|
const nextIndex = (currentIndex + 1) % ke.length;
|
|
@@ -6861,7 +7069,7 @@ var init_ModelSelector = __esm({
|
|
|
6861
7069
|
onCancel
|
|
6862
7070
|
}) => {
|
|
6863
7071
|
const currentIndex = ke.findIndex((m2) => m2.id === currentModel);
|
|
6864
|
-
const [selectedIndex, setSelectedIndex] =
|
|
7072
|
+
const [selectedIndex, setSelectedIndex] = useState5(currentIndex >= 0 ? currentIndex : 0);
|
|
6865
7073
|
useInput3((input, key) => {
|
|
6866
7074
|
if (key.upArrow) {
|
|
6867
7075
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : ke.length - 1);
|
|
@@ -6903,12 +7111,13 @@ var init_ModelSelector = __esm({
|
|
|
6903
7111
|
import path4 from "path";
|
|
6904
7112
|
import chalk5 from "chalk";
|
|
6905
7113
|
import { Box as Box6, Text as Text6 } from "ink";
|
|
6906
|
-
import React8, { forwardRef, useEffect as
|
|
7114
|
+
import React8, { forwardRef, useEffect as useEffect5, useImperativeHandle, useState as useState6 } from "react";
|
|
6907
7115
|
var InputPrompt;
|
|
6908
7116
|
var init_InputPrompt = __esm({
|
|
6909
7117
|
"src/ui/components/InputPrompt.tsx"() {
|
|
6910
7118
|
"use strict";
|
|
6911
7119
|
init_shared_es();
|
|
7120
|
+
init_command_discovery();
|
|
6912
7121
|
init_SessionContext();
|
|
6913
7122
|
init_useKeypress();
|
|
6914
7123
|
init_file_completion();
|
|
@@ -6919,19 +7128,20 @@ var init_InputPrompt = __esm({
|
|
|
6919
7128
|
placeholder = "Enter your task (press Enter to submit, Shift+Enter for new line)...",
|
|
6920
7129
|
disabled = false,
|
|
6921
7130
|
onHelpToggle,
|
|
7131
|
+
cwd,
|
|
6922
7132
|
currentFolder,
|
|
6923
7133
|
gitBranch,
|
|
6924
7134
|
onInputChange
|
|
6925
7135
|
}, ref) => {
|
|
6926
7136
|
const { messages, agentMode, selectedModel, setSelectedModel, isAgentRunning, usageStats } = useSession();
|
|
6927
|
-
const [value, setValue] =
|
|
6928
|
-
const [cursorOffset, setCursorOffset] =
|
|
6929
|
-
const [allFiles, setAllFiles] =
|
|
6930
|
-
const [suggestions, setSuggestions] =
|
|
6931
|
-
const [activeSuggestion, setActiveSuggestion] =
|
|
6932
|
-
const [showSuggestions, setShowSuggestions] =
|
|
6933
|
-
const [mentionStartIndex, setMentionStartIndex] =
|
|
6934
|
-
const
|
|
7137
|
+
const [value, setValue] = useState6("");
|
|
7138
|
+
const [cursorOffset, setCursorOffset] = useState6(0);
|
|
7139
|
+
const [allFiles, setAllFiles] = useState6([]);
|
|
7140
|
+
const [suggestions, setSuggestions] = useState6([]);
|
|
7141
|
+
const [activeSuggestion, setActiveSuggestion] = useState6(0);
|
|
7142
|
+
const [showSuggestions, setShowSuggestions] = useState6(false);
|
|
7143
|
+
const [mentionStartIndex, setMentionStartIndex] = useState6(-1);
|
|
7144
|
+
const BUILTIN_SLASH_COMMANDS = [
|
|
6935
7145
|
{ name: "/help", desc: "Show help" },
|
|
6936
7146
|
{ name: "/resume", desc: "Resume session" },
|
|
6937
7147
|
{ name: "/clear", desc: "Clear history" },
|
|
@@ -6942,7 +7152,21 @@ var init_InputPrompt = __esm({
|
|
|
6942
7152
|
{ name: "/logout", desc: "Log out" },
|
|
6943
7153
|
{ name: "/exit", desc: "Exit CLI" }
|
|
6944
7154
|
];
|
|
6945
|
-
const [
|
|
7155
|
+
const [customCommands, setCustomCommands] = useState6([]);
|
|
7156
|
+
const [isSlashCommand, setIsSlashCommand] = useState6(false);
|
|
7157
|
+
useEffect5(() => {
|
|
7158
|
+
try {
|
|
7159
|
+
const projectDir = cwd || process.cwd();
|
|
7160
|
+
const discovered = discoverCommands(projectDir);
|
|
7161
|
+
const formatted = discovered.map((cmd) => ({
|
|
7162
|
+
name: `/${cmd.name}`,
|
|
7163
|
+
desc: cmd.description || ""
|
|
7164
|
+
}));
|
|
7165
|
+
setCustomCommands(formatted);
|
|
7166
|
+
} catch {
|
|
7167
|
+
}
|
|
7168
|
+
}, [cwd]);
|
|
7169
|
+
const allSlashCommands = [...BUILTIN_SLASH_COMMANDS, ...customCommands];
|
|
6946
7170
|
useImperativeHandle(ref, () => ({
|
|
6947
7171
|
clear: () => {
|
|
6948
7172
|
setValue("");
|
|
@@ -6951,7 +7175,7 @@ var init_InputPrompt = __esm({
|
|
|
6951
7175
|
onInputChange?.("");
|
|
6952
7176
|
}
|
|
6953
7177
|
}));
|
|
6954
|
-
|
|
7178
|
+
useEffect5(() => {
|
|
6955
7179
|
setTimeout(() => {
|
|
6956
7180
|
try {
|
|
6957
7181
|
const files = getFiles();
|
|
@@ -6969,7 +7193,18 @@ var init_InputPrompt = __esm({
|
|
|
6969
7193
|
const checkSuggestions = (text, cursor) => {
|
|
6970
7194
|
if (text.startsWith("/") && cursor <= text.length && !text.includes(" ", 1)) {
|
|
6971
7195
|
const query2 = text.slice(1);
|
|
6972
|
-
const
|
|
7196
|
+
const builtinMatches = BUILTIN_SLASH_COMMANDS.filter((cmd) => cmd.name.slice(1).toLowerCase().startsWith(query2.toLowerCase())).map((cmd) => cmd.desc ? `${cmd.name} ${cmd.desc}` : cmd.name);
|
|
7197
|
+
const customMatches = customCommands.filter((cmd) => cmd.name.slice(1).toLowerCase().startsWith(query2.toLowerCase())).map((cmd) => cmd.desc ? `${cmd.name} ${cmd.desc}` : cmd.name);
|
|
7198
|
+
const matches = [];
|
|
7199
|
+
if (builtinMatches.length > 0) {
|
|
7200
|
+
matches.push(...builtinMatches);
|
|
7201
|
+
}
|
|
7202
|
+
if (customMatches.length > 0) {
|
|
7203
|
+
if (builtinMatches.length > 0) {
|
|
7204
|
+
matches.push("\u2500\u2500\u2500\u2500\u2500 custom commands \u2500\u2500\u2500\u2500\u2500");
|
|
7205
|
+
}
|
|
7206
|
+
matches.push(...customMatches);
|
|
7207
|
+
}
|
|
6973
7208
|
if (matches.length > 0) {
|
|
6974
7209
|
setSuggestions(matches);
|
|
6975
7210
|
setShowSuggestions(true);
|
|
@@ -7035,8 +7270,8 @@ var init_InputPrompt = __esm({
|
|
|
7035
7270
|
cleanPath = cleanPath.replace(/\\ /g, " ");
|
|
7036
7271
|
if (path4.isAbsolute(cleanPath)) {
|
|
7037
7272
|
try {
|
|
7038
|
-
const
|
|
7039
|
-
const rel = path4.relative(
|
|
7273
|
+
const cwd2 = process.cwd();
|
|
7274
|
+
const rel = path4.relative(cwd2, cleanPath);
|
|
7040
7275
|
if (!rel.startsWith("..") && !path4.isAbsolute(rel)) {
|
|
7041
7276
|
cleanPath = rel;
|
|
7042
7277
|
}
|
|
@@ -7055,20 +7290,31 @@ var init_InputPrompt = __esm({
|
|
|
7055
7290
|
return;
|
|
7056
7291
|
}
|
|
7057
7292
|
if (showSuggestions && !key.shift) {
|
|
7293
|
+
const isSeparator = (idx) => suggestions[idx]?.startsWith("\u2500\u2500\u2500\u2500\u2500");
|
|
7058
7294
|
if (key.name === "up") {
|
|
7059
|
-
setActiveSuggestion(
|
|
7060
|
-
|
|
7061
|
-
|
|
7295
|
+
setActiveSuggestion((prev) => {
|
|
7296
|
+
let next = prev > 0 ? prev - 1 : suggestions.length - 1;
|
|
7297
|
+
while (isSeparator(next) && next !== prev) {
|
|
7298
|
+
next = next > 0 ? next - 1 : suggestions.length - 1;
|
|
7299
|
+
}
|
|
7300
|
+
return next;
|
|
7301
|
+
});
|
|
7062
7302
|
return;
|
|
7063
7303
|
}
|
|
7064
7304
|
if (key.name === "down") {
|
|
7065
|
-
setActiveSuggestion(
|
|
7066
|
-
|
|
7067
|
-
|
|
7305
|
+
setActiveSuggestion((prev) => {
|
|
7306
|
+
let next = prev < suggestions.length - 1 ? prev + 1 : 0;
|
|
7307
|
+
while (isSeparator(next) && next !== prev) {
|
|
7308
|
+
next = next < suggestions.length - 1 ? next + 1 : 0;
|
|
7309
|
+
}
|
|
7310
|
+
return next;
|
|
7311
|
+
});
|
|
7068
7312
|
return;
|
|
7069
7313
|
}
|
|
7070
7314
|
if (key.name === "tab" || key.name === "return") {
|
|
7071
|
-
|
|
7315
|
+
if (!isSeparator(activeSuggestion)) {
|
|
7316
|
+
completeSuggestion(key.name === "return");
|
|
7317
|
+
}
|
|
7072
7318
|
return;
|
|
7073
7319
|
}
|
|
7074
7320
|
if (key.name === "escape") {
|
|
@@ -7100,14 +7346,13 @@ var init_InputPrompt = __esm({
|
|
|
7100
7346
|
setCursorOffset(Math.min(value.length, cursorOffset + 1));
|
|
7101
7347
|
} else if (key.ctrl && input === "u") {
|
|
7102
7348
|
updateValue("", 0);
|
|
7103
|
-
} else if (key.name === "
|
|
7104
|
-
if (
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
setSelectedModel(getNextModel(selectedModel));
|
|
7109
|
-
}
|
|
7349
|
+
} else if (key.ctrl && key.name === "m" && !isAgentRunning) {
|
|
7350
|
+
if (key.shift) {
|
|
7351
|
+
setSelectedModel(getPreviousModel(selectedModel));
|
|
7352
|
+
} else {
|
|
7353
|
+
setSelectedModel(getNextModel(selectedModel));
|
|
7110
7354
|
}
|
|
7355
|
+
} else if (key.name === "tab" && !showSuggestions) {
|
|
7111
7356
|
} else if (key.paste) {
|
|
7112
7357
|
const newValue = value.slice(0, cursorOffset) + input + value.slice(cursorOffset);
|
|
7113
7358
|
updateValue(newValue, cursorOffset + input.length);
|
|
@@ -7141,7 +7386,13 @@ var init_InputPrompt = __esm({
|
|
|
7141
7386
|
marginBottom: 0,
|
|
7142
7387
|
paddingX: 1
|
|
7143
7388
|
},
|
|
7144
|
-
suggestions.map((
|
|
7389
|
+
suggestions.map((item, idx) => {
|
|
7390
|
+
const isSeparator = item.startsWith("\u2500\u2500\u2500\u2500\u2500");
|
|
7391
|
+
if (isSeparator) {
|
|
7392
|
+
return /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.dim, key: item }, " ", item);
|
|
7393
|
+
}
|
|
7394
|
+
return /* @__PURE__ */ React8.createElement(Text6, { color: idx === activeSuggestion ? theme.text.accent : theme.text.dim, key: item }, idx === activeSuggestion ? "\u276F " : " ", item);
|
|
7395
|
+
})
|
|
7145
7396
|
), /* @__PURE__ */ React8.createElement(
|
|
7146
7397
|
Box6,
|
|
7147
7398
|
{
|
|
@@ -7162,7 +7413,7 @@ var init_InputPrompt = __esm({
|
|
|
7162
7413
|
}
|
|
7163
7414
|
return /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.primary, key: idx }, line);
|
|
7164
7415
|
})), !hasContent && disabled && /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.dim, italic: true }, "Waiting for agent to complete...")))
|
|
7165
|
-
), /* @__PURE__ */ React8.createElement(Box6, { justifyContent: "space-between", paddingX: 1 }, /* @__PURE__ */ React8.createElement(Box6, { gap: 2 }, /* @__PURE__ */ React8.createElement(Box6, null, /* @__PURE__ */ React8.createElement(Text6, { color: agentMode === "plan" ? theme.status.inProgress : theme.text.dim }, agentMode === "plan" ? "\u23F8 plan" : "\u25B6 build"), /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.dim }, " (shift+tab)")), /* @__PURE__ */ React8.createElement(Box6, null, /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.dim }, "model:"), /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.info }, St(selectedModel)), /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.dim }, " (
|
|
7416
|
+
), /* @__PURE__ */ React8.createElement(Box6, { justifyContent: "space-between", paddingX: 1 }, /* @__PURE__ */ React8.createElement(Box6, { gap: 2 }, /* @__PURE__ */ React8.createElement(Box6, null, /* @__PURE__ */ React8.createElement(Text6, { color: agentMode === "plan" ? theme.status.inProgress : theme.text.dim }, agentMode === "plan" ? "\u23F8 plan" : "\u25B6 build"), /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.dim }, " (shift+tab)")), /* @__PURE__ */ React8.createElement(Box6, null, /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.dim }, "model:"), /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.info }, St(selectedModel)), /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.dim }, " (ctrl+m)"))), /* @__PURE__ */ React8.createElement(Box6, null, /* @__PURE__ */ React8.createElement(Text6, { color: usageStats && usageStats.contextPct >= 90 ? theme.text.error : usageStats && usageStats.contextPct >= 75 ? theme.text.warning : theme.text.dim }, usageStats?.contextPct ?? 0, "% context used"), /* @__PURE__ */ React8.createElement(Text6, { color: theme.text.dim }, " ", "(", usageStats ? usageStats.inputTokens >= 1e3 ? `${(usageStats.inputTokens / 1e3).toFixed(1)}K` : usageStats.inputTokens : 0, " / ", usageStats ? usageStats.contextWindow >= 1e3 ? `${(usageStats.contextWindow / 1e3).toFixed(0)}K` : usageStats.contextWindow : "200K", ")"))));
|
|
7166
7417
|
});
|
|
7167
7418
|
InputPrompt.displayName = "InputPrompt";
|
|
7168
7419
|
}
|
|
@@ -7212,7 +7463,7 @@ var init_Header = __esm({
|
|
|
7212
7463
|
import chalk6 from "chalk";
|
|
7213
7464
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
7214
7465
|
import { all, createLowlight } from "lowlight";
|
|
7215
|
-
import React10, { useMemo
|
|
7466
|
+
import React10, { useMemo } from "react";
|
|
7216
7467
|
function parseMarkdownSections(text) {
|
|
7217
7468
|
const sections = [];
|
|
7218
7469
|
const lines = text.split(/\r?\n/);
|
|
@@ -7372,7 +7623,7 @@ var init_markdown = __esm({
|
|
|
7372
7623
|
text,
|
|
7373
7624
|
isPending = false
|
|
7374
7625
|
}) => {
|
|
7375
|
-
const sections =
|
|
7626
|
+
const sections = useMemo(() => parseMarkdownSections(text), [text]);
|
|
7376
7627
|
const elements = sections.map((section, index) => {
|
|
7377
7628
|
if (section.type === "table" && section.tableRows) {
|
|
7378
7629
|
return /* @__PURE__ */ React10.createElement(Table, { key: `table-${index}`, rows: section.tableRows });
|
|
@@ -7622,7 +7873,7 @@ var init_ErrorMessage = __esm({
|
|
|
7622
7873
|
// src/ui/components/messages/LoadingMessage.tsx
|
|
7623
7874
|
import { Box as Box11, Text as Text11 } from "ink";
|
|
7624
7875
|
import Spinner2 from "ink-spinner";
|
|
7625
|
-
import React13, { useEffect as
|
|
7876
|
+
import React13, { useEffect as useEffect6, useState as useState7 } from "react";
|
|
7626
7877
|
var LOADING_MESSAGES, SHIMMER_INTERVAL_MS, TEXT_ROTATION_INTERVAL_MS, LoadingMessage;
|
|
7627
7878
|
var init_LoadingMessage = __esm({
|
|
7628
7879
|
"src/ui/components/messages/LoadingMessage.tsx"() {
|
|
@@ -7639,10 +7890,10 @@ var init_LoadingMessage = __esm({
|
|
|
7639
7890
|
SHIMMER_INTERVAL_MS = 80;
|
|
7640
7891
|
TEXT_ROTATION_INTERVAL_MS = 2e3;
|
|
7641
7892
|
LoadingMessage = () => {
|
|
7642
|
-
const [messageIndex, setMessageIndex] =
|
|
7643
|
-
const [shimmerPosition, setShimmerPosition] =
|
|
7893
|
+
const [messageIndex, setMessageIndex] = useState7(0);
|
|
7894
|
+
const [shimmerPosition, setShimmerPosition] = useState7(0);
|
|
7644
7895
|
const message = LOADING_MESSAGES[messageIndex];
|
|
7645
|
-
|
|
7896
|
+
useEffect6(() => {
|
|
7646
7897
|
const rotationInterval = setInterval(() => {
|
|
7647
7898
|
setMessageIndex((prev) => (prev + 1) % LOADING_MESSAGES.length);
|
|
7648
7899
|
setShimmerPosition(0);
|
|
@@ -7651,7 +7902,7 @@ var init_LoadingMessage = __esm({
|
|
|
7651
7902
|
clearInterval(rotationInterval);
|
|
7652
7903
|
};
|
|
7653
7904
|
}, []);
|
|
7654
|
-
|
|
7905
|
+
useEffect6(() => {
|
|
7655
7906
|
const shimmerInterval = setInterval(() => {
|
|
7656
7907
|
setShimmerPosition((prev) => (prev + 1) % (message.length + 1));
|
|
7657
7908
|
}, SHIMMER_INTERVAL_MS);
|
|
@@ -7695,16 +7946,38 @@ var init_TodoMessage = __esm({
|
|
|
7695
7946
|
"use strict";
|
|
7696
7947
|
init_theme();
|
|
7697
7948
|
TodoMessage = ({ todos }) => {
|
|
7698
|
-
const
|
|
7699
|
-
const inProgress = todos.filter((t) => t.status === "in_progress");
|
|
7700
|
-
const pending = todos.filter((t) => t.status === "pending");
|
|
7949
|
+
const completedCount = todos.filter((t) => t.status === "completed").length;
|
|
7701
7950
|
const total = todos.length;
|
|
7702
|
-
const completedCount = completed.length;
|
|
7703
7951
|
const progress = total > 0 ? Math.round(completedCount / total * 100) : 0;
|
|
7704
7952
|
const barLength = 20;
|
|
7705
7953
|
const filledLength = Math.round(barLength * completedCount / total);
|
|
7706
7954
|
const bar = "\u2588".repeat(filledLength) + "\u2591".repeat(barLength - filledLength);
|
|
7707
|
-
|
|
7955
|
+
const getStatusIcon = (status) => {
|
|
7956
|
+
switch (status) {
|
|
7957
|
+
case "completed":
|
|
7958
|
+
return { icon: "\u2713", color: theme.status.completed };
|
|
7959
|
+
case "in_progress":
|
|
7960
|
+
return { icon: "\u2192", color: theme.status.inProgress };
|
|
7961
|
+
case "pending":
|
|
7962
|
+
return { icon: "\u25CB", color: theme.status.pending };
|
|
7963
|
+
}
|
|
7964
|
+
};
|
|
7965
|
+
const getTextColor = (status) => {
|
|
7966
|
+
switch (status) {
|
|
7967
|
+
case "completed":
|
|
7968
|
+
return theme.text.dim;
|
|
7969
|
+
case "in_progress":
|
|
7970
|
+
return theme.text.primary;
|
|
7971
|
+
case "pending":
|
|
7972
|
+
return theme.text.secondary;
|
|
7973
|
+
}
|
|
7974
|
+
};
|
|
7975
|
+
return /* @__PURE__ */ React15.createElement(Box13, { flexDirection: "column", marginY: 0 }, /* @__PURE__ */ React15.createElement(Box13, { flexDirection: "row" }, /* @__PURE__ */ React15.createElement(Text13, { color: theme.text.info }, "\u{1F4DD} "), /* @__PURE__ */ React15.createElement(Text13, { color: theme.text.dim }, "Todo Progress: "), /* @__PURE__ */ React15.createElement(Text13, { color: theme.text.accent }, bar), /* @__PURE__ */ React15.createElement(Text13, { color: theme.text.primary }, " ", progress, "%"), /* @__PURE__ */ React15.createElement(Text13, { color: theme.text.dim }, " ", "(", completedCount, "/", total, ")")), /* @__PURE__ */ React15.createElement(Box13, { flexDirection: "column", marginLeft: 2 }, todos.map((todo, idx) => {
|
|
7976
|
+
const { icon, color } = getStatusIcon(todo.status);
|
|
7977
|
+
const textColor = getTextColor(todo.status);
|
|
7978
|
+
const displayText = todo.status === "in_progress" ? todo.activeForm || todo.content : todo.content;
|
|
7979
|
+
return /* @__PURE__ */ React15.createElement(Box13, { flexDirection: "row", key: `todo-${idx}` }, /* @__PURE__ */ React15.createElement(Text13, { color }, icon, " "), /* @__PURE__ */ React15.createElement(Text13, { color: textColor }, displayText));
|
|
7980
|
+
})));
|
|
7708
7981
|
};
|
|
7709
7982
|
}
|
|
7710
7983
|
});
|
|
@@ -7886,11 +8159,11 @@ var init_UserMessage = __esm({
|
|
|
7886
8159
|
"use strict";
|
|
7887
8160
|
init_theme();
|
|
7888
8161
|
UserMessage = ({ text }) => {
|
|
7889
|
-
return /* @__PURE__ */ React17.createElement(Box15, { flexDirection: "row", marginTop: 1
|
|
8162
|
+
return /* @__PURE__ */ React17.createElement(Box15, { alignItems: "center", flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text15, { color: theme.text.info }, "\u{1F464} "), /* @__PURE__ */ React17.createElement(
|
|
7890
8163
|
Box15,
|
|
7891
8164
|
{
|
|
7892
|
-
borderStyle: "round",
|
|
7893
8165
|
borderColor: theme.text.dim,
|
|
8166
|
+
borderStyle: "round",
|
|
7894
8167
|
paddingLeft: 1,
|
|
7895
8168
|
paddingRight: 1
|
|
7896
8169
|
},
|
|
@@ -7902,7 +8175,7 @@ var init_UserMessage = __esm({
|
|
|
7902
8175
|
|
|
7903
8176
|
// src/ui/components/MessageList.tsx
|
|
7904
8177
|
import { Box as Box16, Static } from "ink";
|
|
7905
|
-
import React18, { useMemo as
|
|
8178
|
+
import React18, { useMemo as useMemo3 } from "react";
|
|
7906
8179
|
var MessageList;
|
|
7907
8180
|
var init_MessageList = __esm({
|
|
7908
8181
|
"src/ui/components/MessageList.tsx"() {
|
|
@@ -7972,7 +8245,7 @@ var init_MessageList = __esm({
|
|
|
7972
8245
|
return null;
|
|
7973
8246
|
}
|
|
7974
8247
|
};
|
|
7975
|
-
const { completedMessages, pendingMessages } =
|
|
8248
|
+
const { completedMessages, pendingMessages } = useMemo3(() => {
|
|
7976
8249
|
const completed = [];
|
|
7977
8250
|
const pending = [];
|
|
7978
8251
|
for (const msg of messages) {
|
|
@@ -7984,7 +8257,7 @@ var init_MessageList = __esm({
|
|
|
7984
8257
|
}
|
|
7985
8258
|
return { completedMessages: completed, pendingMessages: pending };
|
|
7986
8259
|
}, [messages]);
|
|
7987
|
-
const staticItems =
|
|
8260
|
+
const staticItems = useMemo3(() => [
|
|
7988
8261
|
{ id: "header", type: "header" },
|
|
7989
8262
|
...completedMessages.map((msg) => ({ ...msg, _isMessage: true }))
|
|
7990
8263
|
], [completedMessages]);
|
|
@@ -8036,7 +8309,7 @@ var init_QueuedMessageDisplay = __esm({
|
|
|
8036
8309
|
|
|
8037
8310
|
// src/ui/components/SessionSelector.tsx
|
|
8038
8311
|
import { Box as Box18, Text as Text17, useInput as useInput4 } from "ink";
|
|
8039
|
-
import React20, { useEffect as
|
|
8312
|
+
import React20, { useEffect as useEffect7, useState as useState8 } from "react";
|
|
8040
8313
|
function getSessionPrefix(authMethod) {
|
|
8041
8314
|
return authMethod === "api-key" ? "[Team]" : "[Me]";
|
|
8042
8315
|
}
|
|
@@ -8051,13 +8324,13 @@ var init_SessionSelector = __esm({
|
|
|
8051
8324
|
onSelect,
|
|
8052
8325
|
onCancel
|
|
8053
8326
|
}) => {
|
|
8054
|
-
const [allSessions, setAllSessions] =
|
|
8055
|
-
const [selectedIndex, setSelectedIndex] =
|
|
8056
|
-
const [isLoading, setIsLoading] =
|
|
8057
|
-
const [hasMore, setHasMore] =
|
|
8058
|
-
const [totalSessions, setTotalSessions] =
|
|
8059
|
-
const [error, setError] =
|
|
8060
|
-
|
|
8327
|
+
const [allSessions, setAllSessions] = useState8([]);
|
|
8328
|
+
const [selectedIndex, setSelectedIndex] = useState8(0);
|
|
8329
|
+
const [isLoading, setIsLoading] = useState8(false);
|
|
8330
|
+
const [hasMore, setHasMore] = useState8(true);
|
|
8331
|
+
const [totalSessions, setTotalSessions] = useState8(0);
|
|
8332
|
+
const [error, setError] = useState8(null);
|
|
8333
|
+
useEffect7(() => {
|
|
8061
8334
|
loadMoreSessions();
|
|
8062
8335
|
}, []);
|
|
8063
8336
|
const loadMoreSessions = async () => {
|
|
@@ -8164,11 +8437,11 @@ var init_SessionSelector = __esm({
|
|
|
8164
8437
|
});
|
|
8165
8438
|
|
|
8166
8439
|
// src/ui/hooks/useModeToggle.ts
|
|
8167
|
-
import { useEffect as
|
|
8440
|
+
import { useEffect as useEffect8 } from "react";
|
|
8168
8441
|
function useModeToggle() {
|
|
8169
8442
|
const { subscribe, unsubscribe } = useKeypressContext();
|
|
8170
8443
|
const { agentMode, setAgentMode, isAgentRunning } = useSession();
|
|
8171
|
-
|
|
8444
|
+
useEffect8(() => {
|
|
8172
8445
|
const handleKeypress = (key) => {
|
|
8173
8446
|
if (key.name === "tab" && key.shift && !isAgentRunning) {
|
|
8174
8447
|
const newMode = agentMode === "plan" ? "build" : "plan";
|
|
@@ -8189,7 +8462,7 @@ var init_useModeToggle = __esm({
|
|
|
8189
8462
|
});
|
|
8190
8463
|
|
|
8191
8464
|
// src/ui/hooks/useOverlayEscapeGuard.ts
|
|
8192
|
-
import { useCallback as useCallback3, useMemo as
|
|
8465
|
+
import { useCallback as useCallback3, useMemo as useMemo4, useRef as useRef2 } from "react";
|
|
8193
8466
|
var useOverlayEscapeGuard;
|
|
8194
8467
|
var init_useOverlayEscapeGuard = __esm({
|
|
8195
8468
|
"src/ui/hooks/useOverlayEscapeGuard.ts"() {
|
|
@@ -8200,7 +8473,7 @@ var init_useOverlayEscapeGuard = __esm({
|
|
|
8200
8473
|
suppressUntilRef.current = Date.now() + suppressionMs;
|
|
8201
8474
|
}, [suppressionMs]);
|
|
8202
8475
|
const isCancelSuppressed = useCallback3(() => Date.now() < suppressUntilRef.current, []);
|
|
8203
|
-
const isOverlayOpen =
|
|
8476
|
+
const isOverlayOpen = useMemo4(() => overlays.some(Boolean), [overlays]);
|
|
8204
8477
|
return { isOverlayOpen, isCancelSuppressed, markOverlayClosed };
|
|
8205
8478
|
};
|
|
8206
8479
|
}
|
|
@@ -8211,7 +8484,7 @@ import { execSync as execSync3 } from "child_process";
|
|
|
8211
8484
|
import { homedir as homedir3 } from "os";
|
|
8212
8485
|
import { Box as Box19, Text as Text18, useApp } from "ink";
|
|
8213
8486
|
import Spinner3 from "ink-spinner";
|
|
8214
|
-
import React21, { useEffect as
|
|
8487
|
+
import React21, { useEffect as useEffect9, useRef as useRef3, useState as useState9 } from "react";
|
|
8215
8488
|
var getGitBranch, getCurrentFolder, AppContent, App;
|
|
8216
8489
|
var init_App = __esm({
|
|
8217
8490
|
"src/ui/App.tsx"() {
|
|
@@ -8219,6 +8492,7 @@ var init_App = __esm({
|
|
|
8219
8492
|
init_shared_es();
|
|
8220
8493
|
init_login();
|
|
8221
8494
|
init_setup();
|
|
8495
|
+
init_command_discovery();
|
|
8222
8496
|
init_stdio();
|
|
8223
8497
|
init_token_storage();
|
|
8224
8498
|
init_version();
|
|
@@ -8244,43 +8518,43 @@ var init_App = __esm({
|
|
|
8244
8518
|
return "";
|
|
8245
8519
|
}
|
|
8246
8520
|
};
|
|
8247
|
-
getCurrentFolder = () => {
|
|
8248
|
-
const cwd = process.cwd();
|
|
8521
|
+
getCurrentFolder = (configCwd) => {
|
|
8522
|
+
const cwd = configCwd || process.cwd();
|
|
8249
8523
|
const home = homedir3();
|
|
8250
8524
|
if (cwd.startsWith(home)) {
|
|
8251
8525
|
return `~${cwd.slice(home.length)}`;
|
|
8252
8526
|
}
|
|
8253
8527
|
return cwd;
|
|
8254
8528
|
};
|
|
8255
|
-
AppContent = ({ config: config2, sessionId, webUrl, queuedTasks = [], onExit, onSubmitTask, apiClient, onResumeSession }) => {
|
|
8529
|
+
AppContent = ({ config: config2, sessionId, webUrl, queuedTasks = [], onExit, onSubmitTask, apiClient, onResumeSession, onClearSession }) => {
|
|
8256
8530
|
const { exit } = useApp();
|
|
8257
8531
|
const { addMessage, clearMessages, isAgentRunning, messages, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel } = useSession();
|
|
8258
8532
|
useModeToggle();
|
|
8259
|
-
const [terminalWidth, setTerminalWidth] =
|
|
8260
|
-
const [showHelp, setShowHelp] =
|
|
8261
|
-
const [showInput, setShowInput] =
|
|
8262
|
-
const [gitBranch] =
|
|
8263
|
-
const [currentFolder] =
|
|
8264
|
-
const [hasInputContent, setHasInputContent] =
|
|
8265
|
-
const [exitWarning, setExitWarning] =
|
|
8533
|
+
const [terminalWidth, setTerminalWidth] = useState9(process.stdout.columns || 80);
|
|
8534
|
+
const [showHelp, setShowHelp] = useState9(false);
|
|
8535
|
+
const [showInput, setShowInput] = useState9(true);
|
|
8536
|
+
const [gitBranch] = useState9(() => getGitBranch());
|
|
8537
|
+
const [currentFolder] = useState9(() => getCurrentFolder(config2.cwd));
|
|
8538
|
+
const [hasInputContent, setHasInputContent] = useState9(false);
|
|
8539
|
+
const [exitWarning, setExitWarning] = useState9(null);
|
|
8266
8540
|
const inputPromptRef = useRef3(null);
|
|
8267
|
-
const [showSessionSelector, setShowSessionSelector] =
|
|
8268
|
-
const [showModelSelector, setShowModelSelector] =
|
|
8269
|
-
const [showFeedbackDialog, setShowFeedbackDialog] =
|
|
8270
|
-
const [isLoadingSession, setIsLoadingSession] =
|
|
8271
|
-
const [authState, setAuthState] =
|
|
8541
|
+
const [showSessionSelector, setShowSessionSelector] = useState9(false);
|
|
8542
|
+
const [showModelSelector, setShowModelSelector] = useState9(false);
|
|
8543
|
+
const [showFeedbackDialog, setShowFeedbackDialog] = useState9(false);
|
|
8544
|
+
const [isLoadingSession, setIsLoadingSession] = useState9(false);
|
|
8545
|
+
const [authState, setAuthState] = useState9(
|
|
8272
8546
|
() => config2.supatestApiKey ? "authenticated" /* Authenticated */ : "unauthenticated" /* Unauthenticated */
|
|
8273
8547
|
);
|
|
8274
|
-
const [showAuthDialog, setShowAuthDialog] =
|
|
8548
|
+
const [showAuthDialog, setShowAuthDialog] = useState9(false);
|
|
8275
8549
|
const { isOverlayOpen, isCancelSuppressed, markOverlayClosed } = useOverlayEscapeGuard({
|
|
8276
8550
|
overlays: [showHelp, showSessionSelector, showAuthDialog, showModelSelector, showFeedbackDialog]
|
|
8277
8551
|
});
|
|
8278
|
-
|
|
8552
|
+
useEffect9(() => {
|
|
8279
8553
|
if (!config2.supatestApiKey) {
|
|
8280
8554
|
setShowAuthDialog(true);
|
|
8281
8555
|
}
|
|
8282
8556
|
}, [config2.supatestApiKey]);
|
|
8283
|
-
|
|
8557
|
+
useEffect9(() => {
|
|
8284
8558
|
if (sessionId) {
|
|
8285
8559
|
setSessionId(sessionId);
|
|
8286
8560
|
}
|
|
@@ -8322,6 +8596,7 @@ var init_App = __esm({
|
|
|
8322
8596
|
if (command === "/clear") {
|
|
8323
8597
|
clearTerminalViewportAndScrollback();
|
|
8324
8598
|
clearMessages();
|
|
8599
|
+
onClearSession?.();
|
|
8325
8600
|
return;
|
|
8326
8601
|
}
|
|
8327
8602
|
if (command === "/exit") {
|
|
@@ -8420,6 +8695,25 @@ var init_App = __esm({
|
|
|
8420
8695
|
}
|
|
8421
8696
|
return;
|
|
8422
8697
|
}
|
|
8698
|
+
const projectDir = config2.cwd || process.cwd();
|
|
8699
|
+
const spaceIndex = trimmedTask.indexOf(" ");
|
|
8700
|
+
const commandName = spaceIndex > 0 ? trimmedTask.slice(1, spaceIndex) : trimmedTask.slice(1);
|
|
8701
|
+
const commandArgs = spaceIndex > 0 ? trimmedTask.slice(spaceIndex + 1) : void 0;
|
|
8702
|
+
const expandedContent = expandCommand(projectDir, commandName, commandArgs);
|
|
8703
|
+
if (expandedContent) {
|
|
8704
|
+
addMessage({
|
|
8705
|
+
type: "user",
|
|
8706
|
+
content: trimmedTask
|
|
8707
|
+
});
|
|
8708
|
+
onSubmitTask?.(expandedContent);
|
|
8709
|
+
return;
|
|
8710
|
+
}
|
|
8711
|
+
addMessage({
|
|
8712
|
+
type: "error",
|
|
8713
|
+
content: `Unknown command: ${trimmedTask}. Type /help for available commands.`,
|
|
8714
|
+
errorType: "warning"
|
|
8715
|
+
});
|
|
8716
|
+
return;
|
|
8423
8717
|
}
|
|
8424
8718
|
if (authState !== "authenticated" /* Authenticated */) {
|
|
8425
8719
|
addMessage({
|
|
@@ -8509,7 +8803,7 @@ var init_App = __esm({
|
|
|
8509
8803
|
markOverlayClosed();
|
|
8510
8804
|
setShowHelp(false);
|
|
8511
8805
|
};
|
|
8512
|
-
|
|
8806
|
+
useEffect9(() => {
|
|
8513
8807
|
const handleResize = () => {
|
|
8514
8808
|
setTerminalWidth(process.stdout.columns || 80);
|
|
8515
8809
|
};
|
|
@@ -8569,7 +8863,7 @@ var init_App = __esm({
|
|
|
8569
8863
|
},
|
|
8570
8864
|
{ isActive: !isOverlayOpen }
|
|
8571
8865
|
);
|
|
8572
|
-
|
|
8866
|
+
useEffect9(() => {
|
|
8573
8867
|
if (config2.task) {
|
|
8574
8868
|
addMessage({
|
|
8575
8869
|
type: "user",
|
|
@@ -8625,6 +8919,7 @@ var init_App = __esm({
|
|
|
8625
8919
|
InputPrompt,
|
|
8626
8920
|
{
|
|
8627
8921
|
currentFolder,
|
|
8922
|
+
cwd: config2.cwd,
|
|
8628
8923
|
gitBranch,
|
|
8629
8924
|
onHelpToggle: () => setShowHelp((prev) => !prev),
|
|
8630
8925
|
onInputChange: (val) => setHasInputContent(val.trim().length > 0),
|
|
@@ -8642,7 +8937,7 @@ var init_App = __esm({
|
|
|
8642
8937
|
});
|
|
8643
8938
|
|
|
8644
8939
|
// src/ui/hooks/useBracketedPaste.ts
|
|
8645
|
-
import { useEffect as
|
|
8940
|
+
import { useEffect as useEffect10 } from "react";
|
|
8646
8941
|
var ENABLE_BRACKETED_PASTE, DISABLE_BRACKETED_PASTE, useBracketedPaste;
|
|
8647
8942
|
var init_useBracketedPaste = __esm({
|
|
8648
8943
|
"src/ui/hooks/useBracketedPaste.ts"() {
|
|
@@ -8651,7 +8946,7 @@ var init_useBracketedPaste = __esm({
|
|
|
8651
8946
|
ENABLE_BRACKETED_PASTE = "\x1B[?2004h";
|
|
8652
8947
|
DISABLE_BRACKETED_PASTE = "\x1B[?2004l";
|
|
8653
8948
|
useBracketedPaste = () => {
|
|
8654
|
-
|
|
8949
|
+
useEffect10(() => {
|
|
8655
8950
|
writeToStdout(ENABLE_BRACKETED_PASTE);
|
|
8656
8951
|
const cleanup = () => {
|
|
8657
8952
|
writeToStdout(DISABLE_BRACKETED_PASTE);
|
|
@@ -8672,7 +8967,7 @@ __export(interactive_exports, {
|
|
|
8672
8967
|
runInteractive: () => runInteractive
|
|
8673
8968
|
});
|
|
8674
8969
|
import { render } from "ink";
|
|
8675
|
-
import React22, { useEffect as
|
|
8970
|
+
import React22, { useEffect as useEffect11, useRef as useRef4 } from "react";
|
|
8676
8971
|
function getToolDescription2(toolName, input) {
|
|
8677
8972
|
switch (toolName) {
|
|
8678
8973
|
case "Read":
|
|
@@ -8874,17 +9169,18 @@ var init_interactive = __esm({
|
|
|
8874
9169
|
shouldInterruptAgent,
|
|
8875
9170
|
setShouldInterruptAgent,
|
|
8876
9171
|
agentMode,
|
|
9172
|
+
setAgentMode,
|
|
8877
9173
|
planFilePath,
|
|
8878
9174
|
selectedModel
|
|
8879
9175
|
} = useSession();
|
|
8880
9176
|
const agentRef = useRef4(null);
|
|
8881
|
-
|
|
9177
|
+
useEffect11(() => {
|
|
8882
9178
|
if (shouldInterruptAgent && agentRef.current) {
|
|
8883
9179
|
agentRef.current.abort();
|
|
8884
9180
|
setShouldInterruptAgent(false);
|
|
8885
9181
|
}
|
|
8886
9182
|
}, [shouldInterruptAgent, setShouldInterruptAgent]);
|
|
8887
|
-
|
|
9183
|
+
useEffect11(() => {
|
|
8888
9184
|
let isMounted = true;
|
|
8889
9185
|
const runAgent2 = async () => {
|
|
8890
9186
|
setIsAgentRunning(true);
|
|
@@ -8917,6 +9213,12 @@ var init_interactive = __esm({
|
|
|
8917
9213
|
// Note: onComplete is now called after agent.run() returns
|
|
8918
9214
|
// to capture the providerSessionId from the result
|
|
8919
9215
|
onComplete: () => {
|
|
9216
|
+
},
|
|
9217
|
+
onExitPlanMode: () => {
|
|
9218
|
+
if (isMounted) setAgentMode("build");
|
|
9219
|
+
},
|
|
9220
|
+
onEnterPlanMode: () => {
|
|
9221
|
+
if (isMounted) setAgentMode("plan");
|
|
8920
9222
|
}
|
|
8921
9223
|
},
|
|
8922
9224
|
apiClient,
|
|
@@ -9033,11 +9335,18 @@ var init_interactive = __esm({
|
|
|
9033
9335
|
setShouldRunAgent(true);
|
|
9034
9336
|
}
|
|
9035
9337
|
}, [shouldRunAgent, taskQueue, addMessage]);
|
|
9338
|
+
const handleClearSession = React22.useCallback(() => {
|
|
9339
|
+
setSessionId(void 0);
|
|
9340
|
+
setContextSessionId(void 0);
|
|
9341
|
+
setProviderSessionId(void 0);
|
|
9342
|
+
setTaskQueue([]);
|
|
9343
|
+
}, [setContextSessionId]);
|
|
9036
9344
|
return /* @__PURE__ */ React22.createElement(React22.Fragment, null, /* @__PURE__ */ React22.createElement(
|
|
9037
9345
|
App,
|
|
9038
9346
|
{
|
|
9039
9347
|
apiClient,
|
|
9040
9348
|
config: { ...config2, task: currentTask },
|
|
9349
|
+
onClearSession: handleClearSession,
|
|
9041
9350
|
onExit,
|
|
9042
9351
|
onResumeSession: async (session) => {
|
|
9043
9352
|
try {
|