@ojokesusu/lintasai 1.1.2 → 1.2.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.
@@ -0,0 +1,280 @@
1
+ #Requires -Module Pester
2
+
3
+ <#
4
+ .SYNOPSIS
5
+ Pester 5+ tests untuk setup-pola-b.ps1 (ProjectRoot resolution + npx mode copy).
6
+
7
+ .DESCRIPTION
8
+ Empat invocation branches yang di-test:
9
+ 1. No -ProjectRoot, kit at <tmp>/.claude-kit/setup-pola-b.ps1
10
+ -> project = parent of kit (traditional Pola B mode).
11
+ 2. -ProjectRoot supplied (valid path)
12
+ -> project = that explicit path (no derivation).
13
+ 3. -ProjectRoot non-existent path
14
+ -> script errors out clearly (not silent fail). Resolve-Path -ErrorAction Stop
15
+ melempar terminating error; test assert exit code non-zero.
16
+ 4. -ProjectRoot supplied + kit at npm-cache-like path (TEMP\nodemod-test\@ojokesusu\lintasai)
17
+ -> npx mode auto-detect, kit di-COPY ke $ProjectRoot/.claude-kit/.
18
+
19
+ Plus:
20
+ 5. Manifest sanity: setelah setup sukses, .install-manifest.json terbuat.
21
+
22
+ Strategi:
23
+ - Real kit (parent dari folder tests/) jadi source-of-truth file.
24
+ - Per-test fake project root di $env:TEMP dengan layout sesuai branch.
25
+ - Branch 1, 2, 4, 5: pakai -Force -SkipTeamFiles untuk skip prompt + faster run.
26
+ - Branch 3: pakai path yang sengaja tidak ada, assert exit != 0 + pesan error.
27
+ - Test memanggil script di child PowerShell -NoProfile -NonInteractive supaya
28
+ env caller tidak ke-pollute (Set-StrictMode di lib/manifest.ps1 dll).
29
+ #>
30
+
31
+ BeforeAll {
32
+ # ---- Resolve repo root (kit folder asli, parent dari tests/) ----
33
+ $script:KitRepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..') | Select-Object -ExpandProperty Path
34
+ $script:SetupScript = Join-Path $script:KitRepoRoot 'setup-pola-b.ps1'
35
+
36
+ if (-not (Test-Path $script:SetupScript)) {
37
+ throw "setup-pola-b.ps1 not found at $script:SetupScript - tests assume layout tests/ sibling to setup-pola-b.ps1"
38
+ }
39
+
40
+ # ---- Helper: copy kit asli ke target folder (jadi .claude-kit di fake project) ----
41
+ function script:Copy-RealKit {
42
+ param(
43
+ [Parameter(Mandatory)][string]$Destination
44
+ )
45
+ if (-not (Test-Path -LiteralPath $Destination)) {
46
+ $null = New-Item -ItemType Directory -Path $Destination -Force
47
+ }
48
+ # Copy semua isi kit kecuali subfolder yang bisa bikin loop/lambat (kalau ada)
49
+ Get-ChildItem -Path $script:KitRepoRoot -Force | Where-Object {
50
+ $_.Name -notin @('.git', 'node_modules')
51
+ } | ForEach-Object {
52
+ $dest = Join-Path $Destination $_.Name
53
+ if ($_.PSIsContainer) {
54
+ Copy-Item -LiteralPath $_.FullName -Destination $dest -Recurse -Force
55
+ } else {
56
+ Copy-Item -LiteralPath $_.FullName -Destination $dest -Force
57
+ }
58
+ }
59
+ }
60
+
61
+ # ---- Helper: bikin file dummy supaya proyek "tidak hampir kosong" ----
62
+ # setup-pola-b.ps1 skip docs/ skeleton kalau project root hampir kosong (heuristic
63
+ # menghitung non-hidden file/dir). Test branch 1/2/4/5 mau real flow, jadi taruh
64
+ # 2 file dummy supaya nonHiddenFiles.Count > 1.
65
+ function script:Add-ProjectContent {
66
+ param([Parameter(Mandatory)][string]$Root)
67
+ [System.IO.File]::WriteAllText(
68
+ (Join-Path $Root 'README.md'),
69
+ "# Test Project`nDummy content untuk lewatin proyek-hampir-kosong heuristic.`n",
70
+ (New-Object System.Text.UTF8Encoding $false)
71
+ )
72
+ [System.IO.File]::WriteAllText(
73
+ (Join-Path $Root 'package.json'),
74
+ '{"name":"test","version":"0.0.1"}',
75
+ (New-Object System.Text.UTF8Encoding $false)
76
+ )
77
+ }
78
+
79
+ # ---- Helper: run setup-pola-b.ps1 di child PowerShell, capture stdout + exit ----
80
+ function script:Invoke-Setup {
81
+ param(
82
+ [Parameter(Mandatory)][string]$ScriptPath,
83
+ [string[]]$Args = @()
84
+ )
85
+ $pwshExe = (Get-Process -Id $PID).Path
86
+ if (-not $pwshExe) { $pwshExe = 'powershell.exe' }
87
+ $allArgs = @('-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', $ScriptPath) + $Args
88
+ $output = & $pwshExe @allArgs 2>&1 | Out-String
89
+ return [pscustomobject]@{
90
+ Output = $output
91
+ ExitCode = $LASTEXITCODE
92
+ }
93
+ }
94
+
95
+ # ---- Helper: cleanup folder kalau ada ----
96
+ function script:Remove-TestRoot {
97
+ param([Parameter(Mandatory)][string]$Path)
98
+ if (Test-Path -LiteralPath $Path) {
99
+ Remove-Item -Recurse -Force -LiteralPath $Path -ErrorAction SilentlyContinue
100
+ }
101
+ }
102
+ }
103
+
104
+ Describe "setup-pola-b.ps1 Branch 1: traditional invocation (no -ProjectRoot)" {
105
+ BeforeAll {
106
+ # Layout: <tmp>/<project>/.claude-kit/setup-pola-b.ps1
107
+ $script:Root1 = Join-Path $env:TEMP ("lintasAI-setup-test-traditional-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
108
+ $null = New-Item -ItemType Directory -Path $script:Root1 -Force
109
+ script:Add-ProjectContent -Root $script:Root1
110
+ $script:Kit1 = Join-Path $script:Root1 '.claude-kit'
111
+ script:Copy-RealKit -Destination $script:Kit1
112
+ $script:SetupInKit1 = Join-Path $script:Kit1 'setup-pola-b.ps1'
113
+ }
114
+
115
+ AfterAll {
116
+ script:Remove-TestRoot -Path $script:Root1
117
+ }
118
+
119
+ It "Resolves project root = parent of kit folder" {
120
+ $result = script:Invoke-Setup -ScriptPath $script:SetupInKit1 -Args @('-Force', '-SkipTeamFiles')
121
+ $result.ExitCode | Should -Be 0
122
+ # Output harus mention root proyek sesuai $Root1.
123
+ $result.Output | Should -Match ([regex]::Escape($script:Root1))
124
+ # AGENTS.md harus dideploy di project root.
125
+ (Test-Path -LiteralPath (Join-Path $script:Root1 'AGENTS.md')) | Should -BeTrue
126
+ }
127
+
128
+ It "Does NOT print npx mode banner" {
129
+ # Traditional mode: tidak boleh ada string "[npx] Mode" karena ProjectRoot tidak di-pass.
130
+ # Re-run dengan -DryRun supaya tidak duplicate setup di same root.
131
+ $result = script:Invoke-Setup -ScriptPath $script:SetupInKit1 -Args @('-Force', '-SkipTeamFiles', '-DryRun')
132
+ $result.Output | Should -Not -Match '\[npx\] Mode'
133
+ }
134
+ }
135
+
136
+ Describe "setup-pola-b.ps1 Branch 2: -ProjectRoot supplied (valid path)" {
137
+ BeforeAll {
138
+ # Layout: <tmp>/<project>/.claude-kit/setup-pola-b.ps1 (kit lives standard place)
139
+ # but we ALSO pass -ProjectRoot explicitly to exercise the npx-mode code path
140
+ # bahkan kalau path-nya same. Script harus treat sebagai npx mode.
141
+ $script:Root2 = Join-Path $env:TEMP ("lintasAI-setup-test-explicitroot-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
142
+ $null = New-Item -ItemType Directory -Path $script:Root2 -Force
143
+ script:Add-ProjectContent -Root $script:Root2
144
+ $script:Kit2 = Join-Path $script:Root2 '.claude-kit'
145
+ script:Copy-RealKit -Destination $script:Kit2
146
+ $script:SetupInKit2 = Join-Path $script:Kit2 'setup-pola-b.ps1'
147
+ }
148
+
149
+ AfterAll {
150
+ script:Remove-TestRoot -Path $script:Root2
151
+ }
152
+
153
+ It "Uses the explicit ProjectRoot path (no derivation)" {
154
+ $result = script:Invoke-Setup -ScriptPath $script:SetupInKit2 -Args @(
155
+ '-Force', '-SkipTeamFiles', '-ProjectRoot', $script:Root2
156
+ )
157
+ $result.ExitCode | Should -Be 0
158
+ # Banner npx mode harus muncul (proves the branch executed).
159
+ $result.Output | Should -Match '\[npx\] Mode: explicit ProjectRoot'
160
+ # AGENTS.md harus ada di explicit root.
161
+ (Test-Path -LiteralPath (Join-Path $script:Root2 'AGENTS.md')) | Should -BeTrue
162
+ }
163
+ }
164
+
165
+ Describe "setup-pola-b.ps1 Branch 3: -ProjectRoot non-existent path" {
166
+ BeforeAll {
167
+ # Kit lives di TEMP/<kit-folder>/.claude-kit dengan parent project legit, tapi
168
+ # we pass -ProjectRoot ke path yang tidak ada -> Resolve-Path -ErrorAction Stop
169
+ # harus throw -> exit code non-zero, with clear error message (not silent).
170
+ $script:Root3 = Join-Path $env:TEMP ("lintasAI-setup-test-badroot-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
171
+ $null = New-Item -ItemType Directory -Path $script:Root3 -Force
172
+ script:Add-ProjectContent -Root $script:Root3
173
+ $script:Kit3 = Join-Path $script:Root3 '.claude-kit'
174
+ script:Copy-RealKit -Destination $script:Kit3
175
+ $script:SetupInKit3 = Join-Path $script:Kit3 'setup-pola-b.ps1'
176
+ # Path yang DIJAMIN tidak ada
177
+ $script:BadRoot = Join-Path $env:TEMP ("lintasAI-DOES-NOT-EXIST-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
178
+ }
179
+
180
+ AfterAll {
181
+ script:Remove-TestRoot -Path $script:Root3
182
+ script:Remove-TestRoot -Path $script:BadRoot # defensive (script harusnya tidak create)
183
+ }
184
+
185
+ It "Errors out (non-zero exit) with clear message" {
186
+ $result = script:Invoke-Setup -ScriptPath $script:SetupInKit3 -Args @(
187
+ '-Force', '-SkipTeamFiles', '-ProjectRoot', $script:BadRoot
188
+ )
189
+ $result.ExitCode | Should -Not -Be 0
190
+ # Pesan harus include "Resolve-Path" / "not exist" / nama path yang tidak valid -
191
+ # apa pun yang membuktikan ini BUKAN silent fail.
192
+ # Resolve-Path throws "Cannot find path '...' because it does not exist."
193
+ $result.Output | Should -Match '(?i)(cannot find path|does not exist|not exist|tidak ditemukan|tidak ada)'
194
+ }
195
+
196
+ It "Does NOT create .claude-kit at the bogus path" {
197
+ # Script tidak boleh men-create folder di path yang tidak ada (defensive guard).
198
+ (Test-Path -LiteralPath $script:BadRoot) | Should -BeFalse
199
+ }
200
+ }
201
+
202
+ Describe "setup-pola-b.ps1 Branch 4: -ProjectRoot + kit at npm-cache-like path" {
203
+ BeforeAll {
204
+ # Layout simulasi npx:
205
+ # <tmp>\nodemod-test\@ojokesusu\lintasai\setup-pola-b.ps1 (kit di npm cache)
206
+ # <tmp>\<project-root>\ (target project, beda parent)
207
+ # Script harus detect mismatch ($KitDir tidak dalam $ProjectRoot/.claude-kit/)
208
+ # dan COPY isi kit ke $ProjectRoot/.claude-kit/.
209
+ $script:Root4 = Join-Path $env:TEMP ("lintasAI-setup-test-npx-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
210
+ $null = New-Item -ItemType Directory -Path $script:Root4 -Force
211
+ script:Add-ProjectContent -Root $script:Root4
212
+
213
+ # Kit DIPASANG di npm-cache-like location (BUKAN di $script:Root4/.claude-kit)
214
+ $script:NpmCacheRoot = Join-Path $env:TEMP ("nodemod-test-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
215
+ $script:KitInNpmCache = Join-Path $script:NpmCacheRoot '@ojokesusu\lintasai'
216
+ $null = New-Item -ItemType Directory -Path $script:KitInNpmCache -Force
217
+ script:Copy-RealKit -Destination $script:KitInNpmCache
218
+ $script:SetupInNpmCache = Join-Path $script:KitInNpmCache 'setup-pola-b.ps1'
219
+ }
220
+
221
+ AfterAll {
222
+ script:Remove-TestRoot -Path $script:Root4
223
+ script:Remove-TestRoot -Path $script:NpmCacheRoot
224
+ }
225
+
226
+ It "Detects npx mode and copies kit to <ProjectRoot>/.claude-kit/" {
227
+ # Pre-assert: target .claude-kit tidak ada sebelum jalan.
228
+ $targetKit = Join-Path $script:Root4 '.claude-kit'
229
+ (Test-Path -LiteralPath $targetKit) | Should -BeFalse
230
+
231
+ $result = script:Invoke-Setup -ScriptPath $script:SetupInNpmCache -Args @(
232
+ '-Force', '-SkipTeamFiles', '-ProjectRoot', $script:Root4
233
+ )
234
+ $result.ExitCode | Should -Be 0
235
+ # Verifikasi log npx mode + copy
236
+ $result.Output | Should -Match '\[npx\] Mode: explicit ProjectRoot'
237
+ $result.Output | Should -Match '(?i)\[npx\] Copy kit'
238
+
239
+ # Hasil: .claude-kit di project root harus ada, berisi setup-pola-b.ps1 yang ke-copy
240
+ (Test-Path -LiteralPath $targetKit) | Should -BeTrue
241
+ (Test-Path -LiteralPath (Join-Path $targetKit 'setup-pola-b.ps1')) | Should -BeTrue
242
+ (Test-Path -LiteralPath (Join-Path $targetKit 'CHANGELOG.md')) | Should -BeTrue
243
+ (Test-Path -LiteralPath (Join-Path $targetKit 'lib\manifest.ps1')) | Should -BeTrue
244
+ }
245
+ }
246
+
247
+ Describe "setup-pola-b.ps1 manifest creation" {
248
+ BeforeAll {
249
+ # Reuse pattern Branch 1 (traditional) – setelah setup sukses, manifest harus
250
+ # terbuat di .claude-kit/.install-manifest.json. Pakai fresh root supaya hash
251
+ # & state-nya independent dari describe lain.
252
+ $script:Root5 = Join-Path $env:TEMP ("lintasAI-setup-test-manifest-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
253
+ $null = New-Item -ItemType Directory -Path $script:Root5 -Force
254
+ script:Add-ProjectContent -Root $script:Root5
255
+ $script:Kit5 = Join-Path $script:Root5 '.claude-kit'
256
+ script:Copy-RealKit -Destination $script:Kit5
257
+ $script:SetupInKit5 = Join-Path $script:Kit5 'setup-pola-b.ps1'
258
+ }
259
+
260
+ AfterAll {
261
+ script:Remove-TestRoot -Path $script:Root5
262
+ }
263
+
264
+ It "Creates .install-manifest.json after successful setup" {
265
+ $result = script:Invoke-Setup -ScriptPath $script:SetupInKit5 -Args @('-Force', '-SkipTeamFiles')
266
+ $result.ExitCode | Should -Be 0
267
+ $manifestPath = Join-Path $script:Kit5 '.install-manifest.json'
268
+ (Test-Path -LiteralPath $manifestPath) | Should -BeTrue
269
+ }
270
+
271
+ It "Manifest is valid JSON with expected top-level fields" {
272
+ $manifestPath = Join-Path $script:Kit5 '.install-manifest.json'
273
+ (Test-Path -LiteralPath $manifestPath) | Should -BeTrue
274
+ $json = Get-Content -LiteralPath $manifestPath -Raw -Encoding UTF8
275
+ { $json | ConvertFrom-Json -ErrorAction Stop } | Should -Not -Throw
276
+ $obj = $json | ConvertFrom-Json
277
+ $obj.PSObject.Properties.Name | Should -Contain 'schema_version'
278
+ $obj.PSObject.Properties.Name | Should -Contain 'files'
279
+ }
280
+ }
package/uninstall.ps1 CHANGED
@@ -63,9 +63,20 @@ param(
63
63
  [switch]$DeleteAgents,
64
64
  [switch]$KeepKit,
65
65
  [switch]$Yes,
66
- [switch]$AllowProjectRootMismatch
66
+ [switch]$AllowProjectRootMismatch,
67
+ [string]$ProjectRoot = $null
67
68
  )
68
69
 
70
+ # ---- Resolve $ProjectRoot early (param-driven, fallback to script location) ----
71
+ # Kalau user pass -ProjectRoot pakai itu (untuk smoke test / CI). Kalau tidak, derive dari
72
+ # $PSScriptRoot (script ada di .claude-kit\, parent = project root).
73
+ if (-not $ProjectRoot) {
74
+ $ProjectRoot = Split-Path -Parent $PSScriptRoot
75
+ } else {
76
+ $ProjectRoot = (Resolve-Path $ProjectRoot -ErrorAction Stop).Path
77
+ }
78
+ Write-Host "Root proyek : $ProjectRoot"
79
+
69
80
  # ---- Deprecation handling: -Force jadi alias backward-compat untuk -AllowModified ----
70
81
  # Sebelumnya -Force overloaded (modified bypass + signature bypass). Pisahkan supaya
71
82
  # intent jelas. -Force masih bekerja untuk backward-compat tapi warn user.
@@ -81,8 +92,8 @@ if ($Force) {
81
92
  $ErrorActionPreference = 'Stop'
82
93
 
83
94
  # ---- Resolve folder kit (tempat script ini berada) ----
95
+ # $ProjectRoot sudah di-resolve di awal (param-driven). Jangan override.
84
96
  $KitDir = if ($PSScriptRoot) { $PSScriptRoot } elseif ($MyInvocation.MyCommand.Path) { Split-Path -Parent $MyInvocation.MyCommand.Path } else { (Get-Location).Path }
85
- $ProjectRoot = Split-Path -Parent $KitDir
86
97
  $ProjectName = Split-Path -Leaf $ProjectRoot
87
98
  $Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
88
99
 
package/update-kit.ps1 CHANGED
@@ -70,11 +70,22 @@ param(
70
70
  [switch]$DryRun,
71
71
  [switch]$AllowUntrustedRepo,
72
72
  [switch]$Force,
73
- [switch]$AllowUnsignedTag
73
+ [switch]$AllowUnsignedTag,
74
+ [string]$ProjectRoot = $null
74
75
  )
75
76
 
76
77
  $ErrorActionPreference = 'Stop'
77
78
 
79
+ # ---- Resolve $ProjectRoot early (param-driven, fallback to script location) ----
80
+ # Kalau user pass -ProjectRoot pakai itu (untuk smoke test / CI). Kalau tidak, derive dari
81
+ # $PSScriptRoot (script ada di .claude-kit\, parent = project root).
82
+ if (-not $ProjectRoot) {
83
+ $ProjectRoot = Split-Path -Parent $PSScriptRoot
84
+ } else {
85
+ $ProjectRoot = (Resolve-Path $ProjectRoot -ErrorAction Stop).Path
86
+ }
87
+ Write-Host "Root proyek : $ProjectRoot"
88
+
78
89
  # ---- Deprecation handling: -Force jadi alias backward-compat untuk -AllowUntrustedRepo ----
79
90
  # Sebelumnya -Force overloaded (GPG bypass + RepoUrl bypass). GPG bypass sekarang lewat
80
91
  # -AllowUnsignedTag; -Force narrowed jadi alias RepoUrl-allowlist-bypass saja, dengan warn.
@@ -89,9 +100,11 @@ if ($Force) {
89
100
  }
90
101
 
91
102
  # ---- Resolve paths (do this FIRST, sebelum Move-Item rename folder) ----
103
+ # $ProjectRoot (PascalCase) sudah di-resolve dari param di awal. $projectRoot (camelCase)
104
+ # di-alias supaya legacy code di bawah tetap jalan tanpa rename massal.
92
105
  $kitDir = if ($PSScriptRoot) { $PSScriptRoot } elseif ($MyInvocation.MyCommand.Path) { Split-Path -Parent $MyInvocation.MyCommand.Path } else { (Get-Location).Path }
93
106
  $kitFolderName = Split-Path -Leaf $kitDir
94
- $projectRoot = Split-Path -Parent $kitDir
107
+ $projectRoot = $ProjectRoot
95
108
  $timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
96
109
  $backupDir = "$kitDir.backup-$timestamp"
97
110
 
@@ -1,7 +0,0 @@
1
- # DEPRECATED (lintasAI v1.0.0 republish 2026-06-04)
2
-
3
- File ini deprecated. First-session workflow sudah jadi bagian dari JALANKAN_KIT.md.
4
-
5
- -> Pakai [JALANKAN_KIT.md](JALANKAN_KIT.md)
6
-
7
- Content lama bisa dicek di git history sebelum 2026-06-04.