@warnyin/agents 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -23,6 +23,19 @@
23
23
 
24
24
  ## [Unreleased]
25
25
 
26
+ ## [0.14.0] - 2026-06-11
27
+
28
+ ### Added
29
+ - **DESIGN sizing gate — establish tier ก่อนจ่าย ceremony** (เสริม feature `change-sizing`) — เพิ่ม **step 1.5 "Establish tier"** ใน `src/.warnyin/workflow/stages/design.md §4` (ก่อน `business.md`/`proposal`): DESIGN **ประเมินขนาด change เบื้องต้นเอง** ตาม rubric (`triage.md` signals + hard-floor) → **มั่นใจ = กำหนด tier + บันทึก `proposal.md`**; **ไม่มั่นใจ/ก้ำกึ่ง = ถาม user เป็น options** (ประเมินด้วย `/warnyin:triage` ก่อน / user กำหนด tier เองถ้ารู้; ก้ำกึ่ง default = ปัดขึ้น `standard`); **hard-floor** (auth/migration/secret/public-API/security-sensitive) บังคับ ≥ standard เสมอ. อุดช่องว่างเดิมที่ `§7` **บริโภค** tier แต่ไม่มีตัวการันตีว่า established จริง (DESIGN เคยเดินโดยไม่รู้ขนาด). `§7` เพิ่มประโยคชี้ "tier ถูก established ที่ §4 step 1.5" (ไม่ inline rubric — ชี้ `triage.md`); ปรับช่อง `ขนาด` ใน proposal template `เล็ก/กลาง/ใหญ่` → `fast/standard/large` (vocab ตรง triage). tier = judgment (⚠ ไม่ใช่ validator). payload ติดมากับ `--update` รอบถัดไป
30
+
31
+ ### Fixed
32
+ - **`docs/features/global-install/spec.md`** — เพิ่ม `WHEN` ที่ขาดใน scenario "--global + --project → error" (validate-topic C5)
33
+
34
+ ## [0.13.0] - 2026-06-11
35
+
36
+ ### Added
37
+ - **Global install mode — ติดตั้งครั้งเดียวใช้ได้ทุกโปรเจกต์ (opt-in)** (feature `global-install`) — `npx @warnyin/agents --global` ติดตั้ง adapter → `~/.claude/{commands/warnyin,agents,skills}` + playbook → `~/.warnyin/{workflow,template}` **ครั้งเดียว** → `/warnyin:*` ใช้ได้ทุกโปรเจกต์ (Claude Code โหลด user-level `~/.claude/`). **Hybrid:** workspace (`docs/`) ยัง per-project; โปรเจกต์ที่มี `./.warnyin/` local → ใช้ local ก่อน (override → คง reproducibility); **per-project ยังเป็น default**. **mode resolution** `resolveMode()` (pure-fn): flag `--global`/`--project`; ไม่ระบุ+TTY → prompt; **non-TTY → project (CI-safe ไม่ค้าง)**; `--global --project` → error. **ปลอดภัยต่อ homedir:** first-install `overwrite:false` (ไม่ทับไฟล์ user ใน `~/.claude/{agents,skills}`), `installGlobalNote()` เขียน `~/.claude/CLAUDE.md` แบบ **append-with-marker** (ไม่แตะ personal global memory), homedir guard (falsy/root → error), echo target paths. **resolve playbook local-first → global** ผ่าน convention canonical ใน `CLAUDE.md`/`AGENTS.md`/`CLAUDE.global.md` (ไม่ duplicate ลงทุก adapter). `/warnyin:init` รับ workspace bootstrap (scaffold + seed `docs/`, อ่าน template local→global). **backward compatible** (project mode = default ไม่เปลี่ยน; `--global` opt-in). **limitation:** Codex/Antigravity global root doc รอบนี้ยังไม่รองรับ (per-project ใช้ได้เต็ม). zero-dep + cross-platform (`os.homedir()`, HOME/USERPROFILE); payload ติดมากับ `--update` รอบถัดไป
38
+
26
39
  ## [0.12.0] - 2026-06-11
27
40
 
28
41
  ### Added
package/README.md CHANGED
@@ -18,12 +18,24 @@ Discovery (optional) ──▶ DESIGN ──▶ BUILD ──▶ VERIFY ──▶
18
18
 
19
19
  ```bash
20
20
  cd my-project
21
- npx @warnyin/agents # ติดตั้ง (ข้ามไฟล์ที่มีอยู่ ไม่เขียนทับ)
21
+ npx @warnyin/agents # ติดตั้งลงโปรเจกต์นี้ (ข้ามไฟล์ที่มีอยู่ ไม่เขียนทับ)
22
22
  npx @warnyin/agents --dry-run # ดูก่อนว่าจะสร้างอะไร
23
23
  npx @warnyin/agents --update # อัปเดต playbook กลางเป็นเวอร์ชันล่าสุด
24
24
  # ทางสำรอง (ดึงตรงจาก main): npx github:warnyin/warnyin-agents
25
25
  ```
26
26
 
27
+ **ติดตั้งแบบ global (ใช้ได้ทุกโปรเจกต์ — ติดตั้งครั้งเดียว):**
28
+
29
+ ```bash
30
+ npx @warnyin/agents --global # ลง ~/.claude + ~/.warnyin → /warnyin:* ใช้ได้ทุกโปรเจกต์
31
+ npx @warnyin/agents --project # บังคับลงโปรเจกต์ (ไม่ถาม)
32
+ # ไม่ระบุ flag: ถ้าเป็น terminal จะถามให้เลือก; ถ้า non-TTY (CI/pipe) → ลงโปรเจกต์อัตโนมัติ
33
+ ```
34
+
35
+ - **global vs project:** `--project` (ค่าเริ่มต้น) = ติดตั้ง vendored ลง repo (commit ลง git ได้ → ทีม share เวอร์ชันเป๊ะ); `--global` = ติดตั้งครั้งเดียวที่ `~/` ใช้ทุกโปรเจกต์ (สะดวกสำหรับคนทำหลาย repo)
36
+ - **global เป็น Hybrid:** โปรเจกต์ที่มี `.warnyin/` ของตัวเอง (local) → ใช้ local ก่อนเสมอ (override global); workspace (`docs/`) ยังแยกต่อโปรเจกต์ — รัน `/warnyin:init` ครั้งแรกในแต่ละโปรเจกต์
37
+ - **global ปลอดภัยต่อ `~/`:** ไม่ทับไฟล์ที่มีอยู่ของคุณใน `~/.claude/` (เขียนทับเฉพาะ `--update`), แสดง path ที่จะเขียนก่อน
38
+ - _(global รองรับ Claude Code; Codex/Antigravity ใช้ติดตั้งแบบ per-project)_
27
39
  - โปรเจกต์ที่มี `CLAUDE.md` / `AGENTS.md` อยู่แล้ว → installer **ต่อท้ายเป็น section** ไม่เขียนทับ
28
40
  - `--update` เขียนทับเฉพาะ core (`.warnyin/workflow/`, `.claude/commands/warnyin/`, template ใน `.warnyin/template/`) — ไม่แตะ `docs/` และงานจริง
29
41
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warnyin/agents",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Warnyin Standard Workflow installer — 5-stage ways of work (Discovery/DESIGN/BUILD/VERIFY/SHIP) สำหรับทุกโปรเจกต์",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,5 @@
1
+ <!-- warnyin:global-note -->
2
+
3
+ ## การ resolve playbook (local-first → global)
4
+ - path `.warnyin/workflow/...` / `.warnyin/template/...`: หาในโปรเจกต์ `./.warnyin/` ก่อน ไม่มี → `~/.warnyin/` (global install)
5
+ - ถ้ายังไม่มี `docs/stages/` (global mode โปรเจกต์ใหม่) → รัน `/warnyin:init` ก่อน (สร้าง workspace)
@@ -28,3 +28,7 @@ Claude Code อ่าน `.claude/` + ไฟล์นี้, ส่วน Codex/
28
28
 
29
29
  ## อัปเดต workflow
30
30
  `npx @warnyin/agents --update` — เขียนทับเฉพาะ playbook กลาง (`.warnyin/workflow/`, `.claude/commands/warnyin/`, template `.warnyin/template/stages/[topic]/`) ไม่แตะ `docs/` และงานจริงใน `docs/stages/`
31
+
32
+ ## การ resolve playbook (local-first → global)
33
+ - path `.warnyin/workflow/...` / `.warnyin/template/...`: หาในโปรเจกต์ `./.warnyin/` ก่อน ไม่มี → `~/.warnyin/` (global install)
34
+ - ถ้ายังไม่มี `docs/stages/` (global mode โปรเจกต์ใหม่) → รัน `/warnyin:init` ก่อน (สร้าง workspace)
@@ -7,7 +7,7 @@
7
7
  |---|---|
8
8
  | **Slug** | `<kebab-case>` |
9
9
  | **ประเภท** | `feature` / `bugfix` / `refactor` / `docs` |
10
- | **ขนาด** | `เล็ก` / `กลาง` / `ใหญ่` |
10
+ | **ขนาด** | `fast` / `standard` / `large` (จาก triage หรือ ประเมินใน DESIGN step 1.5) |
11
11
  | **วันที่** | `YYYY-MM-DD` |
12
12
  | **มาจาก Discovery?** | `./discovery.md` หรือ `ไม่มี` |
13
13
 
@@ -31,6 +31,16 @@
31
31
 
32
32
  ## 3. ลำดับขั้นการทำงาน (process)
33
33
 
34
+ 0. **Workspace bootstrap (ทำก่อนวิเคราะห์โปรเจกต์ — idempotent):**
35
+ - **หา template:** ตรวจ `./.warnyin/template/` ก่อน (local) — ไม่มีหรือไม่มี `./.warnyin/` → fallback `~/.warnyin/template/` (global install); ใช้ path ที่พบเป็น `<template>`
36
+ - **สร้าง scaffold (ถ้ายังไม่มี):**
37
+ - `docs/stages/context.md` — ไม่มีให้สร้างไฟล์เปล่า; มีอยู่แล้ว → ข้าม
38
+ - `docs/stages/achieved/.gitkeep` — ไม่มีให้สร้างไฟล์เปล่า (สร้าง dir `docs/stages/achieved/` ด้วยถ้าจำเป็น); มีอยู่แล้ว → ข้าม
39
+ - **seed `docs/` จาก `<template>/docs/**` (ถ้า template มี):**
40
+ - วน entry ใน `<template>/docs/` — **ข้าม entry ที่ชื่อขึ้นต้นด้วย `[`** (placeholder เช่น `[component]/`, `[topic]/` — seedDocs-skip invariant)
41
+ - entry ที่เหลือ: ปลายทาง `docs/<entry>` — **ไม่ทับไฟล์ที่มีอยู่แล้ว** (copy เฉพาะที่ยังไม่มี)
42
+ - **ทั้งหมดนี้เป็น agent-driven** — agent ตรวจ/สร้างไฟล์เปล่าเอง; ไม่ต้องรัน script
43
+
34
44
  1. **สแกนภาพรวม:** โครงสร้าง repo, package manifest, ภาษา/framework, แบ่งเป็น **component** อะไรบ้าง (เช่น api-service, admin-console)
35
45
  2. **วิเคราะห์ลึกต่อ component (ขนานได้, read-only):** โครงสร้างโฟลเดอร์/โมดูล, pattern/convention ที่ใช้จริงในโค้ด, วิธี build/test ที่มีอยู่
36
46
  3. **วิเคราะห์ infra:** docker/compose, env, service ที่ต้องรันสำหรับ local dev
@@ -44,7 +54,7 @@
44
54
  5. **เสนอ summary → user ยืนยันครั้งเดียว**
45
55
  6. **เขียนไฟล์จริงลง `docs/` ให้ครบตาราง §4 (ขั้นบังคับ ห้ามข้าม)** — ทำตามกลไก 6.1–6.4 นี้:
46
56
 
47
- **6.1 ไฟล์ root** — copy template แล้วเติม:
57
+ **6.1 ไฟล์ root** — copy template แล้วเติม (ขั้นนี้ = เติมเนื้อหาหลังวิเคราะห์ เสริมขั้น 0 ที่สร้าง scaffold/seed ไว้แล้ว):
48
58
  ```
49
59
  mkdir -p docs
50
60
  cp .warnyin/template/docs/project.md docs/project.md
@@ -52,6 +62,7 @@
52
62
  cp .warnyin/template/docs/rule.md docs/rule.md
53
63
  cp .warnyin/template/docs/troubleshooting.md docs/troubleshooting.md
54
64
  ```
65
+ - ใช้ `.warnyin/template/` (local) — per-project install มีให้เสมอ; global mode ขั้น 0 seed ให้แล้ว (template อ่าน local→global ตามขั้น 0)
55
66
  - ไฟล์ไหนมีอยู่แล้วใน `docs/` → **ห้าม `cp` ทับ** ให้เปิดอ่านแล้ว Edit เติมแทน
56
67
  - `project.md` → เติมจากผลสัมภาษณ์ user (ข้อ 4) · `infra.md` → เติมจาก config จริง (ข้อ 3) · `rule.md`/`troubleshooting.md` → วางโครงหัวข้อ ใส่ `<!-- ยังว่าง รอเติม -->` ในส่วนที่ยังไม่มีข้อมูล
57
68
 
@@ -49,6 +49,11 @@
49
49
 
50
50
  1. **เตรียมพื้นที่:** ใช้/สร้างโฟลเดอร์ `docs/stages/<slug>/` (ถ้ามาจาก Discovery ใช้อันเดิม)
51
51
  2. **Ground + เคลียร์ความไม่ชัด:** อ่าน Input; ทุกจุดกำกวมเรื่อง design → ถามทีละข้อ + recommended answer จนชัด (ถ้าใหญ่/ไม่ชัดมาก → แนะนำ Discovery ก่อน)
52
+ 1.5 **Establish tier (ก่อนจ่าย ceremony):** ประเมินขนาด change เบื้องต้นตาม rubric (`triage.md` §2 — signals + hard-floor)
53
+ - **มั่นใจ** → กำหนด tier + บันทึก `proposal.md` ช่อง `ขนาด`
54
+ - **ไม่มั่นใจ/ก้ำกึ่ง** → ถาม user (options): (ก) ประเมินด้วย `/warnyin:triage` ก่อน · (ข) user กำหนด tier เองถ้ารู้ [ก้ำกึ่ง default = ปัดขึ้น standard]
55
+ - **hard-floor** (auth/migration/secret/public-API/security-sensitive) → ≥ standard เสมอ
56
+ - tier → drive ceremony ตาม §7
52
57
  3. **business.md** *(optional — ข้ามได้ถ้า change เล็ก เช่น fix bug นิดหน่อย)*: what & why เชิงธุรกิจ — goal, คุณค่า, persona, success metric
53
58
  4. **proposal.md** (what & why): สรุป change ที่จะทำ, เหตุผล, ทางเลือกที่พิจารณา/ตัดทิ้ง, scope in/out
54
59
  5. **design.md** (how): ออกแบบเชิงเทคนิคแบบ vertical slice — slice มีอะไรบ้าง, แต่ละ slice ตัดผ่าน layer ไหน, data model, interface/contract, flow, ผลกระทบต่อระบบเดิม (ใช้ lens `.warnyin/workflow/roles/sa.md`) — **ครอบ "Spec delta" ด้วย**: เทียบพฤติกรรมที่ change นี้แตะกับ `docs/features/<name>/spec.md` ปัจจุบัน แล้วเขียน ADDED/MODIFIED/REMOVED (SHIP merge ตามนี้); change ไม่แตะพฤติกรรม feature → ระบุ "ไม่มี delta"
@@ -105,7 +110,9 @@
105
110
 
106
111
  ## 7. ปรับความละเอียดตามขนาด change (3-tier)
107
112
 
108
- ปรับ ceremony ตาม **tier** ที่ `/warnyin:triage` ประเมิน (`.warnyin/workflow/triage.md`) — fast/standard/large:
113
+ **tier ถูก established ที่ §4 step 1.5** (ประเมินเอง / มั่นใจกำหนด / ไม่มั่นใจถาม options / hard-floor บังคับ ≥ standard) — ส่วน §7 นี้อธิบาย ceremony ที่ drive โดย tier ที่ established
114
+
115
+ ปรับ ceremony ตาม **tier** (canonical rubric ดู `.warnyin/workflow/triage.md`) — fast/standard/large:
109
116
 
110
117
  - **fast** (bugfix, typo, config tweak, wording-guidance สั้น, 1-2 ไฟล์ modify ของเดิม ไม่ cross-cutting): **fast-track** — ข้าม `business.md`, proposal/design สั้น, **ไม่ panel ไม่ dry-run**, 1 task; ทำตาม [fast-track skip-list](../triage.md#fast-track-skip-list) (canonical ใน `triage.md` — ไม่ลอก rubric มาที่นี่). **คง correctness floor:** spec/acceptance ขั้นต่ำของ task ยังต้องครบ
111
118
  - **standard** (feature ปกติ, modify หลายไฟล์/หลาย component, มี logic ใหม่): flow เต็ม — ครบทุก artifact, แตก vertical slice หลาย task + sub-task, dependency ชัด, panel/dry-run ตามเหมาะ
package/src/AGENTS.md CHANGED
@@ -46,3 +46,9 @@ Discovery (optional) ──▶ DESIGN ──▶ BUILD ──▶ VERIFY ──▶
46
46
  ทำตาม `.warnyin/workflow/stages/discovery.md` — เริ่มอ่าน `docs/project.md`, ตี scope กว้าง→แคบ,
47
47
  ถามทีละข้อพร้อมเสนอคำตอบที่แนะนำ, คำถามที่ตอบได้ด้วยโค้ดให้ไปอ่านโค้ดเอง,
48
48
  เขียน output ลง `docs/stages/<slug>/discovery.md` และ `research.md`
49
+
50
+ ## การ resolve playbook (local-first → global)
51
+ - path `.warnyin/workflow/...` / `.warnyin/template/...`: หาในโปรเจกต์ `./.warnyin/` ก่อน ไม่มี → `~/.warnyin/` (global install)
52
+ - ถ้ายังไม่มี `docs/stages/` (global mode โปรเจกต์ใหม่) → รัน `/warnyin:init` ก่อน (สร้าง workspace)
53
+
54
+ > หมายเหตุ: global root doc ของ Codex/Antigravity ไม่รองรับรอบนี้ — convention นี้มีผลเฉพาะ per-project path
package/src/bin/cli.mjs CHANGED
@@ -1,28 +1,50 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * warnyin-agents installer
4
- * ติดตั้ง Warnyin Standard Workflow ลงโปรเจกต์ปัจจุบัน (cwd)
4
+ * ติดตั้ง Warnyin Standard Workflow ลงโปรเจกต์ปัจจุบัน (cwd) หรือแบบ global (~/)
5
5
  *
6
- * npx @warnyin/agents ติดตั้ง (ข้ามไฟล์ที่มีอยู่แล้ว)
6
+ * npx @warnyin/agents ติดตั้งลงโปรเจกต์ (ข้ามไฟล์ที่มีอยู่แล้ว)
7
+ * npx @warnyin/agents --global ติดตั้งแบบ global (~/) ใช้ได้ทุกโปรเจกต์
8
+ * npx @warnyin/agents --project ติดตั้งลงโปรเจกต์ (บังคับ — ไม่ถาม)
7
9
  * npx @warnyin/agents --update อัปเดต playbook กลาง (เขียนทับเฉพาะไฟล์ core)
8
10
  * npx @warnyin/agents --dry-run แสดงว่าจะทำอะไร โดยไม่เขียนไฟล์จริง
9
11
  * (ทางสำรองไม่ผ่าน npm: npx github:warnyin/warnyin-agents)
10
12
  */
11
13
  import fs from 'node:fs'
14
+ import os from 'node:os'
12
15
  import path from 'node:path'
13
16
  import { fileURLToPath } from 'node:url'
17
+ import { createInterface } from 'node:readline/promises'
14
18
 
15
19
  const pkgRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
16
- const target = process.cwd()
20
+ const cwd = process.cwd()
17
21
  const args = new Set(process.argv.slice(2))
18
22
  const UPDATE = args.has('--update')
19
23
  const DRY = args.has('--dry-run')
20
24
 
25
+ /**
26
+ * ★ pure function — เลือก mode จาก flag/TTY/answer (ไม่มี side-effect, export ให้ unit test)
27
+ * @param {{globalFlag?:boolean, projectFlag?:boolean, isTTY?:boolean, answer?:string}} o
28
+ * @returns {'project'|'global'}
29
+ */
30
+ export function resolveMode({ globalFlag, projectFlag, isTTY, answer } = {}) {
31
+ if (globalFlag && projectFlag) {
32
+ throw new Error('--global กับ --project ใช้พร้อมกันไม่ได้ (เลือกอย่างใดอย่างหนึ่ง)')
33
+ }
34
+ if (globalFlag) return 'global'
35
+ if (projectFlag) return 'project'
36
+ if (!isTTY) return 'project' // CI-safe default — npx/pipe ไม่ค้างรอ input
37
+ const a = (answer ?? '').trim().toLowerCase()
38
+ return a === '2' || a === 'global' ? 'global' : 'project'
39
+ }
40
+
21
41
  if (args.has('--help') || args.has('-h')) {
22
- console.log(`warnyin-agents — ติดตั้ง Warnyin Standard Workflow ลงโปรเจกต์ปัจจุบัน
42
+ console.log(`warnyin-agents — ติดตั้ง Warnyin Standard Workflow ลงโปรเจกต์ปัจจุบัน หรือแบบ global
23
43
 
24
44
  ใช้งาน:
25
- npx @warnyin/agents ติดตั้ง (ข้ามไฟล์ที่มีอยู่แล้ว ไม่เขียนทับ)
45
+ npx @warnyin/agents ติดตั้งลงโปรเจกต์ (ถ้า TTY จะถามก่อน; ข้ามไฟล์ที่มีอยู่แล้ว)
46
+ npx @warnyin/agents --global ติดตั้งแบบ global ลง ~/ (~/.warnyin + ~/.claude) ใช้ได้ทุกโปรเจกต์
47
+ npx @warnyin/agents --project ติดตั้งลงโปรเจกต์ (บังคับ ไม่ถาม)
26
48
  npx @warnyin/agents --update อัปเดต playbook กลางเป็นเวอร์ชันล่าสุด
27
49
  (เขียนทับเฉพาะ .warnyin/workflow/, .claude/commands/warnyin/,
28
50
  template .warnyin/template/stages/[topic] — ไม่แตะ docs/ และงานจริง)
@@ -33,15 +55,15 @@ if (args.has('--help') || args.has('-h')) {
33
55
  }
34
56
 
35
57
  // guard กัน self-install — เก็บไว้แบบ defensive (zero-cost)
36
- // หลังย้าย source เข้า src/ → pkgRoot = src/ (sibling ของ bin/) จึงแทบไม่มีทาง === target (repo root / temp sandbox)
58
+ // หลังย้าย source เข้า src/ → pkgRoot = src/ (sibling ของ bin/) จึงแทบไม่มีทาง === cwd (repo root / temp sandbox)
37
59
  // → guard นี้เป็น no-op โดยตั้งใจในเคสปกติ/sandbox; ยังคงดักได้เฉพาะ edge case ที่ install ลงโฟลเดอร์ที่เป็น src/ เอง
38
- if (path.resolve(pkgRoot) === path.resolve(target)) {
60
+ if (path.resolve(pkgRoot) === path.resolve(cwd)) {
39
61
  console.error('✖ กำลังรันอยู่ใน repo ของ warnyin-agents เอง — ให้ cd ไปที่โปรเจกต์ปลายทางก่อน')
40
62
  process.exit(1)
41
63
  }
42
64
 
43
65
  // โครงเก่า (≤0.2.x): workflow/ + warnyin-stages/ ที่ root — เตือนให้ย้ายเอง ไม่แตะงานจริงของ user
44
- const legacyV2 = ['workflow', 'warnyin-stages'].filter((d) => fs.existsSync(path.join(target, d)))
66
+ const legacyV2 = ['workflow', 'warnyin-stages'].filter((d) => fs.existsSync(path.join(cwd, d)))
45
67
  if (legacyV2.length) {
46
68
  console.warn(`⚠ พบโครงเลย์เอาต์เก่า (≤0.2.x): ${legacyV2.join(', ')}
47
69
  เวอร์ชันนี้ย้าย core ไปใต้ .warnyin/ และงานจริงไป docs/stages/ — แนะนำย้ายด้วยตัวเองก่อน:
@@ -52,7 +74,7 @@ if (legacyV2.length) {
52
74
 
53
75
  // โครงเก่า (0.3–0.5.x): ทุกอย่างอยู่ใต้ warnyin/ ที่ root — เวอร์ชันนี้แยกเป็น .warnyin/ (core) + docs/stages (งานจริง)
54
76
  const legacyV5 = ['workflow', 'template', 'installer', 'stages'].filter((d) =>
55
- fs.existsSync(path.join(target, 'warnyin', d)),
77
+ fs.existsSync(path.join(cwd, 'warnyin', d)),
56
78
  )
57
79
  if (legacyV5.length) {
58
80
  console.warn(`⚠ พบโครงเลย์เอาต์เก่า (0.3–0.5.x): warnyin/{${legacyV5.join(', ')}}
@@ -79,6 +101,9 @@ const SCAFFOLD_FILES = [
79
101
 
80
102
  const stats = { created: 0, updated: 0, skipped: 0 }
81
103
 
104
+ // target = ปลายทางที่จะเขียนไฟล์ — ตั้งหลัง resolve mode (project=cwd | global=homedir)
105
+ let target = cwd
106
+
82
107
  function copyTree(relDir, { overwrite }) {
83
108
  const srcDir = path.join(pkgRoot, relDir)
84
109
  if (!fs.existsSync(srcDir)) return
@@ -173,21 +198,113 @@ function installRootDoc(name, srcPath) {
173
198
  console.log(` ± ${name} (ต่อท้าย section Warnyin Standard Workflow)`)
174
199
  }
175
200
 
176
- console.log(`Warnyin Standard Workflow → ${target}${DRY ? ' (dry-run)' : ''}\n`)
201
+ const GLOBAL_NOTE_MARKER = '<!-- warnyin:global-note -->'
177
202
 
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'))
203
+ /**
204
+ * เขียน resolution note ลง ~/.claude/CLAUDE.md แบบ note-only append-with-marker (global mode)
205
+ * — ห้ามเขียนทับทั้งไฟล์ (personal global memory ของ user); append เฉพาะถ้ายังไม่มี marker (idempotent)
206
+ * — defensive skip ถ้า template ไม่มี (worktree T1 เดี่ยวก่อน merge T2) — pattern เดียวกับ copyTree/seedDocs
207
+ */
208
+ function installGlobalNote() {
209
+ const src = path.join(pkgRoot, '.warnyin', 'installer', 'templates', 'CLAUDE.global.md')
210
+ const destRel = path.join('.claude', 'CLAUDE.md')
211
+ const dest = path.join(target, destRel)
212
+ if (!fs.existsSync(src)) {
213
+ console.log(` · ข้าม ${destRel} (ยังไม่มี template CLAUDE.global.md)`)
214
+ return
215
+ }
216
+ const note = fs.readFileSync(src, 'utf8')
217
+ if (!fs.existsSync(dest)) {
218
+ if (!DRY) {
219
+ fs.mkdirSync(path.dirname(dest), { recursive: true })
220
+ fs.writeFileSync(dest, note)
221
+ }
222
+ stats.created++
223
+ console.log(` + ${destRel}`)
224
+ return
225
+ }
226
+ const existing = fs.readFileSync(dest, 'utf8')
227
+ if (existing.includes(GLOBAL_NOTE_MARKER)) {
228
+ stats.skipped++
229
+ return
230
+ }
231
+ const section = (existing.endsWith('\n') ? '\n' : '\n\n') + note
232
+ if (!DRY) fs.appendFileSync(dest, section)
233
+ stats.updated++
234
+ console.log(` ± ${destRel} (ต่อท้าย warnyin global note)`)
235
+ }
236
+
237
+ /** เลือก mode + ติดตั้งตาม mode (target=cwd|homedir). ห่อ async เฉพาะ path TTY (readline) */
238
+ async function main() {
239
+ const globalFlag = args.has('--global')
240
+ const projectFlag = args.has('--project')
241
+ const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY)
242
+
243
+ let mode
244
+ try {
245
+ if (!globalFlag && !projectFlag && isTTY) {
246
+ // ถาม เฉพาะ path TTY — non-TTY/flag ไม่แตะ readline (ไม่ค้าง/ไม่ช้าลง)
247
+ const rl = createInterface({ input: process.stdin, output: process.stdout })
248
+ let answer
249
+ try {
250
+ answer = await rl.question('ติดตั้งแบบไหน? [1] โปรเจกต์นี้ (default) [2] global (~/) : ')
251
+ } finally {
252
+ rl.close()
253
+ }
254
+ mode = resolveMode({ globalFlag, projectFlag, isTTY, answer })
255
+ } else {
256
+ mode = resolveMode({ globalFlag, projectFlag, isTTY })
257
+ }
258
+ } catch (e) {
259
+ console.error(`✖ ${e.message}`)
260
+ process.exit(1)
261
+ }
262
+
263
+ if (mode === 'global') {
264
+ const home = os.homedir()
265
+ // homedir guard — falsy หรือ filesystem root (CI/container ไม่มี passwd) → error แทนเขียนลง root
266
+ if (!home || path.resolve(home) === path.parse(path.resolve(home)).root) {
267
+ console.error('✖ หา home directory ไม่ได้ (หรือเป็น filesystem root) — ใช้ --project แทน')
268
+ process.exit(1)
269
+ }
270
+ target = home
271
+ console.log(`Warnyin Standard Workflow → global ${target}${DRY ? ' (dry-run)' : ''}`)
272
+ console.log(` จะเขียนนอกโปรเจกต์ที่: ${path.join(target, '.warnyin')}, ${path.join(target, '.claude', 'commands', 'warnyin')}, ${path.join(target, '.claude', 'CLAUDE.md')}\n`)
273
+ // first-install overwrite:false (skip ของเดิมใน ~/.claude/{agents,skills}) — ทับเฉพาะ --global --update
274
+ for (const dir of CORE) copyTree(dir, { overwrite: UPDATE })
275
+ // ข้าม scaffold/seedDocs (ยกให้ /warnyin:init) + ข้าม AGENTS.md global (DQ3 limitation)
276
+ installGlobalNote()
277
+ } else {
278
+ target = cwd
279
+ console.log(`Warnyin Standard Workflow → ${target}${DRY ? ' (dry-run)' : ''}\n`)
280
+ for (const dir of CORE) copyTree(dir, { overwrite: UPDATE })
281
+ ensureScaffold()
282
+ seedDocs()
283
+ installRootDoc('CLAUDE.md', path.join(pkgRoot, '.warnyin', 'installer', 'templates', 'CLAUDE.md'))
284
+ installRootDoc('AGENTS.md', path.join(pkgRoot, 'AGENTS.md'))
285
+ }
286
+
287
+ console.log(`\nสรุป: สร้างใหม่ ${stats.created} · อัปเดต ${stats.updated} · ข้าม (มีอยู่แล้ว) ${stats.skipped}`)
183
288
 
184
- console.log(`\nสรุป: สร้างใหม่ ${stats.created} · อัปเดต ${stats.updated} · ข้าม (มีอยู่แล้ว) ${stats.skipped}`)
289
+ if (!UPDATE) {
290
+ if (mode === 'global') {
291
+ console.log(`
292
+ ติดตั้ง global แล้ว — /warnyin:* ใช้ได้ทุกโปรเจกต์ (จาก ~/.claude/commands/)
293
+ เปิดโปรเจกต์ใด ๆ ใน Claude Code → รัน /warnyin:init เพื่อสร้าง workspace (docs/) ของโปรเจกต์นั้น
185
294
 
186
- if (!UPDATE) {
187
- console.log(`
295
+ อัปเดต playbook ภายหลัง: npx @warnyin/agents --global --update`)
296
+ } else {
297
+ console.log(`
188
298
  ขั้นถัดไป:
189
299
  1. เปิด Claude Code ในโปรเจกต์นี้ แล้วรัน /warnyin:init — ให้ agent วิเคราะห์โปรเจกต์ + เติม docs/
190
300
  2. เริ่มงานแรก: /warnyin:discovery <topic> หรือ /warnyin:design <slug> <change>
191
301
 
192
302
  อัปเดต playbook ภายหลัง: npx @warnyin/agents --update`)
303
+ }
304
+ }
305
+ }
306
+
307
+ // main-guard: รันเฉพาะตอน execute ตรง ๆ (ไม่ trigger ตอน import เพื่อ unit-test resolveMode)
308
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
309
+ await main()
193
310
  }