@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.
- package/.gitignore +55 -0
- package/AGENTS.md.template +91 -0
- package/CHANGELOG.md +64 -0
- package/bin/lintasai.js +22 -3
- package/docs/NPX_INSTALL.md +217 -0
- package/kit.ps1 +139 -5
- package/lib/kit-files.psd1 +0 -1
- package/package.json +8 -2
- package/setup-pola-b.ps1 +51 -2
- package/tests/npx-init.Tests.ps1 +237 -0
- package/tests/package-bundle.Tests.ps1 +130 -0
- package/tests/setup-pola-b.Tests.ps1 +280 -0
- package/uninstall.ps1 +13 -2
- package/update-kit.ps1 +15 -2
- package/FIRST_SESSION_PROMPT_v1.md +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ojokesusu/lintasai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "AI workflow kit untuk Claude Code dengan tier model + cross-repo automation (Windows-first)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"lintasai": "./bin/lintasai.js"
|
|
@@ -8,12 +8,18 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"bin/",
|
|
10
10
|
"*.ps1",
|
|
11
|
+
"*.psm1",
|
|
11
12
|
"lib/",
|
|
12
13
|
"templates/",
|
|
13
14
|
"docs/",
|
|
14
15
|
"tests/",
|
|
15
16
|
"*.md",
|
|
16
|
-
"
|
|
17
|
+
"*.template",
|
|
18
|
+
"AGENTS.md.template",
|
|
19
|
+
".github/",
|
|
20
|
+
"decisions/",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
".gitignore"
|
|
17
23
|
],
|
|
18
24
|
"scripts": {
|
|
19
25
|
"test": "echo \"Pester tests run via ./tests/Run-Tests.ps1\" && exit 0"
|
package/setup-pola-b.ps1
CHANGED
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
param(
|
|
39
39
|
[switch]$Force,
|
|
40
40
|
[switch]$DryRun,
|
|
41
|
-
[switch]$SkipTeamFiles
|
|
41
|
+
[switch]$SkipTeamFiles,
|
|
42
|
+
[string]$ProjectRoot = $null
|
|
42
43
|
)
|
|
43
44
|
|
|
44
45
|
$ErrorActionPreference = 'Stop'
|
|
@@ -46,6 +47,50 @@ $ErrorActionPreference = 'Stop'
|
|
|
46
47
|
# ---- Resolve folder kit (tempat script ini berada) ----
|
|
47
48
|
$KitDir = if ($PSScriptRoot) { $PSScriptRoot } elseif ($MyInvocation.MyCommand.Path) { Split-Path -Parent $MyInvocation.MyCommand.Path } else { (Get-Location).Path }
|
|
48
49
|
|
|
50
|
+
# ---- Resolve ProjectRoot (npm wrapper vs traditional invocation) ----
|
|
51
|
+
# Traditional: kit lives at <project>/.claude-kit/, $ProjectRoot = parent of $KitDir.
|
|
52
|
+
# NPX wrapper: $ProjectRoot passed explicitly; kit source at $KitDir (npm cache) must
|
|
53
|
+
# be COPIED to $ProjectRoot/.claude-kit/ before setup continues.
|
|
54
|
+
$script:__lintasAI_NpxMode = $false
|
|
55
|
+
if (-not $ProjectRoot) {
|
|
56
|
+
# Traditional invocation - kit lives at <project>/.claude-kit/, parent is project
|
|
57
|
+
$ProjectRoot = Split-Path -Parent $KitDir
|
|
58
|
+
} else {
|
|
59
|
+
# NPX wrapper invocation - $ProjectRoot passed explicitly
|
|
60
|
+
Write-Host "[npx] Mode: explicit ProjectRoot = $ProjectRoot" -ForegroundColor Cyan
|
|
61
|
+
$script:__lintasAI_NpxMode = $true
|
|
62
|
+
}
|
|
63
|
+
$ProjectRoot = (Resolve-Path $ProjectRoot -ErrorAction Stop).Path
|
|
64
|
+
|
|
65
|
+
# ---- NPX mode: copy kit from $KitDir (npm cache) to $ProjectRoot/.claude-kit/ ----
|
|
66
|
+
# Detection: if $KitDir is NOT already inside $ProjectRoot/.claude-kit/, we need to copy.
|
|
67
|
+
$expectedKitDir = Join-Path $ProjectRoot '.claude-kit'
|
|
68
|
+
$kitDirResolved = (Resolve-Path $KitDir -ErrorAction Stop).Path
|
|
69
|
+
$expectedKitDirNormalized = $expectedKitDir.TrimEnd('\','/')
|
|
70
|
+
$kitDirNormalized = $kitDirResolved.TrimEnd('\','/')
|
|
71
|
+
if ($script:__lintasAI_NpxMode -and ($kitDirNormalized -ne $expectedKitDirNormalized)) {
|
|
72
|
+
Write-Host "[npx] Copy kit: $KitDir -> $expectedKitDir" -ForegroundColor Cyan
|
|
73
|
+
if (-not $DryRun) {
|
|
74
|
+
if (-not (Test-Path $expectedKitDir)) {
|
|
75
|
+
New-Item -ItemType Directory -Path $expectedKitDir -Force | Out-Null
|
|
76
|
+
}
|
|
77
|
+
# Robocopy or Copy-Item: use Copy-Item for cross-platform-ish behavior
|
|
78
|
+
Get-ChildItem -Path $KitDir -Force | ForEach-Object {
|
|
79
|
+
$destPath = Join-Path $expectedKitDir $_.Name
|
|
80
|
+
if ($_.PSIsContainer) {
|
|
81
|
+
Copy-Item -Path $_.FullName -Destination $destPath -Recurse -Force
|
|
82
|
+
} else {
|
|
83
|
+
Copy-Item -Path $_.FullName -Destination $destPath -Force
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
Write-Host "[npx] Kit copied successfully." -ForegroundColor Green
|
|
87
|
+
} else {
|
|
88
|
+
Write-Host "[DRY] [npx] Would copy kit contents from $KitDir to $expectedKitDir" -ForegroundColor Yellow
|
|
89
|
+
}
|
|
90
|
+
# Pivot $KitDir to the deployed location so rest of script operates on project copy
|
|
91
|
+
$KitDir = $expectedKitDir
|
|
92
|
+
}
|
|
93
|
+
|
|
49
94
|
# ---- Dot-source library helpers (heavy-lifting) ----
|
|
50
95
|
# Order penting: agents-md.ps1 dot-source SEBELUM manifest.ps1.
|
|
51
96
|
# agents-md.ps1 punya guard `if ($script:__lintasAI_AgentsMdLoaded)` (anti-redefine).
|
|
@@ -130,7 +175,11 @@ if ($kitFolderName -ne '.claude-kit') {
|
|
|
130
175
|
}
|
|
131
176
|
|
|
132
177
|
# ---- Resolve root proyek (parent dari folder kit) ----
|
|
133
|
-
$ProjectRoot
|
|
178
|
+
# NOTE: $ProjectRoot sudah di-resolve di atas (param eksplisit atau Split-Path -Parent $KitDir).
|
|
179
|
+
# Re-derive di sini HANYA kalau belum di-set (defensif). Skip kalau npx mode (sudah eksplisit).
|
|
180
|
+
if (-not $ProjectRoot) {
|
|
181
|
+
$ProjectRoot = Split-Path -Parent $KitDir
|
|
182
|
+
}
|
|
134
183
|
$ProjectName = Split-Path -Leaf $ProjectRoot
|
|
135
184
|
$Today = Get-Date -Format 'yyyy-MM-dd'
|
|
136
185
|
$Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#Requires -Module Pester
|
|
2
|
+
|
|
3
|
+
<#
|
|
4
|
+
.SYNOPSIS
|
|
5
|
+
Pester 5+ regression tests untuk v1.1.3 fix - npx-init flow (setup-pola-b.ps1
|
|
6
|
+
via npm wrapper dengan -ProjectRoot eksplisit).
|
|
7
|
+
|
|
8
|
+
.DESCRIPTION
|
|
9
|
+
v1.1.3 fix area:
|
|
10
|
+
1) AGENTS.md.template HARUS ke-bundle di tarball (sebelumnya hilang krn glob).
|
|
11
|
+
2) setup-pola-b.ps1 HARUS terima -ProjectRoot tanpa error (parameter exists).
|
|
12
|
+
3) Saat -ProjectRoot dipakai, $ProjectRoot != Split-Path -Parent $KitDir
|
|
13
|
+
(npm cache path BUKAN root proyek user).
|
|
14
|
+
4) Saat $KitDir bukan child of $ProjectRoot/.claude-kit, script auto-copy
|
|
15
|
+
isi kit dari $PSScriptRoot ke $ProjectRoot/.claude-kit.
|
|
16
|
+
5) Project name di-derive dari leaf $ProjectRoot (bukan parent KitDir).
|
|
17
|
+
6) Setelah init: AGENTS.md ada di $ProjectRoot (bukan di npm cache).
|
|
18
|
+
7) Setelah init: .claude-kit/setup-pola-b.ps1 ada di $ProjectRoot.
|
|
19
|
+
|
|
20
|
+
Strategi:
|
|
21
|
+
- BeforeAll: bikin temp dir mock-npm-cache (simulasi $KitDir) + temp dir
|
|
22
|
+
mock-project-root. Copy kit minimal (atau seluruh kit) ke mock-npm-cache.
|
|
23
|
+
- Jalankan setup-pola-b.ps1 -ProjectRoot <mockProjectRoot> -Force -SkipTeamFiles
|
|
24
|
+
dari mock-npm-cache.
|
|
25
|
+
- Assert hasil: AGENTS.md ada di $mockProjectRoot, .claude-kit/setup-pola-b.ps1
|
|
26
|
+
ada di $mockProjectRoot, dst.
|
|
27
|
+
- AfterAll: cleanup kedua temp dir.
|
|
28
|
+
|
|
29
|
+
Catatan:
|
|
30
|
+
- Test #1 (tarball content) pakai `npm pack --dry-run --json` untuk
|
|
31
|
+
enumerate file list TANPA bikin tarball asli (faster + no disk write).
|
|
32
|
+
- Test #2 (parameter exists) pakai AST static check (tidak invoke script).
|
|
33
|
+
- Test #3-7 perlu actual invocation tapi `-DryRun` tidak cukup karena
|
|
34
|
+
v1.1.3 copy step harus betul-betul dieksekusi untuk verifikasi.
|
|
35
|
+
#>
|
|
36
|
+
|
|
37
|
+
BeforeAll {
|
|
38
|
+
$ErrorActionPreference = 'Stop'
|
|
39
|
+
|
|
40
|
+
# ---- Resolve repo root (kit folder yang berisi setup-pola-b.ps1) ----
|
|
41
|
+
$script:KitRepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..') | Select-Object -ExpandProperty Path
|
|
42
|
+
$script:SetupScript = Join-Path $script:KitRepoRoot 'setup-pola-b.ps1'
|
|
43
|
+
$script:PackageJson = Join-Path $script:KitRepoRoot 'package.json'
|
|
44
|
+
$script:AgentsTemplate = Join-Path $script:KitRepoRoot 'AGENTS.md.template'
|
|
45
|
+
|
|
46
|
+
if (-not (Test-Path $script:SetupScript)) {
|
|
47
|
+
throw "setup-pola-b.ps1 not found at $script:SetupScript"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# ---- Temp dir untuk mock-npm-cache + mock-project-root ----
|
|
51
|
+
$stamp = Get-Date -Format 'yyyyMMddHHmmssfff'
|
|
52
|
+
$script:TempBase = Join-Path $env:TEMP "lintasAI-npx-init-tests-$stamp"
|
|
53
|
+
$script:MockNpmCache = Join-Path $script:TempBase 'npm-cache-kit'
|
|
54
|
+
$script:MockProjectRoot = Join-Path $script:TempBase 'my-shiny-project'
|
|
55
|
+
|
|
56
|
+
New-Item -ItemType Directory -Path $script:MockNpmCache -Force | Out-Null
|
|
57
|
+
New-Item -ItemType Directory -Path $script:MockProjectRoot -Force | Out-Null
|
|
58
|
+
|
|
59
|
+
# ---- Copy seluruh kit ke mock-npm-cache (simulasi npm extract tarball) ----
|
|
60
|
+
# Pakai robocopy supaya cepat, exclude tests/ supaya tidak rekursif.
|
|
61
|
+
$robocopyArgs = @(
|
|
62
|
+
$script:KitRepoRoot,
|
|
63
|
+
$script:MockNpmCache,
|
|
64
|
+
'/E', # include subdirs (empty too)
|
|
65
|
+
'/NFL', '/NDL', '/NJH', '/NJS', '/NC', '/NS', '/NP', # quiet
|
|
66
|
+
'/XD', 'node_modules', '.git'
|
|
67
|
+
)
|
|
68
|
+
& robocopy @robocopyArgs | Out-Null
|
|
69
|
+
# robocopy exit 0-7 = success; 8+ = error
|
|
70
|
+
if ($LASTEXITCODE -ge 8) {
|
|
71
|
+
throw "robocopy failed with exit code $LASTEXITCODE"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
$script:MockSetupScript = Join-Path $script:MockNpmCache 'setup-pola-b.ps1'
|
|
75
|
+
if (-not (Test-Path $script:MockSetupScript)) {
|
|
76
|
+
throw "Mock kit setup script missing at $script:MockSetupScript - copy failed"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
AfterAll {
|
|
81
|
+
if ($script:TempBase -and (Test-Path $script:TempBase)) {
|
|
82
|
+
Remove-Item -Path $script:TempBase -Recurse -Force -ErrorAction SilentlyContinue
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Describe "v1.1.3 - npm pack bundles AGENTS.md.template" {
|
|
87
|
+
It "AGENTS.md.template appears in npm pack file list" {
|
|
88
|
+
# `npm pack --dry-run --json` returns metadata including the file list
|
|
89
|
+
# without actually creating a tarball on disk.
|
|
90
|
+
Push-Location $script:KitRepoRoot
|
|
91
|
+
try {
|
|
92
|
+
$packJson = & npm pack --dry-run --json 2>$null | Out-String
|
|
93
|
+
$packJson | Should -Not -BeNullOrEmpty
|
|
94
|
+
$packData = $packJson | ConvertFrom-Json
|
|
95
|
+
# npm returns array of package objects
|
|
96
|
+
$pkg = if ($packData -is [array]) { $packData[0] } else { $packData }
|
|
97
|
+
$files = $pkg.files | ForEach-Object { $_.path }
|
|
98
|
+
$files | Should -Contain 'AGENTS.md.template'
|
|
99
|
+
} finally {
|
|
100
|
+
Pop-Location
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
Describe "v1.1.3 - setup-pola-b.ps1 accepts -ProjectRoot parameter" {
|
|
106
|
+
It "Has -ProjectRoot parameter declared in param block (AST check)" {
|
|
107
|
+
$tokens = $null
|
|
108
|
+
$errors = $null
|
|
109
|
+
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
|
|
110
|
+
$script:SetupScript, [ref]$tokens, [ref]$errors
|
|
111
|
+
)
|
|
112
|
+
$errors | Where-Object { $_.IncompleteInput -eq $false } | Should -BeNullOrEmpty
|
|
113
|
+
|
|
114
|
+
# Find top-level param block
|
|
115
|
+
$paramBlock = $ast.ParamBlock
|
|
116
|
+
$paramBlock | Should -Not -BeNullOrEmpty
|
|
117
|
+
|
|
118
|
+
$paramNames = $paramBlock.Parameters | ForEach-Object { $_.Name.VariablePath.UserPath }
|
|
119
|
+
$paramNames | Should -Contain 'ProjectRoot'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
It "Invokes without parameter binding error when -ProjectRoot supplied" {
|
|
123
|
+
# Quick sanity: -DryRun -Force -SkipTeamFiles + -ProjectRoot should
|
|
124
|
+
# at minimum get past param binding (may fail later for other reasons
|
|
125
|
+
# but NOT ParameterBindingException).
|
|
126
|
+
$sandboxRoot = Join-Path $script:TempBase 'paramcheck-root'
|
|
127
|
+
New-Item -ItemType Directory -Path $sandboxRoot -Force | Out-Null
|
|
128
|
+
|
|
129
|
+
$output = & powershell.exe -NoProfile -ExecutionPolicy Bypass `
|
|
130
|
+
-File $script:MockSetupScript `
|
|
131
|
+
-ProjectRoot $sandboxRoot `
|
|
132
|
+
-DryRun -Force -SkipTeamFiles 2>&1 | Out-String
|
|
133
|
+
|
|
134
|
+
# ParameterBindingException = test fail
|
|
135
|
+
$output | Should -Not -Match 'ParameterBindingException'
|
|
136
|
+
$output | Should -Not -Match "parameter cannot be found that matches"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
Describe "v1.1.3 - ProjectRoot resolution (npx wrapper mode)" {
|
|
141
|
+
It "When -ProjectRoot supplied, root = explicit value (NOT parent of KitDir)" {
|
|
142
|
+
# Run setup with -ProjectRoot = mockProjectRoot. KitDir = mockNpmCache.
|
|
143
|
+
# If fix broken: $ProjectRoot would default to Split-Path -Parent $KitDir
|
|
144
|
+
# = $script:TempBase (parent of mockNpmCache), WRONG.
|
|
145
|
+
# If fix works: $ProjectRoot stays at mockProjectRoot.
|
|
146
|
+
# Verify by checking that AGENTS.md lands in mockProjectRoot, not TempBase.
|
|
147
|
+
|
|
148
|
+
# Clean target first to avoid stale state from prior tests
|
|
149
|
+
$targetRoot = Join-Path $script:TempBase 'resolve-check-root'
|
|
150
|
+
if (Test-Path $targetRoot) { Remove-Item -Path $targetRoot -Recurse -Force }
|
|
151
|
+
New-Item -ItemType Directory -Path $targetRoot -Force | Out-Null
|
|
152
|
+
|
|
153
|
+
$output = & powershell.exe -NoProfile -ExecutionPolicy Bypass `
|
|
154
|
+
-File $script:MockSetupScript `
|
|
155
|
+
-ProjectRoot $targetRoot `
|
|
156
|
+
-Force -SkipTeamFiles 2>&1 | Out-String
|
|
157
|
+
|
|
158
|
+
# AGENTS.md must exist in targetRoot (explicit -ProjectRoot honored)
|
|
159
|
+
$agentsInTarget = Join-Path $targetRoot 'AGENTS.md'
|
|
160
|
+
Test-Path $agentsInTarget | Should -BeTrue -Because "AGENTS.md should land in -ProjectRoot ($targetRoot), got output:`n$output"
|
|
161
|
+
|
|
162
|
+
# AGENTS.md must NOT exist in TempBase (parent of MockNpmCache)
|
|
163
|
+
$agentsInParent = Join-Path $script:TempBase 'AGENTS.md'
|
|
164
|
+
Test-Path $agentsInParent | Should -BeFalse -Because "AGENTS.md must NOT leak to parent of KitDir"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
Describe "v1.1.3 - Auto-copy kit when ProjectRoot lacks .claude-kit" {
|
|
169
|
+
It "Triggers copy from `$PSScriptRoot when target .claude-kit absent" {
|
|
170
|
+
# Setup: fresh project root WITHOUT .claude-kit. Run setup with
|
|
171
|
+
# -ProjectRoot pointing there. Script should detect npx mode and copy
|
|
172
|
+
# kit contents from $KitDir (mockNpmCache) to $ProjectRoot/.claude-kit.
|
|
173
|
+
|
|
174
|
+
$autoCopyRoot = Join-Path $script:TempBase 'autocopy-root'
|
|
175
|
+
if (Test-Path $autoCopyRoot) { Remove-Item -Path $autoCopyRoot -Recurse -Force }
|
|
176
|
+
New-Item -ItemType Directory -Path $autoCopyRoot -Force | Out-Null
|
|
177
|
+
|
|
178
|
+
# Pre-condition: no .claude-kit yet
|
|
179
|
+
$kitTargetDir = Join-Path $autoCopyRoot '.claude-kit'
|
|
180
|
+
Test-Path $kitTargetDir | Should -BeFalse
|
|
181
|
+
|
|
182
|
+
$output = & powershell.exe -NoProfile -ExecutionPolicy Bypass `
|
|
183
|
+
-File $script:MockSetupScript `
|
|
184
|
+
-ProjectRoot $autoCopyRoot `
|
|
185
|
+
-Force -SkipTeamFiles 2>&1 | Out-String
|
|
186
|
+
|
|
187
|
+
# Post-condition: .claude-kit created with key files
|
|
188
|
+
Test-Path $kitTargetDir | Should -BeTrue -Because "Script should auto-copy kit to .claude-kit, output:`n$output"
|
|
189
|
+
Test-Path (Join-Path $kitTargetDir 'setup-pola-b.ps1') | Should -BeTrue
|
|
190
|
+
Test-Path (Join-Path $kitTargetDir 'CHANGELOG.md') | Should -BeTrue
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
Describe "v1.1.3 - Project name derived from -ProjectRoot leaf" {
|
|
195
|
+
It "Project name = Split-Path -Leaf `$ProjectRoot (not KitDir parent)" {
|
|
196
|
+
$namedRoot = Join-Path $script:TempBase 'unique-project-name-xyz'
|
|
197
|
+
if (Test-Path $namedRoot) { Remove-Item -Path $namedRoot -Recurse -Force }
|
|
198
|
+
New-Item -ItemType Directory -Path $namedRoot -Force | Out-Null
|
|
199
|
+
|
|
200
|
+
$output = & powershell.exe -NoProfile -ExecutionPolicy Bypass `
|
|
201
|
+
-File $script:MockSetupScript `
|
|
202
|
+
-ProjectRoot $namedRoot `
|
|
203
|
+
-Force -SkipTeamFiles 2>&1 | Out-String
|
|
204
|
+
|
|
205
|
+
$agentsPath = Join-Path $namedRoot 'AGENTS.md'
|
|
206
|
+
Test-Path $agentsPath | Should -BeTrue -Because "AGENTS.md must exist before name check, output:`n$output"
|
|
207
|
+
|
|
208
|
+
# Project name appears in deployed AGENTS.md (template fills it in heading)
|
|
209
|
+
$agentsContent = Get-Content -Path $agentsPath -Raw
|
|
210
|
+
$expectedName = Split-Path -Leaf $namedRoot
|
|
211
|
+
$agentsContent | Should -Match ([regex]::Escape($expectedName)) -Because "AGENTS.md should reference project name '$expectedName' from -ProjectRoot leaf"
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
Describe "v1.1.3 - Post-init artifacts in `$ProjectRoot" {
|
|
216
|
+
BeforeAll {
|
|
217
|
+
# One init run shared by both `It` blocks below
|
|
218
|
+
$script:PostInitRoot = Join-Path $script:TempBase 'postinit-root'
|
|
219
|
+
if (Test-Path $script:PostInitRoot) { Remove-Item -Path $script:PostInitRoot -Recurse -Force }
|
|
220
|
+
New-Item -ItemType Directory -Path $script:PostInitRoot -Force | Out-Null
|
|
221
|
+
|
|
222
|
+
$script:PostInitOutput = & powershell.exe -NoProfile -ExecutionPolicy Bypass `
|
|
223
|
+
-File $script:MockSetupScript `
|
|
224
|
+
-ProjectRoot $script:PostInitRoot `
|
|
225
|
+
-Force -SkipTeamFiles 2>&1 | Out-String
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
It "AGENTS.md exists at `$ProjectRoot root" {
|
|
229
|
+
$agentsPath = Join-Path $script:PostInitRoot 'AGENTS.md'
|
|
230
|
+
Test-Path $agentsPath | Should -BeTrue -Because "Post-init AGENTS.md must exist, output:`n$script:PostInitOutput"
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
It ".claude-kit/setup-pola-b.ps1 exists at `$ProjectRoot/.claude-kit" {
|
|
234
|
+
$kitSetup = Join-Path $script:PostInitRoot '.claude-kit\setup-pola-b.ps1'
|
|
235
|
+
Test-Path $kitSetup | Should -BeTrue -Because "Post-init kit setup script must exist in deployed .claude-kit"
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#Requires -Module Pester
|
|
2
|
+
|
|
3
|
+
<#
|
|
4
|
+
.SYNOPSIS
|
|
5
|
+
Regression tests untuk package.json files[] bundle.
|
|
6
|
+
|
|
7
|
+
.DESCRIPTION
|
|
8
|
+
Memastikan `npm pack` mem-bundle SEMUA file kritikal kit
|
|
9
|
+
(AGENTS.md.template, bin/lintasai.js, lib/, templates/, docs/, tests/).
|
|
10
|
+
|
|
11
|
+
Latar belakang: pernah ada regression di mana AGENTS.md.template
|
|
12
|
+
hilang dari tarball karena pattern files[] tidak match.
|
|
13
|
+
Test ini = guard supaya regression yang sama tidak terulang.
|
|
14
|
+
|
|
15
|
+
.NOTES
|
|
16
|
+
Pakai `npm pack --dry-run --json` untuk parsing deterministic.
|
|
17
|
+
#>
|
|
18
|
+
|
|
19
|
+
BeforeAll {
|
|
20
|
+
$script:KitRoot = Resolve-Path (Join-Path $PSScriptRoot '..')
|
|
21
|
+
$script:PackageJsonPath = Join-Path $script:KitRoot 'package.json'
|
|
22
|
+
|
|
23
|
+
# Run `npm pack --dry-run --json` SEKALI di BeforeAll (mahal).
|
|
24
|
+
Push-Location $script:KitRoot
|
|
25
|
+
try {
|
|
26
|
+
$rawOutput = & npm pack --dry-run --json 2>&1
|
|
27
|
+
$exitCode = $LASTEXITCODE
|
|
28
|
+
} finally {
|
|
29
|
+
Pop-Location
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if ($exitCode -ne 0) {
|
|
33
|
+
throw "npm pack --dry-run gagal (exit $exitCode). Output: $rawOutput"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# npm pack --json kadang campur stderr ke stdout; ambil JSON array/object di akhir.
|
|
37
|
+
$jsonText = ($rawOutput | Out-String).Trim()
|
|
38
|
+
# Cari posisi '[' atau '{' pertama yang masuk akal — npm pack --json balikin array.
|
|
39
|
+
$jsonStart = $jsonText.IndexOf('[')
|
|
40
|
+
if ($jsonStart -lt 0) { $jsonStart = $jsonText.IndexOf('{') }
|
|
41
|
+
if ($jsonStart -gt 0) {
|
|
42
|
+
$jsonText = $jsonText.Substring($jsonStart)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
$script:PackInfo = $jsonText | ConvertFrom-Json
|
|
46
|
+
# npm pack --json returns array; ambil entry pertama.
|
|
47
|
+
if ($script:PackInfo -is [array]) {
|
|
48
|
+
$script:PackInfo = $script:PackInfo[0]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# File list (relative path di dalam tarball).
|
|
52
|
+
$script:PackFiles = @($script:PackInfo.files | ForEach-Object { $_.path })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Describe "package.json structural integrity" {
|
|
56
|
+
It "package.json adalah valid JSON" {
|
|
57
|
+
{ Get-Content -Raw -Path $script:PackageJsonPath | ConvertFrom-Json } | Should -Not -Throw
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
It "package.json punya files[] array" {
|
|
61
|
+
$pkg = Get-Content -Raw -Path $script:PackageJsonPath | ConvertFrom-Json
|
|
62
|
+
$pkg.files | Should -Not -BeNullOrEmpty
|
|
63
|
+
$pkg.files.Count | Should -BeGreaterThan 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
It "files[] include pattern untuk AGENTS.md.template (explicit atau via wildcard)" {
|
|
67
|
+
$pkg = Get-Content -Raw -Path $script:PackageJsonPath | ConvertFrom-Json
|
|
68
|
+
$patterns = @($pkg.files)
|
|
69
|
+
|
|
70
|
+
# Match jika ada salah satu: explicit "AGENTS.md.template", wildcard "*.template", atau wildcard "*.md".
|
|
71
|
+
$hasExplicit = $patterns -contains 'AGENTS.md.template'
|
|
72
|
+
$hasTemplateWildcard = $patterns -contains '*.template'
|
|
73
|
+
$hasMdWildcard = $patterns -contains '*.md'
|
|
74
|
+
|
|
75
|
+
($hasExplicit -or $hasTemplateWildcard -or $hasMdWildcard) | Should -BeTrue `
|
|
76
|
+
-Because "AGENTS.md.template harus ter-cover oleh pattern di files[] (explicit atau wildcard)"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Describe "npm pack --dry-run bundle coverage" {
|
|
81
|
+
It "Tarball memuat AGENTS.md.template" {
|
|
82
|
+
$script:PackFiles | Should -Contain 'AGENTS.md.template' `
|
|
83
|
+
-Because "AGENTS.md.template adalah file kritikal kit; tanpa ini setup Pola B di project gagal"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
It "Tarball memuat bin/lintasai.js" {
|
|
87
|
+
$script:PackFiles | Should -Contain 'bin/lintasai.js' `
|
|
88
|
+
-Because "Entry point CLI; tanpa ini `npx lintasai` tidak jalan"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
It "Tarball memuat file di lib/" {
|
|
92
|
+
$libFiles = @($script:PackFiles | Where-Object { $_ -like 'lib/*' })
|
|
93
|
+
$libFiles.Count | Should -BeGreaterThan 0 -Because "lib/ harus ada di tarball"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
It "Tarball memuat file di templates/" {
|
|
97
|
+
$templateFiles = @($script:PackFiles | Where-Object { $_ -like 'templates/*' })
|
|
98
|
+
$templateFiles.Count | Should -BeGreaterThan 0 -Because "templates/ harus ada di tarball"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
It "Tarball memuat file di docs/" {
|
|
102
|
+
$docsFiles = @($script:PackFiles | Where-Object { $_ -like 'docs/*' })
|
|
103
|
+
$docsFiles.Count | Should -BeGreaterThan 0 -Because "docs/ harus ada di tarball"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
It "Tarball memuat file di tests/" {
|
|
107
|
+
$testFiles = @($script:PackFiles | Where-Object { $_ -like 'tests/*' })
|
|
108
|
+
$testFiles.Count | Should -BeGreaterThan 0 -Because "tests/ harus ada di tarball (untuk konsumen yang mau re-verify)"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Describe "Tarball size & file count sanity" {
|
|
113
|
+
It "Tarball size < 2 MB" {
|
|
114
|
+
# npm pack --json balikin `size` (unpacked) dan `unpackedSize`. Pakai size (tarball compressed) kalau ada.
|
|
115
|
+
$size = if ($null -ne $script:PackInfo.size) { $script:PackInfo.size } else { $script:PackInfo.unpackedSize }
|
|
116
|
+
$size | Should -Not -BeNullOrEmpty
|
|
117
|
+
$size | Should -BeLessThan (2 * 1024 * 1024) `
|
|
118
|
+
-Because "Kit harus stay lightweight (<2MB). Saat ini: $([math]::Round($size/1024,1)) KB"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
It "Tarball entryCount > 50 (sanity check, bukan empty bundle)" {
|
|
122
|
+
$entryCount = if ($null -ne $script:PackInfo.entryCount) {
|
|
123
|
+
$script:PackInfo.entryCount
|
|
124
|
+
} else {
|
|
125
|
+
$script:PackFiles.Count
|
|
126
|
+
}
|
|
127
|
+
$entryCount | Should -BeGreaterThan 50 `
|
|
128
|
+
-Because "Kit punya banyak file (templates, docs, lib, tests). <50 = something missing. Actual: $entryCount"
|
|
129
|
+
}
|
|
130
|
+
}
|