@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.
Files changed (86) hide show
  1. package/.github/workflows/publish-npm.yml +40 -0
  2. package/.github/workflows/validate.yml +93 -0
  3. package/AUDIT_POST_SETUP_PROMPT_v1.md +280 -0
  4. package/BOOTSTRAP_PROJECT_DOCS_PROMPT_v1.md +3 -0
  5. package/CHANGELOG.md +313 -0
  6. package/CLAUDE_universal_v1.md +1021 -0
  7. package/CONTRIBUTING.md +101 -0
  8. package/FIRST_SESSION_PROMPT_v1.md +7 -0
  9. package/JALANKAN_KIT.md +188 -0
  10. package/LICENSE +21 -0
  11. package/MULAI_DI_SINI.md +145 -0
  12. package/PROJECT_KICKOFF_PROMPT_v1.md +3 -0
  13. package/PROJECT_LIFECYCLE_PROMPT_v1.md +536 -0
  14. package/PROJECT_MIGRATION_PROMPT_v1.md +3 -0
  15. package/README.md +505 -0
  16. package/SETUP_POLA_B_PROMPT_v1.md +5 -0
  17. package/SPLIT_REPO_MIGRATION_PROMPT_v1.md +485 -0
  18. package/TEAM_ROLLOUT_GUIDE_v1.md +172 -0
  19. package/UPDATE_DOCS_PROMPT_v1.md +3 -0
  20. package/UPDATE_KIT_PROMPT_v1.md +213 -0
  21. package/bin/lintasai.js +81 -0
  22. package/docs/SIGNED_RELEASE.md +162 -0
  23. package/install-windows.ps1 +225 -0
  24. package/kit.ps1 +508 -0
  25. package/lib/agents-md.ps1 +174 -0
  26. package/lib/git-helpers.ps1 +104 -0
  27. package/lib/kit-files.psd1 +133 -0
  28. package/lib/manifest-signing.ps1 +65 -0
  29. package/lib/manifest.ps1 +267 -0
  30. package/lib/rollback.ps1 +241 -0
  31. package/lib/safety.ps1 +193 -0
  32. package/lib/template-deploy.ps1 +242 -0
  33. package/lib/version-detect.ps1 +161 -0
  34. package/package.json +36 -0
  35. package/setup-pola-b.ps1 +687 -0
  36. package/templates/ANALOGI_LIBRARY.md +7 -0
  37. package/templates/CLAUDE_TEAM_GUIDE.md +505 -0
  38. package/templates/CROSS_REPO_TYPES_PIPELINE.md +473 -0
  39. package/templates/DB_SCHEMA_SCAN_PROMPT.md +194 -0
  40. package/templates/DISCORD_BOT_INTEGRATION.md +187 -0
  41. package/templates/GLOSSARY_NON_PROGRAMMER.md +361 -0
  42. package/templates/INDEX.md +157 -0
  43. package/templates/MCP_SETUP.md +1145 -0
  44. package/templates/MIGRATE_TO_SUBFOLDER_PROMPT_v1.md +220 -0
  45. package/templates/ONBOARDING.md +172 -0
  46. package/templates/PROJECT_STARTER_TEMPLATES.md +264 -0
  47. package/templates/PROMPT_LIBRARY.md +790 -0
  48. package/templates/RLS_SETUP_PROMPT.md +167 -0
  49. package/templates/SECURITY_INCIDENT_PLAYBOOK.md +191 -0
  50. package/templates/SPLIT_REPO_AGENTS_TEMPLATES.md +32 -0
  51. package/templates/SPLIT_REPO_NON_PROGRAMMER_PROMPTS.md +604 -0
  52. package/templates/SPLIT_REPO_TOOLS_SETUP.md +388 -0
  53. package/templates/STACK_DETECTION_PATTERN.md +261 -0
  54. package/templates/STACK_GUIDE.md +564 -0
  55. package/templates/STACK_MIGRATION_GUIDE.md +154 -0
  56. package/templates/STACK_VERSIONS.md +31 -0
  57. package/templates/UPDATE_GUIDE.md +246 -0
  58. package/templates/_EXAMPLE.md +110 -0
  59. package/templates/_PATTERNS.md +173 -0
  60. package/templates/architecture.md +180 -0
  61. package/templates/architecture_auto.md +61 -0
  62. package/templates/decisions/README.md +108 -0
  63. package/templates/decisions/_TEMPLATE.md +84 -0
  64. package/templates/feature-flags-advanced.md +171 -0
  65. package/templates/github/CODEOWNERS.template +61 -0
  66. package/templates/github/GENERATE_TYPES_SCRIPT.md +77 -0
  67. package/templates/github/PUBLISH_SHARED_WORKFLOW.yml +52 -0
  68. package/templates/github/RECEIVE_BACKEND_UPDATE.yml +106 -0
  69. package/templates/github/RENOVATE_FRONTEND.json +28 -0
  70. package/templates/github/TRIGGER_FRONTEND_UPDATE.yml +29 -0
  71. package/templates/github/pull_request_template.md +44 -0
  72. package/templates/github/scripts/ai-review.js +153 -0
  73. package/templates/github/workflows/ai-review.yml +61 -0
  74. package/templates/github/workflows/backup-schemas.yml +169 -0
  75. package/templates/glossary.md +110 -0
  76. package/templates/split-agents/BACKEND.md +149 -0
  77. package/templates/split-agents/FRONTEND.md +141 -0
  78. package/templates/split-agents/SHARED.md +82 -0
  79. package/templates/split-agents/TOOLS.md +77 -0
  80. package/tests/Run-Tests.ps1 +19 -0
  81. package/tests/lib-safety.Tests.ps1 +66 -0
  82. package/tests/rollback.Tests.ps1 +66 -0
  83. package/tests/uninstall.Tests.ps1 +265 -0
  84. package/tests/update-kit.Tests.ps1 +78 -0
  85. package/uninstall.ps1 +794 -0
  86. package/update-kit.ps1 +907 -0
@@ -0,0 +1,141 @@
1
+ # TEMPLATE: `<project>-frontend/AGENTS.md`
2
+
3
+ > Template ini di-deploy oleh AI saat split repo migration.
4
+ > Customization: replace `<project>` dengan nama project user.
5
+
6
+ ```markdown
7
+ # AGENTS.md - <project>-frontend
8
+
9
+ > Repo ini: Frontend UI only (Next.js SPA mode atau React Vite).
10
+ > Audience AI: Claude Code untuk Frontend Staff (4 orang yang akses repo `<project>-frontend` + `<project>-shared`).
11
+ >
12
+ > Privileges Frontend Staff:
13
+ > - DAPAT edit data (CRUD: SELECT / INSERT / UPDATE / DELETE) di DB lewat API backend
14
+ > - TIDAK punya DDL (CREATE TABLE / ALTER / DROP) — DDL = domain Backend staff + owner
15
+
16
+ ## Scope Kamu (AI)
17
+
18
+ Kamu di repo `<project>-frontend`. Kamu BOLEH:
19
+ - Edit UI components, pages, layouts (di `src/app/`, `src/components/`)
20
+ - Call API backend via fetch/axios (CRUD data lewat endpoint yang sudah ada)
21
+ - Import types dari `@<project>/shared` (auto-installed npm dep)
22
+ - Styling (Tailwind, CSS modules)
23
+ - Update copy/teks UI sesuai brief dari staff content
24
+
25
+ Kamu TIDAK BOLEH:
26
+ - Bikin endpoint API (itu di `<project>-backend`, kamu tidak akses)
27
+ - Direct database query (Prisma) — termasuk dilarang DDL apapun
28
+ - Modify `@<project>/shared` (cuma owner / Backend staff yang publish version baru)
29
+ - Implement business logic (validation, calculation, workflow rules)
30
+ - Akses `.env` yang berisi `DATABASE_URL` atau API SECRET
31
+ - Hardcode URL backend; selalu pakai env var
32
+
33
+ ## Stack
34
+ - Next.js (lihat STACK_VERSIONS.md untuk versi terbaru) atau React Vite (frontend only)
35
+ - TailwindCSS + Shadcn/ui untuk component primitives
36
+ - TypeScript strict mode
37
+ - React Query / SWR untuk data fetching
38
+ - React Hook Form + Zod (validasi client-side, schema dari `@<project>/shared`)
39
+
40
+ ## Workflow
41
+
42
+ Saat staff non-programmer prompt kamu:
43
+
44
+ 1. Baca prompt, identify TASK ID (e.g., `TASK-101`)
45
+ 2. Kalau prompt vague, tanya 1-2 clarifying question (max 2!)
46
+ 3. Cek `@<project>/shared` types yang relevan untuk endpoint yang dipakai
47
+ 4. Bikin/edit komponen + page
48
+ 5. Call API via fetch ke backend URL (`NEXT_PUBLIC_API_URL` env var)
49
+ 6. Test di localhost (`npm run dev`)
50
+ 7. Open PR ke `main`, kasih link preview Vercel ke staff
51
+ 8. Tunggu approval owner sebelum merge
52
+
53
+ ## API URL
54
+
55
+ ```ts
56
+ const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001'
57
+ ```
58
+
59
+ - Local dev: `http://localhost:3001` (backend dev server)
60
+ - Staging: `https://api-staging.<project>.id`
61
+ - Production: `https://api.<project>.id`
62
+
63
+ ## Style Guide
64
+
65
+ - Format: Prettier auto, no manual semicolon decisions
66
+ - Naming: `camelCase` untuk function, `PascalCase` untuk Component
67
+ - File: `kebab-case` untuk page route, `PascalCase` untuk component file
68
+ - Komentar: minimal, hanya kalau non-obvious (kenapa, bukan apa)
69
+ - Komponen: prefer functional component + hooks, hindari class component
70
+ - State: lokal pakai `useState`, server state pakai React Query
71
+
72
+ ## Tools
73
+
74
+ - Storybook: `npm run storybook` (UI dev tanpa app context)
75
+ - Vitest: `npm test` (unit test komponen)
76
+ - Playwright: `npm run e2e` (end-to-end di staging)
77
+ - Vercel preview: per PR auto-deploy, link auto-comment di PR
78
+
79
+ ## Project-Specific Rules
80
+
81
+ <!-- Owner: tambahkan rules spesifik project di sini -->
82
+ <!-- Contoh: design tokens, brand color, accessibility level, dst. -->
83
+
84
+ ## Session Start Auto-Check (Anti-Stale Context)
85
+
86
+ Saat AI Claude Code session pertama tiap hari di repo ini, AI WAJIB execute pre-flight check:
87
+
88
+ ### Check 1: @<project>/shared version
89
+
90
+ ```bash
91
+ # Compare local version vs registry latest
92
+ LOCAL=$(node -p "require('./node_modules/@<project>/shared/package.json').version")
93
+ REMOTE=$(npm view @<project>/shared version --registry=https://npm.pkg.github.com 2>/dev/null || echo "?")
94
+
95
+ if [ "$LOCAL" != "$REMOTE" ]; then
96
+ echo "⚠️ @<project>/shared outdated: local $LOCAL vs remote $REMOTE"
97
+ echo "Run: npm install @<project>/shared@latest"
98
+ fi
99
+ ```
100
+
101
+ Action kalau outdated:
102
+ - Suggest npm install ke staff
103
+ - Atau auto-run kalau staff approve
104
+
105
+ ### Check 2: Backend API spec (Swagger)
106
+
107
+ ```bash
108
+ curl -s --max-time 5 https://api-staging.<project>.id/docs/openapi.json > /tmp/api-spec.json 2>/dev/null
109
+ if [ $? -eq 0 ]; then
110
+ ENDPOINTS=$(jq -r '.paths | keys | length' /tmp/api-spec.json 2>/dev/null)
111
+ echo "✅ Swagger spec cached: $ENDPOINTS endpoints available"
112
+ else
113
+ echo "⚠️ Swagger spec unreachable. Frontend AI tidak punya context API terbaru."
114
+ fi
115
+ ```
116
+
117
+ Use spec untuk:
118
+ - Validate API endpoint sebelum suggest fetch
119
+ - Check request/response shape
120
+ - Detect endpoint baru yang belum dipakai
121
+ - Refuse suggest endpoint yang tidak exist (forward escalation ke backend tim)
122
+
123
+ ### Check 3: Discord context (optional)
124
+
125
+ Kalau ada Discord MCP atau webhook reader configured:
126
+ - Scan #akses-deps channel untuk last 24h
127
+ - Look for backend publish notifications
128
+ - Highlight relevant updates ke staff
129
+
130
+ ### Logging
131
+
132
+ Output check result di awal response sesi:
133
+
134
+ ```
135
+ [Session pre-flight]
136
+ - @<project>/shared: v1.2.0 ✅ (latest)
137
+ - Swagger API: 47 endpoints cached ✅
138
+ - Last backend publish: 2 hours ago (via Discord)
139
+ Ready untuk task.
140
+ ```
141
+ ```
@@ -0,0 +1,82 @@
1
+ # TEMPLATE: `<project>-shared/AGENTS.md`
2
+
3
+ > Template ini di-deploy oleh AI saat split repo migration.
4
+ > Customization: replace `<project>` dengan nama project user.
5
+
6
+ ```markdown
7
+ # AGENTS.md - <project>-shared
8
+
9
+ > Repo ini: TypeScript types + Zod schemas saja. No logic.
10
+ > Audience AI: Owner + backend delegate (saat publish version baru).
11
+ > Staff frontend = READ-ONLY (mereka cuma install sebagai npm dep).
12
+
13
+ ## Scope Kamu (AI)
14
+
15
+ Kamu di repo `<project>-shared`. Kamu BOLEH:
16
+ - Tambah/update TypeScript type definitions
17
+ - Tambah/update Zod validation schemas
18
+ - Update version di `package.json` saat ada change
19
+ - Update `README.md` dengan example usage
20
+
21
+ Kamu TIDAK BOLEH:
22
+ - Implement actual logic (cuma type signatures + schemas)
23
+ - Import package eksternal selain Zod + TypeScript native
24
+ - Side effect (no fetch, no DB, no file I/O, no env access)
25
+ - Bikin React component (itu domain frontend)
26
+ - Bikin Prisma model (itu domain backend)
27
+
28
+ ## Stack
29
+ - TypeScript strict mode
30
+ - Zod untuk runtime validation
31
+ - `tsup` atau `rollup` untuk build (output dual ESM + CJS)
32
+ - npm publish atau GitHub Packages (private)
33
+
34
+ ## Workflow Update
35
+
36
+ Saat update:
37
+
38
+ 1. Edit `src/types/` atau `src/schemas/`
39
+ 2. Run `npm run build` -> `dist/` ter-generate
40
+ 3. Run `npm test` -> pastikan schema parse contoh data dengan benar
41
+ 4. Bump version di `package.json` (semantic versioning, lihat rules bawah)
42
+ 5. Tag git: `git tag v1.X.Y`
43
+ 6. Push tag -> GitHub Actions auto-publish ke npm/GitHub Packages
44
+ 7. Notify backend + frontend tim: `@<project>/shared v1.X.Y published`
45
+ 8. Backend + frontend update dep version di `package.json` mereka
46
+
47
+ ## Versioning Rules (Semver)
48
+
49
+ - `patch` (1.0.X): bug fix di type definition, no breaking, no rename
50
+ - `minor` (1.X.0): tambah type baru, backward compatible (existing field tetap)
51
+ - `major` (X.0.0): breaking change (rename field, hapus type, ubah enum)
52
+ - HARUS coordinate dengan tim sebelum publish
53
+ - HARUS ada migration note di `CHANGELOG.md`
54
+
55
+ ## Output Structure
56
+
57
+ ```
58
+ dist/
59
+ index.js <- compiled JS (CJS)
60
+ index.mjs <- compiled JS (ESM)
61
+ index.d.ts <- TypeScript definition (yang frontend AI baca)
62
+ schemas/
63
+ user.js
64
+ user.d.ts
65
+ types/
66
+ api.d.ts
67
+ ```
68
+
69
+ ## Folder Convention
70
+
71
+ ```
72
+ src/
73
+ index.ts <- barrel export semua type & schema
74
+ types/ <- pure TypeScript type (interface, type alias)
75
+ schemas/ <- Zod schema (runtime validatable)
76
+ utils/ <- type guard, helper type (no runtime logic)
77
+ ```
78
+
79
+ ## Project-Specific Rules
80
+
81
+ <!-- Owner: tambahkan rules spesifik project di sini -->
82
+ ```
@@ -0,0 +1,77 @@
1
+ > ⚠️ OPT-IN: File ini hanya dipakai kalau team >20 staff, compliance audit, ATAU kalau pakai 4-repo split (Backend Staff men-jalankan admin scripts dari repo terpisah). Untuk team kecil-sedang dengan 3-repo split default, admin scripts cukup di `<project>-backend/scripts/` (Backend Staff + owner access).
2
+ >
3
+ > Audience: owner + Backend Staff (2 orang) saat 4-repo split aktif. Frontend Staff TIDAK punya akses.
4
+
5
+ # TEMPLATE: `<project>-tools/AGENTS.md`
6
+
7
+ > Template ini di-deploy oleh AI saat split repo migration.
8
+ > Customization: replace `<project>` dengan nama project user.
9
+
10
+ ```markdown
11
+ # AGENTS.md - <project>-tools
12
+
13
+ > Repo ini: Admin scripts, sync tools, migration helpers.
14
+ > Audience AI: Owner ONLY.
15
+ > Staff TIDAK punya akses (repo private, owner-only access).
16
+
17
+ ## Scope Kamu (AI)
18
+
19
+ Kamu di repo `<project>-tools`. Kamu BOLEH:
20
+ - Bikin script CLI untuk admin tasks
21
+ - Bikin sync script (prod -> staging dengan PII redact)
22
+ - Bikin migration helpers (wrapper di atas `prisma migrate`)
23
+ - Bikin one-time scripts (backfill data, cleanup, dst.)
24
+ - Akses credential owner via `scripts/.env.owner`
25
+ - Akses Supabase MCP untuk owner-level ops
26
+
27
+ Kamu TIDAK BOLEH:
28
+ - Commit credential ke git (cek `.gitignore` sebelum stage)
29
+ - Jalankan script destructive ke prod tanpa konfirmasi owner eksplisit
30
+ - Print password / secret ke stdout / log file
31
+ - Share script ini ke repo lain (scope owner-only)
32
+
33
+ ## Stack
34
+ - Node.js (ESM, `"type": "module"` di `package.json`)
35
+ - `pg` untuk direct DB access (saat butuh raw SQL di luar Prisma)
36
+ - `@faker-js/faker` untuk generate demo data
37
+ - `dotenv-cli` untuk env management
38
+ - `commander` atau `yargs` untuk CLI argument parsing
39
+
40
+ ## Scripts Library
41
+
42
+ ```
43
+ scripts/
44
+ sync-prod-to-staging.mjs <- PII redacted sample sync
45
+ backup-prod.mjs <- Snapshot via pg_dump
46
+ migrate-deploy-prod.mjs <- Wrapped `prisma migrate deploy`
47
+ audit-staff-access.mjs <- Check Supabase user activity log
48
+ rotate-secrets.mjs <- Helper rotate API key + push ke Vercel
49
+ cleanup-orphan-records.mjs <- One-time cleanup utility
50
+ ```
51
+
52
+ ## Security
53
+
54
+ - Owner credential CUMA di laptop owner (jangan upload ke server shared)
55
+ - `scripts/.env.owner` WAJIB gitignored, JANGAN commit
56
+ - Output log: mask password di `DATABASE_URL` (regex replace sebelum print)
57
+ - Audit log: tiap script run, append ke `logs/owner-actions.log`
58
+ - Format: `<ISO timestamp> | <script name> | <args summary> | <exit code>`
59
+ - Saat clone repo di laptop baru, fetch `.env.owner` dari password manager (1Password / Bitwarden)
60
+
61
+ ## Workflow
62
+
63
+ Saat owner prompt untuk bikin script baru:
64
+
65
+ 1. Identifikasi: ini one-time atau reusable?
66
+ 2. Kalau one-time: simpan di `scripts/oneoffs/<date>-<desc>.mjs`
67
+ 3. Kalau reusable: simpan di `scripts/` dengan `--help` flag + `README.md` entry
68
+ 4. WAJIB confirm step sebelum eksekusi destructive op:
69
+ ```js
70
+ await confirm('Are you sure? This will delete X rows. [y/N]')
71
+ ```
72
+ 5. WAJIB dry-run mode (`--dry-run` flag) untuk script yang ubah data
73
+
74
+ ## Project-Specific Rules
75
+
76
+ <!-- Owner: tambahkan rules spesifik project di sini -->
77
+ ```
@@ -0,0 +1,19 @@
1
+ #Requires -Module Pester
2
+
3
+ $ErrorActionPreference = 'Stop'
4
+
5
+ # Install Pester kalau belum
6
+ if (-not (Get-Module -ListAvailable -Name Pester | Where-Object { $_.Version.Major -ge 5 })) {
7
+ Write-Host "Installing Pester 5.x..."
8
+ Install-Module -Name Pester -MinimumVersion 5.0 -Force -SkipPublisherCheck -Scope CurrentUser
9
+ }
10
+
11
+ Import-Module Pester -MinimumVersion 5.0
12
+
13
+ $testsDir = $PSScriptRoot
14
+ $config = New-PesterConfiguration
15
+ $config.Run.Path = $testsDir
16
+ $config.Output.Verbosity = 'Detailed'
17
+ $config.Run.Exit = $true
18
+
19
+ Invoke-Pester -Configuration $config
@@ -0,0 +1,66 @@
1
+ #Requires -Module Pester
2
+
3
+ BeforeAll {
4
+ $libPath = Join-Path $PSScriptRoot '..\lib\safety.ps1'
5
+ . $libPath
6
+ Initialize-SafeProjectPath -ProjectRoot 'C:\test-project'
7
+ }
8
+
9
+ Describe "Resolve-SafeProjectPath" {
10
+ It "Should resolve valid relative path" {
11
+ $result = Resolve-SafeProjectPath -RelativePath 'src/app.ts'
12
+ $result | Should -Not -BeNullOrEmpty
13
+ $result | Should -Match 'C:\\test-project'
14
+ }
15
+
16
+ It "Should REJECT absolute path outside project" {
17
+ { Resolve-SafeProjectPath -RelativePath 'C:\Windows\system32' } | Should -Throw
18
+ }
19
+
20
+ It "Should REJECT path traversal via ..\" {
21
+ { Resolve-SafeProjectPath -RelativePath '..\..\evil.txt' } | Should -Throw
22
+ }
23
+
24
+ It "Should REJECT prefix collision (proj vs proj-evil)" {
25
+ Initialize-SafeProjectPath -ProjectRoot 'C:\proj'
26
+ { Resolve-SafeProjectPath -RelativePath '..\proj-evil\file.txt' } | Should -Throw
27
+ }
28
+
29
+ It "Should handle nested valid path" {
30
+ # Re-init: test sebelumnya (prefix collision) ganti root ke C:\proj — reset balik.
31
+ Initialize-SafeProjectPath -ProjectRoot 'C:\test-project'
32
+ $result = Resolve-SafeProjectPath -RelativePath 'src/lib/utils/format.ts'
33
+ $result | Should -Match 'C:\\test-project\\src\\lib\\utils'
34
+ }
35
+ }
36
+
37
+ Describe "Test-PathHasReparsePoint" {
38
+ It "Returns false for regular file" {
39
+ $tempFile = Join-Path $env:TEMP "test-regular-$(Get-Random).txt"
40
+ Set-Content -Path $tempFile -Value "test" -Force
41
+ try {
42
+ Test-PathHasReparsePoint -FullPath $tempFile | Should -BeFalse
43
+ } finally {
44
+ Remove-Item -Force $tempFile -ErrorAction SilentlyContinue
45
+ }
46
+ }
47
+
48
+ It "Handles non-existent path gracefully" {
49
+ Test-PathHasReparsePoint -FullPath "C:\does-not-exist-$(Get-Random)" | Should -BeFalse
50
+ }
51
+ }
52
+
53
+ Describe "Get-FileSha256" {
54
+ It "Computes correct sha256" {
55
+ $tempFile = Join-Path $env:TEMP "test-sha-$(Get-Random).txt"
56
+ # Pakai WriteAllBytes raw supaya tidak ada BOM (PS 5.1 Set-Content -Encoding UTF8 nulis BOM).
57
+ # Expected hash "2cf24dba..." = SHA-256 dari ASCII "hello" persis (5 byte).
58
+ [System.IO.File]::WriteAllBytes($tempFile, [System.Text.Encoding]::ASCII.GetBytes("hello"))
59
+ try {
60
+ $hash = Get-FileSha256 -FilePath $tempFile
61
+ $hash | Should -Be "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
62
+ } finally {
63
+ Remove-Item -Force $tempFile -ErrorAction SilentlyContinue
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,66 @@
1
+ #Requires -Module Pester
2
+
3
+ BeforeAll {
4
+ $libPath = Join-Path $PSScriptRoot '..\lib\rollback.ps1'
5
+ . $libPath
6
+ }
7
+
8
+ Describe "rollback.ps1 graceful no-op" {
9
+ It "Invoke-Rollback function exists" {
10
+ Get-Command Invoke-Rollback -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
11
+ }
12
+
13
+ It "Get-RollbackPreview function exists" {
14
+ Get-Command Get-RollbackPreview -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
15
+ }
16
+
17
+ It "Returns no-manifest status when manifest tidak ada" {
18
+ $tempRoot = Join-Path $env:TEMP "rollback-test-nomanifest-$(Get-Random)"
19
+ New-Item -ItemType Directory -Path $tempRoot -Force | Out-Null
20
+ try {
21
+ $result = Invoke-Rollback -ProjectRoot $tempRoot -Force
22
+ $result.status | Should -Be 'no-manifest'
23
+ $result.restored | Should -Be 0
24
+ $result.skipped | Should -Be 0
25
+ } finally {
26
+ Remove-Item -Recurse -Force $tempRoot -ErrorAction SilentlyContinue
27
+ }
28
+ }
29
+
30
+ It "Should NOT throw saat no-manifest" {
31
+ $tempRoot = Join-Path $env:TEMP "rollback-test-nothrow-$(Get-Random)"
32
+ New-Item -ItemType Directory -Path $tempRoot -Force | Out-Null
33
+ try {
34
+ { Invoke-Rollback -ProjectRoot $tempRoot -Force } | Should -Not -Throw
35
+ } finally {
36
+ Remove-Item -Recurse -Force $tempRoot -ErrorAction SilentlyContinue
37
+ }
38
+ }
39
+ }
40
+
41
+ Describe "Find-ManifestPath multi-location probe" {
42
+ It "Returns null when no manifest" {
43
+ $tempRoot = Join-Path $env:TEMP "find-manifest-null-$(Get-Random)"
44
+ New-Item -ItemType Directory -Path $tempRoot -Force | Out-Null
45
+ try {
46
+ $result = Find-ManifestPath -ProjectRoot $tempRoot
47
+ $result | Should -BeNullOrEmpty
48
+ } finally {
49
+ Remove-Item -Recurse -Force $tempRoot -ErrorAction SilentlyContinue
50
+ }
51
+ }
52
+
53
+ It "Finds manifest at project root" {
54
+ $tempRoot = Join-Path $env:TEMP "find-manifest-root-$(Get-Random)"
55
+ New-Item -ItemType Directory -Path $tempRoot -Force | Out-Null
56
+ try {
57
+ $manifestPath = Join-Path $tempRoot '.install-manifest.json'
58
+ Set-Content -Path $manifestPath -Value '{"files":[]}' -Encoding UTF8 -Force
59
+ $result = Find-ManifestPath -ProjectRoot $tempRoot
60
+ $result | Should -Not -BeNullOrEmpty
61
+ $result | Should -Be $manifestPath
62
+ } finally {
63
+ Remove-Item -Recurse -Force $tempRoot -ErrorAction SilentlyContinue
64
+ }
65
+ }
66
+ }