Add custom autocomplete rules (tags + suggestions)#239
Merged
Conversation
Users can now add short imperative style rules (e.g. "Use British spelling") that render as additional prompt directives. A new default-on set ships and is what Reset restores. - CustomRulesCatalog: defaultRules, suggestedPalette, normalize (trim, dedup case-insensitive, cap 10 x 60 chars) - SuggestionSettingsModel: customRules published prop, cotabbyCustomRules persistence with first-run seeding, add/remove/set/reset mutators, threaded into snapshot + publisher - SuggestionRequest / SuggestionSettingsSnapshot carry customRules - Both renderers emit a "Your style preferences:" section after the base rules with an explicit subordination line (prompt-injection guard) - CustomRulesTests covers normalization + render ordering/placement
Removable rule chips, freeform add field (Enter/comma to commit, disabled at the 10-rule cap), tappable suggestion chips from the palette, and a Reset that restores the default-on set (shown only when rules differ from defaults). Wired into the Settings profile section and the onboarding profile step. Self-contained — does not touch TagsInputView.
defaultRules is now empty — rules are fully opt-in, nothing is applied on a fresh install. The editor's button becomes "Clear" (restores the empty default), shown only when rules exist. Expanded the palette to span tone, length, formatting, locale spelling, and punctuation so most users find a fitting suggestion without typing their own. Some entries are intentionally mutually exclusive (casual vs professional, British vs American) — they're choices, not a stack.
These icon-export source files were accidentally committed; they are not referenced by the build and don't belong in this feature branch.
…down) A new Language picker forces the completion language so smaller OSS models don't drift back to the input's language. English is the default and emits no prompt directive; any other choice injects "Always write the continuation in <language>…" — in llama's late Final-instruction block and in Foundation Models' high-priority instructions channel (where it supersedes the base "match the existing language" rule). - SuggestionLanguage enum: 13 popular languages, native + English labels, BCP-47-ish raw values, promptInstruction (nil for English) - SuggestionSettingsModel: responseLanguage + cotabbyResponseLanguage persistence, setResponseLanguage, snapshot + publisher - SuggestionRequest carries languageInstruction; factory computes it - Both renderers emit the directive; Settings gets a Language picker - Tests for enum + both renderers
Greptile flagged Clear-vs-Reset inconsistencies. Rules are opt-in with an empty baseline, so the operation is a clear, not a restore: - Rename resetRules() -> clearRules() to match the editor's Clear button. - Fix contradictory/stale doc comments (catalog header, editor header, resetRules docstring) that claimed default-on rules and a Reset action. - Document the first-launch seeding caveat: the unconditional persist writes the seed back, so non-empty defaults must be seeded before that write.
cbc28b1 to
bc3fe0f
Compare
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
Brings back custom AI instructions as rules — short imperative style directives (e.g. "Use British spelling", "Never use em dashes") added as removable tag chips rather than a freeform text block. A small default-on set ships; the rest are tappable suggestions. Rules render as extra prompt directives in both engines, subordinated below the base autocomplete/safety rules.
What's included
CustomRulesCatalog—defaultRules(ship-on, what Reset restores),suggestedPalette, andnormalize(trim, case-insensitive dedup, cap 10 × 60 chars).SuggestionSettingsModel—customRulespublished property,cotabbyCustomRulespersistence with first-run seeding (absent key → seed defaults; present-but-empty → respect cleared),addRule/removeRule/setRules/resetRules, threaded into the snapshot + publisher.customRulesonSuggestionRequestandSuggestionSettingsSnapshot;SuggestionRequestFactorythreads it through.LlamaPromptRendererandFoundationModelPromptRendereremit aYour style preferences:section after the base rules with an explicit subordination line ("never break the rules above") as a prompt-injection guard. For Foundation Models the rules go in the high-priority instructions channel, not the per-request prompt.CustomRulesEditor(chips + freeform add + suggestion chips + Reset) in Settings (under Name) and onboarding. Independent of the existingTagsInputView, which is left untouched.Validation
New
CustomRulesTestscovers normalization (trim/dedup/cap) and renderer placement (rules after base rules; FM rules in instructions not prompt). Registered inproject.pbxproj.Linked issues
Brings back a previously-requested custom-instructions feature.
Risk / rollout notes
Write concisely,Match my existing tone). These lightly overlap the base prompt's tone/length rules by design — they exist so Reset has a meaningful target and new users see the feature populated. Easy to trim to empty if you'd rather defaults be silent.userName), surfaced in the editor copy.normalizeis the single chokepoint enforcing it.Greptile Summary
This PR adds custom autocomplete rules — short imperative style directives stored as removable chip tags — plus a response language selector. Rules are normalized through a single
CustomRulesCatalog.normalizechokepoint (trim, case-insensitive dedup, 10 × 60-char cap) and rendered as subordinated bullet directives in both the Llama and Foundation Model prompt backends.CustomRulesCatalog,SuggestionLanguage, andCustomRulesEditorintroduce the full rules pipeline: catalog/normalization → settings persistence → snapshot propagation → request factory threading → renderer emission with a prompt-injection subordination guard.tabby*namespace tocotabby*(continuing the work from Rename all app UserDefaults keys to the cotabby prefix #238), which resets stored settings for existing users; theonboardingCompleted→cotabbyOnboardingCompletedrename inWelcomeCoordinatoris the highest-impact instance, as it will re-trigger the welcome wizard for every user on upgrade.CustomRulesTests) cover normalization edge cases and renderer placement for both backends, with clear assertions that rules appear after base rules and only in thesessionInstructionschannel for Foundation Models.Confidence Score: 4/5
Safe to merge after verifying whether the onboarding re-trigger for existing users is intentional.
The rules pipeline, normalization, and prompt rendering are all well-implemented and tested. The one concern is the rename of the onboardingCompleted defaults key to cotabbyOnboardingCompleted in WelcomeCoordinator without migrating the existing value — every user who has already completed onboarding will be shown the welcome wizard again on their next launch. This is a real, present regression. If the team has intentionally chosen to re-onboard all users to surface the new rules/profile step, documenting that decision would close the concern; otherwise a one-time migration read of the old key is needed.
Cotabby/App/Coordinators/WelcomeCoordinator.swift — the onboardingCompleted key rename should be verified as intentional or given a one-time migration.
Important Files Changed
Sequence Diagram
sequenceDiagram participant User participant CustomRulesEditor participant SuggestionSettingsModel participant CustomRulesCatalog participant SuggestionRequestFactory participant LlamaPromptRenderer participant FoundationModelPromptRenderer User->>CustomRulesEditor: Add / remove / clear rule CustomRulesEditor->>SuggestionSettingsModel: addRule() / removeRule() / clearRules() SuggestionSettingsModel->>CustomRulesCatalog: normalize(rules) CustomRulesCatalog-->>SuggestionSettingsModel: [String] (trimmed, deduped, capped) SuggestionSettingsModel->>SuggestionSettingsModel: persist to UserDefaults (cotabbyCustomRules) Note over SuggestionSettingsModel: Publisher emits new SuggestionSettingsSnapshot SuggestionRequestFactory->>SuggestionSettingsModel: read snapshot.customRules + responseLanguage alt Llama engine SuggestionRequestFactory->>LlamaPromptRenderer: prompt(..., customRules, languageInstruction) LlamaPromptRenderer-->>SuggestionRequestFactory: prompt with Your style preferences section else Foundation Model engine SuggestionRequestFactory->>FoundationModelPromptRenderer: sessionInstructions(for: request) FoundationModelPromptRenderer-->>SuggestionRequestFactory: instructions with rules + language directive endComments Outside Diff (1)
Cotabby/App/Coordinators/WelcomeCoordinator.swift, line 29-56 (link)Renaming the key from
"onboardingCompleted"to"cotabbyOnboardingCompleted"without migrating the stored value means any user who completed onboarding will haveisOnboardingCompletedreturnfalseon the first launch after the update, sopresentIfNeeded()will unconditionally show the welcome wizard.The comment at line 50–54 justifies skipping the even-older
hasShownWelcomeWindowkey because that flag was set at presentation time (not completion), so skipping it was intentional. That argument does not apply here —onboardingCompletedis set only at completion, making it a clean proxy for "already set up". If re-showing onboarding for all upgrading users is deliberate (e.g., to introduce the new rules/profile step), it is worth documenting that intent alongside thehasShownWelcomeWindowrationale. If it is unintentional, a one-time migration that reads the old key and writes the new one on first launch would prevent the regression.Reviews (4): Last reviewed commit: "Align custom-rules naming and docs to th..." | Re-trigger Greptile