Skip to content

feat: per-workspace custom instructions appended to initial prompt#271

Merged
dgershman merged 1 commit into
mainfrom
feature/crow-265-custom-instructions
May 15, 2026
Merged

feat: per-workspace custom instructions appended to initial prompt#271
dgershman merged 1 commit into
mainfrom
feature/crow-265-custom-instructions

Conversation

@dgershman
Copy link
Copy Markdown
Collaborator

Summary

  • Adds customInstructions: String? field to WorkspaceInfo with backward-compatible decoding (defaults to nil)
  • Adds TextEditor in workspace settings UI for editing multi-line custom instructions
  • Appends ## Custom Instructions section to generated prompts when the field is non-empty (both in ClaudeLauncher.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)
  • Manual: open Settings → edit workspace → verify TextEditor appears, saves, and persists
  • Manual: /crow-workspace with custom instructions set → verify prompt file contains ## Custom Instructions

🤖 Generated with Claude Code

)

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
Copy link
Copy Markdown
Contributor

@dhilgaertner dhilgaertner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code & Security Review

Critical Issues

None.

Security Review

Strengths:

  • WorkspaceInfo.init(from:) uses decodeIfPresent for customInstructions, so malformed/legacy configs default to nil rather than throwing — verified by workspaceCustomInstructionsDefaultsNilWhenKeyMissing.
  • The trimming guard in ClaudeLauncher.generatePrompt (Packages/CrowClaude/Sources/CrowClaude/ClaudeLauncher.swift:70-76) prevents whitespace-only values from emitting an empty ## Custom Instructions header.
  • 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. customInstructions is 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. customInstructionsText can 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

  • generatePrompt is currently test-only in production. Sources/Crow/App/SessionService.swift:343 (launchClaude) doesn't call ClaudeLauncher.generatePrompt; the real prompt flow is the LLM interpreting skills/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:131 accommodates the new editor — looks reasonable.
  • Equatable and Codable synthesis 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.


🤖 Reviewed by Crow via Claude Code

@dgershman dgershman merged commit 8615da6 into main May 15, 2026
3 checks passed
@dgershman dgershman deleted the feature/crow-265-custom-instructions branch May 15, 2026 00:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: per-workspace custom instructions appended to initial prompt

2 participants