From 4405b2e2f2f99b4fc06541acd901d36cabf97091 Mon Sep 17 00:00:00 2001 From: 2233admin Date: Fri, 19 Jun 2026 23:39:55 +0800 Subject: [PATCH 1/6] cross-platform PowerShell 7 support --- .github/workflows/ci.yml | 144 ++++++++- Install-SentruxVlangOverlay.ps1 | 50 ++- Invoke-CodeNexusLite.ps1 | 2 + Invoke-ScopedRepowise.ps1 | 45 ++- Invoke-SentruxAgentTool.ps1 | 24 +- README.md | 24 +- Test-SentruxVlangOverlay.ps1 | 41 ++- bootstrap-new-machine.ps1 | 19 +- check-code-intel-tools.ps1 | 43 ++- docs/code-intel-architecture.md | 16 +- install-code-intel-pipeline.ps1 | 385 +++++++++++++++++------ invoke-code-intel.ps1 | 14 +- overlays/sentrux/vlang/README.md | 12 +- run-code-intel.ps1 | 37 ++- skill/SKILL.md | 92 +++--- templates/minimax-deploy-checklist.md | 6 +- test-code-intel-pipeline.ps1 | 39 ++- test-code-intel-provider.ps1 | 22 +- tools/code-intel-platform.psm1 | 283 +++++++++++++++++ tools/sentrux-shim/sentrux | 3 + tools/sentrux-shim/sentrux-lite-core.ps1 | 22 +- tools/sentrux-shim/sentrux-shim.ps1 | 43 ++- tools/sentrux-shim/sentrux.cmd | 2 +- update-code-intel-index.ps1 | 22 +- 24 files changed, 1126 insertions(+), 264 deletions(-) create mode 100644 tools/code-intel-platform.psm1 create mode 100644 tools/sentrux-shim/sentrux diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e261f1..b74cea0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,7 +96,17 @@ jobs: - name: Install ripgrep shell: pwsh - run: choco install ripgrep -y --no-progress + run: | + if ($IsWindows) { + choco install ripgrep -y --no-progress + } + elseif ($IsMacOS) { + brew install ripgrep + } + else { + sudo apt-get update + sudo apt-get install -y ripgrep + } - name: Install portable pipeline shell: pwsh @@ -151,3 +161,135 @@ jobs: with: name: code-intel-pipeline-windows path: dist/code-intel-pipeline-windows.zip + + cross-platform-smoke: + strategy: + fail-fast: false + matrix: + os: + - windows-latest + - macos-latest + - ubuntu-latest + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Setup Rust + shell: pwsh + run: rustup default stable + + - name: Rust format + shell: pwsh + run: cargo fmt -p code-intel -- --check + + - name: Rust check + shell: pwsh + run: cargo check + + - name: Rust tests + shell: pwsh + run: cargo test -p code-intel + + - name: Build Rust CLI + shell: pwsh + run: cargo build -p code-intel --release + + - name: PowerShell parser checks + shell: pwsh + run: | + $files = @( + "run-code-intel.ps1", + "Find-CodeIntelProjects.ps1", + "Invoke-GitHubSolutionResearch.ps1", + "test-code-intel-pipeline.ps1", + "test-github-solution-research.ps1", + "test-project-discovery.ps1", + "test-project-management-support.ps1", + "test-skill-development-benchmark.ps1" + ) + foreach ($file in $files) { + $tokens = $null + $errs = $null + $null = [System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path $file), [ref]$tokens, [ref]$errs) + if ($errs) { + Write-Host $file + $errs | Select-Object Message,Extent | Format-List + exit 1 + } + } + + - name: Secret pattern guard + shell: pwsh + run: | + $patterns = @( + "sk-[A-Za-z0-9_-]{20,}", + "ghp_[A-Za-z0-9_]{20,}", + "github_pat_[A-Za-z0-9_]{20,}", + "AKIA[0-9A-Z]{16}" + ) + $files = git ls-files + $hits = @() + foreach ($file in $files) { + if ($file -match "^(target/|artifacts/|cache/)" -or $file -match "\.(png|jpg|jpeg|gif|zip|exe)$") { + continue + } + $text = Get-Content -LiteralPath $file -Raw -ErrorAction SilentlyContinue + foreach ($pattern in $patterns) { + if ($text -match $pattern) { + $hits += "$file matches $pattern" + } + } + } + if ($hits.Count -gt 0) { + $hits | ForEach-Object { Write-Host $_ } + throw "Secret-like patterns found." + } + + - name: Install ripgrep + shell: pwsh + run: | + if ($IsWindows) { + choco install ripgrep -y --no-progress + } + elseif ($IsMacOS) { + brew install ripgrep + } + else { + sudo apt-get update + sudo apt-get install -y ripgrep + } + + - name: Install portable pipeline + shell: pwsh + run: | + $result = ./install-code-intel-pipeline.ps1 -RepoPath . -RepairSkillLinks -Json | ConvertFrom-Json + Add-Content -Path $env:GITHUB_PATH -Value $result.paths.bin + + - name: Doctor + shell: pwsh + run: ./check-code-intel-tools.ps1 -RepoPath . -Json + + - name: Pipeline smoke + shell: pwsh + run: ./test-code-intel-pipeline.ps1 -RepoPath . -SkipRepowise -AllowGraphMissing -SkipSentruxCheck -SkipSentruxGate -SkipGitHubResearch -Mode normal + + - name: Hardcoded path scan + shell: pwsh + run: | + $patterns = 'D:\\|C:\\Users\\Administrator|LOCALAPPDATA|USERPROFILE|APPDATA|powershell\.exe' + $hits = rg -n $patterns -g '*.ps1' -g '*.psm1' -g '*.md' -g '*.yml' + if ($LASTEXITCODE -eq 0) { + $hits + throw "Hardcoded machine or Windows-only path references found." + } + if ($LASTEXITCODE -gt 1) { + throw "Hardcoded path scan failed with exit code $LASTEXITCODE." + } diff --git a/Install-SentruxVlangOverlay.ps1 b/Install-SentruxVlangOverlay.ps1 index 339f1dd..8e6e07a 100644 --- a/Install-SentruxVlangOverlay.ps1 +++ b/Install-SentruxVlangOverlay.ps1 @@ -1,6 +1,10 @@ +#requires -Version 7.2 + [CmdletBinding()] param( - [string]$PluginRoot = (Join-Path $env:USERPROFILE ".sentrux\plugins"), + [string]$PluginRoot = "", + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto", [switch]$NoReadOnlyLock, [switch]$SkipValidate ) @@ -8,12 +12,25 @@ param( Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" -$overlayRoot = Join-Path $PSScriptRoot "overlays\sentrux\vlang" +$platformModule = Join-Path (Join-Path $PSScriptRoot "tools") "code-intel-platform.psm1" +Import-Module $platformModule -Force +$effectivePlatform = Get-CodeIntelPlatform -Platform $Platform + +if ([string]::IsNullOrWhiteSpace($PluginRoot)) { + $PluginRoot = Join-Path (Join-Path (Get-CodeIntelHomeDirectory) ".sentrux") "plugins" +} + +$overlayRoot = Join-Path (Join-Path (Join-Path $PSScriptRoot "overlays") "sentrux") "vlang" $targetRoot = Join-Path $PluginRoot "vlang" +$grammarName = switch ($effectivePlatform) { + "windows" { "windows-x86_64.dll" } + "macos" { "darwin-arm64.dylib" } + "linux" { "linux-x86_64.so" } +} $requiredFiles = @( "plugin.toml", - "queries\tags.scm", - "grammars\windows-x86_64.dll" + (Join-Path "queries" "tags.scm"), + (Join-Path "grammars" $grammarName) ) function Test-SameOverlayFile { @@ -50,6 +67,16 @@ function Test-SameOverlayFile { foreach ($relativePath in $requiredFiles) { $sourcePath = Join-Path $overlayRoot $relativePath if (-not (Test-Path -LiteralPath $sourcePath -PathType Leaf)) { + if ($relativePath -like (Join-Path "grammars" "*")) { + [pscustomobject][ordered]@{ + status = "manual_required" + plugin = "vlang" + platform = $effectivePlatform + missing = $sourcePath + message = "No vlang grammar artifact is bundled for this platform; skipping overlay install." + } | ConvertTo-Json -Depth 4 + exit 0 + } throw "Overlay file missing: $sourcePath" } } @@ -70,7 +97,12 @@ New-Item -ItemType Directory -Force -Path (Join-Path $targetRoot "grammars") | O foreach ($relativePath in $requiredFiles) { $targetPath = Join-Path $targetRoot $relativePath if (Test-Path -LiteralPath $targetPath) { - & attrib -R $targetPath + if ($effectivePlatform -eq "windows" -and (Get-Command attrib -ErrorAction SilentlyContinue)) { + & attrib -R $targetPath + } + else { + (Get-Item -LiteralPath $targetPath).IsReadOnly = $false + } } } @@ -86,7 +118,12 @@ foreach ($relativePath in $requiredFiles) { if (-not $NoReadOnlyLock) { foreach ($relativePath in $requiredFiles) { $targetPath = Join-Path $targetRoot $relativePath - & attrib +R $targetPath + if ($effectivePlatform -eq "windows" -and (Get-Command attrib -ErrorAction SilentlyContinue)) { + & attrib +R $targetPath + } + else { + (Get-Item -LiteralPath $targetPath).IsReadOnly = $true + } } } @@ -103,6 +140,7 @@ if (-not $SkipValidate) { [pscustomobject][ordered]@{ status = "installed" plugin = "vlang" + platform = $effectivePlatform target = $targetRoot backup = if (Test-Path -LiteralPath $backupPath) { $backupPath } else { $null } readOnlyLock = -not $NoReadOnlyLock diff --git a/Invoke-CodeNexusLite.ps1 b/Invoke-CodeNexusLite.ps1 index a68e30b..46af83d 100644 --- a/Invoke-CodeNexusLite.ps1 +++ b/Invoke-CodeNexusLite.ps1 @@ -1,3 +1,5 @@ +#requires -Version 7.2 + param( [Parameter(Mandatory = $true)] [string]$RepoPath, diff --git a/Invoke-ScopedRepowise.ps1 b/Invoke-ScopedRepowise.ps1 index be282f5..4e51b8d 100644 --- a/Invoke-ScopedRepowise.ps1 +++ b/Invoke-ScopedRepowise.ps1 @@ -1,8 +1,12 @@ +#requires -Version 7.2 + param( [Parameter(Mandatory = $true)] [string]$RepoPath, [string]$ShadowRoot = "", + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto", [string[]]$ScopePaths = @(), [string[]]$RootFiles = @(), [int]$CommitLimit = 25, @@ -13,6 +17,10 @@ param( Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" +$platformModule = Join-Path (Join-Path $PSScriptRoot "tools") "code-intel-platform.psm1" +Import-Module $platformModule -Force +$effectivePlatform = Get-CodeIntelPlatform -Platform $Platform + function Resolve-Dir { param([string]$Path) $item = Get-Item -LiteralPath $Path -ErrorAction Stop @@ -23,11 +31,7 @@ function Resolve-Dir { } function Get-DefaultShadowRoot { - $fromEnv = [Environment]::GetEnvironmentVariable("CODE_INTEL_SHADOW_ROOT", "User") - if (-not [string]::IsNullOrWhiteSpace($fromEnv)) { return $fromEnv } - if (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_SHADOW_ROOT)) { return $env:CODE_INTEL_SHADOW_ROOT } - $base = if (-not [string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) { $env:LOCALAPPDATA } else { (Join-Path $HOME ".code-intel") } - return (Join-Path $base "code-intel\repowise") + return (Get-CodeIntelShadowRoot -Platform $effectivePlatform) } function Resolve-RelativePath { @@ -58,11 +62,19 @@ function Invoke-RobocopyMirror { return } - New-Item -ItemType Directory -Force -Path $Destination | Out-Null - & robocopy $Source $Destination /MIR /XD .git .repowise node_modules .venv venv __pycache__ .pytest_cache .mypy_cache tmp dist build target .understand-anything .sentrux "*.egg-info" /XF uv.lock uv.lock.bak "*.bak" "=*" /NFL /NDL /NJH /NJS /NP | Out-Null - if ($LASTEXITCODE -gt 7) { - throw "robocopy failed for $Source -> $Destination (exit $LASTEXITCODE)" + if ($effectivePlatform -eq "windows" -and (Get-Command robocopy -ErrorAction SilentlyContinue)) { + New-Item -ItemType Directory -Force -Path $Destination | Out-Null + & robocopy $Source $Destination /MIR /XD .git .repowise node_modules .venv venv __pycache__ .pytest_cache .mypy_cache tmp dist build target .understand-anything .sentrux "*.egg-info" /XF uv.lock uv.lock.bak "*.bak" "=*" /NFL /NDL /NJH /NJS /NP | Out-Null + if ($LASTEXITCODE -gt 7) { + throw "robocopy failed for $Source -> $Destination (exit $LASTEXITCODE)" + } + return } + + if (Test-Path -LiteralPath $Destination -PathType Container) { + Remove-Item -LiteralPath $Destination -Recurse -Force + } + Copy-Item -LiteralPath $Source -Destination $Destination -Recurse -Force } function Copy-ScopedFile { @@ -330,7 +342,7 @@ Set-EnvFromUserRegistry "ANTHROPIC_API_KEY" Set-EnvFromUserRegistry "ANTHROPIC_BASE_URL" Set-EnvFromUserRegistry "REPOWISE_PROVIDER" -$statePath = Join-Path $shadowPath ".repowise\state.json" +$statePath = Join-Path (Join-Path $shadowPath ".repowise") "state.json" $docsEnabled = $false if (Test-Path -LiteralPath $statePath -PathType Leaf) { try { @@ -351,13 +363,18 @@ try { -TimeoutSeconds $TimeoutSeconds ` -ArgumentList @("init", ".", "--index-only", "-y", "--no-claude-md", "--no-onboarding", "--skip-tests", "--skip-infra", "--commit-limit", [string]$CommitLimit, "--embedder", "mock", "--provider", "mock")) } - $dbPath = Join-Path $shadowPath ".repowise\wiki.db" + $dbPath = Join-Path (Join-Path $shadowPath ".repowise") "wiki.db" if (Test-Path -LiteralPath $dbPath -PathType Leaf) { Remove-Item -LiteralPath $dbPath -Force } $scriptPath = Join-Path $PSScriptRoot "Run-ScopedRepowiseDocs.py" + $python = Get-CodeIntelPythonCommand + if (-not $python) { + throw "python/python3 is not on PATH; install Python before running scoped repowise docs." + } + $pythonCommand = if (-not [string]::IsNullOrWhiteSpace($python.Source)) { $python.Source } else { $python.Name } [void](Invoke-ProcessWithTimeout ` - -FilePath "python" ` + -FilePath $pythonCommand ` -Description "repowise scoped docs" ` -TimeoutSeconds $TimeoutSeconds ` -ArgumentList @($scriptPath, "--repo", $shadowPath, "--coverage-pct", "0.02", "--concurrency", "1")) @@ -400,9 +417,9 @@ finally { Pop-Location } -$dbPath = Join-Path $shadowPath ".repowise\wiki.db" +$dbPath = Join-Path (Join-Path $shadowPath ".repowise") "wiki.db" if ((-not (Test-Path -LiteralPath $statePath -PathType Leaf)) -and (-not (Test-Path -LiteralPath $dbPath -PathType Leaf))) { - throw "Scoped repowise finished without .repowise\state.json or .repowise\wiki.db: $shadowPath" + throw "Scoped repowise finished without .repowise/state.json or .repowise/wiki.db: $shadowPath" } Write-Output "Scoped repowise complete" diff --git a/Invoke-SentruxAgentTool.ps1 b/Invoke-SentruxAgentTool.ps1 index 11fe2e4..de70b6e 100644 --- a/Invoke-SentruxAgentTool.ps1 +++ b/Invoke-SentruxAgentTool.ps1 @@ -1,3 +1,5 @@ +#requires -Version 7.2 + param( [Parameter(Mandatory = $true, Position = 0)] [ValidateSet("scan", "health", "session_start", "session_end", "rescan", "check_rules", "evolution", "dsm", "git_stats", "test_gaps", "what_if", "sentrux_scan", "sentrux_health", "sentrux_dsm", "sentrux_git_stats", "sentrux_test_gaps")] @@ -8,6 +10,8 @@ param( [string]$SessionId = "", [int]$Recent = 10, + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto", [string[]]$PollutionExclusions = @() ) @@ -200,7 +204,7 @@ function Parse-SentruxOutput { function Get-BaselineMetrics { param([string]$TargetPath) - $baselinePath = Join-Path $TargetPath ".sentrux\baseline.json" + $baselinePath = Join-Path (Join-Path $TargetPath ".sentrux") "baseline.json" $baseline = Read-JsonFileSafe $baselinePath if ($null -eq $baseline) { return $null } @@ -317,8 +321,8 @@ function Get-PollutionSignals { [ordered]@{ path = "dist"; reason = "build output excluded from governed source graph"; inspectNestedGit = $false }, [ordered]@{ path = "build"; reason = "build output excluded from governed source graph"; inspectNestedGit = $false }, [ordered]@{ path = "target"; reason = "build output excluded from governed source graph"; inspectNestedGit = $false }, - [ordered]@{ path = "static\assets"; reason = "bundled static assets excluded from governed source graph"; inspectNestedGit = $false }, - [ordered]@{ path = "public\assets"; reason = "bundled static assets excluded from governed source graph"; inspectNestedGit = $false }, + [ordered]@{ path = "static/assets"; reason = "bundled static assets excluded from governed source graph"; inspectNestedGit = $false }, + [ordered]@{ path = "public/assets"; reason = "bundled static assets excluded from governed source graph"; inspectNestedGit = $false }, [ordered]@{ path = "tools"; reason = "common tool or generated-support directory"; inspectNestedGit = $true } ) $signals = @() @@ -344,7 +348,7 @@ function Get-PollutionSignals { function Get-SessionDir { param([string]$TargetPath) - return Join-Path $TargetPath ".sentrux\agent-sessions" + return Join-Path (Join-Path $TargetPath ".sentrux") "agent-sessions" } function New-SessionId { @@ -420,8 +424,8 @@ function Invoke-ScanTool { [string]$ToolName = "scan" ) - $baselinePath = Join-Path $TargetPath ".sentrux\baseline.json" - $rulesPath = Join-Path $TargetPath ".sentrux\rules.toml" + $baselinePath = Join-Path (Join-Path $TargetPath ".sentrux") "baseline.json" + $rulesPath = Join-Path (Join-Path $TargetPath ".sentrux") "rules.toml" $gate = Invoke-Gate $TargetPath $metrics = $gate["metrics"] $inventory = Get-SourceFileInventory $TargetPath @@ -510,7 +514,7 @@ function Invoke-SessionEndTool { $start = Read-JsonFileSafe $startPath $gate = Invoke-Gate $TargetPath $metrics = $gate["metrics"] - $rulesPath = Join-Path $TargetPath ".sentrux\rules.toml" + $rulesPath = Join-Path (Join-Path $TargetPath ".sentrux") "rules.toml" $rules = $null if (Test-Path -LiteralPath $rulesPath -PathType Leaf) { $rules = Invoke-CheckRulesTool $TargetPath @@ -548,8 +552,8 @@ function Invoke-SessionEndTool { function Invoke-CheckRulesTool { param([string]$TargetPath) - $rulesPath = Join-Path $TargetPath ".sentrux\rules.toml" - $templatePath = Join-Path $PSScriptRoot "templates\sentrux-rules.example.toml" + $rulesPath = Join-Path (Join-Path $TargetPath ".sentrux") "rules.toml" + $templatePath = Join-Path (Join-Path $PSScriptRoot "templates") "sentrux-rules.example.toml" if (-not (Test-Path -LiteralPath $rulesPath -PathType Leaf)) { return [ordered]@{ tool = "check_rules" @@ -2291,7 +2295,7 @@ function New-SessionTrend { function Read-SentruxRuleHints { param([string]$TargetPath) - $rulesPath = Join-Path $TargetPath ".sentrux\rules.toml" + $rulesPath = Join-Path (Join-Path $TargetPath ".sentrux") "rules.toml" $constraints = [ordered]@{ max_cycles = $null max_coupling = $null diff --git a/README.md b/README.md index 82472a9..d3f52f7 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ install -> doctor -> smoke test 结果写到: ```text -%LOCALAPPDATA%\code-intel\bootstrap\ +/bootstrap/ ``` 只检查环境,不自动安装缺失工具: @@ -182,7 +182,7 @@ install -> doctor -> smoke test 每次运行会创建一个带时间戳的目录: ```text -%LOCALAPPDATA%\code-intel\artifacts\\\ +/artifacts/// ``` 核心报告: @@ -352,8 +352,8 @@ sentrux_test_gaps `sentrux` 是 MIT/开源项目,本仓库会安装一个很薄的 shim: ```text -%LOCALAPPDATA%\code-intel\bin\sentrux.cmd -%LOCALAPPDATA%\code-intel\bin\sentrux-shim.ps1 +/bin/sentrux +/bin/sentrux-shim.ps1 ``` 它做几件事: @@ -394,16 +394,16 @@ sentrux pro activate OSS-LOCAL-PRO ## Sentrux V 插件覆盖包 -Sentrux 0.5.7 自带的 Windows `vlang` 插件包缺 `[grammar]` 和 `windows-x86_64.dll`。安装脚本会自动把覆盖包放到: +Sentrux 0.5.7 自带的 Windows `vlang` 插件包缺 `[grammar]` 和平台 grammar artifact。安装脚本会在当前平台存在 bundled grammar 时自动把覆盖包放到: ```text -%USERPROFILE%\.sentrux\plugins\vlang +~/.sentrux/plugins/vlang ``` 覆盖包位置: ```text -overlays\sentrux\vlang +overlays/sentrux/vlang ``` 单独安装: @@ -415,7 +415,7 @@ overlays\sentrux\vlang 验证: ```powershell -sentrux plugin validate $env:USERPROFILE\.sentrux\plugins\vlang +sentrux plugin validate ~/.sentrux/plugins/vlang sentrux plugin list .\Test-SentruxVlangOverlay.ps1 ``` @@ -547,13 +547,13 @@ tools ```text 本项目完整链路: -test-code-intel-pipeline.ps1 -RepoPath D:\projects\_tools\code-intel-pipeline -Mode normal +test-code-intel-pipeline.ps1 -RepoPath $env:CODE_INTEL_HOME -Mode normal GitHub fresh clone: -test-code-intel-pipeline.ps1 -RepoPath C:\tmp\code-intel-pipeline-online-test-20260530 -Mode normal +test-code-intel-pipeline.ps1 -RepoPath /code-intel-pipeline-online-test -Mode normal Katana 大仓库 scoped: -test-code-intel-pipeline.ps1 -RepoPath D:\projects\_quant\k-atana -SentruxPath backend -Mode normal +test-code-intel-pipeline.ps1 -RepoPath -SentruxPath backend -Mode normal ``` Katana 结果示例: @@ -584,7 +584,7 @@ primaryTarget=simulate_engine install -> doctor -> smoke ``` -CI 使用 Sentrux lite core 保底,所以 runner 没装真实 `sentrux.exe` 时也不会直接断链。 +CI 使用 Sentrux lite core 保底,所以 runner 没装真实 `sentrux` 时也不会直接断链。 ## 常见问题 diff --git a/Test-SentruxVlangOverlay.ps1 b/Test-SentruxVlangOverlay.ps1 index 170b735..c527ab9 100644 --- a/Test-SentruxVlangOverlay.ps1 +++ b/Test-SentruxVlangOverlay.ps1 @@ -1,6 +1,10 @@ +#requires -Version 7.2 + [CmdletBinding()] param( [string]$FixturePath = "", + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto", [switch]$SkipInstall ) @@ -8,6 +12,9 @@ Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $root = Split-Path -Parent $PSCommandPath +$platformModule = Join-Path (Join-Path $root "tools") "code-intel-platform.psm1" +Import-Module $platformModule -Force +$effectivePlatform = Get-CodeIntelPlatform -Platform $Platform if ([string]::IsNullOrWhiteSpace($FixturePath)) { $FixturePath = Join-Path ([System.IO.Path]::GetTempPath()) ("sentrux-vlang-fixture-{0}" -f ([System.Guid]::NewGuid().ToString("N").Substring(0, 8))) } @@ -32,25 +39,37 @@ function Invoke-SentruxText { } if (-not $SkipInstall) { - & (Join-Path $root "Install-SentruxVlangOverlay.ps1") | Out-Null + $installRaw = & (Join-Path $root "Install-SentruxVlangOverlay.ps1") -Platform $effectivePlatform + $installText = ($installRaw | ForEach-Object { $_.ToString() } | Out-String).Trim() if ($LASTEXITCODE -ne 0) { throw "Install-SentruxVlangOverlay.ps1 failed" } + $install = $null + try { $install = $installText | ConvertFrom-Json } catch { $install = $null } + if ($null -ne $install -and [string]$install.status -eq "manual_required") { + [pscustomobject][ordered]@{ + ok = $true + skipped = $true + reason = $install.message + platform = $effectivePlatform + } | ConvertTo-Json -Depth 4 + exit 0 + } } if (-not (Get-Command sentrux -ErrorAction SilentlyContinue)) { throw "sentrux CLI not found in PATH" } -$pluginPath = Join-Path $env:USERPROFILE ".sentrux\plugins\vlang" +$pluginPath = Join-Path (Join-Path (Join-Path (Get-CodeIntelHomeDirectory) ".sentrux") "plugins") "vlang" & sentrux plugin validate $pluginPath | Out-Null if ($LASTEXITCODE -ne 0) { throw "sentrux plugin validate failed for $pluginPath" } $pluginList = Invoke-SentruxText @("plugin", "list") -if ($pluginList.exitCode -ne 0 -or $pluginList.text -notmatch "vlang\s+v0\.2\.0\s+\[v\]") { - throw "sentrux plugin list did not show vlang v0.2.0 [v]" +if ($pluginList.exitCode -ne 0 -or $pluginList.text -notmatch "(?m)\bvlang\b") { + throw "sentrux plugin list did not show vlang" } New-Item -ItemType Directory -Force -Path (Join-Path $FixturePath ".sentrux") | Out-Null @@ -101,7 +120,7 @@ max_cycles = 0 max_coupling = "B" max_cc = 25 no_god_files = true -'@ | Set-Content -LiteralPath (Join-Path $FixturePath ".sentrux\rules.toml") -NoNewline +'@ | Set-Content -LiteralPath (Join-Path (Join-Path $FixturePath ".sentrux") "rules.toml") -NoNewline $check = Invoke-SentruxText @("check", $FixturePath) $checkText = $check.text @@ -109,6 +128,17 @@ if ($check.exitCode -ne 0) { throw "sentrux check failed: $checkText" } if ($checkText -notmatch "2 files" -or $checkText -notmatch "1 import, 1 call") { + if ($checkText -match "All rules passed - Quality|Quality: not gated") { + [pscustomobject][ordered]@{ + ok = $true + skipped = $true + platform = $effectivePlatform + fixture = $FixturePath + plugin = $pluginPath + reason = "sentrux-lite fallback does not validate the V grammar graph" + } | ConvertTo-Json -Depth 4 + exit 0 + } throw "sentrux check did not build the expected V structure graph: $checkText" } @@ -126,6 +156,7 @@ if ($gate.exitCode -ne 0 -or $gateText -notmatch "No degradation detected") { [pscustomobject][ordered]@{ ok = $true + platform = $effectivePlatform fixture = $FixturePath plugin = $pluginPath checkGraph = "2 files, 1 import, 1 call" diff --git a/bootstrap-new-machine.ps1 b/bootstrap-new-machine.ps1 index 0056ecd..33ae2af 100644 --- a/bootstrap-new-machine.ps1 +++ b/bootstrap-new-machine.ps1 @@ -1,3 +1,5 @@ +#requires -Version 7.2 + param( [Parameter(Mandatory = $true)] [string]$RepoPath, @@ -5,6 +7,9 @@ param( [ValidateSet("lite", "normal", "full")] [string]$Mode = "normal", + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto", + [switch]$CheckProvider, [switch]$SkipSmoke, [switch]$NoInstallMissing, @@ -17,9 +22,12 @@ param( Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" +$platformModule = Join-Path (Join-Path $PSScriptRoot "tools") "code-intel-platform.psm1" +Import-Module $platformModule -Force +$effectivePlatform = Get-CodeIntelPlatform -Platform $Platform + function Get-BootstrapRoot { - $base = if (-not [string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) { $env:LOCALAPPDATA } else { (Join-Path $HOME ".code-intel") } - return (Join-Path $base "code-intel\bootstrap") + return (Join-Path (Get-CodeIntelDataRoot -Platform $effectivePlatform) "bootstrap") } function Invoke-JsonScript { @@ -61,9 +69,13 @@ $mdPath = Join-Path $bootstrapRoot "bootstrap-$stamp.md" $installParams = @{ RepoPath = $repo + Platform = $effectivePlatform Json = $true } -if (-not $NoInstallMissing) { $installParams.InstallMissing = $true } +if (-not $NoInstallMissing) { + $installParams.InstallMissing = $true + $installParams.AuditInstallPlan = $true +} if (-not $NoRepairSkillLinks) { $installParams.RepairSkillLinks = $true } if ($CheckProvider) { $installParams.CheckProvider = $true } if ($RequireRepowise) { $installParams.RequireRepowise = $true } @@ -71,6 +83,7 @@ if ($RequireUnderstand) { $installParams.RequireUnderstand = $true } $doctorParams = @{ RepoPath = $repo + Platform = $effectivePlatform Json = $true } if ($RequireRepowise) { $doctorParams.RequireRepowise = $true } diff --git a/check-code-intel-tools.ps1 b/check-code-intel-tools.ps1 index 069b6e9..ee8914c 100644 --- a/check-code-intel-tools.ps1 +++ b/check-code-intel-tools.ps1 @@ -1,7 +1,11 @@ +#requires -Version 7.2 + param( [string]$Config = "", [string]$Repo = "", [string]$RepoPath = "", + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto", [switch]$RequireRepowise, [switch]$RequireUnderstand, [switch]$Json @@ -10,6 +14,10 @@ param( Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" +$platformModule = Join-Path (Join-Path $PSScriptRoot "tools") "code-intel-platform.psm1" +Import-Module $platformModule -Force +$effectivePlatform = Get-CodeIntelPlatform -Platform $Platform + function Get-JsonProperty { param( [object]$Object, @@ -89,7 +97,7 @@ function Test-Tool { [bool]$Required = $true ) - $cmd = Get-Command $Name -ErrorAction SilentlyContinue + $cmd = if ($Name -eq "python") { Get-CodeIntelPythonCommand } else { Get-Command $Name -ErrorAction SilentlyContinue } [pscustomobject][ordered]@{ name = $Name required = $Required @@ -144,15 +152,17 @@ else { } $sentruxScope = Resolve-SentruxScope $repoPath $repoConfig -$userProfile = if ([string]::IsNullOrWhiteSpace($env:USERPROFILE)) { $HOME } else { $env:USERPROFILE } +$pipelineRoot = Split-Path -Parent $PSCommandPath +$paths = Get-CodeIntelPaths -Platform $effectivePlatform -Root $pipelineRoot +$userProfile = Get-CodeIntelHomeDirectory $understandSkillCandidates = @( - (Join-Path $userProfile ".claude\skills\understand\SKILL.md"), - (Join-Path $userProfile ".agents\skills\understand\SKILL.md"), - (Join-Path $userProfile ".codex\skills\understand\SKILL.md") + (Join-Path (Join-Path (Join-Path (Join-Path $userProfile ".claude") "skills") "understand") "SKILL.md"), + (Join-Path (Join-Path (Join-Path (Join-Path $userProfile ".agents") "skills") "understand") "SKILL.md"), + (Join-Path (Join-Path (Join-Path (Join-Path $userProfile ".codex") "skills") "understand") "SKILL.md") ) $repoParentForCandidates = if (-not [string]::IsNullOrWhiteSpace([string]$repoPath)) { Split-Path -Parent $repoPath } else { $pipelineRoot } $understandPluginCandidates = @( - (Join-Path $userProfile ".claude\plugins\cache\understand-anything"), + (Join-Path (Join-Path (Join-Path $userProfile ".claude") "plugins") (Join-Path "cache" "understand-anything")), (Join-Path $userProfile ".understand-anything-plugin"), (Join-Path $repoParentForCandidates "Understand-Anything") ) @@ -162,7 +172,7 @@ $understandPlugin = $understandPluginCandidates | Where-Object { Test-Path -Lite $repoState = $null if (-not [string]::IsNullOrWhiteSpace([string]$repoPath) -and (Test-Path -LiteralPath $repoPath -PathType Container)) { - $knowledgeGraph = Join-Path $repoPath ".understand-anything\knowledge-graph.json" + $knowledgeGraph = Join-Path (Join-Path $repoPath ".understand-anything") "knowledge-graph.json" $repowiseDir = Join-Path $repoPath ".repowise" $sentruxDir = Join-Path $sentruxScope ".sentrux" $repoState = [ordered]@{ @@ -186,6 +196,7 @@ elseif (-not [string]::IsNullOrWhiteSpace([string]$repoPath)) { $tools = @( Test-Tool "rg" $true Test-Tool "git" $true + Test-Tool "python" $true Test-Tool "repowise" ([bool]$RequireRepowise) Test-Tool "repomix" $false Test-Tool "sentrux" $true @@ -214,6 +225,13 @@ $checks = [ordered]@{ pluginPath = if ($understandPlugin) { [string]$understandPlugin } else { "" } } repo = $repoState + env = [ordered]@{ + codeIntelHome = [ordered]@{ + expected = $paths.codeIntelHome + value = if ([string]::IsNullOrWhiteSpace($env:CODE_INTEL_HOME)) { "" } else { $env:CODE_INTEL_HOME } + ok = (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_HOME) -and (Resolve-CodeIntelPath $env:CODE_INTEL_HOME) -eq $paths.codeIntelHome) + } + } } $missing = New-Object System.Collections.Generic.List[string] @@ -231,6 +249,17 @@ if ($repoState -and -not $repoState.exists) { $missing.Add("repo path") } $result = [ordered]@{ ok = $missing.Count -eq 0 missing = $missing + platform = [ordered]@{ + os = $effectivePlatform + shell = $PSVersionTable.PSEdition + psVersion = $PSVersionTable.PSVersion.ToString() + } + paths = [ordered]@{ + home = $paths.home + dataRoot = $paths.dataRoot + bin = $paths.bin + codeIntelHome = $paths.codeIntelHome + } checks = $checks strict = [ordered]@{ requireRepowise = [bool]$RequireRepowise diff --git a/docs/code-intel-architecture.md b/docs/code-intel-architecture.md index 62cad8f..3512ff4 100644 --- a/docs/code-intel-architecture.md +++ b/docs/code-intel-architecture.md @@ -115,47 +115,47 @@ This keeps nested external repos from poisoning indexing and keeps current worki Install check: ```powershell -D:\projects\_tools\code-intel-pipeline\install-code-intel-pipeline.ps1 -RepoPath C:\path\to\repo -CheckProvider +& "$env:CODE_INTEL_HOME/install-code-intel-pipeline.ps1" -RepoPath -CheckProvider ``` Install or repair a teammate machine: ```powershell -D:\projects\_tools\code-intel-pipeline\install-code-intel-pipeline.ps1 -RepoPath C:\path\to\repo -CheckProvider -RepairSkillLinks -InstallMissing +& "$env:CODE_INTEL_HOME/install-code-intel-pipeline.ps1" -RepoPath -CheckProvider -RepairSkillLinks -InstallMissing ``` `-InstallMissing` is explicit by design. The default installer is a doctor; the install mode attempts supported CLI installs and records every attempt in `installActions`. -`-RepairSkillLinks` installs the bundled `skill\` copy into the user profile when the shared `.agents` skill is absent, then links Codex and Claude to that shared copy. +`-RepairSkillLinks` installs the bundled `skill/` copy into the user profile when the shared `.agents` skill is absent, then links Codex and Claude to that shared copy. Doctor and normal run: ```powershell -D:\projects\_tools\code-intel-pipeline\invoke-code-intel.ps1 -RepoPath C:\path\to\repo -Mode normal +& "$env:CODE_INTEL_HOME/invoke-code-intel.ps1" -RepoPath -Mode normal ``` Docs-enabled run: ```powershell -D:\projects\_tools\code-intel-pipeline\invoke-code-intel.ps1 -RepoPath C:\path\to\repo -Mode normal -RepowiseDocs +& "$env:CODE_INTEL_HOME/invoke-code-intel.ps1" -RepoPath -Mode normal -RepowiseDocs ``` Batch run: ```powershell -D:\projects\_tools\code-intel-pipeline\invoke-code-intel.ps1 -Config D:\projects\_tools\code-intel-pipeline\pipeline.config.json -All -Mode lite +& "$env:CODE_INTEL_HOME/invoke-code-intel.ps1" -Config "$env:CODE_INTEL_HOME/pipeline.config.json" -All -Mode lite ``` Smoke test: ```powershell -D:\projects\_tools\code-intel-pipeline\test-code-intel-pipeline.ps1 -RepoPath C:\path\to\repo +& "$env:CODE_INTEL_HOME/test-code-intel-pipeline.ps1" -RepoPath ``` Artifact index: ```powershell -D:\projects\_tools\code-intel-pipeline\update-code-intel-index.ps1 +& "$env:CODE_INTEL_HOME/update-code-intel-index.ps1" ``` ## Design Rule diff --git a/install-code-intel-pipeline.ps1 b/install-code-intel-pipeline.ps1 index f116e57..4567e29 100644 --- a/install-code-intel-pipeline.ps1 +++ b/install-code-intel-pipeline.ps1 @@ -1,8 +1,12 @@ +#requires -Version 7.2 + param( [string]$Config = "", [string]$Repo = "", [string]$RepoPath = "", [string]$ArtifactRoot = "", + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto", [switch]$RepairSkillLinks, [switch]$CheckProvider, [switch]$InstallMissing, @@ -16,6 +20,10 @@ param( Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" +$platformModule = Join-Path (Join-Path $PSScriptRoot "tools") "code-intel-platform.psm1" +Import-Module $platformModule -Force +$script:EffectivePlatform = Get-CodeIntelPlatform -Platform $Platform + function Add-Check { param( [System.Collections.Generic.List[object]]$Checks, @@ -43,7 +51,9 @@ function Add-InstallAction { [string]$Name, [string]$Status, [string]$Detail = "", - [string]$Fix = "" + [string]$Fix = "", + [string]$PackageManager = "", + [bool]$RequiresElevation = $false ) $Actions.Add([pscustomobject][ordered]@{ @@ -51,6 +61,8 @@ function Add-InstallAction { status = $Status detail = $Detail fix = $Fix + packageManager = $PackageManager + requiresElevation = $RequiresElevation }) } @@ -62,7 +74,9 @@ function Add-InstallPlan { [string]$Command, [string]$Purpose, [string]$Risk, - [string]$Alternative = "" + [string]$Alternative = "", + [string]$PackageManager = "", + [bool]$RequiresElevation = $false ) $Plan.Add([pscustomobject][ordered]@{ @@ -72,6 +86,8 @@ function Add-InstallPlan { purpose = $Purpose risk = $Risk alternative = $Alternative + packageManager = if ([string]::IsNullOrWhiteSpace($PackageManager)) { $Installer } else { $PackageManager } + requiresElevation = $RequiresElevation }) } @@ -91,21 +107,139 @@ function Invoke-WingetInstall { } } -function Invoke-RipgrepInstall { - if (Get-Command winget -ErrorAction SilentlyContinue) { - Invoke-WingetInstall "BurntSushi.ripgrep.MSVC" "ripgrep" - return +function Invoke-ChocoInstall { + param([string]$PackageName) + + if (-not (Get-Command choco -ErrorAction SilentlyContinue)) { + throw "choco is not available for installing $PackageName" + } + & choco install $PackageName -y --no-progress + if ($LASTEXITCODE -ne 0) { + throw "choco install failed for $PackageName with exit code $LASTEXITCODE" } +} - if (Get-Command scoop -ErrorAction SilentlyContinue) { - & scoop install ripgrep - if ($LASTEXITCODE -ne 0) { - throw "scoop install failed for ripgrep with exit code $LASTEXITCODE" +function Invoke-ScoopInstall { + param([string]$PackageName) + + if (-not (Get-Command scoop -ErrorAction SilentlyContinue)) { + throw "scoop is not available for installing $PackageName" + } + & scoop install $PackageName + if ($LASTEXITCODE -ne 0) { + throw "scoop install failed for $PackageName with exit code $LASTEXITCODE" + } +} + +function Invoke-BrewInstall { + param([string]$PackageName) + + if (-not (Get-Command brew -ErrorAction SilentlyContinue)) { + throw "brew is not available for installing $PackageName" + } + & brew install $PackageName + if ($LASTEXITCODE -ne 0) { + throw "brew install failed for $PackageName with exit code $LASTEXITCODE" + } +} + +function Invoke-LinuxPackageInstall { + param([string]$PackageName) + + if (Get-Command apt-get -ErrorAction SilentlyContinue) { + $runner = if (Get-Command sudo -ErrorAction SilentlyContinue) { "sudo" } else { "apt-get" } + if ($runner -eq "sudo") { + & sudo apt-get update + if ($LASTEXITCODE -ne 0) { throw "apt-get update failed with exit code $LASTEXITCODE" } + & sudo apt-get install -y $PackageName + } + else { + & apt-get update + if ($LASTEXITCODE -ne 0) { throw "apt-get update failed with exit code $LASTEXITCODE" } + & apt-get install -y $PackageName } + if ($LASTEXITCODE -ne 0) { throw "apt-get install failed for $PackageName with exit code $LASTEXITCODE" } + return + } + + if (Get-Command dnf -ErrorAction SilentlyContinue) { + if (Get-Command sudo -ErrorAction SilentlyContinue) { & sudo dnf install -y $PackageName } else { & dnf install -y $PackageName } + if ($LASTEXITCODE -ne 0) { throw "dnf install failed for $PackageName with exit code $LASTEXITCODE" } return } - throw "no supported installer found for ripgrep; install winget or scoop first" + if (Get-Command pacman -ErrorAction SilentlyContinue) { + if (Get-Command sudo -ErrorAction SilentlyContinue) { & sudo pacman -Sy --noconfirm $PackageName } else { & pacman -Sy --noconfirm $PackageName } + if ($LASTEXITCODE -ne 0) { throw "pacman install failed for $PackageName with exit code $LASTEXITCODE" } + return + } + + throw "no supported Linux package manager found for $PackageName; install apt, dnf, pacman, or install the tool manually" +} + +function Get-ToolPackageName { + param([string]$ToolName) + + switch ($ToolName) { + "rg" { + switch ($script:EffectivePlatform) { + "windows" { return @{ winget = "BurntSushi.ripgrep.MSVC"; choco = "ripgrep"; scoop = "ripgrep" } } + "macos" { return "ripgrep" } + "linux" { return "ripgrep" } + } + } + "git" { + switch ($script:EffectivePlatform) { + "windows" { return @{ winget = "Git.Git"; choco = "git"; scoop = "git" } } + "macos" { return "git" } + "linux" { return "git" } + } + } + "python" { + switch ($script:EffectivePlatform) { + "windows" { return @{ winget = "Python.Python.3.11"; choco = "python"; scoop = "python" } } + "macos" { return "python@3.11" } + "linux" { return "python3" } + } + } + } + + throw "no package mapping for $ToolName on $script:EffectivePlatform" +} + +function Invoke-ToolPackageInstall { + param([string]$ToolName) + + $package = Get-ToolPackageName $ToolName + switch ($script:EffectivePlatform) { + "windows" { + if (Get-Command winget -ErrorAction SilentlyContinue) { + Invoke-WingetInstall $package.winget $ToolName + return + } + if (Get-Command choco -ErrorAction SilentlyContinue) { + Invoke-ChocoInstall $package.choco + return + } + if (Get-Command scoop -ErrorAction SilentlyContinue) { + Invoke-ScoopInstall $package.scoop + return + } + throw "no supported Windows installer found for $ToolName; install winget, choco, or scoop first" + } + "macos" { + Invoke-BrewInstall $package + return + } + "linux" { + Invoke-LinuxPackageInstall $package + return + } + } +} + +function Invoke-RipgrepInstall { + Invoke-ToolPackageInstall "rg" } function Invoke-PipInstall { @@ -113,12 +247,13 @@ function Invoke-PipInstall { [string]$PackageName ) - $python = Get-Command python -ErrorAction SilentlyContinue + $python = Get-CodeIntelPythonCommand if (-not $python) { - throw "python is not on PATH; install Python and rerun this script in a new shell" + throw "python/python3 is not on PATH; install Python and rerun this script in a new shell" } + $pythonCommand = if (-not [string]::IsNullOrWhiteSpace($python.Source)) { $python.Source } else { $python.Name } - & python -m pip install --upgrade $PackageName + & $pythonCommand -m pip install --user --upgrade $PackageName if ($LASTEXITCODE -ne 0) { throw "pip install failed for $PackageName with exit code $LASTEXITCODE" } @@ -128,6 +263,31 @@ function Invoke-SentruxInstall { throw "no published sentrux package installer is configured; use the repo-owned shim/lite core or place a real sentrux.exe on PATH" } +function Get-InstallMetadata { + param([string]$CommandName) + + switch ($CommandName) { + "repowise" { return [ordered]@{ packageManager = "pip"; requiresElevation = $false } } + "sentrux" { return [ordered]@{ packageManager = "manual"; requiresElevation = $false } } + } + + switch ($script:EffectivePlatform) { + "windows" { + if (Get-Command winget -ErrorAction SilentlyContinue) { return [ordered]@{ packageManager = "winget"; requiresElevation = $false } } + if (Get-Command choco -ErrorAction SilentlyContinue) { return [ordered]@{ packageManager = "choco"; requiresElevation = $true } } + if (Get-Command scoop -ErrorAction SilentlyContinue) { return [ordered]@{ packageManager = "scoop"; requiresElevation = $false } } + return [ordered]@{ packageManager = "manual"; requiresElevation = $false } + } + "macos" { return [ordered]@{ packageManager = "brew"; requiresElevation = $false } } + "linux" { + if (Get-Command apt-get -ErrorAction SilentlyContinue) { return [ordered]@{ packageManager = "apt"; requiresElevation = $true } } + if (Get-Command dnf -ErrorAction SilentlyContinue) { return [ordered]@{ packageManager = "dnf"; requiresElevation = $true } } + if (Get-Command pacman -ErrorAction SilentlyContinue) { return [ordered]@{ packageManager = "pacman"; requiresElevation = $true } } + return [ordered]@{ packageManager = "manual"; requiresElevation = $false } + } + } +} + function Install-MissingTool { param( [System.Collections.Generic.List[object]]$Actions, @@ -136,14 +296,15 @@ function Install-MissingTool { [string]$Fix ) - $existing = Get-Command $CommandName -ErrorAction SilentlyContinue + $metadata = Get-InstallMetadata $CommandName + $existing = if ($CommandName -eq "python") { Get-CodeIntelPythonCommand } else { Get-Command $CommandName -ErrorAction SilentlyContinue } if ($existing) { - Add-InstallAction $Actions $CommandName "already_present" $existing.Source "" + Add-InstallAction $Actions $CommandName "already_present" $existing.Source "" $metadata.packageManager ([bool]$metadata.requiresElevation) return } if (-not $InstallMissing) { - Add-InstallAction $Actions $CommandName "not_requested" "missing" $Fix + Add-InstallAction $Actions $CommandName "not_requested" "missing" $Fix $metadata.packageManager ([bool]$metadata.requiresElevation) return } @@ -151,14 +312,14 @@ function Install-MissingTool { & $Installer $after = Get-Command $CommandName -ErrorAction SilentlyContinue if ($after) { - Add-InstallAction $Actions $CommandName "installed" $after.Source "" + Add-InstallAction $Actions $CommandName "installed" $after.Source "" $metadata.packageManager ([bool]$metadata.requiresElevation) } else { - Add-InstallAction $Actions $CommandName "installed_restart_required" "installer completed but command is not visible in this shell" "Open a new terminal and rerun install-code-intel-pipeline.ps1." + Add-InstallAction $Actions $CommandName "installed_restart_required" "installer completed but command is not visible in this shell" "Open a new terminal and rerun install-code-intel-pipeline.ps1." $metadata.packageManager ([bool]$metadata.requiresElevation) } } catch { - Add-InstallAction $Actions $CommandName "install_failed" $_.Exception.Message $Fix + Add-InstallAction $Actions $CommandName "install_failed" $_.Exception.Message $Fix $metadata.packageManager ([bool]$metadata.requiresElevation) } } @@ -170,7 +331,7 @@ function Test-Tool { [string]$Fix = "" ) - $cmd = Get-Command $Name -ErrorAction SilentlyContinue + $cmd = if ($Name -eq "python") { Get-CodeIntelPythonCommand } else { Get-Command $Name -ErrorAction SilentlyContinue } $detail = "missing" if ($cmd) { $detail = $cmd.Source @@ -222,34 +383,17 @@ function Test-EnvVar { } function Get-DefaultArtifactRoot { - $fromEnv = [Environment]::GetEnvironmentVariable("CODE_INTEL_ARTIFACT_ROOT", "User") - if (-not [string]::IsNullOrWhiteSpace($fromEnv)) { return $fromEnv } - if (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_ARTIFACT_ROOT)) { return $env:CODE_INTEL_ARTIFACT_ROOT } - $base = if (-not [string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) { $env:LOCALAPPDATA } else { (Join-Path $HOME ".code-intel") } - return (Join-Path $base "code-intel\artifacts") + return (code-intel-platform\Get-CodeIntelArtifactRoot -Platform $script:EffectivePlatform) } function Get-CodeIntelBinDir { - $fromEnv = [Environment]::GetEnvironmentVariable("CODE_INTEL_BIN", "User") - if (-not [string]::IsNullOrWhiteSpace($fromEnv)) { return $fromEnv } - if (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_BIN)) { return $env:CODE_INTEL_BIN } - $base = if (-not [string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) { $env:LOCALAPPDATA } else { (Join-Path $HOME ".code-intel") } - return (Join-Path $base "code-intel\bin") + return (code-intel-platform\Get-CodeIntelBinDir -Platform $script:EffectivePlatform) } function Add-UserPathPrefix { param([string]$PathToAdd) - $resolved = (New-Item -ItemType Directory -Force -Path $PathToAdd).FullName.TrimEnd('\') - - $userPath = [Environment]::GetEnvironmentVariable("Path", "User") - $userParts = @($userPath -split ";" | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) - $userParts = @($userParts | Where-Object { -not [string]::Equals($_.TrimEnd('\'), $resolved, [System.StringComparison]::OrdinalIgnoreCase) }) - [Environment]::SetEnvironmentVariable("Path", (($resolved) + ";" + ($userParts -join ";")).TrimEnd(";"), "User") - - $processParts = @($env:Path -split ";" | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) - $processParts = @($processParts | Where-Object { -not [string]::Equals($_.TrimEnd('\'), $resolved, [System.StringComparison]::OrdinalIgnoreCase) }) - $env:Path = (($resolved) + ";" + ($processParts -join ";")).TrimEnd(";") + return (code-intel-platform\Add-UserPathPrefix -PathToAdd $PathToAdd -Platform $script:EffectivePlatform) } function Install-SentruxShim { @@ -258,38 +402,47 @@ function Install-SentruxShim { [string]$Root ) - $sourceDir = Join-Path $Root "tools\sentrux-shim" + $sourceDir = Join-Path (Join-Path $Root "tools") "sentrux-shim" $sourcePs1 = Join-Path $sourceDir "sentrux-shim.ps1" $sourceCmd = Join-Path $sourceDir "sentrux.cmd" + $sourceShell = Join-Path $sourceDir "sentrux" $sourceLite = Join-Path $sourceDir "sentrux-lite-core.ps1" - if (-not (Test-Path -LiteralPath $sourcePs1 -PathType Leaf) -or -not (Test-Path -LiteralPath $sourceCmd -PathType Leaf) -or -not (Test-Path -LiteralPath $sourceLite -PathType Leaf)) { - Add-InstallAction $Actions "sentrux-shim" "install_failed" "missing shim source under $sourceDir" "Restore tools\sentrux-shim from the repository." + $sourceLauncher = if ($script:EffectivePlatform -eq "windows") { $sourceCmd } else { $sourceShell } + if (-not (Test-Path -LiteralPath $sourcePs1 -PathType Leaf) -or -not (Test-Path -LiteralPath $sourceLauncher -PathType Leaf) -or -not (Test-Path -LiteralPath $sourceLite -PathType Leaf)) { + Add-InstallAction $Actions "sentrux-shim" "install_failed" "missing shim source under $sourceDir" "Restore tools/sentrux-shim from the repository." "repo-local" $false return } try { $shimDir = Get-CodeIntelBinDir New-Item -ItemType Directory -Force -Path $shimDir | Out-Null - $oldPs1 = Join-Path $shimDir "sentrux.ps1" - if (Test-Path -LiteralPath $oldPs1 -PathType Leaf) { - Remove-Item -LiteralPath $oldPs1 -Force + foreach ($oldFile in @("sentrux.ps1")) { + $oldPath = Join-Path $shimDir $oldFile + if (Test-Path -LiteralPath $oldPath -PathType Leaf) { + Remove-Item -LiteralPath $oldPath -Force + } } Copy-Item -LiteralPath $sourcePs1 -Destination (Join-Path $shimDir "sentrux-shim.ps1") -Force - Copy-Item -LiteralPath $sourceCmd -Destination (Join-Path $shimDir "sentrux.cmd") -Force Copy-Item -LiteralPath $sourceLite -Destination (Join-Path $shimDir "sentrux-lite-core.ps1") -Force - Add-UserPathPrefix $shimDir + $launcherName = if ($script:EffectivePlatform -eq "windows") { "sentrux.cmd" } else { "sentrux" } + $launcherPath = Join-Path $shimDir $launcherName + Copy-Item -LiteralPath $sourceLauncher -Destination $launcherPath -Force + if ($script:EffectivePlatform -ne "windows" -and (Get-Command chmod -ErrorAction SilentlyContinue)) { + & chmod +x $launcherPath + } + $pathResult = Add-UserPathPrefix $shimDir - $statusOutput = & (Join-Path $shimDir "sentrux.cmd") pro status 2>&1 + $statusOutput = & $launcherPath pro status 2>&1 $statusText = ($statusOutput | ForEach-Object { $_.ToString() } | Out-String).Trim() if ($LASTEXITCODE -ne 0 -or $statusText -notmatch "Tier:\s+pro") { - Add-InstallAction $Actions "sentrux-shim" "install_failed" $statusText "Run sentrux pro status and inspect the error." + Add-InstallAction $Actions "sentrux-shim" "install_failed" $statusText "Run sentrux pro status and inspect the error." "repo-local" $false return } - Add-InstallAction $Actions "sentrux-shim" "installed" $shimDir "Open a new terminal if this shell cannot find sentrux from PATH." + Add-InstallAction $Actions "sentrux-shim" "installed" "shim=$shimDir path=$($pathResult.detail)" "Open a new terminal if this shell cannot find sentrux from PATH." "repo-local" $false } catch { - Add-InstallAction $Actions "sentrux-shim" "install_failed" $_.Exception.Message "Check write permission for the user CODE_INTEL_BIN or LOCALAPPDATA directory." + Add-InstallAction $Actions "sentrux-shim" "install_failed" $_.Exception.Message "Check write permission for the code-intel bin directory." "repo-local" $false } } @@ -311,16 +464,20 @@ function Install-SentruxVlangPluginOverlay { } try { - $output = & $overlayScript 2>&1 + $output = & $overlayScript -Platform $script:EffectivePlatform 2>&1 $text = ($output | ForEach-Object { $_.ToString() } | Out-String).Trim() + if ($text -match "manual_required") { + Add-InstallAction $Actions "sentrux-vlang-overlay" "manual_required" $text "Install or build a platform grammar artifact before enabling V parsing." "repo-local" $false + return + } if ($LASTEXITCODE -ne 0) { - Add-InstallAction $Actions "sentrux-vlang-overlay" "install_failed" $text "Run .\Install-SentruxVlangOverlay.ps1 manually and inspect sentrux plugin validate output." + Add-InstallAction $Actions "sentrux-vlang-overlay" "install_failed" $text "Run Install-SentruxVlangOverlay.ps1 manually and inspect sentrux plugin validate output." "repo-local" $false return } - Add-InstallAction $Actions "sentrux-vlang-overlay" "installed" $text "Run sentrux plugin list to confirm vlang is listed." + Add-InstallAction $Actions "sentrux-vlang-overlay" "installed" $text "Run sentrux plugin list to confirm vlang is listed." "repo-local" $false } catch { - Add-InstallAction $Actions "sentrux-vlang-overlay" "install_failed" $_.Exception.Message "Run .\Install-SentruxVlangOverlay.ps1 manually after sentrux is installed." + Add-InstallAction $Actions "sentrux-vlang-overlay" "install_failed" $_.Exception.Message "Run Install-SentruxVlangOverlay.ps1 manually after sentrux is installed." "repo-local" $false } } @@ -364,17 +521,13 @@ function Ensure-SkillLink { $detail = "source skill missing: $Target" } else { - $parent = Split-Path -Parent $Path - New-Item -ItemType Directory -Force -Path $parent | Out-Null - if (-not (Test-Path -LiteralPath $Path)) { - New-Item -ItemType Junction -Path $Path -Target $Target | Out-Null - } + $link = New-CodeIntelLink -Path $Path -Target $Target -Platform $script:EffectivePlatform $ok = Test-Path -LiteralPath $skillFile -PathType Leaf - $detail = if ($ok) { "repaired: $Path" } else { "repair failed: $Path" } + $detail = if ($ok) { "repaired:$($link.mode): $Path" } else { "repair failed: $Path" } } } - Add-Check $Checks "skill:$Name" "skill" $true $ok $detail "Run with -RepairSkillLinks, or create a junction from $Path to $Target." + Add-Check $Checks "skill:$Name" "skill" $true $ok $detail "Run with -RepairSkillLinks, or link/copy $Target to $Path." } function Ensure-SkillSource { @@ -413,22 +566,53 @@ $checks = New-Object System.Collections.Generic.List[object] $installActions = New-Object System.Collections.Generic.List[object] $installPlan = New-Object System.Collections.Generic.List[object] $root = Split-Path -Parent $PSCommandPath +$paths = Get-CodeIntelPaths -Platform $script:EffectivePlatform -Root $root +$homeEnv = Set-CodeIntelUserEnv -Name "CODE_INTEL_HOME" -Value $paths.codeIntelHome -Platform $script:EffectivePlatform +Add-InstallAction $installActions "env:CODE_INTEL_HOME" "installed" $homeEnv.detail "" "env" $false if ([string]::IsNullOrWhiteSpace($Config)) { $Config = Join-Path $root "pipeline.config.json" } -Add-InstallPlan $installPlan "rg" "winget or scoop" "winget install --id BurntSushi.ripgrep.MSVC -e" "Exact file inventory and fast text search." "LOW: established CLI tool; install source should still be package-manager controlled." "Use the rg bundled with Codex if available." -Add-InstallPlan $installPlan "git" "winget" "winget install --id Git.Git -e" "Repository status, worktree, sparse checkout, and history operations." "LOW: foundational tool; ensure official Git for Windows package source." "" -Add-InstallPlan $installPlan "python" "winget" "winget install --id Python.Python.3.11 -e" "Runs provider preflight and scoped repowise docs helper." "LOW/MEDIUM: runtime install affects PATH; verify version and restart shell if needed." "Use an already managed Python 3.11+ runtime." -Add-InstallPlan $installPlan "repowise" "pip" "python -m pip install --upgrade repowise" "Semantic index and wiki/docs memory." "MEDIUM: Python package supply chain; pin or vendor only after team policy decides." "Skip repowise with -SkipRepowise for exact-search-only runs." -Add-InstallPlan $installPlan "sentrux" "repo-local shim or preinstalled binary" "install tools\\sentrux-shim first; optionally place a real sentrux.exe on PATH" "Structural quality and regression gate." "LOW for repo-owned shim; MEDIUM for any separately supplied sentrux.exe." "The repo-owned sentrux-lite core keeps scan/check/gate/plugin usable until the real binary is installed." -Add-InstallPlan $installPlan "sentrux-shim" "repo-local" "copy tools\\sentrux-shim to CODE_INTEL_BIN and prepend user PATH" "Open-source local Pro activation, stable forwarding to real sentrux, and deterministic lite-core fallback." "LOW: repo-owned PowerShell/CMD shim; review tools\\sentrux-shim before install." "Set SENTRUX_AUTO_PRO=0 to disable auto Pro activation." -Add-InstallPlan $installPlan "sentrux-vlang-overlay" "repo-local" "copy overlays\\sentrux\\vlang into USERPROFILE\\.sentrux\\plugins\\vlang" "Fixes the broken upstream Windows vlang plugin package and enables V parsing in real sentrux." "LOW/MEDIUM: ships a Windows tree-sitter DLL built from an MIT grammar; review overlays\\sentrux\\vlang\\THIRD_PARTY.md." "Use -SkipSentruxVlangOverlay to skip this local plugin patch." +function Add-ToolInstallPlan { + param( + [string]$Name, + [string]$Command, + [string]$Purpose, + [string]$Risk, + [string]$Alternative = "" + ) + + $metadata = Get-InstallMetadata $Name + Add-InstallPlan $installPlan $Name $metadata.packageManager $Command $Purpose $Risk $Alternative $metadata.packageManager ([bool]$metadata.requiresElevation) +} + +switch ($script:EffectivePlatform) { + "windows" { + Add-ToolInstallPlan "rg" "winget/choco/scoop install ripgrep" "Exact file inventory and fast text search." "LOW: established CLI tool; install source should still be package-manager controlled." "Use the rg bundled with Codex if available." + Add-ToolInstallPlan "git" "winget/choco/scoop install git" "Repository status, worktree, sparse checkout, and history operations." "LOW: foundational tool; ensure official Git for Windows package source." "" + Add-ToolInstallPlan "python" "winget/choco/scoop install Python 3.11+" "Runs provider preflight and scoped repowise docs helper." "LOW/MEDIUM: runtime install affects PATH; verify version and restart shell if needed." "Use an already managed Python 3.11+ runtime." + } + "macos" { + Add-ToolInstallPlan "rg" "brew install ripgrep" "Exact file inventory and fast text search." "LOW: established CLI tool; install source should still be package-manager controlled." "Use the rg bundled with Codex if available." + Add-ToolInstallPlan "git" "brew install git" "Repository status, worktree, sparse checkout, and history operations." "LOW: foundational tool; ensure official Git package source." "" + Add-ToolInstallPlan "python" "brew install python@3.11" "Runs provider preflight and scoped repowise docs helper." "LOW/MEDIUM: runtime install affects PATH; verify version and restart shell if needed." "Use an already managed Python 3.11+ runtime." + } + "linux" { + Add-ToolInstallPlan "rg" "apt/dnf/pacman install ripgrep" "Exact file inventory and fast text search." "LOW: established CLI tool; install source should still be package-manager controlled." "Use the rg bundled with Codex if available." + Add-ToolInstallPlan "git" "apt/dnf/pacman install git" "Repository status, worktree, sparse checkout, and history operations." "LOW: foundational tool; ensure distro package source." "" + Add-ToolInstallPlan "python" "apt/dnf/pacman install python3" "Runs provider preflight and scoped repowise docs helper." "LOW/MEDIUM: runtime install affects PATH; verify version and restart shell if needed." "Use an already managed Python 3.11+ runtime." + } +} +Add-InstallPlan $installPlan "repowise" "pip" "python/python3 -m pip install --user --upgrade repowise" "Semantic index and wiki/docs memory." "MEDIUM: Python package supply chain; pin or vendor only after team policy decides." "Skip repowise with -SkipRepowise for exact-search-only runs." "pip" $false +$sentruxBinaryName = if ($script:EffectivePlatform -eq "windows") { "sentrux.exe" } else { "sentrux" } +Add-InstallPlan $installPlan "sentrux" "repo-local shim or preinstalled binary" "install tools/sentrux-shim first; optionally place a real $sentruxBinaryName on PATH" "Structural quality and regression gate." "LOW for repo-owned shim; MEDIUM for any separately supplied $sentruxBinaryName." "The repo-owned sentrux-lite core keeps scan/check/gate/plugin usable until the real binary is installed." "repo-local" $false +Add-InstallPlan $installPlan "sentrux-shim" "repo-local" "copy tools/sentrux-shim launcher to CODE_INTEL_BIN and prepend PATH" "Open-source local Pro activation, stable forwarding to real sentrux, and deterministic lite-core fallback." "LOW: repo-owned PowerShell/CMD/sh shim; review tools/sentrux-shim before install." "Set SENTRUX_AUTO_PRO=0 to disable auto Pro activation." "repo-local" $false +Add-InstallPlan $installPlan "sentrux-vlang-overlay" "repo-local" "copy overlays/sentrux/vlang into the user Sentrux plugin directory when a platform grammar exists" "Fixes the broken upstream Windows vlang plugin package and enables V parsing in real sentrux." "LOW/MEDIUM: ships tree-sitter grammar artifacts; review overlays/sentrux/vlang/THIRD_PARTY.md." "Use -SkipSentruxVlangOverlay to skip this local plugin patch." "repo-local" $false Install-MissingTool $installActions "rg" { Invoke-RipgrepInstall } "Install ripgrep with winget (`winget install --id BurntSushi.ripgrep.MSVC -e`) or ensure rg is on PATH." -Install-MissingTool $installActions "git" { Invoke-WingetInstall "Git.Git" "Git for Windows" } "Install Git for Windows (`winget install --id Git.Git -e`) or ensure git is on PATH." -Install-MissingTool $installActions "python" { Invoke-WingetInstall "Python.Python.3.11" "Python 3.11" } "Install Python 3.11+ (`winget install --id Python.Python.3.11 -e`) or ensure python is on PATH." -Install-MissingTool $installActions "repowise" { Invoke-PipInstall "repowise" } "Install repowise into the active Python environment (`python -m pip install --upgrade repowise`)." +Install-MissingTool $installActions "git" { Invoke-ToolPackageInstall "git" } "Install git with the platform package manager or ensure git is on PATH." +Install-MissingTool $installActions "python" { Invoke-ToolPackageInstall "python" } "Install Python 3.11+ with the platform package manager or ensure python is on PATH." +Install-MissingTool $installActions "repowise" { Invoke-PipInstall "repowise" } "Install repowise into the active Python environment (`python/python3 -m pip install --user --upgrade repowise`)." Install-SentruxShim $installActions $root Install-MissingTool $installActions "sentrux" { Invoke-SentruxInstall } "Install the repo-owned shim or ensure sentrux.exe is on PATH." Install-SentruxVlangPluginOverlay $installActions $root @@ -446,41 +630,50 @@ $requiredFiles = @( "bootstrap-new-machine.ps1", "test-code-intel-pipeline.ps1", "test-code-intel-provider.ps1", - "update-code-intel-index.ps1" + "update-code-intel-index.ps1", + "tools/code-intel-platform.psm1" ) foreach ($file in $requiredFiles) { Test-File $checks "pipeline:$file" (Join-Path $root $file) $true } Test-File $checks "config" $Config $true -Test-File $checks "sentrux-shim:cmd" (Join-Path $root "tools\sentrux-shim\sentrux.cmd") $true -Test-File $checks "sentrux-shim:ps1" (Join-Path $root "tools\sentrux-shim\sentrux-shim.ps1") $true -Test-File $checks "sentrux-shim:lite-core" (Join-Path $root "tools\sentrux-shim\sentrux-lite-core.ps1") $true -Test-File $checks "sentrux-vlang-overlay:plugin" (Join-Path $root "overlays\sentrux\vlang\plugin.toml") $true -Test-File $checks "sentrux-vlang-overlay:query" (Join-Path $root "overlays\sentrux\vlang\queries\tags.scm") $true -Test-File $checks "sentrux-vlang-overlay:dll" (Join-Path $root "overlays\sentrux\vlang\grammars\windows-x86_64.dll") $true +$shimSource = Join-Path (Join-Path $root "tools") "sentrux-shim" +$shimLauncherName = if ($script:EffectivePlatform -eq "windows") { "sentrux.cmd" } else { "sentrux" } +Test-File $checks "sentrux-shim:launcher" (Join-Path $shimSource $shimLauncherName) $true +Test-File $checks "sentrux-shim:ps1" (Join-Path $shimSource "sentrux-shim.ps1") $true +Test-File $checks "sentrux-shim:lite-core" (Join-Path $shimSource "sentrux-lite-core.ps1") $true +$overlayRoot = Join-Path (Join-Path (Join-Path $root "overlays") "sentrux") "vlang" +Test-File $checks "sentrux-vlang-overlay:plugin" (Join-Path $overlayRoot "plugin.toml") $true +Test-File $checks "sentrux-vlang-overlay:query" (Join-Path (Join-Path $overlayRoot "queries") "tags.scm") $true +$grammarName = switch ($script:EffectivePlatform) { + "windows" { "windows-x86_64.dll" } + "macos" { "darwin-arm64.dylib" } + "linux" { "linux-x86_64.so" } +} +Test-File $checks "sentrux-vlang-overlay:grammar" (Join-Path (Join-Path $overlayRoot "grammars") $grammarName) $false Test-Tool $checks "rg" $true "Install ripgrep or ensure rg is on PATH." Test-Tool $checks "git" $true "Install Git for Windows or ensure git is on PATH." -Test-Tool $checks "python" $true "Install Python 3.11+ or ensure python is on PATH." +Test-Tool $checks "python" $true "Install Python 3.11+ or ensure python/python3 is on PATH." Test-Tool $checks "repowise" ([bool]$RequireRepowise) "Install repowise into the active Python environment, or omit -RequireRepowise and let the pipeline skip semantic memory." -Test-Tool $checks "sentrux" $true "Install sentrux or ensure sentrux.exe is on PATH." +Test-Tool $checks "sentrux" $true "Install sentrux or ensure it is on PATH." Test-CommandOutput $checks "tool:sentrux-core" "tool" { sentrux check --help } "Enforce architectural rules" "Install the real sentrux binary for full fidelity, or keep the repo-owned sentrux-lite fallback for portable scan/check/gate." Test-CommandOutput $checks "tool:sentrux-pro" "tool" { sentrux pro status } "Tier:\s+pro" "Run install-code-intel-pipeline.ps1 again so the repo shim is installed and auto activation is enabled." -$userProfile = if ([string]::IsNullOrWhiteSpace($env:USERPROFILE)) { "C:\Users\Administrator" } else { $env:USERPROFILE } -$skillSource = Join-Path $userProfile ".agents\skills\code-intel-pipeline" -$codexSkill = Join-Path $userProfile ".codex\skills\code-intel-pipeline" -$claudeSkill = Join-Path $userProfile ".claude\skills\code-intel-pipeline" +$userProfile = Get-CodeIntelHomeDirectory +$skillSource = Join-Path (Join-Path (Join-Path $userProfile ".agents") "skills") "code-intel-pipeline" +$codexSkill = Join-Path (Join-Path (Join-Path $userProfile ".codex") "skills") "code-intel-pipeline" +$claudeSkill = Join-Path (Join-Path (Join-Path $userProfile ".claude") "skills") "code-intel-pipeline" $bundledSkill = Join-Path $root "skill" Ensure-SkillSource $checks $skillSource $bundledSkill $RepairSkillLinks Ensure-SkillLink $checks "codex" $codexSkill $skillSource $RepairSkillLinks Ensure-SkillLink $checks "claude" $claudeSkill $skillSource $RepairSkillLinks $understandCandidates = @( - (Join-Path $userProfile ".claude\skills\understand\SKILL.md"), - (Join-Path $userProfile ".agents\skills\understand\SKILL.md"), - (Join-Path $userProfile ".codex\skills\understand\SKILL.md") + (Join-Path (Join-Path (Join-Path (Join-Path $userProfile ".claude") "skills") "understand") "SKILL.md"), + (Join-Path (Join-Path (Join-Path (Join-Path $userProfile ".agents") "skills") "understand") "SKILL.md"), + (Join-Path (Join-Path (Join-Path (Join-Path $userProfile ".codex") "skills") "understand") "SKILL.md") ) $understandFound = [bool]($understandCandidates | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1) $understandDetail = "missing" @@ -567,6 +760,18 @@ $result = [ordered]@{ ok = $missingRequired.Count -eq 0 root = $root config = $Config + platform = [ordered]@{ + os = $script:EffectivePlatform + shell = $PSVersionTable.PSEdition + psVersion = $PSVersionTable.PSVersion.ToString() + } + paths = [ordered]@{ + home = $paths.home + dataRoot = $paths.dataRoot + bin = $paths.bin + codeIntelHome = $paths.codeIntelHome + artifactRoot = if ([string]::IsNullOrWhiteSpace($ArtifactRoot)) { $paths.artifactRoot } else { $ArtifactRoot } + } repo = $Repo repoPath = $RepoPath repairedSkillLinks = [bool]$RepairSkillLinks diff --git a/invoke-code-intel.ps1 b/invoke-code-intel.ps1 index b55fb7f..78e7d43 100644 --- a/invoke-code-intel.ps1 +++ b/invoke-code-intel.ps1 @@ -1,3 +1,5 @@ +#requires -Version 7.2 + param( [string]$Repo = "", [string]$RepoPath = "", @@ -6,6 +8,9 @@ param( [string]$Config = "", + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto", + [ValidateSet("lite", "normal", "full")] [string]$Mode = "normal", @@ -50,10 +55,10 @@ function Invoke-OneRepo { Write-Host "Code intel invoke: doctor $label" $global:LASTEXITCODE = 0 if (-not [string]::IsNullOrWhiteSpace($DirectRepoPath)) { - & $doctor -Config $Config -RepoPath $DirectRepoPath + & $doctor -Config $Config -RepoPath $DirectRepoPath -Platform $Platform } else { - & $doctor -Config $Config -Repo $RepoName + & $doctor -Config $Config -Repo $RepoName -Platform $Platform } if ($LASTEXITCODE -ne 0) { return [pscustomobject][ordered]@{ @@ -69,6 +74,7 @@ function Invoke-OneRepo { $invokeParams = @{ Config = $Config Mode = $Mode + Platform = $Platform } if (-not [string]::IsNullOrWhiteSpace($DirectRepoPath)) { $invokeParams.RepoPath = $DirectRepoPath } else { $invokeParams.Repo = $RepoName } if ($RepowiseDocs) { $invokeParams.RepowiseDocs = $true } @@ -80,10 +86,10 @@ function Invoke-OneRepo { } else { if (-not [string]::IsNullOrWhiteSpace($DirectRepoPath)) { - & $runner -Config $Config -RepoPath $DirectRepoPath -Mode $Mode + & $runner -Config $Config -RepoPath $DirectRepoPath -Mode $Mode -Platform $Platform } else { - & $runner -Config $Config -Repo $RepoName -Mode $Mode + & $runner -Config $Config -Repo $RepoName -Mode $Mode -Platform $Platform } } diff --git a/overlays/sentrux/vlang/README.md b/overlays/sentrux/vlang/README.md index d95c930..b65c389 100644 --- a/overlays/sentrux/vlang/README.md +++ b/overlays/sentrux/vlang/README.md @@ -1,11 +1,11 @@ # Sentrux V 覆盖包 -Sentrux 0.5.7 自带的 `vlang` 标准包在 Windows 上不完整:`plugin.toml` 缺少 `[grammar]`,也没有 `grammars/windows-x86_64.dll`。`sentrux plugin add vlang` 和 `add-standard` 会反复装回这个坏包。 +Sentrux 0.5.7 自带的 `vlang` 标准包不完整:`plugin.toml` 缺少 `[grammar]`,部分平台也没有可用 grammar artifact。`sentrux plugin add vlang` 和 `add-standard` 会反复装回这个坏包。 这个覆盖包补齐三件事: - `plugin.toml`:声明 `tree-sitter-v` grammar 和 ABI 13。 -- `grammars/windows-x86_64.dll`:由 `nedpals/tree-sitter-v` 编译,并导出 Sentrux 期望的 `tree_sitter_vlang()`。 +- `grammars/`:由 `nedpals/tree-sitter-v` 编译,并导出 Sentrux 期望的 `tree_sitter_vlang()`。当前 installer 会按平台寻找 `windows-x86_64.dll`、`linux-x86_64.so`、`darwin-arm64.dylib`。 - `queries/tags.scm`:改成该 grammar 真实存在的节点,并使用 Sentrux 能建图的 `@definition.*`、`@reference.call`、`@import.module` 捕获协议。 安装: @@ -17,9 +17,9 @@ Sentrux 0.5.7 自带的 `vlang` 标准包在 Windows 上不完整:`plugin.toml 验证: ```powershell -sentrux plugin validate $env:USERPROFILE\.sentrux\plugins\vlang +sentrux plugin validate ~/.sentrux/plugins/vlang sentrux plugin list -sentrux check C:\tmp\sentrux-vlang-fixture +sentrux check ``` 注意:`sentrux scan ` 是打开 GUI 的命令,不适合作为非交互 smoke test。用 `check` 或 `gate` 验证结构引擎。 @@ -27,8 +27,8 @@ sentrux check C:\tmp\sentrux-vlang-fixture 构建 DLL 的来源: ```powershell -git clone --depth=1 https://github.com/nedpals/tree-sitter-v.git C:\tmp\tree-sitter-v -gcc -shared -O2 -I C:\tmp\tree-sitter-v\src -o C:\tmp\tree-sitter-v\windows-x86_64.dll C:\tmp\tree-sitter-v\src\parser.c C:\tmp\tree-sitter-v\src\scanner.c C:\tmp\tree-sitter-v\sentrux_vlang_alias.c +git clone --depth=1 https://github.com/nedpals/tree-sitter-v.git +gcc -shared -O2 -I /src -o / /src/parser.c /src/scanner.c /sentrux_vlang_alias.c ``` `sentrux_vlang_alias.c` 只做一件事:把 grammar 原生导出的 `tree_sitter_v()` 包装成 Sentrux 查找的 `tree_sitter_vlang()`。 diff --git a/run-code-intel.ps1 b/run-code-intel.ps1 index 76f0436..1bbd638 100644 --- a/run-code-intel.ps1 +++ b/run-code-intel.ps1 @@ -1,9 +1,14 @@ +#requires -Version 7.2 + param( [string]$Repo = "", [string]$RepoPath = "", [string]$Config = "", + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto", + [ValidateSet("lite", "normal", "full")] [string]$Mode = "normal", @@ -39,6 +44,11 @@ param( Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" +$platformModule = Join-Path (Join-Path $PSScriptRoot "tools") "code-intel-platform.psm1" +Import-Module $platformModule -Force +$effectivePlatform = Get-CodeIntelPlatform -Platform $Platform +$codeIntelPaths = Get-CodeIntelPaths -Platform $effectivePlatform -Root $PSScriptRoot + [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new() $OutputEncoding = [System.Text.UTF8Encoding]::new() $env:PYTHONIOENCODING = "utf-8" @@ -363,19 +373,11 @@ function Get-JsonProperty { } function Get-DefaultArtifactRoot { - $fromEnv = [Environment]::GetEnvironmentVariable("CODE_INTEL_ARTIFACT_ROOT", "User") - if (-not [string]::IsNullOrWhiteSpace($fromEnv)) { return $fromEnv } - if (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_ARTIFACT_ROOT)) { return $env:CODE_INTEL_ARTIFACT_ROOT } - $base = if (-not [string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) { $env:LOCALAPPDATA } else { (Join-Path $HOME ".code-intel") } - return (Join-Path $base "code-intel\artifacts") + return (Get-CodeIntelArtifactRoot -Platform $effectivePlatform) } function Get-DefaultShadowRoot { - $fromEnv = [Environment]::GetEnvironmentVariable("CODE_INTEL_SHADOW_ROOT", "User") - if (-not [string]::IsNullOrWhiteSpace($fromEnv)) { return $fromEnv } - if (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_SHADOW_ROOT)) { return $env:CODE_INTEL_SHADOW_ROOT } - $base = if (-not [string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) { $env:LOCALAPPDATA } else { (Join-Path $HOME ".code-intel") } - return (Join-Path $base "code-intel\repowise") + return (Get-CodeIntelShadowRoot -Platform $effectivePlatform) } function Resolve-ChildPath { @@ -1241,7 +1243,7 @@ function New-SentruxInsight { $gateStep = @($Steps | Where-Object { $_.name -like "sentrux gate*" } | Select-Object -Last 1) $checkStep = @($Steps | Where-Object { $_.name -eq "sentrux check" } | Select-Object -First 1) - $rulesPath = if ([string]::IsNullOrWhiteSpace($TargetPath)) { "" } else { Join-Path $TargetPath ".sentrux\rules.toml" } + $rulesPath = if ([string]::IsNullOrWhiteSpace($TargetPath)) { "" } else { Join-Path (Join-Path $TargetPath ".sentrux") "rules.toml" } $baseline = Read-JsonFileSafe $BaselinePath $gateOutput = if ($gateStep.Count -gt 0) { [string]$gateStep[0].output } else { "" } @@ -1839,7 +1841,7 @@ function New-HospitalModalities { return @( (New-Modality "xray" "fast file inventory and repo surface" $InventoryStep (Get-StepScore $InventoryStep) (Join-Path $RunDir "files.txt") $xrayFinding "Sees files, not semantic impact.") - (New-Modality "anatomy" "Understand Anything architecture graph" $UnderstandStep $GraphScore (Join-Path $RepoPath ".understand-anything\knowledge-graph.json") (Get-FirstLine ([string]$UnderstandStep.output)) "Requires a prebuilt graph from the Understand tool.") + (New-Modality "anatomy" "Understand Anything architecture graph" $UnderstandStep $GraphScore (Join-Path (Join-Path $RepoPath ".understand-anything") "knowledge-graph.json") (Get-FirstLine ([string]$UnderstandStep.output)) "Requires a prebuilt graph from the Understand tool.") (New-Modality "ct" "Sentrux DSM, hotspots, and structural slices" $SentruxGateStep $CtScore $ctArtifact $ctFinding "Static structure is not runtime truth.") (New-Modality "mri" "CodeNexus context and impact localization" $null $MriScore $mriArtifact $mriFinding "Lite mode is local evidence, not a full semantic backend.") (New-Modality "pet" "execution proxy: test gaps, evolution, and what-if risk" $null $PetScore $petArtifact $petFinding "No live runtime trace is captured yet.") @@ -3680,6 +3682,17 @@ $report = [ordered]@{ repoName = $repoName mode = $Mode language = $Language + platform = [ordered]@{ + os = $effectivePlatform + shell = $PSVersionTable.PSEdition + psVersion = $PSVersionTable.PSVersion.ToString() + } + paths = [ordered]@{ + home = $codeIntelPaths.home + dataRoot = $codeIntelPaths.dataRoot + bin = $codeIntelPaths.bin + codeIntelHome = $codeIntelPaths.codeIntelHome + } artifactDir = $runDir sentruxPath = if ([string]::IsNullOrWhiteSpace($SentruxPath)) { $repoPath } else { (Resolve-ChildPath $repoPath $SentruxPath) } tools = $toolState diff --git a/skill/SKILL.md b/skill/SKILL.md index 19799eb..01888c1 100644 --- a/skill/SKILL.md +++ b/skill/SKILL.md @@ -9,49 +9,49 @@ Use the local pipeline instead of inventing another code-indexing stack. Canonical files: -- Pipeline: `D:\projects\_tools\code-intel-pipeline\run-code-intel.ps1` -- Project discovery: `D:\projects\_tools\code-intel-pipeline\Find-CodeIntelProjects.ps1` -- Doctor: `D:\projects\_tools\code-intel-pipeline\check-code-intel-tools.ps1` -- Sentrux Agent tools: `D:\projects\_tools\code-intel-pipeline\Invoke-SentruxAgentTool.ps1` -- Config: `D:\projects\_tools\code-intel-pipeline\pipeline.config.json` -- Artifacts: `%LOCALAPPDATA%\code-intel\artifacts\\\` by default, or `CODE_INTEL_ARTIFACT_ROOT` when set. -- Artifact data contract: `D:\projects\_tools\code-intel-pipeline\docs\artifact-data-contract.md` -- Agent goal intake: `D:\projects\_tools\code-intel-pipeline\docs\agent-goal-intake.md` -- Harness factory reference: `D:\projects\_tools\code-intel-pipeline\docs\harness-factory-reference.md` -- Skill development benchmark: `D:\projects\_tools\code-intel-pipeline\docs\skill-development-benchmark.md` -- Implementation minimalism benchmark: `D:\projects\_tools\code-intel-pipeline\docs\implementation-minimalism-benchmark.md` -- Ponytail impact scoreboard: `D:\projects\_tools\code-intel-pipeline\docs\ponytail-impact-scoreboard.md` -- Project management support: `D:\projects\_tools\code-intel-pipeline\docs\project-management-support.md` -- Issue tracker config: `D:\projects\_tools\code-intel-pipeline\docs\agents\issue-tracker.md` -- Triage label config: `D:\projects\_tools\code-intel-pipeline\docs\agents\triage-labels.md` -- Domain docs config: `D:\projects\_tools\code-intel-pipeline\docs\agents\domain.md` -- Templates: `D:\projects\_tools\code-intel-pipeline\templates\` +- Pipeline: `$env:CODE_INTEL_HOME/run-code-intel.ps1` +- Project discovery: `$env:CODE_INTEL_HOME/Find-CodeIntelProjects.ps1` +- Doctor: `$env:CODE_INTEL_HOME/check-code-intel-tools.ps1` +- Sentrux Agent tools: `$env:CODE_INTEL_HOME/Invoke-SentruxAgentTool.ps1` +- Config: `$env:CODE_INTEL_HOME/pipeline.config.json` +- Artifacts: `CODE_INTEL_ARTIFACT_ROOT` when set, otherwise the platform code-intel data root under `artifacts///`. +- Artifact data contract: `$env:CODE_INTEL_HOME/docs/artifact-data-contract.md` +- Agent goal intake: `$env:CODE_INTEL_HOME/docs/agent-goal-intake.md` +- Harness factory reference: `$env:CODE_INTEL_HOME/docs/harness-factory-reference.md` +- Skill development benchmark: `$env:CODE_INTEL_HOME/docs/skill-development-benchmark.md` +- Implementation minimalism benchmark: `$env:CODE_INTEL_HOME/docs/implementation-minimalism-benchmark.md` +- Ponytail impact scoreboard: `$env:CODE_INTEL_HOME/docs/ponytail-impact-scoreboard.md` +- Project management support: `$env:CODE_INTEL_HOME/docs/project-management-support.md` +- Issue tracker config: `$env:CODE_INTEL_HOME/docs/agents/issue-tracker.md` +- Triage label config: `$env:CODE_INTEL_HOME/docs/agents/triage-labels.md` +- Domain docs config: `$env:CODE_INTEL_HOME/docs/agents/domain.md` +- Templates: `$env:CODE_INTEL_HOME/templates/` ## Required First Step On a new machine or teammate session, run the installer first: ```powershell -D:\projects\_tools\code-intel-pipeline\install-code-intel-pipeline.ps1 -RepoPath +& "$env:CODE_INTEL_HOME/install-code-intel-pipeline.ps1" -RepoPath ``` -Use `-CheckProvider` to also ping the MiniMax Anthropic-compatible endpoint. Use `-RepairSkillLinks` when the shared skill should be installed or repaired for Codex and Claude. If the `.agents` copy is missing, the installer seeds it from the repo's bundled `skill\` directory first. Use `-InstallMissing` on teammate machines when missing CLI tools should be installed automatically where supported. Never ask the installer to write API keys; it only checks whether user-scoped env vars exist. +Use `-CheckProvider` to also ping the MiniMax Anthropic-compatible endpoint. Use `-RepairSkillLinks` when the shared skill should be installed or repaired for Codex and Claude. If the `.agents` copy is missing, the installer seeds it from the repo's bundled `skill/` directory first. Use `-InstallMissing` on teammate machines when missing CLI tools should be installed automatically where supported. Never ask the installer to write API keys; it only checks whether user-scoped env vars exist. Team bootstrap: ```powershell -D:\projects\_tools\code-intel-pipeline\install-code-intel-pipeline.ps1 -RepoPath -CheckProvider -RepairSkillLinks -InstallMissing +& "$env:CODE_INTEL_HOME/install-code-intel-pipeline.ps1" -RepoPath -CheckProvider -RepairSkillLinks -InstallMissing ``` For one-command teammate setup, use: ```powershell -D:\projects\_tools\code-intel-pipeline\bootstrap-new-machine.ps1 -RepoPath +& "$env:CODE_INTEL_HOME/bootstrap-new-machine.ps1" -RepoPath ``` -The installer also installs the repo-owned Sentrux shim into `CODE_INTEL_BIN` or `%LOCALAPPDATA%\code-intel\bin`, prepends that directory to the user PATH, and verifies `sentrux pro status`. The shim auto-activates local open-source Pro features, forwards all non-`pro` commands to the real `sentrux.exe` when present, and falls back to `sentrux-lite-core.ps1` for portable `scan`, `health`, `check`, `gate`, and `plugin list/validate`. +The installer also installs the repo-owned Sentrux shim into `CODE_INTEL_BIN` or the platform code-intel data root under `bin`, prepends that directory to PATH, and verifies `sentrux pro status`. The shim auto-activates local open-source Pro features, forwards all non-`pro` commands to the real `sentrux` when present, and falls back to `sentrux-lite-core.ps1` for portable `scan`, `health`, `check`, `gate`, and `plugin list/validate`. -The installer applies the repo-owned Sentrux V overlay by default because the upstream Windows `vlang` standard plugin package is incomplete in Sentrux 0.5.7. The overlay lives under `overlays\sentrux\vlang`, installs with `Install-SentruxVlangOverlay.ps1`, and should make `sentrux plugin list` show `vlang v0.2.0 [v]`. Use `Test-SentruxVlangOverlay.ps1` to prove the plugin builds a real V graph with 2 files, 1 import, and 1 call. Use `-SkipSentruxVlangOverlay` only when explicitly testing upstream plugin behavior. +The installer applies the repo-owned Sentrux V overlay by default because the upstream Windows `vlang` standard plugin package is incomplete in Sentrux 0.5.7. The overlay lives under `overlays/sentrux/vlang`, installs with `Install-SentruxVlangOverlay.ps1`, and should make `sentrux plugin list` show `vlang v0.2.0 [v]`. Use `Test-SentruxVlangOverlay.ps1` to prove the plugin builds a real V graph with 2 files, 1 import, and 1 call when this platform has a bundled grammar artifact. Use `-SkipSentruxVlangOverlay` only when explicitly testing upstream plugin behavior. For machine-readable bootstrap status, add `-Json` and read `installActions` first. Valid statuses are `already_present`, `not_requested`, `installed`, `installed_restart_required`, and `install_failed`. @@ -60,7 +60,7 @@ Use `-AuditInstallPlan` before `-InstallMissing` when reviewing a new teammate m Always run the doctor before using the pipeline: ```powershell -D:\projects\_tools\code-intel-pipeline\check-code-intel-tools.ps1 -RepoPath +& "$env:CODE_INTEL_HOME/check-code-intel-tools.ps1" -RepoPath ``` Use `-Json` when another agent needs machine-readable output. @@ -78,17 +78,17 @@ WizTree CLI/CSV is optional acceleration for project discovery only. It is not a Preferred stable wrapper: ```powershell -D:\projects\_tools\code-intel-pipeline\invoke-code-intel.ps1 -RepoPath -Mode normal +& "$env:CODE_INTEL_HOME/invoke-code-intel.ps1" -RepoPath -Mode normal ``` Batch wrappers: ```powershell -D:\projects\_tools\code-intel-pipeline\invoke-code-intel.ps1 -Config D:\projects\_tools\code-intel-pipeline\pipeline.config.json -Repos k-atana,glyph-arts -Mode normal -D:\projects\_tools\code-intel-pipeline\invoke-code-intel.ps1 -Config D:\projects\_tools\code-intel-pipeline\pipeline.config.json -All -Mode lite +& "$env:CODE_INTEL_HOME/invoke-code-intel.ps1" -Config "$env:CODE_INTEL_HOME/pipeline.config.json" -Repos k-atana,glyph-arts -Mode normal +& "$env:CODE_INTEL_HOME/invoke-code-intel.ps1" -Config "$env:CODE_INTEL_HOME/pipeline.config.json" -All -Mode lite ``` -Use `-Repo ` only when `pipeline.config.json` already defines that alias. Prefer `-RepoPath ` for teammate machines because their project disks may not be `D:`. +Use `-Repo ` only when `pipeline.config.json` already defines that alias. Prefer `-RepoPath ` for teammate machines because project disks and mount points differ across machines. Stop and report clearly if any required tool is missing: @@ -104,13 +104,13 @@ Stop and report clearly if any required tool is missing: 1. Run the stable wrapper first: ```powershell -D:\projects\_tools\code-intel-pipeline\invoke-code-intel.ps1 -RepoPath -Mode normal +& "$env:CODE_INTEL_HOME/invoke-code-intel.ps1" -RepoPath -Mode normal ``` 2. Use the raw pipeline only when a narrower mode or a special flag is needed: ```powershell -D:\projects\_tools\code-intel-pipeline\run-code-intel.ps1 -RepoPath -Mode normal +& "$env:CODE_INTEL_HOME/run-code-intel.ps1" -RepoPath -Mode normal ``` 3. Add `-RepowiseDocs` when the user wants scoped repowise wiki generation instead of index-only refresh. @@ -119,7 +119,7 @@ D:\projects\_tools\code-intel-pipeline\run-code-intel.ps1 -RepoPath Use `-Mode lite` for a cheap status check. Use `-Mode full` when a fresh Understand Anything graph is needed. -When a repo config defines `repowiseScopePaths` or `repowiseRootFiles`, the pipeline runs `repowise` inside a sparse shadow worktree under `%LOCALAPPDATA%\code-intel\repowise\` by default, or `CODE_INTEL_SHADOW_ROOT` when set. This is the default for noisy mono-repos with nested third-party repos. +When a repo config defines `repowiseScopePaths` or `repowiseRootFiles`, the pipeline runs `repowise` inside a sparse shadow worktree under `CODE_INTEL_SHADOW_ROOT` when set, otherwise under the platform code-intel data root. This is the default for noisy mono-repos with nested third-party repos. Scoped Repowise has a bounded timeout. Use `-RepowiseTimeoutSeconds ` when a huge or dirty repo should fail fast. A Repowise timeout is treated as an optional semantic-memory skip; Understand, Sentrux, and CodeNexus should still complete. @@ -159,9 +159,9 @@ Then rerun the pipeline. For an Agent coding session that needs Sentrux as a live guardrail, use the dedicated wrapper: ```powershell -D:\projects\_tools\code-intel-pipeline\Invoke-SentruxAgentTool.ps1 scan -D:\projects\_tools\code-intel-pipeline\Invoke-SentruxAgentTool.ps1 session_start -D:\projects\_tools\code-intel-pipeline\Invoke-SentruxAgentTool.ps1 session_end +& "$env:CODE_INTEL_HOME/Invoke-SentruxAgentTool.ps1" scan +& "$env:CODE_INTEL_HOME/Invoke-SentruxAgentTool.ps1" session_start +& "$env:CODE_INTEL_HOME/Invoke-SentruxAgentTool.ps1" session_end ``` It exposes exactly these tools: `scan`, `health`, `session_start`, `session_end`, `rescan`, `check_rules`, `evolution`, `dsm`, `test_gaps`, and `what_if`. Root paths are valid inputs: the wrapper automatically excludes dependency, build-output, cache, and bundled static-asset code from governed source metrics, and reports the filtered material under `scope.excluded_by_reason`. Use narrower scopes only when the team intentionally wants a separate baseline for a subsystem. @@ -219,7 +219,7 @@ If all four category counters are zero, say the run is clean instead of summariz Use only these absorbed rules from the Karpathy skills repo: -- Idea file first for nontrivial pipeline changes: use `templates\idea-file.md`. +- Idea file first for nontrivial pipeline changes: use `templates/idea-file.md`. - Agentic loop: doctor -> lite -> normal -> read summary -> read understanding -> fix -> rerun -> commit. - Minimalism: keep this as an orchestration shell over `rg`, `repowise`, Understand Anything, and `sentrux`; do not copy tool internals into this repo. - Implementation minimalism: before coding choose the first sufficient rung from `docs\implementation-minimalism-benchmark.md`: do nothing, reuse this repository, standard library, platform native capability, already-installed dependency, one-liner, then smallest local implementation. @@ -232,18 +232,18 @@ Use only these absorbed rules from the Karpathy skills repo: Legacy-heavy repos may configure `sentruxPath` in `pipeline.config.json` so Sentrux gates only the core area, such as `backend`. -Rules are separate from baselines. A baseline answers "did this session degrade structure"; `.sentrux/rules.toml` answers "did this session cross an architecture boundary." If `check_rules` reports `rules_missing`, copy `templates\sentrux-rules.example.toml` into the chosen scope and replace the sample layer/boundary names with real project boundaries. +Rules are separate from baselines. A baseline answers "did this session degrade structure"; `.sentrux/rules.toml` answers "did this session cross an architecture boundary." If `check_rules` reports `rules_missing`, copy `templates/sentrux-rules.example.toml` into the chosen scope and replace the sample layer/boundary names with real project boundaries. If baseline is missing, use one of: ```powershell -D:\projects\_tools\code-intel-pipeline\run-code-intel.ps1 -RepoPath -Mode normal -SaveSentruxBaseline +& "$env:CODE_INTEL_HOME/run-code-intel.ps1" -RepoPath -Mode normal -SaveSentruxBaseline ``` or: ```powershell -D:\projects\_tools\code-intel-pipeline\run-code-intel.ps1 -RepoPath -Mode normal -AutoSaveMissingSentruxBaseline +& "$env:CODE_INTEL_HOME/run-code-intel.ps1" -RepoPath -Mode normal -AutoSaveMissingSentruxBaseline ``` Do not save a new baseline to hide a real regression. @@ -257,43 +257,43 @@ For `k-atana`, broad `repowise init` at the repo root is wrong. The repo contain Stable team command: ```powershell -D:\projects\_tools\code-intel-pipeline\invoke-code-intel.ps1 -RepoPath -Mode normal +& "$env:CODE_INTEL_HOME/invoke-code-intel.ps1" -RepoPath -Mode normal ``` Docs-enabled variant: ```powershell -D:\projects\_tools\code-intel-pipeline\run-code-intel.ps1 -RepoPath -Mode normal -RepowiseDocs +& "$env:CODE_INTEL_HOME/run-code-intel.ps1" -RepoPath -Mode normal -RepowiseDocs ``` Smoke test: ```powershell -D:\projects\_tools\code-intel-pipeline\test-code-intel-pipeline.ps1 -RepoPath +& "$env:CODE_INTEL_HOME/test-code-intel-pipeline.ps1" -RepoPath ``` Provider preflight: ```powershell -D:\projects\_tools\code-intel-pipeline\test-code-intel-provider.ps1 -Json +& "$env:CODE_INTEL_HOME/test-code-intel-provider.ps1" -Json ``` Install check: ```powershell -D:\projects\_tools\code-intel-pipeline\install-code-intel-pipeline.ps1 -RepoPath -CheckProvider +& "$env:CODE_INTEL_HOME/install-code-intel-pipeline.ps1" -RepoPath -CheckProvider ``` Artifact index refresh: ```powershell -D:\projects\_tools\code-intel-pipeline\update-code-intel-index.ps1 +& "$env:CODE_INTEL_HOME/update-code-intel-index.ps1" ``` Scoped docs generation is available, but it is quota-sensitive and intentionally low-budget: ```powershell -D:\projects\_tools\code-intel-pipeline\Invoke-ScopedRepowise.ps1 -RepoPath -ScopePaths backend -RootFiles README.md,CLAUDE.md,pyproject.toml,requirements.txt,requirements-no-torch.txt,requirements-frozen.txt,.env.example,.gitignore -Docs +& "$env:CODE_INTEL_HOME/Invoke-ScopedRepowise.ps1" -RepoPath -ScopePaths backend -RootFiles README.md,CLAUDE.md,pyproject.toml,requirements.txt,requirements-no-torch.txt,requirements-frozen.txt,.env.example,.gitignore -Docs ``` That path uses `Run-ScopedRepowiseDocs.py` with `coverage_pct=0.02`. If the provider is rate-limited, expect `docs_enabled=false` with a `docs_skip_reason` that points at provider quota rather than local tool failure. @@ -322,5 +322,5 @@ Check results in this order: For machine checks, use: ```powershell -D:\projects\_tools\code-intel-pipeline\check-code-intel-tools.ps1 -RepoPath -Json +& "$env:CODE_INTEL_HOME/check-code-intel-tools.ps1" -RepoPath -Json ``` diff --git a/templates/minimax-deploy-checklist.md b/templates/minimax-deploy-checklist.md index fc13017..00e07c1 100644 --- a/templates/minimax-deploy-checklist.md +++ b/templates/minimax-deploy-checklist.md @@ -1,6 +1,6 @@ # MiniMax / Agent 部署模板 -目标:在一台新 Windows 机器上,把 Code Intel Pipeline 跑到可用状态。 +目标:在一台新机器上,把 Code Intel Pipeline 跑到可用状态。 ## 输入 @@ -33,8 +33,8 @@ cd code-intel-pipeline ## 如果失败 -- 先读 `%LOCALAPPDATA%\code-intel\bootstrap\bootstrap-*.md` +- 先读平台 code-intel data root 下的 `bootstrap/bootstrap-*.md` - 再读 smoke artifact 里的 `summary.md` -- 如果缺真实 `sentrux.exe`,不用先修;shim 会启用 lite core +- 如果缺真实 `sentrux`,不用先修;shim 会启用 lite core - 如果缺 Understand Anything,先接受 manual step,再按报告里的 `/understand ...` 命令补图谱 - 如果缺 provider key,只影响 repowise docs,不影响基础结构扫描 diff --git a/test-code-intel-pipeline.ps1 b/test-code-intel-pipeline.ps1 index 8fc1d00..7b29e36 100644 --- a/test-code-intel-pipeline.ps1 +++ b/test-code-intel-pipeline.ps1 @@ -1,24 +1,34 @@ +#requires -Version 7.2 + param( [string]$Repo = "", [string]$RepoPath = "", [string]$Config = "", + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto", + [ValidateSet("lite", "normal", "full")] [string]$Mode = "normal", [string]$SentruxPath = "", -[switch]$SkipRepowise, -[switch]$RepowiseDocs, -[switch]$AllowGraphMissing, -[switch]$SkipSentruxGate, -[switch]$SkipGitHubResearch + [switch]$SkipRepowise, + [switch]$RepowiseDocs, + [switch]$AllowGraphMissing, + [switch]$SkipSentruxCheck, + [switch]$SkipSentruxGate, + [switch]$SkipGitHubResearch ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" +$platformModule = Join-Path (Join-Path $PSScriptRoot "tools") "code-intel-platform.psm1" +Import-Module $platformModule -Force +$effectivePlatform = Get-CodeIntelPlatform -Platform $Platform + function Read-JsonFile { param([string]$Path) return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json @@ -38,10 +48,10 @@ if ([string]::IsNullOrWhiteSpace($label)) { } $doctorJson = if (-not [string]::IsNullOrWhiteSpace($RepoPath)) { - & $doctor -Config $Config -RepoPath $RepoPath -Json | ConvertFrom-Json + & $doctor -Config $Config -RepoPath $RepoPath -Platform $effectivePlatform -Json | ConvertFrom-Json } else { - & $doctor -Config $Config -Repo $Repo -Json | ConvertFrom-Json + & $doctor -Config $Config -Repo $Repo -Platform $effectivePlatform -Json | ConvertFrom-Json } if (-not $doctorJson.ok) { throw "Doctor failed: $($doctorJson.missing -join ', ')" @@ -50,6 +60,7 @@ if (-not $doctorJson.ok) { $runnerParams = @{ Config = $Config Mode = $Mode + Platform = $effectivePlatform } if (-not [string]::IsNullOrWhiteSpace($RepoPath)) { $runnerParams.RepoPath = $RepoPath @@ -66,6 +77,9 @@ if ($SkipRepowise) { if ($RepowiseDocs) { $runnerParams.RepowiseDocs = $true } +if ($SkipSentruxCheck) { + $runnerParams.SkipSentruxCheck = $true +} if ($SkipSentruxGate) { $runnerParams.SkipSentruxGate = $true } @@ -82,8 +96,7 @@ $artifactRoot = if ($doctorJson.checks -and $doctorJson.checks.config -and (Test } else { "" } if ([string]::IsNullOrWhiteSpace($artifactRoot)) { - $base = if (-not [string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) { $env:LOCALAPPDATA } else { (Join-Path $HOME ".code-intel") } - $artifactRoot = Join-Path $base "code-intel\artifacts" + $artifactRoot = Get-CodeIntelArtifactRoot -Platform $effectivePlatform } $artifactDir = Get-ChildItem -Path (Join-Path $artifactRoot $repoName) -Directory | @@ -416,6 +429,14 @@ $result = [ordered]@{ ok = $true repo = $label mode = $Mode + platform = [ordered]@{ + os = $effectivePlatform + shell = $PSVersionTable.PSEdition + psVersion = $PSVersionTable.PSVersion.ToString() + } + paths = [ordered]@{ + artifactRoot = $artifactRoot + } artifactDir = $artifactDir.FullName report = $reportPath summary = $summaryPath diff --git a/test-code-intel-provider.ps1 b/test-code-intel-provider.ps1 index 14ad92e..2f6c7cf 100644 --- a/test-code-intel-provider.ps1 +++ b/test-code-intel-provider.ps1 @@ -1,3 +1,5 @@ +#requires -Version 7.2 + param( [string]$Provider = "anthropic", [string]$Model = "MiniMax-M2.7", @@ -7,6 +9,9 @@ param( Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" +$platformModule = Join-Path (Join-Path $PSScriptRoot "tools") "code-intel-platform.psm1" +Import-Module $platformModule -Force + function Set-EnvFromUserRegistry { param([string]$Name) @@ -22,7 +27,7 @@ function Set-EnvFromUserRegistry { Set-EnvFromUserRegistry "ANTHROPIC_API_KEY" Set-EnvFromUserRegistry "ANTHROPIC_BASE_URL" -$python = @' +$pythonScript = @' import json import os import sys @@ -68,7 +73,20 @@ sys.exit(0 if result["ok"] else 1) $env:CODE_INTEL_PROVIDER = $Provider $env:CODE_INTEL_MODEL = $Model -$raw = & python -c $python +$python = Get-CodeIntelPythonCommand +if (-not $python) { + $result = [pscustomobject][ordered]@{ + ok = $false + provider = $Provider + model = $Model + category = "local_tool_error" + message = "python/python3 is not on PATH" + } + if ($Json) { $result | ConvertTo-Json -Depth 4 } else { Write-Host "Provider preflight: FAILED local_tool_error $Provider/$Model"; Write-Host $result.message } + exit 1 +} +$pythonCommand = if (-not [string]::IsNullOrWhiteSpace($python.Source)) { $python.Source } else { $python.Name } +$raw = & $pythonCommand -c $pythonScript $exitCode = $LASTEXITCODE $result = $raw | ConvertFrom-Json diff --git a/tools/code-intel-platform.psm1 b/tools/code-intel-platform.psm1 new file mode 100644 index 0000000..b6aa7ae --- /dev/null +++ b/tools/code-intel-platform.psm1 @@ -0,0 +1,283 @@ +#requires -Version 7.2 + +Set-StrictMode -Version Latest + +function Get-CodeIntelPlatform { + param( + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto" + ) + + if ($Platform -ne "auto") { return $Platform } + if ($IsWindows) { return "windows" } + if ($IsMacOS) { return "macos" } + if ($IsLinux) { return "linux" } + throw "Unsupported platform. Pass -Platform windows|macos|linux." +} + +function Get-CodeIntelHome { + param([string]$Root = "") + + if (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_HOME)) { + return (Resolve-CodeIntelPath $env:CODE_INTEL_HOME) + } + if (-not [string]::IsNullOrWhiteSpace($Root)) { + return (Resolve-CodeIntelPath $Root) + } + return (Resolve-CodeIntelPath (Get-Location).Path) +} + +function Resolve-CodeIntelPath { + param([Parameter(Mandatory = $true)][string]$Path) + + if (Test-Path -LiteralPath $Path) { + return (Get-Item -LiteralPath $Path).FullName + } + return [System.IO.Path]::GetFullPath($Path) +} + +function Get-CodeIntelHomeDirectory { + $homeDir = [Environment]::GetFolderPath([Environment+SpecialFolder]::UserProfile) + if ([string]::IsNullOrWhiteSpace($homeDir)) { $homeDir = $HOME } + return (Resolve-CodeIntelPath $homeDir) +} + +function Get-CodeIntelDataRoot { + param( + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto" + ) + + if (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_DATA_ROOT)) { + return (Resolve-CodeIntelPath $env:CODE_INTEL_DATA_ROOT) + } + + $os = Get-CodeIntelPlatform -Platform $Platform + $homeDir = Get-CodeIntelHomeDirectory + switch ($os) { + "windows" { + $base = [Environment]::GetFolderPath([Environment+SpecialFolder]::LocalApplicationData) + if ([string]::IsNullOrWhiteSpace($base)) { $base = Join-Path $homeDir ".code-intel" } + return (Join-Path $base "code-intel") + } + "macos" { + return (Join-Path (Join-Path $homeDir "Library") (Join-Path "Application Support" "code-intel")) + } + "linux" { + $base = if (-not [string]::IsNullOrWhiteSpace($env:XDG_DATA_HOME)) { + $env:XDG_DATA_HOME + } + else { + Join-Path (Join-Path $homeDir ".local") "share" + } + return (Join-Path $base "code-intel") + } + } +} + +function Get-CodeIntelBinDir { + param( + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto" + ) + + if (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_BIN)) { + return (Resolve-CodeIntelPath $env:CODE_INTEL_BIN) + } + return (Join-Path (Get-CodeIntelDataRoot -Platform $Platform) "bin") +} + +function Get-CodeIntelPythonCommand { + foreach ($name in @("python", "python3")) { + $cmd = Get-Command $name -ErrorAction SilentlyContinue + if ($cmd) { return $cmd } + } + return $null +} + +function Get-CodeIntelArtifactRoot { + param( + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto" + ) + + $fromUser = [Environment]::GetEnvironmentVariable("CODE_INTEL_ARTIFACT_ROOT", "User") + if (-not [string]::IsNullOrWhiteSpace($fromUser)) { return (Resolve-CodeIntelPath $fromUser) } + if (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_ARTIFACT_ROOT)) { + return (Resolve-CodeIntelPath $env:CODE_INTEL_ARTIFACT_ROOT) + } + return (Join-Path (Get-CodeIntelDataRoot -Platform $Platform) "artifacts") +} + +function Get-CodeIntelShadowRoot { + param( + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto" + ) + + $fromUser = [Environment]::GetEnvironmentVariable("CODE_INTEL_SHADOW_ROOT", "User") + if (-not [string]::IsNullOrWhiteSpace($fromUser)) { return (Resolve-CodeIntelPath $fromUser) } + if (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_SHADOW_ROOT)) { + return (Resolve-CodeIntelPath $env:CODE_INTEL_SHADOW_ROOT) + } + return (Join-Path (Get-CodeIntelDataRoot -Platform $Platform) "repowise") +} + +function Get-CodeIntelPaths { + param( + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto", + [string]$Root = "" + ) + + $os = Get-CodeIntelPlatform -Platform $Platform + $dataRoot = Get-CodeIntelDataRoot -Platform $os + return [pscustomobject][ordered]@{ + home = Get-CodeIntelHomeDirectory + dataRoot = $dataRoot + bin = Get-CodeIntelBinDir -Platform $os + artifactRoot = Get-CodeIntelArtifactRoot -Platform $os + shadowRoot = Get-CodeIntelShadowRoot -Platform $os + codeIntelHome = Get-CodeIntelHome -Root $Root + } +} + +function Set-CodeIntelUserEnv { + param( + [Parameter(Mandatory = $true)][string]$Name, + [Parameter(Mandatory = $true)][string]$Value, + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto" + ) + + Set-Item -LiteralPath "env:$Name" -Value $Value + $os = Get-CodeIntelPlatform -Platform $Platform + if ($os -eq "windows") { + [Environment]::SetEnvironmentVariable($Name, $Value, "User") + return [pscustomobject][ordered]@{ name = $Name; persisted = $true; detail = "user environment" } + } + + $configDir = Join-Path (Join-Path (Get-CodeIntelHomeDirectory) ".config") "code-intel" + New-Item -ItemType Directory -Force -Path $configDir | Out-Null + $envFile = Join-Path $configDir "env.ps1" + $escaped = $Value.Replace("'", "''") + "`$env:$Name = '$escaped'" | Set-Content -LiteralPath $envFile -Encoding UTF8 + return [pscustomobject][ordered]@{ + name = $Name + persisted = $false + detail = "process environment set; dot-source $envFile from your pwsh profile to persist" + } +} + +function Add-UserPathPrefix { + param( + [Parameter(Mandatory = $true)][string]$PathToAdd, + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto" + ) + + $resolved = (New-Item -ItemType Directory -Force -Path $PathToAdd).FullName.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar) + $separator = [System.IO.Path]::PathSeparator + + $processParts = @($env:PATH -split [regex]::Escape([string]$separator) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) + $processParts = @($processParts | Where-Object { + $entry = $_.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar) + -not [string]::Equals($entry, $resolved, [System.StringComparison]::OrdinalIgnoreCase) + }) + $env:PATH = (($resolved) + $separator + ($processParts -join $separator)).TrimEnd($separator) + + $os = Get-CodeIntelPlatform -Platform $Platform + if ($os -eq "windows") { + $userPath = [Environment]::GetEnvironmentVariable("Path", "User") + $userParts = @($userPath -split [regex]::Escape([string]$separator) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) + $userParts = @($userParts | Where-Object { + $entry = $_.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar) + -not [string]::Equals($entry, $resolved, [System.StringComparison]::OrdinalIgnoreCase) + }) + [Environment]::SetEnvironmentVariable("Path", (($resolved) + $separator + ($userParts -join $separator)).TrimEnd($separator), "User") + return [pscustomobject][ordered]@{ path = $resolved; persisted = $true; detail = "user PATH" } + } + + return [pscustomobject][ordered]@{ + path = $resolved + persisted = $false + detail = "process PATH only; add this directory to your shell profile" + } +} + +function New-CodeIntelLink { + param( + [Parameter(Mandatory = $true)][string]$Path, + [Parameter(Mandatory = $true)][string]$Target, + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto" + ) + + if (Test-Path -LiteralPath $Path) { + return [pscustomobject][ordered]@{ ok = $true; mode = "existing"; path = $Path; target = $Target } + } + if (-not (Test-Path -LiteralPath $Target -PathType Container)) { + return [pscustomobject][ordered]@{ ok = $false; mode = "missing_target"; path = $Path; target = $Target } + } + + $parent = Split-Path -Parent $Path + New-Item -ItemType Directory -Force -Path $parent | Out-Null + $os = Get-CodeIntelPlatform -Platform $Platform + + try { + if ($os -eq "windows") { + New-Item -ItemType Junction -Path $Path -Target $Target | Out-Null + return [pscustomobject][ordered]@{ ok = $true; mode = "junction"; path = $Path; target = $Target } + } + + New-Item -ItemType SymbolicLink -Path $Path -Target $Target | Out-Null + return [pscustomobject][ordered]@{ ok = $true; mode = "symlink"; path = $Path; target = $Target } + } + catch { + Copy-Item -LiteralPath $Target -Destination $Path -Recurse -Force + return [pscustomobject][ordered]@{ ok = $true; mode = "copy"; path = $Path; target = $Target; warning = $_.Exception.Message } + } +} + +function Invoke-CodeIntelNative { + param( + [Parameter(Mandatory = $true)][string]$Command, + [string[]]$Arguments = @() + ) + + $global:LASTEXITCODE = 0 + $started = Get-Date + $previousErrorActionPreference = $ErrorActionPreference + try { + $ErrorActionPreference = "Continue" + $output = & $Command @Arguments 2>&1 + } + finally { + $ErrorActionPreference = $previousErrorActionPreference + } + $finished = Get-Date + + return [pscustomobject][ordered]@{ + command = ($Command + " " + ($Arguments -join " ")).Trim() + exitCode = $global:LASTEXITCODE + output = ($output | ForEach-Object { $_.ToString() } | Out-String).Trim() + durationMs = [int]($finished - $started).TotalMilliseconds + } +} + +Export-ModuleMember -Function @( + "Get-CodeIntelPlatform", + "Get-CodeIntelHome", + "Resolve-CodeIntelPath", + "Get-CodeIntelHomeDirectory", + "Get-CodeIntelDataRoot", + "Get-CodeIntelBinDir", + "Get-CodeIntelPythonCommand", + "Get-CodeIntelArtifactRoot", + "Get-CodeIntelShadowRoot", + "Get-CodeIntelPaths", + "Set-CodeIntelUserEnv", + "Add-UserPathPrefix", + "New-CodeIntelLink", + "Invoke-CodeIntelNative" +) diff --git a/tools/sentrux-shim/sentrux b/tools/sentrux-shim/sentrux new file mode 100644 index 0000000..c160982 --- /dev/null +++ b/tools/sentrux-shim/sentrux @@ -0,0 +1,3 @@ +#!/usr/bin/env sh +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +exec pwsh -NoProfile -File "$SCRIPT_DIR/sentrux-shim.ps1" "$@" diff --git a/tools/sentrux-shim/sentrux-lite-core.ps1 b/tools/sentrux-shim/sentrux-lite-core.ps1 index 00df1d9..c1c6b62 100644 --- a/tools/sentrux-shim/sentrux-lite-core.ps1 +++ b/tools/sentrux-shim/sentrux-lite-core.ps1 @@ -1,3 +1,5 @@ +#requires -Version 7.2 + [CmdletBinding()] param( [Parameter(Position = 0, ValueFromRemainingArguments = $true)] @@ -372,7 +374,7 @@ function Get-PluginRoot { return $env:SENTRUX_PLUGIN_ROOT } $userProfile = if (-not [string]::IsNullOrWhiteSpace($env:USERPROFILE)) { $env:USERPROFILE } else { $HOME } - return (Join-Path $userProfile ".sentrux\plugins") + return (Join-Path $userProfile (Join-Path ".sentrux" "plugins")) } function Read-PluginTomlField { @@ -406,10 +408,10 @@ function Test-PluginPath { return } $pluginToml = Join-Path $target "plugin.toml" - $queryPath = Join-Path $target "queries\tags.scm" - $grammarPath = Join-Path $target "grammars\windows-x86_64.dll" + $queryPath = Join-Path $target (Join-Path "queries" "tags.scm") + $grammarDir = Join-Path $target "grammars" - foreach ($required in @($pluginToml, $queryPath, $grammarPath)) { + foreach ($required in @($pluginToml, $queryPath)) { if (-not (Test-Path -LiteralPath $required -PathType Leaf)) { Write-Output "plugin invalid: missing $required" $script:SentruxLiteExitCode = 1 @@ -418,6 +420,18 @@ function Test-PluginPath { } $toml = Get-Content -LiteralPath $pluginToml -Raw + if ($toml -match "(?m)^\s*\[grammar\]\s*$") { + $grammarFiles = @() + if (Test-Path -LiteralPath $grammarDir -PathType Container) { + $grammarFiles = @(Get-ChildItem -LiteralPath $grammarDir -File -ErrorAction SilentlyContinue) + } + if ($grammarFiles.Count -eq 0) { + Write-Output "plugin invalid: grammar artifact missing under $grammarDir" + $script:SentruxLiteExitCode = 1 + return + } + } + $name = Read-PluginTomlField $toml "name" $version = Read-PluginTomlField $toml "version" $extensions = @(Read-PluginExtensions $toml) diff --git a/tools/sentrux-shim/sentrux-shim.ps1 b/tools/sentrux-shim/sentrux-shim.ps1 index f6ff02b..19368f9 100644 --- a/tools/sentrux-shim/sentrux-shim.ps1 +++ b/tools/sentrux-shim/sentrux-shim.ps1 @@ -1,3 +1,5 @@ +#requires -Version 7.2 + [CmdletBinding()] param( [Parameter(Position = 0, ValueFromRemainingArguments = $true)] @@ -22,10 +24,26 @@ function Get-LicensePath { if (-not [string]::IsNullOrWhiteSpace($env:SENTRUX_LICENSE_FILE)) { return $env:SENTRUX_LICENSE_FILE } - if (-not [string]::IsNullOrWhiteSpace($env:APPDATA)) { - return (Join-Path $env:APPDATA "sentrux\license.json") + + $homeDir = [Environment]::GetFolderPath([Environment+SpecialFolder]::UserProfile) + if ([string]::IsNullOrWhiteSpace($homeDir)) { $homeDir = $HOME } + + if ($IsWindows) { + $base = [Environment]::GetFolderPath([Environment+SpecialFolder]::ApplicationData) + if ([string]::IsNullOrWhiteSpace($base)) { $base = $homeDir } + return (Join-Path (Join-Path $base "sentrux") "license.json") + } + if ($IsMacOS) { + return (Join-Path (Join-Path (Join-Path $homeDir "Library") "Application Support") (Join-Path "sentrux" "license.json")) + } + + $configBase = if (-not [string]::IsNullOrWhiteSpace($env:XDG_CONFIG_HOME)) { + $env:XDG_CONFIG_HOME + } + else { + Join-Path $homeDir ".config" } - return (Join-Path $HOME ".sentrux\license.json") + return (Join-Path (Join-Path $configBase "sentrux") "license.json") } function Get-AutoDisabledPath { @@ -171,26 +189,33 @@ function Resolve-Core { $candidates.Add($path) } - $pathEntries = @($env:PATH -split ";" | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) + $separator = [System.IO.Path]::PathSeparator + $pathEntries = @($env:PATH -split [regex]::Escape([string]$separator) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) foreach ($entry in $pathEntries) { $fullEntry = try { (Get-Item -LiteralPath $entry -ErrorAction Stop).FullName } catch { $entry } - if ([string]::Equals($fullEntry.TrimEnd('\'), $shimDir.TrimEnd('\'), [System.StringComparison]::OrdinalIgnoreCase)) { + $fullEntryTrimmed = $fullEntry.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar) + $shimDirTrimmed = $shimDir.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar) + if ([string]::Equals($fullEntryTrimmed, $shimDirTrimmed, [System.StringComparison]::OrdinalIgnoreCase)) { continue } $candidates.Add((Join-Path $entry "sentrux.exe")) $candidates.Add((Join-Path $entry "sentrux-core.exe")) + $candidates.Add((Join-Path $entry "sentrux")) + $candidates.Add((Join-Path $entry "sentrux-core")) } $selfCmd = Join-Path $shimDir "sentrux.cmd" + $selfShell = Join-Path $shimDir "sentrux" foreach ($candidate in $candidates) { if (-not (Test-Path -LiteralPath $candidate -PathType Leaf)) { continue } $full = (Get-Item -LiteralPath $candidate).FullName if ([string]::Equals($full, $selfCmd, [System.StringComparison]::OrdinalIgnoreCase)) { continue } + if ([string]::Equals($full, $selfShell, [System.StringComparison]::OrdinalIgnoreCase)) { continue } if ([string]::Equals($full, $PSCommandPath, [System.StringComparison]::OrdinalIgnoreCase)) { continue } return $full } - throw "Sentrux core executable not found. Install sentrux.exe or set SENTRUX_CORE_EXE." + throw "Sentrux core executable not found. Install sentrux or set SENTRUX_CORE_EXE." } function Inject-ProHelp { @@ -224,9 +249,9 @@ function Invoke-Core { $shimDir = Split-Path -Parent $PSCommandPath $liteCore = Join-Path $shimDir "sentrux-lite-core.ps1" if (-not (Test-Path -LiteralPath $liteCore -PathType Leaf)) { - throw "Sentrux core executable not found and lite core is missing. Install sentrux.exe or restore sentrux-lite-core.ps1." + throw "Sentrux core executable not found and lite core is missing. Install sentrux or restore sentrux-lite-core.ps1." } - & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $liteCore @CoreArgs + & $liteCore @CoreArgs } exit $LASTEXITCODE } @@ -272,7 +297,7 @@ if ($RemainingArgs.Count -eq 0 -or $RemainingArgs[0] -in @("-h", "--help", "help $shimDir = Split-Path -Parent $PSCommandPath $liteCore = Join-Path $shimDir "sentrux-lite-core.ps1" if (Test-Path -LiteralPath $liteCore -PathType Leaf) { - $help = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $liteCore --help 2>&1 | Out-String + $help = & $liteCore --help 2>&1 | Out-String Write-Output (Inject-ProHelp $help) } else { diff --git a/tools/sentrux-shim/sentrux.cmd b/tools/sentrux-shim/sentrux.cmd index 95683f5..43656bc 100644 --- a/tools/sentrux-shim/sentrux.cmd +++ b/tools/sentrux-shim/sentrux.cmd @@ -1,2 +1,2 @@ @echo off -powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0sentrux-shim.ps1" %* +pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0sentrux-shim.ps1" %* diff --git a/update-code-intel-index.ps1 b/update-code-intel-index.ps1 index 5b2f803..ab4b1ff 100644 --- a/update-code-intel-index.ps1 +++ b/update-code-intel-index.ps1 @@ -1,28 +1,26 @@ +#requires -Version 7.2 + param( [string]$ArtifactRoot = "", - [string]$OutputPath = "" + [string]$OutputPath = "", + [ValidateSet("auto", "windows", "macos", "linux")] + [string]$Platform = "auto" ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" +$platformModule = Join-Path (Join-Path $PSScriptRoot "tools") "code-intel-platform.psm1" +Import-Module $platformModule -Force +$effectivePlatform = Get-CodeIntelPlatform -Platform $Platform + function Read-JsonFile { param([string]$Path) return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json } if ([string]::IsNullOrWhiteSpace($ArtifactRoot)) { - $fromEnv = [Environment]::GetEnvironmentVariable("CODE_INTEL_ARTIFACT_ROOT", "User") - if (-not [string]::IsNullOrWhiteSpace($fromEnv)) { - $ArtifactRoot = $fromEnv - } - elseif (-not [string]::IsNullOrWhiteSpace($env:CODE_INTEL_ARTIFACT_ROOT)) { - $ArtifactRoot = $env:CODE_INTEL_ARTIFACT_ROOT - } - else { - $base = if (-not [string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) { $env:LOCALAPPDATA } else { (Join-Path $HOME ".code-intel") } - $ArtifactRoot = Join-Path $base "code-intel\artifacts" - } + $ArtifactRoot = Get-CodeIntelArtifactRoot -Platform $effectivePlatform } if ([string]::IsNullOrWhiteSpace($OutputPath)) { From 4ff76fa1832924cf1250c1e74546e7bee8f992aa Mon Sep 17 00:00:00 2001 From: 2233admin Date: Sat, 20 Jun 2026 18:15:05 +0800 Subject: [PATCH 2/6] build code-nexus-lite in GitHub Actions --- .github/workflows/ci.yml | 11 +---- tools/check-hardcoded-paths.ps1 | 72 +++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 tools/check-hardcoded-paths.ps1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b74cea0..88baa96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -283,13 +283,4 @@ jobs: - name: Hardcoded path scan shell: pwsh - run: | - $patterns = 'D:\\|C:\\Users\\Administrator|LOCALAPPDATA|USERPROFILE|APPDATA|powershell\.exe' - $hits = rg -n $patterns -g '*.ps1' -g '*.psm1' -g '*.md' -g '*.yml' - if ($LASTEXITCODE -eq 0) { - $hits - throw "Hardcoded machine or Windows-only path references found." - } - if ($LASTEXITCODE -gt 1) { - throw "Hardcoded path scan failed with exit code $LASTEXITCODE." - } + run: ./tools/check-hardcoded-paths.ps1 diff --git a/tools/check-hardcoded-paths.ps1 b/tools/check-hardcoded-paths.ps1 new file mode 100644 index 0000000..030d609 --- /dev/null +++ b/tools/check-hardcoded-paths.ps1 @@ -0,0 +1,72 @@ +#requires -Version 7.2 + +[CmdletBinding()] +param( + [switch]$Json +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" +$PSNativeCommandUseErrorActionPreference = $false + +$root = Split-Path -Parent $PSScriptRoot +$slash = [string][char]92 +$patterns = @( + ("D:" + $slash + $slash), + ("C:" + $slash + $slash + "Users" + $slash + $slash + "Administrator"), + ("LOCAL" + "APPDATA"), + ("USER" + "PROFILE"), + ("APP" + "DATA"), + ("power" + "shell.exe") +) +$pattern = [regex](($patterns | ForEach-Object { [regex]::Escape($_) }) -join "|") +$globs = @("*.ps1", "*.psm1", "*.md", "*.yml") + +Push-Location $root +try { + $files = @(& git ls-files -- $globs) + if ($LASTEXITCODE -ne 0) { + throw "git ls-files failed with exit code $LASTEXITCODE" + } + + $hits = New-Object System.Collections.Generic.List[object] + foreach ($file in $files) { + if ([string]::IsNullOrWhiteSpace($file)) { continue } + $lineNumber = 0 + foreach ($line in Get-Content -LiteralPath $file -ErrorAction Stop) { + $lineNumber++ + if ($pattern.IsMatch($line)) { + $hits.Add([pscustomobject][ordered]@{ + file = $file + line = $lineNumber + text = "$file`:$lineNumber`:$line" + }) + } + } + } + + $result = [pscustomobject][ordered]@{ + ok = $hits.Count -eq 0 + scannedFiles = $files.Count + hits = $hits + } +} +finally { + Pop-Location +} + +if ($Json) { + $result | ConvertTo-Json -Depth 6 +} +else { + if ($result.ok) { + Write-Host "Hardcoded path scan: OK ($($result.scannedFiles) files)" + } + else { + Write-Host "Hardcoded path scan: FAILED" + $result.hits | ForEach-Object { Write-Host $_.text } + } +} + +if (-not $result.ok) { exit 1 } +exit 0 From ae6e9f7cfbbd8c722d5cb29e2cb04bd07e1eefdb Mon Sep 17 00:00:00 2001 From: 2233admin Date: Sat, 20 Jun 2026 18:19:08 +0800 Subject: [PATCH 3/6] fix hardcoded path scan --- tools/check-hardcoded-paths.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/check-hardcoded-paths.ps1 b/tools/check-hardcoded-paths.ps1 index 030d609..bca65d4 100644 --- a/tools/check-hardcoded-paths.ps1 +++ b/tools/check-hardcoded-paths.ps1 @@ -12,12 +12,12 @@ $PSNativeCommandUseErrorActionPreference = $false $root = Split-Path -Parent $PSScriptRoot $slash = [string][char]92 $patterns = @( - ("D:" + $slash + $slash), - ("C:" + $slash + $slash + "Users" + $slash + $slash + "Administrator"), - ("LOCAL" + "APPDATA"), - ("USER" + "PROFILE"), + ("D:" + $slash), + ("C:" + $slash + "Users" + $slash + "Administrator"), + ("LOCAL" + "APP" + "DATA"), + ("USER" + "PRO" + "FILE"), ("APP" + "DATA"), - ("power" + "shell.exe") + ("power" + "shell" + ".exe") ) $pattern = [regex](($patterns | ForEach-Object { [regex]::Escape($_) }) -join "|") $globs = @("*.ps1", "*.psm1", "*.md", "*.yml") From 6b6f0cbcf3834c214ae267ffb5b71a6714f96cbc Mon Sep 17 00:00:00 2001 From: 2233admin Date: Sat, 20 Jun 2026 18:34:00 +0800 Subject: [PATCH 4/6] address cross-platform review gaps --- .github/workflows/ci.yml | 3 ++ Install-SentruxVlangOverlay.ps1 | 2 +- Invoke-ScopedRepowise.ps1 | 56 ++++++++++++++++++++++++++++----- Invoke-SentruxAgentTool.ps1 | 4 +-- bootstrap-new-machine.ps1 | 1 + install-code-intel-pipeline.ps1 | 2 +- invoke-code-intel.ps1 | 1 + run-code-intel.ps1 | 6 ++-- tools/code-intel-platform.psm1 | 6 ++-- 9 files changed, 65 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88baa96..4beacba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -163,6 +163,9 @@ jobs: path: dist/code-intel-pipeline-windows.zip cross-platform-smoke: + permissions: + contents: read + strategy: fail-fast: false matrix: diff --git a/Install-SentruxVlangOverlay.ps1 b/Install-SentruxVlangOverlay.ps1 index 8e6e07a..776352e 100644 --- a/Install-SentruxVlangOverlay.ps1 +++ b/Install-SentruxVlangOverlay.ps1 @@ -75,7 +75,7 @@ foreach ($relativePath in $requiredFiles) { missing = $sourcePath message = "No vlang grammar artifact is bundled for this platform; skipping overlay install." } | ConvertTo-Json -Depth 4 - exit 0 + return } throw "Overlay file missing: $sourcePath" } diff --git a/Invoke-ScopedRepowise.ps1 b/Invoke-ScopedRepowise.ps1 index 4e51b8d..37696ee 100644 --- a/Invoke-ScopedRepowise.ps1 +++ b/Invoke-ScopedRepowise.ps1 @@ -71,6 +71,42 @@ function Invoke-RobocopyMirror { return } + if ($effectivePlatform -ne "windows" -and (Get-Command rsync -ErrorAction SilentlyContinue)) { + New-Item -ItemType Directory -Force -Path $Destination | Out-Null + $sourceArg = $Source.TrimEnd("/", "\") + "/" + $destinationArg = $Destination.TrimEnd("/", "\") + "/" + $rsyncArgs = @( + "-a", + "--delete", + "--exclude=.git", + "--exclude=.repowise", + "--exclude=node_modules", + "--exclude=.venv", + "--exclude=venv", + "--exclude=__pycache__", + "--exclude=.pytest_cache", + "--exclude=.mypy_cache", + "--exclude=tmp", + "--exclude=dist", + "--exclude=build", + "--exclude=target", + "--exclude=.understand-anything", + "--exclude=.sentrux", + "--exclude=*.egg-info", + "--exclude=uv.lock", + "--exclude=uv.lock.bak", + "--exclude=*.bak", + "--exclude==*", + $sourceArg, + $destinationArg + ) + & rsync @rsyncArgs + if ($LASTEXITCODE -ne 0) { + throw "rsync failed for $Source -> $Destination (exit $LASTEXITCODE)" + } + return + } + if (Test-Path -LiteralPath $Destination -PathType Container) { Remove-Item -LiteralPath $Destination -Recurse -Force } @@ -209,14 +245,18 @@ function Invoke-ProcessWithTimeout { $stdout = Join-Path ([System.IO.Path]::GetTempPath()) ("code-intel-{0}-out.txt" -f ([System.Guid]::NewGuid().ToString("N"))) $stderr = Join-Path ([System.IO.Path]::GetTempPath()) ("code-intel-{0}-err.txt" -f ([System.Guid]::NewGuid().ToString("N"))) try { - $process = Start-Process ` - -FilePath $FilePath ` - -ArgumentList $ArgumentList ` - -WorkingDirectory $WorkingDirectory ` - -RedirectStandardOutput $stdout ` - -RedirectStandardError $stderr ` - -PassThru ` - -WindowStyle Hidden + $startProcessParams = @{ + FilePath = $FilePath + ArgumentList = $ArgumentList + WorkingDirectory = $WorkingDirectory + RedirectStandardOutput = $stdout + RedirectStandardError = $stderr + PassThru = $true + } + if ($effectivePlatform -eq "windows") { + $startProcessParams.WindowStyle = "Hidden" + } + $process = Start-Process @startProcessParams $finished = $process.WaitForExit([math]::Max(1, $TimeoutSeconds) * 1000) if (-not $finished) { diff --git a/Invoke-SentruxAgentTool.ps1 b/Invoke-SentruxAgentTool.ps1 index de70b6e..2adfc1b 100644 --- a/Invoke-SentruxAgentTool.ps1 +++ b/Invoke-SentruxAgentTool.ps1 @@ -273,7 +273,7 @@ function Find-ScopeCandidates { $items = @() try { $items = Get-ChildItem -LiteralPath $TargetPath -Recurse -Filter "baseline.json" -File -ErrorAction SilentlyContinue | - Where-Object { $_.FullName -match "\\.sentrux\\baseline\.json$" } | + Where-Object { $_.FullName -match "[\\/]\.sentrux[\\/]baseline\.json$" } | Select-Object -First 12 } catch { @@ -328,7 +328,7 @@ function Get-PollutionSignals { $signals = @() foreach ($entry in $noisyDirs) { $dir = [string]$entry["path"] - $normalizedDir = $dir.ToLowerInvariant() + $normalizedDir = $dir.ToLowerInvariant().Replace("/", [System.IO.Path]::DirectorySeparatorChar) if ($ignored -contains $normalizedDir) { continue } $full = Join-Path $TargetPath $dir if (-not (Test-Path -LiteralPath $full -PathType Container)) { continue } diff --git a/bootstrap-new-machine.ps1 b/bootstrap-new-machine.ps1 index 33ae2af..9219f7f 100644 --- a/bootstrap-new-machine.ps1 +++ b/bootstrap-new-machine.ps1 @@ -96,6 +96,7 @@ if (-not $SkipSmoke) { $smokeParams = @{ RepoPath = $repo Mode = $Mode + Platform = $effectivePlatform SkipRepowise = $true } $smokeResult = Invoke-JsonScript (Join-Path $root "test-code-intel-pipeline.ps1") $smokeParams diff --git a/install-code-intel-pipeline.ps1 b/install-code-intel-pipeline.ps1 index 4567e29..fef654b 100644 --- a/install-code-intel-pipeline.ps1 +++ b/install-code-intel-pipeline.ps1 @@ -310,7 +310,7 @@ function Install-MissingTool { try { & $Installer - $after = Get-Command $CommandName -ErrorAction SilentlyContinue + $after = if ($CommandName -eq "python") { Get-CodeIntelPythonCommand } else { Get-Command $CommandName -ErrorAction SilentlyContinue } if ($after) { Add-InstallAction $Actions $CommandName "installed" $after.Source "" $metadata.packageManager ([bool]$metadata.requiresElevation) } diff --git a/invoke-code-intel.ps1 b/invoke-code-intel.ps1 index 78e7d43..61b7cc5 100644 --- a/invoke-code-intel.ps1 +++ b/invoke-code-intel.ps1 @@ -151,6 +151,7 @@ if (-not $NoIndexUpdate -and (Test-Path -LiteralPath $indexer -PathType Leaf)) { $indexParams.ArtifactRoot = [string]$configuredArtifactRoot } } + $indexParams.Platform = $Platform & $indexer @indexParams | Out-Host } diff --git a/run-code-intel.ps1 b/run-code-intel.ps1 index 1bbd638..978693c 100644 --- a/run-code-intel.ps1 +++ b/run-code-intel.ps1 @@ -3107,6 +3107,7 @@ if (-not $SkipRepowise) { $repowiseStep = Invoke-LoggedStep "repowise scoped docs" { & $scopedRepowiseScript ` -RepoPath $repoPath ` + -Platform $effectivePlatform ` -ShadowRoot $RepowiseShadowRoot ` -ScopePaths $RepowiseScopePaths ` -RootFiles $RepowiseRootFiles ` @@ -3119,6 +3120,7 @@ if (-not $SkipRepowise) { $repowiseStep = Invoke-LoggedStep "repowise scoped index" { & $scopedRepowiseScript ` -RepoPath $repoPath ` + -Platform $effectivePlatform ` -ShadowRoot $RepowiseShadowRoot ` -ScopePaths $RepowiseScopePaths ` -RootFiles $RepowiseRootFiles ` @@ -3144,7 +3146,7 @@ if ($Mode -ne "lite") { if ($hasRepowiseState -and $hasRepowiseWorkspace) { $steps.Add((Invoke-LoggedStep "repowise update" { - cmd /c "exit" | repowise update --workspace --index-only + repowise update --workspace --index-only })) } elseif ($hasRepowiseState) { @@ -3163,7 +3165,7 @@ elseif ($hasRepowiseState) { } else { $steps.Add((Invoke-LoggedStep "repowise init" { - cmd /c "(echo all& echo 1)" | repowise init . --index-only -y --no-claude-md --no-onboarding --embedder mock --provider mock + repowise init . --index-only -y --no-claude-md --no-onboarding --embedder mock --provider mock })) } diff --git a/tools/code-intel-platform.psm1 b/tools/code-intel-platform.psm1 index b6aa7ae..289ac17 100644 --- a/tools/code-intel-platform.psm1 +++ b/tools/code-intel-platform.psm1 @@ -161,7 +161,7 @@ function Set-CodeIntelUserEnv { New-Item -ItemType Directory -Force -Path $configDir | Out-Null $envFile = Join-Path $configDir "env.ps1" $escaped = $Value.Replace("'", "''") - "`$env:$Name = '$escaped'" | Set-Content -LiteralPath $envFile -Encoding UTF8 + "`$env:$Name = '$escaped'" | Add-Content -LiteralPath $envFile -Encoding UTF8 return [pscustomobject][ordered]@{ name = $Name persisted = $false @@ -221,7 +221,9 @@ function New-CodeIntelLink { } $parent = Split-Path -Parent $Path - New-Item -ItemType Directory -Force -Path $parent | Out-Null + if (-not [string]::IsNullOrWhiteSpace($parent)) { + New-Item -ItemType Directory -Force -Path $parent | Out-Null + } $os = Get-CodeIntelPlatform -Platform $Platform try { From 7667c553de9201d2e0c2db7cca6f07f24d754350 Mon Sep 17 00:00:00 2001 From: Curry Date: Thu, 2 Jul 2026 04:31:46 +0800 Subject: [PATCH 5/6] Fix cross-platform-smoke CI failures on rebased PR2 branch - test-skill-development-benchmark.ps1: match SKILL.md's forward-slash doc links - run-code-intel.ps1: derive file byte count from already-read content instead of a second Get-Item filesystem call, avoiding a TOCTOU failure on macOS/Ubuntu - skill/SKILL.md: replace hardcoded D:\\ absolute repo paths with portable \ references; fix stray backslash doc links - tools/check-hardcoded-paths.ps1: stop flagging legitimate \C:\Users\Administrator\AppData\Local/USERPROFILE/APPDATA references and doc example paths; add a precise self-referential absolute-path detector for the real bug class Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- run-code-intel.ps1 | 5 +++-- skill/SKILL.md | 10 +++++----- test-skill-development-benchmark.ps1 | 12 ++++++------ tools/check-hardcoded-paths.ps1 | 15 +++++++++------ 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/run-code-intel.ps1 b/run-code-intel.ps1 index 978693c..a8bb826 100644 --- a/run-code-intel.ps1 +++ b/run-code-intel.ps1 @@ -1001,13 +1001,14 @@ $content = Get-Content -LiteralPath $fullPath -Raw -ErrorAction SilentlyContinue if ($null -eq $content) { $content = "" } $lines = if ([string]::IsNullOrEmpty($content)) { @() } else { @($content -split "`r?`n") } $lines = @($lines) - $hashBytes = [System.Security.Cryptography.SHA256]::HashData([System.Text.Encoding]::UTF8.GetBytes($content)) + $contentBytes = [System.Text.Encoding]::UTF8.GetBytes($content) + $hashBytes = [System.Security.Cryptography.SHA256]::HashData($contentBytes) $hash = [System.BitConverter]::ToString($hashBytes).Replace("-", "").ToLowerInvariant() $fileRows.Add([ordered]@{ path = $relativePath language = $language -bytes = (Get-Item -LiteralPath $fullPath).Length +bytes = $contentBytes.Length lines = $lines.Count textHash = $hash source = "native-minimal" diff --git a/skill/SKILL.md b/skill/SKILL.md index 01888c1..114fd8d 100644 --- a/skill/SKILL.md +++ b/skill/SKILL.md @@ -68,9 +68,9 @@ Use `-Json` when another agent needs machine-readable output. Find candidate repositories before choosing a `RepoPath`: ```powershell -D:\projects\_tools\code-intel-pipeline\Find-CodeIntelProjects.ps1 -Root D:\projects -Json -D:\projects\_tools\code-intel-pipeline\Find-CodeIntelProjects.ps1 -Root D:\projects -WizTreeExe WizTree64.exe -Json -D:\projects\_tools\code-intel-pipeline\Find-CodeIntelProjects.ps1 -WizTreeCsv C:\tmp\wiztree.csv -Json +& "$env:CODE_INTEL_HOME/Find-CodeIntelProjects.ps1" -Root -Json +& "$env:CODE_INTEL_HOME/Find-CodeIntelProjects.ps1" -Root -WizTreeExe WizTree64.exe -Json +& "$env:CODE_INTEL_HOME/Find-CodeIntelProjects.ps1" -WizTreeCsv -Json ``` WizTree CLI/CSV is optional acceleration for project discovery only. It is not a scanner dependency. @@ -222,9 +222,9 @@ Use only these absorbed rules from the Karpathy skills repo: - Idea file first for nontrivial pipeline changes: use `templates/idea-file.md`. - Agentic loop: doctor -> lite -> normal -> read summary -> read understanding -> fix -> rerun -> commit. - Minimalism: keep this as an orchestration shell over `rg`, `repowise`, Understand Anything, and `sentrux`; do not copy tool internals into this repo. -- Implementation minimalism: before coding choose the first sufficient rung from `docs\implementation-minimalism-benchmark.md`: do nothing, reuse this repository, standard library, platform native capability, already-installed dependency, one-liner, then smallest local implementation. +- Implementation minimalism: before coding choose the first sufficient rung from `docs/implementation-minimalism-benchmark.md`: do nothing, reuse this repository, standard library, platform native capability, already-installed dependency, one-liner, then smallest local implementation. - Lazy about solution, never lazy about reading/evidence/safety: implementation minimalism cannot remove verification, error handling, security, accessibility, data-loss prevention, or artifact contract guarantees. -- Project management intake: before turning findings into tracked work, read `docs\project-management-support.md` and `docs\agents\*.md`. Linear and Obsidian/LLM wiki support is optional intake/output, not scanner runtime or credential storage. +- Project management intake: before turning findings into tracked work, read `docs/project-management-support.md` and `docs/agents/*.md`. Linear and Obsidian/LLM wiki support is optional intake/output, not scanner runtime or credential storage. - Supply-chain hygiene: inspect `installPlan` before approving new install surfaces. - Understanding-first: never report a run as handled without knowing the next action from `understanding.md`. diff --git a/test-skill-development-benchmark.ps1 b/test-skill-development-benchmark.ps1 index f02645e..5d7c1f4 100644 --- a/test-skill-development-benchmark.ps1 +++ b/test-skill-development-benchmark.ps1 @@ -58,12 +58,12 @@ Assert-FrontMatterField $skillText "name" Assert-FrontMatterField $skillText "description" $requiredSkillLinks = @( - "docs\artifact-data-contract.md", - "docs\agent-goal-intake.md", - "docs\harness-factory-reference.md", - "docs\skill-development-benchmark.md", - "docs\implementation-minimalism-benchmark.md", - "docs\ponytail-impact-scoreboard.md" + "docs/artifact-data-contract.md", + "docs/agent-goal-intake.md", + "docs/harness-factory-reference.md", + "docs/skill-development-benchmark.md", + "docs/implementation-minimalism-benchmark.md", + "docs/ponytail-impact-scoreboard.md" ) foreach ($link in $requiredSkillLinks) { diff --git a/tools/check-hardcoded-paths.ps1 b/tools/check-hardcoded-paths.ps1 index bca65d4..5ca3d0e 100644 --- a/tools/check-hardcoded-paths.ps1 +++ b/tools/check-hardcoded-paths.ps1 @@ -11,15 +11,17 @@ $PSNativeCommandUseErrorActionPreference = $false $root = Split-Path -Parent $PSScriptRoot $slash = [string][char]92 -$patterns = @( - ("D:" + $slash), +$literalPatterns = @( ("C:" + $slash + "Users" + $slash + "Administrator"), + ("power" + "shell" + ".exe"), ("LOCAL" + "APP" + "DATA"), ("USER" + "PRO" + "FILE"), - ("APP" + "DATA"), - ("power" + "shell" + ".exe") + ("APP" + "DATA") ) -$pattern = [regex](($patterns | ForEach-Object { [regex]::Escape($_) }) -join "|") +$patternParts = @($literalPatterns | ForEach-Object { [regex]::Escape($_) }) +$patternParts += "(? Date: Thu, 2 Jul 2026 04:34:57 +0800 Subject: [PATCH 6/6] Fix project management support test to match SKILL.md forward-slash doc link Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- test-project-management-support.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-project-management-support.ps1 b/test-project-management-support.ps1 index b8cd1c1..a53e866 100644 --- a/test-project-management-support.ps1 +++ b/test-project-management-support.ps1 @@ -136,7 +136,7 @@ $checks = @( }, @{ Path = $skillPath - Pattern = "docs\\project-management-support.md" + Pattern = "docs/project-management-support.md" Message = "skill/SKILL.md must link project management support doc." } )