From 86abf04b87ff0c386853ee9fd3e649c2de964ac7 Mon Sep 17 00:00:00 2001 From: acoliver Date: Sat, 28 Mar 2026 22:24:37 -0300 Subject: [PATCH 01/66] docs: add execution playbook for issue #1569b ESLint warning resolution 53-step linear todo list covering all 30,012 warnings across 5 phases. typescriptexpert implements, deepthinker verifies, coordinator orchestrates. All decisions resolved: disable 9 sonarjs false-positive rules, keep todo-tag, use codemod for arrow-function-convention, follow TypeScript undefined convention. --- project-plans/issue1569b/README.md | 448 +++++++++++++++++++++++++++++ 1 file changed, 448 insertions(+) create mode 100644 project-plans/issue1569b/README.md diff --git a/project-plans/issue1569b/README.md b/project-plans/issue1569b/README.md new file mode 100644 index 0000000000..2a7c53ce54 --- /dev/null +++ b/project-plans/issue1569b/README.md @@ -0,0 +1,448 @@ +# Issue #1569b: Resolve All ESLint Warnings Before Package Extraction + +## Execution Playbook + +**To start:** Tell the coordinator "execute the issue1569b plan" and it will run +start-to-finish using the todo list below. No further input needed unless a decision +point is hit. + +**Branch:** `issue1569b` (already created from main) + +**Subagent pattern:** `typescriptexpert` implements, `deepthinker` verifies after each +phase. Coordinator runs shell commands (auto-fix, lint, commit) directly. + +**Lint command (avoids OOM):** Must lint per-package: +```bash +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/core/src --no-error-on-unmatched-pattern 2>&1 | tail -1 +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/cli/src --no-error-on-unmatched-pattern 2>&1 | tail -1 +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/a2a-server/src --no-error-on-unmatched-pattern 2>&1 | tail -1 +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/vscode-ide-companion/src --no-error-on-unmatched-pattern 2>&1 | tail -1 +``` + +**Full verification suite (run between phases):** +```bash +npm run test && npm run lint && npm run typecheck && npm run format && npm run build && node scripts/start.js --profile-load synthetic "write me a haiku and nothing else" +``` + +**Commit pattern:** One commit per phase. Single PR at the end with all commits. + +--- + +## Linear Todo List + +Each item is one todo. Execute strictly in order. Items marked `[COORDINATOR]` are +shell commands run directly. Items marked `[typescriptexpert]` or `[deepthinker]` are +subagent delegations. + +--- + +### Phase 0: Auto-fix Sweep + +**T01 [COORDINATOR]** Run auto-fix for 100%-fixable rules across all packages: +```bash +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/core/src packages/cli/src packages/a2a-server/src packages/vscode-ide-companion/src --fix --rule '{"vitest/prefer-strict-equal":"warn","@typescript-eslint/prefer-nullish-coalescing":"warn"}' --no-error-on-unmatched-pattern +``` +Then run auto-fix for partially-fixable rules one at a time: +```bash +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/core/src packages/cli/src packages/a2a-server/src packages/vscode-ide-companion/src --fix --rule '{"@typescript-eslint/consistent-type-imports":"warn"}' --no-error-on-unmatched-pattern +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/core/src packages/cli/src packages/a2a-server/src packages/vscode-ide-companion/src --fix --rule '{"@typescript-eslint/strict-boolean-expressions":"warn"}' --no-error-on-unmatched-pattern +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/core/src packages/cli/src packages/a2a-server/src packages/vscode-ide-companion/src --fix --rule '{"@typescript-eslint/no-unnecessary-condition":"warn"}' --no-error-on-unmatched-pattern +``` +Run `npm run format` after. Run `npm run test` to catch breakage. Count remaining warnings. +Commit: `fix(lint): auto-fix eslint warnings (Phase 0)` + +--- + +### Phase 1: Manual TypeScript Strictness + +After Phase 0 auto-fix, ~2,200 warnings remain for these rules: +- `@typescript-eslint/strict-boolean-expressions` +- `@typescript-eslint/no-unnecessary-condition` +- `@typescript-eslint/no-misused-promises` +- `@typescript-eslint/switch-exhaustiveness-check` +- `@typescript-eslint/consistent-type-imports` (unfixable remainder) +- `@typescript-eslint/prefer-nullish-coalescing` (unfixable remainder) +- `@typescript-eslint/prefer-optional-chain` + +Split into subagent tasks by sub-package. Before each task, run per-package lint +to get the EXACT current file list and warning count for that scope. + +**T02 [typescriptexpert]** Fix all remaining `@typescript-eslint/*` warnings in +`packages/cli/src/ui/` (~600 warnings). Rules: strict-boolean-expressions, +no-unnecessary-condition, no-misused-promises, switch-exhaustiveness-check, +prefer-optional-chain. For each file: read it, understand the context, fix the +warning correctly (add null checks, add `?? undefined`, add `void`, add missing +switch cases, etc.). Run `npm run test` and `npm run typecheck` when done. + +**T03 [typescriptexpert]** Same as T02 for `packages/core/src/providers/` (~400 warnings). + +**T04 [typescriptexpert]** Same for `packages/core/src/tools/` (~250 warnings). + +**T05 [typescriptexpert]** Same for `packages/core/src/core/` (~220 warnings). + +**T06 [typescriptexpert]** Same for `packages/cli/src/config/` + `packages/cli/src/auth/` +(~205 warnings combined). + +**T07 [typescriptexpert]** Same for `packages/core/src/utils/` + `packages/core/src/hooks/` ++ `packages/core/src/services/` (~190 warnings combined). + +**T08 [typescriptexpert]** Same for ALL remaining directories in packages/core/src/ not +covered above (~250 warnings — mcp/, prompt-config/, recording/, storage/, auth/, +debug/, scheduler/, ide/, lsp/, policy/). + +**T09 [typescriptexpert]** Same for ALL remaining directories in packages/cli/src/ not +covered above + packages/a2a-server/ + packages/vscode-ide-companion/ (~85 warnings). + +**T10 [COORDINATOR]** Run full verification suite. Count remaining TS warnings (should be 0). +Commit: `fix(lint): resolve typescript strictness warnings (Phase 1)` + +**T11 [deepthinker]** Review Phase 0+1 changes. Read `git diff HEAD~2 --stat` to see scope. +Spot-check: are null checks correct? Are `void` annotations on fire-and-forget promises +correct? Are switch exhaustiveness fixes using `satisfies never` pattern? Are type imports +correct? Flag anything that changes runtime behavior incorrectly. + +--- + +### Phase 2: Complexity, Style, Import, ESLint-comments + +Rules: `complexity`, `max-lines-per-function`, `max-lines`, `no-else-return`, +`no-lonely-if`, `no-unneeded-ternary`, `import/*`, `eslint-comments/*`, `no-console`. + +**T12 [typescriptexpert]** Fix all complexity/style/import warnings in +`packages/cli/src/ui/` (~387 warnings). For complexity/max-lines-per-function: +extract helper functions, use early returns, simplify conditionals. For style rules: +apply the fix (remove else-after-return, merge lonely-if, simplify ternary). For +import rules: fix import ordering/duplicates. For no-console: replace with +debugLogger or remove. Run `npm run test` when done. + +**T13 [typescriptexpert]** Same for `packages/core/src/tools/` + `packages/core/src/providers/` +(~228 warnings combined). + +**T14 [typescriptexpert]** Same for `packages/cli/src/auth/` + `packages/cli/src/config/` ++ `packages/cli/src/utils/` (~150 warnings combined). + +**T15 [typescriptexpert]** Same for ALL remaining directories (~435 warnings). + +**T16 [COORDINATOR]** Run full verification suite. Count remaining warnings. +Commit: `fix(lint): resolve complexity/style/import warnings (Phase 2)` + +--- + +### Phase 3: Vitest Test Quality + +Rules: `vitest/no-conditional-in-test`, `vitest/no-conditional-expect`, +`vitest/require-to-throw-message`, `vitest/require-top-level-describe`, +`vitest/max-nested-describe`, `vitest/expect-expect`. + +**T17 [typescriptexpert]** Fix all vitest warnings in `packages/cli/src/**/*.test.{ts,tsx}` +(~400 warnings). For no-conditional-in-test/no-conditional-expect: refactor +conditional logic into separate test cases or use test.each. For +require-to-throw-message: add expected error message strings. For +require-top-level-describe: wrap bare tests in describe blocks. For +max-nested-describe: flatten nested describes. Run `npm run test` when done. + +**T18 [typescriptexpert]** Same for `packages/core/src/**/*.test.{ts,tsx}` (~400 warnings). + +**T19 [COORDINATOR]** Run full verification suite. +Commit: `fix(lint): resolve vitest test quality warnings (Phase 3)` + +**T20 [deepthinker]** Review Phase 2+3 changes. Spot-check: are extracted functions +well-named and logically cohesive? Did complexity refactoring preserve behavior? +Are test refactorings actually testing the same things? Flag anything suspicious. + +--- + +### Phase 4.0: Sonarjs Config Tuning + +**T21 [typescriptexpert]** Modify `eslint.config.js` to disable 9 sonarjs rules and +add test-file override. Specific changes to the general TS/TSX config block +(targeting `packages/*/src/**/*.{ts,tsx}`), add to rules section: + +```javascript +// Disabled sonarjs rules — see project-plans/issue1569b/README.md for rationale +'sonarjs/declarations-in-global-scope': 'off', // ESM false positives (2,937) +'sonarjs/no-unused-vars': 'off', // Redundant with @typescript-eslint (242) +'sonarjs/max-lines-per-function': 'off', // Redundant with base rule at 80 (690) +'sonarjs/process-argv': 'off', // CLI tool (366) +'sonarjs/standard-input': 'off', // CLI tool (239) +'sonarjs/publicly-writable-directories': 'off', // Test /tmp usage (240) +'sonarjs/pseudo-random': 'off', // Non-crypto context (87) +'sonarjs/no-reference-error': 'off', // TypeScript ambient false positives (280) +'sonarjs/no-undefined-assignment': 'off', // TypeScript undefined convention (938) +``` + +In the vitest test config block (targeting `packages/*/src/**/*.test.{ts,tsx}`), add: +```javascript +'sonarjs/no-duplicate-string': 'off', // Tests repeat strings legitimately (1,894) +``` + +Also verify the existing `sonarjs/max-lines-per-function` override if any — the base +ESLint `max-lines-per-function` should remain at `['warn', { max: 80, ... }]`. + +Run full verification suite after. Count warnings — should drop by ~6,019. +Commit: `fix(lint): disable false-positive/redundant sonarjs rules (Phase 4.0)` + +--- + +### Phase 4.1: Arrow Function Convention Codemod + +**T22 [typescriptexpert]** Write and run an AST-based codemod (jscodeshift or +ts-morph script) to fix `sonarjs/arrow-function-convention` (5,234 warnings). +The fix: remove unnecessary parens from single-parameter arrow functions where +the parameter is a simple identifier (no type annotation, no destructuring, no +default value, no rest param). Save the script to `scripts/codemods/arrow-parens.ts`. + +IMPORTANT edge cases that MUST keep parens: +- `(x: string) => ...` — typed parameter +- `({a, b}) => ...` — destructured parameter +- `([a, b]) => ...` — array destructured parameter +- `(x = 5) => ...` — default value +- `(...args) => ...` — rest parameter +- `() => ...` — zero parameters + +Run across all packages. Run `npm run format` after. Run `npm run test`. +Count remaining `arrow-function-convention` warnings — should be near 0. +If >50 remain, fix manually in the same task. +Commit: `fix(lint): remove unnecessary arrow function parens (Phase 4.1)` + +--- + +### Phase 4.3: Shorthand Property Grouping + +**T23 [typescriptexpert]** Fix `sonarjs/shorthand-property-grouping` warnings in +`packages/core/src/` (~400 warnings). In object literals, reorder properties so +shorthand properties (`{ foo, bar }`) are grouped together, not interleaved with +non-shorthand (`{ foo, baz: 1, bar }`). Run `npm run test` when done. + +**T24 [typescriptexpert]** Same for `packages/cli/src/` + `packages/a2a-server/src/` ++ `packages/vscode-ide-companion/src/` (~327 warnings). + +--- + +### Phase 4.4: Suggestion-fixable Code Quality + +**T25 [typescriptexpert]** Fix sonarjs suggestion-fixable warnings in `packages/core/src/` +(~350 warnings). Rules: `prefer-regexp-exec` (use .exec() instead of .match()), +`different-types-comparison` (fix type coercion in comparisons), +`no-alphabetical-sort` (replace .sort() with locale-aware or numeric sort), +`no-unused-function-argument` (prefix with _ or remove), +`prefer-immediate-return` (return expression directly instead of temp variable), +`no-undefined-argument` (remove trailing undefined args). Run `npm run test`. + +**T26 [typescriptexpert]** Same for `packages/cli/src/` + remaining packages (~309 warnings). + +--- + +### Phase 4.5: Duplicate String Constants (source only) + +**T27 [typescriptexpert]** Fix `sonarjs/no-duplicate-string` in source files only +(NOT test files — those are disabled in 4.0). ~142 warnings. Extract repeated +string literals into named constants. Run `npm run test`. + +**T28 [COORDINATOR]** Run full verification suite. +Commit: `fix(lint): resolve sonarjs mechanical warnings (Phases 4.1-4.5)` + +**T29 [deepthinker]** Review Phases 4.0-4.5 changes. Verify: config disables are +correct rules with correct keys. Codemod didn't break typed/destructured params. +Shorthand grouping didn't change semantics. String constant extraction is sensible. + +--- + +### Phase 4.6: Security Hotspot Review + Regex + +**T30 [typescriptexpert]** Fix sonarjs security/regex warnings in `packages/core/src/` +(~300 warnings). Rules: `regular-expr` (review regex for correctness), +`slow-regex` (optimize catastrophic backtracking), `concise-regex` (simplify), +`no-os-command-from-path` / `os-command` (use absolute paths or validate), +`file-permissions` (use restrictive permissions), `sockets` / `encryption` / +`hashing` (add eslint-disable with justification comment if intentional). +Run `npm run test`. + +**T31 [typescriptexpert]** Same for `packages/cli/src/` + remaining (~216 warnings). + +--- + +### Phase 4.7: Error Handling + +**T32 [typescriptexpert]** Fix `sonarjs/no-ignored-exceptions` across all packages +(167 warnings). For each empty catch block: add proper error handling (log, rethrow, +or add a comment explaining why swallowing is intentional with eslint-disable). +Never silently swallow errors without justification. Run `npm run test`. + +--- + +### Phase 4.8: Grab-bag + TODO Cleanup + +**T33 [typescriptexpert]** Fix remaining small sonarjs rules across all packages +(~350 warnings). Rules: `void-use`, `destructuring-assignment-syntax`, +`bool-param-default`, `no-duplicated-branches`, `no-identical-functions`, +`no-inconsistent-returns`, `no-dead-store`, `constructor-for-side-effects`, +`assertions-in-tests`, `generator-without-yield`, and ~15 other rules with <10 each. +Run `npm run test`. + +**T34 [typescriptexpert]** Fix `sonarjs/todo-tag` (281 warnings). For each TODO comment: +- If it's LLM placeholder cruft ("TODO: implement this", "TODO: add error handling") — + actually implement it or remove the TODO if it's already done +- If it's a legitimate future task — create or reference a GitHub issue number in the + comment (e.g., `// TODO(#1234): migrate to new API`) +- If it's obsolete — remove it +Goal: zero `todo-tag` warnings. Run `npm run test`. + +**T35 [COORDINATOR]** Run full verification suite. +Commit: `fix(lint): resolve sonarjs security/error/cleanup warnings (Phases 4.6-4.8)` + +--- + +### Phase 4.9: Complexity Refactoring (hardest phase) + +Rules: `nested-control-flow`, `cyclomatic-complexity`, `cognitive-complexity`, +`elseif-without-else`, `too-many-break-or-continue-in-loop`, +`expression-complexity`, `no-nested-conditional`, plus smaller structural rules. + +These require genuine refactoring: extract functions, simplify conditionals, +reduce nesting, use early returns / guard clauses. + +**T36 [typescriptexpert]** Fix sonarjs complexity warnings in `packages/cli/src/ui/` +(~500 warnings). Focus: extract React components, extract helper functions from +large render methods, simplify conditional rendering, use early returns. +Run `npm run test`. + +**T37 [typescriptexpert]** Fix complexity in `packages/core/src/providers/` (~450). +Focus: extract API call helpers, simplify retry/error handling logic, use +strategy patterns where appropriate. Run `npm run test`. + +**T38 [typescriptexpert]** Fix complexity in `packages/core/src/tools/` (~250). +Focus: break up large tool implementation functions, extract validation helpers. +Run `npm run test`. + +**T39 [typescriptexpert]** Fix complexity in `packages/core/src/core/` (~200). +Focus: simplify algorithms, use early returns, extract sub-functions. +Run `npm run test`. + +**T40 [typescriptexpert]** Fix complexity in `packages/core/src/prompt-config/` ++ `packages/core/src/services/` (~200). Focus: extract pipeline stages. +Run `npm run test`. + +**T41 [typescriptexpert]** Fix complexity in `packages/core/src/utils/` ++ `packages/core/src/recording/` (~180). Focus: flatten control flow. +Run `npm run test`. + +**T42 [typescriptexpert]** Fix complexity in `packages/cli/src/config/` ++ `packages/cli/src/utils/` + `packages/cli/src/auth/` (~200). +Focus: reduce if/else chains, use maps/lookups. Run `npm run test`. + +**T43 [typescriptexpert]** Fix complexity in ALL remaining directories (~382): +`packages/cli/src/runtime/`, `packages/cli/src/commands/`, +`packages/cli/src/services/`, `packages/core/src/mcp/`, +`packages/core/src/hooks/`, `packages/core/src/storage/`, +`packages/core/src/scheduler/`, `packages/a2a-server/`, +`packages/vscode-ide-companion/`. Run `npm run test`. + +**T44 [COORDINATOR]** Run full verification suite. +Commit: `fix(lint): refactor complex functions for sonarjs compliance (Phase 4.9)` + +**T45 [deepthinker]** Review Phase 4.9 complexity refactoring. This is the highest-risk +phase. Check: are extracted functions well-named, logically cohesive, and not just +moving complexity around? Are early returns correct? Did any behavioral changes slip +in? Are there any new bugs from control flow changes? Spot-check the 10 largest diffs. + +--- + +### Phase 4.10: Deprecation Fixes + +**T46 [typescriptexpert]** Fix sonarjs deprecation warnings across all packages +(148 warnings). Replace deprecated API calls with modern equivalents. +Run `npm run test`. + +--- + +### Phase 4.11: Type System Improvements + +**T47 [typescriptexpert]** Fix sonarjs type system warnings across all packages +(~250 warnings). Rules: `max-union-size` (break up large union types into +intermediate types), `variable-name` (fix naming convention violations), +`no-unused-collection` (remove or use unused collections), +`no-implicit-dependencies`, `use-type-alias`. Run `npm run test`. + +--- + +### Phase 4.12: File Splitting + +**T48 [typescriptexpert]** Fix `sonarjs/max-lines` warnings (58 warnings). +Split oversized files into smaller modules. Maintain the same public API via +re-exports from the original file path to avoid breaking imports. +Run `npm run test` and `npm run typecheck`. + +**T49 [COORDINATOR]** Run full verification suite. +Commit: `fix(lint): resolve remaining sonarjs warnings (Phases 4.10-4.12)` + +**T50 [deepthinker]** Final review of Phases 4.10-4.12. Check: are deprecated API +replacements correct? Are file splits clean with proper re-exports? Are type +changes backwards-compatible? + +--- + +### Phase 5: Final Verification + PR + +**T51 [COORDINATOR]** Run per-package lint and confirm 0 warnings, 0 errors: +```bash +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/core/src --no-error-on-unmatched-pattern 2>&1 | tail -1 +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/cli/src --no-error-on-unmatched-pattern 2>&1 | tail -1 +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/a2a-server/src --no-error-on-unmatched-pattern 2>&1 | tail -1 +NODE_OPTIONS=--max-old-space-size=12288 npx eslint packages/vscode-ide-companion/src --no-error-on-unmatched-pattern 2>&1 | tail -1 +``` +Run full verification suite one last time. + +**T52 [COORDINATOR]** Push branch and create PR: +```bash +git push -u origin issue1569b +``` +Create PR with `gh pr create` — title: "Resolve all ESLint warnings before package +extraction (Fixes #1569)" — body includes: +- Summary of what was done (30,012 warnings resolved) +- Phase breakdown with commit references +- List of disabled rules with rationale +- Note: todo-tag warnings driven to zero (not disabled) +- "closes #1569" in body + +**T53 [COORDINATOR]** Watch CI: `gh pr checks NUM --watch --interval 300`. +Loop up to 5 times. If failures: investigate, fix, push, watch again. +Review all CodeRabbit comments per project rules (evaluate each against source, +address or dismiss with explanation, resolve addressed ones). + +--- + +## Reference: Warning Inventory + +### By subsystem + +| Subsystem | Warnings | Files | +|---|---:|---:| +| packages/core | 16,188 | 905 | +| packages/cli | 13,375 | 955 | +| packages/a2a-server | 345 | 22 | +| packages/vscode-ide-companion | 104 | 9 | + +### By semantic category + +| Category | Count | % | +|---|---:|---:| +| sonarjs-quality | 18,947 | 63.1% | +| typescript-strictness | 6,515 | 21.7% | +| test-quality | 3,288 | 11.0% | +| complexity-size | 1,067 | 3.6% | +| control-flow-style | 88 | 0.3% | +| other (eslint-comments) | 59 | 0.2% | +| import-hygiene | 44 | 0.1% | +| console-logging | 4 | 0.0% | + +### Key Decisions (all resolved) + +1. **Null vs undefined:** Disable `sonarjs/no-undefined-assignment`. TypeScript + `undefined` convention. Absorbed into Phase 4.0. +2. **Arrow function codemod:** Try codemod first (Phase 4.1 T22). +3. **todo-tag:** Keep enabled — claudefraud detection. Fix all 281 in Phase 4.8 T34. +4. **Disabled sonarjs rules (9):** declarations-in-global-scope, no-unused-vars, + max-lines-per-function, process-argv, standard-input, publicly-writable-directories, + pseudo-random, no-reference-error, no-undefined-assignment. +5. **Disabled in tests only (1):** no-duplicate-string. From 6e4afcf1778d3173cc18aacd03a352b50b019019 Mon Sep 17 00:00:00 2001 From: acoliver Date: Sun, 29 Mar 2026 04:26:46 -0300 Subject: [PATCH 02/66] fix(lint): auto-fix eslint warnings (Phase 0) Run eslint --fix across all packages with consistent-type-imports disabled (its auto-fix incorrectly converts value imports to type imports in test files). 243 warnings resolved automatically: - @typescript-eslint/prefer-nullish-coalescing - @typescript-eslint/strict-boolean-expressions (partial) - @typescript-eslint/no-unnecessary-condition (partial) - Various other auto-fixable rules Remaining: 29,769 warnings (down from 30,012) --- packages/a2a-server/src/config/extension.ts | 3 +- packages/a2a-server/src/http/app.ts | 7 +- .../a2a-server/src/utils/testing_utils.ts | 4 +- .../__tests__/auth-flow-orchestrator.spec.ts | 5 +- .../oauthManager.proactive-renewal.test.ts | 3 +- .../proactive-renewal-manager.spec.ts | 3 +- .../token-access-coordinator.spec.ts | 3 +- .../src/auth/oauth-manager.issue1468.spec.ts | 3 +- .../auth/oauth-manager.refresh-race.spec.ts | 3 +- .../oauth-manager.runtime-messagebus.spec.ts | 3 +- .../cli/src/auth/proactive-renewal-manager.ts | 5 +- packages/cli/src/auth/provider-usage-info.ts | 4 +- .../auth/proxy/credential-store-factory.ts | 18 +- .../src/auth/proxy/sandbox-proxy-lifecycle.ts | 4 +- packages/cli/src/auth/qwen-oauth-provider.ts | 6 +- .../cli/src/auth/token-access-coordinator.ts | 3 +- .../cli/src/commands/extensions/config.ts | 3 +- .../cli/src/commands/extensions/settings.ts | 23 +- packages/cli/src/config/extension.ts | 8 +- packages/cli/src/config/extensions/consent.ts | 5 +- packages/cli/src/config/extensions/github.ts | 49 ++-- .../config/extensions/settingsIntegration.ts | 5 +- .../src/config/extensions/settingsStorage.ts | 6 +- packages/cli/src/config/interactiveContext.ts | 3 +- packages/cli/src/config/mcpServerConfig.ts | 15 +- packages/cli/src/config/postConfigRuntime.ts | 2 +- .../cli/src/config/settings-validation.ts | 18 +- packages/cli/src/config/settings.ts | 3 +- packages/cli/src/config/toolGovernance.ts | 2 +- packages/cli/src/config/trustedFolders.ts | 3 +- .../src/extensions/extensionAutoUpdater.ts | 2 +- .../cli/src/integration-tests/test-utils.ts | 3 +- .../todo-continuation.integration.test.ts | 3 +- packages/cli/src/runtime/bucketFailover.ts | 3 +- packages/cli/src/runtime/runtimeAccessors.ts | 6 +- .../todoContinuationService.ts | 31 +-- packages/cli/src/ui/commands/authCommand.ts | 6 +- packages/cli/src/ui/commands/chatCommand.ts | 11 +- .../cli/src/ui/commands/compressCommand.ts | 2 +- packages/cli/src/ui/commands/hooksCommand.ts | 2 +- packages/cli/src/ui/commands/logoutCommand.ts | 13 +- packages/cli/src/ui/commands/lspCommand.ts | 2 +- packages/cli/src/ui/commands/mouseCommand.ts | 3 +- .../cli/src/ui/commands/policiesCommand.ts | 3 +- .../cli/src/ui/commands/setupGithubCommand.ts | 3 +- .../cli/src/ui/commands/subagentCommand.ts | 49 ++-- .../cli/src/ui/commands/toolkeyCommand.ts | 11 +- .../cli/src/ui/commands/toolkeyfileCommand.ts | 11 +- packages/cli/src/ui/components/Help.tsx | 17 +- .../cli/src/ui/components/InputPrompt.tsx | 2 +- .../cli/src/ui/components/LoggingDialog.tsx | 19 +- .../cli/src/ui/components/OAuthCodeDialog.tsx | 9 +- .../components/ProfileCreateWizard/utils.ts | 3 +- .../cli/src/ui/components/SettingsDialog.tsx | 8 +- .../cli/src/ui/components/StatsDisplay.tsx | 4 +- .../ProfileAttachmentWizard.tsx | 4 +- packages/cli/src/ui/components/TodoPanel.tsx | 13 +- .../cli/src/ui/components/ToolsDialog.tsx | 3 +- .../src/ui/components/shared/MaxSizedBox.tsx | 32 +-- .../ui/components/shared/VirtualizedList.tsx | 9 +- .../ui/components/shared/word-navigation.ts | 11 +- .../AppContainer/hooks/useAppBootstrap.ts | 9 +- .../AppContainer/hooks/useAppDialogs.ts | 3 +- .../AppContainer/hooks/useAppInput.ts | 3 +- .../hooks/useRecordingInfrastructure.ts | 2 +- .../src/ui/contexts/KeypressContext.test.tsx | 33 ++- .../cli/src/ui/contexts/ToolCallProvider.tsx | 8 +- .../cli/src/ui/hooks/atCommandProcessor.ts | 2 +- .../geminiStream/toolCompletionHandler.ts | 10 +- .../cli/src/ui/hooks/slashCommandProcessor.ts | 19 +- .../cli/src/ui/hooks/useExtensionUpdates.ts | 3 +- .../useGeminiStream.integration.test.tsx | 7 +- .../hooks/useGeminiStream.subagent.spec.tsx | 7 +- .../cli/src/ui/hooks/useGeminiStream.test.tsx | 12 +- .../hooks/useGeminiStream.thinking.test.tsx | 7 +- .../cli/src/ui/hooks/useStableCallback.ts | 4 +- packages/cli/src/ui/hooks/useTimer.ts | 8 +- .../cli/src/ui/hooks/useTodoContinuation.ts | 6 +- packages/cli/src/ui/themes/color-utils.ts | 3 +- packages/cli/src/ui/themes/theme-manager.ts | 4 +- packages/cli/src/ui/utils/ConsolePatcher.ts | 14 +- packages/cli/src/ui/utils/MarkdownDisplay.tsx | 28 +- .../cli/src/ui/utils/commandUtils.test.ts | 3 +- packages/cli/src/ui/utils/fuzzyFilter.ts | 18 +- packages/cli/src/ui/utils/mouse.ts | 28 +- .../cli/src/ui/utils/secureInputHandler.ts | 15 +- packages/cli/src/ui/utils/terminalSetup.ts | 12 +- packages/cli/src/ui/utils/tokenFormatters.ts | 6 +- packages/cli/src/utils/gitUtils.ts | 2 +- packages/cli/src/utils/sandbox.ts | 9 +- packages/cli/src/utils/sessionUtils.ts | 4 +- packages/cli/src/utils/skillUtils.ts | 3 +- packages/cli/src/utils/stdinSafety.ts | 9 +- .../cli/src/zed-integration/zedIntegration.ts | 31 +-- packages/core/src/agents/executor.ts | 3 +- packages/core/src/auth/precedence.ts | 2 +- .../proxy-provider-key-storage.test.ts | 3 +- .../__tests__/proxy-socket-client.test.ts | 4 +- .../proxy/__tests__/proxy-token-store.test.ts | 3 +- packages/core/src/auth/qwen-device-flow.ts | 3 +- .../src/auth/token-store.refresh-race.spec.ts | 5 +- packages/core/src/config/config.ts | 3 +- packages/core/src/config/endpoints.ts | 3 +- packages/core/src/config/profileManager.ts | 8 +- .../core/src/config/schedulerSingleton.ts | 8 +- packages/core/src/config/storage.ts | 3 +- .../__tests__/geminiChat.runtimeState.test.ts | 5 +- .../geminiClient.runtimeState.test.ts | 5 +- packages/core/src/core/client.ts | 6 +- packages/core/src/core/prompts.ts | 10 +- .../core/src/core/subagentRuntimeSetup.ts | 4 +- .../src/hooks/__tests__/hookSemantics.test.ts | 2 +- packages/core/src/hooks/hookRunner.ts | 11 +- packages/core/src/mcp/file-token-store.ts | 4 +- packages/core/src/models/hydration.ts | 4 +- packages/core/src/policy/config.ts | 241 +++++++++--------- .../src/prompt-config/prompt-installer.ts | 2 +- .../core/src/prompt-config/prompt-service.ts | 4 +- packages/core/src/providers/BaseProvider.ts | 21 +- .../src/providers/LoggingProviderWrapper.ts | 2 +- .../__tests__/RetryOrchestrator.test.ts | 5 +- .../anthropic/AnthropicMessageNormalizer.ts | 20 +- .../anthropic/AnthropicMessageValidator.ts | 4 +- .../src/providers/gemini/GeminiProvider.ts | 19 +- packages/core/src/providers/kimi/usageInfo.ts | 3 +- ...AIResponsesProvider.promptCacheKey.test.ts | 4 +- .../openai-vercel/OpenAIVercelProvider.ts | 9 +- .../OpenAIProvider.emptyResponseRetry.test.ts | 3 +- .../providers/openai/OpenAIRequestBuilder.ts | 21 +- .../src/providers/openai/ToolCallCollector.ts | 2 +- .../providers/openai/ToolCallNormalizer.ts | 2 +- .../src/providers/openai/ToolNameValidator.ts | 13 +- .../openai/syntheticToolResponses.ts | 5 +- .../src/providers/openai/toolNameUtils.ts | 13 +- .../core/src/providers/synthetic/usageInfo.ts | 3 +- packages/core/src/providers/zai/usageInfo.ts | 3 +- .../src/services/history/HistoryService.ts | 15 +- .../shellExecutionService.windows.test.ts | 19 +- packages/core/src/skills/skillLoader.ts | 2 +- .../storage/secure-store-integration.test.ts | 5 +- .../core/src/storage/secure-store.test.ts | 3 +- .../core/src/telemetry/uiTelemetry.test.ts | 47 ++-- .../src/test-utils/mockWorkspaceContext.ts | 4 +- .../src/test-utils/providerCallOptions.ts | 2 +- packages/core/src/tools/ToolFormatter.ts | 17 +- .../ast-edit-characterization.test.ts | 2 +- packages/core/src/tools/apply-patch.ts | 2 +- .../core/src/tools/check-async-tasks.test.ts | 5 +- packages/core/src/tools/edit.test.ts | 2 +- packages/core/src/tools/edit.ts | 51 ++-- packages/core/src/tools/glob.ts | 3 +- .../src/tools/google-web-search-invocation.ts | 2 +- packages/core/src/tools/grep.ts | 3 +- packages/core/src/tools/ls.ts | 3 +- .../core/src/tools/lsp-diagnostics-helper.ts | 2 +- packages/core/src/tools/mcp-client.ts | 17 +- packages/core/src/tools/memoryTool.ts | 76 +++--- packages/core/src/tools/modifiable-tool.ts | 9 +- packages/core/src/tools/shell.ts | 25 +- packages/core/src/tools/task.ts | 6 +- .../core/src/tools/tool-key-storage.test.ts | 5 +- packages/core/src/tools/tool-key-storage.ts | 3 +- packages/core/src/tools/tool-registry.ts | 3 +- packages/core/src/tools/write-file.ts | 2 +- packages/core/src/utils/errors.ts | 2 +- packages/core/src/utils/extensionLoader.ts | 4 +- packages/core/src/utils/fetch.ts | 3 +- packages/core/src/utils/partUtils.ts | 6 +- .../core/src/utils/quotaErrorDetection.ts | 2 +- packages/core/src/utils/retry.test.ts | 12 +- packages/core/src/utils/retry.ts | 5 +- packages/core/src/utils/toolOutputLimiter.ts | 94 ++++--- .../src/open-files-manager.ts | 2 +- 173 files changed, 788 insertions(+), 1027 deletions(-) diff --git a/packages/a2a-server/src/config/extension.ts b/packages/a2a-server/src/config/extension.ts index 6c79cddb7a..4e7e1b3667 100644 --- a/packages/a2a-server/src/config/extension.ts +++ b/packages/a2a-server/src/config/extension.ts @@ -151,8 +151,7 @@ export function loadInstallMetadata( const metadataFilePath = path.join(extensionDir, INSTALL_METADATA_FILENAME); try { const configContent = fs.readFileSync(metadataFilePath, 'utf-8'); - const metadata = JSON.parse(configContent) as ExtensionInstallMetadata; - return metadata; + return JSON.parse(configContent) as ExtensionInstallMetadata; } catch (e) { logger.warn( `Failed to load or parse extension install metadata at ${metadataFilePath}: ${e}`, diff --git a/packages/a2a-server/src/http/app.ts b/packages/a2a-server/src/http/app.ts index 2b3ef61e5c..f18eb9d570 100644 --- a/packages/a2a-server/src/http/app.ts +++ b/packages/a2a-server/src/http/app.ts @@ -211,11 +211,10 @@ export async function createApp() { eventBus.off('event', eventHandler); eventBus.finished(); return res.end(); - } else { - const result = await commandToExecute.execute(context, args ?? []); - logger.info('[CoreAgent] Sending /executeCommand response: ', result); - return res.status(200).json(result); } + const result = await commandToExecute.execute(context, args ?? []); + logger.info('[CoreAgent] Sending /executeCommand response: ', result); + return res.status(200).json(result); } catch (e) { logger.error( `Error executing /executeCommand: ${command} with args: ${JSON.stringify( diff --git a/packages/a2a-server/src/utils/testing_utils.ts b/packages/a2a-server/src/utils/testing_utils.ts index ce427cdc97..5420bc28cb 100644 --- a/packages/a2a-server/src/utils/testing_utils.ts +++ b/packages/a2a-server/src/utils/testing_utils.ts @@ -219,7 +219,7 @@ export function createMockConfig( }, ); - const scheduler = { + return { schedule, cancelAll: vi.fn(), dispose: vi.fn(), @@ -230,8 +230,6 @@ export function createMockConfig( getTool: vi.fn(), }, } as unknown as CoreToolScheduler; - - return scheduler; }, ), ...overrides, diff --git a/packages/cli/src/auth/__tests__/auth-flow-orchestrator.spec.ts b/packages/cli/src/auth/__tests__/auth-flow-orchestrator.spec.ts index 027aafd8a4..1881ad3ad1 100644 --- a/packages/cli/src/auth/__tests__/auth-flow-orchestrator.spec.ts +++ b/packages/cli/src/auth/__tests__/auth-flow-orchestrator.spec.ts @@ -8,9 +8,10 @@ */ import { describe, it, expect, vi } from 'vitest'; -import type { OAuthToken, TokenStore } from '../types.js'; -import type { OAuthProvider } from '../types.js'; import type { + OAuthToken, + TokenStore, + OAuthProvider, BucketFailoverOAuthManagerLike, AuthenticatorInterface, } from '../types.js'; diff --git a/packages/cli/src/auth/__tests__/oauthManager.proactive-renewal.test.ts b/packages/cli/src/auth/__tests__/oauthManager.proactive-renewal.test.ts index d1bb91cc61..a4ed9dcc80 100644 --- a/packages/cli/src/auth/__tests__/oauthManager.proactive-renewal.test.ts +++ b/packages/cli/src/auth/__tests__/oauthManager.proactive-renewal.test.ts @@ -14,8 +14,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { OAuthManager } from '../oauth-manager.js'; -import type { OAuthProvider } from '../types.js'; -import type { TokenStore, OAuthToken } from '../types.js'; +import type { OAuthProvider, TokenStore, OAuthToken } from '../types.js'; /** * Create a mock token with specified expiry time diff --git a/packages/cli/src/auth/__tests__/proactive-renewal-manager.spec.ts b/packages/cli/src/auth/__tests__/proactive-renewal-manager.spec.ts index a33dfc3973..9ed9d046f0 100644 --- a/packages/cli/src/auth/__tests__/proactive-renewal-manager.spec.ts +++ b/packages/cli/src/auth/__tests__/proactive-renewal-manager.spec.ts @@ -14,8 +14,7 @@ import { ProactiveRenewalManager, MAX_PROACTIVE_RENEWAL_FAILURES, } from '../proactive-renewal-manager.js'; -import type { OAuthProvider } from '../types.js'; -import type { TokenStore, OAuthToken } from '../types.js'; +import type { OAuthProvider, TokenStore, OAuthToken } from '../types.js'; function createMockToken( expirySeconds: number, diff --git a/packages/cli/src/auth/__tests__/token-access-coordinator.spec.ts b/packages/cli/src/auth/__tests__/token-access-coordinator.spec.ts index 938f740b95..8da4af3e11 100644 --- a/packages/cli/src/auth/__tests__/token-access-coordinator.spec.ts +++ b/packages/cli/src/auth/__tests__/token-access-coordinator.spec.ts @@ -21,8 +21,7 @@ import { describe, it, expect, vi } from 'vitest'; import { TokenAccessCoordinator } from '../token-access-coordinator.js'; -import type { OAuthProvider } from '../types.js'; -import type { OAuthToken, TokenStore } from '../types.js'; +import type { OAuthProvider, OAuthToken, TokenStore } from '../types.js'; import type { OAuthTokenRequestMetadata } from '@vybestack/llxprt-code-core'; // -------------------------------------------------------------------------- diff --git a/packages/cli/src/auth/oauth-manager.issue1468.spec.ts b/packages/cli/src/auth/oauth-manager.issue1468.spec.ts index 792d59a4d9..7d7dd6ed5c 100644 --- a/packages/cli/src/auth/oauth-manager.issue1468.spec.ts +++ b/packages/cli/src/auth/oauth-manager.issue1468.spec.ts @@ -56,8 +56,7 @@ vi.mock('../runtime/runtimeSettings.js', () => ({ })); import { OAuthManager } from './oauth-manager.js'; -import type { OAuthProvider } from './types.js'; -import type { OAuthToken, TokenStore } from './types.js'; +import type { OAuthProvider, OAuthToken, TokenStore } from './types.js'; import { LoadedSettings } from '../config/settings.js'; import type { Settings } from '../config/settings.js'; import type { OAuthTokenRequestMetadata } from '@vybestack/llxprt-code-core'; diff --git a/packages/cli/src/auth/oauth-manager.refresh-race.spec.ts b/packages/cli/src/auth/oauth-manager.refresh-race.spec.ts index cddabafa8d..0a6874aacb 100644 --- a/packages/cli/src/auth/oauth-manager.refresh-race.spec.ts +++ b/packages/cli/src/auth/oauth-manager.refresh-race.spec.ts @@ -11,8 +11,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { OAuthManager } from './oauth-manager.js'; -import type { OAuthProvider } from './types.js'; -import type { OAuthToken, TokenStore } from './types.js'; +import type { OAuthProvider, OAuthToken, TokenStore } from './types.js'; import { promises as fs } from 'fs'; import { join } from 'path'; import { tmpdir } from 'os'; diff --git a/packages/cli/src/auth/oauth-manager.runtime-messagebus.spec.ts b/packages/cli/src/auth/oauth-manager.runtime-messagebus.spec.ts index 9bc21e6459..f448a6598c 100644 --- a/packages/cli/src/auth/oauth-manager.runtime-messagebus.spec.ts +++ b/packages/cli/src/auth/oauth-manager.runtime-messagebus.spec.ts @@ -7,8 +7,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { MessageBus, PolicyEngine } from '@vybestack/llxprt-code-core'; import { OAuthManager } from './oauth-manager.js'; -import type { OAuthProvider } from './types.js'; -import type { OAuthToken, TokenStore } from './types.js'; +import type { OAuthProvider, OAuthToken, TokenStore } from './types.js'; const mockEphemeralSettings = new Map(); diff --git a/packages/cli/src/auth/proactive-renewal-manager.ts b/packages/cli/src/auth/proactive-renewal-manager.ts index 722555fc9b..0ea391d64d 100644 --- a/packages/cli/src/auth/proactive-renewal-manager.ts +++ b/packages/cli/src/auth/proactive-renewal-manager.ts @@ -15,8 +15,7 @@ import { mergeRefreshedToken, type OAuthTokenWithExtras, } from '@vybestack/llxprt-code-core'; -import type { OAuthToken, TokenStore } from './types.js'; -import type { OAuthProvider } from './types.js'; +import type { OAuthToken, TokenStore, OAuthProvider } from './types.js'; import { createProfileManager, isLoadBalancerProfileLike, @@ -290,7 +289,7 @@ export class ProactiveRenewalManager { normalizedBucket, ); - if (!currentToken || !currentToken.refresh_token) { + if (!currentToken?.refresh_token) { this.clearProactiveRenewal(key); return; } diff --git a/packages/cli/src/auth/provider-usage-info.ts b/packages/cli/src/auth/provider-usage-info.ts index 7d8226a548..e1e62e4998 100644 --- a/packages/cli/src/auth/provider-usage-info.ts +++ b/packages/cli/src/auth/provider-usage-info.ts @@ -236,11 +236,11 @@ export async function getHigherPriorityAuth( // SettingsService not registered (subagent/test context) — skip authOnly check } - if (merged.providerApiKeys && merged.providerApiKeys[providerName]) { + if (merged.providerApiKeys?.[providerName]) { return 'API Key'; } - if (merged.providerKeyfiles && merged.providerKeyfiles[providerName]) { + if (merged.providerKeyfiles?.[providerName]) { return 'Keyfile'; } diff --git a/packages/cli/src/auth/proxy/credential-store-factory.ts b/packages/cli/src/auth/proxy/credential-store-factory.ts index d09e4bde03..adc596497a 100644 --- a/packages/cli/src/auth/proxy/credential-store-factory.ts +++ b/packages/cli/src/auth/proxy/credential-store-factory.ts @@ -56,12 +56,11 @@ export function createTokenStore(): TokenStore { proxyTokenStore = new ProxyTokenStore(socketPath); } return proxyTokenStore; - } else { - if (!directTokenStore) { - directTokenStore = new KeyringTokenStore(); - } - return directTokenStore; } + if (!directTokenStore) { + directTokenStore = new KeyringTokenStore(); + } + return directTokenStore; } /** @@ -83,12 +82,11 @@ export function createProviderKeyStorage(): ProviderKeyStorage { proxyKeyStorage = new ProxyProviderKeyStorage(client); } return proxyKeyStorage as unknown as ProviderKeyStorage; - } else { - if (!directKeyStorage) { - directKeyStorage = getProviderKeyStorage(); - } - return directKeyStorage; } + if (!directKeyStorage) { + directKeyStorage = getProviderKeyStorage(); + } + return directKeyStorage; } /** diff --git a/packages/cli/src/auth/proxy/sandbox-proxy-lifecycle.ts b/packages/cli/src/auth/proxy/sandbox-proxy-lifecycle.ts index 300fa91d5a..4c568697a7 100644 --- a/packages/cli/src/auth/proxy/sandbox-proxy-lifecycle.ts +++ b/packages/cli/src/auth/proxy/sandbox-proxy-lifecycle.ts @@ -113,13 +113,11 @@ class CodexFlowAdapter implements OAuthFlowInterface { ); // Complete the device auth flow - const token = await this.flow.completeDeviceAuth( + return this.flow.completeDeviceAuth( codeResult.authorization_code, codeResult.code_verifier, 'https://auth.openai.com/deviceauth/callback', ); - - return token; } /** diff --git a/packages/cli/src/auth/qwen-oauth-provider.ts b/packages/cli/src/auth/qwen-oauth-provider.ts index bd61ececf5..b881da58d9 100644 --- a/packages/cli/src/auth/qwen-oauth-provider.ts +++ b/packages/cli/src/auth/qwen-oauth-provider.ts @@ -282,12 +282,10 @@ export class QwenOAuthProvider implements OAuthProvider { await this.ensureInitialized(); return this.errorHandler.handleGracefully( - async () => { + async () => // Line 59: RETURN AWAIT this.tokenStore.getToken('qwen') // Read-only - OAuthManager owns all refresh operations - const token = (await this.tokenStore?.getToken('qwen')) || null; - return token; - }, + (await this.tokenStore?.getToken('qwen')) || null, null, // Return null on error this.name, 'getToken', diff --git a/packages/cli/src/auth/token-access-coordinator.ts b/packages/cli/src/auth/token-access-coordinator.ts index bf21e424a0..22a4cb5102 100644 --- a/packages/cli/src/auth/token-access-coordinator.ts +++ b/packages/cli/src/auth/token-access-coordinator.ts @@ -17,8 +17,9 @@ import { type Config, type OAuthTokenRequestMetadata, } from '@vybestack/llxprt-code-core'; -import type { OAuthToken, TokenStore } from './types.js'; import type { + OAuthToken, + TokenStore, AuthenticatorInterface, BucketFailoverOAuthManagerLike, } from './types.js'; diff --git a/packages/cli/src/commands/extensions/config.ts b/packages/cli/src/commands/extensions/config.ts index 185a60c352..a157c70976 100644 --- a/packages/cli/src/commands/extensions/config.ts +++ b/packages/cli/src/commands/extensions/config.ts @@ -56,14 +56,13 @@ async function configureSetting( settingKey: string, scope: ExtensionSettingScope, ): Promise { - const success = await updateSetting( + return updateSetting( extensionName, extensionPath, settingKey, promptForSetting, scope, ); - return success; } /** diff --git a/packages/cli/src/commands/extensions/settings.ts b/packages/cli/src/commands/extensions/settings.ts index 920f05e4dd..a1401cde25 100644 --- a/packages/cli/src/commands/extensions/settings.ts +++ b/packages/cli/src/commands/extensions/settings.ts @@ -64,19 +64,18 @@ export async function promptForSetting( }; stdin.on('data', onData); }); - } else { - const readline = await import('node:readline'); - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - return new Promise((resolve) => { - rl.question(prompt, (answer) => { - rl.close(); - resolve(answer); - }); - }); } + const readline = await import('node:readline'); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + return new Promise((resolve) => { + rl.question(prompt, (answer) => { + rl.close(); + resolve(answer); + }); + }); } /** diff --git a/packages/cli/src/config/extension.ts b/packages/cli/src/config/extension.ts index 8fb5b75410..a506711c4b 100644 --- a/packages/cli/src/config/extension.ts +++ b/packages/cli/src/config/extension.ts @@ -489,8 +489,7 @@ export function loadInstallMetadata( const metadataFilePath = path.join(extensionDir, INSTALL_METADATA_FILENAME); try { const configContent = fs.readFileSync(metadataFilePath, 'utf-8'); - const metadata = JSON.parse(configContent) as ExtensionInstallMetadata; - return metadata; + return JSON.parse(configContent) as ExtensionInstallMetadata; } catch (_e) { return undefined; } @@ -549,10 +548,7 @@ export async function requestConsentNonInteractive( consentDescription: string, ): Promise { console.info(consentDescription); - const result = await promptForConsentNonInteractive( - 'Do you want to continue? [Y/n]: ', - ); - return result; + return promptForConsentNonInteractive('Do you want to continue? [Y/n]: '); } /** diff --git a/packages/cli/src/config/extensions/consent.ts b/packages/cli/src/config/extensions/consent.ts index 465cb8682b..ed65a12b12 100644 --- a/packages/cli/src/config/extensions/consent.ts +++ b/packages/cli/src/config/extensions/consent.ts @@ -175,10 +175,7 @@ export async function requestConsentNonInteractive( consentDescription: string, ): Promise { debugLogger.log(consentDescription); - const result = await promptForConsentNonInteractive( - 'Do you want to continue? [Y/n]: ', - ); - return result; + return promptForConsentNonInteractive('Do you want to continue? [Y/n]: '); } /** diff --git a/packages/cli/src/config/extensions/github.ts b/packages/cli/src/config/extensions/github.ts index 8e3a25708d..fd3723bbfb 100644 --- a/packages/cli/src/config/extensions/github.ts +++ b/packages/cli/src/config/extensions/github.ts @@ -288,27 +288,26 @@ export async function checkForExtensionUpdate( } setExtensionUpdateState(ExtensionUpdateState.UPDATE_AVAILABLE); return; - } else { - const { source, releaseTag } = installMetadata; - if (!source) { - debugLogger.error(`No "source" provided for extension.`); - setExtensionUpdateState(ExtensionUpdateState.ERROR); - return; - } - const { owner, repo } = parseGitHubRepoForReleases(source); + } + const { source, releaseTag } = installMetadata; + if (!source) { + debugLogger.error(`No "source" provided for extension.`); + setExtensionUpdateState(ExtensionUpdateState.ERROR); + return; + } + const { owner, repo } = parseGitHubRepoForReleases(source); - const releaseData = await fetchReleaseFromGithub( - owner, - repo, - installMetadata.ref, - ); - if (releaseData.tag_name !== releaseTag) { - setExtensionUpdateState(ExtensionUpdateState.UPDATE_AVAILABLE); - return; - } - setExtensionUpdateState(ExtensionUpdateState.UP_TO_DATE); + const releaseData = await fetchReleaseFromGithub( + owner, + repo, + installMetadata.ref, + ); + if (releaseData.tag_name !== releaseTag) { + setExtensionUpdateState(ExtensionUpdateState.UPDATE_AVAILABLE); return; } + setExtensionUpdateState(ExtensionUpdateState.UP_TO_DATE); + return; } catch (error) { debugLogger.error( `Failed to check for updates for extension "${installMetadata.source}": ${getErrorMessage(error)}`, @@ -345,14 +344,12 @@ export async function downloadFromGitHubRelease( if (asset) { archiveUrl = asset.url; fileName = asset.name; - } else { - if (releaseData.tarball_url) { - archiveUrl = releaseData.tarball_url; - isTar = true; - } else if (releaseData.zipball_url) { - archiveUrl = releaseData.zipball_url; - isZip = true; - } + } else if (releaseData.tarball_url) { + archiveUrl = releaseData.tarball_url; + isTar = true; + } else if (releaseData.zipball_url) { + archiveUrl = releaseData.zipball_url; + isZip = true; } if (!archiveUrl) { throw new Error( diff --git a/packages/cli/src/config/extensions/settingsIntegration.ts b/packages/cli/src/config/extensions/settingsIntegration.ts index cb07880e43..fd6b145070 100644 --- a/packages/cli/src/config/extensions/settingsIntegration.ts +++ b/packages/cli/src/config/extensions/settingsIntegration.ts @@ -223,10 +223,9 @@ export function getEnvFilePath( extensionName, '.env', ); - } else { - // User settings go in extension directory .env - return path.join(extensionDir, '.env'); } + // User settings go in extension directory .env + return path.join(extensionDir, '.env'); } /** diff --git a/packages/cli/src/config/extensions/settingsStorage.ts b/packages/cli/src/config/extensions/settingsStorage.ts index 6ccfbfab3c..a35cedbaf2 100644 --- a/packages/cli/src/config/extensions/settingsStorage.ts +++ b/packages/cli/src/config/extensions/settingsStorage.ts @@ -46,9 +46,9 @@ export function getKeychainServiceName( const sanitized = extensionName.replace(/[^a-zA-Z0-9-_]/g, ''); // Check if this is a workspace-scoped path - const isWorkspaceScope = - extensionDir && - extensionDir.replace(/\\/g, '/').includes('.llxprt/extensions'); + const isWorkspaceScope = extensionDir + ?.replace(/\\/g, '/') + .includes('.llxprt/extensions'); if (isWorkspaceScope) { // Include workspace identifier for workspace scope diff --git a/packages/cli/src/config/interactiveContext.ts b/packages/cli/src/config/interactiveContext.ts index 0de1efaa3b..eae086a32a 100644 --- a/packages/cli/src/config/interactiveContext.ts +++ b/packages/cli/src/config/interactiveContext.ts @@ -180,8 +180,7 @@ function resolveExtensions( } function resolveInteractiveMode(argv: CliArgs): boolean { - const hasPromptWords = - argv.promptWords && argv.promptWords.some((word) => word.trim() !== ''); + const hasPromptWords = argv.promptWords?.some((word) => word.trim() !== ''); return ( !!argv.promptInteractive || !!argv.experimentalAcp || diff --git a/packages/cli/src/config/mcpServerConfig.ts b/packages/cli/src/config/mcpServerConfig.ts index 61fa96e47e..82ca4fbadb 100644 --- a/packages/cli/src/config/mcpServerConfig.ts +++ b/packages/cli/src/config/mcpServerConfig.ts @@ -56,15 +56,14 @@ export function allowedMcpServers( return isAllowed; }), ); - } else { - blockedMcpServers.push( - ...Object.entries(mcpServers).map(([key, server]) => ({ - name: key, - extensionName: server.extensionName || '', - })), - ); - return {}; } + blockedMcpServers.push( + ...Object.entries(mcpServers).map(([key, server]) => ({ + name: key, + extensionName: server.extensionName || '', + })), + ); + return {}; } export function resolveMcpServers( diff --git a/packages/cli/src/config/postConfigRuntime.ts b/packages/cli/src/config/postConfigRuntime.ts index 9d2e671be8..a69da1526a 100644 --- a/packages/cli/src/config/postConfigRuntime.ts +++ b/packages/cli/src/config/postConfigRuntime.ts @@ -9,6 +9,7 @@ import { DebugLogger, ProfileManager, type Config, + SettingsService, } from '@vybestack/llxprt-code-core'; import { getCliRuntimeContext } from '../runtime/runtimeAccessors.js'; import { setCliRuntimeContext } from '../runtime/runtimeLifecycle.js'; @@ -30,7 +31,6 @@ import type { CliArgs } from './cliArgParser.js'; import type { Settings } from './settings.js'; import type { ProfileLoadResult } from './profileResolution.js'; import type { ProviderModelResult } from './providerModelResolver.js'; -import type { SettingsService } from '@vybestack/llxprt-code-core'; const logger = new DebugLogger('llxprt:config:postConfigRuntime'); diff --git a/packages/cli/src/config/settings-validation.ts b/packages/cli/src/config/settings-validation.ts index 00f7c06f0b..568939194e 100644 --- a/packages/cli/src/config/settings-validation.ts +++ b/packages/cli/src/config/settings-validation.ts @@ -120,15 +120,14 @@ function buildEnumSchema( ...Array>, ], ); - } else { - return z.union( - values.map((v) => z.literal(v)) as [ - z.ZodLiteral, - z.ZodLiteral, - ...Array>, - ], - ); } + return z.union( + values.map((v) => z.literal(v)) as [ + z.ZodLiteral, + z.ZodLiteral, + ...Array>, + ], + ); } /** @@ -295,8 +294,7 @@ export function validateSettings(data: unknown): { data?: unknown; error?: z.ZodError; } { - const result = settingsZodSchema.safeParse(data); - return result; + return settingsZodSchema.safeParse(data); } /** diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts index b919173537..add590f5ae 100644 --- a/packages/cli/src/config/settings.ts +++ b/packages/cli/src/config/settings.ts @@ -74,9 +74,8 @@ export function getSystemSettingsPath(): string { return '/Library/Application Support/LLxprt-Code/settings.json'; } else if (platform() === 'win32') { return 'C:\\ProgramData\\llxprt-code\\settings.json'; - } else { - return '/etc/llxprt-code/settings.json'; } + return '/etc/llxprt-code/settings.json'; } export function getSystemDefaultsPath(): string { diff --git a/packages/cli/src/config/toolGovernance.ts b/packages/cli/src/config/toolGovernance.ts index 3f76c7f4a8..072f9a2662 100644 --- a/packages/cli/src/config/toolGovernance.ts +++ b/packages/cli/src/config/toolGovernance.ts @@ -11,8 +11,8 @@ import { SHELL_TOOL_NAMES, type ApprovalMode, ApprovalMode as ApprovalModeEnum, + GeminiCLIExtension, } from '@vybestack/llxprt-code-core'; -import type { GeminiCLIExtension } from '@vybestack/llxprt-code-core'; import type { Settings } from './settings.js'; import type { CliArgs } from './cliArgParser.js'; import type { ContextResolutionResult } from './interactiveContext.js'; diff --git a/packages/cli/src/config/trustedFolders.ts b/packages/cli/src/config/trustedFolders.ts index 624ac05dd5..0dd3e35ac7 100644 --- a/packages/cli/src/config/trustedFolders.ts +++ b/packages/cli/src/config/trustedFolders.ts @@ -208,8 +208,7 @@ export function saveTrustedFolders( /** Is folder trust feature enabled per the current applied settings */ export function isFolderTrustEnabled(settings: Settings): boolean { // In llxprt, we use flat settings structure - const folderTrustSetting = settings.folderTrust ?? false; - return folderTrustSetting; + return settings.folderTrust ?? false; } function getWorkspaceTrustFromLocalConfig(): boolean | undefined { diff --git a/packages/cli/src/extensions/extensionAutoUpdater.ts b/packages/cli/src/extensions/extensionAutoUpdater.ts index 22bfe38a69..308e0bac3a 100644 --- a/packages/cli/src/extensions/extensionAutoUpdater.ts +++ b/packages/cli/src/extensions/extensionAutoUpdater.ts @@ -286,7 +286,7 @@ export class ExtensionAutoUpdater { continue; } const extension = extensionsByName.get(name); - if (!extension || !extension.installMetadata) { + if (!extension?.installMetadata) { entry.pendingInstall = false; entry.lastError = `Extension "${name}" is no longer installed.`; entry.state = ExtensionUpdateState.ERROR; diff --git a/packages/cli/src/integration-tests/test-utils.ts b/packages/cli/src/integration-tests/test-utils.ts index 98382e91a7..a4a399721c 100644 --- a/packages/cli/src/integration-tests/test-utils.ts +++ b/packages/cli/src/integration-tests/test-utils.ts @@ -16,8 +16,7 @@ import { Config, MessageBus, Profile } from '@vybestack/llxprt-code-core'; */ export async function createTempDirectory(): Promise { const prefix = 'llxprt-test-'; - const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), prefix)); - return tempDir; + return fs.mkdtemp(path.join(os.tmpdir(), prefix)); } /** diff --git a/packages/cli/src/integration-tests/todo-continuation.integration.test.ts b/packages/cli/src/integration-tests/todo-continuation.integration.test.ts index 922debd444..d2139a00fe 100644 --- a/packages/cli/src/integration-tests/todo-continuation.integration.test.ts +++ b/packages/cli/src/integration-tests/todo-continuation.integration.test.ts @@ -265,8 +265,7 @@ describe('Todo Continuation Integration Tests', () => { value: 'test', } as ServerGeminiStreamEvent; // Create a mock Turn object - const mockTurn = {} as Turn; - return mockTurn; + return {} as Turn; }); // When: Send ephemeral message diff --git a/packages/cli/src/runtime/bucketFailover.ts b/packages/cli/src/runtime/bucketFailover.ts index 4ac1d417f7..3fdeda266d 100644 --- a/packages/cli/src/runtime/bucketFailover.ts +++ b/packages/cli/src/runtime/bucketFailover.ts @@ -188,8 +188,7 @@ export async function executeWithBucketFailover( const bucket = buckets[i]; try { - const response = await executor(request, bucket); - return response; + return await executor(request, bucket); } catch (error) { const err = error as Error; lastError = err; diff --git a/packages/cli/src/runtime/runtimeAccessors.ts b/packages/cli/src/runtime/runtimeAccessors.ts index ad150d2f35..a9ebb64368 100644 --- a/packages/cli/src/runtime/runtimeAccessors.ts +++ b/packages/cli/src/runtime/runtimeAccessors.ts @@ -73,17 +73,17 @@ export function getCliRuntimeContext() { const identity = resolveActiveRuntimeIdentity(); const entry = runtimeRegistry.get(identity.runtimeId); - if (!entry || !entry.config) { + if (!entry?.config) { const registeredIds = Array.from(runtimeRegistry.keys()); const scope = getCurrentRuntimeScope(); const activeCtx = peekActiveProviderRuntimeContext(); logger.debug( () => - `[getCliRuntimeContext] MISS: runtimeId=${identity.runtimeId}, hasEntry=${!!entry}, hasConfig=${!!(entry && entry.config)}, registered=[${registeredIds.join(', ')}], scope=${JSON.stringify(scope)}, activeCtx.runtimeId=${activeCtx?.runtimeId}`, + `[getCliRuntimeContext] MISS: runtimeId=${identity.runtimeId}, hasEntry=${!!entry}, hasConfig=${!!entry?.config}, registered=[${registeredIds.join(', ')}], scope=${JSON.stringify(scope)}, activeCtx.runtimeId=${activeCtx?.runtimeId}`, ); } - if (entry && entry.config) { + if (entry?.config) { // @plan:PLAN-20251023-STATELESS-HARDENING.P08 // @requirement:REQ-SP4-004 - Remove singleton fallbacks when stateless hardening is enabled const settingsService = entry.settingsService; diff --git a/packages/cli/src/services/todo-continuation/todoContinuationService.ts b/packages/cli/src/services/todo-continuation/todoContinuationService.ts index 53d0fc3731..2ebf4b8d5e 100644 --- a/packages/cli/src/services/todo-continuation/todoContinuationService.ts +++ b/packages/cli/src/services/todo-continuation/todoContinuationService.ts @@ -125,13 +125,12 @@ export class TodoContinuationService { config.attemptCount, config.previousFailure, ); - } else { - return this.generateStandardPrompt( - taskDescription, - config.attemptCount, - config.previousFailure, - ); } + return this.generateStandardPrompt( + taskDescription, + config.attemptCount, + config.previousFailure, + ); } /** @@ -230,7 +229,7 @@ export class TodoContinuationService { * @returns Formatted task description string */ formatTaskDescription(todo: Todo): string { - if (!todo || !todo.content) { + if (!todo?.content) { return 'Complete task'; } @@ -357,9 +356,8 @@ export class TodoContinuationService { if (isYoloMode) { return this.generateYoloModePrompt(truncatedDescription); - } else { - return this.generateStandardPrompt(truncatedDescription); } + return this.generateStandardPrompt(truncatedDescription); } /** @@ -391,15 +389,14 @@ export class TodoContinuationService { ) { // Good word boundary found return truncated.substring(0, lastSpaceIndex) + '...'; - } else { - // No good word boundary, hard truncate - return ( - truncated.substring( - 0, - TodoContinuationService.MAX_TASK_DESCRIPTION_LENGTH - 3, - ) + '...' - ); } + // No good word boundary, hard truncate + return ( + truncated.substring( + 0, + TodoContinuationService.MAX_TASK_DESCRIPTION_LENGTH - 3, + ) + '...' + ); } /** diff --git a/packages/cli/src/ui/commands/authCommand.ts b/packages/cli/src/ui/commands/authCommand.ts index 0fe5a3e815..99cd3cad2f 100644 --- a/packages/cli/src/ui/commands/authCommand.ts +++ b/packages/cli/src/ui/commands/authCommand.ts @@ -99,14 +99,13 @@ const logoutCompleter: CompleterFn = withFuzzyFilter( const oauthManager = getOAuthManager(); const buckets = await oauthManager.listBuckets(provider); - const options = [ + return [ { value: '--all', description: 'Logout from all buckets' }, ...buckets.map((bucket) => ({ value: bucket, description: `Logout from bucket: ${bucket}`, })), ]; - return options; } catch { return []; } @@ -298,9 +297,8 @@ export class AuthCommandExecutor { messageType: 'info', content: status, }; - } else { - status += ' (authenticated)'; } + status += ' (authenticated)'; } else if (isEnabled && !isAuthenticated) { status += ' (not authenticated)'; } diff --git a/packages/cli/src/ui/commands/chatCommand.ts b/packages/cli/src/ui/commands/chatCommand.ts index bf4e0af9a4..2e831bf104 100644 --- a/packages/cli/src/ui/commands/chatCommand.ts +++ b/packages/cli/src/ui/commands/chatCommand.ts @@ -165,13 +165,12 @@ const saveCommand: SlashCommand = { messageType: 'info', content: `Conversation checkpoint saved with tag: ${decodeTagName(tag)}.`, }; - } else { - return { - type: 'message', - messageType: 'info', - content: 'No conversation found to save.', - }; } + return { + type: 'message', + messageType: 'info', + content: 'No conversation found to save.', + }; }, }; diff --git a/packages/cli/src/ui/commands/compressCommand.ts b/packages/cli/src/ui/commands/compressCommand.ts index 5851419f52..1226fc9a19 100644 --- a/packages/cli/src/ui/commands/compressCommand.ts +++ b/packages/cli/src/ui/commands/compressCommand.ts @@ -41,7 +41,7 @@ export const compressCommand: SlashCommand = { ui.setPendingItem(pendingMessage); const promptId = `compress-${Date.now()}`; const geminiClient = context.services.config?.getGeminiClient(); - if (!geminiClient || !geminiClient.hasChatInitialized()) { + if (!geminiClient?.hasChatInitialized()) { ui.addItem( { type: MessageType.ERROR, diff --git a/packages/cli/src/ui/commands/hooksCommand.ts b/packages/cli/src/ui/commands/hooksCommand.ts index f7d18ebbe6..7793510304 100644 --- a/packages/cli/src/ui/commands/hooksCommand.ts +++ b/packages/cli/src/ui/commands/hooksCommand.ts @@ -455,7 +455,7 @@ export const hooksCommand: SlashCommand = { disableAllCommand, ].find((cmd) => cmd.name === subCommandName); - if (subCommand && subCommand.action) { + if (subCommand?.action) { await subCommand.action(context, subArgs); } else { await listHooks(context); diff --git a/packages/cli/src/ui/commands/logoutCommand.ts b/packages/cli/src/ui/commands/logoutCommand.ts index 75948570b1..815db5ad0c 100644 --- a/packages/cli/src/ui/commands/logoutCommand.ts +++ b/packages/cli/src/ui/commands/logoutCommand.ts @@ -65,14 +65,13 @@ export const logoutCommand: SlashCommand = { messageType: 'info', content: `Successfully logged out of ${provider}`, }; - } else { - // User wasn't authenticated but we cleaned up any stale tokens - return { - type: 'message', - messageType: 'info', - content: `Cleaned up authentication state for ${provider} (was not authenticated)`, - }; } + // User wasn't authenticated but we cleaned up any stale tokens + return { + type: 'message', + messageType: 'info', + content: `Cleaned up authentication state for ${provider} (was not authenticated)`, + }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); diff --git a/packages/cli/src/ui/commands/lspCommand.ts b/packages/cli/src/ui/commands/lspCommand.ts index ed6c359ce4..5c3778e062 100644 --- a/packages/cli/src/ui/commands/lspCommand.ts +++ b/packages/cli/src/ui/commands/lspCommand.ts @@ -101,7 +101,7 @@ async function statusAction( } const lspClient = config.getLspServiceClient?.(); - if (!lspClient || !lspClient.isAlive()) { + if (!lspClient?.isAlive()) { const reason = lspClient?.getUnavailableReason?.() ?? 'service startup failed'; return { diff --git a/packages/cli/src/ui/commands/mouseCommand.ts b/packages/cli/src/ui/commands/mouseCommand.ts index 6f790d66a9..8b1ca64678 100644 --- a/packages/cli/src/ui/commands/mouseCommand.ts +++ b/packages/cli/src/ui/commands/mouseCommand.ts @@ -38,8 +38,7 @@ export const mouseCommand: SlashCommand = { } const currentlyActive = isMouseEventsActive(); - const nextActive = - mode === 'toggle' ? !currentlyActive : mode === 'on' ? true : false; + const nextActive = mode === 'toggle' ? !currentlyActive : mode === 'on'; setMouseEventsActive(nextActive); diff --git a/packages/cli/src/ui/commands/policiesCommand.ts b/packages/cli/src/ui/commands/policiesCommand.ts index c733be7618..d5b12f3979 100644 --- a/packages/cli/src/ui/commands/policiesCommand.ts +++ b/packages/cli/src/ui/commands/policiesCommand.ts @@ -36,9 +36,8 @@ function getTierBand(priority: number): string { return 'Tier 2 (User-defined)'; } else if (priority >= 1.0) { return 'Tier 1 (Defaults)'; - } else { - return 'Tier 0 (System)'; } + return 'Tier 0 (System)'; } /** diff --git a/packages/cli/src/ui/commands/setupGithubCommand.ts b/packages/cli/src/ui/commands/setupGithubCommand.ts index 678a9956ef..d11668d0cf 100644 --- a/packages/cli/src/ui/commands/setupGithubCommand.ts +++ b/packages/cli/src/ui/commands/setupGithubCommand.ts @@ -58,8 +58,7 @@ function getOpenUrlsCommands(readmeUrl: string): string[] { } // Create and join the individual commands - const commands = urlsToOpen.map((url) => `${openCmd} "${url}"`); - return commands; + return urlsToOpen.map((url) => `${openCmd} "${url}"`); } // Add LLxprt Code specific entries to .gitignore file diff --git a/packages/cli/src/ui/commands/subagentCommand.ts b/packages/cli/src/ui/commands/subagentCommand.ts index f800163c03..c3bcc286aa 100644 --- a/packages/cli/src/ui/commands/subagentCommand.ts +++ b/packages/cli/src/ui/commands/subagentCommand.ts @@ -131,7 +131,7 @@ const subagentSchema = [ const names = await manager.listSubagents(); - const suggestions = await Promise.all( + return Promise.all( names.map(async (name) => { try { const details = await manager.loadSubagent(name); @@ -147,8 +147,6 @@ const subagentSchema = [ } }), ); - - return suggestions; }), }, { @@ -287,31 +285,30 @@ const saveCommand: SlashCommand = { return handleManualMode(context, name, profile, finalSystemPrompt, { existed: exists, }); - } else { - // Auto mode: generate using LLM - const configService = services.config; // @plan:PLAN-20250117-SUBAGENTCONFIG.P14 @requirement:REQ-003 - if (!configService) { - return { - type: 'message', - messageType: 'error', - content: - 'Configuration service unavailable. Set up the CLI before using auto mode.', - }; - } + } + // Auto mode: generate using LLM + const configService = services.config; // @plan:PLAN-20250117-SUBAGENTCONFIG.P14 @requirement:REQ-003 + if (!configService) { + return { + type: 'message', + messageType: 'error', + content: + 'Configuration service unavailable. Set up the CLI before using auto mode.', + }; + } - try { - finalSystemPrompt = await generateAutoPrompt(configService, input); - } catch (error) { - const errorMessage = - error instanceof Error ? error.message : String(error); - return { - type: 'message', - messageType: 'error', - content: `Error: Failed to generate system prompt (${errorMessage}). Try manual mode or check your connection.`, - }; - } - return saveSubagent(context, name, profile, finalSystemPrompt, exists); + try { + finalSystemPrompt = await generateAutoPrompt(configService, input); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + return { + type: 'message', + messageType: 'error', + content: `Error: Failed to generate system prompt (${errorMessage}). Try manual mode or check your connection.`, + }; } + return saveSubagent(context, name, profile, finalSystemPrompt, exists); }, }; diff --git a/packages/cli/src/ui/commands/toolkeyCommand.ts b/packages/cli/src/ui/commands/toolkeyCommand.ts index bc095f4864..40fe698c68 100644 --- a/packages/cli/src/ui/commands/toolkeyCommand.ts +++ b/packages/cli/src/ui/commands/toolkeyCommand.ts @@ -99,13 +99,12 @@ export const toolkeyCommand: SlashCommand = { messageType: 'info', content: `${entry.displayName} API key: ${masked}`, }; - } else { - return { - type: 'message', - messageType: 'info', - content: `No API key configured for '${entry.displayName}'`, - }; } + return { + type: 'message', + messageType: 'info', + content: `No API key configured for '${entry.displayName}'`, + }; } // @pseudocode lines 305-308: Clear key (case-insensitive "none") diff --git a/packages/cli/src/ui/commands/toolkeyfileCommand.ts b/packages/cli/src/ui/commands/toolkeyfileCommand.ts index 9df8d1a634..b38dbe375e 100644 --- a/packages/cli/src/ui/commands/toolkeyfileCommand.ts +++ b/packages/cli/src/ui/commands/toolkeyfileCommand.ts @@ -102,13 +102,12 @@ export const toolkeyfileCommand: SlashCommand = { messageType: 'info', content: `${entry.displayName} keyfile: ${currentPath}`, }; - } else { - return { - type: 'message', - messageType: 'info', - content: `No keyfile configured for '${entry.displayName}'`, - }; } + return { + type: 'message', + messageType: 'info', + content: `No keyfile configured for '${entry.displayName}'`, + }; } // @pseudocode lines 355-358: Clear keyfile (case-insensitive "none") diff --git a/packages/cli/src/ui/components/Help.tsx b/packages/cli/src/ui/components/Help.tsx index 99d021d985..239f221e99 100644 --- a/packages/cli/src/ui/components/Help.tsx +++ b/packages/cli/src/ui/components/Help.tsx @@ -75,16 +75,15 @@ export const Help: React.FC = ({ commands }) => ( {command.description && ' - ' + command.description} - {command.subCommands && - command.subCommands.map((subCommand) => ( - - - {' '} - {subCommand.name} - - {subCommand.description && ' - ' + subCommand.description} + {command.subCommands?.map((subCommand) => ( + + + {' '} + {subCommand.name} - ))} + {subCommand.description && ' - ' + subCommand.description} + + ))} ))} diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx index a955053689..fdc3ee69b7 100644 --- a/packages/cli/src/ui/components/InputPrompt.tsx +++ b/packages/cli/src/ui/components/InputPrompt.tsx @@ -440,7 +440,7 @@ export const InputPrompt: React.FC = ({ return; } - if (vimHandleInput && vimHandleInput(key)) { + if (vimHandleInput?.(key)) { return; } diff --git a/packages/cli/src/ui/components/LoggingDialog.tsx b/packages/cli/src/ui/components/LoggingDialog.tsx index 3b0f736a3c..9ed426b88d 100644 --- a/packages/cli/src/ui/components/LoggingDialog.tsx +++ b/packages/cli/src/ui/components/LoggingDialog.tsx @@ -54,17 +54,16 @@ const formatTimestamp = (timestamp: string, isNarrow: boolean): string => { minute: '2-digit', hour12: false, }); - } else { - // Full format for wider screens - return date.toLocaleString('en-US', { - month: 'short', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }); } + // Full format for wider screens + return date.toLocaleString('en-US', { + month: 'short', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false, + }); }; const formatContent = (content: string, maxLength: number): string => { diff --git a/packages/cli/src/ui/components/OAuthCodeDialog.tsx b/packages/cli/src/ui/components/OAuthCodeDialog.tsx index 3cc4173a7f..7497a93b5f 100644 --- a/packages/cli/src/ui/components/OAuthCodeDialog.tsx +++ b/packages/cli/src/ui/components/OAuthCodeDialog.tsx @@ -45,12 +45,11 @@ export const OAuthCodeDialog: React.FC = ({ 'Please paste it into your browser to authenticate with Google.', 'After authenticating, paste the verification code you receive below:', ]; - } else { - return [ - 'Please check your browser and authorize the application.', - 'After authorizing, paste the authorization code below:', - ]; } + return [ + 'Please check your browser and authorize the application.', + 'After authorizing, paste the authorization code below:', + ]; }, [provider]); /** diff --git a/packages/cli/src/ui/components/ProfileCreateWizard/utils.ts b/packages/cli/src/ui/components/ProfileCreateWizard/utils.ts index 07a761de8a..1b40f16473 100644 --- a/packages/cli/src/ui/components/ProfileCreateWizard/utils.ts +++ b/packages/cli/src/ui/components/ProfileCreateWizard/utils.ts @@ -35,8 +35,7 @@ function expandTilde(filePath: string): string { export function needsBaseUrlConfig(provider: string | null): boolean { if (!provider) return false; const providerOption = PROVIDER_OPTIONS.find((p) => p.value === provider); - const result = providerOption?.needsBaseUrl ?? false; - return result; + return providerOption?.needsBaseUrl ?? false; } export function generateProfileNameSuggestions( diff --git a/packages/cli/src/ui/components/SettingsDialog.tsx b/packages/cli/src/ui/components/SettingsDialog.tsx index 779fa788d1..403de73321 100644 --- a/packages/cli/src/ui/components/SettingsDialog.tsx +++ b/packages/cli/src/ui/components/SettingsDialog.tsx @@ -345,9 +345,8 @@ export function SettingsDialog({ const generateSettingsItems = () => { if (subSettingsMode.isActive) { return generateSubSettingsItems(subSettingsMode.parentKey); - } else { - return generateNormalSettingsItems(); } + return generateNormalSettingsItems(); }; const generateSubSettingsItems = (parentKey: string) => { @@ -475,10 +474,7 @@ export function SettingsDialog({ if (!requiresRestart(fullKey)) { saveSingleSetting(fullKey, newValue, settings, selectedScope); } else { - setModifiedSettings((prev) => { - const updated = new Set(prev).add(fullKey); - return updated; - }); + setModifiedSettings((prev) => new Set(prev).add(fullKey)); } }, }; diff --git a/packages/cli/src/ui/components/StatsDisplay.tsx b/packages/cli/src/ui/components/StatsDisplay.tsx index 5974e4b325..c3ee1c601c 100644 --- a/packages/cli/src/ui/components/StatsDisplay.tsx +++ b/packages/cli/src/ui/components/StatsDisplay.tsx @@ -74,7 +74,7 @@ const buildModelRows = (models: Record) => { const getBaseModelName = (name: string) => name.replace('-001', ''); // Models with active usage - const activeRows = Object.entries(models).map( + return Object.entries(models).map( ([name, metrics]: [string, ModelMetrics]) => { const modelName = getBaseModelName(name); const cachedTokens = metrics.tokens.cached; @@ -91,8 +91,6 @@ const buildModelRows = (models: Record) => { }; }, ); - - return activeRows; }; const ModelUsageTable: React.FC<{ diff --git a/packages/cli/src/ui/components/SubagentManagement/ProfileAttachmentWizard.tsx b/packages/cli/src/ui/components/SubagentManagement/ProfileAttachmentWizard.tsx index 7788aa5b74..9e364aa46d 100644 --- a/packages/cli/src/ui/components/SubagentManagement/ProfileAttachmentWizard.tsx +++ b/packages/cli/src/ui/components/SubagentManagement/ProfileAttachmentWizard.tsx @@ -60,9 +60,7 @@ export const ProfileAttachmentWizard: React.FC< .catch(() => { if (!cancelled) setPreviewInfo(null); }); - } else { - if (!cancelled) setPreviewInfo(null); - } + } else if (!cancelled) setPreviewInfo(null); return () => { cancelled = true; }; diff --git a/packages/cli/src/ui/components/TodoPanel.tsx b/packages/cli/src/ui/components/TodoPanel.tsx index 1b8912e956..52fe78f41c 100644 --- a/packages/cli/src/ui/components/TodoPanel.tsx +++ b/packages/cli/src/ui/components/TodoPanel.tsx @@ -88,12 +88,13 @@ const TodoPanelComponent: React.FC = ({ }, [todos]); // Subscribe to tool call updates to re-render when they change - useEffect(() => { - const unsubscribe = subscribe(() => { - setContentKey((prev) => prev + 1); - }); - return unsubscribe; - }, [subscribe]); + useEffect( + () => + subscribe(() => { + setContentKey((prev) => prev + 1); + }), + [subscribe], + ); const maxVisibleItems = useMemo(() => calculateMaxVisibleItems(rows), [rows]); const currentTodoIndex = todos.findIndex( diff --git a/packages/cli/src/ui/components/ToolsDialog.tsx b/packages/cli/src/ui/components/ToolsDialog.tsx index a6f7e11b60..d266442c93 100644 --- a/packages/cli/src/ui/components/ToolsDialog.tsx +++ b/packages/cli/src/ui/components/ToolsDialog.tsx @@ -32,9 +32,8 @@ export const ToolsDialog: React.FC = ({ const availableTools = tools.filter((tool) => { if (action === 'disable') { return !disabledTools.includes(tool.name); - } else { - return disabledTools.includes(tool.name); } + return disabledTools.includes(tool.name); }); // Create items for RadioButtonSelect diff --git a/packages/cli/src/ui/components/shared/MaxSizedBox.tsx b/packages/cli/src/ui/components/shared/MaxSizedBox.tsx index a33978ad4d..6c9c547997 100644 --- a/packages/cli/src/ui/components/shared/MaxSizedBox.tsx +++ b/packages/cli/src/ui/components/shared/MaxSizedBox.tsx @@ -326,17 +326,15 @@ function visitBoxRow(element: React.ReactNode): Row { if (parentProps === undefined || parentProps.wrap === 'wrap') { hasSeenWrapped = true; row.segments.push(segment); + } else if (!hasSeenWrapped) { + row.noWrapSegments.push(segment); } else { - if (!hasSeenWrapped) { - row.noWrapSegments.push(segment); - } else { - // put in the wrapped segment as the row is already stuck in wrapped mode. - row.segments.push(segment); - debugReportError( - 'Text elements without wrapping cannot appear after elements with wrapping in the same row.', - element, - ); - } + // put in the wrapped segment as the row is already stuck in wrapped mode. + row.segments.push(segment); + debugReportError( + 'Text elements without wrapping cannot appear after elements with wrapping in the same row.', + element, + ); } return; } @@ -526,15 +524,13 @@ function layoutInkElementAsStyledText( function addWrappingPartToLines() { if (lines.length === 0) { lines.push([...nonWrappingContent, ...wrappingPart]); + } else if (noWrappingWidth > 0) { + lines.push([ + ...[{ text: ' '.repeat(noWrappingWidth), props: {} }], + ...wrappingPart, + ]); } else { - if (noWrappingWidth > 0) { - lines.push([ - ...[{ text: ' '.repeat(noWrappingWidth), props: {} }], - ...wrappingPart, - ]); - } else { - lines.push(wrappingPart); - } + lines.push(wrappingPart); } wrappingPart = []; wrappingPartWidth = 0; diff --git a/packages/cli/src/ui/components/shared/VirtualizedList.tsx b/packages/cli/src/ui/components/shared/VirtualizedList.tsx index cdfb4befa6..5e5ee1f8d9 100644 --- a/packages/cli/src/ui/components/shared/VirtualizedList.tsx +++ b/packages/cli/src/ui/components/shared/VirtualizedList.tsx @@ -109,14 +109,13 @@ function VirtualizedList( return { index: 0, offset: 0 }; }); - const [isStickingToBottom, setIsStickingToBottom] = useState(() => { - const scrollToEnd = + const [isStickingToBottom, setIsStickingToBottom] = useState( + () => initialScrollIndex === SCROLL_TO_ITEM_END || (typeof initialScrollIndex === 'number' && initialScrollIndex >= data.length - 1 && - initialScrollOffsetInIndex === SCROLL_TO_ITEM_END); - return scrollToEnd; - }); + initialScrollOffsetInIndex === SCROLL_TO_ITEM_END), + ); const containerRef = useRef(null); const [containerHeight, setContainerHeight] = useState(0); const itemRefs = useRef>([]); diff --git a/packages/cli/src/ui/components/shared/word-navigation.ts b/packages/cli/src/ui/components/shared/word-navigation.ts index 1f4b4f2989..faa1333422 100644 --- a/packages/cli/src/ui/components/shared/word-navigation.ts +++ b/packages/cli/src/ui/components/shared/word-navigation.ts @@ -168,13 +168,12 @@ export const findPrevWordStartInLine = ( i--; } return i + 1; - } else { - // We're in punctuation, move to its beginning - while (i >= 0 && !isWordCharStrict(chars[i]) && !isWhitespace(chars[i])) { - i--; - } - return i + 1; } + // We're in punctuation, move to its beginning + while (i >= 0 && !isWordCharStrict(chars[i]) && !isWhitespace(chars[i])) { + i--; + } + return i + 1; }; /** diff --git a/packages/cli/src/ui/containers/AppContainer/hooks/useAppBootstrap.ts b/packages/cli/src/ui/containers/AppContainer/hooks/useAppBootstrap.ts index 69724b503c..fa8e9fe770 100644 --- a/packages/cli/src/ui/containers/AppContainer/hooks/useAppBootstrap.ts +++ b/packages/cli/src/ui/containers/AppContainer/hooks/useAppBootstrap.ts @@ -4,7 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { useCallback, useEffect, useMemo, useState, useRef } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useState, + useRef, +} from 'react'; import { useStdin, useStdout } from 'ink'; import { useResponsive } from '../../../hooks/useResponsive.js'; import { useBracketedPaste } from '../../../hooks/useBracketedPaste.js'; @@ -41,7 +47,6 @@ import { registerCleanup } from '../../../../utils/cleanup.js'; import type { LoadedSettings } from '../../../../config/settings.js'; import type { HistoryItem } from '../../../types.js'; import type { TodoContinuationHook } from './useTodoContinuationFlow.js'; -import React from 'react'; export interface AppBootstrapProps { config: Config; diff --git a/packages/cli/src/ui/containers/AppContainer/hooks/useAppDialogs.ts b/packages/cli/src/ui/containers/AppContainer/hooks/useAppDialogs.ts index ba3a30a754..4abc0e4ee3 100644 --- a/packages/cli/src/ui/containers/AppContainer/hooks/useAppDialogs.ts +++ b/packages/cli/src/ui/containers/AppContainer/hooks/useAppDialogs.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useThemeCommand } from '../../../hooks/useThemeCommand.js'; import { useAuthCommand } from '../../../hooks/useAuthCommand.js'; import { useFolderTrust } from '../../../hooks/useFolderTrust.js'; @@ -37,7 +37,6 @@ import type { import type { LoadedSettings } from '../../../../config/settings.js'; import type { AppState, AppAction } from '../../../reducers/appReducer.js'; import type { HistoryItem, ConsoleMessageItem } from '../../../types.js'; -import React from 'react'; const QUEUE_ERROR_DISPLAY_DURATION_MS = 3000; diff --git a/packages/cli/src/ui/containers/AppContainer/hooks/useAppInput.ts b/packages/cli/src/ui/containers/AppContainer/hooks/useAppInput.ts index f2345c5d7d..dc6bbffd93 100644 --- a/packages/cli/src/ui/containers/AppContainer/hooks/useAppInput.ts +++ b/packages/cli/src/ui/containers/AppContainer/hooks/useAppInput.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { useCallback, useMemo, useRef } from 'react'; +import React, { useCallback, useMemo, useRef } from 'react'; import { useGeminiStream } from '../../../hooks/geminiStream/index.js'; import { useAutoAcceptIndicator } from '../../../hooks/useAutoAcceptIndicator.js'; import { useLoadingIndicator } from '../../../hooks/useLoadingIndicator.js'; @@ -26,7 +26,6 @@ import { useExitHandling } from './useExitHandling.js'; import { useInputHandling } from './useInputHandling.js'; import { useShellFocusAutoReset } from './useShellFocusAutoReset.js'; import * as fs from 'fs'; -import React from 'react'; import type { AppBootstrapResult } from './useAppBootstrap.js'; import type { AppDialogsResult } from './useAppDialogs.js'; diff --git a/packages/cli/src/ui/containers/AppContainer/hooks/useRecordingInfrastructure.ts b/packages/cli/src/ui/containers/AppContainer/hooks/useRecordingInfrastructure.ts index 7127be9615..9853f28a51 100644 --- a/packages/cli/src/ui/containers/AppContainer/hooks/useRecordingInfrastructure.ts +++ b/packages/cli/src/ui/containers/AppContainer/hooks/useRecordingInfrastructure.ts @@ -9,9 +9,9 @@ import type { RecordingIntegration, SessionRecordingService, LockHandle, + SessionMetadata, } from '@vybestack/llxprt-code-core'; import type { RecordingSwapCallbacks } from '../../../../services/performResume.js'; -import type { SessionMetadata } from '@vybestack/llxprt-code-core'; /** * @hook useRecordingInfrastructure diff --git a/packages/cli/src/ui/contexts/KeypressContext.test.tsx b/packages/cli/src/ui/contexts/KeypressContext.test.tsx index 4e89eeec12..4ee70e8419 100644 --- a/packages/cli/src/ui/contexts/KeypressContext.test.tsx +++ b/packages/cli/src/ui/contexts/KeypressContext.test.tsx @@ -694,24 +694,23 @@ describe('Kitty Sequence Parsing', () => { paste: false, }, }; - } else { - // iTerm2 and VSCode send accented characters (å, ø, µ) - // Note: µ (mu) is sent with meta:false on iTerm2/VSCode but - // gets converted to m with meta:true - return { - terminal, - key, - chunk: accentedChar, - expected: { - name: key, - ctrl: false, - meta: true, // Always expect meta:true after conversion - shift: false, - paste: false, - sequence: accentedChar, - }, - }; } + // iTerm2 and VSCode send accented characters (å, ø, µ) + // Note: µ (mu) is sent with meta:false on iTerm2/VSCode but + // gets converted to m with meta:true + return { + terminal, + key, + chunk: accentedChar, + expected: { + name: key, + ctrl: false, + meta: true, // Always expect meta:true after conversion + shift: false, + paste: false, + sequence: accentedChar, + }, + }; }), ), )( diff --git a/packages/cli/src/ui/contexts/ToolCallProvider.tsx b/packages/cli/src/ui/contexts/ToolCallProvider.tsx index a0bb160e3b..45e03e350b 100644 --- a/packages/cli/src/ui/contexts/ToolCallProvider.tsx +++ b/packages/cli/src/ui/contexts/ToolCallProvider.tsx @@ -99,17 +99,15 @@ export const ToolCallProvider: React.FC = ({ const contextValue = useMemo( () => ({ getExecutingToolCalls, - subscribe: (callback: () => void) => { - const unsubscribe = ToolCallTrackerService.subscribeToUpdates( + subscribe: (callback: () => void) => + ToolCallTrackerService.subscribeToUpdates( sessionId, () => { updateExecutingToolCalls(); callback(); }, scopedAgentId, - ); - return unsubscribe; - }, + ), }), [getExecutingToolCalls, scopedAgentId, sessionId, updateExecutingToolCalls], ); diff --git a/packages/cli/src/ui/hooks/atCommandProcessor.ts b/packages/cli/src/ui/hooks/atCommandProcessor.ts index 9be29787a6..ea5f3b8795 100644 --- a/packages/cli/src/ui/hooks/atCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/atCommandProcessor.ts @@ -16,9 +16,9 @@ import { isNodeError, unescapePath, debugLogger, + validatePathWithinWorkspace, } from '@vybestack/llxprt-code-core'; import type { DiscoveredMCPResource } from '@vybestack/llxprt-code-core'; -import { validatePathWithinWorkspace } from '@vybestack/llxprt-code-core'; import { HistoryItem, IndividualToolCallDisplay, diff --git a/packages/cli/src/ui/hooks/geminiStream/toolCompletionHandler.ts b/packages/cli/src/ui/hooks/geminiStream/toolCompletionHandler.ts index a7a02dafe5..2992dc2e93 100644 --- a/packages/cli/src/ui/hooks/geminiStream/toolCompletionHandler.ts +++ b/packages/cli/src/ui/hooks/geminiStream/toolCompletionHandler.ts @@ -16,16 +16,18 @@ */ import { useRef, useCallback, useEffect } from 'react'; -import { GeminiClient } from '@vybestack/llxprt-code-core'; -import type { Part } from '@google/genai'; -import { DEFAULT_AGENT_ID, DebugLogger } from '@vybestack/llxprt-code-core'; +import { + GeminiClient, + DEFAULT_AGENT_ID, + DebugLogger, +} from '@vybestack/llxprt-code-core'; +import type { Part, PartListUnion } from '@google/genai'; import { TrackedToolCall, TrackedCompletedToolCall, TrackedCancelledToolCall, } from '../useReactToolScheduler.js'; import { splitPartsByRole } from './streamUtils.js'; -import type { PartListUnion } from '@google/genai'; const geminiStreamLogger = new DebugLogger('llxprt:ui:gemini-stream'); diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts index 27a4748223..1df3b1ec62 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts @@ -167,15 +167,16 @@ export const useSlashCommandProcessor = ( return new GitService(config.getProjectRoot(), config.storage); }, [config]); - const logger = useMemo(() => { - const l = new Logger( - config?.getSessionId() || '', - config?.storage ?? new Storage(process.cwd()), - ); - // The logger's initialize is async, but we can create the instance - // synchronously. Commands that use it will await its initialization. - return l; - }, [config]); + const logger = useMemo( + () => + // The logger's initialize is async, but we can create the instance + // synchronously. Commands that use it will await its initialization. + new Logger( + config?.getSessionId() || '', + config?.storage ?? new Storage(process.cwd()), + ), + [config], + ); /** * Initialize ProfileManager and SubagentManager for command context diff --git a/packages/cli/src/ui/hooks/useExtensionUpdates.ts b/packages/cli/src/ui/hooks/useExtensionUpdates.ts index f366662c39..b41ae47fba 100644 --- a/packages/cli/src/ui/hooks/useExtensionUpdates.ts +++ b/packages/cli/src/ui/hooks/useExtensionUpdates.ts @@ -121,9 +121,8 @@ export const useExtensionUpdates = ( return true; } return scheduledUpdate.names?.includes(extension.name) === true; - } else { - return extension.installMetadata?.autoUpdate === true; } + return extension.installMetadata?.autoUpdate === true; } const pendingUpdates: string[] = []; diff --git a/packages/cli/src/ui/hooks/useGeminiStream.integration.test.tsx b/packages/cli/src/ui/hooks/useGeminiStream.integration.test.tsx index 946733331f..25b55a0591 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.integration.test.tsx +++ b/packages/cli/src/ui/hooks/useGeminiStream.integration.test.tsx @@ -224,10 +224,9 @@ describe('Todo Continuation Integration - useGeminiStream', () => { 'todo-continuation': true, })); - const mockGetGeminiClient = vi.fn().mockImplementation(() => { - const clientInstance = new MockedGeminiClientClass(mockConfig); - return clientInstance; - }); + const mockGetGeminiClient = vi + .fn() + .mockImplementation(() => new MockedGeminiClientClass(mockConfig)); const contentGeneratorConfig = { model: 'test-model', diff --git a/packages/cli/src/ui/hooks/useGeminiStream.subagent.spec.tsx b/packages/cli/src/ui/hooks/useGeminiStream.subagent.spec.tsx index 222410b941..2e46f5a1c3 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.subagent.spec.tsx +++ b/packages/cli/src/ui/hooks/useGeminiStream.subagent.spec.tsx @@ -166,10 +166,9 @@ describe('useGeminiStream subagent isolation', () => { vertexai: false, }; - const mockGetGeminiClient = vi.fn().mockImplementation(() => { - const clientInstance = new MockedGeminiClientClass(mockConfig); - return clientInstance; - }); + const mockGetGeminiClient = vi + .fn() + .mockImplementation(() => new MockedGeminiClientClass(mockConfig)); mockConfig = { apiKey: 'test-api-key', diff --git a/packages/cli/src/ui/hooks/useGeminiStream.test.tsx b/packages/cli/src/ui/hooks/useGeminiStream.test.tsx index ad806975e5..b685e0736c 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.test.tsx +++ b/packages/cli/src/ui/hooks/useGeminiStream.test.tsx @@ -154,12 +154,12 @@ describe('useGeminiStream', () => { mockAddItem = vi.fn(); // Define the mock for getGeminiClient - const _mockGetGeminiClient = vi.fn().mockImplementation(() => { - // MockedGeminiClientClass is defined in the module scope by the previous change. - // It will use the mockStartChat and mockSendMessageStream that are managed within beforeEach. - const clientInstance = new MockedGeminiClientClass(mockConfig); - return clientInstance; - }); + const _mockGetGeminiClient = vi.fn().mockImplementation( + () => + // MockedGeminiClientClass is defined in the module scope by the previous change. + // It will use the mockStartChat and mockSendMessageStream that are managed within beforeEach. + new MockedGeminiClientClass(mockConfig), + ); const contentGeneratorConfig = { model: 'test-model', diff --git a/packages/cli/src/ui/hooks/useGeminiStream.thinking.test.tsx b/packages/cli/src/ui/hooks/useGeminiStream.thinking.test.tsx index 0b885aff45..9303ceef45 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.thinking.test.tsx +++ b/packages/cli/src/ui/hooks/useGeminiStream.thinking.test.tsx @@ -222,10 +222,9 @@ describe('useGeminiStream - ThinkingBlock Integration', () => { mockAddItem = vi.fn(); - const mockGetGeminiClient = vi.fn().mockImplementation(() => { - const clientInstance = new MockedGeminiClientClass(mockConfig); - return clientInstance; - }); + const mockGetGeminiClient = vi + .fn() + .mockImplementation(() => new MockedGeminiClientClass(mockConfig)); const contentGeneratorConfig = { model: 'test-model', diff --git a/packages/cli/src/ui/hooks/useStableCallback.ts b/packages/cli/src/ui/hooks/useStableCallback.ts index cfed89bd34..9e65000494 100644 --- a/packages/cli/src/ui/hooks/useStableCallback.ts +++ b/packages/cli/src/ui/hooks/useStableCallback.ts @@ -38,12 +38,10 @@ export function useStableCallback unknown>( }); // Return a stable callback that always calls the latest version - const stableCallback = useCallback( + return useCallback( (...args: Parameters) => callbackRef.current(...args), [], // Empty deps = stable reference ) as T; - - return stableCallback; } /** diff --git a/packages/cli/src/ui/hooks/useTimer.ts b/packages/cli/src/ui/hooks/useTimer.ts index f07da383d6..3864e8ec5f 100644 --- a/packages/cli/src/ui/hooks/useTimer.ts +++ b/packages/cli/src/ui/hooks/useTimer.ts @@ -46,11 +46,9 @@ export const useTimer = (isActive: boolean, resetKey: unknown) => { timerRef.current = setInterval(() => { setElapsedTime((prev) => prev + 1); }, 1000); - } else { - if (timerRef.current) { - clearInterval(timerRef.current); - timerRef.current = null; - } + } else if (timerRef.current) { + clearInterval(timerRef.current); + timerRef.current = null; } return () => { diff --git a/packages/cli/src/ui/hooks/useTodoContinuation.ts b/packages/cli/src/ui/hooks/useTodoContinuation.ts index bdd76438d6..984b93bc2a 100644 --- a/packages/cli/src/ui/hooks/useTodoContinuation.ts +++ b/packages/cli/src/ui/hooks/useTodoContinuation.ts @@ -221,11 +221,7 @@ export const useTodoContinuation = ( // Find the most relevant active todo const activeTodo = _findMostRelevantActiveTodo(todoContext.todos); - if ( - !activeTodo || - !activeTodo.content || - activeTodo.content.trim() === '' - ) { + if (!activeTodo?.content || activeTodo.content.trim() === '') { return; } diff --git a/packages/cli/src/ui/themes/color-utils.ts b/packages/cli/src/ui/themes/color-utils.ts index cce234ccb1..7e2d07b6d4 100644 --- a/packages/cli/src/ui/themes/color-utils.ts +++ b/packages/cli/src/ui/themes/color-utils.ts @@ -213,9 +213,8 @@ export function resolveColor(colorValue: string): string | undefined { if (lowerColor.startsWith('#')) { if (/^#[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$/.test(colorValue)) { return lowerColor; - } else { - return undefined; } + return undefined; } // 2. Check if it's an Ink supported name (lowercase) else if (INK_SUPPORTED_NAMES.has(lowerColor)) { diff --git a/packages/cli/src/ui/themes/theme-manager.ts b/packages/cli/src/ui/themes/theme-manager.ts index 4d3aca2107..2901257679 100644 --- a/packages/cli/src/ui/themes/theme-manager.ts +++ b/packages/cli/src/ui/themes/theme-manager.ts @@ -209,7 +209,7 @@ class ThemeManager { const allThemes = [...builtInThemes, ...customThemes]; - const sortedThemes = allThemes.sort((a, b) => { + return allThemes.sort((a, b) => { const typeOrder = (type: ThemeType): number => { switch (type) { case 'dark': @@ -231,8 +231,6 @@ class ThemeManager { } return a.name.localeCompare(b.name); }); - - return sortedThemes; } /** diff --git a/packages/cli/src/ui/utils/ConsolePatcher.ts b/packages/cli/src/ui/utils/ConsolePatcher.ts index c3b274238c..14890f4a17 100644 --- a/packages/cli/src/ui/utils/ConsolePatcher.ts +++ b/packages/cli/src/ui/utils/ConsolePatcher.ts @@ -52,14 +52,12 @@ export class ConsolePatcher { if (type !== 'debug' || this.params.debugMode) { this.originalConsoleError(this.formatArgs(args)); } - } else { - if (type !== 'debug' || this.params.debugMode) { - this.params.onNewMessage?.({ - type, - content: this.formatArgs(args), - count: 1, - }); - } + } else if (type !== 'debug' || this.params.debugMode) { + this.params.onNewMessage?.({ + type, + content: this.formatArgs(args), + count: 1, + }); } }; } diff --git a/packages/cli/src/ui/utils/MarkdownDisplay.tsx b/packages/cli/src/ui/utils/MarkdownDisplay.tsx index 2a5645b956..3332bd025d 100644 --- a/packages/cli/src/ui/utils/MarkdownDisplay.tsx +++ b/packages/cli/src/ui/utils/MarkdownDisplay.tsx @@ -261,23 +261,21 @@ const MarkdownDisplayInternal: React.FC = ({ leadingWhitespace={leadingWhitespace} />, ); - } else { - if (line.trim().length === 0 && !inCodeBlock) { - if (!lastLineEmpty) { - contentBlocks.push( - , - ); - lastLineEmpty = true; - } - } else { - addContentBlock( - - - - - , + } else if (line.trim().length === 0 && !inCodeBlock) { + if (!lastLineEmpty) { + contentBlocks.push( + , ); + lastLineEmpty = true; } + } else { + addContentBlock( + + + + + , + ); } }); diff --git a/packages/cli/src/ui/utils/commandUtils.test.ts b/packages/cli/src/ui/utils/commandUtils.test.ts index ba5aeaab35..8d7b783622 100644 --- a/packages/cli/src/ui/utils/commandUtils.test.ts +++ b/packages/cli/src/ui/utils/commandUtils.test.ts @@ -55,7 +55,7 @@ vi.stubGlobal( const makeWritable = (opts?: { isTTY?: boolean; writeReturn?: boolean }) => { const { isTTY = false, writeReturn = true } = opts ?? {}; - const stream = Object.assign(new EventEmitter(), { + return Object.assign(new EventEmitter(), { write: vi.fn().mockReturnValue(writeReturn), end: vi.fn(), destroy: vi.fn(), @@ -68,7 +68,6 @@ const makeWritable = (opts?: { isTTY?: boolean; writeReturn?: boolean }) => { end: Mock; isTTY?: boolean; }; - return stream; }; const resetEnv = () => { diff --git a/packages/cli/src/ui/utils/fuzzyFilter.ts b/packages/cli/src/ui/utils/fuzzyFilter.ts index 818c50fdaa..e2dd7cf150 100644 --- a/packages/cli/src/ui/utils/fuzzyFilter.ts +++ b/packages/cli/src/ui/utils/fuzzyFilter.ts @@ -45,13 +45,12 @@ export function filterCompletions( const results = fzf.find(query); return results.map((result: FzfResultItem