7.1 KiB
Phase 1: Preview formatter extension seam
Goal: make issue #266 (configurable preview limits + future formatter extension seam) easy to implement.
Today, tool-input-preview.ts uses module-level constants (TOOL_INPUT_PREVIEW_MAX_LENGTH = 200, TOOL_TEXT_SUMMARY_MAX_LENGTH = 80) and there is no path for extension config to reach the formatting layer.
The call chain from handler → gate descriptor → prompt → formatting spans 5 pure-function layers with no config parameter.
The config normalizer only handles booleans and arrays - no pattern exists for optional numeric fields.
Current health metrics
| Metric | Value |
|---|---|
| Health score | 74 B |
| LOC | 30,893 |
| Dead files / exports | 0% |
| Avg cyclomatic | 1.4 |
| p90 cyclomatic | 2 |
| Maintainability | 91.2 (good) |
| Duplication | 9.3% (2,853 lines) |
| Churn hotspots | 41 files |
| Refactoring targets | 5 (4 medium, 1 high) |
Findings
Filtered to what blocks or complicates #266:
| # | Finding | Category | Files | Impact | Risk | Priority |
|---|---|---|---|---|---|---|
| 1 | tool-input-preview.ts is a flat bag of 15 exports (3 constants + 12 functions) mixing prompt formatting, log formatting, and text utilities - no cohesive object to receive config |
B: oversized / C: coupling | tool-input-preview.ts |
5 | 2 | 20 |
| 2 | No config path to gate descriptors - describeToolGate(tcc, check) and formatAskPrompt(result, agent, input) are pure functions with no config parameter; adding one requires threading through 5 layers |
C: parameter relay | permission-gate-handler.ts, tool.ts, permission-prompts.ts, tool-input-preview.ts |
4 | 2 | 16 |
| 3 | Config normalizer (normalizePermissionSystemConfig) has no pattern for optional numeric fields with defaults and bounds checking - only booleans and arrays |
C: coupling (missing abstraction) | extension-config.ts |
3 | 1 | 15 |
| 4 | formatToolInputForPrompt switch statement is the natural home for the future formatter extension seam but is buried in a utility module with no object to hang a register() method on |
C: coupling (missing collaborator) | tool-input-preview.ts |
4 | 2 | 16 |
| 5 | permission-prompts.ts test mocks tool-input-preview at module level - extracting a formatter object would let the test inject it directly, removing the vi.mock() |
D: testability | permission-prompts.test.ts |
2 | 1 | 10 |
Steps
-
✅ Extract
ToolPreviewFormatterclass fromtool-input-preview.ts(#282)- Created
tool-preview-formatter.tswithToolPreviewFormatterclass acceptingToolPreviewFormatterOptionsin its constructor - Moved 7 config-dependent methods onto the class:
formatToolInputForPrompt,formatJsonInputForPrompt,formatSearchInputForPrompt,sanitizeInlineText,formatGenericToolInputForLog,getToolInputPreviewForLog,getPermissionLogContext tool-input-preview.tsretains 8 pure utilities + 3 default constants- Outcome: formatter is a single injectable object; #266 passes config by constructing the formatter with user-configured limits
- Created
-
✅ Thread
ToolPreviewFormatterthrough the gate descriptor chain (#282)describeToolGate(tcc, check, formatter)- accepts the formatter as third parameterformatAskPrompt(result, agentName, input, formatter?)- accepts an optional formatterPermissionGateHandler.handleToolCallconstructs the formatter with default constant values and passes it to the tool gate producerpermission-prompts.test.tsvi.mockremoved - formatter is injected directly- Outcome: config reaches formatting with one parameter instead of threading through 5 layers
-
✅ Add numeric config normalization to
extension-config.ts(#266)- Added
normalizeOptionalPositiveInthelper (exported; validates positive integer) - Added
toolInputPreviewMaxLengthandtoolTextSummaryMaxLengthas optional fields toPermissionSystemExtensionConfig - Updated
normalizePermissionSystemConfigto parse both fields (omit when invalid/absent) - Updated
permissions.schema.json(type: "integer",minimum: 1) andconfig.example.json - Outcome: config system handles numeric fields; fallback to 200/80 constants when fields are absent
- Commit:
feat: add toolInputPreviewMaxLength and toolTextSummaryMaxLength config fields (#266)
- Added
-
✅ Wire config to
ToolPreviewFormatterconstruction (#266)- Added
resolveToolPreviewLimits(config)totool-preview-formatter.ts(narrowPickparam; applies??fallbacks to the three formatter options) PermissionGateHandler.handleToolCallnow constructs the formatter withresolveToolPreviewLimits(this.session.config)session.configis read fresh on every tool call - config reloads take effect automatically- Outcome: user-configured limits take effect at runtime; #266 is complete
- Commit:
feat: use configured preview limits in permission prompts (#266)
- Added
Step dependency diagram
flowchart TD
S1["Step 1: Extract ToolPreviewFormatter"]
S2["Step 2: Thread formatter through gates"]
S3["Step 3: Add numeric config fields"]
S4["Step 4: Wire config to formatter"]
S1 --> S2
S1 --> S4
S3 --> S4
S2 --> S4
Tracks
| Track | Steps | Description |
|---|---|---|
| Formatter extraction | 1 → 2 | Extract the collaborator, thread it through the call chain |
| Config schema | 3 | Add numeric fields to config (independent of extraction) |
| Integration | 4 | Wire config to formatter (depends on both tracks) |