fix(cli): name SIM_CONFIG_DIR in help, and fix the similarity score width (#6795)

Two loose ends from the command-surface audit.

`--help` stated where the profile files live and named only `~/.sim`, so it
was wrong for anyone who had set `SIM_CONFIG_DIR` — which every CI job
pointing the CLI at a scratch directory has. The variable is documented in
the README and the guides; help was the one place that omitted it.

`knowledge search` printed the raw similarity double, `0.2818957269585687`:
a nineteen-character column whose last dozen digits cannot separate one
result from another. A `score` format fixes it to four decimals, the width
`cost` already uses, so the column stays put down the page. `json` and
`yaml` still carry the full double, which is what a script compares.
This commit is contained in:
Waleed
2026-08-17 17:07:58 -07:00
committed by GitHub
parent aa367a4c92
commit 3abac09dc3
7 changed files with 45 additions and 3 deletions
@@ -4,6 +4,7 @@
import type { Command } from 'commander'
import { describe, expect, it } from 'vitest'
import { V2_OPERATIONS, type V2OperationName } from '../generated/v2-api'
import { HELP_EPILOGUE } from '../program'
import { buildGeneratedCommands } from '../runtime/build'
import { flagNameFor, flagSpecFor } from '../runtime/request'
import type { OperationSpec } from '../runtime/types'
@@ -193,4 +194,12 @@ describe('folder-path fields', () => {
expect(paths).toContain('serviceId')
expect(paths).toContain('providerId')
})
it('mentions the variable that moves the profile files, since help names a path', () => {
// The epilogue states where the files live, and SIM_CONFIG_DIR moves both.
// Naming only ~/.sim made help wrong for anyone who had set it — including
// every CI job that points the CLI at a scratch directory.
expect(HELP_EPILOGUE).toContain('~/.sim/config')
expect(HELP_EPILOGUE).toContain('SIM_CONFIG_DIR')
})
})
+1 -1
View File
@@ -259,7 +259,7 @@ export const CLI_CONTRACT: CliContract = {
},
itemsPath: 'results',
columns: [
{ header: 'score', path: 'similarity' },
{ header: 'score', path: 'similarity', format: 'score' },
{ header: 'document', path: 'documentName' },
{ header: 'chunk', path: 'chunkIndex' },
{ header: 'content' },
+5
View File
@@ -123,6 +123,10 @@ export interface ColumnSpec {
* wire encoding for the human formats, so a folder no longer prints as
* `/cli-test-a/nested%20one` in the same row as the `nested one` the server
* put in the adjacent name column.
*
* `score` fixes a similarity to four decimals. The raw double arrives as
* `0.2818957269585687`, a nineteen-character column whose last dozen digits
* cannot separate one result from another.
*/
format?:
| 'auto'
@@ -134,6 +138,7 @@ export interface ColumnSpec {
| 'count'
| 'trace-count'
| 'folder-path'
| 'score'
}
export interface BodyVariantSpec {
+2 -1
View File
@@ -13,7 +13,8 @@ export const PROGRAM_DESCRIPTION = 'Talk to the Sim API from your terminal'
export const HELP_EPILOGUE = `
Profiles work like the AWS CLI: settings live in ~/.sim/config, keys in
~/.sim/credentials (0600). Select one with -P, --profile, or SIM_PROFILE.
~/.sim/credentials (0600), or under SIM_CONFIG_DIR when it is set. Select one
with -P, --profile, or SIM_PROFILE.
Examples:
$ sim login Authorize the default profile
+3 -1
View File
@@ -881,7 +881,9 @@ describe('contract-selected list rendering', () => {
totalResults: 1,
})
expect(printed).toEqual(['0.91\tpolicy.md\t2\tRefunds are available for 30 days.'])
// Four decimals, fixed, like the `cost` column: a similarity is compared
// against its neighbours, so the width has to stay put down the column.
expect(printed).toEqual(['0.9100\tpolicy.md\t2\tRefunds are available for 30 days.'])
})
it('renders row matches as rows', async () => {
@@ -172,6 +172,29 @@ describe('a declared field that the API stops returning', () => {
})
})
describe('a similarity score, at a width a person can read', () => {
const results = {
results: [
{ similarity: 0.2818957269585687, documentName: 'a.md', chunkIndex: 0, content: 'x' },
],
}
const spec = CLI_CONTRACT.searchKnowledge as CommandSpec
it('fixes the score to four decimals in the table', () => {
// The raw double is nineteen characters wide and its last dozen digits
// separate nothing: every row shares them to within a rounding error.
renderResult('searchKnowledge', 'table', results, spec)
const [, row] = tableLines()
expect(row).toContain('0.2819')
expect(row).not.toContain('0.2818957269585687')
})
it('leaves the full double in json, which is what a script compares', () => {
renderResult('searchKnowledge', 'json', results, spec)
expect(JSON.parse(logged[0]).results[0].similarity).toBe(0.2818957269585687)
})
})
describe('folder paths are shown by name, but piped in wire form', () => {
const folders = [
{
+2
View File
@@ -80,6 +80,8 @@ function renderCell(
return bool(value as boolean | null)
case 'cost':
return typeof value === 'number' ? `$${value.toFixed(4)}` : text(null)
case 'score':
return typeof value === 'number' ? value.toFixed(4) : text(null)
case 'count':
return Array.isArray(value) ? String(value.length) : text(null)
case 'folder-path':