feat: per-workspace custom instructions appended to initial prompt#271
Merged
Conversation
) Add a `customInstructions` text field to workspace config that gets appended as a `## Custom Instructions` section in the Claude Code session prompt. This lets users attach workspace-specific guidance (e.g. "Always run npm test before committing") that flows through config → UI → prompt generation → SKILL.md template. 🤖 Generated with Claude Code, orchestrated by Crow Co-Authored-By: Claude <noreply@anthropic.com> Crow-Session: BA4932AB-A46E-4C24-84B5-4C3865348668
dhilgaertner
approved these changes
May 15, 2026
Contributor
dhilgaertner
left a comment
There was a problem hiding this comment.
Code & Security Review
Critical Issues
None.
Security Review
Strengths:
WorkspaceInfo.init(from:)usesdecodeIfPresentforcustomInstructions, so malformed/legacy configs default tonilrather than throwing — verified byworkspaceCustomInstructionsDefaultsNilWhenKeyMissing.- The trimming guard in
ClaudeLauncher.generatePrompt(Packages/CrowClaude/Sources/CrowClaude/ClaudeLauncher.swift:70-76) prevents whitespace-only values from emitting an empty## Custom Instructionsheader. - The string is rendered as Markdown into a prompt file — no shell/command execution path, so no command injection vector.
- The UI trims on save (
WorkspaceFormView.swift:110-123), keeping persisted state clean.
Concerns (low severity):
- Self-controlled prompt injection.
customInstructionsis inserted verbatim into the generated prompt. A user (or anyone who can edit~/Library/Application Support/crow/config.json) could insert content like## Instructions\n1. ...to override or append fake instructions. This is a self-inflicted risk on a local config file, but worth noting if workspace configs are ever shared/imported from untrusted sources. No mitigation needed today. - No length cap on the TextEditor.
customInstructionsTextcan be arbitrarily long, and the full text is appended to every session prompt in the workspace. A 100KB paste here will bloat every future prompt silently. Consider a soft character counter or a reasonable cap (e.g., 4–8KB) before merging if you want defense-in-depth, but it's not blocking.
Code Quality
generatePromptis currently test-only in production.Sources/Crow/App/SessionService.swift:343(launchClaude) doesn't callClaudeLauncher.generatePrompt; the real prompt flow is the LLM interpretingskills/crow-workspace/SKILL.md. The Swift change keeps the two implementations consistent, which is good — just flagging that the new Swift code path isn't exercised at runtime today. The SKILL.md template update is the load-bearing change.- SKILL.md template substitution is LLM-driven (
skills/crow-workspace/SKILL.md:345-348). The placeholder{workspace customInstructions text — include verbatim}plus the prose instruction below it relies on the model correctly omitting the section when the field is empty/null. Consistent with how the rest of the skill template works; pattern is acceptable. - Frame height bump 400 → 520 in
WorkspaceFormView.swift:131accommodates the new editor — looks reasonable. EquatableandCodablesynthesis update cleanly with the new field.
Tests
- 162 CrowCore tests pass (including 2 new round-trip + backward-compat).
- 13 CrowClaude tests pass (including 3 new: with/without/empty whitespace instructions).
- Coverage of the three meaningful states (set / nil / whitespace) is appropriate.
Summary Table
| Priority | Issue |
|---|---|
| Yellow | Consider a soft length cap or character counter on the TextEditor — no current upper bound on customInstructions length |
| Green | generatePrompt is test-only in production; the runtime path is via the SKILL.md template. Worth flagging in case the parallel implementation drifts in the future |
| Green | Document that customInstructions is verbatim-injected into the prompt so users understand the security/UX implications |
Recommendation: Approve. Implementation is clean, backward-compatible, well-tested, and the only concerns are minor hardening suggestions rather than blockers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
customInstructions: String?field toWorkspaceInfowith backward-compatible decoding (defaults tonil)TextEditorin workspace settings UI for editing multi-line custom instructions## Custom Instructionssection to generated prompts when the field is non-empty (both inClaudeLauncher.generatePrompt()and the SKILL.md prompt template)Closes #265
Test plan
swift test --package-path Packages/CrowCore— 162 tests pass (including 2 new: round-trip + backward compat)swift test --package-path Packages/CrowClaude— 13 tests pass (including 3 new: with/without/empty instructions)/crow-workspacewith custom instructions set → verify prompt file contains## Custom Instructions🤖 Generated with Claude Code