diff --git a/eslint.config.js b/eslint.config.js index cda07f41d5..caffbdf336 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -72,6 +72,27 @@ export default tseslint.config( 'import/namespace': 'off', // Disabled due to https://github.com/import-js/eslint-plugin-import/issues/2866 }, }, + { + // `utils/` is the layer every other directory imports, so it must not + // import back into one. The daemon direction is clean and enforced here; + // the remaining `ui/`, `config/`, `i18n/` and `nonInteractive/` edges are + // tracked in #9146 and will be added to this group as they are resolved. + files: ['packages/cli/src/utils/**/*.{ts,tsx}'], + rules: { + 'no-restricted-imports': [ + 'error', + { + patterns: [ + { + group: ['**/serve/*', '**/serve/**'], + message: + 'packages/cli/src/utils must not import serve/. Move lifecycle-free logic down into utils/ instead (#9146).', + }, + ], + }, + ], + }, + }, { // General overrides and rules for the project (TS/TSX files) files: [ diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 7a723ec85f..7e2929bce2 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -14,7 +14,7 @@ import { } from 'node:fs'; import { fileURLToPath, pathToFileURL } from 'node:url'; import type { ArgumentsCamelCase, Argv, Options } from 'yargs'; -import { normalizeServeFastPathArgv } from './serve/fast-path-argv.js'; +import { normalizeServeFastPathArgv } from './utils/serve-fast-path-argv.js'; import { initStartupProfiler } from './utils/startupProfiler.js'; import { initCpuProfiler } from './utils/cpuProfiler.js'; import { diff --git a/packages/cli/src/serve/fast-path.ts b/packages/cli/src/serve/fast-path.ts index 85021680cb..f06d73a13c 100644 --- a/packages/cli/src/serve/fast-path.ts +++ b/packages/cli/src/serve/fast-path.ts @@ -10,7 +10,7 @@ import { isValidMemoryBudgetMb, memoryBudgetRangeError, } from '@qwen-code/acp-bridge/daemonMemoryBudget'; -import { normalizeServeFastPathArgv } from './fast-path-argv.js'; +import { normalizeServeFastPathArgv } from '../utils/serve-fast-path-argv.js'; import type { ServeFastPathSettings } from './fast-path-settings.js'; import { RUNTIME_STARTUP_CANCELLED_MESSAGE } from './runtime-startup-errors.js'; import type { ServeOptions } from './types.js'; diff --git a/packages/cli/src/ui/AppContainer.test.tsx b/packages/cli/src/ui/AppContainer.test.tsx index 4db7330262..4c45de623f 100644 --- a/packages/cli/src/ui/AppContainer.test.tsx +++ b/packages/cli/src/ui/AppContainer.test.tsx @@ -21,9 +21,9 @@ vi.mock('./utils/terminal-resize-reflow.js', () => ({ buildWakeRepaint: buildWakeRepaintSpy, })); -vi.mock('../utils/windowTitle.js', async (importOriginal) => { +vi.mock('./utils/windowTitle.js', async (importOriginal) => { const actual = - await importOriginal(); + await importOriginal(); return { ...actual, writeTerminalTitle: ( @@ -62,7 +62,7 @@ import { import { formatSessionWindowTitle, writeTerminalTitle, -} from '../utils/windowTitle.js'; +} from './utils/windowTitle.js'; import ansiEscapes from 'ansi-escapes'; import { type Config, diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index 7e09894bbf..0772117c82 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -158,7 +158,7 @@ import { formatSessionWindowTitle, titleStatusPrefix, writeTerminalTitle, -} from '../utils/windowTitle.js'; +} from './utils/windowTitle.js'; import { clearScreen } from '../utils/stdioHelpers.js'; import { useTextBuffer } from './components/shared/text-buffer.js'; import { useLogger } from './hooks/useLogger.js'; diff --git a/packages/cli/src/ui/startInteractiveUI.tsx b/packages/cli/src/ui/startInteractiveUI.tsx index c3c26022c7..3d9387522d 100644 --- a/packages/cli/src/ui/startInteractiveUI.tsx +++ b/packages/cli/src/ui/startInteractiveUI.tsx @@ -55,7 +55,7 @@ import { startPostRenderPrefetches } from '../startup/startup-prefetch.js'; import { computeWindowTitle, writeTerminalTitle, -} from '../utils/windowTitle.js'; +} from './utils/windowTitle.js'; import { getCliVersion } from '../utils/version.js'; const debugLogger = createDebugLogger('STARTUP'); diff --git a/packages/cli/src/utils/windowTitle.test.ts b/packages/cli/src/ui/utils/windowTitle.test.ts similarity index 99% rename from packages/cli/src/utils/windowTitle.test.ts rename to packages/cli/src/ui/utils/windowTitle.test.ts index 167f3de5da..e0805d6084 100644 --- a/packages/cli/src/utils/windowTitle.test.ts +++ b/packages/cli/src/ui/utils/windowTitle.test.ts @@ -5,7 +5,7 @@ */ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { StreamingState } from '../ui/types.js'; +import { StreamingState } from '../types.js'; import { computeWindowTitle, writeTerminalTitle, diff --git a/packages/cli/src/utils/windowTitle.ts b/packages/cli/src/ui/utils/windowTitle.ts similarity index 96% rename from packages/cli/src/utils/windowTitle.ts rename to packages/cli/src/ui/utils/windowTitle.ts index e2bd79761e..e15a8ac751 100644 --- a/packages/cli/src/utils/windowTitle.ts +++ b/packages/cli/src/ui/utils/windowTitle.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { sanitizeForOsc } from '../ui/utils/osc8.js'; -import { ICON } from '../ui/constants.js'; -import { StreamingState } from '../ui/types.js'; +import { sanitizeForOsc } from './osc8.js'; +import { ICON } from '../constants.js'; +import { StreamingState } from '../types.js'; export const DEFAULT_WINDOW_TITLE = 'qwen'; diff --git a/packages/cli/src/serve/fast-path-argv.ts b/packages/cli/src/utils/serve-fast-path-argv.ts similarity index 100% rename from packages/cli/src/serve/fast-path-argv.ts rename to packages/cli/src/utils/serve-fast-path-argv.ts diff --git a/packages/cli/src/utils/startupProfiler.ts b/packages/cli/src/utils/startupProfiler.ts index f4082b1d2d..c2029e323a 100644 --- a/packages/cli/src/utils/startupProfiler.ts +++ b/packages/cli/src/utils/startupProfiler.ts @@ -31,7 +31,7 @@ import * as path from 'node:path'; import { performance } from 'node:perf_hooks'; import type { StartupEventAttrs } from '@qwen-code/qwen-code-core'; -import { isServeFastPathArgv } from '../serve/fast-path-argv.js'; +import { isServeFastPathArgv } from './serve-fast-path-argv.js'; interface Checkpoint { name: string; diff --git a/packages/cli/src/utils/systemInfo.ts b/packages/cli/src/utils/systemInfo.ts index 1dff4dd9f4..63905adb27 100644 --- a/packages/cli/src/utils/systemInfo.ts +++ b/packages/cli/src/utils/systemInfo.ts @@ -13,9 +13,9 @@ import { IdeClient, AuthType, createDebugLogger, + formatMemoryUsage, type LspStatusSnapshot, } from '@qwen-code/qwen-code-core'; -import { formatMemoryUsage } from '../ui/utils/formatters.js'; import { GIT_COMMIT_INFO } from '../generated/git-commit.js'; const debugLogger = createDebugLogger('STATUS');