mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-07 07:55:13 +02:00
Merge pull request #45 from Kaelio/luca/klo-654-improve-indents
feat(cli): add box-drawing prefixes to setup messages
This commit is contained in:
commit
fcdf5234c6
14 changed files with 81 additions and 47 deletions
|
|
@ -28,12 +28,12 @@ describe('prompt navigation helpers', () => {
|
|||
'Name this PostgreSQL connection\nKTX will use this short name in commands and config. You can rename it now.',
|
||||
),
|
||||
).toBe(
|
||||
'Name this PostgreSQL connection\n\nKTX will use this short name in commands and config. You can rename it now.\nPress Escape to go back.\n',
|
||||
'Name this PostgreSQL connection\n│\n│ KTX will use this short name in commands and config. You can rename it now.\n│ Press Escape to go back.\n│',
|
||||
);
|
||||
});
|
||||
|
||||
it('adds a blank separator before compact text input values', () => {
|
||||
expect(withTextInputNavigation('Project folder path')).toBe('Project folder path\nPress Escape to go back.\n');
|
||||
expect(withTextInputNavigation('Project folder path')).toBe('Project folder path\n│ Press Escape to go back.\n│');
|
||||
});
|
||||
|
||||
it('normalizes already hinted text input prompts without duplicating the hint', () => {
|
||||
|
|
@ -42,7 +42,19 @@ describe('prompt navigation helpers', () => {
|
|||
'Name this PostgreSQL connection\nKTX will use this short name in commands and config. You can rename it now.\nPress Escape to go back.',
|
||||
),
|
||||
).toBe(
|
||||
'Name this PostgreSQL connection\n\nKTX will use this short name in commands and config. You can rename it now.\nPress Escape to go back.\n',
|
||||
'Name this PostgreSQL connection\n│\n│ KTX will use this short name in commands and config. You can rename it now.\n│ Press Escape to go back.\n│',
|
||||
);
|
||||
});
|
||||
|
||||
it('is idempotent when text input navigation is applied twice', () => {
|
||||
const once = withTextInputNavigation('Project folder path');
|
||||
expect(withTextInputNavigation(once)).toBe(once);
|
||||
});
|
||||
|
||||
it('is idempotent when text input navigation with body is applied twice', () => {
|
||||
const once = withTextInputNavigation(
|
||||
'Name this PostgreSQL connection\nKTX will use this short name in commands and config.',
|
||||
);
|
||||
expect(withTextInputNavigation(once)).toBe(once);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,26 @@ function removeTrailingBlankLines(message: string): string {
|
|||
return message.replace(/\n+$/, '');
|
||||
}
|
||||
|
||||
function prefixContinuationLines(message: string): string {
|
||||
const lines = message.split('\n');
|
||||
if (lines.length <= 1) return message;
|
||||
const [title, ...body] = lines;
|
||||
let trailingEmptyCount = 0;
|
||||
while (trailingEmptyCount < body.length && body[body.length - 1 - trailingEmptyCount] === '') {
|
||||
trailingEmptyCount++;
|
||||
}
|
||||
const contentBody = trailingEmptyCount > 0 ? body.slice(0, -trailingEmptyCount) : body;
|
||||
const trailingBody = trailingEmptyCount > 0 ? body.slice(-trailingEmptyCount) : [];
|
||||
return [
|
||||
title,
|
||||
...contentBody.map((line) => {
|
||||
const stripped = line.replace(/^│\s*/, '');
|
||||
return stripped === '' ? '│' : `│ ${stripped}`;
|
||||
}),
|
||||
...trailingBody,
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function withTextInputBodySpacing(message: string): string {
|
||||
const normalized = removeTrailingBlankLines(message);
|
||||
if (!normalized.includes('\n')) {
|
||||
|
|
@ -39,7 +59,9 @@ export function withMultiselectNavigation(message: string): string {
|
|||
export function withTextInputNavigation(message: string): string {
|
||||
const messageWithoutHint = removeTrailingBlankLines(message)
|
||||
.split('\n')
|
||||
.filter((line) => line !== TEXT_INPUT_NAVIGATION_HINT)
|
||||
.filter((line) => !line.includes(TEXT_INPUT_NAVIGATION_HINT))
|
||||
.map((line) => line.replace(/^│\s*/, ''))
|
||||
.join('\n');
|
||||
return `${withTextInputBodySpacing(messageWithoutHint)}\n${TEXT_INPUT_NAVIGATION_HINT}\n`;
|
||||
const full = `${withTextInputBodySpacing(messageWithoutHint)}\n${TEXT_INPUT_NAVIGATION_HINT}`;
|
||||
return `${prefixContinuationLines(full)}\n│`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@ export async function runKtxSetupAgentsStep(
|
|||
deps: KtxSetupAgentsDeps = {},
|
||||
): Promise<KtxSetupAgentsResult> {
|
||||
if (args.skipAgents) {
|
||||
io.stdout.write('Agent integration skipped.\n');
|
||||
io.stdout.write('│ Agent integration skipped.\n');
|
||||
return { status: 'skipped', projectDir: args.projectDir };
|
||||
}
|
||||
if (!args.agents && args.inputMode === 'disabled') {
|
||||
|
|
|
|||
|
|
@ -58,10 +58,10 @@ function connectionNamePrompt(label: string): string {
|
|||
function textInputPrompt(message: string): string {
|
||||
const normalized = message.replace(/\n+$/, '');
|
||||
if (!normalized.includes('\n')) {
|
||||
return `${normalized}\nPress Escape to go back.\n`;
|
||||
return `${normalized}\n│ Press Escape to go back.\n│`;
|
||||
}
|
||||
const [title, ...bodyLines] = normalized.split('\n');
|
||||
return `${title}\n\n${bodyLines.join('\n')}\nPress Escape to go back.\n`;
|
||||
return `${title}\n│\n│ ${bodyLines.join('\n│ ')}\n│ Press Escape to go back.\n│`;
|
||||
}
|
||||
|
||||
const legacyHistoricSqlServiceAccountPatternsKey = ['serviceAccount', 'UserPatterns'].join('');
|
||||
|
|
|
|||
|
|
@ -1115,7 +1115,7 @@ async function maybeRunHistoricSqlSetupProbe(input: {
|
|||
return;
|
||||
}
|
||||
|
||||
input.io.stdout.write('Historic SQL probe...\n');
|
||||
input.io.stdout.write('│ Historic SQL probe...\n');
|
||||
const probe = input.deps.historicSqlProbe ?? defaultHistoricSqlProbe;
|
||||
const result = await probe({
|
||||
projectDir: input.projectDir,
|
||||
|
|
@ -1123,10 +1123,10 @@ async function maybeRunHistoricSqlSetupProbe(input: {
|
|||
dialect: 'postgres',
|
||||
});
|
||||
for (const line of result.lines) {
|
||||
input.io.stdout.write(`${line}\n`);
|
||||
input.io.stdout.write(`│${line}\n`);
|
||||
}
|
||||
if (!result.ok) {
|
||||
input.io.stdout.write('Setup written; first ingest run will fail until fixed.\n');
|
||||
input.io.stdout.write('│ Setup written; first ingest run will fail until fixed.\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1261,7 +1261,7 @@ async function chooseDrivers(
|
|||
return 'back';
|
||||
}
|
||||
|
||||
io.stdout.write('KTX cannot work without at least one primary source. Select a source or press Escape to go back.\n');
|
||||
io.stdout.write('│ KTX cannot work without at least one primary source. Select a source or press Escape to go back.\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1325,7 +1325,7 @@ export async function runKtxSetupDatabasesStep(
|
|||
deps: KtxSetupDatabasesDeps = {},
|
||||
): Promise<KtxSetupDatabasesResult> {
|
||||
if (args.skipDatabases) {
|
||||
io.stdout.write('Primary source setup skipped. KTX cannot work until you add a primary source.\n');
|
||||
io.stdout.write('│ Primary source setup skipped. KTX cannot work until you add a primary source.\n');
|
||||
return { status: 'skipped', projectDir: args.projectDir };
|
||||
}
|
||||
|
||||
|
|
@ -1382,7 +1382,7 @@ export async function runKtxSetupDatabasesStep(
|
|||
if (drivers === 'missing-input') return { status: 'missing-input', projectDir: args.projectDir };
|
||||
if (drivers.length === 0) {
|
||||
await markDatabasesComplete(args.projectDir, []);
|
||||
io.stdout.write('KTX cannot work without a primary source.\n');
|
||||
io.stdout.write('│ KTX cannot work without a primary source.\n');
|
||||
return { status: 'skipped', projectDir: args.projectDir };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ describe('setup embeddings step', () => {
|
|||
|
||||
await vi.waitFor(() => {
|
||||
expect(io.stdout()).toContain(
|
||||
'\r- Testing local sentence-transformers embeddings (all-MiniLM-L6-v2, 384 dimensions). First run may take up to 60 seconds.',
|
||||
'\r│ - Testing local sentence-transformers embeddings (all-MiniLM-L6-v2, 384 dimensions). First run may take up to 60 seconds.',
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ async function chooseCredentialRef(
|
|||
}
|
||||
if (choice === 'paste') {
|
||||
io.stdout.write(
|
||||
`${[
|
||||
`│ ${[
|
||||
`KTX will save the key in .ktx/secrets/${backend}-api-key with local file permissions,`,
|
||||
'then write a file: reference in ktx.yaml.',
|
||||
].join(' ')}\n`,
|
||||
|
|
@ -352,7 +352,7 @@ function healthCheckStartText(backend: KtxSetupEmbeddingBackend, model: string,
|
|||
|
||||
function startHealthCheckProgress(io: KtxCliIo, message: string): HealthCheckProgress {
|
||||
if (io.stdout.isTTY !== true) {
|
||||
io.stdout.write(`${message}\n`);
|
||||
io.stdout.write(`│ ${message}\n`);
|
||||
const noop = () => undefined;
|
||||
return {
|
||||
succeed: noop,
|
||||
|
|
@ -363,7 +363,7 @@ function startHealthCheckProgress(io: KtxCliIo, message: string): HealthCheckPro
|
|||
let frameIndex = 0;
|
||||
let stopped = false;
|
||||
const writeFrame = () => {
|
||||
io.stdout.write(`${CLEAR_CURRENT_LINE}${HEALTH_CHECK_SPINNER_FRAMES[frameIndex]} ${message}`);
|
||||
io.stdout.write(`${CLEAR_CURRENT_LINE}│ ${HEALTH_CHECK_SPINNER_FRAMES[frameIndex]} ${message}`);
|
||||
};
|
||||
writeFrame();
|
||||
const interval = setInterval(() => {
|
||||
|
|
@ -377,7 +377,7 @@ function startHealthCheckProgress(io: KtxCliIo, message: string): HealthCheckPro
|
|||
}
|
||||
stopped = true;
|
||||
clearInterval(interval);
|
||||
io.stdout.write(`${CLEAR_CURRENT_LINE}${finalMessage}\n`);
|
||||
io.stdout.write(`${CLEAR_CURRENT_LINE}│ ${finalMessage}\n`);
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
@ -396,7 +396,7 @@ export async function runKtxSetupEmbeddingsStep(
|
|||
deps: KtxSetupEmbeddingsDeps = {},
|
||||
): Promise<KtxSetupEmbeddingsResult> {
|
||||
if (args.skipEmbeddings) {
|
||||
io.stdout.write('Embeddings setup skipped.\n');
|
||||
io.stdout.write('│ Embeddings setup skipped.\n');
|
||||
return { status: 'skipped', projectDir: args.projectDir };
|
||||
}
|
||||
|
||||
|
|
@ -408,7 +408,7 @@ export async function runKtxSetupEmbeddingsStep(
|
|||
!args.embeddingApiKeyEnv &&
|
||||
!args.embeddingApiKeyFile
|
||||
) {
|
||||
io.stdout.write(`Embeddings ready: yes (${project.config.ingest.embeddings.model})\n`);
|
||||
io.stdout.write(`│ Embeddings ready: yes (${project.config.ingest.embeddings.model})\n`);
|
||||
return { status: 'ready', projectDir: args.projectDir };
|
||||
}
|
||||
|
||||
|
|
@ -495,7 +495,7 @@ export async function runKtxSetupEmbeddingsStep(
|
|||
credentialRef,
|
||||
}),
|
||||
);
|
||||
io.stdout.write(`Embeddings ready: yes (${model}, ${dimensions} dimensions)\n`);
|
||||
io.stdout.write(`│ Embeddings ready: yes (${model}, ${dimensions} dimensions)\n`);
|
||||
return { status: 'ready', projectDir: args.projectDir };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ describe('setup Anthropic model step', () => {
|
|||
expect(result.status).toBe('ready');
|
||||
expect(prompts.select).not.toHaveBeenCalledWith(expect.objectContaining({ message: 'Paste Anthropic API key now?' }));
|
||||
expect(prompts.password).toHaveBeenCalledWith({
|
||||
message: 'Anthropic API key\nPress Escape to go back.\n',
|
||||
message: 'Anthropic API key\n│ Press Escape to go back.\n│',
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -464,7 +464,7 @@ describe('setup Anthropic model step', () => {
|
|||
);
|
||||
expect(prompts.text).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'Anthropic model ID\nPress Escape to go back.\n',
|
||||
message: 'Anthropic model ID\n│ Press Escape to go back.\n│',
|
||||
placeholder: 'claude-sonnet-4-6',
|
||||
}),
|
||||
);
|
||||
|
|
@ -629,7 +629,7 @@ describe('setup Anthropic model step', () => {
|
|||
|
||||
expect(result.status).toBe('ready');
|
||||
expect(prompts.password).toHaveBeenCalledWith({
|
||||
message: 'Anthropic API key\nPress Escape to go back.\n',
|
||||
message: 'Anthropic API key\n│ Press Escape to go back.\n│',
|
||||
});
|
||||
await expect(readFile(join(tempDir, '.ktx/secrets/anthropic-api-key'), 'utf-8')).rejects.toMatchObject({
|
||||
code: 'ENOENT',
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ async function chooseCredentialRef(
|
|||
const prompts = deps.prompts ?? createPromptAdapter();
|
||||
if (args.showPromptInstructions !== false) {
|
||||
io.stdout.write(
|
||||
'Use Up/Down to move, Enter to confirm the current selection, choose Back to return to the previous step, Ctrl+C to exit.\n',
|
||||
'│ Use Up/Down to move, Enter to confirm the current selection, choose Back to return to the previous step, Ctrl+C to exit.\n',
|
||||
);
|
||||
}
|
||||
while (true) {
|
||||
|
|
@ -272,7 +272,7 @@ async function chooseCredentialRef(
|
|||
}
|
||||
if (choice === 'paste') {
|
||||
io.stdout.write(
|
||||
'KTX will save the key in .ktx/secrets/anthropic-api-key with local file permissions, then write a file: reference in ktx.yaml.\n',
|
||||
'│ KTX will save the key in .ktx/secrets/anthropic-api-key with local file permissions, then write a file: reference in ktx.yaml.\n',
|
||||
);
|
||||
const value = await prompts.password({ message: withTextInputNavigation('Anthropic API key') });
|
||||
if (value === undefined) {
|
||||
|
|
@ -394,7 +394,7 @@ export async function runKtxSetupAnthropicModelStep(
|
|||
deps: KtxSetupModelDeps = {},
|
||||
): Promise<KtxSetupModelResult> {
|
||||
if (args.skipLlm) {
|
||||
io.stdout.write('LLM setup skipped.\n');
|
||||
io.stdout.write('│ LLM setup skipped.\n');
|
||||
return { status: 'skipped', projectDir: args.projectDir };
|
||||
}
|
||||
|
||||
|
|
@ -406,7 +406,7 @@ export async function runKtxSetupAnthropicModelStep(
|
|||
!args.anthropicApiKeyFile &&
|
||||
!args.anthropicModel
|
||||
) {
|
||||
io.stdout.write(`LLM ready: yes (${project.config.llm.models.default})\n`);
|
||||
io.stdout.write(`│ LLM ready: yes (${project.config.llm.models.default})\n`);
|
||||
return { status: 'ready', projectDir: args.projectDir };
|
||||
}
|
||||
|
||||
|
|
@ -439,7 +439,7 @@ export async function runKtxSetupAnthropicModelStep(
|
|||
const health = await healthCheck(buildHealthConfig(credential.value, model.model));
|
||||
if (health.ok) {
|
||||
await persistLlmConfig(args.projectDir, credential.ref, model.model);
|
||||
io.stdout.write(`LLM ready: yes (${model.model})\n`);
|
||||
io.stdout.write(`│ LLM ready: yes (${model.model})\n`);
|
||||
return { status: 'ready', projectDir: args.projectDir };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ describe('setup project step', () => {
|
|||
);
|
||||
expect(prompts.text).not.toHaveBeenCalled();
|
||||
expect(result.status === 'ready' ? result.project.config.project : '').toBe('ktx-project');
|
||||
expect(testIo.stdout()).toContain(`KTX will create:\n ${projectDir}`);
|
||||
expect(testIo.stdout()).toContain(`│ KTX will create:\n│ ${projectDir}`);
|
||||
await expect(stat(join(projectDir, 'ktx.yaml'))).resolves.toBeDefined();
|
||||
});
|
||||
|
||||
|
|
@ -209,7 +209,7 @@ describe('setup project step', () => {
|
|||
expect(result.projectDir).toBe(projectDir);
|
||||
expect(prompts.text).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'Project folder path\nPress Escape to go back.\n',
|
||||
message: 'Project folder path\n│ Press Escape to go back.\n│',
|
||||
placeholder: './analytics-ktx, ~/analytics-ktx, or /Users/you/projects/analytics-ktx',
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ async function loadExistingProject(projectDir: string, deps: KtxSetupProjectDeps
|
|||
}
|
||||
|
||||
function printProjectSummary(io: KtxCliIo, projectDir: string): void {
|
||||
io.stdout.write(`Project: ${projectDir}\n`);
|
||||
io.stdout.write(`│ Project: ${projectDir}\n`);
|
||||
}
|
||||
|
||||
async function promptForNewProjectDir(
|
||||
|
|
@ -155,8 +155,8 @@ async function promptForNewProjectDir(
|
|||
const defaultProjectDir = join(projectDir, DEFAULT_NEW_PROJECT_FOLDER_NAME);
|
||||
|
||||
while (true) {
|
||||
io.stdout.write(`Relative paths are resolved from:\n ${projectDir}\n`);
|
||||
io.stdout.write(`Home paths are resolved from:\n ${homeDir}\n`);
|
||||
io.stdout.write(`│ Relative paths are resolved from:\n│ ${projectDir}\n`);
|
||||
io.stdout.write(`│ Home paths are resolved from:\n│ ${homeDir}\n`);
|
||||
const destinationChoice = await prompts.select({
|
||||
message: 'Where should KTX create the project?',
|
||||
options: [
|
||||
|
|
@ -220,7 +220,7 @@ async function promptForNewProjectDir(
|
|||
confirmedCreation = true;
|
||||
}
|
||||
|
||||
io.stdout.write(`KTX will create:\n ${selectedDir}\n`);
|
||||
io.stdout.write(`│ KTX will create:\n│ ${selectedDir}\n`);
|
||||
if (state !== 'non-empty-directory') {
|
||||
const createAction = await prompts.select({
|
||||
message: `Create KTX project at ${selectedDir}?`,
|
||||
|
|
@ -324,7 +324,7 @@ export async function runKtxSetupProjectStep(
|
|||
|
||||
const prompts = deps.prompts ?? createClackSetupProjectPromptAdapter();
|
||||
io.stdout.write(
|
||||
'Use Up/Down to move, Enter to confirm the current selection, choose Back to return to the previous step, Ctrl+C to exit.\n',
|
||||
'│ Use Up/Down to move, Enter to confirm the current selection, choose Back to return to the previous step, Ctrl+C to exit.\n',
|
||||
);
|
||||
while (true) {
|
||||
const choice = await prompts.select({
|
||||
|
|
|
|||
|
|
@ -66,10 +66,10 @@ function connectionNamePrompt(label: string): string {
|
|||
function textInputPrompt(message: string): string {
|
||||
const normalized = message.replace(/\n+$/, '');
|
||||
if (!normalized.includes('\n')) {
|
||||
return `${normalized}\nPress Escape to go back.\n`;
|
||||
return `${normalized}\n│ Press Escape to go back.\n│`;
|
||||
}
|
||||
const [title, ...bodyLines] = normalized.split('\n');
|
||||
return `${title}\n\n${bodyLines.join('\n')}\nPress Escape to go back.\n`;
|
||||
return `${title}\n│\n│ ${bodyLines.join('\n│ ')}\n│ Press Escape to go back.\n│`;
|
||||
}
|
||||
|
||||
describe('setup sources step', () => {
|
||||
|
|
|
|||
|
|
@ -699,7 +699,7 @@ async function runInitialSourceIngestWithRecovery(input: {
|
|||
deps: KtxSetupSourcesDeps;
|
||||
}): Promise<'ready' | 'continue' | 'back' | 'failed'> {
|
||||
while (true) {
|
||||
input.io.stdout.write(`Building context from ${input.connectionId}. Large sources can take a while.\n`);
|
||||
input.io.stdout.write(`│ Building context from ${input.connectionId}. Large sources can take a while.\n`);
|
||||
const ingestCode = await (input.deps.runInitialIngest ?? defaultRunInitialIngest)(
|
||||
input.args.projectDir,
|
||||
input.connectionId,
|
||||
|
|
@ -727,8 +727,8 @@ async function runInitialSourceIngestWithRecovery(input: {
|
|||
continue;
|
||||
}
|
||||
if (action === 'continue') {
|
||||
input.io.stdout.write(`Context source saved without a completed context build for ${input.connectionId}.\n`);
|
||||
input.io.stdout.write(`Run later: ktx ingest ${input.connectionId}\n`);
|
||||
input.io.stdout.write(`│ Context source saved without a completed context build for ${input.connectionId}.\n`);
|
||||
input.io.stdout.write(`│ Run later: ktx ingest ${input.connectionId}\n`);
|
||||
return 'continue';
|
||||
}
|
||||
return 'back';
|
||||
|
|
@ -1355,7 +1355,7 @@ export async function runKtxSetupSourcesStep(
|
|||
try {
|
||||
if (args.skipSources) {
|
||||
await markSourcesComplete(args.projectDir);
|
||||
io.stdout.write('Context source setup skipped.\n');
|
||||
io.stdout.write('│ Context source setup skipped.\n');
|
||||
return { status: 'skipped', projectDir: args.projectDir };
|
||||
}
|
||||
|
||||
|
|
@ -1368,7 +1368,7 @@ export async function runKtxSetupSourcesStep(
|
|||
return { status: 'failed', projectDir: args.projectDir };
|
||||
}
|
||||
if (args.inputMode !== 'disabled') {
|
||||
io.stdout.write(`${message}\n`);
|
||||
io.stdout.write(`│ ${message}\n`);
|
||||
return { status: 'skipped', projectDir: args.projectDir };
|
||||
}
|
||||
}
|
||||
|
|
@ -1392,7 +1392,7 @@ export async function runKtxSetupSourcesStep(
|
|||
return { status: 'missing-input', projectDir: args.projectDir };
|
||||
}
|
||||
await markSourcesComplete(args.projectDir);
|
||||
io.stdout.write('No context sources selected.\n');
|
||||
io.stdout.write('│ No context sources selected.\n');
|
||||
return { status: 'skipped', projectDir: args.projectDir };
|
||||
}
|
||||
|
||||
|
|
@ -1465,7 +1465,7 @@ export async function runKtxSetupSourcesStep(
|
|||
break;
|
||||
}
|
||||
} else {
|
||||
io.stdout.write(`Context source ${connectionId} saved. It will be built during the context build step.\n`);
|
||||
io.stdout.write(`│ Context source ${connectionId} saved. It will be built during the context build step.\n`);
|
||||
}
|
||||
readyConnectionIds.push(connectionId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -715,7 +715,7 @@ describe('setup status', () => {
|
|||
|
||||
expect(projectPrompts.text).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'Project folder path\nPress Escape to go back.\n',
|
||||
message: 'Project folder path\n│ Press Escape to go back.\n│',
|
||||
placeholder: './analytics-ktx, ~/analytics-ktx, or /Users/you/projects/analytics-ktx',
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue