diff --git a/AGENTS.md b/AGENTS.md index d7dccc8ec7..ef7bd8ed2f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -109,6 +109,13 @@ it **skips anything assigned to a SCREAMING_CASE identifier** — so a those by eye. The pseudo-locale (Settings → General → Appearance, dev/e2e builds) is the completeness check. Full guide: [`docs/i18n.md`](docs/i18n.md). +UI punctuation is plain and intentional: do not use the Unicode em dash (U+2014) +in user-facing copy or locale values. Use a period, colon, comma, semicolon, or +parentheses instead. This applies to English, pseudo, and translated catalogs, +rendered fallback strings. `pnpm run i18n:check` enforces it. Historical +`CHANGELOG.md` entries are excluded because the changelog is generated release +history and remains immutable. + ### Knowledge - **Public docs:** Website-ready user documentation lives in `docs/public/**`. Use `/docs-maintainer` when a change affects CLI commands, config keys, install/deploy flows, workflows, executors, public APIs, screenshots, or user-facing terminology. - **Specs:** Feature specs live in `docs/specs//spec.md` — the durable "what & why" of a feature, written before coding. Use `/spec` to write or update a spec. See `docs/specs/INDEX.md`. diff --git a/CHANGELOG.md b/CHANGELOG.md index ce21efe6db..5a0498baa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -681,7 +681,7 @@ All notable changes to Kandev. - improve ACP model configuration summaries ([#1723](https://github.com/kdlbs/kandev/pull/1723)) - warn about workflow replay cycles ([#1720](https://github.com/kdlbs/kandev/pull/1720)) - route agents through execution profiles ([#1725](https://github.com/kdlbs/kandev/pull/1725)) -- richer frontend SDK — page chrome, nav icons, more host UI ([#1759](https://github.com/kdlbs/kandev/pull/1759)) +- richer frontend SDK: page chrome, nav icons, more host UI ([#1759](https://github.com/kdlbs/kandev/pull/1759)) - add repository defaults to saved GitHub queries ([#1758](https://github.com/kdlbs/kandev/pull/1758)) - add cmd+shift+g shortcut to open the task's github pr ([#1757](https://github.com/kdlbs/kandev/pull/1757)) - typed capability-gated Host data API (ADR 0043) ([#1753](https://github.com/kdlbs/kandev/pull/1753)) @@ -1322,13 +1322,13 @@ All notable changes to Kandev. - inject MCP servers in CLI passthrough mode per agent ([#1158](https://github.com/kdlbs/kandev/pull/1158)) - make chat reverse-i-search shortcut configurable ([#1255](https://github.com/kdlbs/kandev/pull/1255)) -- full GitLab integration — parity with GitHub ([#1120](https://github.com/kdlbs/kandev/pull/1120)) +- full GitLab integration: parity with GitHub ([#1120](https://github.com/kdlbs/kandev/pull/1120)) - support ** and brace globs in copy_files patterns ([#1248](https://github.com/kdlbs/kandev/pull/1248)) - resizable review dialog sidebar with persistence ([#1245](https://github.com/kdlbs/kandev/pull/1245)) - chat history nav with ArrowUp/Down and Ctrl+R fuzzy search ([#1246](https://github.com/kdlbs/kandev/pull/1246)) - include associated PRs in task-listing tools ([#1236](https://github.com/kdlbs/kandev/pull/1236)) - add reverse direction to recent task switcher ([#1241](https://github.com/kdlbs/kandev/pull/1241)) -- multi-branch tasks — N (repo, branch) pairs per task ([#1226](https://github.com/kdlbs/kandev/pull/1226)) +- multi-branch tasks: N (repo, branch) pairs per task ([#1226](https://github.com/kdlbs/kandev/pull/1226)) - add voice mode to task create dialog ([#1230](https://github.com/kdlbs/kandev/pull/1230)) - service-managed UI self-update ([#1210](https://github.com/kdlbs/kandev/pull/1210)) - show inline download progress for Whisper Web model ([#1228](https://github.com/kdlbs/kandev/pull/1228)) @@ -1386,7 +1386,7 @@ All notable changes to Kandev. - retry transient provider 529 errors with visible backoff ([#1173](https://github.com/kdlbs/kandev/pull/1173)) - per-session ACP debug logs with rotation and retention ([#1172](https://github.com/kdlbs/kandev/pull/1172)) - auto-expand the first group in the changes panel ([#1169](https://github.com/kdlbs/kandev/pull/1169)) -- remote tab in task-create dialog — multi-row GitHub repo picker ([#1116](https://github.com/kdlbs/kandev/pull/1116)) +- remote tab in task-create dialog: multi-row GitHub repo picker ([#1116](https://github.com/kdlbs/kandev/pull/1116)) - annotate debug logs with task_id for per-task filtering ([#1168](https://github.com/kdlbs/kandev/pull/1168)) - add PR closed banner and hide CI chip on terminal state ([#1161](https://github.com/kdlbs/kandev/pull/1161)) - collapse mobile task search into a topbar icon ([#1157](https://github.com/kdlbs/kandev/pull/1157)) @@ -1502,7 +1502,7 @@ All notable changes to Kandev. - add per-agent-profile environment variables ([#1040](https://github.com/kdlbs/kandev/pull/1040)) by @Foprta - copy gitignored files from repo into new worktrees ([#946](https://github.com/kdlbs/kandev/pull/946)) ([#950](https://github.com/kdlbs/kandev/pull/950)) - make webhook trigger usable end-to-end ([#1051](https://github.com/kdlbs/kandev/pull/1051)) -- first-class user terminals — stable seq, rename, park/resume ([#1009](https://github.com/kdlbs/kandev/pull/1009)) +- first-class user terminals: stable seq, rename, park/resume ([#1009](https://github.com/kdlbs/kandev/pull/1009)) - roomier pane caps + remember user-set widths ([#1005](https://github.com/kdlbs/kandev/pull/1005)) - inspect mode with pin and area annotations ([#917](https://github.com/kdlbs/kandev/pull/917)) - add Share to session tab context menu ([#1050](https://github.com/kdlbs/kandev/pull/1050)) @@ -1827,7 +1827,7 @@ All notable changes to Kandev. - eager-init agent on profile selection ([#747](https://github.com/kdlbs/kandev/pull/747)) - add message_task_kandev tool ([#745](https://github.com/kdlbs/kandev/pull/745)) - deferred task move with hand-off prompt + boot/turn-end event split ([#743](https://github.com/kdlbs/kandev/pull/743)) -- mobile terminal key-bar — Ctrl/Shift modify OS-keyboard input + iOS keyboard fixes ([#741](https://github.com/kdlbs/kandev/pull/741)) +- mobile terminal key-bar: Ctrl/Shift modify OS-keyboard input + iOS keyboard fixes ([#741](https://github.com/kdlbs/kandev/pull/741)) - make --port the user-facing port flag ([#737](https://github.com/kdlbs/kandev/pull/737)) - expose MCP server to external coding agents ([#732](https://github.com/kdlbs/kandev/pull/732)) - hide Jira buttons when disabled or auth failing ([#725](https://github.com/kdlbs/kandev/pull/725)) @@ -1846,7 +1846,7 @@ All notable changes to Kandev. - close tooltips on popover close and refine branch chip UX ([#808](https://github.com/kdlbs/kandev/pull/808)) - make executors_running the single source of truth for agent_execution_id ([#799](https://github.com/kdlbs/kandev/pull/799)) - use shared prompt template with placeholder autocomplete ([#801](https://github.com/kdlbs/kandev/pull/801)) -- tab session bugs — primary star drift + tab close re-creation ([#800](https://github.com/kdlbs/kandev/pull/800)) +- tab session bugs: primary star drift + tab close re-creation ([#800](https://github.com/kdlbs/kandev/pull/800)) - retry session commits fetch when workspace not ready ([#789](https://github.com/kdlbs/kandev/pull/789)) - focus task name on create dialog open ([#788](https://github.com/kdlbs/kandev/pull/788)) - dedupe cancel-turn clicks to stop "turn cancelled" cascade ([#784](https://github.com/kdlbs/kandev/pull/784)) @@ -1940,7 +1940,7 @@ All notable changes to Kandev. ### Bug Fixes -- ui polish — unified topbar, selector consistency, quick actions editor improvements ([#693](https://github.com/kdlbs/kandev/pull/693)) +- ui polish: unified topbar, selector consistency, quick actions editor improvements ([#693](https://github.com/kdlbs/kandev/pull/693)) - subtask sessions inherit agent profile from parent task ([#692](https://github.com/kdlbs/kandev/pull/692)) - recover from stale execution ID when auto-starting agent on prepared workspace ([#690](https://github.com/kdlbs/kandev/pull/690)) - apply initialValues when TaskCreateDialog mounts already-open ([#688](https://github.com/kdlbs/kandev/pull/688)) @@ -1986,7 +1986,7 @@ All notable changes to Kandev. ### Features -- sidebar filter UX polish — align ops, group steps by workflow ([#647](https://github.com/kdlbs/kandev/pull/647)) +- sidebar filter UX polish: align ops, group steps by workflow ([#647](https://github.com/kdlbs/kandev/pull/647)) - explain why Start task button is disabled via hover tooltip ([#649](https://github.com/kdlbs/kandev/pull/649)) - add filter/group/sort and saved views to task sidebar ([#644](https://github.com/kdlbs/kandev/pull/644)) - vscode-style preview tabs for files, diffs, and commits ([#622](https://github.com/kdlbs/kandev/pull/622)) diff --git a/apps/backend/cmd/mock-agent/handler.go b/apps/backend/cmd/mock-agent/handler.go index b4bbc542f5..e632e4ffa3 100644 --- a/apps/backend/cmd/mock-agent/handler.go +++ b/apps/backend/cmd/mock-agent/handler.go @@ -19,7 +19,7 @@ import ( // orchestrator's routingerr classifier matches the "529 ... Overloaded" // signature in this string and routes it to the retry-with-backoff path. const overloaded529Message = "Internal error: API Error: 529 Overloaded. This is a server-side issue, " + - "usually temporary — try again in a moment. If it persists, check https://status.claude.com." + "usually temporary. Try again in a moment. If it persists, check https://status.claude.com." // overloadedCmdRe matches `/overloaded` or `/e2e:overloaded`, optionally // followed by `:N` — the number of consecutive prompts to fail with a 529 @@ -90,7 +90,7 @@ func (a *mockAgent) handleOverloaded(ctx context.Context, sid acp.SessionId, pro // turn completes (and the orchestrator clears the retry budget). _ = os.Remove(overloadedCounterPath(sid)) e := &emitter{ctx: ctx, conn: a.conn, sid: sid} - e.text(fmt.Sprintf("Provider recovered after %d transient failure(s) — here is your response.", failTimes)) + e.text(fmt.Sprintf("Provider recovered after %d transient failure(s). Here is your response.", failTimes)) return acp.PromptResponse{StopReason: acp.StopReasonEndTurn}, nil, true } @@ -161,7 +161,7 @@ func emitBulk(e *emitter, count int) { e.text(fmt.Sprintf("Generating %d messages to populate the chat history…", count)) for i := 1; i <= count; i++ { e.text(fmt.Sprintf( - "Bulk message %d of %d — filler content for testing chat pagination and the load-older button.", + "Bulk message %d of %d: filler content for testing chat pagination and the load-older button.", i, count)) // The tool-call start flushes the text above into its own message row. toolID := nextToolID() @@ -171,7 +171,7 @@ func emitBulk(e *emitter, count int) { fixedDelay(8) } e.text(fmt.Sprintf( - "Done — emitted %d messages. Scroll up and use “Load older messages” to page through them.", + "Done. Emitted %d messages. Scroll up and use “Load older messages” to page through them.", count)) } @@ -229,6 +229,8 @@ var kandevSystemRegex = regexp.MustCompile(`[\s\S]*? Programs must be written for people to read, and only incidentally for machines to execute.\n" + - "> — Harold Abelson\n\n" + + "> Harold Abelson\n\n" + "> A second blockquote paragraph to verify spacing between consecutive quote blocks " + "and to ensure the left-border styling extends for the full height of wrapped lines.\n\n" + "---\n\n" + diff --git a/apps/backend/internal/agent/agents/helpers.go b/apps/backend/internal/agent/agents/helpers.go index f7789c59b9..6ecb5a367c 100644 --- a/apps/backend/internal/agent/agents/helpers.go +++ b/apps/backend/internal/agent/agents/helpers.go @@ -15,7 +15,7 @@ var agentctlAutoApproveSetting = PermissionSetting{ Supported: true, Default: false, Label: "Auto-approve all permissions", - Description: "Kandev allows every agent permission request without prompting you. Dangerous — use only in trusted workspaces.", + Description: "Kandev allows every agent permission request without prompting you. Dangerous. Use only in trusted workspaces.", ApplyMethod: PermissionApplyMethodAgentctlAutoApprove, } diff --git a/apps/backend/internal/agent/runtime/lifecycle/executor_sprites.go b/apps/backend/internal/agent/runtime/lifecycle/executor_sprites.go index 76429423fe..53dc13146b 100644 --- a/apps/backend/internal/agent/runtime/lifecycle/executor_sprites.go +++ b/apps/backend/internal/agent/runtime/lifecycle/executor_sprites.go @@ -278,7 +278,7 @@ func (r *SpritesExecutor) fallbackToFreshSandbox( }) notice := beginStep("Reconnecting cloud sandbox") - notice.Warning = "Previous sandbox is no longer available — provisioning a fresh one for this branch." + notice.Warning = "Previous sandbox is no longer available; provisioning a fresh one for this branch." notice.WarningDetail = fmt.Sprintf( "The Sprites sandbox %s could not be reached (it was likely destroyed or expired). "+ "Kandev is starting a fresh sandbox %s on the same branch %s; this typically takes 30–60 seconds.", diff --git a/apps/backend/internal/i18n/i18n_test.go b/apps/backend/internal/i18n/i18n_test.go index 65c8019854..e795d4285e 100644 --- a/apps/backend/internal/i18n/i18n_test.go +++ b/apps/backend/internal/i18n/i18n_test.go @@ -67,7 +67,7 @@ func TestTf(t *testing.T) { want string }{ {"interpolates a placeholder", "en", "share.pageTitle", - map[string]any{"title": "Fix the flake"}, "Fix the flake — kandev share"}, + map[string]any{"title": "Fix the flake"}, "Fix the flake; kandev share"}, {"count of one selects the singular", "en", "share.messageCount", map[string]any{"count": 1}, "1 message"}, {"count of zero selects the plural", "en", "share.messageCount", @@ -94,7 +94,7 @@ func TestTf(t *testing.T) { // A placeholder with no value stays visible; a silent hole in a // sentence is much harder to notice in review than "{{title}}". {"missing var leaves the placeholder", "en", "share.pageTitle", - nil, "{{title}} — kandev share"}, + nil, "{{title}}; kandev share"}, {"unsupported locale falls back to en", "klingon", "share.messageCount", map[string]any{"count": 2}, "2 messages"}, {"non-numeric count is ignored", "en", "share.untitledTask", diff --git a/apps/backend/internal/i18n/locales/en.json b/apps/backend/internal/i18n/locales/en.json index 13949f9337..9a9af9d161 100644 --- a/apps/backend/internal/i18n/locales/en.json +++ b/apps/backend/internal/i18n/locales/en.json @@ -17,8 +17,8 @@ "share.noMetadata": "no metadata", "share.openRenderedView": "**[Open the rendered view →]({{url}})** for a properly styled conversation.", "share.openShareHTML": "**Open {{file}}** for the rendered conversation.", - "share.pageTitle": "{{title}} — kandev share", - "share.pitch": "Shared from **[kandev]({{url}})** — the open-source agentic dev environment.", + "share.pageTitle": "{{title}}; kandev share", + "share.pitch": "Shared from **[kandev]({{url}})**; the open-source agentic dev environment.", "share.rawExport": "Raw export:", "share.redacted": "Redacted:", "share.redactedBeforePublish": "Redacted before publish:", diff --git a/apps/backend/internal/i18n/locales/pseudo.json b/apps/backend/internal/i18n/locales/pseudo.json index 7aadea0158..3db213e0a7 100644 --- a/apps/backend/internal/i18n/locales/pseudo.json +++ b/apps/backend/internal/i18n/locales/pseudo.json @@ -17,8 +17,8 @@ "share.noMetadata": "ńō ḿēţàďàţà", "share.openRenderedView": "**[Ōƥēń ţĥē ŕēńďēŕēď vĩēŵ →]({{url}})** ƒōŕ à ƥŕōƥēŕĺŷ śţŷĺēď ćōńvēŕśàţĩōń.", "share.openShareHTML": "**Ōƥēń {{file}}** ƒōŕ ţĥē ŕēńďēŕēď ćōńvēŕśàţĩōń.", - "share.pageTitle": "{{title}} — ķàńďēv śĥàŕē", - "share.pitch": "Śĥàŕēď ƒŕōḿ **[ķàńďēv]({{url}})** — ţĥē ōƥēń-śōũŕćē àĝēńţĩć ďēv ēńvĩŕōńḿēńţ.", + "share.pageTitle": "{{title}}; ķàńďēv śĥàŕē", + "share.pitch": "Śĥàŕēď ƒŕōḿ **[ķàńďēv]({{url}})**; ţĥē ōƥēń-śōũŕćē àĝēńţĩć ďēv ēńvĩŕōńḿēńţ.", "share.rawExport": "Ŕàŵ ēxƥōŕţ:", "share.redacted": "Ŕēďàćţēď:", "share.redactedBeforePublish": "Ŕēďàćţēď ƀēƒōŕē ƥũƀĺĩśĥ:", diff --git a/apps/backend/internal/i18n/locales/pt-pt.json b/apps/backend/internal/i18n/locales/pt-pt.json index f87fb72d22..8ec4626928 100644 --- a/apps/backend/internal/i18n/locales/pt-pt.json +++ b/apps/backend/internal/i18n/locales/pt-pt.json @@ -17,8 +17,8 @@ "share.noMetadata": "sem metadados", "share.openRenderedView": "**[Abrir a vista renderizada →]({{url}})** para ver a conversa devidamente formatada.", "share.openShareHTML": "**Abra {{file}}** para ver a conversa renderizada.", - "share.pageTitle": "{{title}} — partilha do kandev", - "share.pitch": "Partilhado a partir do **[kandev]({{url}})** — o ambiente de desenvolvimento com agentes de código aberto.", + "share.pageTitle": "{{title}}; partilha do kandev", + "share.pitch": "Partilhado a partir do **[kandev]({{url}})**; o ambiente de desenvolvimento com agentes de código aberto.", "share.rawExport": "Exportação em bruto:", "share.redacted": "Censurado:", "share.redactedBeforePublish": "Censurado antes da publicação:", diff --git a/apps/backend/internal/i18n/locales/zh-cn.json b/apps/backend/internal/i18n/locales/zh-cn.json index 84949b67e8..22e3841814 100644 --- a/apps/backend/internal/i18n/locales/zh-cn.json +++ b/apps/backend/internal/i18n/locales/zh-cn.json @@ -17,8 +17,8 @@ "share.noMetadata": "无元数据", "share.openRenderedView": "**[打开渲染视图 →]({{url}})**,查看正确排版的对话。", "share.openShareHTML": "**打开 {{file}}** 查看渲染后的对话。", - "share.pageTitle": "{{title}} — kandev 分享", - "share.pitch": "由 **[kandev]({{url}})** 分享——开源的智能代理开发环境。", + "share.pageTitle": "{{title}}; kandev 分享", + "share.pitch": "由 **[kandev]({{url}})** 分享--开源的智能代理开发环境。", "share.rawExport": "原始导出:", "share.redacted": "已脱敏:", "share.redactedBeforePublish": "发布前已脱敏:", diff --git a/apps/backend/internal/task/share/gist_html_test.go b/apps/backend/internal/task/share/gist_html_test.go index 81d8f56a06..4c75822343 100644 --- a/apps/backend/internal/task/share/gist_html_test.go +++ b/apps/backend/internal/task/share/gist_html_test.go @@ -43,7 +43,7 @@ func TestBuildShareHTML_RendersChatLayout(t *testing.T) { doc := BuildShareHTML(snap, "en") assertContains(t, doc, "") - assertContains(t, doc, "Investigate <flaky> test — kandev share") + assertContains(t, doc, "Investigate <flaky> test; kandev share") assertContains(t, doc, "