@darkrei08/setup-ai 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -0
- package/bin/setup-ai.mjs +191 -0
- package/package.json +45 -0
- package/setup-ai.ps1 +418 -0
- package/setup-ai.sh +1091 -0
package/setup-ai.ps1
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
#Requires -Version 7.0
|
|
2
|
+
<#
|
|
3
|
+
==============================================================================
|
|
4
|
+
AI Dev Suite — Engineering Excellence Edition (Windows)
|
|
5
|
+
Version: 3.0.0
|
|
6
|
+
|
|
7
|
+
Windows-native installer, sibling of setup-ai.sh. Uses each tool's official
|
|
8
|
+
Windows method: winget for language runtimes, the vendor install.ps1 scripts
|
|
9
|
+
for the AI CLIs, `go install` for gentle-ai, npm for opencode, and
|
|
10
|
+
`npx skills` / `pi install` for skills and pi packages.
|
|
11
|
+
|
|
12
|
+
The Node launcher bin/setup-ai.mjs dispatches here on win32 and can pass a
|
|
13
|
+
module selection via -Only (from its interactive menu).
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
pwsh -File setup-ai.ps1 # core module set
|
|
17
|
+
pwsh -File setup-ai.ps1 -All # every module (incl. GUI apps)
|
|
18
|
+
pwsh -File setup-ai.ps1 -Only pi,codex,opencode
|
|
19
|
+
pwsh -File setup-ai.ps1 -List
|
|
20
|
+
pwsh -File setup-ai.ps1 -Help
|
|
21
|
+
==============================================================================
|
|
22
|
+
#>
|
|
23
|
+
|
|
24
|
+
[CmdletBinding()]
|
|
25
|
+
param(
|
|
26
|
+
[string]$Only = "",
|
|
27
|
+
[switch]$All,
|
|
28
|
+
[switch]$List,
|
|
29
|
+
[switch]$Yes,
|
|
30
|
+
[switch]$Help
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
$ErrorActionPreference = 'Stop'
|
|
34
|
+
Set-StrictMode -Version Latest
|
|
35
|
+
|
|
36
|
+
$ScriptVersion = "3.0.0"
|
|
37
|
+
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
38
|
+
$LogDir = Join-Path $ScriptDir "logs"
|
|
39
|
+
New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
|
|
40
|
+
|
|
41
|
+
$RunId = (Get-Date -AsUTC -Format "yyyyMMddTHHmmssZ")
|
|
42
|
+
$HumanLog = Join-Path $LogDir "setup_$RunId.log"
|
|
43
|
+
$JsonlLog = Join-Path $LogDir "setup_$RunId.jsonl"
|
|
44
|
+
$ReportFile = Join-Path $LogDir "engineering-report_$RunId.md"
|
|
45
|
+
|
|
46
|
+
$EE_Slug = "micio86dev/Engineering-Excellence"
|
|
47
|
+
$EE_Skill = "engineering-excellence"
|
|
48
|
+
$PiSkillDir = Join-Path $HOME ".pi\agent\skills\$EE_Skill"
|
|
49
|
+
$PiExtDir = Join-Path $HOME ".pi\agent\extensions"
|
|
50
|
+
|
|
51
|
+
# ------------------------------------------------------------------------------
|
|
52
|
+
# Logging (human + JSONL)
|
|
53
|
+
# ------------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
function Write-Log {
|
|
56
|
+
param(
|
|
57
|
+
[ValidateSet('INFO','WARN','ERROR','DEBUG')] [string]$Level,
|
|
58
|
+
[string]$Phase, [string]$Event, [string]$Message, [int]$ReturnCode = 0, [string]$Meta = ""
|
|
59
|
+
)
|
|
60
|
+
$ts = (Get-Date -AsUTC -Format "yyyy-MM-ddTHH:mm:ssZ")
|
|
61
|
+
$obj = [ordered]@{
|
|
62
|
+
timestamp = $ts; level = $Level; phase = $Phase; event = $Event
|
|
63
|
+
message = $Message; return_code = $ReturnCode; run_id = $RunId; pid = $PID
|
|
64
|
+
}
|
|
65
|
+
if ($Meta) { $obj.meta = $Meta }
|
|
66
|
+
($obj | ConvertTo-Json -Compress) | Add-Content -Path $JsonlLog
|
|
67
|
+
|
|
68
|
+
$line = "$ts [$Level] $Phase $Event`: $Message"
|
|
69
|
+
$line | Add-Content -Path $HumanLog
|
|
70
|
+
switch ($Level) {
|
|
71
|
+
'INFO' { Write-Host $line -ForegroundColor Blue }
|
|
72
|
+
'WARN' { Write-Host $line -ForegroundColor Yellow }
|
|
73
|
+
'ERROR' { Write-Host $line -ForegroundColor Red }
|
|
74
|
+
'DEBUG' { if ($env:DEBUG -eq '1') { Write-Host $line -ForegroundColor DarkGray } }
|
|
75
|
+
default { Write-Host $line }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function Test-Cmd { param([string]$Name) [bool](Get-Command $Name -ErrorAction SilentlyContinue) }
|
|
80
|
+
|
|
81
|
+
# Run a step; $Optional means failures are logged as WARN and swallowed.
|
|
82
|
+
function Invoke-Step {
|
|
83
|
+
param([string]$Phase, [scriptblock]$Action, [switch]$Optional)
|
|
84
|
+
Write-Log INFO $Phase "step_start" "Running step"
|
|
85
|
+
try {
|
|
86
|
+
& $Action 2>&1 | Tee-Object -FilePath $HumanLog -Append | Out-Host
|
|
87
|
+
Write-Log INFO $Phase "step_ok" "Step completed"
|
|
88
|
+
return $true
|
|
89
|
+
} catch {
|
|
90
|
+
if ($Optional) {
|
|
91
|
+
Write-Log WARN $Phase "step_failed_optional" "$($_.Exception.Message); continuing" 1
|
|
92
|
+
return $false
|
|
93
|
+
}
|
|
94
|
+
Write-Log ERROR $Phase "step_failed" "$($_.Exception.Message)" 1
|
|
95
|
+
throw
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function Install-Winget {
|
|
100
|
+
param([string]$Id, [string]$Phase)
|
|
101
|
+
if (-not (Test-Cmd winget)) {
|
|
102
|
+
Write-Log WARN $Phase "winget_missing" "winget not available; install '$Id' manually"
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
# Skip if already installed.
|
|
106
|
+
$installed = winget list --id $Id -e 2>$null | Select-String -SimpleMatch $Id
|
|
107
|
+
if ($installed) {
|
|
108
|
+
Write-Log INFO $Phase "already_present" "$Id already installed"
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
Invoke-Step -Phase $Phase -Optional -Action {
|
|
112
|
+
winget install -e --id $Id --accept-package-agreements --accept-source-agreements --silent
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function Invoke-RemoteScript {
|
|
117
|
+
param([string]$Url, [string]$Phase)
|
|
118
|
+
# Mirrors the vendor's documented `irm <url> | iex`, but logged.
|
|
119
|
+
Invoke-Step -Phase $Phase -Optional -Action {
|
|
120
|
+
$script = Invoke-RestMethod -Uri $Url -UseBasicParsing
|
|
121
|
+
Invoke-Expression $script
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# ==============================================================================
|
|
126
|
+
# Module registry
|
|
127
|
+
# ==============================================================================
|
|
128
|
+
|
|
129
|
+
$ModuleOrder = @('base','node','bun','pi','go','ee','pi-workflows','herdr','gentle-ai','engram','codex','antigravity','opencode','cockpit')
|
|
130
|
+
|
|
131
|
+
$ModuleDesc = [ordered]@{
|
|
132
|
+
'base' = 'Core dev tools via winget (git, gh, python, neovim)'
|
|
133
|
+
'node' = 'Node.js LTS (winget OpenJS.NodeJS.LTS) + npm@latest'
|
|
134
|
+
'bun' = 'Bun runtime (bun.sh install.ps1)'
|
|
135
|
+
'pi' = 'pi.dev coding agent CLI (pi.dev install.ps1)'
|
|
136
|
+
'go' = 'Go toolchain (winget GoLang.Go)'
|
|
137
|
+
'ee' = 'Engineering Excellence skill (npx skills add, detected agents)'
|
|
138
|
+
'pi-workflows' = 'pi-extensible-workflows (module resolution fix for pi extensions)'
|
|
139
|
+
'herdr' = 'herdr terminal multiplexer (herdr.dev install.ps1)'
|
|
140
|
+
'gentle-ai' = 'gentle-ai (go install) + gentle-pi package'
|
|
141
|
+
'engram' = 'Engram persistent memory for pi (gentle-engram: /remember /recall)'
|
|
142
|
+
'codex' = 'OpenAI Codex CLI (chatgpt.com install.ps1)'
|
|
143
|
+
'antigravity' = 'Google Antigravity CLI (antigravity.google install.ps1)'
|
|
144
|
+
'opencode' = 'opencode agent CLI (npm opencode-ai)'
|
|
145
|
+
'cockpit' = 'cockpit-tools desktop GUI (optional, .msi, CC BY-NC-SA)'
|
|
146
|
+
}
|
|
147
|
+
$ModuleOptional = @{ 'cockpit' = $true }
|
|
148
|
+
|
|
149
|
+
# ==============================================================================
|
|
150
|
+
# Modules
|
|
151
|
+
# ==============================================================================
|
|
152
|
+
|
|
153
|
+
function Mod-Base {
|
|
154
|
+
Write-Log INFO "base" "start" "Core dev tools (winget)"
|
|
155
|
+
Install-Winget -Id "Git.Git" -Phase "base"
|
|
156
|
+
Install-Winget -Id "GitHub.cli" -Phase "base"
|
|
157
|
+
Install-Winget -Id "Python.Python.3.12" -Phase "base"
|
|
158
|
+
Install-Winget -Id "Neovim.Neovim" -Phase "base"
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function Mod-Node {
|
|
162
|
+
Write-Log INFO "node" "start" "Node.js"
|
|
163
|
+
if (-not (Test-Cmd node)) { Install-Winget -Id "OpenJS.NodeJS.LTS" -Phase "node" }
|
|
164
|
+
if (Test-Cmd npm) { Invoke-Step -Phase "node" -Optional -Action { npm install -g npm@latest } }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function Mod-Bun {
|
|
168
|
+
Write-Log INFO "bun" "start" "Bun"
|
|
169
|
+
if (Test-Cmd bun) { Write-Log INFO "bun" "already_present" "bun already installed"; return }
|
|
170
|
+
Invoke-RemoteScript -Url "https://bun.sh/install.ps1" -Phase "bun"
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function Mod-Pi {
|
|
174
|
+
Write-Log INFO "pi" "start" "pi.dev CLI"
|
|
175
|
+
if (Test-Cmd pi) { Write-Log INFO "pi" "already_present" "pi already installed"; return }
|
|
176
|
+
Invoke-RemoteScript -Url "https://pi.dev/install.ps1" -Phase "pi"
|
|
177
|
+
New-Item -ItemType Directory -Force -Path $PiExtDir | Out-Null
|
|
178
|
+
New-Item -ItemType Directory -Force -Path (Join-Path $HOME ".pi\agent\skills") | Out-Null
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function Mod-Go {
|
|
182
|
+
Write-Log INFO "go" "start" "Go toolchain"
|
|
183
|
+
if (Test-Cmd go) { Write-Log INFO "go" "already_present" "Go already installed"; return }
|
|
184
|
+
Install-Winget -Id "GoLang.Go" -Phase "go"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function Mod-Ee {
|
|
188
|
+
Write-Log INFO "ee" "start" "Engineering Excellence"
|
|
189
|
+
if (-not (Test-Cmd npx)) { Write-Log WARN "ee" "npx_missing" "npx not found; install node first"; return }
|
|
190
|
+
$agents = @{ pi = ".pi"; claude = ".claude"; gemini = ".gemini"; cursor = ".cursor"; antigravity = ".antigravity" }
|
|
191
|
+
$any = $false
|
|
192
|
+
foreach ($a in @('pi','claude','gemini','cursor','antigravity')) {
|
|
193
|
+
if (Test-Path (Join-Path $HOME $agents[$a])) {
|
|
194
|
+
Invoke-Step -Phase "ee" -Optional -Action {
|
|
195
|
+
npx --yes skills@latest add $EE_Slug --skill $EE_Skill --global --agent $a --copy --yes
|
|
196
|
+
}
|
|
197
|
+
$any = $true
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (-not $any) {
|
|
201
|
+
Invoke-Step -Phase "ee" -Optional -Action {
|
|
202
|
+
npx --yes skills@latest add $EE_Slug --skill $EE_Skill --global --agent pi --copy --yes
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (Test-Path (Join-Path $PiSkillDir "SKILL.md")) {
|
|
206
|
+
Write-Log INFO "ee" "skill_installed" "EE skill present for pi"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function Mod-PiWorkflows {
|
|
211
|
+
Write-Log INFO "pi-workflows" "start" "pi-extensible-workflows"
|
|
212
|
+
if (-not (Test-Cmd pi)) { Write-Log WARN "pi-workflows" "pi_missing" "pi not found; skipped"; return }
|
|
213
|
+
$ver = ""
|
|
214
|
+
try { $ver = (npm view pi-extensible-workflows version).Trim() } catch {}
|
|
215
|
+
if (-not $ver) { Write-Log WARN "pi-workflows" "version_unresolved" "Could not resolve version; skipped"; return }
|
|
216
|
+
Write-Log INFO "pi-workflows" "version" "Version $ver"
|
|
217
|
+
Invoke-Step -Phase "pi-workflows" -Optional -Action { pi install "npm:pi-extensible-workflows@$ver" }
|
|
218
|
+
New-Item -ItemType Directory -Force -Path $PiExtDir | Out-Null
|
|
219
|
+
Set-Content -Path (Join-Path $PiExtDir ".npmrc") -Value "ignore-scripts=false"
|
|
220
|
+
Push-Location $PiExtDir
|
|
221
|
+
try {
|
|
222
|
+
Invoke-Step -Phase "pi-workflows" -Optional -Action {
|
|
223
|
+
npm install --save-exact --no-audit --no-fund "pi-extensible-workflows@$ver"
|
|
224
|
+
}
|
|
225
|
+
Invoke-Step -Phase "pi-workflows" -Optional -Action {
|
|
226
|
+
node -e "console.log(require.resolve('pi-extensible-workflows',{paths:[process.cwd()]}))"
|
|
227
|
+
}
|
|
228
|
+
} finally { Pop-Location }
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function Mod-Herdr {
|
|
232
|
+
Write-Log INFO "herdr" "start" "herdr"
|
|
233
|
+
if (Test-Cmd herdr) { Write-Log INFO "herdr" "already_present" "herdr already installed"; return }
|
|
234
|
+
Invoke-RemoteScript -Url "https://herdr.dev/install.ps1" -Phase "herdr"
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function Mod-GentleAi {
|
|
238
|
+
Write-Log INFO "gentle-ai" "start" "gentle-ai"
|
|
239
|
+
if (-not (Test-Cmd gentle-ai) -and -not (Test-Cmd gga)) {
|
|
240
|
+
if (Test-Cmd go) {
|
|
241
|
+
Invoke-Step -Phase "gentle-ai" -Optional -Action {
|
|
242
|
+
go install github.com/gentleman-programming/gentle-ai/v2/cmd/gentle-ai@latest
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
Write-Log WARN "gentle-ai" "go_missing" "Go not found; cannot 'go install' gentle-ai on Windows"
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (Test-Cmd pi) {
|
|
249
|
+
Invoke-Step -Phase "gentle-ai" -Optional -Action { pi install npm:gentle-pi }
|
|
250
|
+
Invoke-Step -Phase "gentle-ai" -Optional -Action { pi install npm:pi-mcp-adapter }
|
|
251
|
+
Write-Log INFO "gentle-ai" "pi_enabled" "gentle-pi registered in pi (verify: /gentle-ai:status)"
|
|
252
|
+
}
|
|
253
|
+
@"
|
|
254
|
+
gentle-ai next steps (run yourself, per project):
|
|
255
|
+
1) Set your API keys
|
|
256
|
+
2) Run your selected agent
|
|
257
|
+
3) Try: /sdd-new my-feature (in pi: /gentle-ai:status, /gentleman:models)
|
|
258
|
+
GGA (per project): gga init then gga install
|
|
259
|
+
"@ | Tee-Object -FilePath $HumanLog -Append | Out-Host
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function Mod-Engram {
|
|
263
|
+
Write-Log INFO "engram" "start" "Engram memory (pi)"
|
|
264
|
+
# The gentle-engram pi extension auto-starts `engram serve`; the Engram Go
|
|
265
|
+
# binary must be on PATH first, or the extension loads but silently fails.
|
|
266
|
+
if (-not (Test-Cmd engram)) {
|
|
267
|
+
if (Test-Cmd go) {
|
|
268
|
+
Invoke-Step -Phase "engram" -Optional -Action {
|
|
269
|
+
go install github.com/Gentleman-Programming/engram/cmd/engram@latest
|
|
270
|
+
}
|
|
271
|
+
$goBin = Join-Path $HOME "go\bin"
|
|
272
|
+
if ((Test-Path (Join-Path $goBin "engram.exe")) -and ($env:Path -notlike "*$goBin*")) {
|
|
273
|
+
$env:Path = "$goBin;$env:Path"
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
Write-Log WARN "engram" "go_missing" "Go not found; cannot install engram binary (needed on PATH)"
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (-not (Test-Cmd pi)) { Write-Log WARN "engram" "pi_missing" "pi not found; skipped"; return }
|
|
280
|
+
Invoke-Step -Phase "engram" -Optional -Action { pi install npm:gentle-engram }
|
|
281
|
+
Invoke-Step -Phase "engram" -Optional -Action { pi install npm:pi-mcp-adapter }
|
|
282
|
+
Invoke-Step -Phase "engram" -Optional -Action { npm exec --yes --package gentle-engram@latest -- pi-engram init }
|
|
283
|
+
Write-Log INFO "engram" "enabled" "Engram enabled — RESTART pi, verify: mem_current_project / mem_doctor / 'engram tui'"
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function Mod-Codex {
|
|
287
|
+
Write-Log INFO "codex" "start" "Codex CLI"
|
|
288
|
+
if (Test-Cmd codex) { Write-Log INFO "codex" "already_present" "codex already installed"; return }
|
|
289
|
+
Invoke-RemoteScript -Url "https://chatgpt.com/codex/install.ps1" -Phase "codex"
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function Mod-Antigravity {
|
|
293
|
+
Write-Log INFO "antigravity" "start" "Antigravity CLI"
|
|
294
|
+
if (Test-Cmd agy) { Write-Log INFO "antigravity" "already_present" "agy already installed"; return }
|
|
295
|
+
Invoke-RemoteScript -Url "https://antigravity.google/cli/install.ps1" -Phase "antigravity"
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function Mod-Opencode {
|
|
299
|
+
Write-Log INFO "opencode" "start" "opencode"
|
|
300
|
+
if (Test-Cmd opencode) { Write-Log INFO "opencode" "already_present" "opencode already installed"; return }
|
|
301
|
+
if (Test-Cmd npm) {
|
|
302
|
+
Invoke-Step -Phase "opencode" -Optional -Action { npm install -g opencode-ai }
|
|
303
|
+
} else {
|
|
304
|
+
Write-Log WARN "opencode" "npm_missing" "npm not found; install node first"
|
|
305
|
+
}
|
|
306
|
+
@"
|
|
307
|
+
OpenCode Go (paid) is hosted-model access; after install run: opencode auth login
|
|
308
|
+
The SAME key works in pi.dev — add a custom provider (baseUrl https://opencode.ai/zen/v1,
|
|
309
|
+
api openai-completions) or run /provider add. Docs: https://pi.dev/docs/latest/custom-provider
|
|
310
|
+
"@ | Tee-Object -FilePath $HumanLog -Append | Out-Host
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function Mod-Cockpit {
|
|
314
|
+
Write-Log INFO "cockpit" "start" "cockpit-tools (GUI)"
|
|
315
|
+
Write-Log INFO "cockpit" "license_notice" "cockpit-tools is a desktop GUI app under CC BY-NC-SA 4.0"
|
|
316
|
+
try {
|
|
317
|
+
$rel = Invoke-RestMethod -Uri "https://api.github.com/repos/jlcodes99/cockpit-tools/releases/latest" -Headers @{ 'User-Agent' = 'setup-ai' }
|
|
318
|
+
$asset = $rel.assets | Where-Object { $_.name -match '\.msi$' } | Select-Object -First 1
|
|
319
|
+
if (-not $asset) { Write-Log WARN "cockpit" "no_msi" "No .msi asset in latest release; download manually"; return }
|
|
320
|
+
$msi = Join-Path $env:TEMP $asset.name
|
|
321
|
+
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $msi
|
|
322
|
+
Invoke-Step -Phase "cockpit" -Optional -Action { Start-Process msiexec.exe -ArgumentList "/i `"$msi`" /qb" -Wait }
|
|
323
|
+
} catch {
|
|
324
|
+
Write-Log WARN "cockpit" "failed" "$($_.Exception.Message)"
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
$ModuleFn = @{
|
|
329
|
+
'base' = ${function:Mod-Base}; 'node' = ${function:Mod-Node}; 'bun' = ${function:Mod-Bun}
|
|
330
|
+
'pi' = ${function:Mod-Pi}; 'go' = ${function:Mod-Go}; 'ee' = ${function:Mod-Ee}
|
|
331
|
+
'pi-workflows' = ${function:Mod-PiWorkflows}; 'herdr' = ${function:Mod-Herdr}
|
|
332
|
+
'gentle-ai' = ${function:Mod-GentleAi}; 'engram' = ${function:Mod-Engram}
|
|
333
|
+
'codex' = ${function:Mod-Codex}; 'antigravity' = ${function:Mod-Antigravity}
|
|
334
|
+
'opencode' = ${function:Mod-Opencode}; 'cockpit' = ${function:Mod-Cockpit}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
# ==============================================================================
|
|
338
|
+
# CLI / selection
|
|
339
|
+
# ==============================================================================
|
|
340
|
+
|
|
341
|
+
function Show-List {
|
|
342
|
+
Write-Host "AI Dev Suite $ScriptVersion — modules (core = installed by default):`n"
|
|
343
|
+
foreach ($m in $ModuleOrder) {
|
|
344
|
+
$tag = if ($ModuleOptional.ContainsKey($m)) { "optional" } else { "core " }
|
|
345
|
+
"{0,-10} {1,-14} {2}" -f "[$tag]", $m, $ModuleDesc[$m] | Write-Host
|
|
346
|
+
}
|
|
347
|
+
Write-Host "`nUse: -Only <csv> | -All | (default = core)"
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function Show-Help {
|
|
351
|
+
Get-Content $MyInvocation.MyCommand.Path | Select-Object -First 30 | ForEach-Object { $_ }
|
|
352
|
+
Show-List
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function Resolve-Selection {
|
|
356
|
+
$requested = @()
|
|
357
|
+
if ($All) {
|
|
358
|
+
$requested = $ModuleOrder
|
|
359
|
+
} elseif ($Only) {
|
|
360
|
+
foreach ($r in ($Only -split ',')) {
|
|
361
|
+
$r = $r.Trim()
|
|
362
|
+
if (-not $r) { continue }
|
|
363
|
+
if (-not $ModuleDesc.Contains($r)) { Write-Error "Unknown module: $r"; exit 2 }
|
|
364
|
+
$requested += $r
|
|
365
|
+
}
|
|
366
|
+
} else {
|
|
367
|
+
$requested = $ModuleOrder | Where-Object { -not $ModuleOptional.ContainsKey($_) }
|
|
368
|
+
}
|
|
369
|
+
# Order by ModuleOrder so dependencies run first.
|
|
370
|
+
return $ModuleOrder | Where-Object { $requested -contains $_ }
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
# ==============================================================================
|
|
374
|
+
# Main
|
|
375
|
+
# ==============================================================================
|
|
376
|
+
|
|
377
|
+
New-Item -ItemType File -Force -Path $HumanLog | Out-Null
|
|
378
|
+
New-Item -ItemType File -Force -Path $JsonlLog | Out-Null
|
|
379
|
+
|
|
380
|
+
if ($Help) { Show-Help; exit 0 }
|
|
381
|
+
if ($List) { Show-List; exit 0 }
|
|
382
|
+
|
|
383
|
+
Write-Log INFO "bootstrap" "start" "AI Dev Suite (Windows) started" 0 "script_version=$ScriptVersion"
|
|
384
|
+
|
|
385
|
+
$selected = Resolve-Selection
|
|
386
|
+
Write-Log INFO "bootstrap" "modules_selected" "Modules queued" 0 ("modules=" + ($selected -join ' '))
|
|
387
|
+
|
|
388
|
+
$failed = @()
|
|
389
|
+
foreach ($m in $selected) {
|
|
390
|
+
try { & $ModuleFn[$m] }
|
|
391
|
+
catch { $failed += $m; Write-Log ERROR "modules" "module_failed" "Module $m failed: $($_.Exception.Message)" 1 }
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
# Report
|
|
395
|
+
@"
|
|
396
|
+
# AI Dev Suite — Engineering Report (Windows)
|
|
397
|
+
|
|
398
|
+
**Script version:** $ScriptVersion
|
|
399
|
+
**Run ID:** $RunId
|
|
400
|
+
**Selected modules:** $($selected -join ' ')
|
|
401
|
+
**Failed modules:** $(if ($failed) { $failed -join ' ' } else { 'none' })
|
|
402
|
+
|
|
403
|
+
## Versions
|
|
404
|
+
- Node: $(if (Test-Cmd node) { node --version } else { 'n/a' })
|
|
405
|
+
- npm: $(if (Test-Cmd npm) { npm --version } else { 'n/a' })
|
|
406
|
+
- Go: $(if (Test-Cmd go) { (go version) } else { 'n/a' })
|
|
407
|
+
- pi: $(if (Test-Cmd pi) { 'installed' } else { 'n/a' })
|
|
408
|
+
|
|
409
|
+
Logs: $HumanLog ; $JsonlLog
|
|
410
|
+
"@ | Set-Content -Path $ReportFile
|
|
411
|
+
|
|
412
|
+
Write-Host "`n============================================================" -ForegroundColor Green
|
|
413
|
+
Write-Host " AI Dev Suite (Windows) setup finished" -ForegroundColor Green
|
|
414
|
+
Write-Host "============================================================" -ForegroundColor Green
|
|
415
|
+
Write-Host "Modules : $($selected -join ' ')"
|
|
416
|
+
if ($failed) { Write-Host "Failed : $($failed -join ' ')" -ForegroundColor Yellow }
|
|
417
|
+
Write-Host "Report : $ReportFile"
|
|
418
|
+
Write-Host "`nNext: open a new terminal so PATH updates apply."
|