diff --git a/.github/instructions/ci-cd-workflows.instructions.md b/.github/instructions/ci-cd-workflows.instructions.md index d89d2063..e2c5a782 100644 --- a/.github/instructions/ci-cd-workflows.instructions.md +++ b/.github/instructions/ci-cd-workflows.instructions.md @@ -68,8 +68,8 @@ npm run docs:build **Matrix Strategy:** - **Operating Systems**: Ubuntu, Windows, macOS -- **Node.js Versions**: 20.x, 22.x, 24.x -- **Total Combinations**: 9 test runs per trigger +- **Node.js Versions**: 22.x, 24.x +- **Total Combinations**: 6 test runs per trigger **Jobs:** @@ -78,7 +78,7 @@ npm run docs:build - Run linter (continue-on-error: true) - Execute `npm run test:platform` - Upload test results and coverage artifacts -- Upload coverage to Codecov (Ubuntu + Node 20.x only) +- Upload coverage to Codecov (Ubuntu + Node 22.x only) **2. platform-verification** - Verify CLI installation - Test `npm link` and `hana-cli --version` diff --git a/.github/instructions/project-overview.instructions.md b/.github/instructions/project-overview.instructions.md index 4f9ff56c..b47ef8dc 100644 --- a/.github/instructions/project-overview.instructions.md +++ b/.github/instructions/project-overview.instructions.md @@ -139,7 +139,7 @@ hana-developer-cli-tool-example/ ## Technology Stack ### Runtime and Module System -- **Node.js**: ≥20.19.0 required +- **Node.js**: ≥22.0.0 required (Node.js 24 LTS highly recommended, following the SAP CAP 10 recommendation) - **Module System**: Pure ESM (`"type": "module"` in package.json) - **Import Pattern**: Always use `import`/`export`, never `require()` @@ -155,7 +155,7 @@ hana-developer-cli-tool-example/ - `@cap-js/sqlite` (2.1.3) via CAP/CDS for testing and local development ### SAP Cloud Application Programming Model (CAP) -- **Core Framework**: `@sap/cds` (9.7.1) +- **Core Framework**: `@sap/cds` (10.x) - **Extensions**: - GraphQL: `@cap-js/graphql` (0.14.0) - OData V2: `@cap-js-community/odata-v2-adapter` (1.15.9) @@ -1130,7 +1130,7 @@ npm run changelog # Generate CHANGELOG.md from CHANGELOG.json **File**: `scripts/postinstall.js` **Purpose:** -- Validates Node.js version (≥20.19.0) +- Validates Node.js version (≥22.0.0) - Checks for required dependencies - Displays installation success message - Shows quick start instructions diff --git a/CLAUDE.md b/CLAUDE.md index 4cd4bc13..7e2b47f4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -**hana-cli** is a developer-centric CLI tool for SAP HANA database development with 183+ commands. It supports four operating modes: CLI, interactive menu, REST API server, and MCP server (for AI assistants). It runs on Node.js ≥20.19.0 and is a pure ESM project (`"type": "module"`). +**hana-cli** is a developer-centric CLI tool for SAP HANA database development with 183+ commands. It supports four operating modes: CLI, interactive menu, REST API server, and MCP server (for AI assistants). It targets SAP CAP 10 and runs on Node.js ≥22.0.0 (Node.js 24 LTS highly recommended, following the CAP 10 recommendation). It is a pure ESM project (`"type": "module"`). ## Commands @@ -97,7 +97,7 @@ Most commands share: `--schema`/`-s` (defaults to `**CURRENT_SCHEMA**`), `--tabl - Config: `tests/.mocharc.json` (16 parallel jobs, 10s timeout) - Mocking: `sinon` + `esmock` for ESM mocking, `mock-fs` for filesystem, `supertest` for HTTP routes - Coverage threshold: 80% lines/functions/branches (enforced in CI via `npm run coverage:check`) -- Cross-platform CI: Windows/macOS/Ubuntu × Node 20/22/24 in `.github/workflows/cross-platform-tests.yml` +- Cross-platform CI: Windows/macOS/Ubuntu × Node 22/24 in `.github/workflows/cross-platform-tests.yml` ## Development Guides diff --git a/README.md b/README.md index 410faad0..8da2201f 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ graph LR Otherwise you can also run it from the sources as described here: -- Install Node.js version 20.19.0 or later on your development machine [https://nodejs.org/en/download/](https://nodejs.org/en/download/) +- Install Node.js on your development machine [https://nodejs.org/en/download/](https://nodejs.org/en/download/). Version 22 or later is required; **Node.js 24 (LTS) is highly recommended**, following the SAP CAP 10 recommendation. - Clone the repository from [https://github.com/SAP-samples/hana-developer-cli-tool-example](https://github.com/SAP-samples/hana-developer-cli-tool-example) diff --git a/agent-instructions/HANA_CLI_QUICKSTART.md b/agent-instructions/HANA_CLI_QUICKSTART.md index a7d6e388..0c016660 100644 --- a/agent-instructions/HANA_CLI_QUICKSTART.md +++ b/agent-instructions/HANA_CLI_QUICKSTART.md @@ -1,6 +1,6 @@ # hana-cli Quick Start Guide -> Generated from hana-cli v4.202603.2 on 2026-03-17 +> Generated from hana-cli v4.202607.0 on 2026-07-13 ## Install diff --git a/agent-instructions/HANA_CLI_REFERENCE.md b/agent-instructions/HANA_CLI_REFERENCE.md index acb9ea10..edfae612 100644 --- a/agent-instructions/HANA_CLI_REFERENCE.md +++ b/agent-instructions/HANA_CLI_REFERENCE.md @@ -1,6 +1,6 @@ # SAP HANA CLI (hana-cli) — Complete Command Reference -> Generated from hana-cli v4.202603.2 on 2026-03-17 +> Generated from hana-cli v4.202607.0 on 2026-07-13 > This file is auto-generated. Do not edit manually. ## What is hana-cli? @@ -13,7 +13,7 @@ npm install -g hana-cli ``` -**Requirements:** Node.js ≥ 20.19.0 +**Requirements:** Node.js ≥ 22.0.0 (Node.js 24 LTS recommended) ### Connection Setup @@ -1020,6 +1020,33 @@ Developer utilities, templates, docs, and interactive helpers --- +#### `mcpServerInstall` + +**Aliases:** `mcp`, `mcpInstall`, `mcp-install` +**Syntax:** `hana-cli mcpServerInstall` + +**Parameters:** + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `--client` (`-c`) | string | `'auto'` | client | +| `--name` (`-n`) | string | `'hana-cli'` | name | +| `--dryRun` (`--dr`) | boolean | `false` | dryRun | +| `--global` (`-g`) | boolean | `false` | global | + +**Related:** `mcpServerStatus`, `helpDocu` + +--- + +#### `mcpServerStatus` + +**Aliases:** `mcp-status`, `mcpStatus` +**Syntax:** `hana-cli mcpServerStatus` + +**Related:** `mcpServerInstall`, `helpDocu` + +--- + #### `readMe` **Aliases:** `readme` @@ -1840,6 +1867,20 @@ Inspect tables, views, procedures, indexes, and related objects **Syntax:** `hana-cli commandMap` +--- + +#### `vscode` + +**Aliases:** `code`, `extension` +**Syntax:** `hana-cli vscode [action]` + +**Parameters:** + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `--insiders` | boolean | `false` | Use code-insiders instead of code | + + --- ### Performance Monitoring @@ -3417,6 +3458,8 @@ Diagnose and investigate system problems | `massRename` (mr, massrename, massRN, massrn) | Mass Operations | Rename objects in bulk | | `massUpdate` (mu, massupdate, massUpd, massupd) | Mass Operations | Update many records at once | | `massUsers` (massUser, mUsers, mUser, mu) | Mass Operations | Mass Operations | +| `mcpServerInstall` (mcp, mcpInstall, mcp-install) | Developer Tools | Developer Tools | +| `mcpServerStatus` (mcp-status, mcpStatus) | Developer Tools | Developer Tools | | `memoryAnalysis` | Performance Monitoring | Analyze memory consumption | | `memoryLeaks` (memleak, ml) | Performance Monitoring | Find potential memory leaks | | `objects` (o, listObjects, listobjects) | Schema Tools | List all database objects | @@ -3465,5 +3508,6 @@ Diagnose and investigate system problems | `version` | System Tools | Check HANA version | | `viewDocs` (docs, doc, documentation) | Developer Tools | Developer Tools | | `views` (v, listViews, listviews) | Schema Tools | List views | +| `vscode` (code, extension) | Other | Other | | `workloadManagement` (wlm, workloads, workloadClass, workloadmgmt) | System Admin | Manage workload assignments | | `xsaServices` (xsa, xsaSvc, xsaservices) | System Admin | Manage XSA services | diff --git a/agent-instructions/README.md b/agent-instructions/README.md index a7b8f3ef..0e576b8d 100644 --- a/agent-instructions/README.md +++ b/agent-instructions/README.md @@ -134,4 +134,4 @@ node scripts/generate-agent-instructions.js --force ## Version -Generated from **hana-cli v4.202603.2** on 2026-03-17. +Generated from **hana-cli v4.202607.0** on 2026-07-13. diff --git a/agent-instructions/categories/developer-tools.md b/agent-instructions/categories/developer-tools.md index 838d0dde..2e559652 100644 --- a/agent-instructions/categories/developer-tools.md +++ b/agent-instructions/categories/developer-tools.md @@ -16,6 +16,8 @@ Developer utilities, templates, docs, and interactive helpers | `interactive` | `i`, `repl`, `shell` | - | | `issue` | `Issue`, `openIssue`, `openissue`, `reportIssue`, `reportissue` | Report issues or get help | | `kb` | - | - | +| `mcpServerInstall` | `mcp`, `mcpInstall`, `mcp-install` | - | +| `mcpServerStatus` | `mcp-status`, `mcpStatus` | - | | `readMe` | `readme` | View help documentation | | `readMeUI` | `readmeui`, `readMeUi`, `readmeUI` | - | | `sdiTasks` | `sditasks`, `sdi`, `smartDataIntegration` | Manage SDI tasks | @@ -178,6 +180,31 @@ hana-cli kb [query...] --- +## `mcpServerInstall` + +**Aliases:** `mcp`, `mcpInstall`, `mcp-install` + +### Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `--client` (`-c`) | string | `'auto'` | client | +| `--name` (`-n`) | string | `'hana-cli'` | name | +| `--dryRun` (`--dr`) | boolean | `false` | dryRun | +| `--global` (`-g`) | boolean | `false` | global | + +**Related:** `mcpServerStatus`, `helpDocu` + +--- + +## `mcpServerStatus` + +**Aliases:** `mcp-status`, `mcpStatus` + +**Related:** `mcpServerInstall`, `helpDocu` + +--- + ## `readMe` **Aliases:** `readme` diff --git a/agent-instructions/categories/other.md b/agent-instructions/categories/other.md index bb4e3278..1db3ba0d 100644 --- a/agent-instructions/categories/other.md +++ b/agent-instructions/categories/other.md @@ -4,9 +4,27 @@ | Command | Aliases | Description | |---------|---------|-------------| | `commandMap` | - | - | +| `vscode` | `code`, `extension` | - | ## `commandMap` +--- + +## `vscode` + +```bash +hana-cli vscode [action] +``` + +**Aliases:** `code`, `extension` + +### Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `--insiders` | boolean | `false` | Use code-insiders instead of code | + + --- diff --git a/agent-instructions/claude/CLAUDE.md b/agent-instructions/claude/CLAUDE.md index 05188592..4ef67f03 100644 --- a/agent-instructions/claude/CLAUDE.md +++ b/agent-instructions/claude/CLAUDE.md @@ -4,8 +4,8 @@ hana-cli (npm: hana-cli, install: `npm install -g hana-cli`) is a command-line tool for SAP HANA database development. It simplifies complex multi-step database operations into single commands. It is a development tool, not a replacement for hdbsql or production admin tools. -**Version:** 4.202603.2 -**Requirements:** Node.js ≥ 20.19.0 +**Version:** 4.202607.0 +**Requirements:** Node.js ≥ 22.0.0 (Node.js 24 LTS recommended) **Module:** ESM (`"type": "module"`) ## When to Use hana-cli diff --git a/agent-instructions/cline/.clinerules b/agent-instructions/cline/.clinerules index 05188592..4ef67f03 100644 --- a/agent-instructions/cline/.clinerules +++ b/agent-instructions/cline/.clinerules @@ -4,8 +4,8 @@ hana-cli (npm: hana-cli, install: `npm install -g hana-cli`) is a command-line tool for SAP HANA database development. It simplifies complex multi-step database operations into single commands. It is a development tool, not a replacement for hdbsql or production admin tools. -**Version:** 4.202603.2 -**Requirements:** Node.js ≥ 20.19.0 +**Version:** 4.202607.0 +**Requirements:** Node.js ≥ 22.0.0 (Node.js 24 LTS recommended) **Module:** ESM (`"type": "module"`) ## When to Use hana-cli diff --git a/agent-instructions/copilot/.github/copilot-instructions.md b/agent-instructions/copilot/.github/copilot-instructions.md index d40de033..2a6a9c70 100644 --- a/agent-instructions/copilot/.github/copilot-instructions.md +++ b/agent-instructions/copilot/.github/copilot-instructions.md @@ -4,8 +4,8 @@ hana-cli (npm: hana-cli, install: `npm install -g hana-cli`) is a command-line tool for SAP HANA database development. It simplifies complex multi-step database operations into single commands. It is a development tool, not a replacement for hdbsql or production admin tools. -**Version:** 4.202603.2 -**Requirements:** Node.js ≥ 20.19.0 +**Version:** 4.202607.0 +**Requirements:** Node.js ≥ 22.0.0 (Node.js 24 LTS recommended) **Module:** ESM (`"type": "module"`) ## When to Use hana-cli diff --git a/agent-instructions/copilot/.github/instructions/hana-cli-usage.instructions.md b/agent-instructions/copilot/.github/instructions/hana-cli-usage.instructions.md index 4c3ef9de..6e00c6de 100644 --- a/agent-instructions/copilot/.github/instructions/hana-cli-usage.instructions.md +++ b/agent-instructions/copilot/.github/instructions/hana-cli-usage.instructions.md @@ -9,8 +9,8 @@ applyTo: "*.hdbcds,*.hdbtable,*.hdbview,*.hdbprocedure,*.hdbfunction,*.cds,mta.y hana-cli (npm: hana-cli, install: `npm install -g hana-cli`) is a command-line tool for SAP HANA database development. It simplifies complex multi-step database operations into single commands. It is a development tool, not a replacement for hdbsql or production admin tools. -**Version:** 4.202603.2 -**Requirements:** Node.js ≥ 20.19.0 +**Version:** 4.202607.0 +**Requirements:** Node.js ≥ 22.0.0 (Node.js 24 LTS recommended) **Module:** ESM (`"type": "module"`) ## When to Use hana-cli diff --git a/agent-instructions/cursor/.cursorrules b/agent-instructions/cursor/.cursorrules index 05188592..4ef67f03 100644 --- a/agent-instructions/cursor/.cursorrules +++ b/agent-instructions/cursor/.cursorrules @@ -4,8 +4,8 @@ hana-cli (npm: hana-cli, install: `npm install -g hana-cli`) is a command-line tool for SAP HANA database development. It simplifies complex multi-step database operations into single commands. It is a development tool, not a replacement for hdbsql or production admin tools. -**Version:** 4.202603.2 -**Requirements:** Node.js ≥ 20.19.0 +**Version:** 4.202607.0 +**Requirements:** Node.js ≥ 22.0.0 (Node.js 24 LTS recommended) **Module:** ESM (`"type": "module"`) ## When to Use hana-cli diff --git a/agent-instructions/generic/AGENT_INSTRUCTIONS.md b/agent-instructions/generic/AGENT_INSTRUCTIONS.md index 0e1da08e..12d67071 100644 --- a/agent-instructions/generic/AGENT_INSTRUCTIONS.md +++ b/agent-instructions/generic/AGENT_INSTRUCTIONS.md @@ -4,8 +4,8 @@ hana-cli (npm: hana-cli, install: `npm install -g hana-cli`) is a command-line tool for SAP HANA database development. It simplifies complex multi-step database operations into single commands. It is a development tool, not a replacement for hdbsql or production admin tools. -**Version:** 4.202603.2 -**Requirements:** Node.js ≥ 20.19.0 +**Version:** 4.202607.0 +**Requirements:** Node.js ≥ 22.0.0 (Node.js 24 LTS recommended) **Module:** ESM (`"type": "module"`) ## When to Use hana-cli diff --git a/agent-instructions/llms.txt b/agent-instructions/llms.txt index ff94e4b7..03457eb6 100644 --- a/agent-instructions/llms.txt +++ b/agent-instructions/llms.txt @@ -3,7 +3,7 @@ > SAP HANA Developer CLI Tool — simplifies database development operations ## Install: npm install -g hana-cli -## Version: 4.202603.2 +## Version: 4.202607.0 ## Docs: https://github.com/SAP-samples/hana-developer-cli-tool-example ## Commands @@ -128,6 +128,8 @@ - massRename (mr, massrename, massRN, massrn): Rename objects in bulk - massUpdate (mu, massupdate, massUpd, massupd): Update many records at once - massUsers (massUser, mUsers, mUser, mu): Mass Operations +- mcpServerInstall (mcp, mcpInstall, mcp-install): Developer Tools +- mcpServerStatus (mcp-status, mcpStatus): Developer Tools - memoryAnalysis: Analyze memory consumption - memoryLeaks (memleak, ml): Find potential memory leaks - objects (o, listObjects, listobjects): List all database objects @@ -176,6 +178,7 @@ - version: Check HANA version - viewDocs (docs, doc, documentation): Developer Tools - views (v, listViews, listviews): List views +- vscode (code, extension): Other - workloadManagement (wlm, workloads, workloadClass, workloadmgmt): Manage workload assignments - xsaServices (xsa, xsaSvc, xsaservices): Manage XSA services diff --git a/agent-instructions/package.json b/agent-instructions/package.json index 56c20ce5..cd209654 100644 --- a/agent-instructions/package.json +++ b/agent-instructions/package.json @@ -49,6 +49,6 @@ "author": "SAP", "license": "Apache-2.0", "engines": { - "node": ">=18.0.0" + "node": ">=22.0.0" } } diff --git a/agent-instructions/windsurf/.windsurfrules b/agent-instructions/windsurf/.windsurfrules index 05188592..4ef67f03 100644 --- a/agent-instructions/windsurf/.windsurfrules +++ b/agent-instructions/windsurf/.windsurfrules @@ -4,8 +4,8 @@ hana-cli (npm: hana-cli, install: `npm install -g hana-cli`) is a command-line tool for SAP HANA database development. It simplifies complex multi-step database operations into single commands. It is a development tool, not a replacement for hdbsql or production admin tools. -**Version:** 4.202603.2 -**Requirements:** Node.js ≥ 20.19.0 +**Version:** 4.202607.0 +**Requirements:** Node.js ≥ 22.0.0 (Node.js 24 LTS recommended) **Module:** ESM (`"type": "module"`) ## When to Use hana-cli diff --git a/docs/01-getting-started/index.md b/docs/01-getting-started/index.md index d7641890..4c3e93c4 100644 --- a/docs/01-getting-started/index.md +++ b/docs/01-getting-started/index.md @@ -34,7 +34,7 @@ npm link **Requirements:** -- [Node.js 20.19.0 or later](https://nodejs.org/) +- [Node.js 22 or later](https://nodejs.org/) — **Node.js 24 (LTS) highly recommended** (follows SAP CAP 10 guidance) - Access to a SAP HANA database instance [Full Installation Guide →](./installation.md) diff --git a/docs/01-getting-started/installation.md b/docs/01-getting-started/installation.md index be8eb883..1dc55e0e 100644 --- a/docs/01-getting-started/installation.md +++ b/docs/01-getting-started/installation.md @@ -13,7 +13,7 @@ See the [Changelog](/99-reference/changelog) for complete details on all changes ## Prerequisites -- **Node.js**: Version 20.19.0 or later +- **Node.js**: Version 22.0.0 or later required; **Node.js 24 (LTS) is highly recommended** (aligns with the SAP CAP 10 recommendation) - Download from [nodejs.org](https://nodejs.org/) - Verify: `node --version` diff --git a/docs/03-features/mcp/server-implementation-complete.md b/docs/03-features/mcp/server-implementation-complete.md index f5527f71..225d9d1e 100644 --- a/docs/03-features/mcp/server-implementation-complete.md +++ b/docs/03-features/mcp/server-implementation-complete.md @@ -183,7 +183,7 @@ npm run build - **MCP Server Version:** 1.202602.0 - **Main CLI Version:** 4.202602.0 - **MCP SDK:** ^1.26.0 -- **Node.js:** ≥20.19.0 +- **Node.js:** ≥22.0.0 (Node.js 24 LTS recommended) - **TypeScript:** ^5.7.3 ## Configuration diff --git a/docs/03-features/mcp/server-updates.md b/docs/03-features/mcp/server-updates.md index 0327f9ee..61f808ef 100644 --- a/docs/03-features/mcp/server-updates.md +++ b/docs/03-features/mcp/server-updates.md @@ -159,7 +159,7 @@ The MCP server now properly exposes and documents all new commands added in rece ## Version Compatibility - **MCP SDK:** ^1.26.0 -- **Node.js:** ≥20.19.0 (matching parent project) +- **Node.js:** ≥22.0.0, Node.js 24 LTS recommended (matching parent project) - **TypeScript:** ^5.7.3 - **CLI Version:** 4.202602.0 and later diff --git a/docs/03-features/vscode-extension.md b/docs/03-features/vscode-extension.md index f10653bd..d85a512f 100644 --- a/docs/03-features/vscode-extension.md +++ b/docs/03-features/vscode-extension.md @@ -157,7 +157,7 @@ code --install-extension hana-cli-0.1.0.vsix ## Requirements - **VS Code** 1.85.0 or later -- **Node.js** 20.19.0+ (for the embedded server) +- **Node.js** 22.0.0+ (Node.js 24 LTS recommended, for the embedded server) - **hana-cli** installed globally or the extension includes its dependencies ### Compatibility diff --git a/docs/99-reference/faq-extended.md b/docs/99-reference/faq-extended.md index c18d78ee..c4717c9b 100644 --- a/docs/99-reference/faq-extended.md +++ b/docs/99-reference/faq-extended.md @@ -15,7 +15,7 @@ Answers for common questions from users and contributors. ### Q: What's the minimum Node.js version required? -**A:** Node.js 20.19.0 or higher. +**A:** Node.js 22.0.0 or higher. **Node.js 24 (LTS) is highly recommended**, in line with the SAP CAP 10 recommendation. ```bash node --version diff --git a/docs/superpowers/plans/2026-07-13-esm-modernization.md b/docs/superpowers/plans/2026-07-13-esm-modernization.md new file mode 100644 index 00000000..e412c28d --- /dev/null +++ b/docs/superpowers/plans/2026-07-13-esm-modernization.md @@ -0,0 +1,695 @@ +# ESM Modernization Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace CommonJS-interop legacy patterns (`createRequire` static requires, `fileURLToPath` `__dirname`/`__filename` shims) with Node 22+ built-ins, and delete the two esbuild workarounds added during the extension-activation fix. + +**Architecture:** Purely mechanical, per-file substitutions grouped by category and directory. Each task ends with `npm test` + `npm run lint` green and a commit. The extension bundle is the critical verification: it must load from a `node_modules`-free directory with `activate` exported. + +**Tech Stack:** Node.js ≥22 (ESM), esbuild, Mocha, ESLint flat config, TypeScript (mcp-server). + +## Global Constraints + +- Node baseline: `>=22.0.0` (per `package.json` engines). `import.meta.dirname`/`import.meta.filename` (Node 20.11+) and JSON import attributes `with { type: 'json' }` (stable Node 22) are both safe. +- Pure ESM project (`"type": "module"`). +- Do NOT modify the exported `base.require()` / `baseLite.require()` or their backing `createRequire`/`standardRequire` — they are intentionally dynamic (path-based JSON, `uuid`/`express`, `@sap/cds-dk` global-npm fallback). +- Do NOT hand-edit `mcp-server/build/` — regenerate from `src/`. +- Do NOT touch the sqlite/tar/generic-pool/@cap-js/@sap-cloud-sdk esbuild externals. +- All user-facing strings use `base.bundle.getText(...)` — no new hardcoded strings introduced. +- Work on branch `esm-modernization` (already created). Commit per task. + +--- + +### Task 1: Baseline verification + +**Files:** none (verification only) + +- [ ] **Step 1: Confirm branch and clean tree** + +Run: `git status && git branch --show-current` +Expected: on `esm-modernization`, only the committed spec present. + +- [ ] **Step 2: Establish green baseline** + +Run: `npm test` +Expected: PASS (record the passing count for comparison). + +- [ ] **Step 3: Establish lint baseline** + +Run: `npm run lint` +Expected: PASS (0 errors). + +--- + +### Task 2: Convert `@sap/textbundle` to static import in base-lite.js + +**Files:** +- Modify: `utils/base-lite.js` (lines ~8, ~86) + +**Interfaces:** +- Produces: unchanged public exports (`bundle`, `require`, etc.). `TextBundle` is now imported statically. + +- [ ] **Step 1: Add the static import and replace the require** + +In `utils/base-lite.js`, replace line 86: +```js +const TextBundle = require('@sap/textbundle').TextBundle +``` +with a top-of-file static import. Add near the other imports (after line 5's `import`s): +```js +import { TextBundle } from '@sap/textbundle' +``` +and delete the old line 86 entirely. + +Note: `export const require = createRequire(import.meta.url)` (line ~10) and its `createRequire` import STAY — they remain the public dynamic-require API. + +- [ ] **Step 2: Run tests** + +Run: `npm run test:utils` +Expected: PASS (base-lite is exercised by utility tests). + +- [ ] **Step 3: Lint** + +Run: `npm run lint` +Expected: PASS. + +- [ ] **Step 4: Commit** + +```bash +git add utils/base-lite.js +git commit -m "refactor: static import @sap/textbundle in base-lite" +``` + +--- + +### Task 3: Convert `@sap/textbundle` to static import in base.js + +**Files:** +- Modify: `utils/base.js` (line ~125) + +**Interfaces:** +- Produces: unchanged exports. `standardRequire` and exported `require()` retained. + +- [ ] **Step 1: Replace the require with a static import** + +In `utils/base.js`, replace line ~125: +```js +const TextBundle = require('@sap/textbundle').TextBundle +``` +with a static import added alongside the other top imports: +```js +import { TextBundle } from '@sap/textbundle' +``` +and delete the old line ~125. + +Keep `import { createRequire } from 'module'` and `const standardRequire = createRequire(import.meta.url)` — they back the exported `require()`. + +- [ ] **Step 2: Run tests** + +Run: `npm run test:utils` +Expected: PASS. + +- [ ] **Step 3: Lint** + +Run: `npm run lint` +Expected: PASS. + +- [ ] **Step 4: Commit** + +```bash +git add utils/base.js +git commit -m "refactor: static import @sap/textbundle in base" +``` + +--- + +### Task 4: Convert terminal-kit to lazy Proxy in base.js + +**Files:** +- Modify: `utils/base.js` (remove line ~300 `import terminalKit`, rework lines ~282–340) + +**Interfaces:** +- Produces: `export const terminal` — a synchronous object exposing `.table(...)` and `.progressBar(...)`. Consumed synchronously by `utils/massConvert.js`, `utils/massExport.js`, `utils/massDelete.js`, `utils/massGrant.js` (all inside `async` functions) and internally by `base.js` table rendering. + +- [ ] **Step 1: Write a failing test for lazy terminal access** + +Create `tests/utils/terminalLazy.Test.js`: +```js +// @ts-check +import { assert } from '../../utils/base.js' +import * as base from '../../utils/base.js' + +describe('terminal lazy access', () => { + it('exposes a synchronous terminal object with table and progressBar', () => { + assert.strictEqual(typeof base.terminal, 'object') + assert.strictEqual(typeof base.terminal.table, 'function') + assert.strictEqual(typeof base.terminal.progressBar, 'function') + }) + + it('progressBar returns a controller with startItem/itemDone/stop', () => { + const bar = base.terminal.progressBar({ title: 'test', percent: false }) + assert.strictEqual(typeof bar.startItem, 'function') + assert.strictEqual(typeof bar.itemDone, 'function') + assert.strictEqual(typeof bar.stop, 'function') + bar.stop() + }) +}) +``` + +- [ ] **Step 2: Run it to confirm current behavior** + +Run: `npx mocha tests/utils/terminalLazy.Test.js` +Expected: PASS today (eager import already provides this) — this test locks in the contract we must preserve after refactor. + +- [ ] **Step 3: Remove the eager import and implement the lazy Proxy** + +In `utils/base.js`: +1. Delete line ~300: `import terminalKit from 'terminal-kit'`. +2. Replace the terminal init block (lines ~302–340, the `let _terminal` / try-catch / `export const terminal`) with a lazy Proxy. Keep the existing `getTerminalKit()` lazy loader (lines ~282–290) as the underlying loader. New block: + +```js +// Console-based fallback used in test mode and before/if terminal-kit loads. +const consoleTerminalStub = { + table: (data) => { + if (data && Array.isArray(data) && data.length > 0) { + console.table(data) + } + }, + progressBar: () => ({ + startItem: () => {}, + itemDone: () => {}, + stop: () => {} + }) +} + +// Cached real terminal-kit terminal (loaded lazily on first use). +let _realTerminal = null +let _terminalKitLoadFailed = false + +function loadRealTerminalSync() { + if (_realTerminal || _terminalKitLoadFailed) return _realTerminal + if (process.env.NODE_ENV === 'test') return null + try { + // standardRequire is CJS-synchronous and works in the CLI (has + // node_modules). In the bundled extension terminal-kit is never + // reached because the extension does not render terminal output; + // the Proxy falls back to the console stub. + _realTerminal = standardRequire('terminal-kit').terminal + } catch (error) { + _terminalKitLoadFailed = true + console.warn(bundle.getText("warning.terminalKitInitFail", [error.message])) + } + return _realTerminal +} + +// Synchronous facade: forwards to real terminal-kit when available, +// otherwise to the console stub. No eager terminal-kit load at module init. +export const terminal = new Proxy({}, { + get(_target, prop) { + const real = loadRealTerminalSync() + const impl = real || consoleTerminalStub + const value = impl[prop] + return typeof value === 'function' ? value.bind(impl) : value + } +}) +``` + +Note: this uses `standardRequire('terminal-kit')` (the existing CJS require) so `base.terminal` stays synchronous with zero call-site changes. In the extension bundle, `standardRequire` is `createRequire`-based and terminal-kit is simply never accessed (no terminal rendering), so it never loads. This keeps the source free of a static/top-level terminal-kit import — which is what lets esbuild avoid pulling it into the load-time graph. + +- [ ] **Step 4: Run the lazy test + full utils suite** + +Run: `npx mocha tests/utils/terminalLazy.Test.js && npm run test:utils` +Expected: PASS. + +- [ ] **Step 5: Lint** + +Run: `npm run lint` +Expected: PASS (no unused `terminalKit` binding remains). + +- [ ] **Step 6: Commit** + +```bash +git add utils/base.js tests/utils/terminalLazy.Test.js +git commit -m "refactor: lazy terminal-kit access via Proxy, drop eager import" +``` + +--- + +### Task 5: Modernize versionCheck.js (require → import + JSON attribute) + +**Files:** +- Modify: `utils/versionCheck.js` + +**Interfaces:** +- Consumes: `check-node-version` default export; `../package.json` via import attribute. +- Produces: unchanged `checkVersion()` export. + +- [ ] **Step 1: Rewrite the imports and JSON load** + +Replace the top of `utils/versionCheck.js`: +```js +// @ts-check +import * as base from './base.js' +import { createRequire } from 'module' +const require = createRequire(import.meta.url) +const check = require('check-node-version') +import chalk from 'chalk' +``` +with: +```js +// @ts-check +import * as base from './base.js' +import check from 'check-node-version' +import chalk from 'chalk' +import pkg from '../package.json' with { type: 'json' } +``` + +Then replace the JSON read inside `checkVersion()`: +```js +const version = require("../package.json").engines.node +``` +with: +```js +const version = pkg.engines.node +``` + +- [ ] **Step 2: Run tests** + +Run: `npm run test:utils` +Expected: PASS. + +- [ ] **Step 3: Smoke-check the version gate** + +Run: `node bin/cli.js version` +Expected: prints version info without a MODULE_NOT_FOUND or import-attribute error. + +- [ ] **Step 4: Lint** + +Run: `npm run lint` +Expected: PASS. + +- [ ] **Step 5: Commit** + +```bash +git add utils/versionCheck.js +git commit -m "refactor: static import check-node-version + JSON import attribute" +``` + +--- + +### Task 6: `__dirname`/`__filename` sweep — utils/ + +**Files:** +- Modify: `utils/base.js:8`, `utils/base-lite.js:8`, `utils/config-loader.js:11` + +**Transformation rules (apply per site):** +- Variant A: `const __dirname = fileURLToPath(new URL('.', import.meta.url))` → `const __dirname = import.meta.dirname` +- After replacing, remove now-unused imports (`import { fileURLToPath } from 'url'`, `import { URL } from 'url'`) **only if** `fileURLToPath`/`URL` are not used elsewhere in that file. + +- [ ] **Step 1: base.js** + +In `utils/base.js`, replace line 8: +```js +const __dirname = fileURLToPath(new URL('.', import.meta.url)) +``` +with: +```js +const __dirname = import.meta.dirname +``` +Then remove lines 6–7 (`import { fileURLToPath } from 'url'` and `import { URL } from 'url'`) — verify with `grep -n "fileURLToPath\|\bURL\b" utils/base.js` that neither is used elsewhere first; if `URL` IS used elsewhere, keep only that import. + +- [ ] **Step 2: base-lite.js** + +In `utils/base-lite.js`, replace line 8 the same way, then remove the now-unused `fileURLToPath`/`URL` imports (verify usage first with grep). + +- [ ] **Step 3: config-loader.js** + +In `utils/config-loader.js`, replace line 11 the same way; remove unused `fileURLToPath`/`URL` imports (verify first). + +- [ ] **Step 4: Run tests + lint** + +Run: `npm run test:utils && npm run lint` +Expected: PASS (lint catches any leftover unused import). + +- [ ] **Step 5: Commit** + +```bash +git add utils/base.js utils/base-lite.js utils/config-loader.js +git commit -m "refactor: import.meta.dirname in utils/" +``` + +--- + +### Task 7: `__dirname`/`__filename` sweep — routes/ + +**Files:** +- Modify: `routes/docs.js:9`, `routes/static.js:9`, `routes/swagger.js:9-10` + +**Transformation rules:** +- Variant A (docs.js, static.js): `const __dirname = fileURLToPath(new URL('.', import.meta.url))` → `const __dirname = import.meta.dirname` +- Variant B (swagger.js): `const __filename = fileURLToPath(import.meta.url)` + `const __dirname = dirname(__filename)` → `const __dirname = import.meta.dirname` (drop the `__filename` line; keep `join` import, drop `dirname`/`fileURLToPath` if unused). + +- [ ] **Step 1: docs.js and static.js** + +Replace line 9 in each with `const __dirname = import.meta.dirname`; remove unused `fileURLToPath`/`URL` imports (verify with grep per file). + +- [ ] **Step 2: swagger.js** + +In `routes/swagger.js`, replace lines 9–10: +```js +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) +``` +with: +```js +const __dirname = import.meta.dirname +``` +Then update imports: `import { dirname, join } from 'path'` → `import { join } from 'path'`, and remove `import { fileURLToPath } from 'url'` (verify `fileURLToPath`/`dirname` unused elsewhere first). `__dirname` is still used at line ~13 (`readFileSync(join(__dirname, '../package.json'))`) — keep `join`. + +- [ ] **Step 3: Run tests + lint** + +Run: `npm run test:routes && npm run lint` +Expected: PASS. + +- [ ] **Step 4: Commit** + +```bash +git add routes/docs.js routes/static.js routes/swagger.js +git commit -m "refactor: import.meta.dirname in routes/" +``` + +--- + +### Task 8: `__dirname`/`__filename` sweep — bin/ + +**Files:** +- Modify: `bin/changeLog.js:7`, `bin/examples.js:8`, `bin/interactive.js:10`, `bin/kb.js:8`, `bin/readMe.js:20`, `bin/version.js:73`, `bin/viewDocs.js:10`, `bin/mcpServerInstall.js:9-10`, `bin/mcpServerStatus.js:9-10`, `bin/vscode.js:9-10` + +**Transformation rules:** +- Variant A: `const __dirname = fileURLToPath(new URL('.', import.meta.url))` → `const __dirname = import.meta.dirname` +- Variant C: `const __dirname = dirname(fileURLToPath(import.meta.url))` → `const __dirname = import.meta.dirname` +- Variant B: `const __filename = fileURLToPath(import.meta.url)` + `const __dirname = dirname(__filename)` → `const __dirname = import.meta.dirname` +- Per file: after replacing, drop `fileURLToPath` and (if now unused) `dirname` from imports; KEEP `join`/`resolve` where still used. Verify each with grep before removing. + +- [ ] **Step 1: Variant A files (changeLog.js:7, readMe.js:20, version.js:73)** + +Replace each site with `const __dirname = import.meta.dirname`. Note `readMe.js` and `version.js` have the shim inside a function body (indented) — preserve indentation. Remove unused `fileURLToPath`/`URL` imports (verify per file). + +- [ ] **Step 2: Variant C files (examples.js:8, interactive.js:10, kb.js:8, viewDocs.js:10)** + +Replace each `const __dirname = dirname(fileURLToPath(import.meta.url))` with `const __dirname = import.meta.dirname`. Update the `path` import to drop `dirname` if unused (e.g. `import { dirname, join } from 'path'` → `import { join } from 'path'`); remove `fileURLToPath` import if unused. Verify per file with `grep -n "dirname\|fileURLToPath" bin/.js`. + +- [ ] **Step 3: Variant B files (mcpServerInstall.js:9-10, mcpServerStatus.js:9-10, vscode.js:9-10)** + +In each, replace: +```js +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) +``` +with: +```js +const __dirname = import.meta.dirname +``` +Update imports: drop `fileURLToPath` from `'url'` import; drop `dirname` from the `'path'` import but keep `join`/`resolve` (both used in these files). Verify `__filename` is not referenced elsewhere (grep confirmed it is only used to derive `__dirname` in these three). + +- [ ] **Step 4: Run tests + lint** + +Run: `npm run test:cli && npm run lint` +Expected: PASS. + +- [ ] **Step 5: Commit** + +```bash +git add bin/changeLog.js bin/examples.js bin/interactive.js bin/kb.js bin/readMe.js bin/version.js bin/viewDocs.js bin/mcpServerInstall.js bin/mcpServerStatus.js bin/vscode.js +git commit -m "refactor: import.meta.dirname in bin/" +``` + +--- + +### Task 9: `__dirname`/`__filename` sweep — scripts/ and tests/ + +**Files:** +- Modify (scripts, Variant A/C/D): `scripts/add-epilogue-final.js:12`, `scripts/enhance-command-docs.js:11`, `scripts/generate-command-docs.js:11`, `scripts/generate-sidebar-config.js:10`, `scripts/generate-sidebar-config-fixed.js:11`, `scripts/populate-command-docs.js:11`, `scripts/test-context-implementation.js:11`, `scripts/update-epilogues.js:12`, `scripts/validate-i18n.js:24` (pattern `path.dirname(fileURLToPath(import.meta.url))`) +- Modify (scripts, `__filename`): `scripts/build-docs-index.js:18`, `scripts/extract-release-notes.js:7`, `scripts/generate-agent-instructions.js:27`, `scripts/install-agent-instructions.js:21`, `scripts/postinstall.js:8-9`, `scripts/prepare-release.js:8-9`, `scripts/run-test-output.js:8` +- Modify (tests): `tests/appFactory.js:13`, `tests/platformSupport.Test.js:8`, `tests/routes/docs.Test.js:13`, `tests/utils/profileIntegration.Test.js:15` + +**Transformation rules:** +- `const __dirname = path.dirname(fileURLToPath(import.meta.url))` → `const __dirname = import.meta.dirname` (keep `import path from 'path'` if `path` used elsewhere; drop `fileURLToPath` if unused). +- `const __dirname = fileURLToPath(new URL('.', import.meta.url))` → `const __dirname = import.meta.dirname`. +- `const __filename = fileURLToPath(import.meta.url)` standalone → `const __filename = import.meta.filename` (only if `__filename` is actually used; if it derives `__dirname` on the next line, collapse to `const __dirname = import.meta.dirname`). +- IMPORTANT exceptions in `tests/platformSupport.Test.js`: line 8 is Variant A (`__dirname`) — convert. Line 238 `const currentFile = fileURLToPath(import.meta.url)` is a **separate** use assigned to `currentFile` — convert to `const currentFile = import.meta.filename`. Do NOT remove the `fileURLToPath` import unless BOTH are gone. + +- [ ] **Step 1: scripts/ `path.dirname(...)` variant** + +For each of the 9 listed scripts, replace the shim line with `const __dirname = import.meta.dirname`. Keep `import path from 'path'` where `path.*` is used elsewhere; remove `fileURLToPath` import if unused (verify per file with `grep -n "fileURLToPath\|path\." scripts/.js`). + +- [ ] **Step 2: scripts/ `__filename` variant** + +For `build-docs-index.js`, `extract-release-notes.js`, `generate-agent-instructions.js`, `install-agent-instructions.js`, `run-test-output.js`: if `__filename` derives `__dirname`, collapse to `const __dirname = import.meta.dirname`; if `__filename` is used standalone (e.g. `import.meta.url === ...` main-guard), use `const __filename = import.meta.filename`. For `postinstall.js:8-9` and `prepare-release.js:8-9` (Variant B), collapse both lines to `const __dirname = import.meta.dirname`, keep `join`, drop `dirname`/`fileURLToPath` if unused. Verify each with grep. + +- [ ] **Step 3: tests/ files** + +`tests/appFactory.js:13`, `tests/routes/docs.Test.js:13`: Variant A → `import.meta.dirname`. `tests/utils/profileIntegration.Test.js:15`: `__filename` → `import.meta.filename` (or collapse if it derives `__dirname`). `tests/platformSupport.Test.js`: line 8 → `import.meta.dirname`, line 238 `currentFile` → `import.meta.filename`; remove `fileURLToPath` import only if both uses are gone. + +- [ ] **Step 4: Run full test suite + lint** + +Run: `npm test && npm run lint` +Expected: PASS (scripts aren't unit-tested but must not break import resolution; lint validates them). + +- [ ] **Step 5: Sanity-run one script** + +Run: `npm run validate:i18n` +Expected: runs (validates i18n) without a `__dirname`/import error. + +- [ ] **Step 6: Commit** + +```bash +git add scripts/ tests/ +git commit -m "refactor: import.meta.dirname/filename in scripts/ and tests/" +``` + +--- + +### Task 10: Modernize mcp-server/src/*.ts and regenerate build/ + +**Files:** +- Modify: `mcp-server/src/command-metadata.ts:6,10`, `mcp-server/src/docs-search.ts:12-13`, `mcp-server/src/executor.ts:9-10`, `mcp-server/src/index.ts:38-39`, `mcp-server/src/readme-knowledge-base.ts:12-13`, `mcp-server/src/resources.ts:15-16` +- Regenerate: `mcp-server/build/*` (via build script) + +**Interfaces:** +- `command-metadata.ts`: the `require = createRequire(import.meta.url)` — inspect what it loads. If it loads a static local module/JSON, convert to static `import`; if dynamic, retain (same rule as base.js). Verify before changing. + +- [ ] **Step 1: Inspect command-metadata.ts require usage** + +Run: `grep -n "require(" mcp-server/src/command-metadata.ts` +Decision: if it requires a static path (e.g. `../commandMetadata.js` or a JSON), convert to `import` / import-attribute. If it requires a dynamic/computed path, KEEP `createRequire` (document why in a comment). Apply accordingly. + +- [ ] **Step 2: Convert `__filename`/`__dirname` shims in the 5 TS files** + +In `docs-search.ts`, `executor.ts`, `index.ts`, `readme-knowledge-base.ts`, `resources.ts`, replace: +```ts +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +``` +with: +```ts +const __dirname = import.meta.dirname; +``` +Update imports: drop `fileURLToPath` from `'node:url'`/`'url'`; drop `dirname` from `'node:path'`/`'path'` if unused, keep `join`/`readFileSync` etc. Verify each with grep. `__dirname` remains used by `join(__dirname, ...)` calls in all five. + +- [ ] **Step 3: Find and run the mcp-server build** + +Run: `cat mcp-server/package.json | grep -A20 '"scripts"'` to find the build command (e.g. `npm run build` in mcp-server, or a root `types`/build task). Then run it to regenerate `mcp-server/build/`. +Expected: TypeScript compiles with no errors; `build/` regenerated. + +- [ ] **Step 4: Verify build output modernized** + +Run: `grep -rn "createRequire\|fileURLToPath(import.meta.url)" mcp-server/build/ | grep -v "retained"` +Expected: only intentionally-retained dynamic `createRequire` (if any from Step 1) remains. + +- [ ] **Step 5: MCP smoke test** + +Run: `node mcp-server/build/index.js --help 2>&1 | head` (or the project's MCP smoke command) +Expected: starts / prints usage without a module-resolution error. + +- [ ] **Step 6: Commit** + +```bash +git add mcp-server/src/ mcp-server/build/ +git commit -m "refactor: import.meta.dirname in mcp-server, regenerate build" +``` + +--- + +### Task 11: Delete esbuild workarounds + +**Files:** +- Modify: `vscode-extension/esbuild.config.mjs` (remove `bundleTextBundlePlugin`, `stubTerminalKitPlugin`, the `'terminal-kit'` external entry, and the two plugin references in the `plugins` array) + +- [ ] **Step 1: Remove the two plugin definitions** + +In `vscode-extension/esbuild.config.mjs`, delete the entire `bundleTextBundlePlugin` block and the entire `stubTerminalKitPlugin` block (including their leading comment blocks). + +- [ ] **Step 2: Remove terminal-kit from externals** + +In the `external: [...]` array, remove the `'terminal-kit'` entry (and its comment if present). Leave `sqlite3`, `better-sqlite3`, `generic-pool`, `@cap-js/cds-test`, `@sap-cloud-sdk/*`, `tar` untouched. + +- [ ] **Step 3: Remove the plugin references** + +Change: +```js +plugins: [resolveRoutesPlugin, stripCLIBootstrapPlugin, dynamicImportPlugin, bundleTextBundlePlugin, stubTerminalKitPlugin], +``` +to: +```js +plugins: [resolveRoutesPlugin, stripCLIBootstrapPlugin, dynamicImportPlugin], +``` + +- [ ] **Step 4: Rebuild the bundle** + +Run: `cd vscode-extension && npm run bundle 2>&1 | tail -20` +Expected: build succeeds. Watch for a `terminal-kit` parse error (the original reason it was external). **If it errors on terminal-kit's README/parse:** re-add `'terminal-kit'` to `external` ONLY (keep both plugins deleted) and rebuild — the lazy Proxy's console fallback keeps the extension working. Document this outcome in the commit message. + +- [ ] **Step 5: Verify no textbundle/terminal-kit runtime require leaked in** + +Run (from `vscode-extension/`): `grep -oE '[A-Za-z_$][A-Za-z0-9_$]*\("@sap/textbundle"\)' dist/extension.js | sort -u` +Expected: empty (bundled, not a runtime require). + +- [ ] **Step 6: Commit** + +```bash +git add vscode-extension/esbuild.config.mjs vscode-extension/dist/ +git commit -m "build: remove esbuild textbundle/terminal-kit workarounds" +``` + +--- + +### Task 12: End-to-end extension verification (critical) + +**Files:** none (verification + repackage) + +- [ ] **Step 1: In-place load probe from a clean dir** + +Run: +```bash +cd vscode-extension +TMP=$(mktemp -d) +cp dist/extension.js "$TMP/extension.js" +mkdir -p "$TMP/_i18n" && cp ../_i18n/messages.properties "$TMP/_i18n/" 2>/dev/null || true +cat > "$TMP/probe.cjs" <<'EOF' +const Module = require('module') +const stub = new Proxy({}, { get: () => new Proxy(function(){}, { get: ()=>()=>{}, apply: ()=>({ appendLine(){}, show(){}, dispose(){}, createOutputChannel(){return{appendLine(){},show(){},dispose(){}}} }) }) }) +const o = Module._load +Module._load = function(r){ if(r==='vscode') return stub; return o.apply(this, arguments) } +const ext = require('./extension.js') +console.error('activate exported?', typeof ext.activate === 'function') +EOF +cd "$TMP" && node probe.cjs; echo "EXIT: $?"; rm -rf "$TMP" +``` +Expected: `activate exported? true`, EXIT 0. (The `_i18n` copy mirrors the .vsix layout; if the probe still logs an i18n ENOENT it is a probe-fixture issue, not a load failure — the require chain completing is what matters.) + +- [ ] **Step 2: Repackage the .vsix** + +Run: `cd vscode-extension && npx vsce package 2>&1 | tail -3` +Expected: `Packaged: ...hana-cli-0.1.0.vsix`. + +- [ ] **Step 3: Reinstall** + +Run: `node bin/cli.js vscode uninstall; node bin/cli.js vscode install` +Expected: uninstalled then installed successfully. + +- [ ] **Step 4: Manual reload confirmation** + +Reload the VS Code window (Command Palette → Developer: Reload Window). Confirm: +- Developer: Show Running Extensions → hana-cli **Activated** (not "Activating…"). +- View → Output → "hana-cli" channel exists with "activated successfully". +- A `hana-cli.*` command (e.g. HANA: Open hana-cli Tools) runs. + +- [ ] **Step 5: Commit any repackage artifacts** + +```bash +git add vscode-extension/hana-cli-0.1.0.vsix +git commit -m "build: repackage extension .vsix after ESM modernization" +``` + +--- + +### Task 13: Update .github/instructions docs + +**Files:** +- Modify: `.github/instructions/automation-script-development.instructions.md`, `.github/instructions/scripts-directory-development.instructions.md` (and any other instruction files whose examples show `fileURLToPath(import.meta.url)` / `createRequire`) + +- [ ] **Step 1: Find all instruction examples using the legacy patterns** + +Run: `grep -rn "fileURLToPath(import.meta.url)\|fileURLToPath(new URL\|createRequire" .github/instructions/` +Expected: a list of doc code-block sites. + +- [ ] **Step 2: Update each example to the modern pattern** + +Replace `const __filename = fileURLToPath(import.meta.url)` / `const __dirname = path.dirname(fileURLToPath(import.meta.url))` with `const __dirname = import.meta.dirname` (and `import.meta.filename` where a filename is shown). Add a one-line note where helpful: `// Node 20.11+: import.meta.dirname / import.meta.filename`. For any `createRequire` example that demonstrates loading a static package, show a static `import` instead; keep `createRequire` only where the example is specifically about dynamic resolution. + +- [ ] **Step 3: Verify no stale legacy examples remain** + +Run: `grep -rn "fileURLToPath" .github/instructions/` +Expected: empty (or only intentional references discussing the old pattern historically). + +- [ ] **Step 4: Commit** + +```bash +git add .github/instructions/ +git commit -m "docs: modernize import.meta.dirname examples in instructions" +``` + +--- + +### Task 14: Final full verification + +**Files:** none + +- [ ] **Step 1: Full test suite** + +Run: `npm test` +Expected: PASS, count ≥ Task 1 baseline. + +- [ ] **Step 2: Lint** + +Run: `npm run lint` +Expected: PASS, 0 errors. + +- [ ] **Step 3: Confirm no unintended createRequire/shims remain in scoped code** + +Run: +```bash +grep -rn "fileURLToPath" bin/ routes/ utils/ scripts/ tests/ | grep -v node_modules +grep -rn "createRequire" utils/ | grep -vE "base\.js|base-lite\.js" +``` +Expected: first returns only intentionally-kept `fileURLToPath` (if any documented); second returns nothing (only base.js/base-lite.js retain `createRequire` for the exported dynamic `require()`). + +- [ ] **Step 4: CLI smoke across the touched surfaces** + +Run: +```bash +node bin/cli.js version +node bin/cli.js vscode status +``` +Expected: both run without module/import errors. + +- [ ] **Step 5: Update stale Node baseline in CLAUDE.md (housekeeping)** + +In project `CLAUDE.md`, the overview says "Node.js ≥20.19.0" — update to "Node.js ≥22" to match `package.json` engines. + +- [ ] **Step 6: Commit** + +```bash +git add CLAUDE.md +git commit -m "docs: correct Node baseline to >=22 in CLAUDE.md" +``` + +--- + +## Self-Review Notes + +- **Spec coverage:** Category 1 → Tasks 2,3,5. Category 2 (terminal-kit) → Task 4. Category 3 (shims) → Tasks 6–9. MCP → Task 10. esbuild cleanup → Task 11. Extension verify → Task 12. Docs → Task 13. Final → Task 14. All spec sections covered. +- **Retained-by-design:** exported `base.require()`/`baseLite.require()` and their `createRequire`/`standardRequire` are explicitly kept (Tasks 3, 6 notes; Task 14 Step 3 asserts this). +- **terminal-kit contingency:** Task 11 Step 4 documents the fallback (re-add to `external`) if the README-parse issue resurfaces. +- **Type/name consistency:** `terminal` export contract locked by the Task 4 test; `import.meta.dirname`/`import.meta.filename` names used consistently. diff --git a/docs/superpowers/specs/2026-07-13-esm-modernization-design.md b/docs/superpowers/specs/2026-07-13-esm-modernization-design.md new file mode 100644 index 00000000..6f53e42e --- /dev/null +++ b/docs/superpowers/specs/2026-07-13-esm-modernization-design.md @@ -0,0 +1,142 @@ +# ESM Modernization: Retire CommonJS-Interop Shims — Design + +**Date:** 2026-07-13 +**Status:** Approved (design) +**Baseline:** Node ≥22 (per `package.json` engines; Node 24 recommended, aligned with CAP 10 rules) + +## Background + +While fixing a VS Code extension activation hang, we found the root cause was +`createRequire(import.meta.url)`-based requires of `@sap/textbundle` in +`utils/base.js` and `utils/base-lite.js`. esbuild does **not** trace +`createRequire()` requires into a bundle — it emits them as runtime +`require("@sap/textbundle")` calls resolved against the bundle's own location. +The installed `.vsix` ships no `node_modules`, so the require threw +`MODULE_NOT_FOUND` at module load, before `activate()` ran → the extension was +stuck "Activating…" and every `hana-cli.*` command reported "not found". + +Two esbuild workarounds were added to unblock the extension: +- `bundleTextBundlePlugin` — rewrites the `@sap/textbundle` require into a + static import so esbuild bundles it. +- `stubTerminalKitPlugin` — stubs the CLI-only `terminal-kit` (also loaded via + a static import in `base.js`) out of the extension bundle. + +These are band-aids over source-level legacy patterns. On a Node 22+ baseline +the underlying patterns can be replaced with stable built-ins, which lets us +delete both workarounds. + +## Goal + +Modernize CommonJS-interop legacy patterns to Node 22+ built-ins across the +codebase, and remove the two esbuild workarounds. + +## Scope + +### In scope + +1. **Static package requires → static `import`** + - `@sap/textbundle` in `utils/base.js` and `utils/base-lite.js` + - `check-node-version` in `utils/versionCheck.js` +2. **`terminal-kit` → lazy Proxy** (Approach A): no static import; `base.terminal` + stays a synchronous object via a lazy-initializing Proxy. +3. **`__dirname` / `__filename` shims → `import.meta.dirname` / `import.meta.filename`** + across `utils/`, `bin/`, `routes/`, `scripts/`, `tests/` (~40 sites). +4. **MCP server** — modernize `mcp-server/src/*.ts`; regenerate `mcp-server/build/`. +5. **Docs** — update `.github/instructions/*.md` examples to teach the modern pattern. +6. **esbuild cleanup** — delete `bundleTextBundlePlugin` and `stubTerminalKitPlugin`; + remove `terminal-kit` from the `external` list. + +### Out of scope (stays as-is by design) + +- **Exported `base.require()` / `baseLite.require()`** — genuinely dynamic: + path-based JSON loads, dynamic `uuid`/`express` loads, and the `@sap/cds-dk` + deep import with a global-npm (`npm root -g`) fallback. `createRequire` is the + correct tool here. Consumers: `bin/cds.js`, `bin/cli.js`, `bin/createContainer.js`, + `bin/createContainerUsers.js`, `bin/inspectTable.js`, `bin/inspectView.js`, + `bin/version.js`, `utils/connections.js`. Only clarifying comments may change. +- **Hand-editing `mcp-server/build/`** — always regenerated from `src/`. +- **sqlite/tar/generic-pool/@cap-js/@sap-cloud-sdk externals** in esbuild — absent + from `node_modules`, behind guarded requires, must remain `external`. + +## Design + +### Category 1 — Static package requires → `import` + +| File | Change | +|---|---| +| `utils/base.js` | `@sap/textbundle` (line ~125) → `import { TextBundle } from '@sap/textbundle'`. **Keep** `createRequire` import + `standardRequire` — they back the exported `require()` (lines ~19, ~33). | +| `utils/base-lite.js` | `@sap/textbundle` (line ~86) → static import. **Keep** `export const require` (public API, used by `bin/version.js`). | +| `utils/versionCheck.js` | `check-node-version` → `import check from 'check-node-version'`; drop `createRequire`. `require("../package.json")` → `import pkg from '../package.json' with { type: 'json' }` (stable on Node 22). | + +### Category 2 — `terminal-kit` lazy Proxy (Approach A) + +`base.terminal` is consumed **synchronously as an object** — +`base.terminal.progressBar(...)` in `massConvert/massExport/massDelete/massGrant` +and `terminal.table(...)` internally. All such call sites are inside `async` +functions. + +- Remove `import terminalKit from 'terminal-kit'` (`base.js` ~line 300). +- `base.terminal` becomes a `Proxy` that, on first property access, resolves a + cached terminal-kit instance (via the existing lazy loader at `base.js` + ~lines 282–290) and forwards `.table` / `.progressBar`. +- Test-mode (`NODE_ENV === 'test'`) and load-failure paths keep the existing + console-based stub. +- **Zero call-site changes.** terminal-kit is never in the extension's + load-time graph → the esbuild stub is unnecessary. + +### Category 3 — `__dirname` / `__filename` shims + +- `const __dirname = fileURLToPath(new URL('.', import.meta.url))` and + `const __dirname = path.dirname(fileURLToPath(import.meta.url))` + → `const __dirname = import.meta.dirname`. +- `const __filename = fileURLToPath(import.meta.url)` → `const __filename = import.meta.filename`. +- Keep the local `__dirname` / `__filename` names to minimize diff. +- Remove now-unused `fileURLToPath` / `URL` / `dirname` imports per file. + +### esbuild cleanup (`vscode-extension/esbuild.config.mjs`) + +- Delete `bundleTextBundlePlugin` (static import is now traced natively). +- Delete `stubTerminalKitPlugin`; remove `'terminal-kit'` from `external`. +- Keep `stripCLIBootstrapPlugin`, `dynamicImportPlugin`, `resolveRoutesPlugin`, + and the sqlite/tar/etc. externals unchanged. +- **Contingency:** the original reason `terminal-kit` was `external` was a + README-without-extension that esbuild couldn't parse. Since it is now only + reached via `await import()` (lazy), verify it bundles cleanly. If the parse + issue resurfaces, return `terminal-kit` to `external` — the lazy Proxy's + console fallback still yields a working extension. + +### MCP server + +Modernize `mcp-server/src/command-metadata.ts`, `resources.ts`, +`readme-knowledge-base.ts`, `index.ts`, `executor.ts`, `docs-search.ts` +(`createRequire`, `__filename` shims) → `import.meta.dirname` / static import, +then run the TS build to regenerate `mcp-server/build/`. + +## Testing & Verification + +1. **Baseline:** `npm test` green before changes. +2. **Per-category:** `npm test` + `npm run lint` (catches unused imports) after + each category. Commit per category for bisectability. +3. **Extension (critical):** rebuild the bundle, then run the in-place load + probe from the installed extension dir (no `node_modules`) — must load with + `activate` exported. Reload VS Code; confirm the "hana-cli" output channel + appears with "activated successfully" and commands run. +4. **CLI smoke:** `hana-cli version`; a `--output table` command (exercises the + terminal-kit lazy path); `hana-cli vscode status`. +5. **MCP:** build + a basic tool-list smoke check. + +## Risks & Controls + +- Work on a feature branch; commit per category. +- The exported `require()` and the sqlite/etc. externals are untouched by design. +- `import.meta.dirname` (Node 20.11+) and JSON import attributes (stable Node 22) + are both safely above the ≥22 baseline. +- terminal-kit bundling contingency documented above. + +## Success Criteria + +- No `createRequire`-based **static** package requires remain (exported dynamic + `require()` intentionally retained). +- No `fileURLToPath`-based `__dirname`/`__filename` shims remain in scoped code. +- Both esbuild workarounds deleted; extension activates from a clean `.vsix`. +- `npm test`, lint, extension load probe, CLI smoke, and MCP build all pass. diff --git a/docs/troubleshooting/index.md b/docs/troubleshooting/index.md index f9dfc5bd..a5fc5879 100644 --- a/docs/troubleshooting/index.md +++ b/docs/troubleshooting/index.md @@ -506,7 +506,7 @@ hana-cli --debug --verbose | Tee-Object -FilePath hana-cli-debug.log ``` 2. **Upgrade to the required version** - - hana-cli requires Node.js 20.19.0 or later + - hana-cli requires Node.js 22.0.0 or later (Node.js 24 LTS recommended) - See the [Installation Guide](../01-getting-started/installation.md) for platform-specific steps #### Insufficient Privileges diff --git a/scripts/generate-agent-instructions.js b/scripts/generate-agent-instructions.js index 91dc727f..a8a59bed 100644 --- a/scripts/generate-agent-instructions.js +++ b/scripts/generate-agent-instructions.js @@ -42,7 +42,7 @@ const FORCE = process.argv.includes('--force') * @returns {Promise>} */ async function loadCommandMetadata() { - const mod = await import('./bin/commandMetadata.js') + const mod = await import('../bin/commandMetadata.js') return mod.commandMetadata } @@ -51,7 +51,7 @@ async function loadCommandMetadata() { */ async function loadEnrichedMetadata() { try { - const mod = await import('./mcp-server/build/command-metadata.js') + const mod = await import('../mcp-server/build/command-metadata.js') return { enriched: mod.COMMAND_METADATA_MAP || {}, categories: mod.CATEGORIES || {}, @@ -68,7 +68,7 @@ async function loadEnrichedMetadata() { */ async function loadExamplesPresets() { try { - const mod = await import('./mcp-server/build/examples-presets.js') + const mod = await import('../mcp-server/build/examples-presets.js') return { examples: mod.COMMAND_EXAMPLES || {}, presets: mod.COMMAND_PRESETS || {}, @@ -220,7 +220,7 @@ function buildCanonicalCommands(commandMeta, commandFiles, enriched, examples, p * Get the package version */ function getVersion() { - const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')) + const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')) return pkg.version } @@ -271,7 +271,7 @@ function generateReference(canonical, categories, workflows, version) { lines.push(`npm install -g hana-cli`) lines.push('```') lines.push(``) - lines.push(`**Requirements:** Node.js ≥ 20.19.0`) + lines.push(`**Requirements:** Node.js ≥ 22.0.0 (Node.js 24 LTS recommended)`) lines.push(``) lines.push(`### Connection Setup`) lines.push(``) @@ -767,7 +767,7 @@ function agentPreamble(version) { hana-cli (npm: hana-cli, install: \`npm install -g hana-cli\`) is a command-line tool for SAP HANA database development. It simplifies complex multi-step database operations into single commands. It is a development tool, not a replacement for hdbsql or production admin tools. **Version:** ${version} -**Requirements:** Node.js ≥ 20.19.0 +**Requirements:** Node.js ≥ 22.0.0 (Node.js 24 LTS recommended) **Module:** ESM (\`"type": "module"\`) ## When to Use hana-cli