From 17ee877b7ee9a216f8150b1877954bb036aaaac8 Mon Sep 17 00:00:00 2001 From: S Smith Date: Mon, 6 Jul 2026 07:30:27 +0000 Subject: [PATCH 1/2] fix npm installer shell, clean up repo, remove devcontainer build step --- .devcontainer/devcontainer.json | 28 ++++-- .devcontainer/install.sh | 14 ++- AGENTS.md | 2 + Makefile | 2 +- README.md | 8 -- ansible.cfg | 5 +- roles/devcontainer_sync/defaults/main.yml | 11 +-- .../tasks/sync_devcontainer.yml | 86 ++----------------- tests/cloud-example/.devcontainer/install.sh | 4 +- tests/golden_output | 22 ++--- tests/node-example/.devcontainer/install.sh | 10 +-- tests/python-example/.devcontainer/install.sh | 4 +- tests/rust-example/.devcontainer/install.sh | 4 +- 13 files changed, 65 insertions(+), 135 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 72d585c..de44dc9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,13 +4,29 @@ "customizations": { "vscode": { "extensions": [ - "openai.chatgpt", - "redhat.vscode-yaml" - ] + "ankurmathur.opencode-provider-bridge", + "redhat.vscode-yaml", + "timonwong.shellcheck" + ], + "mcp": { + "servers": { + "context-mode": { + "command": "context-mode" + }, + "smart-tree": { + "args": [ + "--mcp" + ], + "command": "st", + "type": "stdio" + } + } + } } }, "mounts": [ - "source=codex-config,target=/opt/.codex,type=volume" + "source=claude-code-config,target=/opt/claude-code-config,type=volume", + "source=opencode-config,target=/opt/opencode-config,type=volume" ], - "postCreateCommand": "sudo chown $USER:$USER /opt/.codex && ln -s /opt/.codex $HOME/.codex && bash .devcontainer/install.sh" -} \ No newline at end of file + "postCreateCommand": "sudo chown $USER:$USER /opt/claude-code-config && mkdir -p /opt/claude-code-config/.claude && touch /opt/claude-code-config/.claude.json && ln -sfn /opt/claude-code-config/.claude.json $HOME/.claude.json && ln -sfn /opt/claude-code-config/.claude $HOME/.claude && echo \"export CLAUDE_CODE_DISABLE_CRON=1\" >> ~/.bashrc && sudo chown $USER:$USER /opt/opencode-config && mkdir -p $HOME/.config && ln -sfn /opt/opencode-config $HOME/.config/opencode && bash \"${containerWorkspaceFolder}/.devcontainer/install.sh\"" +} diff --git a/.devcontainer/install.sh b/.devcontainer/install.sh index 65c18a5..27a08a0 100644 --- a/.devcontainer/install.sh +++ b/.devcontainer/install.sh @@ -1,3 +1,13 @@ #!/bin/bash -sudo apt update && sudo apt install ansible -y -curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh \ No newline at end of file + +set -euo pipefail + +DEVCONTAINER_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +containerWorkspaceFolder="$(dirname -- "$DEVCONTAINER_DIR")" +export containerWorkspaceFolder + +sudo apt update && sudo apt install ansible shellcheck -y +curl -fsSL https://claude.ai/install.sh | bash +curl -fsSL https://opencode.ai/install | bash +curl -sSL https://raw.githubusercontent.com/8b-is/smart-tree/main/scripts/install.sh | bash +bash -c "source /usr/local/share/nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" diff --git a/AGENTS.md b/AGENTS.md index bc2fe69..29c9f88 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,6 +23,7 @@ roles/devcontainer_sync/defaults/main.yml vscode_extensions: [...] vscode_settings: {...} features: {...} + mcp: {...} mounts: [...] post_create_commands: [...] container_env: {...} @@ -60,6 +61,7 @@ roles/devcontainer_sync/defaults/main.yml vscode_extensions: [...] vscode_settings: {...} features: {...} + mcp: {...} mounts: [...] post_create_commands: [...] post_start_commands: [...] diff --git a/Makefile b/Makefile index 3d49a25..31f419a 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,6 @@ endif find ${WORKSPACE_ROOT} -type d -name ".devcontainer-bak-*" -exec rm -rf {} + test: - $(ANSIBLE_PLAYBOOK) --diff $(PLAYBOOK) -e @$(EXAMPLE_VARS) -e workspace_root=$(TEST_WORKSPACE_ROOT) -e devcontainer_sync_create_missing=true -e devcontainer_auto_rebuild=false -e devcontainer_sync_backup=false -e devcontainer_sync_backup_existing_dir=false + $(ANSIBLE_PLAYBOOK) --diff $(PLAYBOOK) -e @$(EXAMPLE_VARS) -e workspace_root=$(TEST_WORKSPACE_ROOT) -e devcontainer_sync_create_missing=true -e devcontainer_sync_backup=false -e devcontainer_sync_backup_existing_dir=false find $(TEST_WORKSPACE_ROOT) -type f ! -path $(TEST_WORKSPACE_ROOT)/golden_output -print0 | sort -z | xargs -0r cat > $(TEST_WORKSPACE_ROOT)/golden_output $(MAKE) shellcheck diff --git a/README.md b/README.md index 09662be..718588f 100644 --- a/README.md +++ b/README.md @@ -176,14 +176,6 @@ Apply changes: make apply ``` -By default, a changed `.devcontainer` directory is rebuilt after all managed and extra files are written. The role hashes relative file paths and contents before and after synchronization, then runs: - -```bash -devcontainer up --workspace-folder /path/to/project --remove-existing-container -``` - -Set `devcontainer_auto_rebuild: false` to render changes without rebuilding. Check mode never runs the rebuild command. - Missing targets are skipped by default. Create them with: ```bash diff --git a/ansible.cfg b/ansible.cfg index 42d8054..6a0eac0 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -8,4 +8,7 @@ bin_ansible_callbacks = True retry_files_enabled = False [diff] -always = True \ No newline at end of file +always = False + +[connection] +pipelining = True \ No newline at end of file diff --git a/roles/devcontainer_sync/defaults/main.yml b/roles/devcontainer_sync/defaults/main.yml index 62d8eb9..ffda8a7 100644 --- a/roles/devcontainer_sync/defaults/main.yml +++ b/roles/devcontainer_sync/defaults/main.yml @@ -3,7 +3,6 @@ workspace_root: "{{ lookup('env', 'WORKSPACE_ROOT') | default('~/dev/', true) }} local_timezone: America/Chicago devcontainer_sync_create_missing: false -devcontainer_auto_rebuild: true devcontainer_sync_backup: true devcontainer_sync_backup_existing_dir: true devcontainer_sync_extra_files_root: "{{ playbook_dir }}/files" @@ -71,8 +70,7 @@ devcontainer_plugins: contextmode: install_steps: - curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')/install.sh" | bash - - bash -c "source ~/.nvm/nvm.sh && nvm install 24" - - npm install context-mode + - bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" && npm install --save-dev context-mode - mkdir -p "${containerWorkspaceFolder}"/.github/hooks - wget https://raw.githubusercontent.com/mksglu/context-mode/refs/heads/main/configs/vscode-copilot/hooks.json -O .github/hooks/context-mode.json mcp: @@ -190,10 +188,9 @@ devcontainer_plugins: typescript: install_steps: - - sudo apt update && sudo apt install -y npm --no-install-recommends - - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash - - bash -c "source ~/.nvm/nvm.sh && nvm install 24" - - if [ -f "${containerWorkspaceFolder}/package.json" ]; then npm install; fi + - curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')/install.sh" | bash + - bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" + - if [ -f "${containerWorkspaceFolder}/package.json" ]; then bash -c "npm install"; fi github: install_steps: diff --git a/roles/devcontainer_sync/tasks/sync_devcontainer.yml b/roles/devcontainer_sync/tasks/sync_devcontainer.yml index d5bae15..5e3d750 100644 --- a/roles/devcontainer_sync/tasks/sync_devcontainer.yml +++ b/roles/devcontainer_sync/tasks/sync_devcontainer.yml @@ -199,41 +199,9 @@ path: "{{ [workspace_root, devcontainer_sync_target_root, '.devcontainer/devcontainer.json'] | path_join }}" register: devcontainer_sync_target -- name: Find initial .devcontainer files - ansible.builtin.find: - paths: "{{ [workspace_root, devcontainer_sync_target_root, '.devcontainer'] | path_join }}" - file_type: file - recurse: true - hidden: true - get_checksum: true - checksum_algorithm: sha256 - register: devcontainer_sync_initial_files - changed_when: false - when: devcontainer_sync_target.stat.exists - -- name: Store initial .devcontainer hash +- name: Determine whether this devcontainer should be rendered ansible.builtin.set_fact: - devcontainer_sync_initial_hash: >- - {%- if not devcontainer_sync_target.stat.exists -%} - missing - {%- else -%} - {%- set entries = [] -%} - {%- set target_dir = [workspace_root, devcontainer_sync_target_root, '.devcontainer'] | path_join -%} - {%- for file in devcontainer_sync_initial_files.files | sort(attribute='path') -%} - {%- set _ = entries.append([file.path | relpath(target_dir), file.checksum]) -%} - {%- endfor -%} - {{ entries | to_json | hash('sha256') }} - {%- endif -%} - -- name: Check whether .editorconfig would change - ansible.builtin.copy: - src: editorconfig - dest: "{{ [workspace_root, devcontainer_sync_target_root, '.editorconfig'] | path_join }}" - mode: "0644" - backup: false - check_mode: true - register: devcontainer_sync_editorconfig_render_preview - when: devcontainer_sync_target.stat.exists + devcontainer_sync_should_render: "{{ devcontainer_sync_target.stat.exists or devcontainer_sync_create_missing | bool }}" - name: Check whether devcontainer.json would change ansible.builtin.copy: @@ -287,7 +255,7 @@ path: "{{ [workspace_root, devcontainer_sync_target_root, '.devcontainer'] | path_join }}" state: directory mode: "0755" - when: devcontainer_sync_target.stat.exists or devcontainer_sync_create_missing | bool + when: devcontainer_sync_should_render - name: Render .editorconfig ansible.builtin.copy: @@ -295,7 +263,7 @@ dest: "{{ [workspace_root, devcontainer_sync_target_root, '.editorconfig'] | path_join }}" mode: "0644" backup: "{{ devcontainer_sync_backup }}" - when: devcontainer_sync_target.stat.exists or devcontainer_sync_create_missing | bool + when: devcontainer_sync_should_render - name: Render devcontainer.json ansible.builtin.copy: @@ -303,7 +271,7 @@ dest: "{{ [workspace_root, devcontainer_sync_target_root, '.devcontainer/devcontainer.json'] | path_join }}" mode: "0644" backup: "{{ devcontainer_sync_backup }}" - when: devcontainer_sync_target.stat.exists or devcontainer_sync_create_missing | bool + when: devcontainer_sync_should_render - name: Render install.sh ansible.builtin.template: @@ -311,7 +279,7 @@ dest: "{{ [workspace_root, devcontainer_sync_target_root, '.devcontainer/install.sh'] | path_join }}" mode: "0755" backup: "{{ devcontainer_sync_backup }}" - when: devcontainer_sync_target.stat.exists or devcontainer_sync_create_missing | bool + when: devcontainer_sync_should_render - name: Render aws_configure.sh ansible.builtin.template: @@ -320,50 +288,12 @@ mode: "0755" backup: "{{ devcontainer_sync_backup }}" when: - - devcontainer_sync_target.stat.exists or devcontainer_sync_create_missing | bool + - devcontainer_sync_should_render - "'aws' in devcontainer_sync_plugin_names" - name: Copy project extra .devcontainer files ansible.builtin.include_tasks: extra_files.yml - when: devcontainer_sync_target.stat.exists or devcontainer_sync_create_missing | bool - -- name: Find rendered .devcontainer files - ansible.builtin.find: - paths: "{{ [workspace_root, devcontainer_sync_target_root, '.devcontainer'] | path_join }}" - file_type: file - recurse: true - hidden: true - get_checksum: true - checksum_algorithm: sha256 - register: devcontainer_sync_rendered_files - changed_when: false - when: devcontainer_sync_target.stat.exists or devcontainer_sync_create_missing | bool - -- name: Store rendered .devcontainer hash - ansible.builtin.set_fact: - devcontainer_sync_rendered_hash: >- - {%- set entries = [] -%} - {%- set target_dir = [workspace_root, devcontainer_sync_target_root, '.devcontainer'] | path_join -%} - {%- for file in devcontainer_sync_rendered_files.files | sort(attribute='path') -%} - {%- set _ = entries.append([file.path | relpath(target_dir), file.checksum]) -%} - {%- endfor -%} - {{ entries | to_json | hash('sha256') }} - when: devcontainer_sync_target.stat.exists or devcontainer_sync_create_missing | bool - -# - name: Rebuild changed devcontainer -# ansible.builtin.command: -# argv: -# - devcontainer -# - up -# - --workspace-folder -# - "{{ [workspace_root, devcontainer_sync_target_root] | path_join }}" -# - --remove-existing-container -# when: -# - devcontainer_auto_rebuild | bool -# - not ansible_check_mode -# - devcontainer_sync_target.stat.exists or devcontainer_sync_create_missing | bool -# - devcontainer_sync_initial_hash != devcontainer_sync_rendered_hash -# changed_when: true + when: devcontainer_sync_should_render - name: Report skipped missing devcontainer file ansible.builtin.debug: diff --git a/tests/cloud-example/.devcontainer/install.sh b/tests/cloud-example/.devcontainer/install.sh index 072d07d..d4e73e7 100755 --- a/tests/cloud-example/.devcontainer/install.sh +++ b/tests/cloud-example/.devcontainer/install.sh @@ -19,9 +19,7 @@ curl -sSL https://raw.githubusercontent.com/8b-is/smart-tree/main/scripts/instal curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')/install.sh" | bash -bash -c "source ~/.nvm/nvm.sh && nvm install 24" - -npm install context-mode +bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" && npm install --save-dev context-mode mkdir -p "${containerWorkspaceFolder}"/.github/hooks diff --git a/tests/golden_output b/tests/golden_output index 7327449..600f60e 100644 --- a/tests/golden_output +++ b/tests/golden_output @@ -138,9 +138,7 @@ curl -sSL https://raw.githubusercontent.com/8b-is/smart-tree/main/scripts/instal curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')/install.sh" | bash -bash -c "source ~/.nvm/nvm.sh && nvm install 24" - -npm install context-mode +bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" && npm install --save-dev context-mode mkdir -p "${containerWorkspaceFolder}"/.github/hooks @@ -216,11 +214,9 @@ sudo apt update && sudo apt install -y ripgrep ncat sudo ln -sf /usr/share/zoneinfo/America/Chicago /etc/localtime && sudo dpkg-reconfigure -f noninteractive tzdata -sudo apt update && sudo apt install -y npm --no-install-recommends - -curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash +bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" -if [ -f "${containerWorkspaceFolder}/package.json" ]; then npm install; fi +if [ -f "${containerWorkspaceFolder}/package.json" ]; then bash -c "npm install"; fi curl -fsSL https://claude.ai/install.sh | bash @@ -230,9 +226,7 @@ curl -sSL https://raw.githubusercontent.com/8b-is/smart-tree/main/scripts/instal curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')/install.sh" | bash -bash -c "source ~/.nvm/nvm.sh && nvm install 24" - -npm install context-mode +bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" && npm install --save-dev context-mode mkdir -p "${containerWorkspaceFolder}"/.github/hooks @@ -404,9 +398,7 @@ curl -sSL https://raw.githubusercontent.com/8b-is/smart-tree/main/scripts/instal curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')/install.sh" | bash -bash -c "source ~/.nvm/nvm.sh && nvm install 24" - -npm install context-mode +bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" && npm install --save-dev context-mode mkdir -p "${containerWorkspaceFolder}"/.github/hooks @@ -513,9 +505,7 @@ curl -sSL https://raw.githubusercontent.com/8b-is/smart-tree/main/scripts/instal curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')/install.sh" | bash -bash -c "source ~/.nvm/nvm.sh && nvm install 24" - -npm install context-mode +bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" && npm install --save-dev context-mode mkdir -p "${containerWorkspaceFolder}"/.github/hooks diff --git a/tests/node-example/.devcontainer/install.sh b/tests/node-example/.devcontainer/install.sh index 0f923d3..57c97ba 100755 --- a/tests/node-example/.devcontainer/install.sh +++ b/tests/node-example/.devcontainer/install.sh @@ -9,11 +9,9 @@ sudo apt update && sudo apt install -y ripgrep ncat sudo ln -sf /usr/share/zoneinfo/America/Chicago /etc/localtime && sudo dpkg-reconfigure -f noninteractive tzdata -sudo apt update && sudo apt install -y npm --no-install-recommends +bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" -curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash - -if [ -f "${containerWorkspaceFolder}/package.json" ]; then npm install; fi +if [ -f "${containerWorkspaceFolder}/package.json" ]; then bash -c "npm install"; fi curl -fsSL https://claude.ai/install.sh | bash @@ -23,9 +21,7 @@ curl -sSL https://raw.githubusercontent.com/8b-is/smart-tree/main/scripts/instal curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')/install.sh" | bash -bash -c "source ~/.nvm/nvm.sh && nvm install 24" - -npm install context-mode +bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" && npm install --save-dev context-mode mkdir -p "${containerWorkspaceFolder}"/.github/hooks diff --git a/tests/python-example/.devcontainer/install.sh b/tests/python-example/.devcontainer/install.sh index 2d56e09..68104cd 100755 --- a/tests/python-example/.devcontainer/install.sh +++ b/tests/python-example/.devcontainer/install.sh @@ -24,9 +24,7 @@ curl -sSL https://raw.githubusercontent.com/8b-is/smart-tree/main/scripts/instal curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')/install.sh" | bash -bash -c "source ~/.nvm/nvm.sh && nvm install 24" - -npm install context-mode +bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" && npm install --save-dev context-mode mkdir -p "${containerWorkspaceFolder}"/.github/hooks diff --git a/tests/rust-example/.devcontainer/install.sh b/tests/rust-example/.devcontainer/install.sh index d493d28..007eccc 100755 --- a/tests/rust-example/.devcontainer/install.sh +++ b/tests/rust-example/.devcontainer/install.sh @@ -21,9 +21,7 @@ curl -sSL https://raw.githubusercontent.com/8b-is/smart-tree/main/scripts/instal curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')/install.sh" | bash -bash -c "source ~/.nvm/nvm.sh && nvm install 24" - -npm install context-mode +bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" && npm install --save-dev context-mode mkdir -p "${containerWorkspaceFolder}"/.github/hooks From 91d0d980c54668e1e54e64d2b77d86652734b228 Mon Sep 17 00:00:00 2001 From: S Smith Date: Mon, 6 Jul 2026 07:33:43 +0000 Subject: [PATCH 2/2] missed one --- roles/devcontainer_sync/defaults/main.yml | 2 +- tests/golden_output | 2 +- tests/node-example/.devcontainer/install.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/devcontainer_sync/defaults/main.yml b/roles/devcontainer_sync/defaults/main.yml index ffda8a7..36f1419 100644 --- a/roles/devcontainer_sync/defaults/main.yml +++ b/roles/devcontainer_sync/defaults/main.yml @@ -190,7 +190,7 @@ devcontainer_plugins: install_steps: - curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')/install.sh" | bash - bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" - - if [ -f "${containerWorkspaceFolder}/package.json" ]; then bash -c "npm install"; fi + - if [ -f "${containerWorkspaceFolder}/package.json" ]; then bash -c "source ~/.nvm/nvm.sh && npm install"; fi github: install_steps: diff --git a/tests/golden_output b/tests/golden_output index 600f60e..0d2351e 100644 --- a/tests/golden_output +++ b/tests/golden_output @@ -216,7 +216,7 @@ sudo ln -sf /usr/share/zoneinfo/America/Chicago /etc/localtime && sudo dpkg-reco bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" -if [ -f "${containerWorkspaceFolder}/package.json" ]; then bash -c "npm install"; fi +if [ -f "${containerWorkspaceFolder}/package.json" ]; then bash -c "source ~/.nvm/nvm.sh && npm install"; fi curl -fsSL https://claude.ai/install.sh | bash diff --git a/tests/node-example/.devcontainer/install.sh b/tests/node-example/.devcontainer/install.sh index 57c97ba..fc7beb8 100755 --- a/tests/node-example/.devcontainer/install.sh +++ b/tests/node-example/.devcontainer/install.sh @@ -11,7 +11,7 @@ sudo ln -sf /usr/share/zoneinfo/America/Chicago /etc/localtime && sudo dpkg-reco bash -c "source ~/.nvm/nvm.sh && nvm install 24 --latest-npm --no-progress" -if [ -f "${containerWorkspaceFolder}/package.json" ]; then bash -c "npm install"; fi +if [ -f "${containerWorkspaceFolder}/package.json" ]; then bash -c "source ~/.nvm/nvm.sh && npm install"; fi curl -fsSL https://claude.ai/install.sh | bash