sanook-cli 0.4.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.
Files changed (148) hide show
  1. package/.env.example +23 -0
  2. package/CHANGELOG.md +38 -0
  3. package/LICENSE +201 -0
  4. package/README.md +239 -0
  5. package/dist/agentContext.js +2 -0
  6. package/dist/approval.js +78 -0
  7. package/dist/bin.js +461 -0
  8. package/dist/brain.js +186 -0
  9. package/dist/commands.js +66 -0
  10. package/dist/compaction.js +85 -0
  11. package/dist/config.js +101 -0
  12. package/dist/cost.js +59 -0
  13. package/dist/diff.js +36 -0
  14. package/dist/gateway/auth.js +32 -0
  15. package/dist/gateway/ledger.js +94 -0
  16. package/dist/gateway/lock.js +114 -0
  17. package/dist/gateway/schedule.js +74 -0
  18. package/dist/gateway/scheduler.js +87 -0
  19. package/dist/gateway/serve.js +57 -0
  20. package/dist/gateway/server.js +94 -0
  21. package/dist/gateway/telegram.js +115 -0
  22. package/dist/git.js +55 -0
  23. package/dist/hooks.js +104 -0
  24. package/dist/knowledge.js +68 -0
  25. package/dist/loop.js +169 -0
  26. package/dist/mcp.js +191 -0
  27. package/dist/memory.js +108 -0
  28. package/dist/providers/codex.js +86 -0
  29. package/dist/providers/keys.js +37 -0
  30. package/dist/providers/models.js +55 -0
  31. package/dist/providers/registry.js +241 -0
  32. package/dist/session.js +36 -0
  33. package/dist/skill-install.js +190 -0
  34. package/dist/skills.js +111 -0
  35. package/dist/tools/bash.js +26 -0
  36. package/dist/tools/edit.js +107 -0
  37. package/dist/tools/git.js +68 -0
  38. package/dist/tools/index.js +36 -0
  39. package/dist/tools/list.js +24 -0
  40. package/dist/tools/permission.js +30 -0
  41. package/dist/tools/read.js +18 -0
  42. package/dist/tools/recall.js +12 -0
  43. package/dist/tools/remember.js +14 -0
  44. package/dist/tools/schedule.js +61 -0
  45. package/dist/tools/search.js +54 -0
  46. package/dist/tools/skill.js +65 -0
  47. package/dist/tools/task.js +46 -0
  48. package/dist/tools/util.js +5 -0
  49. package/dist/tools/write.js +27 -0
  50. package/dist/ui/app.js +132 -0
  51. package/dist/ui/banner.js +20 -0
  52. package/dist/ui/brain-wizard.js +29 -0
  53. package/dist/ui/render.js +57 -0
  54. package/dist/ui/setup.js +46 -0
  55. package/package.json +77 -0
  56. package/second-brain/AGENTS.md +18 -0
  57. package/second-brain/CLAUDE.md +96 -0
  58. package/second-brain/Evals/retrieval-eval.md +30 -0
  59. package/second-brain/GEMINI.md +15 -0
  60. package/second-brain/Home.md +33 -0
  61. package/second-brain/README.md +29 -0
  62. package/second-brain/Runbooks/ingest-quarantine.md +27 -0
  63. package/second-brain/Runbooks/sleep-time-consolidation.md +26 -0
  64. package/second-brain/Shared/AI-Context-Index.md +52 -0
  65. package/second-brain/Shared/Core-Facts/protected-facts.md +21 -0
  66. package/second-brain/Shared/Decision-Memory/decision-log.md +24 -0
  67. package/second-brain/Shared/Memory-Inbox/memory-inbox.md +23 -0
  68. package/second-brain/Shared/Operating-State/current-state.md +30 -0
  69. package/second-brain/Shared/Provenance/ingest-log.md +27 -0
  70. package/second-brain/Shared/Rules/context-assembly-policy.md +28 -0
  71. package/second-brain/Shared/Rules/frontmatter-standard.md +33 -0
  72. package/second-brain/Shared/Rules/skills-admission.md +30 -0
  73. package/second-brain/Shared/User-Memory/user-preferences.md +25 -0
  74. package/second-brain/Templates/bug.md +22 -0
  75. package/second-brain/Templates/handoff.md +21 -0
  76. package/second-brain/Templates/project.md +24 -0
  77. package/second-brain/Templates/session.md +26 -0
  78. package/second-brain/USER.md +36 -0
  79. package/second-brain/Vault Structure Map.md +106 -0
  80. package/skills/agent-tool-mcp-builder/SKILL.md +88 -0
  81. package/skills/api-design-review/SKILL.md +70 -0
  82. package/skills/async-concurrency-correctness/SKILL.md +93 -0
  83. package/skills/audit-accessibility-wcag/SKILL.md +59 -0
  84. package/skills/audit-technical-seo/SKILL.md +62 -0
  85. package/skills/auth-jwt-session/SKILL.md +88 -0
  86. package/skills/brainstorm-design/SKILL.md +73 -0
  87. package/skills/build-etl-pipeline/SKILL.md +58 -0
  88. package/skills/build-form-validation/SKILL.md +103 -0
  89. package/skills/build-office-docs/SKILL.md +80 -0
  90. package/skills/build-react-component/SKILL.md +116 -0
  91. package/skills/build-spreadsheet/SKILL.md +106 -0
  92. package/skills/caching-strategy/SKILL.md +75 -0
  93. package/skills/cicd-pipeline-author/SKILL.md +65 -0
  94. package/skills/cloud-cost-optimize/SKILL.md +91 -0
  95. package/skills/code-comments/SKILL.md +52 -0
  96. package/skills/code-review/SKILL.md +61 -0
  97. package/skills/db-migration-safety/SKILL.md +67 -0
  98. package/skills/debug-frontend-browser/SKILL.md +58 -0
  99. package/skills/debug-root-cause/SKILL.md +54 -0
  100. package/skills/dependency-upgrade/SKILL.md +56 -0
  101. package/skills/deploy-release/SKILL.md +64 -0
  102. package/skills/diff-table-parity/SKILL.md +58 -0
  103. package/skills/dockerfile-optimize/SKILL.md +82 -0
  104. package/skills/error-message/SKILL.md +58 -0
  105. package/skills/estimate-work/SKILL.md +54 -0
  106. package/skills/explore-codebase/SKILL.md +73 -0
  107. package/skills/git-commit-pr/SKILL.md +65 -0
  108. package/skills/gitops-deploy-workflow/SKILL.md +97 -0
  109. package/skills/implement-from-design/SKILL.md +69 -0
  110. package/skills/incident-response-sre/SKILL.md +78 -0
  111. package/skills/k8s-debug-workload/SKILL.md +135 -0
  112. package/skills/k8s-manifest-review/SKILL.md +86 -0
  113. package/skills/llm-eval-harness/SKILL.md +63 -0
  114. package/skills/manage-client-server-state/SKILL.md +94 -0
  115. package/skills/mermaid-diagram/SKILL.md +61 -0
  116. package/skills/message-queue-jobs/SKILL.md +139 -0
  117. package/skills/naming-helper/SKILL.md +57 -0
  118. package/skills/observability-instrument/SKILL.md +113 -0
  119. package/skills/optimize-core-web-vitals/SKILL.md +75 -0
  120. package/skills/optimize-sql-query/SKILL.md +67 -0
  121. package/skills/performance-profiling/SKILL.md +65 -0
  122. package/skills/process-pdf/SKILL.md +107 -0
  123. package/skills/profile-dataset/SKILL.md +97 -0
  124. package/skills/prompt-engineering/SKILL.md +70 -0
  125. package/skills/rag-pipeline/SKILL.md +53 -0
  126. package/skills/rate-limiting/SKILL.md +96 -0
  127. package/skills/refactor-cleanup/SKILL.md +54 -0
  128. package/skills/regex-build/SKILL.md +72 -0
  129. package/skills/release-notes/SKILL.md +79 -0
  130. package/skills/rest-graphql-contract/SKILL.md +71 -0
  131. package/skills/scrape-structured-web-data/SKILL.md +61 -0
  132. package/skills/secrets-management/SKILL.md +96 -0
  133. package/skills/security-review/SKILL.md +62 -0
  134. package/skills/shell-script-robust/SKILL.md +71 -0
  135. package/skills/style-responsive-tailwind/SKILL.md +70 -0
  136. package/skills/terraform-plan-review/SKILL.md +95 -0
  137. package/skills/type-safety-strict/SKILL.md +82 -0
  138. package/skills/validate-data-quality/SKILL.md +62 -0
  139. package/skills/wrangle-tabular-data/SKILL.md +75 -0
  140. package/skills/write-adr/SKILL.md +75 -0
  141. package/skills/write-analytical-sql/SKILL.md +71 -0
  142. package/skills/write-data-viz/SKILL.md +58 -0
  143. package/skills/write-docs/SKILL.md +54 -0
  144. package/skills/write-plan/SKILL.md +59 -0
  145. package/skills/write-playwright-e2e/SKILL.md +86 -0
  146. package/skills/write-prd/SKILL.md +65 -0
  147. package/skills/write-rfc/SKILL.md +75 -0
  148. package/skills/write-tests/SKILL.md +50 -0
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: diff-table-parity
3
+ description: Compares two tables or query results and diagnoses exactly how they differ — row counts, key set differences, per-column value mismatches — for migration and refactor validation.
4
+ when_to_use: When the user must prove two datasets match (or explain why they don't) — validate a data migration, regression-check an ETL change, or confirm a query refactor returns identical results, going past 'counts differ' to where/why/whether-expected.
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ Reach for this when the question is "are A and B the same data, and if not, exactly how do they differ?" Concrete triggers:
10
+
11
+ - Validating a data migration (old store -> new store) before cutover.
12
+ - Regression-checking an ETL/pipeline change: same inputs should yield same outputs.
13
+ - Confirming a query/view refactor returns identical results to the original.
14
+ - A `COUNT(*)` already differs and you need where + why, not just the delta number.
15
+
16
+ NOT this skill: asserting business rules on a single dataset (non-null, ranges, uniqueness, referential integrity) — that is validate-data-quality. This skill always compares **two** datasets against each other; it never decides whether either is "correct" in isolation.
17
+
18
+ ## Steps
19
+
20
+ 1. **Pin the comparison contract.** Before touching data, write down: the join key(s) that uniquely identify a row in both sides, the column set to compare, and the side names (call them A = baseline/source, B = candidate/target). If a natural key does not exist, find a deterministic composite key — never compare by row position/ordinal, ORDER BY is not a contract.
21
+
22
+ 2. **Reconcile schemas first.** List columns on each side. Note columns only in A, only in B, and renamed pairs. Decide per column: compare, ignore (e.g. audit timestamps, surrogate auto-IDs), or normalize before compare (cast, trim, round). Comparing a `numeric(10,2)` against a `float8` raw will produce noise — align types now, not after you see mismatches.
23
+
24
+ 3. **Row-set diff on the key.** Produce three buckets and their counts:
25
+ - keys only in A (dropped/missing in B)
26
+ - keys only in B (added/extra in B)
27
+ - keys in both (candidates for value comparison)
28
+ Use a `FULL OUTER JOIN ... ON a.key = b.key` and branch on `a.key IS NULL` / `b.key IS NULL`, or set operations (`EXCEPT` both directions). Capture a sample (10–20) of each non-empty bucket, not just the count.
29
+
30
+ 4. **Column-value diff on the intersection.** For the in-both keys, compare each chosen column. For every column report: mismatch count, mismatch % of intersection, and 5–10 sample `(key, a_value, b_value)` triples. Use null-safe comparison (`IS DISTINCT FROM`, or `COALESCE` to a sentinel) so NULL-vs-NULL counts as equal and NULL-vs-value counts as a mismatch. Rank columns by mismatch % so the worst offender surfaces first.
31
+
32
+ 5. **Classify every diff as expected or defect.** A raw mismatch list is not a verdict. Bucket each pattern:
33
+ - **Expected/benign:** type coercion (`"1.0"` vs `1`), rounding/precision, timezone or `timestamptz` rendering, whitespace/case/collation, NULL-vs-empty-string, stable-but-different sort. Note the rule and move on.
34
+ - **Real defect:** value drift, truncation, wrong mapping, dropped/duplicated rows, encoding corruption. These block parity.
35
+ State the classification rule you applied so it is auditable, don't hand-wave "looks like rounding."
36
+
37
+ 6. **Emit a parity verdict with evidence.** End with one of: PARITY (zero unexplained diffs), PARITY-WITH-NOTES (all diffs classified expected, list the rules), or FAIL (defects remain, list them with sample rows). Always attach the numbers: total A, total B, only-A, only-B, in-both, and per-column mismatch counts. A bare "tables match" is not an acceptable output.
38
+
39
+ 7. **Scale for large tables.** Don't pull millions of rows into the agent. Tier the work:
40
+ - Cheap gate: compare `COUNT(*)` and per-column aggregates (`SUM`, `MIN`, `MAX`, `COUNT(DISTINCT)`) — if these match, deep diff is often unnecessary.
41
+ - Block hashing: `md5`/`xxhash` of concatenated normalized columns per row, then diff the hash sets; only materialize full rows for the keys whose hashes differ.
42
+ - Sampling: if even hashing is too heavy, diff a deterministic sample (`WHERE abs(hashtext(key)) % 100 = 0`) and report it as a sample, not a proof.
43
+
44
+ ## Common Errors
45
+
46
+ - **Float / precision false positives.** `=` on floating point flags rows that are equal to business precision. Round to the contract's scale (`round(x, 2)`) or compare `abs(a - b) < epsilon` before declaring a defect. The same trap hits `numeric` vs `double precision` and currency stored at different scales.
47
+ - **Plain `=` swallows NULL rows.** `a.col = b.col` is NULL (not true) when either side is NULL, so genuinely-differing NULL rows silently vanish from the mismatch count. Use `IS DISTINCT FROM` everywhere.
48
+ - **Key collision / non-unique join key.** If the "key" is not unique on one side, the join fans out and inflates both counts and mismatches — a 1:N join makes a clean migration look broken. Verify `COUNT(*) = COUNT(DISTINCT key)` on **both** sides before trusting any downstream number.
49
+ - **Non-deterministic ordering treated as a diff.** Two queries returning the same set in different order are at parity; comparing by position reports 100% mismatch. Always diff on the key, never on row order, and never assume `LIMIT` without `ORDER BY` is stable.
50
+ - **Collation / encoding / trailing whitespace.** `'café'` vs `'cafe'`, `'A '` vs `'A'`, or different Unicode normalization read as value defects. Normalize (trim, casefold, NFC) per the contract before classifying.
51
+ - **Comparing across a snapshot skew.** If A and B are read at different times and the source is still mutating, "diffs" are just new writes. Pin both reads to the same snapshot/transaction or a frozen extract.
52
+
53
+ ## Verify
54
+
55
+ - Sanity arithmetic holds: `only_A + in_both = total_A` and `only_B + in_both = total_B`. If not, the join key is wrong or non-unique — stop and fix step 1/3.
56
+ - Re-run on identical input (A vs A) → must report PARITY with zero diffs. If a self-compare shows mismatches, your normalization or null-handling is broken, not the data.
57
+ - Every column flagged as defect has at least one concrete sample `(key, a_value, b_value)` attached; every column dismissed as expected names the classification rule.
58
+ - The final verdict is one of PARITY / PARITY-WITH-NOTES / FAIL with the full count table — never ship just a single number or "matches".
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: dockerfile-optimize
3
+ description: Authors and optimizes Dockerfiles for small, secure, fast-building container images: multi-stage builds, minimal/distroless bases, layer caching, non-root users, and .dockerignore. Triggers when writing or reviewing a Dockerfile, shrinking image size, fixing slow builds, or hardening a container image.
4
+ when_to_use: ผู้ใช้กำลังเขียน/แก้ Dockerfile, image ใหญ่/build ช้า, อยาก harden container, หรือย้ายไป multi-stage/distroless
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ ใช้ skill นี้เมื่อ:
10
+ - เขียน Dockerfile ใหม่ หรือ review Dockerfile ที่มีอยู่
11
+ - image ใหญ่เกินไป (เช่น >500MB สำหรับ app เล็ก) — ต้องลด size
12
+ - build ช้าทุกครั้งเพราะ cache invalidate ตลอด
13
+ - harden container ก่อน deploy (non-root, drop caps, read-only fs)
14
+ - migrate single-stage → multi-stage หรือ → distroless
15
+
16
+ อย่าใช้เมื่อ: ปัญหาอยู่ที่ runtime orchestration (k8s manifests, compose networking) ไม่ใช่ตัว image เอง
17
+
18
+ ## Steps
19
+
20
+ 1. **อ่านบริบทก่อนแก้** — `cat Dockerfile .dockerignore` + ดู lockfile (`package-lock.json` / `go.mod` / `requirements.txt` / `pom.xml`) เพื่อรู้ภาษา/runtime. รัน `docker images <name>` เช็ค size ปัจจุบันเป็น baseline. ถ้ามี image อยู่แล้ว: `docker history <image> --no-trunc | sort -k1` หา layer ที่ใหญ่ที่สุดก่อน
21
+
22
+ 2. **Multi-stage split** — แยก `builder` (มี compiler/dev deps/SDK) ออกจาก `runtime` (มีแค่ binary/artifact + runtime deps). final stage ต้อง `COPY --from=builder` เฉพาะ output ที่จำเป็น เช่น:
23
+ - Go: copy แค่ binary → runtime ใช้ `scratch` หรือ `distroless/static`
24
+ - Node: build ใน `node:20` → copy `dist/` + `node_modules` (prod เท่านั้น) → runtime `node:20-slim` หรือ `distroless/nodejs20`
25
+ - Python: build wheels ใน builder → `pip install --no-index` ใน runtime จาก wheels
26
+ - ตั้งชื่อ stage ชัด (`AS builder`, `AS runtime`) — ห้ามมี stage ลอยที่ไม่ถูก copy
27
+
28
+ 3. **เลือก + pin base image** — เลือกตามลำดับ: `distroless` (no shell, เล็กสุด, ปลอดภัยสุด) > `alpine` (มี shell, glibc-incompat เสี่ยง) > `-slim` (debian, glibc ครบ). **pin ด้วย digest** ไม่ใช่ tag ลอย:
29
+ ```dockerfile
30
+ FROM node:20-slim@sha256:<digest>
31
+ ```
32
+ หา digest: `docker buildx imagetools inspect node:20-slim`. tag เปลี่ยนเงียบได้ digest ไม่เปลี่ยน → reproducible build
33
+
34
+ 4. **Layer ordering + cache mounts** — เรียงจาก "เปลี่ยนน้อย → เปลี่ยนบ่อย": copy manifest+lockfile ก่อน install, แล้วค่อย `COPY . .`:
35
+ ```dockerfile
36
+ COPY package.json package-lock.json ./
37
+ RUN --mount=type=cache,target=/root/.npm npm ci
38
+ COPY . .
39
+ ```
40
+ ใช้ `--mount=type=cache` (BuildKit) สำหรับ package cache: npm `/root/.npm`, pip `/root/.cache/pip`, go `/root/.cache/go-build`+`/go/pkg/mod`, apt `/var/cache/apt`. รวม `RUN apt-get update && apt-get install -y --no-install-recommends ... && rm -rf /var/lib/apt/lists/*` ใน RUN เดียว
41
+
42
+ 5. **Non-root + harden** — สร้าง user แล้วสลับก่อน CMD:
43
+ ```dockerfile
44
+ RUN groupadd -r app && useradd -r -g app -u 10001 app
45
+ USER 10001
46
+ ```
47
+ distroless ใช้ `USER nonroot` หรือ `:nonroot` tag. เพิ่ม `HEALTHCHECK`, ใช้ exec-form `CMD ["app"]` (ไม่ใช่ shell-form), ตั้ง `WORKDIR` ที่ user เขียนได้. caps/read-only fs บังคับตอน runtime: `--read-only --cap-drop=ALL --security-opt=no-new-privileges` (เขียนไว้ใน compose/k8s + comment ใน Dockerfile)
48
+
49
+ 6. **.dockerignore + ลบ build artifacts** — สร้าง/แก้ `.dockerignore` ตัด `.git`, `node_modules`, `*.md`, `.env*`, `dist`, `__pycache__`, `target`, `coverage`, `*.log`, `.DS_Store`, secrets. ตรวจ final stage **ห้ามมี**: compiler, package-manager cache, `.env`, private key, `.git`. secret ที่ใช้ตอน build → ใช้ `RUN --mount=type=secret,id=...` ไม่ใช่ `ARG`/`COPY` (ARG ติดใน history)
50
+
51
+ 7. **Scan + วัด diff** — build แล้ว scan:
52
+ ```bash
53
+ docker build -t app:new .
54
+ trivy image --severity HIGH,CRITICAL app:new # หรือ grype app:new
55
+ docker images app:new --format '{{.Size}}'
56
+ ```
57
+ รายงาน before/after: size (MB), build time (cold + warm cache), CVE count. แก้ HIGH/CRITICAL ที่มาจาก base image โดยขยับ base version ก่อน
58
+
59
+ ## Common Errors
60
+
61
+ - **ARG เก็บ secret ติด history** — `ARG TOKEN` + `docker history` เห็นค่าได้ แม้ลบใน layer หลัง. ใช้ `RUN --mount=type=secret` เสมอ
62
+ - **alpine + glibc binary พัง** — alpine ใช้ musl; prebuilt wheel/native module ที่ link glibc จะ segfault หรือ `Error loading shared library`. Python/Node native deps → ใช้ `-slim` (debian) ปลอดภัยกว่า
63
+ - **`COPY . .` ก่อน install** — ทำให้ทุก source change invalidate layer install → build ช้าทุกครั้ง. ต้อง copy lockfile ก่อนเสมอ
64
+ - **cache mount ไม่ทำงาน** — ต้องเปิด BuildKit: `DOCKER_BUILDKIT=1` หรือ `docker buildx build`. ไม่งั้น `--mount` ถูก ignore เงียบ
65
+ - **distroless แล้ว debug ไม่ได้** — ไม่มี shell, `docker exec ... sh` จะ fail. ใช้ `:debug` tag ตอน dev หรือ `ctr`/ephemeral debug container แทน
66
+ - **USER แล้วเขียน WORKDIR ไม่ได้** — non-root เขียน path ที่ root เป็นเจ้าของไม่ได้ → `EACCES`. `chown` ให้ user หรือ `COPY --chown=app:app`
67
+ - **`latest`/floating tag** — build ซ้ำได้ผลต่างกัน + CVE สุ่มเข้ามา. pin digest
68
+ - **`apt-get install` แยก RUN จาก update** — apt cache layer เก่า → ติดตั้ง package เวอร์ชันเก่า/พัง. รวม `update && install && rm -rf lists` ใน RUN เดียว
69
+ - **ลืม `--no-install-recommends` / `npm ci` แทน `npm install`** — ดึง deps เกินจำเป็น, lockfile ไม่ตรง
70
+
71
+ ## Verify
72
+
73
+ ก่อนถือว่าเสร็จ ต้องผ่านทุกข้อ (มีหลักฐานรันจริง ไม่ใช่ "แก้แล้ว"):
74
+
75
+ - [ ] `docker build` ผ่าน (cold cache) — บันทึก size + time
76
+ - [ ] build ซ้ำ (warm cache) เร็วขึ้นชัด — source change เล็กไม่ trigger re-install
77
+ - [ ] `docker run app:new id` แสดง uid ≠ 0 (non-root จริง)
78
+ - [ ] `docker history app:new` — final layers ไม่มี compiler/secret/.git/package cache
79
+ - [ ] `trivy image --severity HIGH,CRITICAL` = 0 (หรือ document ตัวที่แก้ไม่ได้ + เหตุผล)
80
+ - [ ] container start + health check ผ่าน (`docker run` แล้ว app ตอบได้จริง)
81
+ - [ ] size diff: รายงาน before → after เป็น MB + % ลด
82
+ - [ ] base image pin ด้วย `@sha256:` digest
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: error-message
3
+ description: Sanook writes and audits user-facing error and exception messages so they state what failed, why, and the next action — actionable, specific, non-blaming, no leaked internals — applying a consistent voice across CLI/API/UI.
4
+ when_to_use: User asks to improve error/exception/validation/toast messages, write copy for failure states, or audit messages for clarity; designing error handling for a CLI/API/form.
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ - Writing or rewriting a single error/exception/validation/toast string.
10
+ - Designing error handling for a new CLI command, API endpoint, or form.
11
+ - Auditing an existing codebase for vague or leaky failure messages.
12
+
13
+ Skip if the message is a developer-only debug log never shown to a user — those can stay verbose.
14
+
15
+ ## Steps
16
+
17
+ Every user-facing message must answer three questions in order: **what failed → why → what to do next**. If a message is missing the "next step", it is not done.
18
+
19
+ 1. **Classify the failure first.** Pick the channel before writing words, because it sets length and tone:
20
+ - `validation` (user input, recoverable) → short, points at the exact field/arg, names the valid format. No error code.
21
+ - `operational` (network/timeout/permission/rate-limit) → state the cause + a retry or fix path. Include a code/correlation id.
22
+ - `fatal` (bug, corrupt state, unhandled) → apologize once, give code + correlation id + where to report. Never dump the stack to the user.
23
+
24
+ 2. **Write the three parts explicitly.** Template: `<what failed in plain terms>. <why / which input>. <imperative next action>.`
25
+ - Bad: `Invalid input`
26
+ - Good: `Config file not found at ./app.config.json. Create it or pass --config <path>.`
27
+ - For CLI, the next action is usually a concrete command or flag. For forms, it is the corrected format. For API, it is the field path plus the allowed value/range.
28
+
29
+ 3. **Strip blame and jargon.** Replace `you entered an illegal value` → `Value must be between 1 and 100`. Drop "illegal/invalid/bad" as the only descriptor; say what *would* be valid. Never imply user fault for system failures (`you broke X` → `X is unavailable`).
30
+
31
+ 4. **Remove internal leakage from anything user-visible.** No raw stack traces, no file paths under home/build dirs, no SQL, no internal class/module names, no secrets/tokens/connection strings, no `undefined`/`null`/`[object Object]`. Route those to the log; show the user a code that maps back to the log entry. Format: `Something went wrong (E_UPLOAD_502, ref a1b2c3). Check logs or report with this ref.`
32
+
33
+ 5. **Match exit/status to severity** so machines and humans agree: CLI non-zero exit + stderr for failures, never stderr-with-exit-0. API: 4xx for caller fixable, 5xx for server fault; put a stable `code` field in the body, not just an HTTP status. Don't return 200 with `{"error": ...}`.
34
+
35
+ 6. **Keep one voice.** Decide person and mood once (recommended: imperative, second-or-no person, present tense, no exclamation marks, no emoji unless the UI's design system already uses them) and apply it to every message. Sentence case, end with a period; no trailing "please".
36
+
37
+ 7. **Audit mode** (when scanning a codebase):
38
+ - Find sites: `rg -n "throw |raise |console\.(error|warn)|res\.status\([45]|reject\(|panic|abort\(" --type-add 'src:*.{ts,tsx,js,py,go,rs}' -tsrc`
39
+ - Find vague/leaky strings: `rg -niE "something went wrong|unknown error|invalid|failed|oops|error occurred|undefined|null|\[object" -tsrc`
40
+ - For each hit: flag if it is missing what/why/next, blames the user, or leaks internals → propose a rewrite inline using the Step 2 template. Group by file. Don't rewrite logger-only calls.
41
+
42
+ ## Common Errors
43
+
44
+ - **"Next step" gets dropped under pressure.** A message that only says what failed (`Connection refused`) is half-done. Always append the action (`Connection refused. Is the server running on port 5432? Set DB_HOST to override.`).
45
+ - **Catch block re-leaks the cause.** `catch (e) { showUser(e.message) }` defeats the whole skill — `e.message` is often a driver/stack string. Map exceptions to curated user messages; log the raw `e` separately.
46
+ - **Validation that hides which field failed.** `Invalid request body` forces the caller to guess. Name the path: `body.email must be a valid email address`.
47
+ - **Severity/channel mismatch.** A recoverable validation error written as a scary fatal ("FATAL: aborting"), or a real crash swallowed into a friendly toast with exit 0. Tone and exit code must match the class from Step 1.
48
+ - **Localized/duplicated strings drift.** If the same error exists in multiple languages or layers (API + UI), changing one and not the other reintroduces the bad copy. Find duplicates before rewriting.
49
+ - **Correlation id printed but never logged.** A ref code the user can quote is useless if it isn't also written to the log. Verify both sides exist.
50
+ - **Over-apologizing.** "We are so sorry, oops!" on a routine validation error reads as noise. Reserve apology for fatal/server faults, once.
51
+
52
+ ## Verify
53
+
54
+ - Pick 3–5 rewritten messages: each answers what + why + next, names a concrete field/command, and contains zero internal identifiers or stack text.
55
+ - Trigger a real failure (bad input, killed dependency) and read the *actual* rendered output, not the source string — confirm no `undefined`/path/stack leaked through interpolation.
56
+ - CLI: failure path returns non-zero exit and writes to stderr. API: 4xx/5xx matches fault owner and body carries a stable `code`.
57
+ - For fatal-class messages, confirm the ref/correlation id shown to the user also appears in the log.
58
+ - Audit output lists every flagged site with a proposed rewrite; no logger-only call was rewritten by mistake.
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: estimate-work
3
+ description: Produces grounded effort estimates for a task/feature — decomposing into subtasks, assigning size (story points or t-shirt S/M/L), surfacing assumptions, unknowns, and risk buffers, and giving an optimistic/likely/pessimistic range instead of a single false number.
4
+ when_to_use: User asks 'how long / how big / how many points', wants a sprint or scoping estimate, or wants a feature broken into sized chunks before planning. Pairs with write-plan and write-prd.
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ - "How long will X take?" / "How big is this?" / "How many points?"
10
+ - Scoping a feature before a sprint, or splitting it into sized chunks for planning.
11
+ - Comparing two approaches by cost before committing.
12
+
13
+ Do NOT use when the ask is "just build it" with no scoping need — go straight to write-plan. Do NOT invent a deadline; estimate effort, not calendar dates (velocity and availability are the caller's to apply).
14
+
15
+ ## Steps
16
+
17
+ 1. **Read the actual code first — estimate from the repo, not from the prompt.** Locate the files the change touches (grep for the feature/module, read the entry points, the data model, the call sites). An estimate written before looking at the code is a guess. If the codebase is unfamiliar, spend the first pass mapping where the change lands.
18
+
19
+ 2. **Decompose into discrete subtasks** that each map to a verifiable outcome — not phases. Bad: "backend / frontend / testing". Good: "add `status` column + migration", "expose it in the list endpoint", "render the badge", "wire the filter". Each subtask should be independently sizable and ideally ≤ 1 size class on its own. If a subtask is L or bigger, split it — large items hide the most error.
20
+
21
+ 3. **Size each subtask with the basis stated.** Pick ONE scale and stay in it:
22
+ - **T-shirt:** S = touch 1–2 files, known pattern, no new interface. M = a few files + tests, one new interface. L = cross-cutting, new component, or unfamiliar area. XL = must be split (don't size it, split it).
23
+ - **Story points (Fibonacci 1/2/3/5/8):** anchor to a reference task the caller already knows, not to hours.
24
+ - State the basis in one clause: "M — new endpoint + RLS policy + tests, pattern exists in `orders`".
25
+
26
+ 4. **List assumptions and unknowns separately** — each as a line that says how it moves the estimate. Assumption = something you're treating as true to size at all ("auth/permissions already exist", "no data migration of existing rows"). Unknown = something you genuinely can't size yet ("does the 3rd-party API page its results? if yes +1 size"). Unknowns are the real cost drivers; name them explicitly.
27
+
28
+ 5. **Give a range, never a point value.** For each subtask and for the total, give **optimistic / likely / pessimistic**. Optimistic = everything as assumed. Likely = your honest center. Pessimistic = the named unknowns resolve the hard way. Add a **risk buffer** to the total proportional to the unknowns — roughly +15% mostly-known, +30–50% with live unknowns or unfamiliar code. Sum subtask ranges; don't hand-wave a single total.
29
+
30
+ 6. **Flag the biggest risk drivers and what would shrink the range.** Name the 1–3 items that dominate the spread, and the concrete action that collapses each: a time-boxed **spike** ("4h spike: confirm the API pagination shape"), a **decision** the caller must make ("sync vs async — async is +L"), or a missing input ("need the final schema"). This is the highest-value output: a wide range with a clear "do this to narrow it" beats a fake-precise number.
31
+
32
+ 7. **Output a tidy table + one-line summary.** Table columns: `Subtask | Size | Basis | Key unknown`. Then a total range line and a one-sentence summary (e.g. "Likely M-sized / 8 pts; the API pagination unknown is what could push it to L — resolve with a 4h spike"). Round at sane granularity; avoid false precision like "6.5 points" or "11.3 hours".
33
+
34
+ ## Common Errors
35
+
36
+ - **Single number with no range.** "About 3 days" is a promise, not an estimate. Always optimistic/likely/pessimistic.
37
+ - **Estimating the happy path only.** The likely number must include review, tests, edge cases, and rework — not just "type the code". Most real cost is in the last 20%.
38
+ - **Phase-based decomposition** ("design / build / test") — these aren't sizable units and hide where the work actually is. Decompose by deliverable.
39
+ - **Hours masquerading as points.** Story points are relative complexity, not disguised time. Don't multiply points by a fixed hour rate inside the estimate.
40
+ - **Burying unknowns in prose.** If a risk isn't on its own line with a size impact, it will be forgotten and blow the estimate. List, don't narrate.
41
+ - **Padding silently.** A buffer that's hidden inside each number is undebuggable. State the buffer as an explicit line item with its reason.
42
+ - **Estimating without reading the code.** The #1 source of bad numbers. "Add a field" is S if the pattern exists and L if it triggers a migration + cache invalidation + API version bump — you only know which by looking.
43
+ - **Splitting forever.** Decomposition has a floor: stop at the smallest independently-shippable unit. Sub-S fragments add noise, not accuracy.
44
+
45
+ ## Verify
46
+
47
+ Before returning, the estimate passes only if:
48
+ - [ ] Every subtask maps to a concrete deliverable, and none is sized XL (all XL split).
49
+ - [ ] Every subtask and the total have a 3-point range, not a single value.
50
+ - [ ] Assumptions and unknowns are listed separately, each with a stated size impact.
51
+ - [ ] The total range = sum of subtask ranges + a named, explained buffer.
52
+ - [ ] The 1–3 dominant risk drivers are flagged, each with a spike/decision/input that would shrink the range.
53
+ - [ ] No false precision (no decimals on points; hours rounded to a sane grain).
54
+ - [ ] The basis for each size traces to something real in the repo, not the prompt wording.
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: explore-codebase
3
+ description: Explores an unfamiliar codebase to map architecture, locate where a feature lives, and find reusable utilities before writing code — returning a concise summary of entrypoints, key modules, and conventions. Use when entering a new repo or before a change that spans files you don't know yet.
4
+ when_to_use: เข้า codebase ใหม่; ก่อนแก้งานที่กระจายหลายไฟล์ยังไม่รู้โครงสร้าง; หา util ที่ reuse ได้
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ Run this before writing or changing code in a repo you don't already have a mental model of. Concretely:
10
+
11
+ - First time touching this repo, or returning after major churn.
12
+ - The change spans files you can't name yet ("add auth to the API", "rename this config everywhere").
13
+ - You suspect a helper/util already exists and don't want to reinvent it.
14
+
15
+ Skip it when the change is fully described by a one-line diff in a file you already know (typo, log line, single rename). Exploring then is wasted context.
16
+
17
+ **Goal of the skill:** produce a short, reusable map — not to read the whole repo. Stop as soon as you can answer: where does the relevant code live, what conventions must I follow, what can I reuse.
18
+
19
+ ## Steps
20
+
21
+ 1. **Anchor on the manifest + docs first (cheap, high signal).**
22
+ - Read `README*` and any `CONTRIBUTING`/`docs/` index.
23
+ - Read the package/build manifest to learn language, deps, scripts, and entry: `package.json`, `pyproject.toml`/`setup.cfg`, `go.mod`, `Cargo.toml`, `pom.xml`/`build.gradle`, `Gemfile`, `composer.json`.
24
+ - From `scripts`/targets, note the real **build / test / lint / start** commands. Do not guess them.
25
+
26
+ 2. **Map structure shallowly — directories before files.**
27
+ - List the top 1–2 levels of source dirs (e.g. `src/`, `app/`, `lib/`, `cmd/`, `internal/`, `pkg/`). Use a depth-limited listing, not a full recursive dump.
28
+ - Find the entrypoint(s): `main`, `index`, `app`, `cmd/*/main.go`, `bin/*`, the `main`/`module` field in the manifest, or framework conventions (`pages/`, `app/`, `routes/`, `controllers/`).
29
+ - Note where tests live (`test/`, `tests/`, `__tests__/`, `*_test.go`, `*.spec.*`) and config (`config/`, `*.config.*`, dotfiles, `.env.example`).
30
+
31
+ 3. **Extract conventions (so your code matches the repo, not your defaults).**
32
+ - Framework / runtime + version (from manifest + lockfile).
33
+ - Test runner and how a single test runs (jest/vitest/pytest/go test/...).
34
+ - Lint/format tool and config (`.eslintrc*`, `ruff`/`flake8`, `.prettierrc`, `.editorconfig`, `gofmt`).
35
+ - Import/module style, path aliases (`tsconfig` `paths`, `jsconfig`), error-handling and logging patterns. Open 1–2 representative source files to confirm, don't infer from names alone.
36
+
37
+ 4. **Locate the target feature + reusable utils with search, not browsing.**
38
+ - Use ripgrep for the concept: `rg -n <symbol|route|string|error message>`. Search user-facing strings and route paths — they pin down the real implementation fast.
39
+ - Find existing helpers before writing new ones: `rg -n "function|def|export (function|const)" <util-or-helpers-dir>`, or grep for likely names (`format`, `parse`, `client`, `validate`, `retry`).
40
+ - Follow imports outward from the entrypoint or from the matched file to see how pieces wire together. Read only the files on that path.
41
+
42
+ 5. **For a large/sprawling repo, delegate the wide read.**
43
+ - Spin up a read-only Explore subagent with a narrow brief ("find where X is handled and what utils exist for Y; return file:line refs + summary"). It reads in its own context and returns a digest, keeping the main context lean.
44
+
45
+ 6. **Return a tight summary — references, not contents.**
46
+ Report only:
47
+ - **Entrypoints:** file:line where execution/requests start.
48
+ - **Where it lives:** the modules/files relevant to the task, with one-line roles.
49
+ - **Conventions:** framework, test/lint/build commands, code-style notes to follow.
50
+ - **Reuse:** existing utils/helpers to call instead of writing new ones (file:line).
51
+ - **Gotchas:** anything surprising (codegen, monorepo boundaries, generated dirs, indirection).
52
+
53
+ ## Common Errors / Gotchas
54
+
55
+ - **Reading everything until context fills up.** The most common failure. Cap your exploration; prefer `rg` + targeted reads over opening files to "get a feel". If you're reading the 10th file without a hypothesis, stop and search.
56
+ - **Vague summary nobody can act on** ("it's a typical web app"). Every claim must carry a `file:line` or exact command. If it isn't specific enough to start the change, it isn't done.
57
+ - **Grepping inside generated/vendored trees.** Exclude `node_modules/`, `dist/`, `build/`, `.next/`, `coverage/`, `target/`, `vendor/`, `.venv/`. Use `rg` (respects `.gitignore` by default) and don't override it with `-uu` unless needed.
58
+ - **Trusting folder names over reality** — a `utils/` may be dead, the real helpers elsewhere. Confirm by checking who imports it (`rg "from .*utils"` / `rg "import.*utils"`).
59
+ - **Guessing build/test commands** instead of reading them from the manifest/CI. Wrong command wastes a cycle; pull the exact one from `scripts`/CI config.
60
+ - **Monorepo blindness.** If you see `packages/`, `apps/`, `workspaces`, `pnpm-workspace.yaml`, `turbo.json`, or `go.work`, identify which package owns the task before mapping — the root manifest isn't the whole story.
61
+ - **Subagent brief too broad.** "Explain the codebase" returns a wall of text. Scope it to the task: one feature to locate, one question to answer.
62
+
63
+ ## Verify
64
+
65
+ The exploration succeeded when all are true:
66
+
67
+ - You can name the **entrypoint** and the specific **files/modules** the upcoming change will touch, each with `file:line`.
68
+ - You have the **exact** build, test, and lint commands (copied from manifest/CI, not guessed).
69
+ - You listed any **reusable utils** relevant to the task, or confirmed none exist.
70
+ - A teammate could start the change from your summary **without re-reading the repo**.
71
+ - Your context is not bloated with full file contents you didn't need — references over dumps.
72
+
73
+ If you can't point to where the change goes, you haven't finished exploring — search more, or delegate a scoped Explore subagent.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: git-commit-pr
3
+ description: Stages and writes a Conventional Commits message from the actual diff, then opens a pull request with a structured description (summary, changes, test plan) using the gh CLI. Use when changes are ready to commit and/or turn into a PR.
4
+ when_to_use: Use when changes are ready to commit and/or turn into a pull request — i.e. work is finished and needs committing, a PR must be opened, or the user explicitly says commit / push / open a PR.
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ - Work is finished and the user asked to commit, push, or open a PR.
10
+ - Do NOT auto-commit or auto-push. Only commit/push when the user explicitly requests it.
11
+ - If the user only said "commit" (not "push"/"PR"), stop after the commit step.
12
+
13
+ ## Steps
14
+
15
+ 1. **Read the actual diff first.** Run `git status` and `git diff` (staged + unstaged) and `git log --oneline -5`. Never write a commit message from memory or from the task description — describe what the diff actually changed.
16
+ 2. **Scan for secrets before staging.** Grep the diff for keys/tokens (e.g. `sk-`, `ghp_`, `glpat-`, `AKIA`, `Bearer `, `password`, `api_key`, `.env` contents). If found, stop and flag it — do not commit.
17
+ 3. **Split into logical units.** If the diff covers unrelated concerns (e.g. a bugfix + an unrelated refactor), make separate commits with `git add <specific paths>` per unit. One commit = one coherent change. Do not lump everything into one blob.
18
+ 4. **Write a Conventional Commits message.** Format: `type(scope): summary` where type ∈ `feat|fix|refactor|chore|docs|test|perf|build|ci`. Keep the subject ≤ ~72 chars, imperative mood. In the body, explain **why** the change was made (the problem/intent), not just a restatement of the file list.
19
+ 5. **Add a co-author trailer** if your environment defines one (blank line, then `Co-Authored-By: Name <email>`). Skip if none is configured.
20
+ 6. **Branch if on the default branch.** Run `git branch --show-current`. If it's `main`/`master`, create a topic branch (`git switch -c <type>/<short-desc>`) before committing — never commit directly to the default branch.
21
+ 7. **Commit.** Use a heredoc for multi-line messages so formatting survives:
22
+ ```
23
+ git commit -m "$(cat <<'EOF'
24
+ fix(parser): handle empty input without panicking
25
+
26
+ Empty stdin previously hit an unchecked index. Guard the read
27
+ and return an empty result instead.
28
+ EOF
29
+ )"
30
+ ```
31
+ 8. **Push the branch** (only if the user wants a PR/push): `git push -u origin <branch>`.
32
+ 9. **Open the PR with a structured body** via `gh`:
33
+ ```
34
+ gh pr create --title "<type(scope): summary>" --body "$(cat <<'EOF'
35
+ ## Summary
36
+ One or two sentences on what this PR does and why.
37
+
38
+ ## Changes
39
+ - bullet per meaningful change
40
+ - grouped by area if large
41
+
42
+ ## Test plan
43
+ - [ ] command(s) run to verify, with expected result
44
+ - [ ] manual checks performed
45
+ EOF
46
+ )"
47
+ ```
48
+ 10. **Return the PR URL** that `gh pr create` prints.
49
+
50
+ ## Common Errors
51
+
52
+ - **Interactive flags fail.** `-i` (e.g. `git rebase -i`, `git add -i`) and editor-launching commands hang in a non-interactive environment. Always pass the message via `-m`/`--body` (heredoc), never let an editor open.
53
+ - **`gh` not authenticated.** If `gh pr create` errors with auth, run `gh auth status` to confirm. Don't fall back to printing a manual PR link unless asked.
54
+ - **No upstream / no remote branch.** First push must use `git push -u origin <branch>`, or `gh pr create` can't find the head branch.
55
+ - **Committed to default branch by accident.** If you discover you committed to `main`/`master`, move the commit to a new branch (`git switch -c <branch>`) and reset the default branch back (`git reset --hard origin/main`) — but reset is destructive, so confirm before running.
56
+ - **Empty diff.** If `git status` shows nothing staged/changed, there is nothing to commit — report that instead of creating an empty commit.
57
+ - **Pre-commit hooks rewrite files.** If a hook modifies files, the commit may abort. Re-stage the hook's changes and commit again; if it still fails, surface the hook output rather than using `--no-verify`.
58
+
59
+ ## Verify
60
+
61
+ - `git log -1 --stat` shows your commit with a Conventional Commits subgraph and the expected files.
62
+ - `git status` is clean (or shows only intentionally-unstaged paths).
63
+ - No secret strings appear in `git show HEAD`.
64
+ - For a PR: `gh pr view --json url,title,body` returns the PR with the title, and a body containing `## Summary`, `## Changes`, and `## Test plan`.
65
+ - The current branch is NOT the default branch when a PR was opened.
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: gitops-deploy-workflow
3
+ description: Sets up GitOps delivery with ArgoCD or Flux CD — declarative app definitions, app-of-apps/Kustomize overlays, sync policies, progressive delivery (canary/blue-green), and drift reconciliation. Triggers when configuring ArgoCD/Flux, structuring a GitOps repo, or debugging out-of-sync/drift.
4
+ when_to_use: ตั้ง/แก้ ArgoCD หรือ Flux, จัดโครง GitOps repo, app out-of-sync/drift, ทำ progressive delivery
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ - ตั้งหรือแก้ ArgoCD / Flux CD ครั้งแรก หรือ refactor โครง GitOps repo
10
+ - App ค้าง `OutOfSync` / `Degraded` หรือ manifest จริงใน cluster ถูกแก้มือ (drift)
11
+ - เพิ่ม env ใหม่ (dev/staging/prod) หรือ wire promotion path
12
+ - ทำ progressive delivery (canary / blue-green) แทน rolling update ตรงๆ
13
+
14
+ **SKIP** ถ้าเป็น push-based CI deploy (`kubectl apply` ใน pipeline) — นั่นไม่ใช่ GitOps, อย่าฝืนใส่ controller
15
+
16
+ ## Steps
17
+
18
+ **1. เลือก controller + วาง repo layout ก่อนเขียน manifest แรก**
19
+
20
+ | | ArgoCD | Flux CD |
21
+ |---|---|---|
22
+ | Source of truth | `Application` CRD | `Kustomization` + `GitRepository` CRD |
23
+ | UI | มี (web) | ไม่มี (CLI `flux`) |
24
+ | เหมาะกับ | ทีมที่อยาก see/diff ใน UI | GitOps-native, multi-tenant, น้อย human |
25
+
26
+ โครง repo แยก **config ออกจาก app source** (คนละ repo หรืออย่างน้อยคนละ dir). Per-env overlay ด้วย Kustomize:
27
+ ```
28
+ clusters/<cluster>/ # bootstrap: app-of-apps หรือ Flux Kustomization root
29
+ apps/<app>/base/ # kustomization.yaml + deployment/service/hpa
30
+ apps/<app>/overlays/dev/ # patch image tag, replicas, resources
31
+ apps/<app>/overlays/staging/
32
+ apps/<app>/overlays/prod/
33
+ ```
34
+ **ห้าม** copy-paste manifest เต็มต่อ env — base + `patchesStrategicMerge` เท่านั้น. env ต่างกันที่ image tag / replicas / resource / ingress host
35
+
36
+ **2. App-of-apps (ArgoCD) — root Application ชี้ไปยังโฟลเดอร์ที่มี child Applications**
37
+ - Root `Application.spec.source.path` → dir ที่มี Application manifest ของแต่ละ app
38
+ - Child `Application` แต่ละตัวชี้ overlay ของ env นั้น (`overlays/prod`)
39
+ - ได้ bootstrap ทั้ง cluster ด้วย `kubectl apply -f root-app.yaml` ครั้งเดียว
40
+ - Flux เทียบเท่า: root `Kustomization` ชี้ `clusters/<cluster>/` ที่มี child `Kustomization` per app
41
+
42
+ **3. Sync policy — เปิด automation แบบมี guard**
43
+ - ArgoCD `syncPolicy.automated`: `prune: true` (ลบ resource ที่หายจาก Git), `selfHeal: true` (revert drift ใน cluster), `syncOptions: [CreateNamespace=true]`
44
+ - prod ที่ต้อง gate → ใช้ **manual sync** หรือ sync window แทน auto, อย่าเปิด `selfHeal` ถ้ายังมีคนแก้มือเป็นปกติ (จะตีกัน)
45
+ - Flux: `Kustomization.spec.prune: true` + `interval: 1m` + `wait: true` (รอ health ก่อนถือว่าสำเร็จ)
46
+ - ใส่ `ignoreDifferences` สำหรับ field ที่ controller อื่นเขียน (HPA แก้ `replicas`, webhook inject sidecar) ไม่งั้น OutOfSync ตลอด
47
+
48
+ **4. Env promotion path: dev → staging → prod**
49
+ - Promote = เปลี่ยน image tag ใน overlay ของ env ถัดไป ผ่าน Git commit/PR (ไม่ใช่ `kubectl set image`)
50
+ - ใช้ **digest pinning** (`@sha256:...`) ไม่ใช่ mutable tag เช่น `latest` — กัน drift เงียบเมื่อ tag ถูก repush
51
+ - Auto image update: ArgoCD Image Updater หรือ Flux `ImagePolicy` + `ImageUpdateAutomation` เขียน tag ใหม่กลับ Git ให้ (write-back ต้องมี deploy key/PAT แบบ write)
52
+ - prod ผ่าน PR + required review เสมอ; dev auto-promote ได้
53
+
54
+ **5. Progressive delivery — แทน rolling update**
55
+ - ArgoCD → **Argo Rollouts**: เปลี่ยน `Deployment` เป็น `Rollout`, ใส่ `strategy.canary.steps` (`setWeight` + `pause`) หรือ `strategy.blueGreen` (`activeService`/`previewService`)
56
+ - Flux → **Flagger**: สร้าง `Canary` CRD ชี้ target Deployment, กำหนด `analysis.metrics` (success-rate/latency จาก Prometheus) + `stepWeight` + `threshold`
57
+ - ผูก analysis เข้า metric จริง — canary ที่ไม่มี metric gate = blue-green ปลอม. ตั้ง `maxWeight`/`threshold` ให้ rollback อัตโนมัติเมื่อ error rate เกิน
58
+
59
+ **6. RBAC + secrets — ห้าม commit plain secret ลง Git เด็ดขาด**
60
+ - ใช้ **Sealed Secrets** (encrypt ด้วย controller public key, commit ciphertext ได้) หรือ **External Secrets Operator** (sync จาก Vault/cloud secret manager) หรือ SOPS + age/KMS
61
+ - ArgoCD RBAC: `policy.csv` map group → `role:` + จำกัด `applications, sync` ต่อ project; ใช้ `AppProject` คุม source repo/dest namespace ที่อนุญาต
62
+ - Flux: multi-tenancy ผ่าน `Kustomization.spec.serviceAccountName` + impersonation, แยก namespace ต่อ tenant
63
+ - ถ้าเจอ secret ดิบใน manifest → หยุด, แจ้ง, แนะนำ rotate ทันที (committed = leaked แม้ลบทีหลัง)
64
+
65
+ **7. Verify sync + health** (ดู ## Verify)
66
+
67
+ ## Common Errors
68
+
69
+ - **OutOfSync ไม่หาย แต่ diff ว่าง** → controller (HPA/sidecar injector/webhook) เขียน field ที่ Git ไม่มี → เพิ่ม `ignoreDifferences` (ArgoCD) หรือ exclude ใน Flux, อย่า force replicas ใน manifest
70
+ - **selfHeal ตีกับ HPA** → manifest hardcode `replicas` + auto-sync revert ค่าที่ HPA ตั้ง ทุก loop → ลบ `replicas` ออกจาก manifest หรือ ignore field นั้น
71
+ - **`ComparisonError: app path does not exist`** → `source.path` ผิด หรือ `targetRevision` ชี้ branch/tag ที่ไม่มีไฟล์ — เช็ก path สัมพัทธ์จาก repo root, ไม่ใช่จาก cluster dir
72
+ - **Prune ลบ resource ที่ไม่ได้ตั้งใจ** → resource ถูกสร้างนอก Git (manual / helm hook) → mark `Prune=false` annotation หรือย้ายเข้า Git; อย่าเปิด prune ครั้งแรกบน cluster ที่มี resource เก่าค้าง — dry-run ก่อน
73
+ - **Image tag `latest` → ไม่ trigger sync** → Git ไม่เปลี่ยน controller ไม่ rollout แม้ image ใหม่ → pin digest หรือใช้ Image Updater เขียน tag กลับ Git
74
+ - **Flux reconcile เงียบ ไม่ apply** → `GitRepository` auth fail (deploy key) หรือ `Kustomization` `dependsOn` ค้างที่ dependency ยัง not-ready — เช็ก `flux get sources git` ก่อน
75
+ - **Canary ไม่ promote / ค้าง progressing** → metric provider (Prometheus) ไม่ตอบ หรือ query คืน no-data → Flagger ตีเป็น fail → verify metric query แยกก่อนผูก
76
+ - **Secret ใน Git history** → ลบไฟล์ไม่พอ, ยังอยู่ใน history → rotate ค่าจริง + ใช้ Sealed/External Secrets แทน
77
+
78
+ ## Verify
79
+
80
+ ```bash
81
+ # ArgoCD
82
+ argocd app get <app> # Sync Status = Synced, Health = Healthy
83
+ argocd app diff <app> # ต้องว่าง (no diff = no drift)
84
+ argocd app sync <app> --dry-run # ก่อน sync จริงครั้งแรก/หลังเปิด prune
85
+ kubectl get applications -n argocd # ทุก app ไม่ค้าง OutOfSync/Degraded
86
+
87
+ # Flux
88
+ flux get sources git # READY=True, ไม่มี auth error
89
+ flux get kustomizations # READY=True, ไม่ค้าง dependency
90
+ flux get helmreleases # ถ้าใช้ helm
91
+ flux reconcile kustomization <name> --with-source # force reconcile + ดูผล
92
+
93
+ # Progressive delivery
94
+ kubectl argo rollouts get rollout <name> # canary steps + weight
95
+ kubectl describe canary <name> # Flagger: phase Succeeded, ไม่ใช่ Failed
96
+ ```
97
+ **ผ่านเมื่อ:** Synced/Healthy ทุก app · `app diff` ว่าง · ไม่มี plain secret ใน Git (grep `password:|token:|sk-|AKIA|BEGIN PRIVATE KEY` ใน repo ต้องไม่เจอ) · canary promote ถึง 100% หรือ rollback อัตโนมัติเมื่อ metric เกิน threshold (ทดสอบด้วย bad image ครั้งหนึ่ง) · selfHeal revert drift ได้จริง (ลอง `kubectl edit` resource แล้วดูมัน revert)
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: implement-from-design
3
+ description: Translates a design (Figma/screenshot/mockup) into pixel-faithful, responsive, token-driven frontend code, then visually diffs the result; used when building UI from a visual spec.
4
+ when_to_use: When the user provides a Figma link, screenshot, or mockup and wants it built as a faithful, responsive component/page — and wants the output visually verified against the design.
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ Invoke when the deliverable is **code that reproduces a given visual spec** and you have a concrete source to match against:
10
+
11
+ - A Figma link/frame, a screenshot, or a static mockup image of a component or page.
12
+ - The user says "build this", "implement this design", "make it match", "pixel-perfect", or attaches an image.
13
+ - Faithfulness matters — the output will be compared back to the source.
14
+
15
+ Do NOT use when:
16
+ - There is **no source design** and you're inventing one → use `frontend-design` instead (this skill reproduces; it does not originate).
17
+ - The work is logic/data only with no visual surface, or a pure refactor with no rendered change.
18
+
19
+ This skill orchestrates. It hands structure off to `build-react-component` and styling off to `style-responsive-tailwind`, then owns the **extract → map → build → visual-diff** loop around them.
20
+
21
+ ## Steps
22
+
23
+ 1. **Extract the design intent into explicit values — never eyeball into code.** Read the source and write down, as a short table, the actual numbers before touching the editor:
24
+ - **Spacing scale** — list every gap/padding/margin you see; cluster them (e.g. 4/8/12/16/24/32) so you reuse a scale instead of 13 one-off pixel values.
25
+ - **Type ramp** — for each text role: font-family, size, weight, line-height, letter-spacing.
26
+ - **Color tokens** — every fill/border/text color as hex; note semantic role (bg, surface, border, text-primary, text-muted, accent).
27
+ - **Radii / shadows / borders** — corner radii, each box-shadow (x/y/blur/spread/color), border widths.
28
+ - **Breakpoints & layout** — what reflows, stacks, hides, or resizes between mobile/tablet/desktop. If only one viewport is given, infer the responsive intent and state your assumption.
29
+ - For Figma: pull exact values from the inspect/dev panel (or Figma MCP `get_design_context` + `get_screenshot`), don't trace from a screenshot. For a raw screenshot: sample colors with a picker, measure spacing against known reference elements — do not guess hex from memory.
30
+
31
+ 2. **Map every extracted value to existing tokens — reuse, never invent.** Open the project's `tailwind.config`/theme, CSS custom properties, and design-system components FIRST. For each extracted value, bind it to the nearest existing token (`bg-surface`, `text-muted`, `rounded-lg`, `shadow-card`, `space-y-4`). Only add a new token when nothing within ~1–2px/one weight step exists, and add it to the theme config — never hardcode an arbitrary `#3B7AF2` or `gap-[13px]` inline. If the project has a component for it (button, input, card, badge), extend that component rather than rebuilding the markup.
32
+
33
+ 3. **Build the structure** by handing off to `build-react-component`: semantic HTML, correct element nesting matching the design's visual hierarchy, props for the variable parts (text, state, slots), accessible roles/labels. Keep markup minimal — one wrapper per visual group the design actually shows, no decorative `<div>` soup.
34
+
35
+ 4. **Apply styling** by handing off to `style-responsive-tailwind`, feeding it the token map from step 2. Translate the design's auto-layout/flex/grid into Tailwind layout utilities. Match spacing, type, color, radius, shadow to the mapped tokens — not to raw extracted pixels.
36
+
37
+ 5. **Implement the responsive states the design implies.** Build mobile-first, then layer `md:`/`lg:` for the breakpoints from step 1. Reproduce every reflow the design shows (stack→row, sidebar collapse, grid column count, font-size step). If a viewport wasn't designed, make a sensible scale and leave a `// responsive: tablet inferred` comment — don't silently ship an untested breakpoint as if it were specified.
38
+
39
+ 6. **Add micro-interactions/animation only where the design specifies them.** Hover/active/focus/disabled states, transitions, and motion go in **only if the source shows them** (a hover variant, a motion note, an obvious affordance). Match the designed easing/duration. Do not sprinkle gradient glows, scale-on-hover, fade-ins, or "delight" the design didn't ask for — that's the #1 source of AI-slop drift away from the spec.
40
+
41
+ 7. **Visual-diff loop — render, screenshot, compare, iterate until faithful.** This is the verification gate, not optional polish:
42
+ - Run the app/Storybook (`npm run dev` / `npm run storybook`) and get the component's URL.
43
+ - `mcp__chrome-devtools__navigate_page` to it. For each designed breakpoint set the viewport with `mcp__chrome-devtools__emulate` (e.g. `viewport: "390x844x3,mobile"` for phone, `"1440x900x1"` for desktop) or `resize_page`.
44
+ - `mcp__chrome-devtools__take_screenshot` (set `format: "png"`, `fullPage: true` for pages) at each breakpoint, saving to a `filePath`.
45
+ - Open the screenshot next to the source mock and compare **spacing, alignment, type, color, radius, shadow** point by point. Note every mismatch as a concrete delta ("card padding 24px in design, 16px rendered"; "heading 600 in design, 700 rendered").
46
+ - Fix the specific deltas, re-screenshot, repeat. Stop when nothing material differs — not after the first render.
47
+
48
+ ## Common Errors
49
+
50
+ - **Coding straight from a screenshot without extracting values.** You end up with vibes-based spacing and invented hex colors. Always do step 1's value table first; for Figma always read inspect/dev-mode numbers.
51
+ - **Inventing tokens that already exist.** Hardcoding `gap-[13px]`, `text-[#1F2937]`, `rounded-[10px]` when `gap-3`, `text-gray-800`, `rounded-lg` are right there. Map to the theme first; arbitrary `[...]` values are a smell.
52
+ - **Treating Figma's exported React/Tailwind as final code.** The export is a *representation of the design*, not house style — it's verbose, absolute-positioned, and ignores your component library. Re-author it into project conventions.
53
+ - **AI-slop additions.** Adding gradients, glow shadows, hover-scale, fade-in animations, rounded-everything, or emoji the design never contained. If it's not in the source, it doesn't ship. Reproduce, don't embellish.
54
+ - **Absolute positioning to "match pixels."** Copying Figma's x/y into `absolute` coordinates produces a layout that shatters at any other width. Use flow layout (flex/grid) that reproduces the *relationship*, not the coordinates.
55
+ - **Declaring done after one render.** First render is never faithful. Skipping the iterate part of the visual-diff loop is how 90%-right ships as "matched."
56
+ - **Only checking one viewport.** The desktop looks perfect, mobile overflows. Screenshot every designed breakpoint, not just the one you developed in.
57
+ - **Stale dev server / cache.** Editing styles but screenshotting an old build → phantom mismatches. Confirm HMR applied (or `navigate_page` with `ignoreCache`) before trusting a diff.
58
+ - **Drift from forgotten interactive states.** Design shows a hover/disabled variant; you build only the default. Re-screenshot with the state triggered (or via the component's prop) to verify those too.
59
+
60
+ ## Verify
61
+
62
+ - [ ] Step-1 value table exists (spacing, type ramp, colors, radii/shadows, breakpoints) and every code value traces back to it.
63
+ - [ ] No arbitrary `[...]` Tailwind values or inline hex/px that should be a token; new tokens (if any) were added to the theme config, not hardcoded.
64
+ - [ ] Existing design-system components were reused/extended where one fit, instead of re-implemented.
65
+ - [ ] Layout uses flow (flex/grid), not absolute coordinates copied from the design tool.
66
+ - [ ] Every breakpoint the design implies is implemented and was screenshotted; reflows match.
67
+ - [ ] Only the interactions/animations present in the source were added — nothing extra.
68
+ - [ ] Visual-diff ran via chrome-devtools at each breakpoint; the final screenshots were compared point-by-point to the source and remaining deltas are zero or explicitly justified in a code comment.
69
+ - [ ] Saved before/after screenshots (or their paths) are available as evidence — not just a "looks good" claim.