@ojokesusu/lintasai 1.1.2
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/.github/workflows/publish-npm.yml +40 -0
- package/.github/workflows/validate.yml +93 -0
- package/AUDIT_POST_SETUP_PROMPT_v1.md +280 -0
- package/BOOTSTRAP_PROJECT_DOCS_PROMPT_v1.md +3 -0
- package/CHANGELOG.md +313 -0
- package/CLAUDE_universal_v1.md +1021 -0
- package/CONTRIBUTING.md +101 -0
- package/FIRST_SESSION_PROMPT_v1.md +7 -0
- package/JALANKAN_KIT.md +188 -0
- package/LICENSE +21 -0
- package/MULAI_DI_SINI.md +145 -0
- package/PROJECT_KICKOFF_PROMPT_v1.md +3 -0
- package/PROJECT_LIFECYCLE_PROMPT_v1.md +536 -0
- package/PROJECT_MIGRATION_PROMPT_v1.md +3 -0
- package/README.md +505 -0
- package/SETUP_POLA_B_PROMPT_v1.md +5 -0
- package/SPLIT_REPO_MIGRATION_PROMPT_v1.md +485 -0
- package/TEAM_ROLLOUT_GUIDE_v1.md +172 -0
- package/UPDATE_DOCS_PROMPT_v1.md +3 -0
- package/UPDATE_KIT_PROMPT_v1.md +213 -0
- package/bin/lintasai.js +81 -0
- package/docs/SIGNED_RELEASE.md +162 -0
- package/install-windows.ps1 +225 -0
- package/kit.ps1 +508 -0
- package/lib/agents-md.ps1 +174 -0
- package/lib/git-helpers.ps1 +104 -0
- package/lib/kit-files.psd1 +133 -0
- package/lib/manifest-signing.ps1 +65 -0
- package/lib/manifest.ps1 +267 -0
- package/lib/rollback.ps1 +241 -0
- package/lib/safety.ps1 +193 -0
- package/lib/template-deploy.ps1 +242 -0
- package/lib/version-detect.ps1 +161 -0
- package/package.json +36 -0
- package/setup-pola-b.ps1 +687 -0
- package/templates/ANALOGI_LIBRARY.md +7 -0
- package/templates/CLAUDE_TEAM_GUIDE.md +505 -0
- package/templates/CROSS_REPO_TYPES_PIPELINE.md +473 -0
- package/templates/DB_SCHEMA_SCAN_PROMPT.md +194 -0
- package/templates/DISCORD_BOT_INTEGRATION.md +187 -0
- package/templates/GLOSSARY_NON_PROGRAMMER.md +361 -0
- package/templates/INDEX.md +157 -0
- package/templates/MCP_SETUP.md +1145 -0
- package/templates/MIGRATE_TO_SUBFOLDER_PROMPT_v1.md +220 -0
- package/templates/ONBOARDING.md +172 -0
- package/templates/PROJECT_STARTER_TEMPLATES.md +264 -0
- package/templates/PROMPT_LIBRARY.md +790 -0
- package/templates/RLS_SETUP_PROMPT.md +167 -0
- package/templates/SECURITY_INCIDENT_PLAYBOOK.md +191 -0
- package/templates/SPLIT_REPO_AGENTS_TEMPLATES.md +32 -0
- package/templates/SPLIT_REPO_NON_PROGRAMMER_PROMPTS.md +604 -0
- package/templates/SPLIT_REPO_TOOLS_SETUP.md +388 -0
- package/templates/STACK_DETECTION_PATTERN.md +261 -0
- package/templates/STACK_GUIDE.md +564 -0
- package/templates/STACK_MIGRATION_GUIDE.md +154 -0
- package/templates/STACK_VERSIONS.md +31 -0
- package/templates/UPDATE_GUIDE.md +246 -0
- package/templates/_EXAMPLE.md +110 -0
- package/templates/_PATTERNS.md +173 -0
- package/templates/architecture.md +180 -0
- package/templates/architecture_auto.md +61 -0
- package/templates/decisions/README.md +108 -0
- package/templates/decisions/_TEMPLATE.md +84 -0
- package/templates/feature-flags-advanced.md +171 -0
- package/templates/github/CODEOWNERS.template +61 -0
- package/templates/github/GENERATE_TYPES_SCRIPT.md +77 -0
- package/templates/github/PUBLISH_SHARED_WORKFLOW.yml +52 -0
- package/templates/github/RECEIVE_BACKEND_UPDATE.yml +106 -0
- package/templates/github/RENOVATE_FRONTEND.json +28 -0
- package/templates/github/TRIGGER_FRONTEND_UPDATE.yml +29 -0
- package/templates/github/pull_request_template.md +44 -0
- package/templates/github/scripts/ai-review.js +153 -0
- package/templates/github/workflows/ai-review.yml +61 -0
- package/templates/github/workflows/backup-schemas.yml +169 -0
- package/templates/glossary.md +110 -0
- package/templates/split-agents/BACKEND.md +149 -0
- package/templates/split-agents/FRONTEND.md +141 -0
- package/templates/split-agents/SHARED.md +82 -0
- package/templates/split-agents/TOOLS.md +77 -0
- package/tests/Run-Tests.ps1 +19 -0
- package/tests/lib-safety.Tests.ps1 +66 -0
- package/tests/rollback.Tests.ps1 +66 -0
- package/tests/uninstall.Tests.ps1 +265 -0
- package/tests/update-kit.Tests.ps1 +78 -0
- package/uninstall.ps1 +794 -0
- package/update-kit.ps1 +907 -0
package/kit.ps1
ADDED
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
kit.ps1 - Single entry point untuk operasi kit lintasAI
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Router yang delegate ke script per-operation. Tujuan: 1 command yang gampang
|
|
7
|
+
diingat staff IT, daripada hafal 3 nama script berbeda.
|
|
8
|
+
|
|
9
|
+
Subcommand yang tersedia:
|
|
10
|
+
setup - Setup Pola B di proyek (delegate ke setup-pola-b.ps1)
|
|
11
|
+
update - Update kit ke versi terbaru via re-clone (delegate ke update-kit.ps1)
|
|
12
|
+
uninstall - Hapus kit dari proyek dengan AMAN (manifest-based, delegate ke uninstall.ps1)
|
|
13
|
+
doctor - Diagnostic: cek versi + file utuh + broken cross-ref + integrity (sha256)
|
|
14
|
+
scan - Re-run scan project (tanpa setup) — generate ringkasan kandidat CRITICAL
|
|
15
|
+
version - Print versi kit aktif (dari manifest)
|
|
16
|
+
rollback - Rollback kit ke snapshot sebelum update (delegate ke lib\rollback.ps1)
|
|
17
|
+
help - Tampilkan usage
|
|
18
|
+
|
|
19
|
+
.PARAMETER Command
|
|
20
|
+
Subcommand yang mau dijalankan. Required.
|
|
21
|
+
|
|
22
|
+
.PARAMETER Args
|
|
23
|
+
Arguments yang di-pass ke subcommand. Optional.
|
|
24
|
+
|
|
25
|
+
.EXAMPLE
|
|
26
|
+
.\.claude-kit\kit.ps1 setup
|
|
27
|
+
.\.claude-kit\kit.ps1 setup -Force
|
|
28
|
+
.\.claude-kit\kit.ps1 update
|
|
29
|
+
.\.claude-kit\kit.ps1 update -NoBackup
|
|
30
|
+
.\.claude-kit\kit.ps1 uninstall -DryRun
|
|
31
|
+
.\.claude-kit\kit.ps1 uninstall -Force
|
|
32
|
+
.\.claude-kit\kit.ps1 doctor
|
|
33
|
+
.\.claude-kit\kit.ps1 scan
|
|
34
|
+
.\.claude-kit\kit.ps1 version
|
|
35
|
+
.\.claude-kit\kit.ps1 help
|
|
36
|
+
|
|
37
|
+
.NOTES
|
|
38
|
+
Versi : 1.0
|
|
39
|
+
Tanggal: 2026-06-03
|
|
40
|
+
Run : .\.claude-kit\kit.ps1 <command>
|
|
41
|
+
Atau : powershell -ExecutionPolicy Bypass -File .\.claude-kit\kit.ps1 <command>
|
|
42
|
+
|
|
43
|
+
Backward compatibility: setup-pola-b.ps1 + update-kit.ps1 tetap bisa dipanggil
|
|
44
|
+
langsung (tidak deprecated). kit.ps1 cuma alternative entry point yang lebih ringkas.
|
|
45
|
+
#>
|
|
46
|
+
|
|
47
|
+
[CmdletBinding()]
|
|
48
|
+
param(
|
|
49
|
+
[Parameter(Position = 0)]
|
|
50
|
+
[ValidateSet('setup', 'update', 'uninstall', 'doctor', 'scan', 'version', 'rollback', 'help', '')]
|
|
51
|
+
[string]$Command = '',
|
|
52
|
+
|
|
53
|
+
[Parameter(ValueFromRemainingArguments = $true)]
|
|
54
|
+
[string[]]$ExtraArgs
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
$ErrorActionPreference = 'Stop'
|
|
58
|
+
|
|
59
|
+
# ---- Resolve paths ----
|
|
60
|
+
$KitDir = if ($PSScriptRoot) { $PSScriptRoot } elseif ($MyInvocation.MyCommand.Path) { Split-Path -Parent $MyInvocation.MyCommand.Path } else { (Get-Location).Path }
|
|
61
|
+
$ProjectRoot = Split-Path -Parent $KitDir
|
|
62
|
+
|
|
63
|
+
# ---- Resolve lib paths (optional dependencies) ----
|
|
64
|
+
$libRollback = Join-Path $PSScriptRoot 'lib\rollback.ps1'
|
|
65
|
+
$libSafety = Join-Path $PSScriptRoot 'lib\safety.ps1'
|
|
66
|
+
|
|
67
|
+
# ---- Helper: detect kit version (defense-in-depth fallback chain) ----
|
|
68
|
+
# Source 1: .install-manifest.json -> metadata.kit_version (PRIMARY — what was installed)
|
|
69
|
+
# Source 2: $script:KIT_VERSION constant (kit.ps1's own version awareness)
|
|
70
|
+
# Source 3: CHANGELOG.md first "## v X.Y.Z" line, or "## [X.Y.Z]" entry
|
|
71
|
+
# Last resort: "unknown" (NOT "not installed" — misleading; the kit itself is present)
|
|
72
|
+
function Get-KitVersion {
|
|
73
|
+
$ver = $null
|
|
74
|
+
|
|
75
|
+
# Source 1: manifest
|
|
76
|
+
$manifestPath = Join-Path $KitDir '.install-manifest.json'
|
|
77
|
+
if (Test-Path $manifestPath) {
|
|
78
|
+
try {
|
|
79
|
+
$json = [System.IO.File]::ReadAllText($manifestPath, [System.Text.Encoding]::UTF8)
|
|
80
|
+
$m = ConvertFrom-Json $json -ErrorAction Stop
|
|
81
|
+
if ($m.metadata -and $m.metadata.kit_version) {
|
|
82
|
+
$ver = $m.metadata.kit_version
|
|
83
|
+
}
|
|
84
|
+
} catch { }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Source 2: $script:KIT_VERSION constant (optional — may not be defined)
|
|
88
|
+
if (-not $ver -and $script:KIT_VERSION) {
|
|
89
|
+
$ver = $script:KIT_VERSION
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Source 3: CHANGELOG.md — try "## v1.2.3" first, then "## [1.2.3]"
|
|
93
|
+
if (-not $ver) {
|
|
94
|
+
$changelogPath = Join-Path $KitDir 'CHANGELOG.md'
|
|
95
|
+
if (Test-Path $changelogPath) {
|
|
96
|
+
try {
|
|
97
|
+
$first = Get-Content $changelogPath -Encoding UTF8 -ErrorAction Stop |
|
|
98
|
+
Where-Object { $_ -match '^##\s+v\d+\.' } |
|
|
99
|
+
Select-Object -First 1
|
|
100
|
+
if ($first -and ($first -match '(v\d+\.\d+\.\d+)')) {
|
|
101
|
+
$ver = $matches[1]
|
|
102
|
+
} else {
|
|
103
|
+
$bracket = Select-String -Path $changelogPath -Pattern '## \[(\d+\.\d+\.\d+)\]' -List -ErrorAction SilentlyContinue
|
|
104
|
+
if ($bracket) { $ver = 'v' + $bracket.Matches[0].Groups[1].Value }
|
|
105
|
+
}
|
|
106
|
+
} catch { }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Last resort
|
|
111
|
+
if (-not $ver) { $ver = 'unknown' }
|
|
112
|
+
|
|
113
|
+
return $ver
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# ---- Helper: print usage ----
|
|
117
|
+
function Show-Help {
|
|
118
|
+
Write-Host ""
|
|
119
|
+
Write-Host "kit.ps1 - Single entry point untuk kit lintasAI (v$(Get-KitVersion))" -ForegroundColor Cyan
|
|
120
|
+
Write-Host ""
|
|
121
|
+
Write-Host "USAGE:" -ForegroundColor Cyan
|
|
122
|
+
Write-Host " .\.claude-kit\kit.ps1 <command> [args]"
|
|
123
|
+
Write-Host ""
|
|
124
|
+
Write-Host "COMMANDS:" -ForegroundColor Cyan
|
|
125
|
+
Write-Host " setup - Setup Pola B di proyek (copy AGENTS.md, docs skeleton, file tim)"
|
|
126
|
+
Write-Host " Args: -Force, -DryRun, -SkipTeamFiles"
|
|
127
|
+
Write-Host ""
|
|
128
|
+
Write-Host " update - Update kit ke versi terbaru via re-clone fresh dari GitHub"
|
|
129
|
+
Write-Host " Args: -NoBackup, -RepoUrl <url>, -Branch <name>, -DryRun"
|
|
130
|
+
Write-Host ""
|
|
131
|
+
Write-Host " uninstall - Hapus kit dari proyek dengan AMAN (manifest-based diff)"
|
|
132
|
+
Write-Host " Args: -DryRun, -Force, -DeleteAgents, -KeepKit, -Yes, -AllowProjectRootMismatch"
|
|
133
|
+
Write-Host " File project (yang BUKAN dari kit) AMAN tidak terhapus."
|
|
134
|
+
Write-Host " Path traversal + symlink protection aktif by default."
|
|
135
|
+
Write-Host ""
|
|
136
|
+
Write-Host " doctor - Diagnostic: cek versi + file inti utuh + cross-ref"
|
|
137
|
+
Write-Host " (no args)"
|
|
138
|
+
Write-Host ""
|
|
139
|
+
Write-Host " scan - Re-run scan project untuk identifikasi kandidat CRITICAL"
|
|
140
|
+
Write-Host " (tanpa setup ulang)"
|
|
141
|
+
Write-Host ""
|
|
142
|
+
Write-Host " version - Print versi kit aktif (dari .install-manifest.json)"
|
|
143
|
+
Write-Host ""
|
|
144
|
+
Write-Host " rollback - Rollback kit ke snapshot sebelum update terakhir"
|
|
145
|
+
Write-Host " (delegate ke lib\rollback.ps1)"
|
|
146
|
+
Write-Host ""
|
|
147
|
+
Write-Host " help - Tampilkan help ini"
|
|
148
|
+
Write-Host ""
|
|
149
|
+
Write-Host "EXAMPLES:" -ForegroundColor Cyan
|
|
150
|
+
Write-Host " .\.claude-kit\kit.ps1 setup -Force"
|
|
151
|
+
Write-Host " .\.claude-kit\kit.ps1 update"
|
|
152
|
+
Write-Host " .\.claude-kit\kit.ps1 doctor"
|
|
153
|
+
Write-Host ""
|
|
154
|
+
Write-Host "BACKWARD COMPATIBILITY:" -ForegroundColor Cyan
|
|
155
|
+
Write-Host " Setup script lama tetap bisa dipanggil langsung:"
|
|
156
|
+
Write-Host " .\.claude-kit\setup-pola-b.ps1 -Force"
|
|
157
|
+
Write-Host " .\.claude-kit\update-kit.ps1"
|
|
158
|
+
Write-Host ""
|
|
159
|
+
Write-Host " kit.ps1 cuma alternative entry point yang lebih ringkas."
|
|
160
|
+
Write-Host ""
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# ---- Helper: doctor diagnostic ----
|
|
164
|
+
function Invoke-Doctor {
|
|
165
|
+
Write-Host ""
|
|
166
|
+
Write-Host "=== Kit Doctor (diagnostic) ===" -ForegroundColor Cyan
|
|
167
|
+
Write-Host ""
|
|
168
|
+
|
|
169
|
+
$ok = 0
|
|
170
|
+
$warn = 0
|
|
171
|
+
$err = 0
|
|
172
|
+
|
|
173
|
+
# 1. Kit version
|
|
174
|
+
$version = Get-KitVersion
|
|
175
|
+
if ($version -match '^v?\d+\.\d+\.\d+') {
|
|
176
|
+
$display = if ($version -match '^v') { $version } else { "v$version" }
|
|
177
|
+
Write-Host ("OK Kit version: {0}" -f $display) -ForegroundColor Green
|
|
178
|
+
$ok++
|
|
179
|
+
} elseif ($version -eq 'unknown') {
|
|
180
|
+
Write-Host "WARN Kit version: unknown (manifest hilang, `$script:KIT_VERSION kosong, CHANGELOG.md tidak parseable)" -ForegroundColor Yellow
|
|
181
|
+
$warn++
|
|
182
|
+
} else {
|
|
183
|
+
Write-Host ("ERROR Kit version: format tidak dikenali ('{0}')" -f $version) -ForegroundColor Red
|
|
184
|
+
$err++
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# 2. File inti check
|
|
188
|
+
# Source of truth: lib\kit-files.psd1 (single manifest, di-load semua konsumen)
|
|
189
|
+
$kitFilesPsd1 = Join-Path $KitDir 'lib\kit-files.psd1'
|
|
190
|
+
if (-not (Test-Path $kitFilesPsd1)) {
|
|
191
|
+
Write-Host "ERROR lib\kit-files.psd1 hilang (manifest single-source-of-truth)" -ForegroundColor Red
|
|
192
|
+
$err++
|
|
193
|
+
$wajibAda = @()
|
|
194
|
+
} else {
|
|
195
|
+
$kitFiles = Import-PowerShellDataFile -Path $kitFilesPsd1
|
|
196
|
+
$wajibAda = @(
|
|
197
|
+
$kitFiles.core_prompts +
|
|
198
|
+
$kitFiles.universal_rules +
|
|
199
|
+
$kitFiles.scripts +
|
|
200
|
+
$kitFiles.lib_files +
|
|
201
|
+
$kitFiles.templates +
|
|
202
|
+
$kitFiles.docs +
|
|
203
|
+
$kitFiles.tests +
|
|
204
|
+
$kitFiles.ci +
|
|
205
|
+
$kitFiles.meta
|
|
206
|
+
) | ForEach-Object { $_ -replace '/', '\' }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if ($wajibAda.Count -gt 0) {
|
|
210
|
+
$missing = @()
|
|
211
|
+
foreach ($f in $wajibAda) {
|
|
212
|
+
$p = Join-Path $KitDir $f
|
|
213
|
+
if (-not (Test-Path $p)) { $missing += $f }
|
|
214
|
+
}
|
|
215
|
+
if ($missing.Count -eq 0) {
|
|
216
|
+
Write-Host ("OK {0} file inti utuh" -f $wajibAda.Count) -ForegroundColor Green
|
|
217
|
+
$ok++
|
|
218
|
+
} else {
|
|
219
|
+
Write-Host ("ERROR {0} file missing:" -f $missing.Count) -ForegroundColor Red
|
|
220
|
+
$missing | ForEach-Object { Write-Host (" - {0}" -f $_) -ForegroundColor Red }
|
|
221
|
+
$err++
|
|
222
|
+
Write-Host " Saran: .\.claude-kit\kit.ps1 update (re-clone fresh)" -ForegroundColor Yellow
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# 2b. Integrity check (sha256) — verifies files haven't been modified since install
|
|
227
|
+
$manifestPath = Join-Path $KitDir '.install-manifest.json'
|
|
228
|
+
if (Test-Path $manifestPath) {
|
|
229
|
+
try {
|
|
230
|
+
$manifestRaw = Get-Content -Path $manifestPath -Raw -ErrorAction Stop
|
|
231
|
+
$manifest = $manifestRaw | ConvertFrom-Json -ErrorAction Stop
|
|
232
|
+
} catch {
|
|
233
|
+
Write-Host ("WARN Integrity check skipped: gagal parse manifest ({0})" -f $_.Exception.Message) -ForegroundColor Yellow
|
|
234
|
+
$warn++
|
|
235
|
+
$manifest = $null
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if ($manifest -ne $null) {
|
|
239
|
+
$pristine = 0
|
|
240
|
+
$modifiedList = @()
|
|
241
|
+
$integrityMissing = 0
|
|
242
|
+
|
|
243
|
+
$entries = @()
|
|
244
|
+
if ($manifest.files) { $entries = $manifest.files }
|
|
245
|
+
elseif ($manifest.entries) { $entries = $manifest.entries }
|
|
246
|
+
|
|
247
|
+
foreach ($entry in $entries) {
|
|
248
|
+
$relPath = $null
|
|
249
|
+
if ($entry.path) { $relPath = $entry.path }
|
|
250
|
+
elseif ($entry.file) { $relPath = $entry.file }
|
|
251
|
+
if (-not $relPath) { continue }
|
|
252
|
+
|
|
253
|
+
$expectedSha = $null
|
|
254
|
+
if ($entry.sha256) { $expectedSha = $entry.sha256 }
|
|
255
|
+
if (-not $expectedSha) { continue }
|
|
256
|
+
|
|
257
|
+
$relPathNorm = $relPath -replace '/', '\'
|
|
258
|
+
# Manifest stores paths relative to PROJECT ROOT (e.g. ".claude-kit/lib/rollback.ps1",
|
|
259
|
+
# "AGENTS.md"), bukan relative ke $KitDir. Lihat setup-pola-b.ps1 Add-ToManifest:
|
|
260
|
+
# `$relPath = $FilePath.Replace($script:ProjectRoot, '')`. Pakai $ProjectRoot
|
|
261
|
+
# (sudah didefine di line ~61) supaya tidak double-prefix ".claude-kit/.claude-kit/...".
|
|
262
|
+
$absPath = Join-Path $ProjectRoot $relPathNorm
|
|
263
|
+
|
|
264
|
+
if (-not (Test-Path $absPath)) {
|
|
265
|
+
$integrityMissing++
|
|
266
|
+
continue
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
$actualSha = (Get-FileHash -Path $absPath -Algorithm SHA256 -ErrorAction Stop).Hash
|
|
271
|
+
if ($actualSha -eq $expectedSha.ToUpper() -or $actualSha.ToLower() -eq $expectedSha.ToLower()) {
|
|
272
|
+
$pristine++
|
|
273
|
+
} else {
|
|
274
|
+
$modifiedList += $relPath
|
|
275
|
+
}
|
|
276
|
+
} catch {
|
|
277
|
+
$modifiedList += ("{0} (hash error: {1})" -f $relPath, $_.Exception.Message)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
$modifiedCount = $modifiedList.Count
|
|
282
|
+
Write-Host ("OK Integrity: PRISTINE={0}, MODIFIED={1}, MISSING={2}" -f $pristine, $modifiedCount, $integrityMissing) -ForegroundColor Green
|
|
283
|
+
if ($modifiedCount -gt 0) {
|
|
284
|
+
Write-Host "WARN File berikut dimodifikasi sejak install (mungkin disengaja):" -ForegroundColor Yellow
|
|
285
|
+
$modifiedList | ForEach-Object { Write-Host (" - {0}" -f $_) -ForegroundColor Yellow }
|
|
286
|
+
$warn++
|
|
287
|
+
} else {
|
|
288
|
+
$ok++
|
|
289
|
+
}
|
|
290
|
+
if ($integrityMissing -gt 0) {
|
|
291
|
+
Write-Host ("ERROR Integrity: {0} file di manifest tapi tidak ada di disk" -f $integrityMissing) -ForegroundColor Red
|
|
292
|
+
$err++
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
} else {
|
|
296
|
+
Write-Host "INFO Integrity check skipped: .install-manifest.json tidak ada (kit pre-manifest atau belum di-install)" -ForegroundColor Cyan
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
# 3. AGENTS.md di root proyek
|
|
300
|
+
$agentsAtRoot = Join-Path $ProjectRoot 'AGENTS.md'
|
|
301
|
+
if (Test-Path $agentsAtRoot) {
|
|
302
|
+
Write-Host "OK AGENTS.md ada di root proyek" -ForegroundColor Green
|
|
303
|
+
$ok++
|
|
304
|
+
} else {
|
|
305
|
+
Write-Host "WARN AGENTS.md belum di-copy ke root proyek" -ForegroundColor Yellow
|
|
306
|
+
Write-Host " Saran: .\.claude-kit\kit.ps1 setup" -ForegroundColor Yellow
|
|
307
|
+
$warn++
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# 4. docs/ folder
|
|
311
|
+
$docsDir = Join-Path $ProjectRoot 'docs'
|
|
312
|
+
if (Test-Path $docsDir) {
|
|
313
|
+
$docsCount = (Get-ChildItem $docsDir -Recurse -File).Count
|
|
314
|
+
Write-Host ("OK docs/ ada ({0} file)" -f $docsCount) -ForegroundColor Green
|
|
315
|
+
$ok++
|
|
316
|
+
} else {
|
|
317
|
+
Write-Host "WARN docs/ belum dibuat" -ForegroundColor Yellow
|
|
318
|
+
Write-Host " Saran: .\.claude-kit\kit.ps1 setup" -ForegroundColor Yellow
|
|
319
|
+
$warn++
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
# 5. .github/ folder (team mode files)
|
|
323
|
+
$githubDir = Join-Path $ProjectRoot '.github'
|
|
324
|
+
if (Test-Path $githubDir) {
|
|
325
|
+
Write-Host "OK .github/ ada (Team Mode aktif)" -ForegroundColor Green
|
|
326
|
+
$ok++
|
|
327
|
+
} else {
|
|
328
|
+
Write-Host "INFO .github/ tidak ada (Team Mode skipped, atau project non-GitHub)" -ForegroundColor Cyan
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# 6. .git/ internal (should NOT exist for Pola B clone)
|
|
332
|
+
$internalGit = Join-Path $KitDir '.git'
|
|
333
|
+
if (Test-Path $internalGit) {
|
|
334
|
+
Write-Host "WARN .claude-kit\.git\ masih ada (sisa dari clone)" -ForegroundColor Yellow
|
|
335
|
+
Write-Host " Saran: Remove-Item '$internalGit' -Recurse -Force" -ForegroundColor Yellow
|
|
336
|
+
$warn++
|
|
337
|
+
} else {
|
|
338
|
+
Write-Host "OK .claude-kit/ tidak ada .git/ internal" -ForegroundColor Green
|
|
339
|
+
$ok++
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
# Ringkasan
|
|
343
|
+
Write-Host ""
|
|
344
|
+
Write-Host ("Result: OK={0} WARN={1} ERROR={2}" -f $ok, $warn, $err) -ForegroundColor Cyan
|
|
345
|
+
if ($err -eq 0 -and $warn -eq 0) {
|
|
346
|
+
Write-Host "Kit sehat. Siap dipakai." -ForegroundColor Green
|
|
347
|
+
return 0
|
|
348
|
+
} elseif ($err -eq 0) {
|
|
349
|
+
Write-Host "Kit OK dengan warning. Bisa dipakai, tapi fix warning kalau bisa." -ForegroundColor Yellow
|
|
350
|
+
return 0
|
|
351
|
+
} else {
|
|
352
|
+
Write-Host "Kit BERMASALAH. Fix ERROR di atas dulu." -ForegroundColor Red
|
|
353
|
+
return 1
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
# ---- Helper: scan project ----
|
|
358
|
+
function Invoke-Scan {
|
|
359
|
+
Write-Host ""
|
|
360
|
+
Write-Host "=== Kit Scan — Universal Adaptive Scan (kit v$(Get-KitVersion)) ===" -ForegroundColor Cyan
|
|
361
|
+
Write-Host ""
|
|
362
|
+
Write-Host "Catatan: kit.ps1 scan = scan ringan untuk count kandidat." -ForegroundColor Yellow
|
|
363
|
+
Write-Host " Untuk bulk-bootstrap actual, paste JALANKAN_KIT.md ke Claude Code (yang trigger AI workflow)." -ForegroundColor Yellow
|
|
364
|
+
Write-Host ""
|
|
365
|
+
|
|
366
|
+
# Patterns CRITICAL (universal, sesuai JALANKAN_KIT)
|
|
367
|
+
$patterns = @{
|
|
368
|
+
'Auth' = @('auth*', 'session*', 'login*', 'oauth*', 'jwt*', 'passport*')
|
|
369
|
+
'DB' = @('db.*', 'prisma*', 'drizzle*', 'repository*', 'schema*', 'models')
|
|
370
|
+
'Security' = @('crypto*', 'encrypt*', 'decrypt*', 'permissions*', 'acl*', 'policies*', '*-guard*', 'rate-limit*', 'csrf*', 'cors*')
|
|
371
|
+
'API/Router' = @('routes', 'controllers', 'handlers', 'api', 'endpoints', 'resolvers', 'actions')
|
|
372
|
+
'Entry/MW' = @('main.*', 'index.*', 'app.*', 'server.*', 'layout.*', 'middleware.*', 'interceptor.*')
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
$sourceFolders = @('src', 'app', 'lib', 'internal', 'pkg', 'cmd', 'features', 'modules', 'routes', 'controllers', 'handlers', 'services', 'domain')
|
|
376
|
+
$skipFolders = @('node_modules', 'dist', '.next', 'target', 'build', 'vendor', '__pycache__', '.venv', 'generated', '.git')
|
|
377
|
+
|
|
378
|
+
$totalByCategory = @{}
|
|
379
|
+
foreach ($cat in $patterns.Keys) { $totalByCategory[$cat] = 0 }
|
|
380
|
+
|
|
381
|
+
foreach ($src in $sourceFolders) {
|
|
382
|
+
$srcPath = Join-Path $ProjectRoot $src
|
|
383
|
+
if (-not (Test-Path $srcPath)) { continue }
|
|
384
|
+
|
|
385
|
+
$allFiles = Get-ChildItem -Path $srcPath -Recurse -File -ErrorAction SilentlyContinue |
|
|
386
|
+
Where-Object {
|
|
387
|
+
$skipped = $false
|
|
388
|
+
foreach ($skip in $skipFolders) {
|
|
389
|
+
if ($_.FullName -match "[\\/]$skip[\\/]") { $skipped = $true; break }
|
|
390
|
+
}
|
|
391
|
+
-not $skipped
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
foreach ($cat in $patterns.Keys) {
|
|
395
|
+
foreach ($p in $patterns[$cat]) {
|
|
396
|
+
$matched = $allFiles | Where-Object { $_.Name -like $p -or $_.FullName -match $p }
|
|
397
|
+
$totalByCategory[$cat] += $matched.Count
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
Write-Host "Kandidat CRITICAL per kategori (rough estimate):" -ForegroundColor Cyan
|
|
403
|
+
$grandTotal = 0
|
|
404
|
+
foreach ($cat in $patterns.Keys) {
|
|
405
|
+
$count = $totalByCategory[$cat]
|
|
406
|
+
Write-Host (" {0,-15} {1,4} file" -f $cat, $count)
|
|
407
|
+
$grandTotal += $count
|
|
408
|
+
}
|
|
409
|
+
Write-Host ""
|
|
410
|
+
Write-Host ("Total kandidat (rough, may include duplicates): {0}" -f $grandTotal) -ForegroundColor Yellow
|
|
411
|
+
Write-Host ""
|
|
412
|
+
|
|
413
|
+
if ($grandTotal -ge 30) {
|
|
414
|
+
Write-Host "Saran: total >= 30, pakai subfolder grouping saat bulk-bootstrap." -ForegroundColor Cyan
|
|
415
|
+
Write-Host " Mapping: docs/api/, docs/lib/, docs/security/, docs/features/<n>/, dst." -ForegroundColor Cyan
|
|
416
|
+
} else {
|
|
417
|
+
Write-Host "Saran: total < 30, flat docs/ masih oke." -ForegroundColor Cyan
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
Write-Host ""
|
|
421
|
+
Write-Host "Next step:" -ForegroundColor Cyan
|
|
422
|
+
Write-Host " 1. Paste isi .claude-kit\JALANKAN_KIT.md ke Claude Code untuk full workflow."
|
|
423
|
+
Write-Host " 2. AI akan tanya pilih (1)/(2)/(3)/(4) di step 12 (default (3) Skeleton-first)."
|
|
424
|
+
Write-Host ""
|
|
425
|
+
|
|
426
|
+
return 0
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
# ---- Helper: print version ----
|
|
430
|
+
# Uses Get-KitVersion fallback chain (manifest -> $script:KIT_VERSION -> CHANGELOG -> "unknown").
|
|
431
|
+
function Show-Version {
|
|
432
|
+
Write-Host (Get-KitVersion)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
# ---- Router ----
|
|
436
|
+
switch ($Command) {
|
|
437
|
+
'setup' {
|
|
438
|
+
$script = Join-Path $KitDir 'setup-pola-b.ps1'
|
|
439
|
+
if (-not (Test-Path $script)) {
|
|
440
|
+
Write-Host "ERROR: setup-pola-b.ps1 tidak ada di $KitDir" -ForegroundColor Red
|
|
441
|
+
exit 1
|
|
442
|
+
}
|
|
443
|
+
& $script @ExtraArgs
|
|
444
|
+
exit $LASTEXITCODE
|
|
445
|
+
}
|
|
446
|
+
'update' {
|
|
447
|
+
$script = Join-Path $KitDir 'update-kit.ps1'
|
|
448
|
+
if (-not (Test-Path $script)) {
|
|
449
|
+
Write-Host "ERROR: update-kit.ps1 tidak ada di $KitDir" -ForegroundColor Red
|
|
450
|
+
exit 1
|
|
451
|
+
}
|
|
452
|
+
& $script @ExtraArgs
|
|
453
|
+
exit $LASTEXITCODE
|
|
454
|
+
}
|
|
455
|
+
'uninstall' {
|
|
456
|
+
$script = Join-Path $KitDir 'uninstall.ps1'
|
|
457
|
+
if (-not (Test-Path $script)) {
|
|
458
|
+
Write-Host "ERROR: uninstall.ps1 tidak ada di $KitDir" -ForegroundColor Red
|
|
459
|
+
Write-Host " Kit ini di-install pakai versi <v1.0.0 (sebelum uninstall.ps1 ada)." -ForegroundColor Red
|
|
460
|
+
Write-Host " Saran: update kit dulu (.\.claude-kit\kit.ps1 update), atau hapus manual." -ForegroundColor Red
|
|
461
|
+
exit 1
|
|
462
|
+
}
|
|
463
|
+
& $script @ExtraArgs
|
|
464
|
+
exit $LASTEXITCODE
|
|
465
|
+
}
|
|
466
|
+
'doctor' {
|
|
467
|
+
$code = Invoke-Doctor
|
|
468
|
+
exit $code
|
|
469
|
+
}
|
|
470
|
+
'scan' {
|
|
471
|
+
$code = Invoke-Scan
|
|
472
|
+
exit $code
|
|
473
|
+
}
|
|
474
|
+
'version' {
|
|
475
|
+
Show-Version
|
|
476
|
+
exit 0
|
|
477
|
+
}
|
|
478
|
+
'rollback' {
|
|
479
|
+
if (Test-Path $libRollback) {
|
|
480
|
+
try {
|
|
481
|
+
. $libRollback
|
|
482
|
+
$result = Invoke-Rollback -ProjectRoot $ProjectRoot
|
|
483
|
+
# Result hash bisa di-suppress kalau dipanggil di pipe; di sini kita
|
|
484
|
+
# tidak butuh nilainya — biarin function-nya yang print status.
|
|
485
|
+
$null = $result
|
|
486
|
+
$global:LASTEXITCODE = 0
|
|
487
|
+
exit 0
|
|
488
|
+
} catch {
|
|
489
|
+
Write-Host ("[ERROR] Rollback gagal: {0}" -f $_) -ForegroundColor Red
|
|
490
|
+
$global:LASTEXITCODE = 2
|
|
491
|
+
exit 2
|
|
492
|
+
}
|
|
493
|
+
} else {
|
|
494
|
+
Write-Host "ERROR: lib\rollback.ps1 tidak ada di $KitDir" -ForegroundColor Red
|
|
495
|
+
Write-Host " Kit ini di-install pakai versi <v1.x (sebelum rollback ada)." -ForegroundColor Red
|
|
496
|
+
Write-Host " Saran: update kit dulu (.\.claude-kit\kit.ps1 update)." -ForegroundColor Red
|
|
497
|
+
exit 1
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
'help' {
|
|
501
|
+
Show-Help
|
|
502
|
+
exit 0
|
|
503
|
+
}
|
|
504
|
+
default {
|
|
505
|
+
Show-Help
|
|
506
|
+
if ($Command -eq '') { exit 0 } else { exit 1 }
|
|
507
|
+
}
|
|
508
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
lib/agents-md.ps1 - Library helper untuk deploy AGENTS.md dari template kit.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Extracted dari setup-pola-b.ps1 (v1.9) supaya logic fill AGENTS.md.template
|
|
7
|
+
reusable dari kit.ps1, update-kit.ps1, atau script lain yang butuh refresh
|
|
8
|
+
AGENTS.md tanpa run ulang full setup.
|
|
9
|
+
|
|
10
|
+
Tanggung jawab:
|
|
11
|
+
1. Detect AGENTS.md existing di project root (preserve customization)
|
|
12
|
+
2. Backup pre-overwrite ke AGENTS.md.backup-<timestamp>
|
|
13
|
+
3. Fill template dengan placeholder set yang dikasih caller
|
|
14
|
+
4. Tulis UTF-8 tanpa BOM (PS 5.1 default = with BOM, beberapa tool sensitif)
|
|
15
|
+
5. Pakai .Replace() literal (bukan -replace regex) supaya input user dengan
|
|
16
|
+
karakter $0/$1/$ tidak corrupt output (defense vs regex injection)
|
|
17
|
+
|
|
18
|
+
TIDAK tangani:
|
|
19
|
+
- Manifest tracking (caller wajib panggil Add-ToManifest sendiri kalau perlu)
|
|
20
|
+
- Template file resolution (caller kasih -TemplatePath absolute)
|
|
21
|
+
- Project root validation (caller wajib pastikan path valid)
|
|
22
|
+
|
|
23
|
+
.NOTES
|
|
24
|
+
Versi : 1.0
|
|
25
|
+
Tanggal: 2026-06-06
|
|
26
|
+
Target : PowerShell 5.1+ (Windows Server 2025 / Win10 / Win11)
|
|
27
|
+
Encoding: UTF-8 tanpa BOM (System.Text.UTF8Encoding $false)
|
|
28
|
+
#>
|
|
29
|
+
|
|
30
|
+
# Module-level guard supaya re-dot-source tidak duplikasi function definition
|
|
31
|
+
if ($script:__lintasAI_AgentsMdLoaded) { return }
|
|
32
|
+
$script:__lintasAI_AgentsMdLoaded = $true
|
|
33
|
+
|
|
34
|
+
function Deploy-AgentsMd {
|
|
35
|
+
<#
|
|
36
|
+
.SYNOPSIS
|
|
37
|
+
Deploy AGENTS.md ke project root: fill template + backup existing.
|
|
38
|
+
|
|
39
|
+
.DESCRIPTION
|
|
40
|
+
Tiga mode behavior berdasarkan state existing AGENTS.md + flag -Preserve:
|
|
41
|
+
- Tidak ada existing -> CREATE (write template-filled fresh)
|
|
42
|
+
- Existing + -Preserve -> PRESERVE (skip, jangan timpa, return action=preserved)
|
|
43
|
+
- Existing + tanpa flag -> BACKUP + UPDATE (rename existing ke .backup-<timestamp>, tulis fresh)
|
|
44
|
+
|
|
45
|
+
Placeholders adalah hashtable @{ '<KEY>' = 'value' } - caller bebas tentukan
|
|
46
|
+
key set. Template biasanya pakai <NAMA_PROYEK>, <TANGGAL_HARI_INI>, <NAMA_KAMU>,
|
|
47
|
+
<URL_REPO_STANDAR>, <VERSI_KIT>, tapi function ini agnostic - apa pun yang
|
|
48
|
+
ada di hashtable di-replace pakai .Replace() literal.
|
|
49
|
+
|
|
50
|
+
.PARAMETER ProjectRoot
|
|
51
|
+
Absolute path ke root proyek (parent dari .claude-kit/). AGENTS.md akan
|
|
52
|
+
ditulis ke <ProjectRoot>\AGENTS.md.
|
|
53
|
+
|
|
54
|
+
.PARAMETER TemplatePath
|
|
55
|
+
Absolute path ke AGENTS.md.template (biasanya <KitDir>\AGENTS.md.template).
|
|
56
|
+
|
|
57
|
+
.PARAMETER Placeholders
|
|
58
|
+
Hashtable berisi pair @{ '<PLACEHOLDER>' = 'replacement_value' }.
|
|
59
|
+
Key biasanya wrap dengan angle bracket (e.g. '<NAMA_PROYEK>') sesuai konvensi
|
|
60
|
+
template, tapi function tidak enforce - replace literal apa adanya.
|
|
61
|
+
|
|
62
|
+
.PARAMETER Preserve
|
|
63
|
+
Kalau aktif DAN AGENTS.md existing ada -> SKIP overwrite, return action='preserved'.
|
|
64
|
+
Anti-destruktif default: protect user customization.
|
|
65
|
+
|
|
66
|
+
Tanpa flag ini, behavior default = backup + overwrite (untuk -Force scenario).
|
|
67
|
+
|
|
68
|
+
.OUTPUTS
|
|
69
|
+
PSCustomObject:
|
|
70
|
+
action : 'created' | 'updated' | 'preserved'
|
|
71
|
+
backup_path : string (path file backup) atau $null kalau tidak ada backup
|
|
72
|
+
target_path : string (path AGENTS.md final di project root)
|
|
73
|
+
placeholders_applied : int (jumlah key di hashtable yang di-replace)
|
|
74
|
+
|
|
75
|
+
.EXAMPLE
|
|
76
|
+
# Setup awal, project belum punya AGENTS.md
|
|
77
|
+
$result = Deploy-AgentsMd `
|
|
78
|
+
-ProjectRoot 'C:\proyek\akses' `
|
|
79
|
+
-TemplatePath 'C:\proyek\akses\.claude-kit\AGENTS.md.template' `
|
|
80
|
+
-Placeholders @{
|
|
81
|
+
'<NAMA_PROYEK>' = 'akses'
|
|
82
|
+
'<TANGGAL_HARI_INI>' = '2026-06-06'
|
|
83
|
+
'<NAMA_KAMU>' = 'dokterbrutal'
|
|
84
|
+
'<URL_REPO_STANDAR>' = 'https://github.com/foo/akses'
|
|
85
|
+
'<VERSI_KIT>' = 'v1.5.0'
|
|
86
|
+
}
|
|
87
|
+
# $result.action = 'created', $result.backup_path = $null
|
|
88
|
+
|
|
89
|
+
.EXAMPLE
|
|
90
|
+
# Re-run setup, AGENTS.md existing - mau preserve customization user
|
|
91
|
+
$result = Deploy-AgentsMd `
|
|
92
|
+
-ProjectRoot 'C:\proyek\akses' `
|
|
93
|
+
-TemplatePath 'C:\proyek\akses\.claude-kit\AGENTS.md.template' `
|
|
94
|
+
-Placeholders @{ '<NAMA_PROYEK>' = 'akses' } `
|
|
95
|
+
-Preserve
|
|
96
|
+
# $result.action = 'preserved', $result.backup_path = $null
|
|
97
|
+
#>
|
|
98
|
+
[CmdletBinding()]
|
|
99
|
+
param(
|
|
100
|
+
[Parameter(Mandatory = $true)]
|
|
101
|
+
[string]$ProjectRoot,
|
|
102
|
+
|
|
103
|
+
[Parameter(Mandatory = $true)]
|
|
104
|
+
[string]$TemplatePath,
|
|
105
|
+
|
|
106
|
+
[Parameter(Mandatory = $true)]
|
|
107
|
+
[hashtable]$Placeholders,
|
|
108
|
+
|
|
109
|
+
[switch]$Preserve
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# ---- Validasi input ----
|
|
113
|
+
if (-not (Test-Path -LiteralPath $ProjectRoot -PathType Container)) {
|
|
114
|
+
throw "Deploy-AgentsMd: ProjectRoot tidak ditemukan atau bukan folder: $ProjectRoot"
|
|
115
|
+
}
|
|
116
|
+
if (-not (Test-Path -LiteralPath $TemplatePath -PathType Leaf)) {
|
|
117
|
+
throw "Deploy-AgentsMd: TemplatePath tidak ditemukan: $TemplatePath"
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
$target = Join-Path $ProjectRoot 'AGENTS.md'
|
|
121
|
+
$existing = Test-Path -LiteralPath $target -PathType Leaf
|
|
122
|
+
|
|
123
|
+
# ---- Mode PRESERVE: existing + flag aktif -> skip, return early ----
|
|
124
|
+
if ($existing -and $Preserve) {
|
|
125
|
+
return [PSCustomObject]@{
|
|
126
|
+
action = 'preserved'
|
|
127
|
+
backup_path = $null
|
|
128
|
+
target_path = $target
|
|
129
|
+
placeholders_applied = 0
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# ---- Mode BACKUP: existing tanpa Preserve -> rename ke backup ----
|
|
134
|
+
$backupPath = $null
|
|
135
|
+
if ($existing) {
|
|
136
|
+
$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
|
137
|
+
$backupPath = "$target.backup-$timestamp"
|
|
138
|
+
try {
|
|
139
|
+
Copy-Item -LiteralPath $target -Destination $backupPath -Force -ErrorAction Stop
|
|
140
|
+
} catch {
|
|
141
|
+
throw "Deploy-AgentsMd: Gagal backup AGENTS.md existing ke '$backupPath': $_"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
# ---- Fill template + tulis ----
|
|
146
|
+
try {
|
|
147
|
+
$content = Get-Content -LiteralPath $TemplatePath -Raw -Encoding UTF8
|
|
148
|
+
} catch {
|
|
149
|
+
throw "Deploy-AgentsMd: Gagal baca template '$TemplatePath': $_"
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Pakai .Replace() literal (bukan -replace regex) - input user dengan $0/$1/$
|
|
153
|
+
# tidak akan corrupt output. Defensive default vs regex injection.
|
|
154
|
+
$applied = 0
|
|
155
|
+
foreach ($key in $Placeholders.Keys) {
|
|
156
|
+
$val = [string]$Placeholders[$key]
|
|
157
|
+
$content = $content.Replace([string]$key, $val)
|
|
158
|
+
$applied++
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
# Tulis UTF-8 tanpa BOM (PS 5.1 default UTF8 = with BOM, beberapa tool sensitif)
|
|
162
|
+
try {
|
|
163
|
+
[System.IO.File]::WriteAllText($target, $content, (New-Object System.Text.UTF8Encoding $false))
|
|
164
|
+
} catch {
|
|
165
|
+
throw "Deploy-AgentsMd: Gagal tulis AGENTS.md ke '$target': $_"
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return [PSCustomObject]@{
|
|
169
|
+
action = $(if ($existing) { 'updated' } else { 'created' })
|
|
170
|
+
backup_path = $backupPath
|
|
171
|
+
target_path = $target
|
|
172
|
+
placeholders_applied = $applied
|
|
173
|
+
}
|
|
174
|
+
}
|