@warnyin/agents 0.10.0 → 0.11.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 (78) hide show
  1. package/CHANGELOG.md +115 -110
  2. package/README.md +148 -148
  3. package/package.json +38 -38
  4. package/src/.claude/agents/warnyin-infra.md +13 -13
  5. package/src/.claude/agents/warnyin-qa.md +13 -13
  6. package/src/.claude/agents/warnyin-sa.md +13 -13
  7. package/src/.claude/agents/warnyin-security.md +13 -13
  8. package/src/.claude/agents/warnyin-tech-lead.md +13 -13
  9. package/src/.claude/commands/warnyin/build.md +31 -30
  10. package/src/.claude/commands/warnyin/design.md +27 -26
  11. package/src/.claude/commands/warnyin/discovery.md +17 -17
  12. package/src/.claude/commands/warnyin/explore.md +14 -14
  13. package/src/.claude/commands/warnyin/init.md +12 -12
  14. package/src/.claude/commands/warnyin/install-skill.md +14 -14
  15. package/src/.claude/commands/warnyin/next.md +17 -17
  16. package/src/.claude/commands/warnyin/ship.md +28 -28
  17. package/src/.claude/commands/warnyin/update-codemaps.md +12 -12
  18. package/src/.claude/commands/warnyin/verify.md +20 -20
  19. package/src/.claude/skills/explore/SKILL.md +8 -8
  20. package/src/.claude/skills/next/SKILL.md +8 -8
  21. package/src/.claude/skills/update-codemaps/SKILL.md +8 -8
  22. package/src/.warnyin/installer/templates/CLAUDE.md +29 -29
  23. package/src/.warnyin/template/docs/codemap/index.md +18 -18
  24. package/src/.warnyin/template/docs/features/[feature-name]/business.md +5 -5
  25. package/src/.warnyin/template/docs/features/[feature-name]/feature.md +5 -5
  26. package/src/.warnyin/template/docs/features/[feature-name]/spec.md +16 -16
  27. package/src/.warnyin/template/docs/infra.md +16 -16
  28. package/src/.warnyin/template/docs/project.md +18 -18
  29. package/src/.warnyin/template/docs/rule.md +7 -7
  30. package/src/.warnyin/template/docs/techstack/[component]/about.md +6 -6
  31. package/src/.warnyin/template/docs/techstack/[component]/rule.md +6 -6
  32. package/src/.warnyin/template/docs/techstack/[component]/standard.md +6 -6
  33. package/src/.warnyin/template/docs/techstack/[component]/structure.md +7 -7
  34. package/src/.warnyin/template/docs/techstack/[component]/test.md +7 -7
  35. package/src/.warnyin/template/docs/troubleshooting.md +32 -32
  36. package/src/.warnyin/template/stages/[topic]/build.md +58 -58
  37. package/src/.warnyin/template/stages/[topic]/business.md +21 -21
  38. package/src/.warnyin/template/stages/[topic]/design.md +63 -57
  39. package/src/.warnyin/template/stages/[topic]/discovery.md +69 -69
  40. package/src/.warnyin/template/stages/[topic]/proposal.md +43 -43
  41. package/src/.warnyin/template/stages/[topic]/research.md +49 -49
  42. package/src/.warnyin/template/stages/[topic]/ship.md +32 -32
  43. package/src/.warnyin/template/stages/[topic]/tasks/[task-name]/issue.md +19 -19
  44. package/src/.warnyin/template/stages/[topic]/tasks/[task-name]/rule.md +13 -13
  45. package/src/.warnyin/template/stages/[topic]/tasks/[task-name]/spec.md +36 -36
  46. package/src/.warnyin/template/stages/[topic]/tasks/[task-name]/standard.md +21 -21
  47. package/src/.warnyin/template/stages/[topic]/tasks/[task-name]/task.md +40 -39
  48. package/src/.warnyin/template/stages/[topic]/test.md +46 -46
  49. package/src/.warnyin/template/stages/[topic]/troubleshooting.md +34 -34
  50. package/src/.warnyin/template/stages/[topic]/verify.md +44 -44
  51. package/src/.warnyin/workflow/README.md +101 -101
  52. package/src/.warnyin/workflow/api-doc.md +93 -93
  53. package/src/.warnyin/workflow/codemap.md +91 -91
  54. package/src/.warnyin/workflow/contexts/README.md +51 -49
  55. package/src/.warnyin/workflow/contexts/build.md +25 -25
  56. package/src/.warnyin/workflow/contexts/research.md +25 -25
  57. package/src/.warnyin/workflow/contexts/review.md +25 -25
  58. package/src/.warnyin/workflow/explore.md +32 -32
  59. package/src/.warnyin/workflow/init.md +125 -125
  60. package/src/.warnyin/workflow/next.md +48 -48
  61. package/src/.warnyin/workflow/roles/README.md +47 -47
  62. package/src/.warnyin/workflow/roles/ba.md +25 -25
  63. package/src/.warnyin/workflow/roles/developer.md +31 -30
  64. package/src/.warnyin/workflow/roles/infra.md +24 -24
  65. package/src/.warnyin/workflow/roles/po.md +28 -28
  66. package/src/.warnyin/workflow/roles/qa.md +35 -35
  67. package/src/.warnyin/workflow/roles/sa.md +28 -28
  68. package/src/.warnyin/workflow/roles/security.md +39 -39
  69. package/src/.warnyin/workflow/roles/tech-lead.md +28 -28
  70. package/src/.warnyin/workflow/scripts/build-wave.mjs +143 -125
  71. package/src/.warnyin/workflow/scripts/validate-topic.mjs +378 -378
  72. package/src/.warnyin/workflow/stages/build.md +98 -98
  73. package/src/.warnyin/workflow/stages/design.md +126 -122
  74. package/src/.warnyin/workflow/stages/discovery.md +78 -78
  75. package/src/.warnyin/workflow/stages/ship.md +92 -92
  76. package/src/.warnyin/workflow/stages/verify.md +80 -80
  77. package/src/AGENTS.md +48 -48
  78. package/src/bin/cli.mjs +193 -193
package/src/AGENTS.md CHANGED
@@ -1,48 +1,48 @@
1
- # AGENTS.md
2
-
3
- มาตรฐานเปิดสำหรับ AI agent ทุกเจ้าที่อ่านไฟล์นี้ (Codex, Antigravity, และเครื่องมืออื่นที่รองรับ `AGENTS.md`)
4
-
5
- ## repo นี้คืออะไร
6
-
7
- repo มาตรฐานกลางของ **ways of work** สำหรับทุกโปรเจกต์ — เดินงานผ่าน 5 stage:
8
-
9
- ```
10
- Discovery (optional) ──▶ DESIGN ──▶ BUILD ──▶ VERIFY ──▶ SHIP
11
- ```
12
-
13
- ## กฎสำคัญ: ทำตาม playbook กลางเสมอ
14
-
15
- แก่นของแต่ละ stage คือ **single source of truth** อยู่ที่ `.warnyin/workflow/stages/`
16
- ก่อนทำงานใน stage ใด ให้เปิดอ่านไฟล์ playbook ของ stage นั้นแล้วทำตามอย่างเคร่งครัด
17
-
18
- | Stage | playbook | สถานะ |
19
- |---|---|---|
20
- | Discovery | `.warnyin/workflow/stages/discovery.md` | ✅ พร้อมใช้ |
21
- | DESIGN | `.warnyin/workflow/stages/design.md` | ✅ พร้อมใช้ |
22
- | BUILD | `.warnyin/workflow/stages/build.md` | ✅ พร้อมใช้ |
23
- | VERIFY | `.warnyin/workflow/stages/verify.md` | ✅ พร้อมใช้ |
24
- | SHIP | `.warnyin/workflow/stages/ship.md` | ✅ พร้อมใช้ |
25
-
26
- ## วิธีเริ่ม
27
-
28
- 0. ครั้งแรกในโปรเจกต์ (docs/ ยังว่าง) → ทำตาม `.warnyin/workflow/init.md` เพื่อวิเคราะห์โปรเจกต์ + เติม `docs/` ก่อน
29
- 1. อ่าน `.warnyin/workflow/README.md` เพื่อเข้าใจภาพรวมและโครงสร้าง
30
- 2. งานใหม่ → copy `.warnyin/template/stages/[topic]/` เป็น `docs/stages/<slug>/`
31
- 3. รัน stage ตามลำดับ โดยทำตาม playbook ของแต่ละ stage
32
- 4. output ของงานเก็บใน `docs/stages/<slug>/`, ความรู้ถาวรระดับโปรเจกต์อยู่ใน `docs/`
33
-
34
- ## สำรวจโดยไม่สร้าง artifact (EXPLORE)
35
-
36
- อยากถาม/สำรวจข้อมูลเฉยๆ โดยไม่เปิด topic → ทำตาม `.warnyin/workflow/explore.md`
37
- (read-only เด็ดขาด — ไม่สร้าง/แก้ไฟล์ใดๆ จบที่คำตอบในแชท)
38
-
39
- ## เช็คงานค้าง / หาขั้นตอนถัดไป (NEXT)
40
-
41
- อยากรู้ว่ามีงานอะไรค้างและควรไปต่อยังไง → ทำตาม `.warnyin/workflow/next.md`
42
- (สแกน `docs/stages/*` ระบุ stage ปัจจุบันจาก artifact จริง + gate ที่ขาด — read-only ไม่แก้ไฟล์)
43
-
44
- ## รัน Discovery
45
-
46
- ทำตาม `.warnyin/workflow/stages/discovery.md` — เริ่มอ่าน `docs/project.md`, ตี scope กว้าง→แคบ,
47
- ถามทีละข้อพร้อมเสนอคำตอบที่แนะนำ, คำถามที่ตอบได้ด้วยโค้ดให้ไปอ่านโค้ดเอง,
48
- เขียน output ลง `docs/stages/<slug>/discovery.md` และ `research.md`
1
+ # AGENTS.md
2
+
3
+ มาตรฐานเปิดสำหรับ AI agent ทุกเจ้าที่อ่านไฟล์นี้ (Codex, Antigravity, และเครื่องมืออื่นที่รองรับ `AGENTS.md`)
4
+
5
+ ## repo นี้คืออะไร
6
+
7
+ repo มาตรฐานกลางของ **ways of work** สำหรับทุกโปรเจกต์ — เดินงานผ่าน 5 stage:
8
+
9
+ ```
10
+ Discovery (optional) ──▶ DESIGN ──▶ BUILD ──▶ VERIFY ──▶ SHIP
11
+ ```
12
+
13
+ ## กฎสำคัญ: ทำตาม playbook กลางเสมอ
14
+
15
+ แก่นของแต่ละ stage คือ **single source of truth** อยู่ที่ `.warnyin/workflow/stages/`
16
+ ก่อนทำงานใน stage ใด ให้เปิดอ่านไฟล์ playbook ของ stage นั้นแล้วทำตามอย่างเคร่งครัด
17
+
18
+ | Stage | playbook | สถานะ |
19
+ |---|---|---|
20
+ | Discovery | `.warnyin/workflow/stages/discovery.md` | ✅ พร้อมใช้ |
21
+ | DESIGN | `.warnyin/workflow/stages/design.md` | ✅ พร้อมใช้ |
22
+ | BUILD | `.warnyin/workflow/stages/build.md` | ✅ พร้อมใช้ |
23
+ | VERIFY | `.warnyin/workflow/stages/verify.md` | ✅ พร้อมใช้ |
24
+ | SHIP | `.warnyin/workflow/stages/ship.md` | ✅ พร้อมใช้ |
25
+
26
+ ## วิธีเริ่ม
27
+
28
+ 0. ครั้งแรกในโปรเจกต์ (docs/ ยังว่าง) → ทำตาม `.warnyin/workflow/init.md` เพื่อวิเคราะห์โปรเจกต์ + เติม `docs/` ก่อน
29
+ 1. อ่าน `.warnyin/workflow/README.md` เพื่อเข้าใจภาพรวมและโครงสร้าง
30
+ 2. งานใหม่ → copy `.warnyin/template/stages/[topic]/` เป็น `docs/stages/<slug>/`
31
+ 3. รัน stage ตามลำดับ โดยทำตาม playbook ของแต่ละ stage
32
+ 4. output ของงานเก็บใน `docs/stages/<slug>/`, ความรู้ถาวรระดับโปรเจกต์อยู่ใน `docs/`
33
+
34
+ ## สำรวจโดยไม่สร้าง artifact (EXPLORE)
35
+
36
+ อยากถาม/สำรวจข้อมูลเฉยๆ โดยไม่เปิด topic → ทำตาม `.warnyin/workflow/explore.md`
37
+ (read-only เด็ดขาด — ไม่สร้าง/แก้ไฟล์ใดๆ จบที่คำตอบในแชท)
38
+
39
+ ## เช็คงานค้าง / หาขั้นตอนถัดไป (NEXT)
40
+
41
+ อยากรู้ว่ามีงานอะไรค้างและควรไปต่อยังไง → ทำตาม `.warnyin/workflow/next.md`
42
+ (สแกน `docs/stages/*` ระบุ stage ปัจจุบันจาก artifact จริง + gate ที่ขาด — read-only ไม่แก้ไฟล์)
43
+
44
+ ## รัน Discovery
45
+
46
+ ทำตาม `.warnyin/workflow/stages/discovery.md` — เริ่มอ่าน `docs/project.md`, ตี scope กว้าง→แคบ,
47
+ ถามทีละข้อพร้อมเสนอคำตอบที่แนะนำ, คำถามที่ตอบได้ด้วยโค้ดให้ไปอ่านโค้ดเอง,
48
+ เขียน output ลง `docs/stages/<slug>/discovery.md` และ `research.md`
package/src/bin/cli.mjs CHANGED
@@ -1,193 +1,193 @@
1
- #!/usr/bin/env node
2
- /**
3
- * warnyin-agents installer
4
- * ติดตั้ง Warnyin Standard Workflow ลงโปรเจกต์ปัจจุบัน (cwd)
5
- *
6
- * npx @warnyin/agents ติดตั้ง (ข้ามไฟล์ที่มีอยู่แล้ว)
7
- * npx @warnyin/agents --update อัปเดต playbook กลาง (เขียนทับเฉพาะไฟล์ core)
8
- * npx @warnyin/agents --dry-run แสดงว่าจะทำอะไร โดยไม่เขียนไฟล์จริง
9
- * (ทางสำรองไม่ผ่าน npm: npx github:warnyin/warnyin-agents)
10
- */
11
- import fs from 'node:fs'
12
- import path from 'node:path'
13
- import { fileURLToPath } from 'node:url'
14
-
15
- const pkgRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
16
- const target = process.cwd()
17
- const args = new Set(process.argv.slice(2))
18
- const UPDATE = args.has('--update')
19
- const DRY = args.has('--dry-run')
20
-
21
- if (args.has('--help') || args.has('-h')) {
22
- console.log(`warnyin-agents — ติดตั้ง Warnyin Standard Workflow ลงโปรเจกต์ปัจจุบัน
23
-
24
- ใช้งาน:
25
- npx @warnyin/agents ติดตั้ง (ข้ามไฟล์ที่มีอยู่แล้ว ไม่เขียนทับ)
26
- npx @warnyin/agents --update อัปเดต playbook กลางเป็นเวอร์ชันล่าสุด
27
- (เขียนทับเฉพาะ .warnyin/workflow/, .claude/commands/warnyin/,
28
- template .warnyin/template/stages/[topic] — ไม่แตะ docs/ และงานจริง)
29
- npx @warnyin/agents --dry-run แสดงรายการไฟล์ที่จะสร้าง/อัปเดต โดยไม่เขียนจริง
30
-
31
- หลังติดตั้ง: เปิด Claude Code ในโปรเจกต์ แล้วรัน /warnyin:init ให้ agent วิเคราะห์โปรเจกต์ + เติม docs/`)
32
- process.exit(0)
33
- }
34
-
35
- // guard กัน self-install — เก็บไว้แบบ defensive (zero-cost)
36
- // หลังย้าย source เข้า src/ → pkgRoot = src/ (sibling ของ bin/) จึงแทบไม่มีทาง === target (repo root / temp sandbox)
37
- // → guard นี้เป็น no-op โดยตั้งใจในเคสปกติ/sandbox; ยังคงดักได้เฉพาะ edge case ที่ install ลงโฟลเดอร์ที่เป็น src/ เอง
38
- if (path.resolve(pkgRoot) === path.resolve(target)) {
39
- console.error('✖ กำลังรันอยู่ใน repo ของ warnyin-agents เอง — ให้ cd ไปที่โปรเจกต์ปลายทางก่อน')
40
- process.exit(1)
41
- }
42
-
43
- // โครงเก่า (≤0.2.x): workflow/ + warnyin-stages/ ที่ root — เตือนให้ย้ายเอง ไม่แตะงานจริงของ user
44
- const legacyV2 = ['workflow', 'warnyin-stages'].filter((d) => fs.existsSync(path.join(target, d)))
45
- if (legacyV2.length) {
46
- console.warn(`⚠ พบโครงเลย์เอาต์เก่า (≤0.2.x): ${legacyV2.join(', ')}
47
- เวอร์ชันนี้ย้าย core ไปใต้ .warnyin/ และงานจริงไป docs/stages/ — แนะนำย้ายด้วยตัวเองก่อน:
48
- 1. mkdir -p docs/stages && git mv warnyin-stages/* docs/stages/ # งานจริงของคุณ (ปลอดภัย ไม่ถูกแตะโดย installer)
49
- 2. rm -rf workflow warnyin-stages # core เก่า + โฟลเดอร์ที่ย้ายของออกแล้ว
50
- แล้วรันคำสั่งนี้อีกครั้ง\n`)
51
- }
52
-
53
- // โครงเก่า (0.3–0.5.x): ทุกอย่างอยู่ใต้ warnyin/ ที่ root — เวอร์ชันนี้แยกเป็น .warnyin/ (core) + docs/stages (งานจริง)
54
- const legacyV5 = ['workflow', 'template', 'installer', 'stages'].filter((d) =>
55
- fs.existsSync(path.join(target, 'warnyin', d)),
56
- )
57
- if (legacyV5.length) {
58
- console.warn(`⚠ พบโครงเลย์เอาต์เก่า (0.3–0.5.x): warnyin/{${legacyV5.join(', ')}}
59
- เวอร์ชันนี้ย้าย core ไป .warnyin/ และงานจริงไป docs/stages/ — แนะนำย้ายด้วยตัวเองก่อน:
60
- 1. mkdir -p docs/stages && git mv warnyin/stages/* docs/stages/ # งานจริงของคุณ (active + achieved) — ปลอดภัย ไม่ถูกแตะ
61
- 2. rm -rf warnyin # core เก่าทั้งหมด (เวอร์ชันใหม่ installer จะวางที่ .warnyin/)
62
- แล้วรันคำสั่งนี้อีกครั้ง — installer จะวาง .warnyin/ ชุดใหม่ให้\n`)
63
- }
64
-
65
- // core = playbook กลาง + command + agent + template — เขียนทับได้เมื่อ --update
66
- const CORE = [
67
- path.join('.warnyin', 'workflow'),
68
- path.join('.warnyin', 'template'),
69
- path.join('.claude', 'commands', 'warnyin'),
70
- path.join('.claude', 'agents'),
71
- path.join('.claude', 'skills'),
72
- ]
73
- // scaffold = พื้นที่ทำงานเปล่าของโปรเจกต์ — installer "สร้างเอง" ไม่ copy tree จาก package
74
- // (สำคัญ: ถ้า copy docs/stages จาก pkgRoot งานจริงของ repo ต้นทางจะรั่วไป target ทุกครั้ง — ดู verify installer-test-ci)
75
- const SCAFFOLD_FILES = [
76
- path.join('docs', 'stages', 'context.md'), // บริบทงานที่จดไว้ (next/discovery/explore อ่าน "ถ้ามี")
77
- path.join('docs', 'stages', 'achieved', '.gitkeep'), // ให้ git track โฟลเดอร์ archive เปล่า
78
- ]
79
-
80
- const stats = { created: 0, updated: 0, skipped: 0 }
81
-
82
- function copyTree(relDir, { overwrite }) {
83
- const srcDir = path.join(pkgRoot, relDir)
84
- if (!fs.existsSync(srcDir)) return
85
- for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
86
- const rel = path.join(relDir, entry.name)
87
- if (entry.isDirectory()) {
88
- copyTree(rel, { overwrite })
89
- continue
90
- }
91
- const src = path.join(pkgRoot, rel)
92
- const dest = path.join(target, rel)
93
- const exists = fs.existsSync(dest)
94
- if (exists && !overwrite) {
95
- stats.skipped++
96
- continue
97
- }
98
- if (exists && overwrite && fs.readFileSync(src).equals(fs.readFileSync(dest))) {
99
- stats.skipped++
100
- continue
101
- }
102
- if (!DRY) {
103
- fs.mkdirSync(path.dirname(dest), { recursive: true })
104
- fs.copyFileSync(src, dest)
105
- }
106
- stats[exists ? 'updated' : 'created']++
107
- console.log(` ${exists ? '↻' : '+'} ${rel}`)
108
- }
109
- }
110
-
111
- /** สร้างโครง scaffold เปล่าของ docs/stages เอง (ไม่ copy จาก package — กันงานจริงของ repo ต้นทางรั่วไป target) — ไม่ทับไฟล์ที่มีอยู่ */
112
- function ensureScaffold() {
113
- for (const rel of SCAFFOLD_FILES) {
114
- const dest = path.join(target, rel)
115
- if (fs.existsSync(dest)) {
116
- stats.skipped++
117
- continue
118
- }
119
- if (!DRY) {
120
- fs.mkdirSync(path.dirname(dest), { recursive: true })
121
- fs.writeFileSync(dest, '')
122
- }
123
- stats.created++
124
- console.log(` + ${rel}`)
125
- }
126
- }
127
-
128
- /** seed docs/ ของโปรเจกต์จาก .warnyin/template/docs — ข้ามโฟลเดอร์ template `[...]` (ไว้ให้ /warnyin:init copy เป็นชื่อจริง) และไม่ทับไฟล์ที่มีอยู่ */
129
- const TEMPLATE_DOCS = path.join('.warnyin', 'template', 'docs')
130
- function seedDocs(relDir = TEMPLATE_DOCS) {
131
- const srcDir = path.join(pkgRoot, relDir)
132
- if (!fs.existsSync(srcDir)) return
133
- for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
134
- if (entry.name.startsWith('[')) continue
135
- const rel = path.join(relDir, entry.name)
136
- if (entry.isDirectory()) {
137
- seedDocs(rel)
138
- continue
139
- }
140
- const destRel = path.join('docs', path.relative(TEMPLATE_DOCS, rel))
141
- const dest = path.join(target, destRel)
142
- if (fs.existsSync(dest)) {
143
- stats.skipped++
144
- continue
145
- }
146
- if (!DRY) {
147
- fs.mkdirSync(path.dirname(dest), { recursive: true })
148
- fs.copyFileSync(path.join(pkgRoot, rel), dest)
149
- }
150
- stats.created++
151
- console.log(` + ${destRel}`)
152
- }
153
- }
154
-
155
- /** CLAUDE.md / AGENTS.md: ไม่มี → สร้างจาก template; มีอยู่แล้วแต่ยังไม่มี workflow → ต่อท้ายเป็น section */
156
- function installRootDoc(name, srcPath) {
157
- const dest = path.join(target, name)
158
- const content = fs.readFileSync(srcPath, 'utf8')
159
- if (!fs.existsSync(dest)) {
160
- if (!DRY) fs.writeFileSync(dest, content)
161
- stats.created++
162
- console.log(` + ${name}`)
163
- return
164
- }
165
- const existing = fs.readFileSync(dest, 'utf8')
166
- if (existing.includes('warnyin/workflow/stages/')) {
167
- stats.skipped++
168
- return
169
- }
170
- const section = '\n\n' + content.replace(/^#\s[^\n]*\n/, '## Warnyin Standard Workflow\n')
171
- if (!DRY) fs.appendFileSync(dest, section)
172
- stats.updated++
173
- console.log(` ± ${name} (ต่อท้าย section Warnyin Standard Workflow)`)
174
- }
175
-
176
- console.log(`Warnyin Standard Workflow → ${target}${DRY ? ' (dry-run)' : ''}\n`)
177
-
178
- for (const dir of CORE) copyTree(dir, { overwrite: UPDATE })
179
- ensureScaffold()
180
- seedDocs()
181
- installRootDoc('CLAUDE.md', path.join(pkgRoot, '.warnyin', 'installer', 'templates', 'CLAUDE.md'))
182
- installRootDoc('AGENTS.md', path.join(pkgRoot, 'AGENTS.md'))
183
-
184
- console.log(`\nสรุป: สร้างใหม่ ${stats.created} · อัปเดต ${stats.updated} · ข้าม (มีอยู่แล้ว) ${stats.skipped}`)
185
-
186
- if (!UPDATE) {
187
- console.log(`
188
- ขั้นถัดไป:
189
- 1. เปิด Claude Code ในโปรเจกต์นี้ แล้วรัน /warnyin:init — ให้ agent วิเคราะห์โปรเจกต์ + เติม docs/
190
- 2. เริ่มงานแรก: /warnyin:discovery <topic> หรือ /warnyin:design <slug> <change>
191
-
192
- อัปเดต playbook ภายหลัง: npx @warnyin/agents --update`)
193
- }
1
+ #!/usr/bin/env node
2
+ /**
3
+ * warnyin-agents installer
4
+ * ติดตั้ง Warnyin Standard Workflow ลงโปรเจกต์ปัจจุบัน (cwd)
5
+ *
6
+ * npx @warnyin/agents ติดตั้ง (ข้ามไฟล์ที่มีอยู่แล้ว)
7
+ * npx @warnyin/agents --update อัปเดต playbook กลาง (เขียนทับเฉพาะไฟล์ core)
8
+ * npx @warnyin/agents --dry-run แสดงว่าจะทำอะไร โดยไม่เขียนไฟล์จริง
9
+ * (ทางสำรองไม่ผ่าน npm: npx github:warnyin/warnyin-agents)
10
+ */
11
+ import fs from 'node:fs'
12
+ import path from 'node:path'
13
+ import { fileURLToPath } from 'node:url'
14
+
15
+ const pkgRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
16
+ const target = process.cwd()
17
+ const args = new Set(process.argv.slice(2))
18
+ const UPDATE = args.has('--update')
19
+ const DRY = args.has('--dry-run')
20
+
21
+ if (args.has('--help') || args.has('-h')) {
22
+ console.log(`warnyin-agents — ติดตั้ง Warnyin Standard Workflow ลงโปรเจกต์ปัจจุบัน
23
+
24
+ ใช้งาน:
25
+ npx @warnyin/agents ติดตั้ง (ข้ามไฟล์ที่มีอยู่แล้ว ไม่เขียนทับ)
26
+ npx @warnyin/agents --update อัปเดต playbook กลางเป็นเวอร์ชันล่าสุด
27
+ (เขียนทับเฉพาะ .warnyin/workflow/, .claude/commands/warnyin/,
28
+ template .warnyin/template/stages/[topic] — ไม่แตะ docs/ และงานจริง)
29
+ npx @warnyin/agents --dry-run แสดงรายการไฟล์ที่จะสร้าง/อัปเดต โดยไม่เขียนจริง
30
+
31
+ หลังติดตั้ง: เปิด Claude Code ในโปรเจกต์ แล้วรัน /warnyin:init ให้ agent วิเคราะห์โปรเจกต์ + เติม docs/`)
32
+ process.exit(0)
33
+ }
34
+
35
+ // guard กัน self-install — เก็บไว้แบบ defensive (zero-cost)
36
+ // หลังย้าย source เข้า src/ → pkgRoot = src/ (sibling ของ bin/) จึงแทบไม่มีทาง === target (repo root / temp sandbox)
37
+ // → guard นี้เป็น no-op โดยตั้งใจในเคสปกติ/sandbox; ยังคงดักได้เฉพาะ edge case ที่ install ลงโฟลเดอร์ที่เป็น src/ เอง
38
+ if (path.resolve(pkgRoot) === path.resolve(target)) {
39
+ console.error('✖ กำลังรันอยู่ใน repo ของ warnyin-agents เอง — ให้ cd ไปที่โปรเจกต์ปลายทางก่อน')
40
+ process.exit(1)
41
+ }
42
+
43
+ // โครงเก่า (≤0.2.x): workflow/ + warnyin-stages/ ที่ root — เตือนให้ย้ายเอง ไม่แตะงานจริงของ user
44
+ const legacyV2 = ['workflow', 'warnyin-stages'].filter((d) => fs.existsSync(path.join(target, d)))
45
+ if (legacyV2.length) {
46
+ console.warn(`⚠ พบโครงเลย์เอาต์เก่า (≤0.2.x): ${legacyV2.join(', ')}
47
+ เวอร์ชันนี้ย้าย core ไปใต้ .warnyin/ และงานจริงไป docs/stages/ — แนะนำย้ายด้วยตัวเองก่อน:
48
+ 1. mkdir -p docs/stages && git mv warnyin-stages/* docs/stages/ # งานจริงของคุณ (ปลอดภัย ไม่ถูกแตะโดย installer)
49
+ 2. rm -rf workflow warnyin-stages # core เก่า + โฟลเดอร์ที่ย้ายของออกแล้ว
50
+ แล้วรันคำสั่งนี้อีกครั้ง\n`)
51
+ }
52
+
53
+ // โครงเก่า (0.3–0.5.x): ทุกอย่างอยู่ใต้ warnyin/ ที่ root — เวอร์ชันนี้แยกเป็น .warnyin/ (core) + docs/stages (งานจริง)
54
+ const legacyV5 = ['workflow', 'template', 'installer', 'stages'].filter((d) =>
55
+ fs.existsSync(path.join(target, 'warnyin', d)),
56
+ )
57
+ if (legacyV5.length) {
58
+ console.warn(`⚠ พบโครงเลย์เอาต์เก่า (0.3–0.5.x): warnyin/{${legacyV5.join(', ')}}
59
+ เวอร์ชันนี้ย้าย core ไป .warnyin/ และงานจริงไป docs/stages/ — แนะนำย้ายด้วยตัวเองก่อน:
60
+ 1. mkdir -p docs/stages && git mv warnyin/stages/* docs/stages/ # งานจริงของคุณ (active + achieved) — ปลอดภัย ไม่ถูกแตะ
61
+ 2. rm -rf warnyin # core เก่าทั้งหมด (เวอร์ชันใหม่ installer จะวางที่ .warnyin/)
62
+ แล้วรันคำสั่งนี้อีกครั้ง — installer จะวาง .warnyin/ ชุดใหม่ให้\n`)
63
+ }
64
+
65
+ // core = playbook กลาง + command + agent + template — เขียนทับได้เมื่อ --update
66
+ const CORE = [
67
+ path.join('.warnyin', 'workflow'),
68
+ path.join('.warnyin', 'template'),
69
+ path.join('.claude', 'commands', 'warnyin'),
70
+ path.join('.claude', 'agents'),
71
+ path.join('.claude', 'skills'),
72
+ ]
73
+ // scaffold = พื้นที่ทำงานเปล่าของโปรเจกต์ — installer "สร้างเอง" ไม่ copy tree จาก package
74
+ // (สำคัญ: ถ้า copy docs/stages จาก pkgRoot งานจริงของ repo ต้นทางจะรั่วไป target ทุกครั้ง — ดู verify installer-test-ci)
75
+ const SCAFFOLD_FILES = [
76
+ path.join('docs', 'stages', 'context.md'), // บริบทงานที่จดไว้ (next/discovery/explore อ่าน "ถ้ามี")
77
+ path.join('docs', 'stages', 'achieved', '.gitkeep'), // ให้ git track โฟลเดอร์ archive เปล่า
78
+ ]
79
+
80
+ const stats = { created: 0, updated: 0, skipped: 0 }
81
+
82
+ function copyTree(relDir, { overwrite }) {
83
+ const srcDir = path.join(pkgRoot, relDir)
84
+ if (!fs.existsSync(srcDir)) return
85
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
86
+ const rel = path.join(relDir, entry.name)
87
+ if (entry.isDirectory()) {
88
+ copyTree(rel, { overwrite })
89
+ continue
90
+ }
91
+ const src = path.join(pkgRoot, rel)
92
+ const dest = path.join(target, rel)
93
+ const exists = fs.existsSync(dest)
94
+ if (exists && !overwrite) {
95
+ stats.skipped++
96
+ continue
97
+ }
98
+ if (exists && overwrite && fs.readFileSync(src).equals(fs.readFileSync(dest))) {
99
+ stats.skipped++
100
+ continue
101
+ }
102
+ if (!DRY) {
103
+ fs.mkdirSync(path.dirname(dest), { recursive: true })
104
+ fs.copyFileSync(src, dest)
105
+ }
106
+ stats[exists ? 'updated' : 'created']++
107
+ console.log(` ${exists ? '↻' : '+'} ${rel}`)
108
+ }
109
+ }
110
+
111
+ /** สร้างโครง scaffold เปล่าของ docs/stages เอง (ไม่ copy จาก package — กันงานจริงของ repo ต้นทางรั่วไป target) — ไม่ทับไฟล์ที่มีอยู่ */
112
+ function ensureScaffold() {
113
+ for (const rel of SCAFFOLD_FILES) {
114
+ const dest = path.join(target, rel)
115
+ if (fs.existsSync(dest)) {
116
+ stats.skipped++
117
+ continue
118
+ }
119
+ if (!DRY) {
120
+ fs.mkdirSync(path.dirname(dest), { recursive: true })
121
+ fs.writeFileSync(dest, '')
122
+ }
123
+ stats.created++
124
+ console.log(` + ${rel}`)
125
+ }
126
+ }
127
+
128
+ /** seed docs/ ของโปรเจกต์จาก .warnyin/template/docs — ข้ามโฟลเดอร์ template `[...]` (ไว้ให้ /warnyin:init copy เป็นชื่อจริง) และไม่ทับไฟล์ที่มีอยู่ */
129
+ const TEMPLATE_DOCS = path.join('.warnyin', 'template', 'docs')
130
+ function seedDocs(relDir = TEMPLATE_DOCS) {
131
+ const srcDir = path.join(pkgRoot, relDir)
132
+ if (!fs.existsSync(srcDir)) return
133
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
134
+ if (entry.name.startsWith('[')) continue
135
+ const rel = path.join(relDir, entry.name)
136
+ if (entry.isDirectory()) {
137
+ seedDocs(rel)
138
+ continue
139
+ }
140
+ const destRel = path.join('docs', path.relative(TEMPLATE_DOCS, rel))
141
+ const dest = path.join(target, destRel)
142
+ if (fs.existsSync(dest)) {
143
+ stats.skipped++
144
+ continue
145
+ }
146
+ if (!DRY) {
147
+ fs.mkdirSync(path.dirname(dest), { recursive: true })
148
+ fs.copyFileSync(path.join(pkgRoot, rel), dest)
149
+ }
150
+ stats.created++
151
+ console.log(` + ${destRel}`)
152
+ }
153
+ }
154
+
155
+ /** CLAUDE.md / AGENTS.md: ไม่มี → สร้างจาก template; มีอยู่แล้วแต่ยังไม่มี workflow → ต่อท้ายเป็น section */
156
+ function installRootDoc(name, srcPath) {
157
+ const dest = path.join(target, name)
158
+ const content = fs.readFileSync(srcPath, 'utf8')
159
+ if (!fs.existsSync(dest)) {
160
+ if (!DRY) fs.writeFileSync(dest, content)
161
+ stats.created++
162
+ console.log(` + ${name}`)
163
+ return
164
+ }
165
+ const existing = fs.readFileSync(dest, 'utf8')
166
+ if (existing.includes('warnyin/workflow/stages/')) {
167
+ stats.skipped++
168
+ return
169
+ }
170
+ const section = '\n\n' + content.replace(/^#\s[^\n]*\n/, '## Warnyin Standard Workflow\n')
171
+ if (!DRY) fs.appendFileSync(dest, section)
172
+ stats.updated++
173
+ console.log(` ± ${name} (ต่อท้าย section Warnyin Standard Workflow)`)
174
+ }
175
+
176
+ console.log(`Warnyin Standard Workflow → ${target}${DRY ? ' (dry-run)' : ''}\n`)
177
+
178
+ for (const dir of CORE) copyTree(dir, { overwrite: UPDATE })
179
+ ensureScaffold()
180
+ seedDocs()
181
+ installRootDoc('CLAUDE.md', path.join(pkgRoot, '.warnyin', 'installer', 'templates', 'CLAUDE.md'))
182
+ installRootDoc('AGENTS.md', path.join(pkgRoot, 'AGENTS.md'))
183
+
184
+ console.log(`\nสรุป: สร้างใหม่ ${stats.created} · อัปเดต ${stats.updated} · ข้าม (มีอยู่แล้ว) ${stats.skipped}`)
185
+
186
+ if (!UPDATE) {
187
+ console.log(`
188
+ ขั้นถัดไป:
189
+ 1. เปิด Claude Code ในโปรเจกต์นี้ แล้วรัน /warnyin:init — ให้ agent วิเคราะห์โปรเจกต์ + เติม docs/
190
+ 2. เริ่มงานแรก: /warnyin:discovery <topic> หรือ /warnyin:design <slug> <change>
191
+
192
+ อัปเดต playbook ภายหลัง: npx @warnyin/agents --update`)
193
+ }