@opensassi/opencode 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/AGENTS.md +35 -0
  2. package/README.md +81 -0
  3. package/bin/opencode.js +3 -0
  4. package/lib/cli.js +38 -0
  5. package/lib/commands/init.js +117 -0
  6. package/lib/commands/print-agents.js +6 -0
  7. package/lib/commands/print-skill.js +8 -0
  8. package/lib/commands/run.js +57 -0
  9. package/lib/index.js +4 -0
  10. package/lib/util/paths.js +21 -0
  11. package/package.json +40 -0
  12. package/scripts/asm-optimizer/run-baseline.sh +158 -0
  13. package/scripts/check-artifacts.js +131 -0
  14. package/scripts/extract-artifacts.js +204 -0
  15. package/scripts/install/linux/ubuntu-noble-24.04/install.sh +94 -0
  16. package/scripts/install/osx/macos-sequoia-15.0/install.sh +115 -0
  17. package/scripts/install/windows/wsl2/install.ps1 +98 -0
  18. package/scripts/install.ps1 +32 -0
  19. package/scripts/install.sh +83 -0
  20. package/scripts/puppeteer-config.json +3 -0
  21. package/scripts/test-artifacts.js +346 -0
  22. package/scripts/validate-all.js +18 -0
  23. package/scripts/verify-artifact.js +157 -0
  24. package/skills/asm-optimizer/SKILL.md +295 -0
  25. package/skills/daily-evaluation/SKILL.md +86 -0
  26. package/skills/git/SKILL.md +100 -0
  27. package/skills/issue/SKILL.md +104 -0
  28. package/skills/npm-optimizer/SKILL.md +218 -0
  29. package/skills/opensassi/SKILL.md +77 -0
  30. package/skills/opensassi/scripts/ensure-gitignore.sh +89 -0
  31. package/skills/opensassi/scripts/env-check.ps1 +139 -0
  32. package/skills/opensassi/scripts/env-check.sh +200 -0
  33. package/skills/opensassi/scripts/install-flamegraph.sh +32 -0
  34. package/skills/opensassi/scripts/install-npm-deps.sh +25 -0
  35. package/skills/profiler/SKILL.md +213 -0
  36. package/skills/profiler/scripts/benchmark.sh +63 -0
  37. package/skills/profiler/scripts/common.sh +55 -0
  38. package/skills/profiler/scripts/compare.sh +63 -0
  39. package/skills/profiler/scripts/profile.sh +63 -0
  40. package/skills/profiler/scripts/setup.sh +32 -0
  41. package/skills/session-evaluation/SKILL.md +128 -0
  42. package/skills/skill-manager/SKILL.md +251 -0
  43. package/skills/system-design/SKILL.md +558 -0
  44. package/skills/system-design-review/SKILL.md +396 -0
  45. package/skills/todo/SKILL.md +165 -0
  46. package/skills-index.json +137 -0
@@ -0,0 +1,218 @@
1
+ ---
2
+ name: npm-optimizer
3
+ description: Port an existing npm package to a C++ native addon — preserve 100% test compatibility while significantly improving performance through profiling-driven architectural iteration.
4
+ ---
5
+
6
+ # Skill: npm-optimizer
7
+
8
+ ## Persona
9
+
10
+ Senior systems engineer specializing in Node.js native addon development and performance optimization. Strong background in C++, V8 internals, perf profiling, and the npm build pipeline (node-gyp, N-API).
11
+
12
+ ## On Activation
13
+
14
+ 1. Check that a target npm package name is available in context. If not, prompt.
15
+ 2. Run `show-state` to summarize current progress through the `execute` pipeline.
16
+ 3. List which phases of `execute` have been completed and which are pending.
17
+
18
+ ## Commands
19
+
20
+ ### `execute`
21
+
22
+ Run the full port pipeline. Each phase must complete before the next begins. The agent pauses after each phase, reports results, and waits for acknowledgment before proceeding.
23
+
24
+ ---
25
+
26
+ **Phase 1 — Spec & Discovery**
27
+
28
+ Goal: Understand the original package's full surface area before writing any code.
29
+
30
+ 1.1. Clone the target package into `external/<name>/`:
31
+ ```
32
+ git clone --depth 1 <repo-url> external/<name>
33
+ ```
34
+
35
+ 1.2. Copy the original test suite to `test/orig/`. Run it against the original to
36
+ establish a passing baseline. These tests must never be modified.
37
+
38
+ 1.3. Analyze the original source tree. For each source file, use the system-design
39
+ skill's spec workflow to produce a spec document. Group related files into
40
+ sub-modules. Output tree at:
41
+ ```
42
+ spec/original/
43
+ ├── <sub-module-1>/README.md
44
+ ├── <sub-module-2>/README.md
45
+ └── technical-specification.md (root overview + data flow)
46
+ ```
47
+
48
+ 1.4. From the spec tree, extract:
49
+ - Full API surface: every exported function, its signature, and behavior.
50
+ - Edge cases: undefined properties, NaN, toJSON, circular refs, prototype chain.
51
+ - Data flow: how inputs map to outputs.
52
+
53
+ 1.5. **Validate the ceiling** — Before designing the C++ architecture, build the
54
+ cheapest possible pass-through: a minimal addon that takes a string blob,
55
+ copies it, and returns it. Measure its ops/sec via `npm run benchmark`.
56
+ This is the **upper bound** for any approach that uses this N-API profile
57
+ (one crossing in, one out). If this doesn't exceed the original's speed,
58
+ the entire approach is dead — reconsider at the JS/N-API design level.
59
+
60
+ 1.6. Design the C++ addon architecture:
61
+ - Which functions go native vs stay in JS wrapper.
62
+ - N-API boundary strategy (minimize crossings per call).
63
+ - Build pipeline (binding.gyp, node-addon-api, dependencies).
64
+
65
+ 1.7. Generate implementation spec tree at `spec/implementation/` mirroring the
66
+ original's structure, with cross-reference mappings.
67
+
68
+ ---
69
+
70
+ **Phase 2 — Naive Implementation**
71
+
72
+ Goal: Build the simplest C++ addon that passes 100% of `test/orig/*.js`.
73
+
74
+ 2.1. Scaffold project: `package.json`, `binding.gyp`, `src/`, `index.js`
75
+ 2.2. Implement the C++ module with the direct approach
76
+ (e.g., traverse values through N-API, build string in C++).
77
+ 2.3. Run `test/orig/` tests. Fix until all pass.
78
+ 2.4. Establish baseline benchmark: `npm run benchmark` comparing against original JS.
79
+
80
+ ---
81
+
82
+ **Phase 3 — Profile & Classify**
83
+
84
+ Goal: Identify where time is actually going.
85
+
86
+ 3.1. Create `prof-harness.js` — tight loop exercising the main export.
87
+ 3.2. `perf record -F 199 --call-graph fp -o perf/baseline.profile.data node prof-harness.js`
88
+ 3.3. `perf report -i perf/baseline.profile.data --stdio -s overhead,symbol,dso`
89
+ 3.4. Classify samples into three tiers:
90
+ - **Tier 1 — Infrastructure**: V8 internals, N-API boundary, allocator.
91
+ - **Tier 2 — Our C++ logic**: string building, type dispatch, sorting.
92
+ - **Tier 3 — The original's work**: if we're still calling it.
93
+ 3.5. **Decision**: If Tier 1 > 30% of samples, mark as **architectural bottleneck**
94
+ and proceed to Phase 4A (pivot). Otherwise proceed to Phase 4B (micro-optimize).
95
+
96
+ ---
97
+
98
+ **Phase 4A — Architectural Pivot**
99
+
100
+ Goal: Change the approach when the current architecture hits a fundamental ceiling.
101
+
102
+ 4A.1. Identify the specific architectural cost (e.g., "200+ N-API crossings per call").
103
+ 4A.2. Design an alternative approach that eliminates this cost. Examples:
104
+ - "Let JSON.stringify do the work, then key-sort the blob in C++"
105
+ - "Batch N-API calls" / "Use raw V8 API instead of N-API"
106
+ - "Pre-allocate and reuse buffers across calls"
107
+ 4A.3. **Validate the hypothesis** — before implementing the full approach, build
108
+ the cheapest functional approximation (pass-through, stub). Measure it.
109
+ If the ceiling doesn't leave headroom over the original, reject this approach
110
+ and go back to 4A.2.
111
+ 4A.4. If validated: implement the full pivot approach. Run tests (100% pass).
112
+ 4A.5. Run benchmark. If target met, proceed to Phase 5.
113
+ If not, return to Phase 3 with new profile data.
114
+
115
+ ---
116
+
117
+ **Phase 4B — Micro-Optimize**
118
+
119
+ Goal: Attack specific C++ hotspots identified in Phase 3.
120
+
121
+ 4B.1. For each Tier-2 function, sorted by self% descending:
122
+ - Read the source code of the function.
123
+ - Identify the specific operation consuming time
124
+ (e.g., `std::ostringstream`, repeated allocation, branch-heavy loop).
125
+ - Apply one targeted fix.
126
+ - Rebuild, run tests, benchmark.
127
+ - If gain >= 5%: keep, move to next function.
128
+ - If gain < 5%: revert, try next hypothesis for this function.
129
+ 4B.2. **Three strikes rule**: if three consecutive fixes at this function
130
+ each yield <5%, stop micro-optimizing. Re-run Phase 3 and check
131
+ Tier 1 fraction. If it grew, proceed to Phase 4A.
132
+ 4B.3. When all Tier-2 functions are exhausted: re-run Phase 3.
133
+ If Tier 1 is now dominant, proceed to Phase 4A.
134
+
135
+ ---
136
+
137
+ **Phase 5 — Compatibility Shim & Documentation**
138
+
139
+ Goal: Handle edge cases where the implementation differs from the original.
140
+
141
+ 5.1. Compare output of original vs implementation for:
142
+ - All `test/orig/` cases (must pass).
143
+ - Edge cases: function-valued properties, undefined, toJSON,
144
+ prototype-chain access, Symbol-keyed properties.
145
+ 5.2. For any behavioral difference: add JS wrapper logic in `index.js`
146
+ (e.g., preprocess step for function→{} conversion).
147
+ Document the difference in `spec/cross-reference.md` with rationale.
148
+ 5.3. Generate `test/new/` tests covering implementation-specific behavior.
149
+ 5.4. Update `spec/cross-reference.md` with final benchmark deltas.
150
+
151
+ ---
152
+
153
+ **Phase 6 — Report**
154
+
155
+ Goal: Produce the final deliverable.
156
+
157
+ 6.1. Generate comparison table:
158
+ ```
159
+ | Implementation | Ops/sec | Relative |
160
+ |-------------------------|---------|----------|
161
+ | Original JS | X | 1.0x |
162
+ | C++ addon | Y | Y/X |
163
+ | Pass-through (ceiling) | Z | Z/X |
164
+ ```
165
+ 6.2. Archive final profile: `cp perf/baseline.profile.data perf/baseline/profiles/final.profile.data`
166
+ 6.3. Print the full report inline: benchmark numbers, profile summary, spec cross-reference path,
167
+ and a list of known behavioral differences (if any).
168
+
169
+ ### `show-state`
170
+
171
+ Output the current status of the `execute` pipeline:
172
+
173
+ ```
174
+ Phase 1 (Spec): COMPLETE — spec tree at spec/original/
175
+ Phase 2 (Naive): IN PROGRESS — 43/49 tests passing
176
+ Phase 3 (Profile): PENDING
177
+ Phase 4 (Optimize): PENDING
178
+ Phase 5 (Shim): PENDING
179
+ Phase 6 (Report): PENDING
180
+
181
+ Baseline benchmark: 8,037 ops/sec (original: 33,199)
182
+ Ceiling (pass-through): 104,866 ops/sec
183
+ ```
184
+
185
+ ## Design Principles
186
+
187
+ - **Tests are the contract.** `test/orig/` is never modified.
188
+ `test/new/` covers implementation-specific behavior.
189
+
190
+ - **Profile before optimizing.** Always run `perf record` / `perf report` before
191
+ any change. The data decides, not intuition.
192
+
193
+ - **Classify bottlenecks first.** If >30% of samples are in infrastructure
194
+ (V8 internals, N-API boundary, allocator), the fix is architectural, not micro.
195
+
196
+ - **Three strikes rule.** If three consecutive micro-optimizations yield <5% each,
197
+ stop. The bottleneck is architectural. Pivot.
198
+
199
+ - **Validate the pivot cheaply.** Before building a new approach, build the
200
+ cheapest functional version (pass-through, stub). If the ceiling doesn't leave
201
+ headroom for the real work, reject the approach immediately.
202
+
203
+ - **Fail fast on integration.** When a library integration crashes, investigate
204
+ just enough to understand WHY (one root cause), document it, and pivot.
205
+ Search for existing npm bindings as reference before deep debugging.
206
+
207
+ - **Trace algorithms on paper.** When recursive logic crashes with no clear cause,
208
+ walk through it manually with the simplest reproducer. Do not printf-debug
209
+ recursive algorithms — the bug is almost always in the depth-tracking or
210
+ separator-skipping logic.
211
+
212
+ - **Spec first, implement second.** Before writing any C++, generate the full spec
213
+ tree of the original package. The implementation spec mirrors this. The
214
+ cross-reference is the validation contract.
215
+
216
+ - **Know when to stop.** When the bottleneck moves to a component you're deliberately
217
+ leveraging (e.g., V8's JSON.stringify), further optimization within your code
218
+ gives diminishing returns. Ship it.
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: opensassi
3
+ description: Bootstrap a new project environment — detect OS, install toolchain (git, Node.js via nvm/LTS), clone FlameGraph, set up project infrastructure
4
+ ---
5
+
6
+ # Skill: opensassi
7
+
8
+ ## Persona
9
+
10
+ Senior DevOps engineer specializing in cross-platform development environment provisioning, with deep expertise in nvm, package managers (apt/dnf/yum/pacman/brew/choco), and build toolchain setup.
11
+
12
+ ## On Activation
13
+
14
+ 1. Show the skills-index (from `skills-index.json` or by running `npx @opensassi/opencode opensassi --print-index`)
15
+ 2. Run `init check` to report current environment status (OS, Node.js, git, FlameGraph, npm deps)
16
+ 3. Show available commands
17
+
18
+ To load a sub-skill (e.g., system-design, git, profiler), the agent should run:
19
+ ```
20
+ npx @opensassi/opencode <skill-name>
21
+ ```
22
+ and read the output as the skill's full instructions.
23
+
24
+ ## Dependencies
25
+
26
+ - `bash` or `powershell` (for bootstrap scripts — zero other deps)
27
+ - `git` (installed by bootstrap if missing)
28
+ - The `@opensassi/opencode` npm package (scripts resolve via `npx @opensassi/opencode run --skill opensassi <name>`)
29
+
30
+ ## Commands
31
+
32
+ ### `init`
33
+
34
+ Execute companion scripts from the `@opensassi/opencode` package. If a script is missing or a platform installer does not exist, report the gap and continue; do not generate files.
35
+
36
+ 1. `npx @opensassi/opencode run --skill opensassi env-check.sh` (or `env-check.ps1` on Windows) — bootstrap git + Node.js LTS (creates `.nvmrc` if missing)
37
+ 2. `init install` — run existing platform installer, or report if none found
38
+ 3. `init flamegraph` — clone FlameGraph v1.0
39
+ 4. `npx @opensassi/opencode run --skill opensassi install-npm-deps.sh` — `npm install`
40
+ 5. `npx @opensassi/opencode run --skill opensassi ensure-gitignore.sh` — append common patterns
41
+
42
+ ### `init install`
43
+
44
+ Install the development environment toolchain.
45
+
46
+ **Flow:**
47
+
48
+ 1. **Detect environment** — run `npx @opensassi/opencode run --skill opensassi env-check.sh` (Linux/macOS/WSL/Git Bash) or fall back to `env-check.ps1` (Windows native). Both output structured JSON.
49
+ 2. **Check for existing installer** — look for `npx @opensassi/opencode run install.sh` for platform-specific installers from the package's `scripts/install/` directory
50
+ 3. **If installer exists** — run it (installs: cmake, nasm, gdb, ripgrep, perf, htop, etc.)
51
+ 4. **If installer NOT found**:
52
+ a. Report: "No installer found for this platform"
53
+ b. Continue — env-check already installed git + Node.js, which is sufficient for the project to function
54
+
55
+ ### `init flamegraph`
56
+
57
+ Clone Brendan Gregg's FlameGraph at pinned tag `v1.0` to `scripts/FlameGraph/`:
58
+ - If `scripts/FlameGraph/` does not exist: `git clone --depth=1 --branch v1.0`
59
+ - If it exists: `git fetch --tags --depth=1 && git checkout v1.0`
60
+
61
+ ### `init check`
62
+
63
+ Run `npx @opensassi/opencode run --skill opensassi env-check.sh` (or `env-check.ps1`) and verify:
64
+ - Node.js version (LTS or later)
65
+ - git availability
66
+ - FlameGraph presence at `scripts/FlameGraph/`
67
+ - npm deps installed (`node_modules/` exists)
68
+ - `.gitignore` has common patterns
69
+
70
+ ## Design Principles
71
+
72
+ - **No circular dependencies on Node.js** — Bootstrap scripts use only bash or PowerShell. Node.js is installed BY the bootstrap.
73
+ - **nvm is additive, not destructive** — `nvm install --lts` installs alongside existing Node versions. `nvm use --lts` scopes to the current shell only. System default node is never touched.
74
+ - **`.nvmrc` for the project** — Written with `--lts` so `nvm use` auto-selects when entering the project directory.
75
+ - **FlameGraph pinned at v1.0** — Tag is stable; pinned clones are idempotent.
76
+ - **`install.ps1` is WSL-only** — Not modified by this skill. Windows-native installer is a future extension.
77
+ - **env-check scripts output JSON** — Structured `{os, distro, version, codename, pkg_manager, shell, is_wsl, arch, node_version, nvm_version, git_version}` for AI agent consumption.
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bash
2
+ # ensure-gitignore.sh — Add common .gitignore patterns if missing.
3
+ # Usage: bash ensure-gitignore.sh
4
+
5
+ set -euo pipefail
6
+
7
+ PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || (cd "$(dirname "$0")/../../.." && pwd))"
8
+ GITIGNORE="$PROJECT_ROOT/.gitignore"
9
+
10
+ PATTERNS=(
11
+ "# Build directories"
12
+ "/build*/"
13
+ "/bin/"
14
+ "/lib/"
15
+ "/install/"
16
+
17
+ "# IDE"
18
+ ".vscode/"
19
+ ".idea/"
20
+ "*.swp"
21
+ "*.swo"
22
+
23
+ "# Dependencies"
24
+ "node_modules/"
25
+
26
+ "# Generated artifacts"
27
+ ".artifacts/"
28
+ "src/**/.artifacts/"
29
+
30
+ "# FlameGraph (cloned by opensassi skill at v1.0)"
31
+ "scripts/FlameGraph/"
32
+
33
+ "# Browser automation"
34
+ ".playwright-mcp/"
35
+
36
+ "# Profiling and performance data"
37
+ ".profiler/"
38
+
39
+ "# Sessions"
40
+ "sessions/"
41
+ "!sessions/export-session.sh"
42
+ "!sessions/.gitkeep"
43
+ "!sessions/daily/.gitkeep"
44
+
45
+ "# Performance experiments"
46
+ "perf/experiments/"
47
+ "perf/baseline/"
48
+
49
+ "# Test data"
50
+ "test/data/"
51
+
52
+ "# CTest temporary files"
53
+ "Testing/"
54
+
55
+ "# External project clones"
56
+ "/external/"
57
+
58
+ "# OS files"
59
+ ".DS_Store"
60
+ "Thumbs.db"
61
+
62
+ "# Core dumps"
63
+ "core"
64
+ )
65
+
66
+ ensure_pattern() {
67
+ local pattern="$1"
68
+ # Skip comment lines
69
+ if [[ "$pattern" == \#* ]]; then
70
+ return 0
71
+ fi
72
+ # Check for existing pattern with optional leading /
73
+ local alt="${pattern#!}"
74
+ [[ "$pattern" == \!* ]] && alt="!/${alt#/}" || alt="/${alt#/}"
75
+ if grep -qxF "$pattern" "$GITIGNORE" 2>/dev/null || grep -qxF "$alt" "$GITIGNORE" 2>/dev/null; then
76
+ return 0
77
+ fi
78
+ echo "$pattern" >> "$GITIGNORE"
79
+ echo " [added] $pattern"
80
+ }
81
+
82
+ # Ensure file exists
83
+ touch "$GITIGNORE"
84
+
85
+ echo "[INFO] Ensuring .gitignore patterns..."
86
+ for p in "${PATTERNS[@]}"; do
87
+ ensure_pattern "$p"
88
+ done
89
+ echo "[INFO] .gitignore is up to date"
@@ -0,0 +1,139 @@
1
+ # env-check.ps1 — Bootstrap environment detection for opencode opensassi skill (Windows native).
2
+ # Detects OS, ensures git, installs Node.js LTS via nvm-windows if needed.
3
+ # Outputs JSON to stdout: {os, distro, version, codename, pkg_manager, shell,
4
+ # is_wsl, arch, node_version, nvm_version, git_version}
5
+ #
6
+ # Usage: powershell -ExecutionPolicy Bypass -File env-check.ps1
7
+
8
+ $ErrorActionPreference = "Stop"
9
+
10
+ # --- OS Detection ---
11
+ $os = "win32"
12
+ $distro = "windows"
13
+ $version = ""
14
+ $codename = ""
15
+ $arch = [Environment]::GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")
16
+ if ($arch -match "AMD64|x86_64") { $arch = "x64" }
17
+ elseif ($arch -match "ARM64") { $arch = "arm64" }
18
+ $shell = "powershell"
19
+ $is_wsl = $false
20
+
21
+ # Detect package manager
22
+ $pkg_manager = ""
23
+ if (Get-Command winget -ErrorAction SilentlyContinue) { $pkg_manager = "winget" }
24
+ elseif (Get-Command choco -ErrorAction SilentlyContinue) { $pkg_manager = "choco" }
25
+ elseif (Get-Command scoop -ErrorAction SilentlyContinue) { $pkg_manager = "scoop" }
26
+
27
+ # --- Ensure git ---
28
+ $git_version = ""
29
+ try {
30
+ $git_out = & git --version 2>$null
31
+ if ($git_out) {
32
+ $git_version = ($git_out -replace 'git version ', '').Trim()
33
+ }
34
+ } catch {
35
+ Write-Host "[INFO] git not found. Installing via winget..." -ForegroundColor Yellow
36
+ if ($pkg_manager -eq "winget") {
37
+ & winget install Git.Git --silent --accept-package-agreements 2>$null
38
+ # Refresh PATH so git is available
39
+ $env:Path = [Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [Environment]::GetEnvironmentVariable("Path", "User")
40
+ $git_out = & git --version 2>$null
41
+ if ($git_out) { $git_version = ($git_out -replace 'git version ', '').Trim() }
42
+ } else {
43
+ Write-Host "[WARN] Cannot install git automatically. Install from https://git-scm.com/" -ForegroundColor Red
44
+ }
45
+ }
46
+
47
+ # --- Ensure Node.js (LTS via nvm-windows) ---
48
+ $node_version = ""
49
+ $nvm_version = ""
50
+
51
+ function Setup-Node {
52
+ # Check if node is already at LTS or later
53
+ try {
54
+ $current = & node --version 2>$null
55
+ if ($current) {
56
+ $current = $current -replace '^v', ''
57
+ $major = ($current -split '\.')[0]
58
+ if ($major -ge 18) {
59
+ $script:node_version = $current
60
+ Write-Host "[INFO] Node.js v$node_version already available" -ForegroundColor Green
61
+ return
62
+ }
63
+ }
64
+ } catch {
65
+ # node not found, continue
66
+ }
67
+
68
+ # Check for nvm-windows
69
+ $nvm_path = Get-Command nvm -ErrorAction SilentlyContinue
70
+ if (-not $nvm_path) {
71
+ Write-Host "[INFO] nvm-windows not found. Installing via winget..." -ForegroundColor Yellow
72
+ try {
73
+ if ($pkg_manager -eq "winget") {
74
+ & winget install CoreyButler.NVMforWindows --silent --accept-package-agreements 2>$null
75
+ # nvm-windows adds to PATH; need a new shell or reload
76
+ $env:Path = [Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [Environment]::GetEnvironmentVariable("Path", "User")
77
+ # nvm-windows may need a refresh
78
+ $env:NVM_HOME = "$env:USERPROFILE\AppData\Roaming\nvm"
79
+ $env:NVM_SYMLINK = "$env:ProgramFiles\nodejs"
80
+ $env:Path = "$env:NVM_HOME;$env:NVM_SYMLINK;$env:Path"
81
+ } else {
82
+ Write-Host "[WARN] Cannot install nvm-windows automatically. Install from:" -ForegroundColor Red
83
+ Write-Host " https://github.com/coreybutler/nvm-windows/releases" -ForegroundColor Red
84
+ return
85
+ }
86
+ } catch {
87
+ Write-Host "[ERROR] Failed to install nvm-windows." -ForegroundColor Red
88
+ return
89
+ }
90
+ }
91
+
92
+ # Verify nvm is available
93
+ try {
94
+ $nvm_ver = & nvm version 2>$null
95
+ if ($nvm_ver) {
96
+ $script:nvm_version = $nvm_ver.Trim()
97
+ Write-Host "[INFO] nvm-windows v$nvm_version found" -ForegroundColor Green
98
+ }
99
+ } catch {
100
+ Write-Host "[WARN] nvm command not available after install. Restart terminal and retry." -ForegroundColor Red
101
+ return
102
+ }
103
+
104
+ # Install LTS node
105
+ Write-Host "[INFO] Installing latest Node.js LTS via nvm-windows..." -ForegroundColor Yellow
106
+ try {
107
+ & nvm install lts 2>$null
108
+ & nvm use lts 2>$null
109
+ # Write .nvmrc for the project
110
+ $project_root = if (Test-Path ".git") { (Get-Location).Path } else { "." }
111
+ "--lts" | Out-File -FilePath "$project_root\.nvmrc" -Encoding utf8 -Force
112
+ $node_ver = & node --version 2>$null
113
+ if ($node_ver) {
114
+ $script:node_version = ($node_ver -replace '^v', '').Trim()
115
+ Write-Host "[INFO] Node.js v$node_version (LTS) ready via nvm-windows" -ForegroundColor Green
116
+ }
117
+ } catch {
118
+ Write-Host "[ERROR] Failed to install Node.js LTS." -ForegroundColor Red
119
+ }
120
+ }
121
+
122
+ Setup-Node
123
+
124
+ # --- Output JSON ---
125
+ $json = @{
126
+ os = $os
127
+ distro = $distro
128
+ version = $version
129
+ codename = $codename
130
+ pkg_manager = $pkg_manager
131
+ shell = $shell
132
+ is_wsl = $is_wsl
133
+ arch = $arch
134
+ node_version = $node_version
135
+ nvm_version = $nvm_version
136
+ git_version = $git_version
137
+ }
138
+
139
+ Write-Output ($json | ConvertTo-Json -Compress)