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