@asterxsk/kiln 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +170 -0
- package/agent/AGENTS.md +67 -0
- package/agent/README.md +5 -0
- package/agent/extensions/AGENTS.md +68 -0
- package/agent/extensions/ask-user/index.ts +418 -0
- package/agent/extensions/ask-user/install.ps1 +23 -0
- package/agent/extensions/ask-user/install.sh +21 -0
- package/agent/extensions/ask-user/package-lock.json +769 -0
- package/agent/extensions/ask-user/package.json +19 -0
- package/agent/extensions/ask-user/prompt.ts +45 -0
- package/agent/extensions/ask-user/tsconfig.json +7 -0
- package/agent/extensions/background-terminals/docs/implementation-guide.md +942 -0
- package/agent/extensions/background-terminals/index.ts +627 -0
- package/agent/extensions/background-terminals/install.ps1 +23 -0
- package/agent/extensions/background-terminals/install.sh +21 -0
- package/agent/extensions/background-terminals/manager.test.ts +735 -0
- package/agent/extensions/background-terminals/output.test.ts +109 -0
- package/agent/extensions/background-terminals/package-lock.json +769 -0
- package/agent/extensions/background-terminals/package.json +17 -0
- package/agent/extensions/background-terminals/prompt.test.ts +125 -0
- package/agent/extensions/background-terminals/ps.test.ts +82 -0
- package/agent/extensions/background-terminals/result-delivery.test.ts +44 -0
- package/agent/extensions/background-terminals/src/domain.ts +87 -0
- package/agent/extensions/background-terminals/src/manager.ts +907 -0
- package/agent/extensions/background-terminals/src/output.ts +84 -0
- package/agent/extensions/background-terminals/src/prompt.ts +142 -0
- package/agent/extensions/background-terminals/src/result-delivery.ts +27 -0
- package/agent/extensions/background-terminals/src/runtime.ts +36 -0
- package/agent/extensions/background-terminals/src/ui/output-view.ts +79 -0
- package/agent/extensions/background-terminals/src/ui/ps.ts +621 -0
- package/agent/extensions/background-terminals/tsconfig.json +7 -0
- package/agent/extensions/destructive/README.md +31 -0
- package/agent/extensions/destructive/index.ts +88 -0
- package/agent/extensions/destructive/install.ps1 +23 -0
- package/agent/extensions/destructive/install.sh +21 -0
- package/agent/extensions/file-search/index.spec.ts +443 -0
- package/agent/extensions/file-search/index.ts +459 -0
- package/agent/extensions/file-search/install.ps1 +23 -0
- package/agent/extensions/file-search/install.sh +21 -0
- package/agent/extensions/file-search/package-lock.json +2253 -0
- package/agent/extensions/file-search/package.json +23 -0
- package/agent/extensions/file-search/src/args.ts +122 -0
- package/agent/extensions/file-search/src/binaries.ts +422 -0
- package/agent/extensions/file-search/src/output.ts +126 -0
- package/agent/extensions/file-search/src/process.ts +146 -0
- package/agent/extensions/file-search/src/prompt.ts +52 -0
- package/agent/extensions/file-search/tsconfig.json +7 -0
- package/agent/extensions/goal/README.md +50 -0
- package/agent/extensions/goal/index.ts +155 -0
- package/agent/extensions/goal/install.ps1 +23 -0
- package/agent/extensions/goal/install.sh +21 -0
- package/agent/extensions/modelconf/PLAN.md +915 -0
- package/agent/extensions/modelconf/README.md +66 -0
- package/agent/extensions/modelconf/index.ts +296 -0
- package/agent/extensions/modelconf/install.ps1 +23 -0
- package/agent/extensions/modelconf/install.sh +21 -0
- package/agent/extensions/modelconf/package-lock.json +1809 -0
- package/agent/extensions/modelconf/package.json +13 -0
- package/agent/extensions/modelconf/src/fuzzy.ts +26 -0
- package/agent/extensions/modelconf/src/glob.ts +13 -0
- package/agent/extensions/modelconf/src/persistence.test.ts +46 -0
- package/agent/extensions/modelconf/src/persistence.ts +164 -0
- package/agent/extensions/modelconf/src/ui/ModelConfView.test.ts +75 -0
- package/agent/extensions/modelconf/src/ui/ModelConfView.ts +1101 -0
- package/agent/extensions/modelconf/tsconfig.json +15 -0
- package/agent/extensions/pi-web-access/CHANGELOG.md +690 -0
- package/agent/extensions/pi-web-access/LICENSE +21 -0
- package/agent/extensions/pi-web-access/README.md +470 -0
- package/agent/extensions/pi-web-access/SECURITY.md +5 -0
- package/agent/extensions/pi-web-access/activity.ts +101 -0
- package/agent/extensions/pi-web-access/auth-fetch.ts +148 -0
- package/agent/extensions/pi-web-access/banner.png +0 -0
- package/agent/extensions/pi-web-access/brightdata-unlocker.ts +272 -0
- package/agent/extensions/pi-web-access/chrome-cookies.ts +669 -0
- package/agent/extensions/pi-web-access/content-find.ts +139 -0
- package/agent/extensions/pi-web-access/credential-source.ts +191 -0
- package/agent/extensions/pi-web-access/data-uri-sanitize.ts +406 -0
- package/agent/extensions/pi-web-access/datalab-pdf-extract.ts +568 -0
- package/agent/extensions/pi-web-access/declared-web-links.ts +173 -0
- package/agent/extensions/pi-web-access/evidence/CONTRACT-EVIDENCE.md +496 -0
- package/agent/extensions/pi-web-access/evidence/contract-probe.mjs +140 -0
- package/agent/extensions/pi-web-access/exa.ts +526 -0
- package/agent/extensions/pi-web-access/extract.ts +1196 -0
- package/agent/extensions/pi-web-access/feature-config.ts +29 -0
- package/agent/extensions/pi-web-access/fetch-params.ts +111 -0
- package/agent/extensions/pi-web-access/gemini-adc.ts +298 -0
- package/agent/extensions/pi-web-access/gemini-api.ts +353 -0
- package/agent/extensions/pi-web-access/gemini-pdf-extract.ts +108 -0
- package/agent/extensions/pi-web-access/gemini-search.ts +21 -0
- package/agent/extensions/pi-web-access/gemini-url-context.ts +128 -0
- package/agent/extensions/pi-web-access/gemini-web-config.ts +101 -0
- package/agent/extensions/pi-web-access/gemini-web.ts +487 -0
- package/agent/extensions/pi-web-access/github-api.ts +197 -0
- package/agent/extensions/pi-web-access/github-extract.ts +746 -0
- package/agent/extensions/pi-web-access/github-issue-pr.ts +700 -0
- package/agent/extensions/pi-web-access/index.ts +1737 -0
- package/agent/extensions/pi-web-access/package-lock.json +5808 -0
- package/agent/extensions/pi-web-access/package.json +64 -0
- package/agent/extensions/pi-web-access/page-query.ts +96 -0
- package/agent/extensions/pi-web-access/pdf-extract.ts +409 -0
- package/agent/extensions/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
- package/agent/extensions/pi-web-access/promise-try.d.ts +7 -0
- package/agent/extensions/pi-web-access/query-rewrite.ts +51 -0
- package/agent/extensions/pi-web-access/render-search-error.ts +170 -0
- package/agent/extensions/pi-web-access/rsc-extract.ts +338 -0
- package/agent/extensions/pi-web-access/search-types.ts +20 -0
- package/agent/extensions/pi-web-access/source-check.ts +282 -0
- package/agent/extensions/pi-web-access/ssrf-protection.ts +526 -0
- package/agent/extensions/pi-web-access/storage.ts +521 -0
- package/agent/extensions/pi-web-access/summary-model-scope.ts +125 -0
- package/agent/extensions/pi-web-access/test/auth-fetch.test.mjs +208 -0
- package/agent/extensions/pi-web-access/test/brightdata-unlocker.test.mjs +840 -0
- package/agent/extensions/pi-web-access/test/chrome-cookie-extraction.test.mjs +441 -0
- package/agent/extensions/pi-web-access/test/config-path.test.mjs +283 -0
- package/agent/extensions/pi-web-access/test/content-find.test.mjs +25 -0
- package/agent/extensions/pi-web-access/test/credential-source.test.mjs +118 -0
- package/agent/extensions/pi-web-access/test/data-uri-sanitize.test.mjs +210 -0
- package/agent/extensions/pi-web-access/test/datalab-pdf-extract.test.mjs +552 -0
- package/agent/extensions/pi-web-access/test/declared-web-links.test.mjs +212 -0
- package/agent/extensions/pi-web-access/test/fetch-answer-storage.test.mjs +40 -0
- package/agent/extensions/pi-web-access/test/fetch-cache-storage.test.mjs +334 -0
- package/agent/extensions/pi-web-access/test/fetch-content-domain-policy.test.mjs +95 -0
- package/agent/extensions/pi-web-access/test/fetch-modes.test.mjs +53 -0
- package/agent/extensions/pi-web-access/test/fetch-not-found-guidance.test.mjs +92 -0
- package/agent/extensions/pi-web-access/test/fetch-params.test.mjs +86 -0
- package/agent/extensions/pi-web-access/test/fetch-render-call.test.mjs +34 -0
- package/agent/extensions/pi-web-access/test/fetch-routing.test.mjs +173 -0
- package/agent/extensions/pi-web-access/test/gemini-adc-auth.test.mjs +257 -0
- package/agent/extensions/pi-web-access/test/gemini-api-transport.test.mjs +170 -0
- package/agent/extensions/pi-web-access/test/gemini-pdf-extract.test.mjs +133 -0
- package/agent/extensions/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +178 -0
- package/agent/extensions/pi-web-access/test/gemini-web-header-overflow.test.mjs +148 -0
- package/agent/extensions/pi-web-access/test/get-search-content.test.mjs +223 -0
- package/agent/extensions/pi-web-access/test/github-extract.test.mjs +378 -0
- package/agent/extensions/pi-web-access/test/github-issue-pr.test.mjs +565 -0
- package/agent/extensions/pi-web-access/test/inline-content-config.test.mjs +99 -0
- package/agent/extensions/pi-web-access/test/lazy-extract-load.test.mjs +118 -0
- package/agent/extensions/pi-web-access/test/local-video-oversize.test.mjs +52 -0
- package/agent/extensions/pi-web-access/test/package-typebox-dependency.test.mjs +50 -0
- package/agent/extensions/pi-web-access/test/page-query.test.mjs +51 -0
- package/agent/extensions/pi-web-access/test/pdf-config.test.mjs +140 -0
- package/agent/extensions/pi-web-access/test/pdf-extract.test.mjs +500 -0
- package/agent/extensions/pi-web-access/test/proxy-transport.test.mjs +286 -0
- package/agent/extensions/pi-web-access/test/query-rewrite.test.mjs +52 -0
- package/agent/extensions/pi-web-access/test/rsc-fallback.test.mjs +102 -0
- package/agent/extensions/pi-web-access/test/search-error-render.test.mjs +152 -0
- package/agent/extensions/pi-web-access/test/search-providers.test.mjs +274 -0
- package/agent/extensions/pi-web-access/test/source-check.test.mjs +179 -0
- package/agent/extensions/pi-web-access/test/ssrf-allow-ranges-config.test.mjs +205 -0
- package/agent/extensions/pi-web-access/test/ssrf-protection.test.mjs +456 -0
- package/agent/extensions/pi-web-access/test/summary-model-scope.test.mjs +106 -0
- package/agent/extensions/pi-web-access/test/tool-registration-config.test.mjs +182 -0
- package/agent/extensions/pi-web-access/test/web-search-answer-render.test.mjs +66 -0
- package/agent/extensions/pi-web-access/test/youtube-extract-errors.test.mjs +64 -0
- package/agent/extensions/pi-web-access/tsconfig.json +11 -0
- package/agent/extensions/pi-web-access/utils.ts +451 -0
- package/agent/extensions/pi-web-access/video-extract.ts +392 -0
- package/agent/extensions/pi-web-access/youtube-extract.ts +328 -0
- package/agent/extensions/shared/activity-status.ts +31 -0
- package/agent/extensions/shared/child-session.test.ts +270 -0
- package/agent/extensions/shared/child-session.ts +148 -0
- package/agent/extensions/shared/context-utilization.test.ts +48 -0
- package/agent/extensions/shared/context-utilization.ts +47 -0
- package/agent/extensions/shared/dashboard-state.ts +99 -0
- package/agent/extensions/shared/install.ps1 +23 -0
- package/agent/extensions/shared/install.sh +21 -0
- package/agent/extensions/shared/tool-call-timeout.test.ts +117 -0
- package/agent/extensions/shared/tool-call-timeout.ts +104 -0
- package/agent/extensions/skillsconf/README.md +74 -0
- package/agent/extensions/skillsconf/index.ts +92 -0
- package/agent/extensions/skillsconf/install.ps1 +23 -0
- package/agent/extensions/skillsconf/install.sh +21 -0
- package/agent/extensions/skillsconf/package-lock.json +1809 -0
- package/agent/extensions/skillsconf/package.json +18 -0
- package/agent/extensions/skillsconf/src/delete-skill.test.ts +64 -0
- package/agent/extensions/skillsconf/src/delete-skill.ts +45 -0
- package/agent/extensions/skillsconf/src/filter.test.ts +80 -0
- package/agent/extensions/skillsconf/src/filter.ts +74 -0
- package/agent/extensions/skillsconf/src/fuzzy.ts +26 -0
- package/agent/extensions/skillsconf/src/persistence.test.ts +72 -0
- package/agent/extensions/skillsconf/src/persistence.ts +169 -0
- package/agent/extensions/skillsconf/src/ui/SkillConfView.test.ts +337 -0
- package/agent/extensions/skillsconf/src/ui/SkillConfView.ts +758 -0
- package/agent/extensions/skillsconf/src/ui/text-input.ts +91 -0
- package/agent/extensions/skillsconf/src/ui/tui-helpers.ts +144 -0
- package/agent/extensions/skillsconf/tsconfig.json +15 -0
- package/agent/extensions/status line/index.ts +282 -0
- package/agent/extensions/status line/install.ps1 +23 -0
- package/agent/extensions/status line/install.sh +21 -0
- package/agent/extensions/subagents/by-the-way.test.ts +29 -0
- package/agent/extensions/subagents/claude.test.ts +119 -0
- package/agent/extensions/subagents/codex.test.ts +102 -0
- package/agent/extensions/subagents/context-usage.test.ts +107 -0
- package/agent/extensions/subagents/docs/design-plan.md +568 -0
- package/agent/extensions/subagents/docs/effect-v4-extension-guide.md +354 -0
- package/agent/extensions/subagents/docs/effect-v4-notes.md +571 -0
- package/agent/extensions/subagents/index.ts +779 -0
- package/agent/extensions/subagents/install.ps1 +23 -0
- package/agent/extensions/subagents/install.sh +21 -0
- package/agent/extensions/subagents/manager.test.ts +276 -0
- package/agent/extensions/subagents/package-lock.json +2244 -0
- package/agent/extensions/subagents/package.json +19 -0
- package/agent/extensions/subagents/result-delivery.test.ts +27 -0
- package/agent/extensions/subagents/src/backend.ts +73 -0
- package/agent/extensions/subagents/src/backends/claude.ts +701 -0
- package/agent/extensions/subagents/src/backends/codex.ts +1060 -0
- package/agent/extensions/subagents/src/backends/pi.ts +575 -0
- package/agent/extensions/subagents/src/backends/stub.ts +300 -0
- package/agent/extensions/subagents/src/by-the-way.ts +21 -0
- package/agent/extensions/subagents/src/domain.ts +253 -0
- package/agent/extensions/subagents/src/format.ts +74 -0
- package/agent/extensions/subagents/src/manager.ts +736 -0
- package/agent/extensions/subagents/src/prompt.ts +92 -0
- package/agent/extensions/subagents/src/result-delivery.ts +20 -0
- package/agent/extensions/subagents/src/runtime.ts +53 -0
- package/agent/extensions/subagents/src/ui/takeover.ts +583 -0
- package/agent/extensions/subagents/src/ui/transcript.ts +201 -0
- package/agent/extensions/subagents/takeover.test.ts +29 -0
- package/agent/extensions/subagents/tsconfig.json +7 -0
- package/agent/extensions/taste/index.ts +443 -0
- package/agent/extensions/taste/install.ps1 +23 -0
- package/agent/extensions/taste/install.sh +21 -0
- package/agent/extensions/todo/AGENTS.md +38 -0
- package/agent/extensions/todo/LICENSE +21 -0
- package/agent/extensions/todo/config.ts +55 -0
- package/agent/extensions/todo/index.ts +151 -0
- package/agent/extensions/todo/install.ps1 +23 -0
- package/agent/extensions/todo/install.sh +21 -0
- package/agent/extensions/todo/locales/de.json +17 -0
- package/agent/extensions/todo/locales/en.json +15 -0
- package/agent/extensions/todo/locales/es.json +17 -0
- package/agent/extensions/todo/locales/fr.json +17 -0
- package/agent/extensions/todo/locales/pt-BR.json +17 -0
- package/agent/extensions/todo/locales/pt.json +17 -0
- package/agent/extensions/todo/locales/ru.json +17 -0
- package/agent/extensions/todo/locales/uk.json +17 -0
- package/agent/extensions/todo/locales/zh.json +17 -0
- package/agent/extensions/todo/package-lock.json +3358 -0
- package/agent/extensions/todo/package.json +67 -0
- package/agent/extensions/todo/state/i18n-bridge.ts +64 -0
- package/agent/extensions/todo/state/invariants.ts +20 -0
- package/agent/extensions/todo/state/replay.ts +38 -0
- package/agent/extensions/todo/state/selectors.ts +107 -0
- package/agent/extensions/todo/state/state-reducer.ts +326 -0
- package/agent/extensions/todo/state/state.ts +18 -0
- package/agent/extensions/todo/state/store.ts +82 -0
- package/agent/extensions/todo/state/task-graph.ts +57 -0
- package/agent/extensions/todo/todo-overlay.ts +200 -0
- package/agent/extensions/todo/todo.ts +155 -0
- package/agent/extensions/todo/tool/response-envelope.ts +109 -0
- package/agent/extensions/todo/tool/types.ts +206 -0
- package/agent/extensions/todo/verify-ref-system.js +0 -0
- package/agent/extensions/todo/view/format.ts +177 -0
- package/agent/extensions/trim-context/README.md +54 -0
- package/agent/extensions/trim-context/index.ts +487 -0
- package/agent/extensions/trim-context/install.ps1 +23 -0
- package/agent/extensions/trim-context/install.sh +21 -0
- package/agent/install.ps1 +527 -0
- package/agent/install.sh +511 -0
- package/agent/keybindings.json +7 -0
- package/bin/kiln.js +359 -0
- package/package.json +21 -0
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
#!/usr/bin/env pwsh
|
|
2
|
+
<#
|
|
3
|
+
.SYNOPSIS
|
|
4
|
+
Pi custom-setup installer — Windows / PowerShell
|
|
5
|
+
|
|
6
|
+
1) Install/update pi (@earendil-works/pi-coding-agent)
|
|
7
|
+
2) Install pi packages (pi-context-usage, @baretread/pi-forge)
|
|
8
|
+
3) Install custom config (AGENTS.md, keybindings, extensions …)
|
|
9
|
+
4) Install per-extension npm deps via each extension's install.ps1
|
|
10
|
+
|
|
11
|
+
TUI inspired by https://pi.dev/install.ps1 — spinner when TTY, silent otherwise.
|
|
12
|
+
|
|
13
|
+
.EXAMPLE
|
|
14
|
+
powershell -NoProfile -ExecutionPolicy Bypass -c "irm https://raw.githubusercontent.com/asterxsk/kiln/main/agent/install.ps1 -UseBasicParsing | iex"
|
|
15
|
+
.\install.ps1 -RepoUrl https://github.com/asterxsk/kiln -Branch main
|
|
16
|
+
.\install.ps1 -Target "$env:USERPROFILE\.pi\agent" -Yes
|
|
17
|
+
|
|
18
|
+
.NOTES
|
|
19
|
+
Safe to re-run. Managed files are force-overwritten; per-user files (settings.json, taste.md, etc.) are preserved.
|
|
20
|
+
#>
|
|
21
|
+
|
|
22
|
+
param(
|
|
23
|
+
[string]$RepoUrl = $env:PI_CONFIG_REPO,
|
|
24
|
+
[string]$Branch = $(if ($env:PI_CONFIG_BRANCH) { $env:PI_CONFIG_BRANCH } else { "main" }),
|
|
25
|
+
[string]$Target = "",
|
|
26
|
+
[switch]$SkipPi,
|
|
27
|
+
[switch]$SkipPackages,
|
|
28
|
+
[switch]$Yes,
|
|
29
|
+
[switch]$Local
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
$ErrorActionPreference = "Stop"
|
|
33
|
+
Set-StrictMode -Version Latest
|
|
34
|
+
|
|
35
|
+
$script:ClonedTmp = $null
|
|
36
|
+
$script:StartTime = Get-Date
|
|
37
|
+
|
|
38
|
+
$PiPackage = "@earendil-works/pi-coding-agent"
|
|
39
|
+
$NodeMinimum = [version]"22.19.0"
|
|
40
|
+
$Esc = [char]27
|
|
41
|
+
$Cr = [char]13
|
|
42
|
+
$PiHome = if ($env:HOME) { $env:HOME } else { $env:USERPROFILE }
|
|
43
|
+
$PiTmp = Join-Path $PiHome ".pi/tmp"
|
|
44
|
+
try { New-Item -ItemType Directory -Path $PiTmp -Force | Out-Null } catch {}
|
|
45
|
+
$TmpLog = Join-Path $PiTmp ("pi-setup-" + [Guid]::NewGuid().ToString("N").Substring(0,8) + ".log")
|
|
46
|
+
$DefaultRepo = "https://github.com/asterxsk/kiln"
|
|
47
|
+
$env:GIT_TERMINAL_PROMPT = "0"
|
|
48
|
+
$env:GCM_INTERACTIVE = "never"
|
|
49
|
+
$env:GIT_ASKPASS = "echo"
|
|
50
|
+
|
|
51
|
+
# Ensure UTF-8 for glyphs (match pi.dev/install.ps1)
|
|
52
|
+
try { [Console]::OutputEncoding = [Text.Encoding]::UTF8; $OutputEncoding = [Text.Encoding]::UTF8 } catch {}
|
|
53
|
+
try { chcp 65001 2>$null | Out-Null } catch {}
|
|
54
|
+
|
|
55
|
+
# ── VT / ANSI ───────────────────────────────────────────────────────────────
|
|
56
|
+
function Test-IsInteractive {
|
|
57
|
+
return (-not [Console]::IsOutputRedirected) -and ($env:TERM -ne "dumb")
|
|
58
|
+
}
|
|
59
|
+
function Enable-VT {
|
|
60
|
+
if (-not (Test-IsInteractive)) { return $false }
|
|
61
|
+
# kernel32 only exists on Windows
|
|
62
|
+
if (-not $IsWindows -and $env:OS -notmatch "Windows") { return $false }
|
|
63
|
+
if ($null -ne $script:VTEnabled) { return $script:VTEnabled }
|
|
64
|
+
try {
|
|
65
|
+
if (-not ("VT.Native" -as [Type])) {
|
|
66
|
+
Add-Type -Namespace VT -Name Native -MemberDefinition @"
|
|
67
|
+
[DllImport("kernel32.dll", SetLastError=true)] public static extern System.IntPtr GetStdHandle(int n);
|
|
68
|
+
[DllImport("kernel32.dll", SetLastError=true)] public static extern bool GetConsoleMode(System.IntPtr h,out uint m);
|
|
69
|
+
[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetConsoleMode(System.IntPtr h,uint m);
|
|
70
|
+
"@
|
|
71
|
+
}
|
|
72
|
+
$h = [VT.Native]::GetStdHandle(-11)
|
|
73
|
+
[uint32]$m = 0
|
|
74
|
+
$ok = [VT.Native]::GetConsoleMode($h,[ref]$m)
|
|
75
|
+
if ($ok) { $ok = [VT.Native]::SetConsoleMode($h, ($m -bor 0x0004)) }
|
|
76
|
+
$script:VTEnabled = $ok
|
|
77
|
+
return $ok
|
|
78
|
+
} catch { $script:VTEnabled = $false; return $false }
|
|
79
|
+
}
|
|
80
|
+
function Test-Ansi { return Enable-VT }
|
|
81
|
+
$HasAnsi = Test-Ansi
|
|
82
|
+
|
|
83
|
+
# ── palette (scalar vars, dot-free names; braces delimit) ───────────
|
|
84
|
+
if ($HasAnsi) {
|
|
85
|
+
${creset}="$Esc[0m"; ${cbold}="$Esc[1m"; ${cdim}="$Esc[2m"; ${cgreen}="$Esc[32m"
|
|
86
|
+
${cyellow}="$Esc[33m"; ${cred}="$Esc[31m"; ${ccyan}="$Esc[36m"; ${cgray}="$Esc[90m"
|
|
87
|
+
} else {
|
|
88
|
+
${creset}=""; ${cbold}=""; ${cdim}=""; ${cgreen}=""; ${cyellow}=""; ${cred}=""; ${ccyan}=""; ${cgray}=""
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# ── title ───────────────────────────────────────────────────────────────────
|
|
92
|
+
function Write-Title {
|
|
93
|
+
if ($HasAnsi) {
|
|
94
|
+
[Console]::Write("${Esc}[1m \u25C6 Pi Setup${Esc}[0m`n${Esc}[2m custom agent config \u00B7 pi + forge + extensions${Esc}[0m`n`n")
|
|
95
|
+
} else {
|
|
96
|
+
Write-Host ""
|
|
97
|
+
Write-Host " Pi Setup"
|
|
98
|
+
Write-Host " custom agent config \xB7 pi + forge + extensions"
|
|
99
|
+
Write-Host ""
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# ── spinner ─────────────────────────────────────────────────────────────────
|
|
104
|
+
$SpinnerFrames = @('\u280B','\u2819','\u2839','\u2838','\u283C','\u2830','\u2820','\u2826','\u2827','\u2807','\u280F')
|
|
105
|
+
# Fallback to ASCII if font missing — pi.dev uses same
|
|
106
|
+
$SpinnerFrames = @('⠋','⠙','⠹','⠸','⠼','⠰','⠠','⠦','⠧','⠇','⠏')
|
|
107
|
+
$SpinnerIdx = 0
|
|
108
|
+
function Get-Spinner { $s = $SpinnerFrames[$script:SpinnerIdx % $SpinnerFrames.Count]; $script:SpinnerIdx++; return $s }
|
|
109
|
+
|
|
110
|
+
function Write-Line($text) {
|
|
111
|
+
if ($HasAnsi) { [Console]::Write("$text`n") } else { Write-Host $text }
|
|
112
|
+
}
|
|
113
|
+
function Write-StepLine($step, $label, $spin) {
|
|
114
|
+
$prefix = "${cdim}[$step/4]${creset}"
|
|
115
|
+
$frame = if ($spin) { "${cyellow}$spin${creset}" } else { " " }
|
|
116
|
+
$line = " $frame $prefix $label"
|
|
117
|
+
if ($HasAnsi -and (Test-IsInteractive)) {
|
|
118
|
+
# \r + EL (\033[K) so each frame overwrites cleanly regardless of length
|
|
119
|
+
[Console]::Write("$Cr${Esc}[K$line")
|
|
120
|
+
} else {
|
|
121
|
+
Write-Host $line
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function Clear-Line {
|
|
125
|
+
if ($HasAnsi -and (Test-IsInteractive)) { [Console]::Write("$Cr${Esc}[K") }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# ── run with spinner ────────────────────────────────────────────────────────
|
|
129
|
+
function Invoke-NativeWithSpinner {
|
|
130
|
+
# NOTE: the argument parameter must NOT be named $Args — $Args is an
|
|
131
|
+
# automatic variable, and splatting @Args silently binds the (empty)
|
|
132
|
+
# automatic instead of the parameter, dropping every argument.
|
|
133
|
+
param([string]$Step, [string]$Label, [string]$FilePath, [string[]]$ArgumentList)
|
|
134
|
+
# non-interactive: run synchronously, capture to log
|
|
135
|
+
if (-not (Test-IsInteractive) -or -not $HasAnsi) {
|
|
136
|
+
$out = & $FilePath @ArgumentList 2>&1
|
|
137
|
+
$code = $LASTEXITCODE
|
|
138
|
+
if ($out) { $out | ForEach-Object { Add-Content $TmpLog "$_" } }
|
|
139
|
+
return $code
|
|
140
|
+
}
|
|
141
|
+
$script:SpinnerIdx = 0
|
|
142
|
+
$psi = New-Object Diagnostics.ProcessStartInfo
|
|
143
|
+
$psi.FileName = $FilePath
|
|
144
|
+
# Quote args with spaces
|
|
145
|
+
$psi.Arguments = ($ArgumentList | ForEach-Object { if ($_ -match '\s') { '"' + $_ + '"' } else { $_ } }) -join " "
|
|
146
|
+
$psi.RedirectStandardOutput = $true
|
|
147
|
+
$psi.RedirectStandardError = $true
|
|
148
|
+
$psi.UseShellExecute = $false
|
|
149
|
+
$psi.CreateNoWindow = $true
|
|
150
|
+
$p = New-Object Diagnostics.Process
|
|
151
|
+
$p.StartInfo = $psi
|
|
152
|
+
[void]$p.Start()
|
|
153
|
+
while (-not $p.HasExited) {
|
|
154
|
+
Write-StepLine $Step $Label (Get-Spinner)
|
|
155
|
+
Start-Sleep -Milliseconds 80
|
|
156
|
+
}
|
|
157
|
+
$stdout = $p.StandardOutput.ReadToEnd()
|
|
158
|
+
$stderr = $p.StandardError.ReadToEnd()
|
|
159
|
+
if ($stdout) { Add-Content $TmpLog $stdout }
|
|
160
|
+
if ($stderr) { Add-Content $TmpLog $stderr }
|
|
161
|
+
Clear-Line
|
|
162
|
+
return $p.ExitCode
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# ── helpers ─────────────────────────────────────────────────────────────────
|
|
166
|
+
function Resolve-Target {
|
|
167
|
+
if ($Target) { return $Target }
|
|
168
|
+
if ($env:PI_AGENT_DIR) { return $env:PI_AGENT_DIR }
|
|
169
|
+
if ($IsWindows -or $env:OS -match "Windows") {
|
|
170
|
+
return (Join-Path $env:USERPROFILE ".pi\agent")
|
|
171
|
+
}
|
|
172
|
+
$homeDir = if ($env:HOME) { $env:HOME } else { $env:USERPROFILE }
|
|
173
|
+
return (Join-Path $homeDir ".pi/agent")
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function Resolve-RepoUrl {
|
|
177
|
+
if ($RepoUrl) { return $RepoUrl }
|
|
178
|
+
# When piped via irm|iex, $PSScriptRoot is empty — don't probe, use default
|
|
179
|
+
$isPiped = -not $PSScriptRoot
|
|
180
|
+
if (-not $isPiped) {
|
|
181
|
+
try {
|
|
182
|
+
$here = $PSScriptRoot
|
|
183
|
+
if (Test-Path (Join-Path $here ".git")) {
|
|
184
|
+
$u = (git -C $here remote get-url origin 2>$null)
|
|
185
|
+
if ($u) { return $u.Trim() }
|
|
186
|
+
}
|
|
187
|
+
$parent = Split-Path $here -Parent
|
|
188
|
+
if (Test-Path (Join-Path $parent ".git")) {
|
|
189
|
+
$u = (git -C $parent remote get-url origin 2>$null)
|
|
190
|
+
if ($u) { return $u.Trim() }
|
|
191
|
+
}
|
|
192
|
+
if (Test-Path (Join-Path (Get-Location).Path ".git")) {
|
|
193
|
+
$u = (git remote get-url origin 2>$null)
|
|
194
|
+
if ($u) { return $u.Trim() }
|
|
195
|
+
}
|
|
196
|
+
} catch {}
|
|
197
|
+
}
|
|
198
|
+
return $DefaultRepo
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function Resolve-SourceRoot {
|
|
202
|
+
param([string]$Repo)
|
|
203
|
+
# Local checkout detection (only when not piped or -Local forced)
|
|
204
|
+
$checkLocal = $Local -or $PSScriptRoot
|
|
205
|
+
if ($checkLocal) {
|
|
206
|
+
$roots = @()
|
|
207
|
+
if ($PSScriptRoot) { $roots += $PSScriptRoot; $roots += (Split-Path $PSScriptRoot -Parent); $roots += (Get-Location).Path }
|
|
208
|
+
else { $roots += (Get-Location).Path }
|
|
209
|
+
foreach ($r in $roots) {
|
|
210
|
+
if ((Test-Path (Join-Path $r "extensions")) -and (Test-Path (Join-Path $r "AGENTS.md"))) { return $r }
|
|
211
|
+
if (Test-Path (Join-Path $r "agent/extensions")) { return (Join-Path $r "agent") }
|
|
212
|
+
}
|
|
213
|
+
if ($Local) { throw "Local checkout not found (tried: $($roots -join ', '))" }
|
|
214
|
+
}
|
|
215
|
+
# Clone to ~/.pi/tmp — simple git clone, no account required, then copy agent folder.
|
|
216
|
+
# Use a unique subdir per run so concurrent installs never clobber each other.
|
|
217
|
+
if (-not (Test-Path $PiTmp)) { New-Item -ItemType Directory -Path $PiTmp -Force | Out-Null }
|
|
218
|
+
$tmp = Join-Path $PiTmp ("kiln-" + [Guid]::NewGuid().ToString("N").Substring(0,8))
|
|
219
|
+
New-Item -ItemType Directory -Path $tmp -Force | Out-Null
|
|
220
|
+
Write-Line "${cdim} → cloning $Repo (branch $Branch) → $tmp${creset}"
|
|
221
|
+
$env:GIT_TERMINAL_PROMPT = "0"; $env:GIT_ASKPASS = "echo"; $env:GCM_INTERACTIVE = "never"
|
|
222
|
+
$code = Invoke-NativeWithSpinner -Step "3" -Label "fetching config" -FilePath "git" -ArgumentList @("clone","--depth","1","--branch",$Branch,$Repo,$tmp)
|
|
223
|
+
if ($code -ne 0) { throw "git clone failed (exit $code). See $TmpLog" }
|
|
224
|
+
$script:ClonedTmp = $tmp
|
|
225
|
+
if (Test-Path (Join-Path $tmp "agent")) { return (Join-Path $tmp "agent") }
|
|
226
|
+
return $tmp
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function Resolve-Npm {
|
|
230
|
+
# "npm" resolves to npm.ps1 (a PowerShell shim) under Windows PowerShell.
|
|
231
|
+
# npm.ps1 re-parses its own invocation line, which mangles arguments
|
|
232
|
+
# passed via variables/splatting — always prefer the native npm.cmd.
|
|
233
|
+
$cmd = Get-Command "npm.cmd" -ErrorAction SilentlyContinue
|
|
234
|
+
if ($cmd) { return $cmd.Source }
|
|
235
|
+
return "npm"
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function Test-Node {
|
|
239
|
+
$node = Get-Command node -ErrorAction SilentlyContinue
|
|
240
|
+
if (-not $node) { return $null }
|
|
241
|
+
try { $v = (node --version 2>$null).Trim().TrimStart('v').Split('-')[0]; return [version]$v } catch { return $null }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
# ── MAIN ─────────────────────────────────────────────────────────────────────
|
|
245
|
+
"" | Set-Content $TmpLog
|
|
246
|
+
# Cleanup trap
|
|
247
|
+
$null = Register-EngineEvent PowerShell.Exiting -Action { if ($script:ClonedTmp -and (Test-Path $script:ClonedTmp)) { Remove-Item $script:ClonedTmp -Recurse -Force -ErrorAction SilentlyContinue } } -ErrorAction SilentlyContinue
|
|
248
|
+
|
|
249
|
+
Write-Title
|
|
250
|
+
$targetDir = Resolve-Target
|
|
251
|
+
$repoUrl = Resolve-RepoUrl
|
|
252
|
+
$ok = $true
|
|
253
|
+
|
|
254
|
+
Write-Line "${cdim} target ${creset}$targetDir"
|
|
255
|
+
Write-Line "${cdim} repo ${creset}$repoUrl ${cdim}($Branch)${creset}"
|
|
256
|
+
Write-Line "${cdim} log ${creset}$TmpLog`n"
|
|
257
|
+
|
|
258
|
+
# ── preflight ────────────────────────────────────────────────────────────────
|
|
259
|
+
$nodeVer = Test-Node
|
|
260
|
+
if (-not $nodeVer) {
|
|
261
|
+
Write-Line "${cred} ✖ Node.js not found. Install Node 22.19+ from https://nodejs.org${creset}"
|
|
262
|
+
Write-Line " then re-run this installer.`n"
|
|
263
|
+
exit 1
|
|
264
|
+
}
|
|
265
|
+
if ($nodeVer -lt $NodeMinimum) {
|
|
266
|
+
Write-Line "${cyellow} ⚠ Node $nodeVer < $NodeMinimum — please upgrade to Node 22.19+${creset}"
|
|
267
|
+
if (-not $Yes) { Write-Line " continue anyway in 3s…"; Start-Sleep 3 }
|
|
268
|
+
}
|
|
269
|
+
if (-not (Get-Command npm -ErrorAction SilentlyContinue)) {
|
|
270
|
+
Write-Line "${cred} ✖ npm not found (Node installed but npm missing)${creset}"; exit 1
|
|
271
|
+
}
|
|
272
|
+
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
|
273
|
+
Write-Line "${cred} ✖ git not found — required to fetch config${creset}"
|
|
274
|
+
Write-Line " install from https://git-scm.com/downloads`n"; exit 1
|
|
275
|
+
}
|
|
276
|
+
$npmVer = (npm --version 2>$null).Trim()
|
|
277
|
+
$gitVer = (git --version 2>$null).Trim()
|
|
278
|
+
Write-Line "${cdim} node $nodeVer · npm $npmVer · $gitVer${creset}`n"
|
|
279
|
+
$NpmBin = Resolve-Npm
|
|
280
|
+
|
|
281
|
+
# ── [1/4] pi ────────────────────────────────────────────────────────────────
|
|
282
|
+
if ($SkipPi) {
|
|
283
|
+
Write-Line " ${cgreen}✔${creset} ${cdim}[1/4]${creset} Install pi ${cdim}— skipped${creset}"
|
|
284
|
+
} else {
|
|
285
|
+
$hasPi = $null -ne (Get-Command pi -ErrorAction SilentlyContinue)
|
|
286
|
+
$label = if ($hasPi) { "updating pi ($PiPackage)" } else { "installing pi ($PiPackage)" }
|
|
287
|
+
$code = Invoke-NativeWithSpinner -Step "1" -Label $label -FilePath $NpmBin -ArgumentList @("install","-g","$PiPackage@latest","--no-audit","--no-fund","--min-release-age=0")
|
|
288
|
+
if ($code -eq 0) {
|
|
289
|
+
$piVer = try { (pi --version 2>$null).Trim() } catch { "?" }
|
|
290
|
+
Write-Line " ${cgreen}✔${creset} ${cdim}[1/4]${creset} $label ${cdim}— $piVer${creset}"
|
|
291
|
+
} else {
|
|
292
|
+
$isPerm = Select-String -Path $TmpLog -Pattern "EACCES|permission" -Quiet -ErrorAction SilentlyContinue
|
|
293
|
+
if ($isPerm) {
|
|
294
|
+
Write-Line " ${cred}✖${creset} ${cdim}[1/4]${creset} $label ${cred}permission denied${creset}"
|
|
295
|
+
Write-Line " ${cdim}try: npm config set prefix ~/.npm-global or run as admin${creset}"
|
|
296
|
+
} else {
|
|
297
|
+
Write-Line " ${cred}✖${creset} ${cdim}[1/4]${creset} $label ${cred}failed (exit $code)${creset} ${cdim}see $TmpLog${creset}"
|
|
298
|
+
}
|
|
299
|
+
$ok = $false
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
# ── [2/4] pi packages ─────────────────────────────────────────────────────
|
|
304
|
+
if ($SkipPackages) {
|
|
305
|
+
Write-Line " ${cgreen}✔${creset} ${cdim}[2/4]${creset} pi packages ${cdim}— skipped${creset}"
|
|
306
|
+
} else {
|
|
307
|
+
$label = "installing pi-context-usage + @baretread/pi-forge"
|
|
308
|
+
$code = Invoke-NativeWithSpinner -Step "2" -Label $label -FilePath $NpmBin -ArgumentList @("install","-g","pi-context-usage@latest","@baretread/pi-forge@latest","--no-audit","--no-fund")
|
|
309
|
+
if ($code -eq 0) {
|
|
310
|
+
# Verify global ls (handles mise/nvm prefix quirks)
|
|
311
|
+
$hasCtx = $null -ne (npm ls -g pi-context-usage 2>$null | Select-String "pi-context-usage")
|
|
312
|
+
$hasForge = $null -ne (npm ls -g "@baretread/pi-forge" 2>$null | Select-String "pi-forge")
|
|
313
|
+
if (-not $hasCtx -or -not $hasForge) {
|
|
314
|
+
Write-Line " ${cyellow}⚠${creset} ${cdim}[2/4]${creset} pi packages installed but not in global ls — continuing${creset}"
|
|
315
|
+
}
|
|
316
|
+
Write-Line " ${cgreen}✔${creset} ${cdim}[2/4]${creset} pi packages ${cdim}— done${creset}"
|
|
317
|
+
} else {
|
|
318
|
+
Write-Line " ${cyellow}⚠${creset} ${cdim}[2/4]${creset} $label ${cyellow}exit $code — continuing (pi will auto-install)${creset}"
|
|
319
|
+
Write-Line " ${cdim}see $TmpLog${creset}"
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
# ── [3/4] custom config ───────────────────────────────────────────────────
|
|
324
|
+
try {
|
|
325
|
+
$sourceRoot = Resolve-SourceRoot -Repo $repoUrl
|
|
326
|
+
Write-Line "${cdim} source $sourceRoot${creset}"
|
|
327
|
+
if (-not (Test-Path $sourceRoot)) { throw "source not found: $sourceRoot" }
|
|
328
|
+
|
|
329
|
+
if (-not (Test-Path $targetDir)) { New-Item -ItemType Directory -Path $targetDir -Force | Out-Null }
|
|
330
|
+
|
|
331
|
+
# backup (managed files only: extensions + top-level config files).
|
|
332
|
+
# Per-user data (settings.json, auth.json, sessions/, taste/, etc.) is never
|
|
333
|
+
# touched by the installer, so it is deliberately NOT backed up.
|
|
334
|
+
$hasExisting = (Test-Path (Join-Path $targetDir "AGENTS.md")) -or (Test-Path (Join-Path $targetDir "extensions"))
|
|
335
|
+
if ($hasExisting) {
|
|
336
|
+
$bak = "$targetDir.bak.$(Get-Date -Format yyyyMMdd-HHmmss)"
|
|
337
|
+
Write-Line "${cdim} backup $targetDir/extensions → $bak${creset}"
|
|
338
|
+
New-Item -ItemType Directory -Path $bak -Force | Out-Null
|
|
339
|
+
foreach ($f in @("AGENTS.md","keybindings.json","README.md")) {
|
|
340
|
+
$s = Join-Path $targetDir $f
|
|
341
|
+
if (Test-Path $s) { Copy-Item $s (Join-Path $bak $f) -Force -ErrorAction SilentlyContinue }
|
|
342
|
+
}
|
|
343
|
+
$srcBakExt = Join-Path $targetDir "extensions"
|
|
344
|
+
if (Test-Path $srcBakExt) {
|
|
345
|
+
# NOTE: Copy-Item -Exclude only filters top-level items, not recursive
|
|
346
|
+
# contents — copy per-extension while skipping node_modules/.git instead.
|
|
347
|
+
$dstBakExt = Join-Path $bak "extensions"
|
|
348
|
+
Get-ChildItem $srcBakExt -Directory | ForEach-Object {
|
|
349
|
+
$d = Join-Path $dstBakExt $_.Name
|
|
350
|
+
New-Item -ItemType Directory -Path $d -Force | Out-Null
|
|
351
|
+
Get-ChildItem $_.FullName -Exclude @("node_modules",".git","bin") | ForEach-Object {
|
|
352
|
+
Copy-Item $_.FullName $d -Recurse -Force -ErrorAction SilentlyContinue
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
$copied = 0
|
|
359
|
+
$isSelfInstall = ($sourceRoot -eq $targetDir)
|
|
360
|
+
if ($isSelfInstall) { Write-Line "${cdim} source == target — skipping file copy (self-install)${creset}" }
|
|
361
|
+
foreach ($f in @("AGENTS.md","keybindings.json","README.md")) {
|
|
362
|
+
$s = Join-Path $sourceRoot $f
|
|
363
|
+
if (-not (Test-Path $s)) { continue }
|
|
364
|
+
$d = Join-Path $targetDir $f
|
|
365
|
+
if ($isSelfInstall -or $s -eq $d) { $copied++; continue }
|
|
366
|
+
Copy-Item $s $d -Force; $copied++
|
|
367
|
+
}
|
|
368
|
+
# settings.json: seed from repo defaults if missing; an existing file is never modified.
|
|
369
|
+
$repoSettings = Join-Path $sourceRoot "settings.json"
|
|
370
|
+
$settings = Join-Path $targetDir "settings.json"
|
|
371
|
+
$settingsJustCreated = $false
|
|
372
|
+
if ((-not (Test-Path $settings)) -and (Test-Path $repoSettings)) {
|
|
373
|
+
Copy-Item $repoSettings $settings
|
|
374
|
+
$settingsJustCreated = $true
|
|
375
|
+
Write-Line "${cdim} created settings.json from repo defaults${creset}"
|
|
376
|
+
} elseif (Test-Path $settings) {
|
|
377
|
+
Write-Line "${cdim} kept existing settings.json${creset}"
|
|
378
|
+
}
|
|
379
|
+
# ensure Pi extensions (forge + context) are registered — only on a freshly
|
|
380
|
+
# seeded settings.json; an existing file is left completely untouched.
|
|
381
|
+
if ($settingsJustCreated -and (Test-Path $settings) -and (Get-Command node -ErrorAction SilentlyContinue)) {
|
|
382
|
+
try {
|
|
383
|
+
$patch = "const fs=require('fs');const p=process.argv[1];try{let j=JSON.parse(fs.readFileSync(p,'utf8'));let need=['npm:pi-context-usage','npm:@baretread/pi-forge'];j.packages=j.packages||[];let c=false;for(let pkg of need){if(!j.packages.includes(pkg)){j.packages.push(pkg);c=true}}if(c){fs.writeFileSync(p,JSON.stringify(j,null,2)+'\\n');console.log(' patched settings.json packages -> forge + context') } }catch(e){}"
|
|
384
|
+
node -e $patch $settings 2>$null | ForEach-Object { Write-Line "${cdim} $_${creset}" }
|
|
385
|
+
} catch {}
|
|
386
|
+
}
|
|
387
|
+
# taste.md / taste/ — user-specific, preserve if exists
|
|
388
|
+
foreach ($preserve in @("taste.md","taste","taste.json")) {
|
|
389
|
+
$p = Join-Path $targetDir $preserve
|
|
390
|
+
$src = Join-Path $sourceRoot $preserve
|
|
391
|
+
if (Test-Path $p) {
|
|
392
|
+
Write-Line "${cdim} kept existing $preserve${creset}"
|
|
393
|
+
} elseif (Test-Path $src) {
|
|
394
|
+
Copy-Item $src $p -Recurse -Force -Exclude @("node_modules",".git") -ErrorAction SilentlyContinue
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
# extensions — skip stray .md files, handle spaces
|
|
399
|
+
$srcExt = Join-Path $sourceRoot "extensions"
|
|
400
|
+
$dstExt = Join-Path $targetDir "extensions"
|
|
401
|
+
if (Test-Path $srcExt) {
|
|
402
|
+
if ($isSelfInstall -or $srcExt -eq $dstExt) {
|
|
403
|
+
# Self-install: extensions already in place — just count, don't delete/re-copy
|
|
404
|
+
Get-ChildItem $srcExt -Directory | Where-Object { $_.Name -notlike "*.md" } | ForEach-Object { $copied++ }
|
|
405
|
+
} else {
|
|
406
|
+
if (-not (Test-Path $dstExt)) { New-Item -ItemType Directory -Path $dstExt -Force | Out-Null }
|
|
407
|
+
Get-ChildItem $srcExt -Directory | ForEach-Object {
|
|
408
|
+
if ($_.Name -like "*.md") { return }
|
|
409
|
+
$dst = Join-Path $dstExt $_.Name
|
|
410
|
+
if (Test-Path $dst) { Remove-Item $dst -Recurse -Force -ErrorAction SilentlyContinue }
|
|
411
|
+
# NOTE: Copy-Item -Exclude only filters top-level items, not recursive
|
|
412
|
+
# contents — copy per-item while skipping node_modules/.git instead.
|
|
413
|
+
# (node_modules are rebuilt by each extension's installer in step 4;
|
|
414
|
+
# copying them follows junctions and stalls the install.)
|
|
415
|
+
New-Item -ItemType Directory -Path $dst -Force | Out-Null
|
|
416
|
+
Get-ChildItem $_.FullName -Exclude @("node_modules",".git") | ForEach-Object {
|
|
417
|
+
Copy-Item $_.FullName $dst -Recurse -Force -ErrorAction SilentlyContinue
|
|
418
|
+
}
|
|
419
|
+
$copied++
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
# themes — exclude recursive .pi and nul device file
|
|
425
|
+
$srcThemes = Join-Path $sourceRoot "themes"
|
|
426
|
+
if ((Test-Path $srcThemes) -and -not $isSelfInstall -and $srcThemes -ne (Join-Path $targetDir "themes")) {
|
|
427
|
+
$dstThemes = Join-Path $targetDir "themes"
|
|
428
|
+
if (Test-Path $dstThemes) { Remove-Item $dstThemes -Recurse -Force -ErrorAction SilentlyContinue }
|
|
429
|
+
New-Item -ItemType Directory -Path $dstThemes -Force | Out-Null
|
|
430
|
+
Get-ChildItem $srcThemes -Force | Where-Object { $_.Name -notin @(".pi","nul") } | ForEach-Object {
|
|
431
|
+
Copy-Item $_.FullName (Join-Path $dstThemes $_.Name) -Recurse -Force
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
Write-Line " ${cgreen}✔${creset} ${cdim}[3/4]${creset} custom config ${cdim}— $copied items → $targetDir${creset}"
|
|
436
|
+
# cleanup cloned temp — per spec: clone into ~/.pi/tmp, copy agent, delete temp
|
|
437
|
+
if ($script:ClonedTmp -and (Test-Path $script:ClonedTmp)) {
|
|
438
|
+
if ($script:ClonedTmp.StartsWith($PiTmp, [StringComparison]::OrdinalIgnoreCase)) {
|
|
439
|
+
Remove-Item $script:ClonedTmp -Recurse -Force -ErrorAction SilentlyContinue
|
|
440
|
+
$script:ClonedTmp = $null
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
# repo README → alongside the agent dir (e.g. ~/.pi/README.md)
|
|
444
|
+
$repoRoot = Split-Path $sourceRoot -Parent
|
|
445
|
+
$targetParent = Split-Path $targetDir -Parent
|
|
446
|
+
$repoReadme = Join-Path $repoRoot "README.md"
|
|
447
|
+
if ((Test-Path $repoReadme) -and ($repoRoot -ne $targetParent)) {
|
|
448
|
+
Copy-Item $repoReadme (Join-Path $targetParent "README.md") -Force -ErrorAction SilentlyContinue
|
|
449
|
+
}
|
|
450
|
+
} catch {
|
|
451
|
+
Write-Line " ${cred}✖${creset} ${cdim}[3/4]${creset} custom config ${cred}failed:${creset} $($_.Exception.Message)"
|
|
452
|
+
Write-Line " ${cdim}$($_.ScriptStackTrace)${creset}"
|
|
453
|
+
Write-Line " ${cdim}see $TmpLog${creset}"
|
|
454
|
+
$ok = $false
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
# ── [4/4] per-extension deps ──────────────────────────────────────────────
|
|
458
|
+
try {
|
|
459
|
+
$extRoot = Join-Path $targetDir "extensions"
|
|
460
|
+
$exts = @()
|
|
461
|
+
if (Test-Path $extRoot) { $exts = Get-ChildItem $extRoot -Directory | Where-Object { $_.Name -notlike "*.md" } | Sort-Object Name }
|
|
462
|
+
|
|
463
|
+
if ($exts.Count -eq 0) {
|
|
464
|
+
Write-Line " ${cyellow}⚠${creset} ${cdim}[4/4]${creset} no extensions found at $extRoot"
|
|
465
|
+
} else {
|
|
466
|
+
$failed = @()
|
|
467
|
+
$installed = 0
|
|
468
|
+
foreach ($ext in $exts) {
|
|
469
|
+
$label = "extensions/$($ext.Name)"
|
|
470
|
+
$ps1 = Join-Path $ext.FullName "install.ps1"
|
|
471
|
+
$sh = Join-Path $ext.FullName "install.sh"
|
|
472
|
+
$hasPkg = Test-Path (Join-Path $ext.FullName "package.json")
|
|
473
|
+
|
|
474
|
+
if (-not $hasPkg -and -not (Test-Path $ps1)) {
|
|
475
|
+
Write-Line " ${cdim} · $label — no package.json, skipping${creset}"
|
|
476
|
+
continue
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (Test-Path $ps1) {
|
|
480
|
+
# prefer pwsh, fallback to powershell
|
|
481
|
+
$pwsh = Get-Command pwsh -ErrorAction SilentlyContinue
|
|
482
|
+
if ($pwsh) { $code = Invoke-NativeWithSpinner -Step "4" -Label $label -FilePath $pwsh.Source -ArgumentList @("-NoProfile","-File",$ps1) }
|
|
483
|
+
else { $code = Invoke-NativeWithSpinner -Step "4" -Label $label -FilePath "powershell" -ArgumentList @("-NoProfile","-ExecutionPolicy","Bypass","-File",$ps1) }
|
|
484
|
+
} elseif (Test-Path $sh) {
|
|
485
|
+
$code = Invoke-NativeWithSpinner -Step "4" -Label $label -FilePath "sh" -ArgumentList @($sh)
|
|
486
|
+
} else {
|
|
487
|
+
$npmArgs = if (Test-Path (Join-Path $ext.FullName "package-lock.json")) { @("--prefix",$ext.FullName,"ci","--no-audit","--no-fund") } else { @("--prefix",$ext.FullName,"install","--no-audit","--no-fund") }
|
|
488
|
+
$code = Invoke-NativeWithSpinner -Step "4" -Label $label -FilePath $NpmBin -ArgumentList $npmArgs
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if ($code -eq 0) {
|
|
492
|
+
Write-Line " ${cgreen}✔${creset} ${cdim}[4/4]${creset} $label"
|
|
493
|
+
$installed++
|
|
494
|
+
} else {
|
|
495
|
+
Write-Line " ${cred}✖${creset} ${cdim}[4/4]${creset} $label ${cred}failed (exit $code)${creset}"
|
|
496
|
+
$failed += $ext.Name
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
if ($failed.Count -eq 0) {
|
|
500
|
+
Write-Line " ${cgreen}✔${creset} ${cdim}[4/4]${creset} extension deps ${cdim}— $installed installed${creset}"
|
|
501
|
+
} else {
|
|
502
|
+
Write-Line " ${cyellow}⚠${creset} ${cdim}[4/4]${creset} extension deps ${cyellow}$($failed.Count) failed:${creset} $($failed -join ', ') ${cdim}see $TmpLog${creset}"
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
} catch {
|
|
506
|
+
Write-Line " ${cred}✖${creset} ${cdim}[4/4]${creset} extension deps ${cred}failed:${creset} $($_.Exception.Message)"
|
|
507
|
+
$ok = $false
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
# ── cleanup + summary ─────────────────────────────────────────────────────
|
|
511
|
+
if ($script:ClonedTmp -and (Test-Path $script:ClonedTmp)) {
|
|
512
|
+
Remove-Item $script:ClonedTmp -Recurse -Force -ErrorAction SilentlyContinue
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
$elapsed = (Get-Date) - $script:StartTime
|
|
516
|
+
if ($elapsed.TotalSeconds -ge 60) { $elapsedFmt = "{0}m {1}s" -f [int]$elapsed.TotalMinutes, $elapsed.Seconds }
|
|
517
|
+
else { $elapsedFmt = "{0}s" -f [int]$elapsed.TotalSeconds }
|
|
518
|
+
|
|
519
|
+
Write-Line ""
|
|
520
|
+
if ($ok) {
|
|
521
|
+
Write-Line "${cgreen}${cbold} ✓ Setup complete${creset} ${cdim}→ $targetDir · Kiln installed in $elapsedFmt${creset}"
|
|
522
|
+
$piVer2 = try { (pi --version 2>$null).Trim() } catch { $null }
|
|
523
|
+
if ($piVer2) { Write-Line "${cdim} pi $piVer2 · run: pi${creset}" }
|
|
524
|
+
} else {
|
|
525
|
+
Write-Line "${cyellow}${cbold} ⚠ Setup finished with warnings${creset} ${cdim}in $elapsedFmt · see $TmpLog${creset}"
|
|
526
|
+
}
|
|
527
|
+
Write-Line "${cdim} log: $TmpLog${creset}`n"
|