Skip to content

For the complete documentation index, see llms.txt.

Test spec: config file paths

You are a coding agent. Your job is to execute this test spec by running commands in a shell, step by step, from Phase 0 through Phase 5. For each step:

  1. Run the command shown in the code block.
  2. Read the output.
  3. Check every Assert listed for that step.
  4. Record the result: PASS or FAIL with the actual output.

At the end, produce a summary table showing which phases passed and which failed.

If a prerequisite is not met (binary not found, fixture script missing), stop and report that instead of continuing.

The Promptless CLI resolves the config file path in three ways, in this order:

  1. $PROMPTLESS_CLI_DEVELOPER_CONFIG_FILE — escape hatch for tests and for running multiple identities side by side.
  2. $XDG_CONFIG_HOME/promptless/env — XDG-compliant location.
  3. ~/.config/promptless/env — default when neither override is set.

Each phase here is an independent one-shot: set env vars, run login, verify where the file landed, clean up. There’s no carried state — every phase starts from a clean shell.

This spec also pins down the filesystem permissions that must be applied on creation: file mode 0600 and parent directory mode 0700. Anything looser leaks the bearer token to other local users.

For the linear login → whoami → logout flow, see Test spec: authentication happy path. For the fixture contract, see the “Fixture” section of that document.

The promptless binary and the promptless-test-fixture helper must be on $PATH. As a smoke test:

$ promptless --help | head -1

The “drive login” step is repeated in several phases. Define it as a shell function so each phase reads cleanly.

$ drive_login() {
local stderr_file=$1
promptless login --no-browser > "$stderr_file" 2>&1 &
local pid=$!
local url=""
for i in 1 2 3 4 5; do
url=$(grep -o "$URL/cli/auth?[^ ]*" "$stderr_file" 2>/dev/null | head -1)
[ -n "$url" ] && break
sleep 0.2
done
[ -z "$url" ] && { echo "no auth URL captured" >&2; return 1; }
curl -sL "$url" -o /dev/null
wait $pid
}

Phase 0: Start the fake server, clean the environment

Section titled “Phase 0: Start the fake server, clean the environment”
$ unset PROMPTLESS_CLI_API_SECRET
$ unset PROMPTLESS_CLI_DEVELOPER_CONFIG_FILE
$ unset XDG_CONFIG_HOME
$ eval "$(promptless-test-fixture start-fake-server)"
$ export PROMPTLESS_APP_BASE_URL=$URL
$ export PROMPTLESS_API_BASE_URL=$URL

Assert: $URL is non-empty and starts with http://127.0.0.1:.


Phase 1: PROMPTLESS_CLI_DEVELOPER_CONFIG_FILE wins over everything

Section titled “Phase 1: PROMPTLESS_CLI_DEVELOPER_CONFIG_FILE wins over everything”

The developer override is highest priority. When set, neither $XDG_CONFIG_HOME nor the default ~/.config path should be touched.

To prove the latter, set $XDG_CONFIG_HOME to a known-fresh location and verify the developer override path is used instead.

$ DEV_CFG=$(mktemp -u /tmp/promptless-dev-XXXXXX.env)
$ XDG_FRESH=$(mktemp -d /tmp/promptless-xdg-fresh-XXXXXX)
$ export PROMPTLESS_CLI_DEVELOPER_CONFIG_FILE=$DEV_CFG
$ export XDG_CONFIG_HOME=$XDG_FRESH
$ drive_login /tmp/promptless-phase1.stderr

Assert: Login exits 0.

$ test -f $DEV_CFG && echo "dev: found" || echo "dev: missing"
$ ls -1 $XDG_FRESH

Assert:

  • $DEV_CFG exists — the file landed at the developer override path.
  • $XDG_FRESH is empty (or contains no promptless/ subdir) — the XDG path was not touched while the override was set.

Clean up before the next phase.

$ rm -f $DEV_CFG /tmp/promptless-phase1.stderr
$ rm -rf $XDG_FRESH
$ unset PROMPTLESS_CLI_DEVELOPER_CONFIG_FILE

Phase 2: $XDG_CONFIG_HOME is used when developer override is absent

Section titled “Phase 2: $XDG_CONFIG_HOME is used when developer override is absent”

With the developer override unset, the XDG path takes priority over the default. The CLI must also create the promptless subdirectory if it doesn’t exist.

$ export XDG_CONFIG_HOME=$(mktemp -d /tmp/promptless-xdg-XXXXXX)
$ ls -1 $XDG_CONFIG_HOME

Assert: Directory is empty.

$ drive_login /tmp/promptless-phase2.stderr
$ ls -1 $XDG_CONFIG_HOME/promptless/

Assert:

  • Login exits 0.
  • $XDG_CONFIG_HOME/promptless/env exists.
  • The directory contains exactly one entry: env.

A bearer token in a world-readable file is no better than one in /tmp. The CLI applies 0600 to the file and 0700 to the parent directory on every write.

$ stat -f '%Lp' $XDG_CONFIG_HOME/promptless/env 2>/dev/null || stat -c '%a' $XDG_CONFIG_HOME/promptless/env
$ stat -f '%Lp' $XDG_CONFIG_HOME/promptless 2>/dev/null || stat -c '%a' $XDG_CONFIG_HOME/promptless

Assert:

  • File mode is 600.
  • Directory mode is 700.

As an extra check, deliberately loosen the file mode and re-run login — the CLI should re-apply 0600 even if the file already existed.

$ chmod 0644 $XDG_CONFIG_HOME/promptless/env
$ drive_login /tmp/promptless-phase3.stderr
$ stat -f '%Lp' $XDG_CONFIG_HOME/promptless/env 2>/dev/null || stat -c '%a' $XDG_CONFIG_HOME/promptless/env

Assert: Output is 600 — login re-tightened the mode.


Phase 4: Default ~/.config/promptless/env (documented, not exercised)

Section titled “Phase 4: Default ~/.config/promptless/env (documented, not exercised)”

The fallback path when neither override is set is ~/.config/promptless/env.

We deliberately do not exercise this path in the automated test, because doing so would write to the real developer’s home directory and risk overwriting their actual cached credentials.

The resolution logic that produces this path is the same as the XDG path in Phase 2, just with a hardcoded base. The code is src/lib/config.ts:resolveConfigFilePath:

const base =
xdgConfigHome && xdgConfigHome.length > 0 ? xdgConfigHome : join(homedir(), '.config')
return join(base, 'promptless', 'env')

Since Phase 2 proved the XDG branch works and creates the promptless/ subdirectory, the default branch is covered by inspection of this code plus the empty-string fallback test.

Assert: Verify the source code at the path above still uses the same fallback pattern (xdgConfigHome && xdgConfigHome.length > 0 → otherwise homedir() + '.config'). If the implementation has changed, this spec is out of date.


$ promptless-test-fixture stop-fake-server $PID
$ rm -rf $XDG_CONFIG_HOME
$ rm -f /tmp/promptless-phase2.stderr /tmp/promptless-phase3.stderr
$ unset XDG_CONFIG_HOME PROMPTLESS_APP_BASE_URL PROMPTLESS_API_BASE_URL

Assert:

  • Fake server stopped.
  • No /tmp/promptless-* files remain.

PhaseWhat is testedKey assertion
0SetupFake server up; env clean
1Developer override winsFile at $PROMPTLESS_CLI_DEVELOPER_CONFIG_FILE; XDG path untouched
2XDG fallback$XDG_CONFIG_HOME/promptless/env created
3PermissionsFile 0600, dir 0700; re-applied on every login
4Default ~/.config/Documented via source-code inspection (not exercised)
5TeardownServer stopped, temp dirs removed