feat: allow setting system prompt from a file #94

Open
rcsheets wants to merge 2 commits from feat/system-prompt-from-file into main
Owner

glchat could only take a system prompt inline, with --system. A prompt of any length lives better in a file, so this adds --system-file <path>:

glchat --system-file ~/prompts/reviewer.md

Behaviour

  • The file is read by glchat.ReadTextFile, the checks /read already applied, now factored out so both share them: regular files only, at most 1 MiB, UTF-8 without NULs, non-empty. Trailing whitespace is trimmed, so the newline an editor leaves is not part of the prompt, and a leading ~ is expanded (the shell does not expand it in --system-file=~/x).

  • --system and --system-file are mutually exclusive. Ranking one above the other would leave the losing flag unread, and a mistyped path would then start a conversation with no mention of the file.

  • An empty name (--system-file "", typically an unset variable in a script) is an error rather than "no system prompt".

  • A file that cannot be used is reported before discovery and before the TUI starts, against the flag that named it:

    glchat: --system-file: /tmp/pipe is not a regular file
    

The regular-file check matters more here than it does for /read: this read happens before the UI is up, so a plain os.ReadFile on a fifo is a client that prints nothing and never starts.

/read itself is unchanged in behaviour; its error messages are byte-for-byte the same, now produced by wrapping ReadTextFile's error with the /read: prefix.

Testing

  • internal/glchat: ReadTextFile trims only the tail and reports the on-disk size; a device is refused from the stat without being opened. The existing /read rejection table still passes unchanged.
  • cmd/glchat (new test file): the two flags are refused together; a missing file, a directory, a device, an empty file and an empty name each fail with an error led by --system-file:. These cases all return before discovery, so nothing is dialled.
  • gofmt, go build ./..., go vet ./... and go test ./... are clean.
  • By hand against the built binary: a fifo passed to --system-file is refused immediately, where it previously blocked.

README and AGENTS.md are updated; AGENTS.md records that any new path putting a file's text in front of the model goes through ReadTextFile.

🤖 Generated with Claude Code

glchat could only take a system prompt inline, with `--system`. A prompt of any length lives better in a file, so this adds `--system-file <path>`: glchat --system-file ~/prompts/reviewer.md ## Behaviour - The file is read by `glchat.ReadTextFile`, the checks `/read` already applied, now factored out so both share them: regular files only, at most 1 MiB, UTF-8 without NULs, non-empty. Trailing whitespace is trimmed, so the newline an editor leaves is not part of the prompt, and a leading `~` is expanded (the shell does not expand it in `--system-file=~/x`). - `--system` and `--system-file` are mutually exclusive. Ranking one above the other would leave the losing flag unread, and a mistyped path would then start a conversation with no mention of the file. - An empty name (`--system-file ""`, typically an unset variable in a script) is an error rather than "no system prompt". - A file that cannot be used is reported before discovery and before the TUI starts, against the flag that named it: glchat: --system-file: /tmp/pipe is not a regular file The regular-file check matters more here than it does for `/read`: this read happens before the UI is up, so a plain `os.ReadFile` on a fifo is a client that prints nothing and never starts. `/read` itself is unchanged in behaviour; its error messages are byte-for-byte the same, now produced by wrapping `ReadTextFile`'s error with the `/read:` prefix. ## Testing - `internal/glchat`: `ReadTextFile` trims only the tail and reports the on-disk size; a device is refused from the stat without being opened. The existing `/read` rejection table still passes unchanged. - `cmd/glchat` (new test file): the two flags are refused together; a missing file, a directory, a device, an empty file and an empty name each fail with an error led by `--system-file:`. These cases all return before discovery, so nothing is dialled. - `gofmt`, `go build ./...`, `go vet ./...` and `go test ./...` are clean. - By hand against the built binary: a fifo passed to `--system-file` is refused immediately, where it previously blocked. README and AGENTS.md are updated; AGENTS.md records that any new path putting a file's text in front of the model goes through `ReadTextFile`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: allow setting system prompt from a file
All checks were successful
ci / test_and_build (pull_request) Successful in 26s
9760238de5
Collaborator

The latest review couldn't run.

Jump to the current review

Review history
  • 2026-09-27 21:34 UTC · safety check · No verdict returned · r-b98bfc-fa072f
  • 2026-09-27 23:03 UTC · safety check · couldn't run · r-b9a0d7-40cfdc
  • 2026-09-27 23:06 UTC · safety check · couldn't run · r-b9a188-0ccbc7
  • 2026-09-27 23:08 UTC · safety check · failed · r-b9a205-d05dd0
  • 2026-09-28 01:38 UTC · safety check · failed · r-b9c52a-87c7be
  • 2026-09-28 01:42 UTC · safety check · failed · r-b9c5f1-ef593a
  • 2026-09-28 01:47 UTC · safety check · LGTM · r-b9c715-8031ee
  • 2026-09-28 01:50 UTC · safety check · Flag · r-b9c7fb-7b37cf
  • 2026-10-01 09:06 UTC · safety check · couldn't run · r-be22a4-af2ea1
  • 2026-10-01 22:32 UTC · safety check · couldn't run · r-bedf90-89a512
  • 2026-10-01 22:33 UTC · safety check · couldn't run · r-bedfd1-0924e6 · current
<!-- pr-reviewer:history --> **The latest review couldn't run.** [Jump to the current review](#issuecomment-4716) <details><summary>Review history</summary> - 2026-09-27 21:34 UTC · safety check · No verdict returned · `r-b98bfc-fa072f` - 2026-09-27 23:03 UTC · safety check · couldn't run · `r-b9a0d7-40cfdc` - 2026-09-27 23:06 UTC · safety check · couldn't run · `r-b9a188-0ccbc7` - 2026-09-27 23:08 UTC · safety check · failed · `r-b9a205-d05dd0` - 2026-09-28 01:38 UTC · safety check · failed · `r-b9c52a-87c7be` - 2026-09-28 01:42 UTC · safety check · failed · `r-b9c5f1-ef593a` - 2026-09-28 01:47 UTC · safety check · LGTM · `r-b9c715-8031ee` - 2026-09-28 01:50 UTC · safety check · Flag · `r-b9c7fb-7b37cf` - 2026-10-01 09:06 UTC · safety check · couldn't run · `r-be22a4-af2ea1` - 2026-10-01 22:32 UTC · safety check · couldn't run · `r-bedf90-89a512` - 2026-10-01 22:33 UTC · safety check · couldn't run · `r-bedfd1-0924e6` · **current** </details>
Author
Owner

@pr-reviewer-bot review

@pr-reviewer-bot review
Collaborator

Superseded — a newer review is further down this thread. This pass is kept on the dashboard as r-b9a188-0ccbc7.

<!-- pr-reviewer:superseded --> *Superseded — a newer review is further down this thread. This pass is kept on the dashboard as `r-b9a188-0ccbc7`.*
Author
Owner

@pr-reviewer-bot review

@pr-reviewer-bot review
Collaborator

Superseded — a newer review is further down this thread. This pass is kept on the dashboard as r-b9a205-d05dd0.

<!-- pr-reviewer:superseded --> *Superseded — a newer review is further down this thread. This pass is kept on the dashboard as `r-b9a205-d05dd0`.*
Author
Owner

@pr-reviewer-bot review

@pr-reviewer-bot review
Collaborator

Superseded — a newer review is further down this thread. This pass is kept on the dashboard as r-b9c52a-87c7be.

<!-- pr-reviewer:superseded --> *Superseded — a newer review is further down this thread. This pass is kept on the dashboard as `r-b9c52a-87c7be`.*
Author
Owner

@pr-reviewer-bot review

@pr-reviewer-bot review
Collaborator

Superseded — a newer review is further down this thread. This pass is kept on the dashboard as r-b9c5f1-ef593a.

<!-- pr-reviewer:superseded --> *Superseded — a newer review is further down this thread. This pass is kept on the dashboard as `r-b9c5f1-ef593a`.*
Author
Owner

@pr-reviewer-bot review

@pr-reviewer-bot review
Collaborator

Superseded — a newer review is further down this thread. This pass is kept on the dashboard as r-b9c7fb-7b37cf.

<!-- pr-reviewer:superseded --> *Superseded — a newer review is further down this thread. This pass is kept on the dashboard as `r-b9c7fb-7b37cf`.*
rcsheets force-pushed feat/system-prompt-from-file from 9760238de5
All checks were successful
ci / test_and_build (pull_request) Successful in 26s
to 2ac7166bc0
All checks were successful
ci / test_and_build (pull_request) Successful in 28s
2026-09-28 01:46:59 +00:00
Compare
Author
Owner

@pr-reviewer-bot review again

@pr-reviewer-bot review again
Collaborator

Superseded — a newer review is further down this thread. This pass is kept on the dashboard as r-bedfd1-0924e6.

<!-- pr-reviewer:superseded --> *Superseded — a newer review is further down this thread. This pass is kept on the dashboard as `r-bedfd1-0924e6`.*
feat(glchat): read --system-file the way /read reads a file
All checks were successful
ci / test_and_build (pull_request) Successful in 30s
5f425ac359
--system-file read its file with a bare os.ReadFile, and only when
--system was empty. That left several ways for it to go wrong quietly:

- A fifo or device blocked the read, before the TUI was up, so the client
  printed nothing and never started.
- With both flags given, --system won and the file was never opened, so a
  mistyped path went unreported.
- The file's trailing newline became part of the system prompt, and an
  empty file meant no system prompt at all, without a word.

/read already had the checks this needs. They move into an exported
glchat.ReadTextFile -- regular files only, at most 1 MiB, UTF-8 without
NULs, non-empty, trailing whitespace trimmed, a leading ~ expanded -- which
/read and --system-file now both call. /read's messages are unchanged.

--system and --system-file are now mutually exclusive rather than ranked,
and an empty --system-file name is an error rather than "no prompt".
Errors lead with the flag, as the other commands' do:

    glchat: --system-file: /tmp/pipe is not a regular file

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rcsheets force-pushed feat/system-prompt-from-file from 5f425ac359
All checks were successful
ci / test_and_build (pull_request) Successful in 30s
to b17afdafe3
All checks were successful
ci / test_and_build (pull_request) Successful in 30s
2026-10-01 22:32:46 +00:00
Compare
Author
Owner

@pr-reviewer-bot review

@pr-reviewer-bot review
Collaborator

Automated review by pr-reviewer v0.52.3 | Safety Check | Ministral 3 Instruct | tracking id r-bedfd1-0924e6
This is an AI-generated review and may contain mistakes.

Status: ❌ Failed


This review couldn't be completed: that model isn't loaded on the inference service right now, and no alternate model was able to review it either. Consider splitting this PR into smaller changes. Tracking id r-bedfd1-0924e6.

Comment @pr-reviewer-bot retry to try again.

<!-- pr-reviewer:review --> *Automated review by [pr-reviewer](https://git.brooktrails.org/brooktrails/pr-reviewer) v0.52.3 | Safety Check | Ministral 3 Instruct | tracking id `r-bedfd1-0924e6`* *This is an AI-generated review and may contain mistakes.* **Status:** ❌ Failed --- This review couldn't be completed: that model isn't loaded on the inference service right now, and no alternate model was able to review it either. Consider splitting this PR into smaller changes. Tracking id `r-bedfd1-0924e6`. Comment `@pr-reviewer-bot retry` to try again.
All checks were successful
ci / test_and_build (pull_request) Successful in 30s
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/system-prompt-from-file:feat/system-prompt-from-file
git switch feat/system-prompt-from-file
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
brooktrails/gllm!94
No description provided.