@jslee124/forge 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/index.js +1189 -90
  2. package/package.json +2 -1
  3. package/resources/docs/en/ARCHITECTURE.md +519 -0
  4. package/resources/docs/en/AUTHENTICATION.md +224 -0
  5. package/resources/docs/en/CLI_UI.md +266 -0
  6. package/resources/docs/en/CONFIGURATION.md +263 -0
  7. package/resources/docs/en/CONTEXT_MANAGEMENT.md +692 -0
  8. package/resources/docs/en/GETTING_STARTED.md +241 -0
  9. package/resources/docs/en/PLUGINS.md +622 -0
  10. package/resources/docs/en/PRODUCT.md +157 -0
  11. package/resources/docs/en/PROJECT_CONTEXT.md +225 -0
  12. package/resources/docs/en/RELEASING.md +94 -0
  13. package/resources/docs/en/SECURITY.md +272 -0
  14. package/resources/docs/en/SESSIONS.md +134 -0
  15. package/resources/docs/en/TROUBLESHOOTING.md +256 -0
  16. package/resources/docs/index.json +24334 -0
  17. package/resources/docs/zh-CN/ARCHITECTURE.md +174 -0
  18. package/resources/docs/zh-CN/AUTHENTICATION.md +96 -0
  19. package/resources/docs/zh-CN/CLI_UI.md +112 -0
  20. package/resources/docs/zh-CN/CONFIGURATION.md +221 -0
  21. package/resources/docs/zh-CN/CONTEXT_MANAGEMENT.md +200 -0
  22. package/resources/docs/zh-CN/GETTING_STARTED.md +193 -0
  23. package/resources/docs/zh-CN/PLUGINS.md +286 -0
  24. package/resources/docs/zh-CN/PRODUCT.md +86 -0
  25. package/resources/docs/zh-CN/PROJECT_CONTEXT.md +130 -0
  26. package/resources/docs/zh-CN/RELEASING.md +86 -0
  27. package/resources/docs/zh-CN/SECURITY.md +92 -0
  28. package/resources/docs/zh-CN/SESSIONS.md +69 -0
  29. package/resources/docs/zh-CN/TROUBLESHOOTING.md +185 -0
  30. package/resources/skills/forge-plugin-creator/SKILL.md +70 -0
  31. package/resources/skills/forge-plugin-creator/references/plugin-api.md +36 -0
  32. package/resources/skills/forge-plugin-creator/templates/index.mjs +30 -0
  33. package/resources/skills/forge-plugin-creator/templates/plugin.json +8 -0
  34. package/resources/skills/forge-plugin-creator/templates/plugin.test-template.ts +14 -0
  35. package/resources/skills/forge-product-help/SKILL.md +16 -0
@@ -0,0 +1,224 @@
1
+ # Authentication Model
2
+
3
+ 简体中文 · Documentation index
4
+
5
+ ## Status
6
+
7
+ Forge supports DeepSeek and OpenAI API-key authentication through
8
+ `DEEPSEEK_API_KEY` and `OPENAI_API_KEY`, plus ChatGPT subscription
9
+ authentication through the official Codex App Server.
10
+ User-scoped provider routes additionally support an explicitly selected bearer
11
+ credential or no authentication for local/self-hosted endpoints.
12
+ Forge presents the login command and browser/device-code instructions, while
13
+ Codex owns OAuth, credential persistence, refresh, and revocation.
14
+
15
+ ## Supported and planned methods
16
+
17
+ | Method | Intended use | Status |
18
+ | --- | --- | --- |
19
+ | DeepSeek API key | Local development and automation | Implemented |
20
+ | OpenAI API key | Optional usage-based OpenAI API access | Implemented |
21
+ | Provider route bearer key | OpenAI-compatible gateways | Implemented |
22
+ | Provider route without authentication | Local Ollama/vLLM-style servers | Implemented |
23
+ | Sign in with ChatGPT | OpenAI subscription access through Codex App Server | Implemented |
24
+ | Codex access token | Trusted enterprise automation | Deferred |
25
+
26
+ DeepSeek's official API uses `https://api.deepseek.com` and the initial adapter
27
+ uses the official AI SDK provider package. The model ID remains configurable
28
+ because provider model names have a different lifecycle from the Forge release.
29
+
30
+ Official DeepSeek references:
31
+
32
+ - [DeepSeek API model documentation](https://api-docs.deepseek.com/quick_start/pricing/)
33
+ - [AI SDK DeepSeek provider](https://ai-sdk.dev/providers/ai-sdk-providers/deepseek)
34
+
35
+ OpenAI now documents Codex App Server as the integration protocol for embedding
36
+ Codex into a product. Its account surface supports managed ChatGPT browser and
37
+ device-code login. Forge uses that public surface rather than copying OpenCode's
38
+ OAuth client ID or rewriting requests to an undocumented ChatGPT endpoint.
39
+
40
+ Official reference:
41
+ [OpenAI Codex App Server](https://developers.openai.com/codex/app-server)
42
+
43
+ ## Architectural boundary
44
+
45
+ The two execution paths have different ownership boundaries:
46
+
47
+ ```text
48
+ Forge Engine: Forge Runtime -> Model Adapter -> DeepSeek or OpenAI API
49
+
50
+ Codex Engine: Forge CLI -> Codex App Server -> ChatGPT subscription
51
+ ```
52
+
53
+ The Codex Engine is not wrapped as a Forge `ModelAdapter`: App Server owns a
54
+ complete agent runtime, including turns, tools, sandboxing, approvals, and
55
+ history. Treating it as a raw model transport would obscure which runtime made
56
+ security and execution decisions.
57
+
58
+ The provider-neutral authentication manager resolves API keys from an explicit
59
+ environment variable first, then from Forge's owner-readable user credential
60
+ store. Missing credentials produce an actionable error without printing the
61
+ key or a stack trace. Keys are never copied into Forge configuration, prompts,
62
+ traces, plugin events, or repository files.
63
+
64
+ ## OpenAI-compatible provider routes
65
+
66
+ Routes are declared under `providers` in `$FORGE_HOME/config.json`. They name a
67
+ wire protocol, canonical endpoint, explicit authentication mode, and model
68
+ profiles. Repository `.forge/config.json` files may not define routes because
69
+ that would let untrusted project content decide where a user credential is
70
+ sent.
71
+
72
+ ```json
73
+ {
74
+ "schemaVersion": 1,
75
+ "providers": {
76
+ "my-gateway": {
77
+ "api": "openai-responses",
78
+ "baseUrl": "https://gateway.example/openai/v1",
79
+ "auth": { "type": "bearer", "apiKeyEnv": "GATEWAY_API_KEY" },
80
+ "models": [
81
+ {
82
+ "id": "reasoning-model",
83
+ "contextWindow": 128000,
84
+ "maxOutputTokens": 8192,
85
+ "reasoningGears": { "none": "none", "high": "high" }
86
+ }
87
+ ]
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ `auth.type` is mandatory. `bearer` reads the declared variable, or derives
94
+ `FORGE_<ROUTE>_API_KEY`, before consulting Forge's stored credential. `none`
95
+ does not read or send any API key. Stored route credentials are bound to the
96
+ canonical `baseUrl`; after an endpoint change Forge refuses the old key until a
97
+ new one is saved.
98
+
99
+ Remote endpoints require HTTPS. Plain HTTP is accepted only for loopback hosts.
100
+ URLs containing credentials, query strings, or fragments are rejected. Model
101
+ discovery does not follow redirects, remains bounded to 4 MiB and 15 seconds,
102
+ and is optional because model IDs can be entered manually.
103
+
104
+ Forge treats an omitted reasoning setting as **provider default**, which is not
105
+ the same as disabling reasoning. Each `reasoningGears` entry maps a Forge UI
106
+ level to the exact wire value sent to the provider, so an explicit
107
+ `"none": "none"` is required when the endpoint supports disabling reasoning.
108
+ Legacy version-1 `null` mappings are read as their canonical key to avoid
109
+ silently turning `none` into provider default.
110
+
111
+ Model discovery also accepts bounded optional reasoning metadata such as
112
+ `reasoning_efforts`, `supported_reasoning_efforts`, or
113
+ `capabilities.reasoning.efforts`. These fields are non-standard extensions:
114
+ when they are absent Forge reports capabilities as unknown and keeps provider
115
+ defaults instead of guessing or issuing paid probe requests. The setup screen
116
+ prefills advertised levels and lets the user override them before saving.
117
+
118
+ An OpenAI API key is optional and is billed independently of ChatGPT. A ChatGPT
119
+ Plus, Pro, Business, or other subscription does not cause Forge to select the
120
+ API path. Users who only want subscription access should keep
121
+ `model.provider = "deepseek"` or use `forge codex`; they do not need to create
122
+ or export `OPENAI_API_KEY`.
123
+
124
+ ## Compatibility requirements
125
+
126
+ The selected App Server integration satisfies these requirements:
127
+
128
+ - The flow is publicly documented for embedding Codex into a product
129
+ - Forge never supplies or copies an OAuth client identity
130
+ - Codex performs token exchange, refresh, revocation, and account selection
131
+ - The user-facing description accurately distinguishes subscription access from
132
+ usage-based API access
133
+
134
+ Forge must not:
135
+
136
+ - Copy another application's client secret or identity
137
+ - Treat reverse-engineered endpoints as a permanent API contract
138
+ - Ask users to paste access or refresh tokens into chat
139
+ - Read, import, or modify `~/.codex/auth.json` directly
140
+ - Store credentials inside the current repository
141
+
142
+ ## Credential storage
143
+
144
+ The interactive `/login` flow can persist API keys in
145
+ `$FORGE_HOME/auth.json`. The directory is mode `0700`, the file is mode `0600`,
146
+ updates are atomic, and environment variables take precedence. This is a
147
+ plaintext filesystem-protected fallback rather than an OS keychain. ChatGPT
148
+ subscription credentials remain owned entirely by Codex App Server.
149
+ The interactive `/logout` picker lists authenticated providers, removes the
150
+ selected stored API credential, or asks Codex App Server to sign out the
151
+ ChatGPT subscription. A logged-out third-party route remains visible as signed
152
+ out in `/login`, where it can be authenticated again. The route management
153
+ screen has a separate confirmed **Remove provider** action that deletes the
154
+ route, its models, and its stored credential. It reports any provider
155
+ environment variable that remains active; Forge cannot unset a variable in the
156
+ parent shell.
157
+
158
+ The current runtime lookup order is:
159
+
160
+ 1. The provider's explicit or conventional environment variable
161
+ 2. `$FORGE_HOME/auth.json`, outside the project with owner-only permissions
162
+
163
+ An operating-system credential store would be preferable to plaintext file
164
+ storage and remains a future improvement. Environment variables remain the
165
+ recommended injection mechanism for automation.
166
+
167
+ Credentials must be excluded from prompts, run events, JSONL traces, plugin
168
+ events, telemetry, and ordinary error messages.
169
+
170
+ ## Refresh and concurrency
171
+
172
+ Codex App Server owns subscription token refresh and persistence. Forge never
173
+ receives OAuth access or refresh tokens. The App Server may use the Codex
174
+ credential-store configuration shared with other local Codex clients.
175
+
176
+ Credential updates must be atomic. A failed refresh must not destroy the last
177
+ known credential before the error is handled.
178
+
179
+ ## Commands
180
+
181
+ The CLI exposes:
182
+
183
+ ```text
184
+ forge auth login openai
185
+ forge auth status openai
186
+ forge auth status openai-api
187
+ forge auth logout openai
188
+ forge models list --provider openai
189
+ forge codex "Inspect this repository" --model <id> --reasoning-effort <effort>
190
+ forge run "Inspect this repository" --provider openai --model gpt-5.4-mini --reasoning-effort low
191
+ ```
192
+
193
+ Use `forge auth login openai --method device-code` for a headless login. Forge
194
+ prints the official verification URL and code and waits for App Server's
195
+ completion notification. Browser callback validation and PKCE are owned by
196
+ Codex. Cancelling Forge asks App Server to cancel the pending login.
197
+
198
+ Sign-in URLs are much wider than a terminal window. Terminals that linkify
199
+ plain text do so one display line at a time, so a wrapped URL would stay
200
+ clickable only up to its first wrap. Forge therefore emits the URL as a single
201
+ OSC 8 hyperlink on terminals known to support it, including VS Code, Ghostty,
202
+ WezTerm, iTerm2, Kitty, Windows Terminal, Konsole, and VTE 5000 or newer. The
203
+ visible label remains the complete address, so an unsupported terminal still
204
+ shows a copyable URL. Redirected output, `TERM=dumb`, and `NO_COLOR` always
205
+ produce the bare URL. Set `FORCE_HYPERLINK=1` to opt an unrecognized terminal
206
+ in, or `FORCE_HYPERLINK=0` to opt out.
207
+
208
+ `forge auth logout openai` operates on the shared Codex account and can sign
209
+ other local Codex clients out. Forge does not claim that these are Forge-owned
210
+ credentials.
211
+
212
+ `forge auth status openai-api` reports whether an environment or stored
213
+ credential is active; it never validates the key with a paid request.
214
+ `forge auth login openai-api` directs the user to the masked interactive
215
+ `/login` flow or `OPENAI_API_KEY` because the plain subcommand never reads a
216
+ secret from command arguments. The interactive `/model` picker discovers the current Codex catalog
217
+ and also shows native API adapters without duplicating models by reasoning
218
+ effort. The separate `/effort` picker and Shift+Tab shortcut use the selected
219
+ model's supported levels. Forge persists these ordinary
220
+ engine/provider/model/reasoning settings under `$FORGE_HOME/config.json`;
221
+ credentials remain separate in the environment, `$FORGE_HOME/auth.json`, or
222
+ Codex-owned storage.
223
+ Selecting a ChatGPT entry routes subsequent interactive prompts through Codex
224
+ Engine.
@@ -0,0 +1,266 @@
1
+ # Interactive CLI UI
2
+
3
+ 简体中文 · Documentation index
4
+
5
+ ## Status
6
+
7
+ This document defines the implemented Milestone 4.6 terminal experience. Ink
8
+ now provides the interactive renderer while the non-interactive commands and
9
+ Forge-owned runtime retain their existing boundaries.
10
+
11
+ ## Goals
12
+
13
+ The interactive CLI should make common coding-agent actions discoverable
14
+ without moving the agent loop, tools, or policy decisions out of Forge core.
15
+ The terminal UI should provide:
16
+
17
+ - A multi-line prompt editor
18
+ - Discoverable slash-command completion
19
+ - Workspace-file completion with `@`
20
+ - Clear streamed reasoning, answers, tool activity, and run state
21
+ - Terminal-native Markdown for headings, lists, quotes, links, inline code,
22
+ emphasis, and fenced code blocks
23
+ - A readable diff review before file-write approval
24
+ - Keyboard-only operation with predictable cancellation
25
+ - A startup capability summary inside the blue Forge frame
26
+
27
+ Ink is the renderer for the interactive CLI. Commander remains
28
+ responsible for process-level command parsing. React and Ink must stay inside
29
+ `apps/cli`; `@forge/core` must remain independent of the terminal framework.
30
+ Forge uses full-frame Ink updates so terminal reflow during a resize cannot
31
+ leave stale rows from the previous width.
32
+
33
+ ## Startup capability summary
34
+
35
+ Before the first prompt, the blue Forge frame lists enabled user plugins,
36
+ project plugins with `trusted` or `untrusted, skipped` state, and discovered
37
+ built-in, user, and project Skills. Compact category labels follow Pi's resource-list pattern while
38
+ preserving Forge's explicit trust semantics.
39
+
40
+ Startup detection reads plugin manifests and Skill metadata only. It must not
41
+ import a project plugin merely to display its name. Actual plugin activation
42
+ remains part of a native Forge Engine run. Native runs advertise bounded Skill
43
+ metadata and lazily load matching Skills; explicit `$skill-name` remains an
44
+ override. The Codex Engine owns a separate tool
45
+ runtime, so the listing describes Forge resources rather than Codex tools.
46
+
47
+ `/plugins` opens a metadata-only review panel. It shows each project plugin's
48
+ version, capabilities, and current workspace trust state. Trust requires a
49
+ second `y` confirmation after an in-process privilege warning; revocation is
50
+ available from the same panel. A successful decision refreshes the blue startup
51
+ frame immediately, while plugin activation remains deferred until the next
52
+ native Forge Engine task.
53
+
54
+ ## Interaction states
55
+
56
+ The UI owns an explicit state machine so menus, streaming output, and approval
57
+ prompts never compete for the same terminal input:
58
+
59
+ ```text
60
+ editing
61
+ |-- "/" --> selecting_command -- execute/insert --> editing
62
+ |-- "@" --> selecting_file ---- insert ---------> editing
63
+ `-- submit --> running --> awaiting_approval --> running --> editing
64
+ `---------------- completed ------------^
65
+ ```
66
+
67
+ Only the active state consumes keyboard input. Ctrl+C closes an open completion
68
+ menu first, cancels an active run second, and preserves the existing deliberate
69
+ session-exit behavior when Forge is otherwise idle.
70
+
71
+ ## Prompt editor
72
+
73
+ - Enter submits a non-empty prompt when no completion or approval menu owns the
74
+ key.
75
+ - Shift+Enter inserts a newline without submitting. Terminal integrations that
76
+ encode it as Meta+Enter (`ESC+Enter`) are treated the same way.
77
+ - Because some legacy terminals do not distinguish Shift+Enter from Enter,
78
+ Ctrl+J also inserts a newline as a portable fallback. The input footer should
79
+ advertise the shortcut that is available.
80
+ - Forge directly enables the enhanced keyboard protocol in known-compatible
81
+ terminals such as VS Code and Ghostty, avoiding a startup capability query
82
+ that some terminals echo as input. Older or unknown terminals continue to
83
+ use Ctrl+J or Meta+Enter as fallbacks.
84
+ - The editor preserves newlines exactly when constructing the user message.
85
+ - Left/right movement, backspace, delete, Home/End, paste, Unicode text, and
86
+ terminal resize must not corrupt the buffer or display.
87
+ - Up/down keys navigate an open completion menu. When no menu is open, they may
88
+ later be used for prompt history; history is not required for Milestone 4.6.
89
+ - Shift+Tab cycles through the active model's supported thinking-effort levels.
90
+
91
+ ## Slash-command completion
92
+
93
+ Typing `/` as the first non-whitespace character opens a list of available
94
+ commands. Additional characters filter the list by command name.
95
+
96
+ Each command is defined once with its name, description, and handler. The same
97
+ registry drives completion and `/help`, preventing the two surfaces from
98
+ drifting. The registry contains `/help`, `/new`, `/clear`, `/context`,
99
+ `/compact`, `/plugins`, `/login`, `/logout`, `/model`, `/delete-model`,
100
+ `/effort`, `/resume`, and `/exit`.
101
+ `/plugins` opens the project-plugin trust review described above. `/model`
102
+ opens a keyboard picker, discovers current
103
+ ChatGPT/Codex models, includes configured API providers, and saves one model
104
+ entry without multiplying it by effort level. `/effort` opens a separate
105
+ model-specific effort picker; `/effort <level>` sets a supported level directly.
106
+ Both selections are atomically saved to user configuration.
107
+ `/logout` lists authenticated providers and removes a selected stored
108
+ credential without pretending to unset a parent-shell environment variable.
109
+ `/delete-model` shows only user-configured provider models, requires
110
+ confirmation, retains the provider route and credential, and refuses to delete
111
+ the active model until another is selected.
112
+ The `/login` picker always lists configured third-party routes, including their
113
+ full Base URL, API type, authentication mode, status, and model count. Selecting
114
+ a route opens provider management: add or restore a model, delete one configured
115
+ model, log out when a stored credential is active, or remove the provider.
116
+ Deleting a model keeps the route and credential so it can be added again
117
+ immediately. Logout leaves a signed-out
118
+ route visible. Remove provider is a separate confirmed action that deletes the
119
+ route, its models, and its stored credential; it refuses to remove the active
120
+ provider and cannot unset a parent-shell environment variable.
121
+
122
+ Model setup prefills reasoning levels only when `/models` returns recognized,
123
+ bounded capability metadata. It labels the source as discovered or manual;
124
+ with no metadata, Enter keeps the provider default. Forge does not infer that
125
+ missing metadata means a non-reasoning model and does not make paid capability
126
+ probe requests. Provider management separately labels the protocol-supported
127
+ tool surface and the provider's end-to-end agent loop as unverified.
128
+
129
+ - Up/Down changes the highlighted command.
130
+ - Enter executes the highlighted command.
131
+ - Tab completes its name without executing it.
132
+ - Escape closes the menu without changing the input.
133
+ - A slash elsewhere in ordinary prose or a filesystem path does not open the
134
+ command menu.
135
+
136
+ ## Workspace-file mentions
137
+
138
+ Typing `@` opens a bounded list of files beneath the selected workspace. Text
139
+ after the active `@` token filters candidates by relative path using
140
+ case-insensitive fuzzy matching.
141
+
142
+ - Candidate paths are workspace-relative and use `/` as the display separator.
143
+ - `.git`, dependency directories, build output, and paths outside the canonical
144
+ workspace are excluded.
145
+ - The menu shows at most 10 ranked candidates and indicates when more matches
146
+ exist.
147
+ - Up/Down changes the highlighted file; Enter or Tab inserts it; Escape closes
148
+ the menu.
149
+ - Selecting a file inserts a visible mention while retaining a structured
150
+ `{ path }` reference in editor state. Paths with spaces must not depend on
151
+ reparsing the rendered prompt.
152
+ - File discovery is read-only, bounded, cancellable, and does not invoke the
153
+ model or run an external shell command for every keystroke.
154
+
155
+ On submission, Forge sends the user's text plus an explicit list of referenced
156
+ workspace-relative paths to the model. Selecting a mention does not
157
+ automatically inject the complete file contents. The model can use `read_file`
158
+ through the normal tool, policy, and trace path when it needs the contents.
159
+
160
+ ## Pasted image attachments
161
+
162
+ When a bracketed paste or terminal drag-and-drop begins with an absolute image
163
+ path, Forge removes that path from the text and shows a compact
164
+ `[Image #N] filename` attachment. This supports OS and terminal clipboard
165
+ helpers that materialize screenshots beneath temporary directories such as
166
+ `/var/.../T/otty-paste/`. Quoted paths, shell-escaped spaces, and `file://`
167
+ paths are accepted. Backspace removes the most recent attachment when the text
168
+ composer is empty.
169
+
170
+ This is an explicit user action, so the attachment may be outside the selected
171
+ workspace. Forge does not scan arbitrary prompt prose for image paths, and this
172
+ does not widen the workspace boundary used by model file tools. The active
173
+ model must support image input.
174
+
175
+ Example logical message:
176
+
177
+ ```text
178
+ Please explain the cancellation behavior.
179
+
180
+ Referenced files:
181
+ - apps/cli/src/session.ts
182
+ ```
183
+
184
+ ## Diff review
185
+
186
+ File-write approval must present the exact proposed change in a dedicated,
187
+ readable panel before the user decides. The renderer should show:
188
+
189
+ - Operation and path: create, modify, or delete
190
+ - A compact file summary and changed-line counts
191
+ - Unified diff hunks with old/new line numbers
192
+ - Added lines in green with `+`, removed lines in red with `-`, and subdued
193
+ context lines
194
+ - Clear file and hunk headers that remain understandable without color
195
+ - Syntax highlighting when the file type is known, without allowing syntax
196
+ color to obscure addition/removal meaning
197
+ - An explicit truncation message when a safety display limit is reached
198
+
199
+ Approval must never rely on color alone. `--no-color`, a non-color terminal,
200
+ and common color-vision deficiencies must retain the `+`/`-`, headers, and line
201
+ number cues. A diff that exceeds the safe review limit remains unapprovable;
202
+ visual truncation must not silently turn partial content into approval for an
203
+ unseen patch.
204
+
205
+ The approval controls are visible next to the diff and describe their scope.
206
+ For example, approving the first workspace write covers later workspace writes
207
+ only in the current run, while process commands continue to require separate
208
+ approval.
209
+
210
+ Process-command approval uses the same dedicated panel. It renders a
211
+ shell-readable `$ command` line followed by clearly labelled working-directory
212
+ and timeout rows; these details must not appear as detached transcript text.
213
+
214
+ Network-tool approval also uses the dedicated panel. It shows the registered
215
+ tool name and the bounded URL or search query that will be sent externally.
216
+ Plugin-specific secrets and arbitrary input objects are never rendered as an
217
+ approval preview.
218
+
219
+ ## Sign-in panel
220
+
221
+ A pending browser sign-in is a dedicated panel, not transcript text. The Codex
222
+ auth surface reports the URL as a structured `login` output event carrying the
223
+ address as its own field, so the UI never re-parses it out of a text chunk.
224
+
225
+ Sign-in URLs are several times wider than a terminal window. The panel wraps
226
+ the address in a single OSC 8 hyperlink, which keeps the whole URL clickable
227
+ after Ink wraps it; terminals that linkify plain text do so one display line at
228
+ a time and would otherwise leave only the first wrapped line clickable. The
229
+ visible label stays the complete address so an unsupported terminal still shows
230
+ a copyable URL, and escape sequences must not count toward display width or the
231
+ panel border would fall out of alignment.
232
+
233
+ ## Rendering boundaries
234
+
235
+ The CLI may turn runtime events into components such as message blocks, tool
236
+ activity rows, status indicators, and diff panels. It must not infer execution
237
+ success from presentation state or parse previously rendered terminal text.
238
+
239
+ Core events and approval requests remain the source of truth. Interactive and
240
+ non-interactive commands continue to share the same Forge-owned runtime,
241
+ workspace validation, policy gateway, and tool execution behavior.
242
+
243
+ Model Markdown is rendered as a bounded terminal-native subset rather than
244
+ HTML. The renderer must tolerate incomplete constructs while text is streaming
245
+ and strip model-supplied ANSI control sequences before styling output.
246
+
247
+ ## Test strategy
248
+
249
+ Milestone 4.6 should include deterministic tests for:
250
+
251
+ - Slash-menu opening, filtering, navigation, selection, and dismissal
252
+ - File candidate filtering, ignored directories, result limits, spaces, and
253
+ prevention of workspace escape
254
+ - Structured file mentions and the exact model message assembled from them
255
+ - Enter submission versus Shift+Enter, Meta+Enter, and Ctrl+J newline insertion
256
+ - Multi-line editing, paste, Unicode, resize, and cancellation
257
+ - State transitions between editing, completion, running, and approval
258
+ - Startup plugin/Skill listing, trust labels, and metadata-only discovery
259
+ - Diff rendering for create and modify operations, multiple hunks, no-color
260
+ output, truncation, and approval scope
261
+
262
+ No UI test may require a paid model request. Component tests should consume
263
+ scripted input and events, while a small pseudo-terminal integration test proves
264
+ the supported key sequences in representative terminals.
265
+
266
+ `/resources` shows every discovered Skill's source, description, automatic or explicit-only status, shadowing, and bounded diagnostics. It does not import plugin entries or eagerly load Skill bodies. `/plugins` remains limited to executable plugins and points users to `/resources` for Skills.