diff --git a/packages/vscode/AGENTS.md b/packages/vscode/AGENTS.md index 838a97e..78d4df9 100644 --- a/packages/vscode/AGENTS.md +++ b/packages/vscode/AGENTS.md @@ -11,7 +11,7 @@ One extension replacing the standalone `rstack.rslint` and `rstack.rstest` exten 1. **Shell activation** — stacks never self-activate; `register()` returns fast and never blocks on starting a server/worker. 2. **Namespace** — everything user-visible is `rstack.*`. Legacy `rslint.*` / `rstest.*` names appear only in the migration mapping. Command IDs were renamed without aliases (breaking old keybindings was an accepted cost). -3. **Resolve-from-project** — no tool binaries or tool packages in the VSIX; everything resolves from the user's project so the editor runs the CLI's exact versions. Version floors surface as a status, never a crash. All cooperating lint pieces (binary, config loader, plugin host) must come from one resolution root. +3. **Resolve-from-project** — no tool binaries or tool packages in the VSIX; everything resolves from the user's project so the editor runs the CLI's exact versions. Version floors surface as a status, never a crash. All cooperating lint pieces (binary, config loader, plugin host) must come from one resolution root. Enforced by lint: `@typescript-eslint/no-restricted-imports` in the root `rstack.config.ts` rejects any non-type import of `@rslint/core`, `@rstest/core`, `rstack` or `jiti` under `src/` — types only at compile time, runtime modules through explicit project paths. 4. **Status aggregation** — stacks own no UI chrome; they report to the shell's single status bar item, which always exists. In CI the test stack's `MasterLogger` also mirrors every entry to stderr (`RSTACK_E2E_MIRROR_LOGS=1`, set by `e2e/rstest/runTest.ts`) — the output channel is unreadable there; rationale in `stacks/test/logger.ts`. 5. **Worker-cwd decoupling** (test) — a project's cwd is explicit, not derived from the config file path; for native configs behavior stays byte-identical to upstream. 6. **Node runtime selection** (lint, test, fmt) — the Node a project-loading child process runs on is a **User Node runtime** chosen by the extension against one uniform floor, never assumed from PATH; the recovery path is the user's own shell, and the dividing line is the **load bound** (terms in CONTEXT.md; the full rule and rationale in `docs/adr/0001-node-runtime-selection.md`). All three callers — the lint worker, the rstest worker and the `rs fmt --lsp` server — take the decision from the one shared module (`shared/nodeResolution.ts`) and share one escape hatch, the resource-scoped `rstack.nodeExecutable` (`shared/nodeExecutableSetting.ts`); each appends its own consequence to the shared preflight message. diff --git a/rstack.config.ts b/rstack.config.ts index cc56300..306e7f6 100644 --- a/rstack.config.ts +++ b/rstack.config.ts @@ -25,6 +25,38 @@ define.lint(async () => { '@typescript-eslint/no-require-imports': 'off', }, }, + { + // Resolve-from-project (packages/vscode/AGENTS.md, adaptation #3): the + // extension ships no tool packages, so extension source may only take + // *types* from them at compile time. Runtime modules come from the + // user's project through explicit paths — a static value import would + // bundle or `require()` the wrong copy. + files: ['packages/vscode/src/**/*.ts'], + rules: { + '@typescript-eslint/no-restricted-imports': [ + 'error', + { + patterns: [ + { + group: [ + '@rslint/core', + '@rslint/core/*', + '@rstest/core', + '@rstest/core/*', + 'rstack', + 'rstack/*', + 'jiti', + 'jiti/*', + ], + allowTypeImports: true, + message: + 'Extension source may only import types from tool packages; load the runtime module from the project (see AGENTS.md, resolve-from-project).', + }, + ], + }, + ], + }, + }, { languageOptions: { parserOptions: {