create-anpunkit 2.0.1 → 2.1.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/bin/cli.js +3 -2
- package/package.json +1 -1
- package/template/.claude/agents/e2e-runner.md +15 -1
- package/template/.claude/agents/infra-provisioner.md +33 -2
- package/template/.claude/agents/planner.md +15 -5
- package/template/.claude/agents/researcher.md +19 -8
- package/template/.claude/agents/test-author.md +25 -2
- package/template/.claude/anpunkit-manifest.json +22 -22
- package/template/.claude/commands/infra.md +28 -3
- package/template/.claude/commands/overview.md +66 -23
- package/template/.claude/commands/phase.md +68 -16
- package/template/.claude/commands/replan.md +23 -9
- package/template/.cursor/commands/infra.md +26 -1
- package/template/.cursor/commands/overview.md +65 -22
- package/template/.cursor/commands/phase.md +67 -15
- package/template/.cursor/commands/replan.md +22 -8
- package/template/AGENTS.md +101 -25
- package/template/README.md +54 -23
- package/template/anpunkit.png +0 -0
- package/template/commands.src/infra.md +28 -3
- package/template/commands.src/overview.md +66 -23
- package/template/commands.src/phase.md +68 -16
- package/template/commands.src/replan.md +23 -9
- package/template/docs/DESIGN_LOG.md +11 -0
- package/template/index.html +340 -0
- package/template/setup.sh +137 -56
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta
|
|
6
|
+
property="og:title"
|
|
7
|
+
content="anpunkit — Deterministic guardrails for AI coding agents"
|
|
8
|
+
/>
|
|
9
|
+
<meta
|
|
10
|
+
property="og:description"
|
|
11
|
+
content="If you're just starting AI coding agent journey, This one is for YOU!"
|
|
12
|
+
/>
|
|
13
|
+
<meta
|
|
14
|
+
property="og:image"
|
|
15
|
+
content="https://methees.github.io/anpunkit/anpunkit.png"
|
|
16
|
+
/>
|
|
17
|
+
<meta property="og:url" content="https://methees.github.io/anpunkit/" />
|
|
18
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
19
|
+
<title>anpunkit — Deterministic guardrails for AI coding agents</title>
|
|
20
|
+
<style>
|
|
21
|
+
* {
|
|
22
|
+
margin: 0;
|
|
23
|
+
padding: 0;
|
|
24
|
+
box-sizing: border-box;
|
|
25
|
+
}
|
|
26
|
+
body {
|
|
27
|
+
background: #00a8a9;
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
min-height: 100vh;
|
|
32
|
+
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
33
|
+
}
|
|
34
|
+
#__bundler_loading {
|
|
35
|
+
position: fixed;
|
|
36
|
+
bottom: 20px;
|
|
37
|
+
right: 20px;
|
|
38
|
+
font:
|
|
39
|
+
13px/1.4 -apple-system,
|
|
40
|
+
BlinkMacSystemFont,
|
|
41
|
+
sans-serif;
|
|
42
|
+
color: #666;
|
|
43
|
+
background: #fff;
|
|
44
|
+
padding: 8px 14px;
|
|
45
|
+
border-radius: 8px;
|
|
46
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
|
|
47
|
+
z-index: 10000;
|
|
48
|
+
}
|
|
49
|
+
#__bundler_thumbnail {
|
|
50
|
+
position: fixed;
|
|
51
|
+
inset: 0;
|
|
52
|
+
width: 100%;
|
|
53
|
+
height: 100%;
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
justify-content: center;
|
|
57
|
+
background: #00a8a9;
|
|
58
|
+
z-index: 9999;
|
|
59
|
+
}
|
|
60
|
+
#__bundler_thumbnail svg {
|
|
61
|
+
width: 100%;
|
|
62
|
+
height: 100%;
|
|
63
|
+
object-fit: contain;
|
|
64
|
+
}
|
|
65
|
+
#__bundler_placeholder {
|
|
66
|
+
color: #999;
|
|
67
|
+
font-size: 14px;
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
70
|
+
<noscript>
|
|
71
|
+
<style>
|
|
72
|
+
#__bundler_loading {
|
|
73
|
+
display: none;
|
|
74
|
+
}
|
|
75
|
+
</style>
|
|
76
|
+
<div
|
|
77
|
+
style="
|
|
78
|
+
position: fixed;
|
|
79
|
+
bottom: 12px;
|
|
80
|
+
left: 12px;
|
|
81
|
+
font:
|
|
82
|
+
13px/1.4 -apple-system,
|
|
83
|
+
BlinkMacSystemFont,
|
|
84
|
+
sans-serif;
|
|
85
|
+
color: #999;
|
|
86
|
+
background: rgba(255, 255, 255, 0.9);
|
|
87
|
+
padding: 6px 12px;
|
|
88
|
+
border-radius: 6px;
|
|
89
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
|
90
|
+
z-index: 10000;
|
|
91
|
+
"
|
|
92
|
+
>
|
|
93
|
+
This page requires JavaScript to display.
|
|
94
|
+
</div>
|
|
95
|
+
</noscript>
|
|
96
|
+
</head>
|
|
97
|
+
<body>
|
|
98
|
+
<div id="__bundler_thumbnail">
|
|
99
|
+
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
|
|
100
|
+
<rect width="1200" height="800" fill="#00A8A9"></rect>
|
|
101
|
+
<rect
|
|
102
|
+
x="360"
|
|
103
|
+
y="250"
|
|
104
|
+
width="480"
|
|
105
|
+
height="300"
|
|
106
|
+
rx="36"
|
|
107
|
+
fill="#0A1B1C"
|
|
108
|
+
></rect>
|
|
109
|
+
<text
|
|
110
|
+
x="410"
|
|
111
|
+
y="430"
|
|
112
|
+
font-family="monospace"
|
|
113
|
+
font-size="120"
|
|
114
|
+
font-weight="700"
|
|
115
|
+
fill="#00A8A9"
|
|
116
|
+
>
|
|
117
|
+
$
|
|
118
|
+
</text>
|
|
119
|
+
<rect
|
|
120
|
+
x="520"
|
|
121
|
+
y="395"
|
|
122
|
+
width="170"
|
|
123
|
+
height="26"
|
|
124
|
+
rx="6"
|
|
125
|
+
fill="#ED6F2D"
|
|
126
|
+
></rect>
|
|
127
|
+
<rect
|
|
128
|
+
x="520"
|
|
129
|
+
y="445"
|
|
130
|
+
width="240"
|
|
131
|
+
height="20"
|
|
132
|
+
rx="6"
|
|
133
|
+
fill="#3a5a5b"
|
|
134
|
+
></rect>
|
|
135
|
+
</svg>
|
|
136
|
+
</div>
|
|
137
|
+
<div id="__bundler_loading">Unpacking...</div>
|
|
138
|
+
|
|
139
|
+
<script>
|
|
140
|
+
document.addEventListener("DOMContentLoaded", async function () {
|
|
141
|
+
const loading = document.getElementById("__bundler_loading");
|
|
142
|
+
function setStatus(msg) {
|
|
143
|
+
if (loading) loading.textContent = msg;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Error sink persists across replaceWith since it's on window, not the DOM.
|
|
147
|
+
window.addEventListener(
|
|
148
|
+
"error",
|
|
149
|
+
function (e) {
|
|
150
|
+
var p = document.body || document.documentElement;
|
|
151
|
+
var d =
|
|
152
|
+
document.getElementById("__bundler_err") ||
|
|
153
|
+
p.appendChild(document.createElement("div"));
|
|
154
|
+
d.id = "__bundler_err";
|
|
155
|
+
d.style.cssText =
|
|
156
|
+
"position:fixed;bottom:12px;left:12px;right:12px;font:12px/1.4 ui-monospace,monospace;background:#2a1215;color:#ff8a80;padding:10px 14px;border-radius:8px;border:1px solid #5c2b2e;z-index:99999;white-space:pre-wrap;max-height:40vh;overflow:auto";
|
|
157
|
+
d.textContent =
|
|
158
|
+
(d.textContent ? d.textContent + String.fromCharCode(10) : "") +
|
|
159
|
+
"[bundle] " +
|
|
160
|
+
(e.message || e.type) +
|
|
161
|
+
(e.filename
|
|
162
|
+
? " (" + e.filename.slice(0, 60) + ":" + e.lineno + ")"
|
|
163
|
+
: "");
|
|
164
|
+
},
|
|
165
|
+
true,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const manifestEl = document.querySelector(
|
|
170
|
+
'script[type="__bundler/manifest"]',
|
|
171
|
+
);
|
|
172
|
+
const templateEl = document.querySelector(
|
|
173
|
+
'script[type="__bundler/template"]',
|
|
174
|
+
);
|
|
175
|
+
if (!manifestEl || !templateEl) {
|
|
176
|
+
setStatus("Error: missing bundle data");
|
|
177
|
+
console.error(
|
|
178
|
+
"[bundler] Missing script tags — manifestEl:",
|
|
179
|
+
!!manifestEl,
|
|
180
|
+
"templateEl:",
|
|
181
|
+
!!templateEl,
|
|
182
|
+
);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const manifest = JSON.parse(manifestEl.textContent);
|
|
187
|
+
let template = JSON.parse(templateEl.textContent);
|
|
188
|
+
|
|
189
|
+
const uuids = Object.keys(manifest);
|
|
190
|
+
setStatus("Unpacking " + uuids.length + " assets...");
|
|
191
|
+
|
|
192
|
+
const blobUrls = {};
|
|
193
|
+
await Promise.all(
|
|
194
|
+
uuids.map(async (uuid) => {
|
|
195
|
+
const entry = manifest[uuid];
|
|
196
|
+
try {
|
|
197
|
+
const binaryStr = atob(entry.data);
|
|
198
|
+
const bytes = new Uint8Array(binaryStr.length);
|
|
199
|
+
for (let i = 0; i < binaryStr.length; i++)
|
|
200
|
+
bytes[i] = binaryStr.charCodeAt(i);
|
|
201
|
+
|
|
202
|
+
let finalBytes = bytes;
|
|
203
|
+
if (entry.compressed) {
|
|
204
|
+
if (typeof DecompressionStream !== "undefined") {
|
|
205
|
+
const ds = new DecompressionStream("gzip");
|
|
206
|
+
const writer = ds.writable.getWriter();
|
|
207
|
+
const reader = ds.readable.getReader();
|
|
208
|
+
writer.write(bytes);
|
|
209
|
+
writer.close();
|
|
210
|
+
const chunks = [];
|
|
211
|
+
let totalLen = 0;
|
|
212
|
+
while (true) {
|
|
213
|
+
const { done, value } = await reader.read();
|
|
214
|
+
if (done) break;
|
|
215
|
+
chunks.push(value);
|
|
216
|
+
totalLen += value.length;
|
|
217
|
+
}
|
|
218
|
+
finalBytes = new Uint8Array(totalLen);
|
|
219
|
+
let offset = 0;
|
|
220
|
+
for (const chunk of chunks) {
|
|
221
|
+
finalBytes.set(chunk, offset);
|
|
222
|
+
offset += chunk.length;
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
console.warn(
|
|
226
|
+
"DecompressionStream not available, asset " +
|
|
227
|
+
uuid +
|
|
228
|
+
" may not render",
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
blobUrls[uuid] = URL.createObjectURL(
|
|
234
|
+
new Blob([finalBytes], { type: entry.mime }),
|
|
235
|
+
);
|
|
236
|
+
} catch (err) {
|
|
237
|
+
console.error("Failed to decode asset " + uuid + ":", err);
|
|
238
|
+
blobUrls[uuid] = URL.createObjectURL(
|
|
239
|
+
new Blob([], { type: entry.mime }),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
const extResEl = document.querySelector(
|
|
246
|
+
'script[type="__bundler/ext_resources"]',
|
|
247
|
+
);
|
|
248
|
+
const extResources = extResEl ? JSON.parse(extResEl.textContent) : [];
|
|
249
|
+
const resourceMap = {};
|
|
250
|
+
for (const entry of extResources) {
|
|
251
|
+
if (blobUrls[entry.uuid])
|
|
252
|
+
resourceMap[entry.id] = blobUrls[entry.uuid];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
setStatus("Rendering...");
|
|
256
|
+
for (const uuid of uuids)
|
|
257
|
+
template = template.split(uuid).join(blobUrls[uuid]);
|
|
258
|
+
|
|
259
|
+
// Strip integrity + crossorigin — blob URLs from a file:// document inherit
|
|
260
|
+
// a null origin, so crossorigin forces a CORS fetch that SRI then rejects.
|
|
261
|
+
// The manifest bytes are ours; SRI protects against CDN compromise, not this.
|
|
262
|
+
template = template
|
|
263
|
+
.replace(/\s+integrity="[^"]*"/gi, "")
|
|
264
|
+
.replace(/\s+crossorigin="[^"]*"/gi, "");
|
|
265
|
+
|
|
266
|
+
const resourceScript =
|
|
267
|
+
"<script>window.__resources = " +
|
|
268
|
+
JSON.stringify(resourceMap)
|
|
269
|
+
.split("</" + "script>")
|
|
270
|
+
.join("<\\/" + "script>") +
|
|
271
|
+
";</" +
|
|
272
|
+
"script>";
|
|
273
|
+
// Inject after <head> so the DOCTYPE stays first; prepending the script
|
|
274
|
+
// would push the parser into quirks mode. DOMParser always emits a <head>
|
|
275
|
+
// (synthesizing one if the source HTML omitted it) but may carry
|
|
276
|
+
// attributes through, so match the full opening tag. slice() rather than
|
|
277
|
+
// replace() keeps us clear of $-pattern substitution in resourceScript.
|
|
278
|
+
const headOpen = template.match(/<head[^>]*>/i);
|
|
279
|
+
if (headOpen) {
|
|
280
|
+
const i = headOpen.index + headOpen[0].length;
|
|
281
|
+
template =
|
|
282
|
+
template.slice(0, i) + resourceScript + template.slice(i);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Parse the template and swap the root element. Scripts inserted via
|
|
286
|
+
// DOMParser/replaceWith are inert per spec — re-create each with
|
|
287
|
+
// createElement so they execute, awaiting onload for src scripts to
|
|
288
|
+
// preserve ordering (React before ReactDOM before Babel before text/babel).
|
|
289
|
+
const doc = new DOMParser().parseFromString(template, "text/html");
|
|
290
|
+
document.documentElement.replaceWith(doc.documentElement);
|
|
291
|
+
const dead = Array.from(document.scripts);
|
|
292
|
+
for (const old of dead) {
|
|
293
|
+
const s = document.createElement("script");
|
|
294
|
+
for (const a of old.attributes) s.setAttribute(a.name, a.value);
|
|
295
|
+
s.textContent = old.textContent;
|
|
296
|
+
// text/babel scripts with a src: fetch and inline. transformScriptTags
|
|
297
|
+
// does XHR against the src, but blob:null/ from a file:// origin is
|
|
298
|
+
// silently dropped. Inlining makes it a plain inline babel script,
|
|
299
|
+
// which transformScriptTags handles unconditionally.
|
|
300
|
+
if ((s.type === "text/babel" || s.type === "text/jsx") && s.src) {
|
|
301
|
+
const r = await fetch(s.src);
|
|
302
|
+
s.textContent = await r.text();
|
|
303
|
+
s.removeAttribute("src");
|
|
304
|
+
}
|
|
305
|
+
const p = s.src
|
|
306
|
+
? new Promise(function (r) {
|
|
307
|
+
s.onload = s.onerror = r;
|
|
308
|
+
})
|
|
309
|
+
: null;
|
|
310
|
+
old.replaceWith(s);
|
|
311
|
+
if (p) await p;
|
|
312
|
+
}
|
|
313
|
+
// Babel standalone auto-transforms type=text/babel on DOMContentLoaded,
|
|
314
|
+
// which fired before we swapped the document. Trigger manually if present.
|
|
315
|
+
if (
|
|
316
|
+
window.Babel &&
|
|
317
|
+
typeof window.Babel.transformScriptTags === "function"
|
|
318
|
+
) {
|
|
319
|
+
window.Babel.transformScriptTags();
|
|
320
|
+
}
|
|
321
|
+
} catch (err) {
|
|
322
|
+
setStatus("Error unpacking: " + err.message);
|
|
323
|
+
console.error("Bundle unpack error:", err);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
</script>
|
|
327
|
+
|
|
328
|
+
<script type="__bundler/manifest">
|
|
329
|
+
{}
|
|
330
|
+
</script>
|
|
331
|
+
|
|
332
|
+
<script type="__bundler/ext_resources">
|
|
333
|
+
[]
|
|
334
|
+
</script>
|
|
335
|
+
|
|
336
|
+
<script type="__bundler/template">
|
|
337
|
+
"<!DOCTYPE html>\n<html lang=\"en\"><head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>anpunkit — Deterministic guardrails for AI coding agents<\u002Ftitle>\n\n<style>\n/* ───────── Web-safe fonts (Tahoma carries Thai glyphs natively) ───────── */\n\n:root{\n --teal:#00A8A9; --teal-deep:#0A7374; --orange:#ED6F2D; --green:#4CAF50;\n --ink:#16201F; --fg2:#4A5654; --fg3:#7C8785;\n --surface:#FFFFFF; --soft:#F2F8F8; --soft2:#EAF4F4; --border:#E2ECEB;\n --term:#0C2122; --term-2:#0A1B1C; --term-line:#1E3A3B;\n --radius:14px; --radius-sm:10px;\n --mono:Consolas,Menlo,Monaco,\"Courier New\",monospace;\n --sans:Tahoma,Verdana,Arial,Helvetica,sans-serif;\n --maxw:1180px;\n}\n\n*{box-sizing:border-box;margin:0;padding:0;}\nhtml{scroll-behavior:smooth;scroll-padding-top:88px;}\nbody{\n font-family:var(--sans); color:var(--ink); background:var(--surface);\n font-size:19px; line-height:1.6; -webkit-font-smoothing:antialiased;\n text-rendering:optimizeLegibility;\n}\n:lang(th){letter-spacing:0;}\nhtml[lang=\"th\"] body{font-size:20px;line-height:1.75;}\n\na{color:inherit;text-decoration:none;}\n.wrap{max-width:var(--maxw);margin:0 auto;padding:0 40px;}\n.eyebrow{\n font-size:14px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;\n color:var(--teal);display:inline-flex;align-items:center;gap:10px;\n}\nhtml[lang=\"th\"] .eyebrow{letter-spacing:.04em;}\n.eyebrow::before{content:\"\";width:22px;height:2px;background:var(--teal);border-radius:2px;}\n\n/* ───────── Nav ───────── */\nheader.nav{\n position:sticky;top:0;z-index:50;background:rgba(255,255,255,.82);\n backdrop-filter:saturate(160%) blur(14px);border-bottom:1px solid var(--border);\n}\n.nav-inner{display:flex;align-items:center;gap:28px;height:72px;}\n.brand{display:flex;align-items:baseline;gap:10px;font-weight:700;font-size:24px;letter-spacing:-.01em;}\n.brand .dot{color:var(--teal);}\n.brand .ver{font-family:var(--mono);font-size:12px;font-weight:600;color:var(--teal-deep);\n background:var(--soft2);padding:3px 8px;border-radius:20px;align-self:center;letter-spacing:0;}\n.nav-links{display:flex;gap:30px;margin-left:14px;}\n.nav-links a{font-size:16px;font-weight:500;color:var(--fg2);transition:color .15s;}\n.nav-links a:hover{color:var(--teal);}\n.nav-right{margin-left:auto;display:flex;align-items:center;gap:16px;}\n.lang-toggle{display:inline-flex;border:1px solid var(--border);border-radius:30px;overflow:hidden;background:var(--surface);}\n.lang-toggle button{\n font-family:var(--sans);font-size:14px;font-weight:600;border:none;background:transparent;\n color:var(--fg3);padding:7px 15px;cursor:pointer;transition:all .15s;line-height:1;\n}\n.lang-toggle button.active{background:var(--teal);color:#fff;}\n.gh-btn{display:inline-flex;align-items:center;gap:8px;font-size:15px;font-weight:600;\n color:var(--ink);border:1px solid var(--border);border-radius:30px;padding:8px 16px;transition:all .15s;}\n.gh-btn:hover{border-color:var(--teal);color:var(--teal);}\n.gh-btn svg{width:18px;height:18px;}\n\n/* ───────── Hero ───────── */\n.hero{padding:84px 0 72px;}\n.hero-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:64px;align-items:center;}\n.hero h1{font-size:62px;line-height:1.05;font-weight:700;letter-spacing:-.02em;margin:22px 0 24px;text-wrap:balance;}\nhtml[lang=\"th\"] .hero h1{line-height:1.25;letter-spacing:0;font-size:54px;}\n.hero h1 .accent{color:var(--teal);}\n.hero .sub{font-size:21px;line-height:1.6;color:var(--fg2);max-width:33ch;margin-bottom:34px;text-wrap:pretty;}\nhtml[lang=\"th\"] .hero .sub{max-width:42ch;}\n\n.install{margin-bottom:18px;}\n.install-label{font-size:14px;font-weight:600;color:var(--fg3);margin-bottom:10px;}\n.cmd{\n display:flex;align-items:center;gap:14px;background:var(--term);border-radius:var(--radius-sm);\n padding:16px 18px;font-family:var(--mono);font-size:16px;color:#EAF6F5;\n box-shadow:0 14px 34px -16px rgba(10,115,116,.5);max-width:430px;\n}\n.cmd .prompt{color:var(--teal);font-weight:700;}\n.cmd code{flex:1;letter-spacing:.2px;}\n.cmd .copy{\n border:none;background:rgba(255,255,255,.08);color:#BFE3E2;font-family:var(--mono);\n font-size:12px;font-weight:600;padding:6px 11px;border-radius:7px;cursor:pointer;transition:all .15s;letter-spacing:.05em;\n}\n.cmd .copy:hover{background:var(--teal);color:#fff;}\n.install-note{font-size:14px;color:var(--fg3);margin-top:12px;}\n\n.hero-cta{display:flex;gap:14px;margin-top:32px;flex-wrap:wrap;}\n.btn{display:inline-flex;align-items:center;gap:9px;font-family:var(--sans);font-size:16px;font-weight:600;\n padding:13px 24px;border-radius:30px;cursor:pointer;transition:all .16s;border:1px solid transparent;}\n.btn-primary{background:var(--teal);color:#fff;box-shadow:0 10px 24px -10px rgba(0,168,169,.7);}\n.btn-primary:hover{background:var(--teal-deep);transform:translateY(-1px);}\n.btn-ghost{background:transparent;color:var(--ink);border-color:var(--border);}\n.btn-ghost:hover{border-color:var(--teal);color:var(--teal);}\n\n/* gradient panel */\n.hero-panel{\n position:relative;border-radius:22px;padding:30px;min-height:380px;\n background:linear-gradient(158deg,#00A8A9 0%,#16b3a4 38%,#ED6F2D 130%);\n box-shadow:0 30px 60px -24px rgba(10,115,116,.55);overflow:hidden;\n display:flex;align-items:center;justify-content:center;\n}\n.hero-panel::before{content:\"\";position:absolute;inset:0;\n background:radial-gradient(120% 90% at 15% 0%,rgba(255,255,255,.28),transparent 55%);}\n.hero-panel .grid-lines{position:absolute;inset:0;opacity:.16;\n background-image:linear-gradient(rgba(255,255,255,.6) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.6) 1px,transparent 1px);\n background-size:38px 38px;mask-image:radial-gradient(120% 100% at 70% 30%,#000,transparent 80%);}\n.term-card{\n position:relative;width:100%;max-width:400px;background:var(--term-2);border-radius:14px;\n box-shadow:0 22px 48px -16px rgba(0,0,0,.55);border:1px solid rgba(255,255,255,.08);overflow:hidden;\n}\n.term-bar{display:flex;align-items:center;gap:7px;padding:13px 16px;border-bottom:1px solid var(--term-line);}\n.term-bar .d{width:11px;height:11px;border-radius:50%;background:#33514f;}\n.term-bar .d.t{background:var(--teal);}\n.term-bar .label{margin-left:8px;font-family:var(--mono);font-size:12px;color:#6E8F8D;letter-spacing:.04em;}\n.term-body{padding:18px 18px 22px;font-family:var(--mono);font-size:13.5px;line-height:1.85;color:#CFE7E5;}\n.term-body .pr{color:var(--teal);}\n.term-body .ev{color:#fff;font-weight:600;}\n.term-body .ar{color:#7FD4D2;}\n.term-body .ok{color:var(--green);}\n.term-body .cm{color:#5E807E;}\n.term-body .or{color:#F2925E;}\n\n/* ───────── Principle band ───────── */\n.principle{background:var(--ink);color:#fff;padding:62px 0;}\n.principle .wrap{display:grid;grid-template-columns:auto 1fr;gap:50px;align-items:center;}\n.principle .ptag{font-size:14px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--teal);\n writing-mode:horizontal-tb;white-space:nowrap;}\nhtml[lang=\"th\"] .principle .ptag{letter-spacing:.03em;}\n.principle blockquote{font-size:30px;line-height:1.5;font-weight:500;letter-spacing:-.01em;text-wrap:pretty;}\nhtml[lang=\"th\"] .principle blockquote{font-size:27px;line-height:1.7;letter-spacing:0;}\n.principle blockquote b{color:#5FE0DE;font-weight:700;}\n\n/* ───────── Section base ───────── */\nsection.block{padding:96px 0;}\n.block.tint{background:var(--soft);border-top:1px solid var(--border);border-bottom:1px solid var(--border);}\n.sec-head{max-width:720px;margin-bottom:54px;}\n.sec-head h2{font-size:44px;line-height:1.12;font-weight:700;letter-spacing:-.02em;margin:18px 0 18px;text-wrap:balance;}\nhtml[lang=\"th\"] .sec-head h2{line-height:1.3;letter-spacing:0;font-size:40px;}\n.sec-head p{font-size:19px;line-height:1.65;color:var(--fg2);text-wrap:pretty;}\n\n/* problem grid */\n.fail-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:22px;}\n.fail-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);\n padding:26px 26px 28px;transition:transform .18s,box-shadow .18s;display:flex;flex-direction:column;}\n.fail-card:hover{transform:translateY(-3px);box-shadow:0 18px 38px -22px rgba(10,115,116,.4);}\n.fail-num{font-family:var(--mono);font-size:13px;font-weight:700;color:var(--teal);margin-bottom:14px;letter-spacing:.05em;}\n.fail-problem{font-size:18px;font-weight:600;line-height:1.4;color:var(--ink);margin-bottom:16px;}\n.fail-div{height:1px;background:var(--border);margin:0 0 16px;}\n.fail-fixrow{display:flex;gap:11px;align-items:flex-start;}\n.fail-fixrow .fx{flex:none;width:20px;height:20px;border-radius:6px;background:var(--soft2);color:var(--teal);\n display:grid;place-items:center;margin-top:3px;}\n.fail-fixrow .fx svg{width:13px;height:13px;}\n.fail-fix{font-size:15.5px;line-height:1.5;color:var(--fg2);}\n.fail-fix code{font-family:var(--mono);font-size:13px;color:var(--teal-deep);background:var(--soft2);\n padding:1px 6px;border-radius:5px;letter-spacing:0;}\n\n/* ───────── Architecture ───────── */\n.layers{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;}\n.layer{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:30px 28px;display:flex;flex-direction:column;}\n.layer-top{display:flex;align-items:center;gap:13px;margin-bottom:8px;}\n.layer-ico{width:42px;height:42px;border-radius:11px;display:grid;place-items:center;background:var(--soft2);color:var(--teal-deep);}\n.layer-ico svg{width:22px;height:22px;}\n.layer-kicker{font-family:var(--mono);font-size:12px;font-weight:600;color:var(--fg3);letter-spacing:.06em;text-transform:uppercase;}\n.layer h3{font-size:23px;font-weight:700;letter-spacing:-.01em;}\n.layer .ldesc{font-size:16px;line-height:1.55;color:var(--fg2);margin:6px 0 22px;text-wrap:pretty;}\n.layer ul{list-style:none;display:flex;flex-direction:column;gap:14px;margin-top:auto;}\n.layer li{display:flex;flex-direction:column;gap:3px;padding-top:14px;border-top:1px solid var(--border);}\n.layer li:first-child{border-top:none;padding-top:0;}\n.layer li .name{font-family:var(--mono);font-size:14px;font-weight:600;color:var(--ink);letter-spacing:0;}\n.layer li .role{font-size:14.5px;line-height:1.45;color:var(--fg3);}\n.agent-tags{display:flex;flex-wrap:wrap;gap:8px;margin-top:4px;}\n.agent-tags span{font-family:var(--mono);font-size:12.5px;color:var(--teal-deep);background:var(--soft2);\n padding:5px 10px;border-radius:20px;letter-spacing:0;}\n\n.orch-note{margin-top:30px;background:var(--soft2);border-radius:var(--radius);padding:24px 30px;\n display:flex;align-items:center;gap:16px;}\n.orch-note .rt{width:30px;height:30px;flex:none;border-radius:50%;background:var(--teal);color:#fff;display:grid;place-items:center;}\n.orch-note .rt svg{width:17px;height:17px;}\n.orch-note p{font-size:17px;color:var(--ink);line-height:1.5;}\n.orch-note b{color:var(--teal-deep);}\n\n/* ───────── Install ───────── */\n.install-grid{display:grid;grid-template-columns:1fr 1fr;gap:34px;align-items:start;}\n.code-card{background:var(--term);border-radius:var(--radius);overflow:hidden;\n box-shadow:0 24px 50px -26px rgba(10,33,34,.7);}\n.code-card .cc-bar{display:flex;align-items:center;gap:7px;padding:14px 18px;border-bottom:1px solid var(--term-line);}\n.code-card .cc-bar .d{width:11px;height:11px;border-radius:50%;background:#33514f;}\n.code-card .cc-bar .d.t{background:var(--teal);}\n.code-card .cc-bar .label{margin-left:8px;font-family:var(--mono);font-size:12px;color:#6E8F8D;letter-spacing:.04em;}\n.code-card pre{padding:22px 22px 26px;font-family:var(--mono);font-size:14.5px;line-height:1.95;color:#D6ECEA;overflow-x:auto;}\n.code-card .pr{color:var(--teal);}\n.code-card .cm{color:#5E807E;}\n.code-card .fl{color:var(--orange);}\n.code-card .cmd2{color:#fff;}\n.code-card .gn{color:#7FD4A8;}\n\n.col-title{font-size:15px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--fg3);margin-bottom:16px;}\n.steps{display:flex;flex-direction:column;gap:4px;}\n.step{display:flex;gap:16px;padding:18px 0;border-bottom:1px solid var(--border);}\n.step:last-child{border-bottom:none;}\n.step .sn{font-family:var(--mono);font-size:14px;font-weight:700;color:var(--teal);flex:none;width:26px;}\n.step .stx{font-size:16.5px;line-height:1.5;color:var(--fg2);}\n.step .stx b{color:var(--ink);font-weight:600;}\n.step .stx code{font-family:var(--mono);font-size:13px;color:var(--teal-deep);background:var(--soft2);padding:1px 6px;border-radius:5px;letter-spacing:0;}\n.flags-note{margin-top:14px;font-size:14px;color:var(--fg3);}\n.flags-note code{font-family:var(--mono);color:var(--teal-deep);letter-spacing:0;}\n\n/* ───────── Workflow / loop ───────── */\n.loop{display:grid;grid-template-columns:repeat(5,1fr);gap:14px;margin-bottom:18px;}\n.loop-node{position:relative;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);\n padding:22px 20px 24px;display:flex;flex-direction:column;}\n.loop-node::after{content:\"\";position:absolute;right:-11px;top:50%;width:14px;height:14px;\n border-top:2px solid var(--border);border-right:2px solid var(--border);transform:translateY(-50%) rotate(45deg);z-index:2;background:transparent;}\n.loop-node:last-child::after{display:none;}\n.loop-node .st-no{font-family:var(--mono);font-size:12px;font-weight:700;color:var(--fg3);letter-spacing:.08em;margin-bottom:12px;}\n.loop-node .st-name{font-family:var(--mono);font-size:16px;font-weight:700;letter-spacing:.02em;color:var(--teal-deep);margin-bottom:9px;}\n.loop-node .st-desc{font-size:14.5px;line-height:1.5;color:var(--fg2);text-wrap:pretty;}\n.loop-node.red .st-name{color:var(--orange);}\n.loop-node.red{border-color:#F6D2BC;background:linear-gradient(180deg,#FFF6F0,#fff);}\n.loop-node.green .st-name{color:var(--green);}\n.loop-node.green{border-color:#C5E6C6;background:linear-gradient(180deg,#F2FBF2,#fff);}\n.loop-foot{display:flex;flex-wrap:wrap;gap:8px 26px;font-size:15.5px;color:var(--fg2);margin-top:6px;}\n.loop-foot b{color:var(--ink);font-weight:600;}\n.loop-foot code{font-family:var(--mono);font-size:13px;color:var(--teal-deep);background:var(--soft2);padding:1px 6px;border-radius:5px;letter-spacing:0;}\n\n.cmd-block{margin-top:50px;}\n.cmd-block + .cmd-block{margin-top:42px;}\n.cmd-grouptitle{display:flex;align-items:baseline;gap:14px;margin-bottom:22px;flex-wrap:wrap;}\n.cmd-grouptitle .gt{font-size:22px;font-weight:700;letter-spacing:-.01em;color:var(--ink);}\n.cmd-grouptitle .gh{font-size:15px;color:var(--fg3);}\n\n/* lead callout */\n.cmd-lead{margin-top:54px;background:var(--ink);color:#fff;border-radius:var(--radius);\n padding:30px 36px;display:flex;align-items:center;gap:24px;flex-wrap:wrap;}\n.cmd-lead .big{font-size:25px;font-weight:600;line-height:1.4;letter-spacing:-.01em;flex:1;min-width:300px;text-wrap:pretty;}\nhtml[lang=\"th\"] .cmd-lead .big{font-size:22px;line-height:1.6;letter-spacing:0;}\n.cmd-lead .big b{color:#5FE0DE;font-weight:700;}\n.cmd-lead .chips{display:flex;gap:10px;flex-wrap:wrap;}\n.cmd-lead .chips span{font-family:var(--mono);font-size:14px;font-weight:600;color:#BFE3E2;\n background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.14);padding:8px 14px;border-radius:30px;letter-spacing:0;}\n.cmd-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;}\n.cmd-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:24px 24px 26px;\n display:flex;flex-direction:column;transition:transform .18s,box-shadow .18s;}\n.cmd-card:hover{transform:translateY(-3px);box-shadow:0 18px 38px -22px rgba(10,115,116,.4);}\n.cmd-card.accent{border-color:#BfE4E4;background:linear-gradient(180deg,var(--soft),#fff);}\n.cmd-card .cname{font-family:var(--mono);font-size:17px;font-weight:700;color:var(--teal-deep);margin-bottom:12px;letter-spacing:0;}\n.cmd-card .cdesc{font-size:15.5px;line-height:1.55;color:var(--fg2);text-wrap:pretty;}\n.cmd-card .cdesc code{font-family:var(--mono);font-size:13px;color:var(--teal-deep);background:var(--soft2);padding:1px 6px;border-radius:5px;letter-spacing:0;}\n.cmd-card .cdesc b{color:var(--ink);font-weight:600;}\n\n/* auto-run group (de-emphasized) */\n.auto-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;}\n.auto-card{background:var(--soft);border:1px dashed var(--border);border-radius:var(--radius);padding:22px 22px 24px;display:flex;flex-direction:column;}\n.auto-card .ac-top{display:flex;align-items:center;gap:10px;margin-bottom:10px;}\n.auto-card .cname{font-family:var(--mono);font-size:15px;font-weight:700;color:var(--fg2);letter-spacing:0;}\n.auto-badge{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.12em;color:var(--teal-deep);\n background:var(--soft2);border:1px solid #C9E6E6;padding:3px 8px;border-radius:20px;}\n.auto-card .cdesc{font-size:14.5px;line-height:1.5;color:var(--fg3);text-wrap:pretty;}\n.auto-card .cdesc code{font-family:var(--mono);font-size:12.5px;color:var(--teal-deep);background:var(--soft2);padding:1px 6px;border-radius:5px;letter-spacing:0;}\n\n/* ───────── Features / capability catalog ───────── */\n.ver-rail{display:flex;flex-wrap:wrap;gap:12px;margin-bottom:46px;}\n.ver-step{flex:1;min-width:138px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);\n padding:14px 16px;display:flex;flex-direction:column;gap:5px;}\n.ver-step.cur{border-color:#9CDAD9;background:linear-gradient(180deg,var(--soft),#fff);}\n.ver-step .vn{font-family:var(--mono);font-size:14px;font-weight:700;color:var(--teal-deep);letter-spacing:0;}\n.ver-step .vl{font-size:13px;line-height:1.4;color:var(--fg3);text-wrap:pretty;}\n.ver-step.cur .vl{color:var(--fg2);}\n\n.feat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;}\n.feat-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);\n padding:28px 26px 14px;display:flex;flex-direction:column;transition:transform .18s,box-shadow .18s;}\n.feat-card:hover{transform:translateY(-3px);box-shadow:0 18px 38px -22px rgba(10,115,116,.4);}\n.feat-head{display:flex;align-items:center;gap:13px;margin-bottom:6px;}\n.feat-ico{width:42px;height:42px;flex:none;border-radius:11px;display:grid;place-items:center;background:var(--soft2);color:var(--teal-deep);}\n.feat-ico svg{width:22px;height:22px;}\n.feat-card h3{font-size:20px;font-weight:700;letter-spacing:-.01em;color:var(--ink);}\nhtml[lang=\"th\"] .feat-card h3{letter-spacing:0;}\n.feat-list{list-style:none;display:flex;flex-direction:column;margin-top:8px;}\n.feat-list li{display:flex;gap:14px;justify-content:space-between;align-items:flex-start;\n padding:14px 0;border-top:1px solid var(--border);}\n.feat-list li:first-child{border-top:none;}\n.feat-list .ftx{flex:1;font-size:15px;line-height:1.5;color:var(--fg2);text-wrap:pretty;}\n.feat-list .ftx b{color:var(--ink);font-weight:600;}\n.feat-list .fver{flex:none;align-self:flex-start;margin-top:1px;font-family:var(--mono);font-size:11px;font-weight:600;\n color:var(--teal-deep);background:var(--soft2);border:1px solid #D3EAEA;padding:3px 8px;border-radius:20px;letter-spacing:0;white-space:nowrap;}\n\n/* compact install band */\n.install-band{display:grid;grid-template-columns:1.1fr .9fr;gap:48px;align-items:center;}\n.install-band .ib-cmd{display:flex;align-items:center;gap:14px;background:var(--term);border-radius:var(--radius-sm);\n padding:18px 20px;font-family:var(--mono);font-size:17px;color:#EAF6F5;box-shadow:0 16px 38px -18px rgba(10,115,116,.5);margin:18px 0 14px;}\n.install-band .ib-cmd .prompt{color:var(--teal);font-weight:700;}\n.install-band .ib-cmd code{flex:1;letter-spacing:.2px;}\n.install-band .ib-cmd .copy{border:none;background:rgba(255,255,255,.08);color:#BFE3E2;font-family:var(--mono);\n font-size:12px;font-weight:600;padding:7px 12px;border-radius:7px;cursor:pointer;transition:all .15s;letter-spacing:.05em;}\n.install-band .ib-cmd .copy:hover{background:var(--teal);color:#fff;}\n.ib-note{font-size:15px;color:var(--fg3);}\n.ib-flags{display:flex;flex-direction:column;gap:14px;}\n.ib-flag{display:flex;gap:14px;align-items:baseline;}\n.ib-flag .fk{font-family:var(--mono);font-size:14px;font-weight:600;color:var(--teal-deep);background:var(--soft2);\n padding:4px 10px;border-radius:7px;flex:none;letter-spacing:0;}\n.ib-flag .fv{font-size:15px;color:var(--fg2);line-height:1.45;}\n\n/* ───────── Footer ───────── */\nfooter{background:var(--ink);color:#C8D4D2;padding:70px 0 44px;}\n.foot-top{display:flex;justify-content:space-between;align-items:flex-start;gap:40px;flex-wrap:wrap;\n padding-bottom:40px;border-bottom:1px solid rgba(255,255,255,.1);}\n.foot-tag{font-size:27px;font-weight:500;color:#fff;letter-spacing:-.01em;max-width:16ch;line-height:1.35;text-wrap:balance;}\nhtml[lang=\"th\"] .foot-tag{font-size:24px;line-height:1.55;letter-spacing:0;max-width:24ch;}\n.foot-tag .q{color:var(--teal);}\n.foot-links{display:flex;gap:56px;}\n.foot-col h4{font-size:13px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--teal);margin-bottom:16px;}\n.foot-col a,.foot-col span{display:block;font-size:16px;color:#AFC0BE;margin-bottom:11px;transition:color .15s;}\n.foot-col a:hover{color:#fff;}\n.foot-col .mono{font-family:var(--mono);font-size:14px;letter-spacing:0;}\n.foot-bottom{display:flex;justify-content:space-between;align-items:center;gap:20px;padding-top:30px;flex-wrap:wrap;}\n.foot-bottom p{font-size:14px;color:#7C8D8B;}\n.foot-bottom .reqs{font-family:var(--mono);font-size:13px;color:#7C8D8B;letter-spacing:0;}\n\n/* reveal */\n.reveal{opacity:0;transform:translateY(20px);transition:opacity .7s ease,transform .7s ease;}\n.reveal.in{opacity:1;transform:none;}\n@media (prefers-reduced-motion:reduce){.reveal{opacity:1;transform:none;transition:none;}}\n\n/* responsive */\n@media (max-width:980px){\n .hero-grid{grid-template-columns:1fr;gap:44px;}\n .hero h1{font-size:50px;}\n .layers,.fail-grid{grid-template-columns:1fr;}\n .feat-grid{grid-template-columns:1fr 1fr;}\n .install-grid{grid-template-columns:1fr;}\n .principle .wrap{grid-template-columns:1fr;gap:20px;}\n .nav-links{display:none;}\n .loop{grid-template-columns:1fr 1fr;}\n .loop-node::after{display:none;}\n .cmd-grid{grid-template-columns:1fr 1fr;}\n .auto-grid{grid-template-columns:1fr 1fr;}\n .install-band{grid-template-columns:1fr;gap:32px;}\n}\n@media (max-width:560px){\n .wrap{padding:0 22px;}\n .hero h1{font-size:38px;}html[lang=\"th\"] .hero h1{font-size:34px;}\n .sec-head h2{font-size:32px;}\n .foot-links{gap:34px;}\n .loop,.cmd-grid{grid-template-columns:1fr;}\n .auto-grid,.feat-grid{grid-template-columns:1fr;}\n}\n<\u002Fstyle>\n<\u002Fhead>\n<body>\n\n\n\n<!-- ───────── NAV ───────── -->\n<header class=\"nav\">\n <div class=\"wrap nav-inner\">\n <div class=\"brand\"><span>an<span class=\"dot\">punkit<\u002Fspan><\u002Fspan><span class=\"ver\">v2.0.2<\u002Fspan><\u002Fdiv>\n <nav class=\"nav-links\">\n <a href=\"#problem\" data-en=\"Problem\" data-th=\"ปัญหา\">Problem<\u002Fa>\n <a href=\"#architecture\" data-en=\"Architecture\" data-th=\"สถาปัตยกรรม\">Architecture<\u002Fa>\n <a href=\"#workflow\" data-en=\"Workflow\" data-th=\"วงจรการทำงาน\">Workflow<\u002Fa>\n <a href=\"#features\" data-en=\"Features\" data-th=\"ฟีเจอร์\">Features<\u002Fa>\n <a href=\"#install\" data-en=\"Install\" data-th=\"ติดตั้ง\">Install<\u002Fa>\n <\u002Fnav>\n <div class=\"nav-right\">\n <div class=\"lang-toggle\" role=\"group\" aria-label=\"Language\">\n <button data-lang=\"en\" class=\"active\">EN<\u002Fbutton>\n <button data-lang=\"th\">ไทย<\u002Fbutton>\n <\u002Fdiv>\n <a class=\"gh-btn\" href=\"https://github.com/MetheeS/anpunkit\" target=\"_blank\" rel=\"noopener\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.58 2 12.25c0 4.53 2.87 8.37 6.84 9.73.5.1.68-.22.68-.49v-1.7c-2.78.62-3.37-1.37-3.37-1.37-.45-1.18-1.11-1.5-1.11-1.5-.91-.64.07-.62.07-.62 1 .07 1.53 1.06 1.53 1.06.9 1.57 2.34 1.12 2.91.86.09-.66.35-1.12.63-1.38-2.22-.26-4.55-1.14-4.55-5.05 0-1.12.39-2.03 1.03-2.74-.1-.26-.45-1.3.1-2.71 0 0 .84-.28 2.75 1.05A9.3 9.3 0 0112 6.84c.85 0 1.71.12 2.51.34 1.91-1.33 2.75-1.05 2.75-1.05.55 1.41.2 2.45.1 2.71.64.71 1.03 1.62 1.03 2.74 0 3.92-2.34 4.78-4.57 5.04.36.32.68.94.68 1.9v2.82c0 .27.18.6.69.49A10.26 10.26 0 0022 12.25C22 6.58 17.52 2 12 2z\"><\u002Fpath><\u002Fsvg>\n <span>GitHub<\u002Fspan>\n <\u002Fa>\n <\u002Fdiv>\n <\u002Fdiv>\n<\u002Fheader>\n\n<!-- ───────── HERO ───────── -->\n<section class=\"hero\">\n <div class=\"wrap hero-grid\">\n <div class=\"hero-left\">\n <span class=\"eyebrow\" data-en=\"Engineering methodology kit\" data-th=\"ชุดเครื่องมือระเบียบวิธีทางวิศวกรรม\">ชุดเครื่องมือพัฒนาซอฟต์แวร์ร่วมกับเอไอ<\u002Fspan>\n <h1 data-en=\"Deterministic guardrails for <span class='accent'>AI coding agents</span>.\" data-th=\"ตัวกั้นเชิงกำหนดแน่นอน สำหรับ <span class='accent'>AI coding agents</span>\">รูปแบบการทำงานที่แน่นอน สำหรับ AI coding agents<\u002Fh1>\n <p class=\"sub\" data-en=\"anpunkit turns a 10-point manual practice into architecture. The steps that must happen live in hooks — so they can't be skipped, forgotten, or reasoned around.\" data-th=\"anpunkit เปลี่ยนแนวปฏิบัติด้วยมือ 10 ข้อ ให้กลายเป็นสถาปัตยกรรม ขั้นตอนที่ต้องเกิดขึ้นถูกฝังอยู่ใน hooks จึงไม่ถูกข้าม ลืม หรือหาเหตุผลมาเลี่ยงได้\">anpunkit เปลี่ยนแนวปฏิบัติทั้ง 10 ข้อ ให้กลายเป็นสถาปัตยกรรม โดยขั้นตอนที่สำคัญจะถูกฝังอยู่ใน hooks จึงไม่ถูกข้าม ลืม หรือหาเหตุผลมาเลี่ยงได้<\u002Fp>\n\n <div class=\"install\">\n <div class=\"install-label\" data-en=\"Install in any project\" data-th=\"ติดตั้งในโปรเจกต์ใดก็ได้\">Install in any project<\u002Fdiv>\n <div class=\"cmd\">\n <span class=\"prompt\">$<\u002Fspan>\n <code id=\"install-cmd\">npx create-anpunkit<\u002Fcode>\n <button class=\"copy\" id=\"copyBtn\" data-en=\"COPY\" data-th=\"คัดลอก\">COPY<\u002Fbutton>\n <\u002Fdiv>\n <div class=\"install-note\" data-en=\"Non-destructive — it never clobbers your files. Requires Node ≥ 18.\" data-th=\"ไม่ทำลายไฟล์เดิม ไม่เขียนทับไฟล์ของคุณ ต้องใช้ Node ≥ 18\">Non-destructive — it never clobbers your files. Requires Node ≥ 18.<\u002Fdiv>\n <\u002Fdiv>\n\n <div class=\"hero-cta\">\n <a class=\"btn btn-primary\" href=\"#architecture\" data-en=\"Read the architecture\" data-th=\"อ่านสถาปัตยกรรม\">Read the architecture<\u002Fa>\n <a class=\"btn btn-ghost\" href=\"#install\" data-en=\"Quickstart\" data-th=\"เริ่มต้นใช้งาน\">Quickstart<\u002Fa>\n <\u002Fdiv>\n <\u002Fdiv>\n\n <div class=\"hero-panel\">\n <div class=\"grid-lines\"><\u002Fdiv>\n <div class=\"term-card\">\n <div class=\"term-bar\"><span class=\"d t\"><\u002Fspan><span class=\"d\"><\u002Fspan><span class=\"d\"><\u002Fspan><span class=\"label\">session-start.sh<\u002Fspan><\u002Fdiv>\n <div class=\"term-body\">\n<span class=\"pr\">●<\u002Fspan> <span class=\"ev\">new session<\u002Fspan><br>\n<span class=\"ar\">→<\u002Fspan> injecting STATE.md<br>\n<span class=\"ar\">→<\u002Fspan> 2 open issues <span class=\"cm\">// ISSUES.md<\u002Fspan><br>\n<span class=\"ar\">→<\u002Fspan> research INDEX + git state<br>\n<span class=\"ar\">→<\u002Fspan> <span class=\"or\">11 hard rules<\u002Fspan> loaded<br>\n<span class=\"ok\">✓<\u002Fspan> context restored — <span class=\"cm\">no re-orientation<\u002Fspan>\n <\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n<\u002Fsection>\n\n<!-- ───────── PRINCIPLE ───────── -->\n<div class=\"principle\">\n <div class=\"wrap\">\n <div class=\"ptag\" data-en=\"The principle\" data-th=\"หลักการ\" style=\"font-size: 40px\">The principle<\u002Fdiv>\n <blockquote data-en=\"Mandatory steps live in <b>hooks</b>. Discretionary steps live in <b>agents</b> and <b>skills</b>. Hooks are deterministic — they cannot be skipped, forgotten, or reasoned around.\" data-th=\"ขั้นตอนบังคับอยู่ใน <b>hooks</b> ขั้นตอนที่ใช้ดุลพินิจอยู่ใน <b>agents</b> และ <b>skills</b> hooks เป็นแบบกำหนดแน่นอน จึงไม่ถูกข้าม ลืม หรือหาเหตุผลมาเลี่ยงได้\">ขั้นตอนสำคัญอยู่ใน hooks ขั้นตอนที่ใช้ดุลพินิจอยู่ใน agents และ skills\nhooks เป็นแบบกำหนดชัดเจน จึงไม่ถูกข้าม ลืม หรือหาเหตุผลมาเลี่ยงได้<\u002Fblockquote>\n <\u002Fdiv>\n<\u002Fdiv>\n\n<!-- ───────── PROBLEM ───────── -->\n<section class=\"block\" id=\"problem\">\n <div class=\"wrap\">\n <div class=\"sec-head reveal\">\n <span class=\"eyebrow\" data-en=\"The problem\" data-th=\"ปัญหา\">The problem<\u002Fspan>\n <h2 data-en=\"Six recurring failures. Six structural fixes.\" data-th=\"ความล้มเหลวซ้ำซาก 6 แบบ การแก้เชิงโครงสร้าง 6 ข้อ\">จากปัญหาซ้ำซากทั้ง 6 แบบ การแก้เชิงโครงสร้าง 6 ข้อ<\u002Fh2>\n <p data-en=\"A disciplined 10-point manual practice still produced six predictable failures whenever an AI agent was involved. The root cause wasn't the model's capability — it was architecture. Every “must happen” step was buried inside a prompt, so the model could skip it, forget it, or reason its way around it.\" data-th=\"แนวปฏิบัติด้วยมือ 10 ข้อที่มีวินัย ก็ยังเกิดความล้มเหลวที่คาดเดาได้ 6 แบบ ทุกครั้งที่มี AI agent เข้ามาเกี่ยวข้อง ต้นเหตุไม่ใช่ความสามารถของโมเดล แต่เป็นเรื่องสถาปัตยกรรม ทุกขั้นตอนที่ “ต้องเกิดขึ้น” ถูกฝังอยู่ในพรอมป์ต โมเดลจึงข้าม ลืม หรือหาเหตุผลเลี่ยงมันได้\">แม้จะใช้แนวปฏิบัติทั้ง 10 ข้ออย่างมีวินัย ก็ยังเกิดความผิดพลาดที่คาดเดาได้ถึง 6 แบบ ทุกครั้งที่มี AI agent เข้ามาเกี่ยวข้อง ต้นเหตุไม่ใช่ความสามารถของโมเดล แต่เป็นเรื่องสถาปัตยกรรม ทุกขั้นตอนที่ “ต้องเกิดขึ้น” ถูกฝังอยู่ในพรอมต์ โมเดลจึงข้าม ลืม หรือหาเหตุผลเลี่ยงมันได้<\u002Fp>\n <\u002Fdiv>\n\n <div class=\"fail-grid\">\n <div class=\"fail-card reveal\">\n <div class=\"fail-num\">01<\u002Fdiv>\n <div class=\"fail-problem\" data-en=\"Issue log missed — agents repeated known dead ends.\" data-th=\"พลาดบันทึกปัญหา — agent วนทำทางตันที่เคยเจอซ้ำ\">พลาดบันทึกปัญหา — agent วนกับทางตันที่เคยเจอซ้ำไปมา<\u002Fdiv>\n <div class=\"fail-div\"><\u002Fdiv>\n <div class=\"fail-fixrow\"><span class=\"fx\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6L9 17l-5-5\"><\u002Fpath><\u002Fsvg><\u002Fspan>\n <div class=\"fail-fix\" data-en=\"The <code>session-start.sh</code> hook injects open ISSUES.md every session, unconditionally.\" data-th=\"hook <code>session-start.sh</code> จะแทรก ISSUES.md ที่ยังเปิดอยู่ทุกเซสชัน โดยไม่มีเงื่อนไข\">The <code>session-start.sh<\u002Fcode> hook injects open ISSUES.md every session, unconditionally.<\u002Fdiv><\u002Fdiv>\n <\u002Fdiv>\n <div class=\"fail-card reveal\">\n <div class=\"fail-num\">02<\u002Fdiv>\n <div class=\"fail-problem\" data-en=\"Manual re-orientation every session wasted time.\" data-th=\"ต้องทำความเข้าใจสถานะใหม่ทุกเซสชัน เสียเวลา\">ต้องทำความเข้าใจสถานะใหม่ทุกเซสชัน เสียเวลามมากมาย<\u002Fdiv>\n <div class=\"fail-div\"><\u002Fdiv>\n <div class=\"fail-fixrow\"><span class=\"fx\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6L9 17l-5-5\"><\u002Fpath><\u002Fsvg><\u002Fspan>\n <div class=\"fail-fix\" data-en=\"The same hook injects STATE.md, git state, and the research INDEX automatically.\" data-th=\"hook เดียวกันนี้แทรก STATE.md สถานะ git และ research INDEX ให้อัตโนมัติ\">The same hook injects STATE.md, git state, and the research INDEX automatically.<\u002Fdiv><\u002Fdiv>\n <\u002Fdiv>\n <div class=\"fail-card reveal\">\n <div class=\"fail-num\">03<\u002Fdiv>\n <div class=\"fail-problem\" data-en=\"Agents over-struggled on bugs — no circuit breaker.\" data-th=\"agent ดิ้นรนกับบั๊กเกินไป — ไม่มีตัวตัดวงจร\">agent ดิ้นรนแก้บั๊กไม่รู้จบ — ไม่มีใครหยุดให้ถอยมาวิเคราะห์<\u002Fdiv>\n <div class=\"fail-div\"><\u002Fdiv>\n <div class=\"fail-fixrow\"><span class=\"fx\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6L9 17l-5-5\"><\u002Fpath><\u002Fsvg><\u002Fspan>\n <div class=\"fail-fix\" data-en=\"A hard cap at 3 attempts: warn at 2, STOP at 3, and hand control back to you.\" data-th=\"จำกัดที่ 3 ครั้ง: เตือนเมื่อครั้งที่ 2 หยุดเมื่อครั้งที่ 3 แล้วคืนการควบคุมให้คุณ\">A hard cap at 3 attempts: warn at 2, STOP at 3, and hand control back to you.<\u002Fdiv><\u002Fdiv>\n <\u002Fdiv>\n <div class=\"fail-card reveal\">\n <div class=\"fail-num\">04<\u002Fdiv>\n <div class=\"fail-problem\" data-en=\"Context rot — debug noise filled the orchestrator window.\" data-th=\"คอนเท็กซ์เน่า — เสียงรบกวนจากการดีบักเต็มหน้าต่าง orchestrator\">Context rot — debug noise filled the orchestrator window.<\u002Fdiv>\n <div class=\"fail-div\"><\u002Fdiv>\n <div class=\"fail-fixrow\"><span class=\"fx\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6L9 17l-5-5\"><\u002Fpath><\u002Fsvg><\u002Fspan>\n <div class=\"fail-fix\" data-en=\"The debugger runs in an isolated context, writes noise to a file, and returns a terse summary only.\" data-th=\"debugger ทำงานในคอนเท็กซ์แยก เขียนเสียงรบกวนลงไฟล์ แล้วส่งกลับเฉพาะสรุปสั้น ๆ\">debugger ทำงานในคอนเท็กซ์แยก เขียนรายละเอียดลงไฟล์ แล้วส่งกลับเฉพาะสรุปสั้น ๆ<\u002Fdiv><\u002Fdiv>\n <\u002Fdiv>\n <div class=\"fail-card reveal\">\n <div class=\"fail-num\">05<\u002Fdiv>\n <div class=\"fail-problem\" data-en=\"No subagent orchestration — everything ran inline.\" data-th=\"ไม่มีการจัดวง subagent — ทุกอย่างรันรวมกันในที่เดียว\">ไม่มีการจัดวง subagent — ทุกอย่างทำงานรวมกันในที่เดียว<\u002Fdiv>\n <div class=\"fail-div\"><\u002Fdiv>\n <div class=\"fail-fixrow\"><span class=\"fx\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6L9 17l-5-5\"><\u002Fpath><\u002Fsvg><\u002Fspan>\n <div class=\"fail-fix\" data-en=\"8 scoped subagents; the orchestrator routes between them and never implements or debugs.\" data-th=\"subagent เฉพาะทาง 8 ตัว; orchestrator ทำหน้าที่ส่งต่องาน ไม่เขียนโค้ดหรือดีบักเอง\">8 scoped subagents; the orchestrator routes between them and never implements or debugs.<\u002Fdiv><\u002Fdiv>\n <\u002Fdiv>\n <div class=\"fail-card reveal\">\n <div class=\"fail-num\">06<\u002Fdiv>\n <div class=\"fail-problem\" data-en=\"Handoff files grew unbounded — STATE.md bloat.\" data-th=\"ไฟล์ส่งต่องานบวมไม่หยุด — STATE.md อ้วนขึ้นเรื่อย ๆ\">Handoff files grew unbounded — STATE.md bloat.<\u002Fdiv>\n <div class=\"fail-div\"><\u002Fdiv>\n <div class=\"fail-fixrow\"><span class=\"fx\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6L9 17l-5-5\"><\u002Fpath><\u002Fsvg><\u002Fspan>\n <div class=\"fail-fix\" data-en=\"The synthesizer plus the <code>pre-compact.sh</code> hook compress and dedup on every compaction.\" data-th=\"synthesizer ร่วมกับ hook <code>pre-compact.sh</code> บีบอัดและตัดข้อมูลซ้ำทุกครั้งที่บีบคอนเท็กซ์\">The synthesizer plus the <code>pre-compact.sh<\u002Fcode> hook compress and dedup on every compaction.<\u002Fdiv><\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n<\u002Fsection>\n\n<!-- ───────── ARCHITECTURE ───────── -->\n<section class=\"block tint\" id=\"architecture\">\n <div class=\"wrap\">\n <div class=\"sec-head reveal\">\n <span class=\"eyebrow\" data-en=\"Architecture\" data-th=\"สถาปัตยกรรม\">Architecture<\u002Fspan>\n <h2 data-en=\"Three fixed layers.\" data-th=\"สามชั้นที่กำหนดตายตัว\">การทำงานสามประสานที่ออกแบบไว้แล้ว<\u002Fh2>\n <p data-en=\"The kit is organized around three layers: deterministic hooks, orchestrator-mediated subagents, and declarative documents. The orchestrator reads and routes — it never writes code, runs tests, or debugs.\" data-th=\"ชุดเครื่องมือนี้จัดวางรอบสามชั้น: hooks แบบกำหนดแน่นอน, subagents ที่ orchestrator เป็นตัวกลาง, และเอกสารแบบประกาศ orchestrator อ่านและส่งต่องาน ไม่เขียนโค้ด รันเทสต์ หรือดีบักเอง\">ชุดเครื่องมือนี้จัดวางกรอบไว้สามส่วน: hooks ที่ทำงานแน่นอน, subagents ที่ orchestrator เป็นตัวกลาง, และเอกสารมาตรฐานที่ orchestrator อ่านและส่งต่องาน ไม่เขียนโค้ด รันเทสต์ หรือดีบักเอง<\u002Fp>\n <\u002Fdiv>\n\n <div class=\"layers\">\n <!-- Hooks -->\n <div class=\"layer reveal\">\n <div class=\"layer-top\">\n <div class=\"layer-ico\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M13 3v10a4 4 0 11-4-4h9\"><\u002Fpath><\u002Fsvg><\u002Fdiv>\n <div>\n <div class=\"layer-kicker\" data-en=\"Deterministic\" data-th=\"กำหนดแน่นอน\">Deterministic<\u002Fdiv>\n <h3>Hooks<\u002Fh3>\n <\u002Fdiv>\n <\u002Fdiv>\n <p class=\"ldesc\" data-en=\"Fire automatically at lifecycle points. They cannot be skipped, forgotten, or argued with.\" data-th=\"ทำงานอัตโนมัติตามจุดในวงจรชีวิต ไม่สามารถถูกข้าม ลืม หรือต่อรองได้\">ทำงานอัตโนมัติตามจุดที่ตั้งไว้ ไม่สามารถถูกข้าม ลืม หรือต่อรองได้<\u002Fp>\n <ul>\n <li><span class=\"name\">session-start.sh<\u002Fspan><span class=\"role\" data-en=\"Injects git state, STATE.md, open issues & research INDEX on every start.\" data-th=\"แทรกสถานะ git, STATE.md, ปัญหาที่เปิดอยู่ และ research INDEX ทุกครั้งที่เริ่ม\">Injects git state, STATE.md, open issues & research INDEX on every start.<\u002Fspan><\u002Fli>\n <li><span class=\"name\">pre-compact.sh<\u002Fspan><span class=\"role\" data-en=\"Snapshots STATE.md + git diff before any context loss.\" data-th=\"บันทึกสแนปช็อต STATE.md + git diff ก่อนสูญเสียคอนเท็กซ์\">Snapshots STATE.md + git diff before any context loss.<\u002Fspan><\u002Fli>\n <li><span class=\"name\">subagent-stop.sh<\u002Fspan><span class=\"role\" data-en=\"A lightweight trace line per subagent finish — audit without bloat.\" data-th=\"บันทึกร่องรอยสั้น ๆ เมื่อ subagent จบงาน ตรวจสอบได้โดยไม่บวม\">A lightweight trace line per subagent finish — audit without bloat.<\u002Fspan><\u002Fli>\n <\u002Ful>\n <\u002Fdiv>\n\n <!-- Subagents -->\n <div class=\"layer reveal\">\n <div class=\"layer-top\">\n <div class=\"layer-ico\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"7\" r=\"3\"><\u002Fcircle><circle cx=\"5\" cy=\"17\" r=\"2.5\"><\u002Fcircle><circle cx=\"19\" cy=\"17\" r=\"2.5\"><\u002Fcircle><path d=\"M12 10v3m0 0l-5 2m5-2l5 2\"><\u002Fpath><\u002Fsvg><\u002Fdiv>\n <div>\n <div class=\"layer-kicker\" data-en=\"Scoped workers\" data-th=\"ผู้ทำงานเฉพาะทาง\">Scoped workers<\u002Fdiv>\n <h3>Subagents<\u002Fh3>\n <\u002Fdiv>\n <\u002Fdiv>\n <p class=\"ldesc\" data-en=\"Eight scoped workers. The orchestrator routes between them; each runs in its own context.\" data-th=\"ผู้ทำงานเฉพาะทาง 8 ตัว orchestrator ส่งต่องานระหว่างกัน แต่ละตัวรันในคอนเท็กซ์ของตัวเอง\">ผู้ทำงานเฉพาะทาง 8 ตัว orchestrator ส่งต่องานระหว่างกัน แต่ละตัวรันในบริบทของตัวเอง<\u002Fp>\n <div class=\"agent-tags\">\n <span>researcher<\u002Fspan><span>planner<\u002Fspan><span>infra-provisioner<\u002Fspan><span>implementer<\u002Fspan><span>test-author<\u002Fspan><span>debugger<\u002Fspan><span>e2e-runner<\u002Fspan><span>synthesizer<\u002Fspan>\n <\u002Fdiv>\n <ul style=\"margin-top:22px;\">\n <li><span class=\"name\">test-author<\u002Fspan><span class=\"role\" data-en=\"Writes tests blind — never reads the implementation, so the suite can't be biased.\" data-th=\"เขียนเทสต์แบบปิดตา ไม่อ่านโค้ดจริง ชุดทดสอบจึงไม่ลำเอียง\">Writes tests blind — never reads the implementation, so the suite can't be biased.<\u002Fspan><\u002Fli>\n <li><span class=\"name\">debugger<\u002Fspan><span class=\"role\" data-en=\"Isolated context, hard 3-attempt cap, returns a terse summary.\" data-th=\"คอนเท็กซ์แยก จำกัด 3 ครั้ง ส่งกลับเฉพาะสรุปสั้น ๆ\">คอนเท็กซ์แยก จำกัด 3 ครั้ง ส่งกลับเฉพาะสรุปผลสั้น ๆเท่านั้น<\u002Fspan><\u002Fli>\n <\u002Ful>\n <\u002Fdiv>\n\n <!-- Documents -->\n <div class=\"layer reveal\">\n <div class=\"layer-top\">\n <div class=\"layer-ico\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14 3H7a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 002-2V8z\"><\u002Fpath><path d=\"M14 3v5h5M9 13h6M9 17h6\"><\u002Fpath><\u002Fsvg><\u002Fdiv>\n <div>\n <div class=\"layer-kicker\" data-en=\"State layer\" data-th=\"ชั้นสถานะ\">State layer<\u002Fdiv>\n <h3>Documents<\u002Fh3>\n <\u002Fdiv>\n <\u002Fdiv>\n <p class=\"ldesc\" data-en=\"A declarative state layer. Every rule lives in exactly one place — duplication is a defect.\" data-th=\"ชั้นสถานะแบบประกาศ ทุกกฎอยู่ในที่เดียวเท่านั้น การซ้ำคือข้อบกพร่อง\">ชั้นสถานะแบบมาตรฐาน ทุกกฎอยู่ในที่เดียวเท่านั้น การซ้ำคือข้อบกพร่อง<\u002Fp>\n <ul>\n <li><span class=\"name\">AGENTS.md<\u002Fspan><span class=\"role\" data-en=\"Single source of truth — all methodology, roles & hard rules. Tool-agnostic.\" data-th=\"แหล่งความจริงเดียว — ระเบียบวิธี บทบาท และกฎทั้งหมด ไม่ผูกกับเครื่องมือใด\">Single source of truth — all methodology, roles & hard rules. Tool-agnostic.<\u002Fspan><\u002Fli>\n <li><span class=\"name\">STATE.md<\u002Fspan><span class=\"role\" data-en=\"Current position. Rewritten, not appended. Small by design.\" data-th=\"ตำแหน่งปัจจุบัน เขียนทับใหม่ ไม่ต่อท้าย เล็กโดยตั้งใจ\">Current position. Rewritten, not appended. Small by design.<\u002Fspan><\u002Fli>\n <li><span class=\"name\">ISSUES.md · ENDPOINTS.md<\u002Fspan><span class=\"role\" data-en=\"The deduped error log and the API catalogue with a hard coverage gate.\" data-th=\"บันทึกข้อผิดพลาดที่ตัดซ้ำแล้ว และแคตาล็อก API พร้อมประตูตรวจความครอบคลุม\">บันทึกข้อผิดพลาดที่ตัดซ้ำแล้ว และแคตาล็อก API พร้อมช่องทางเชื่อมค่อทั้งหมด<\u002Fspan><\u002Fli>\n <\u002Ful>\n <\u002Fdiv>\n <\u002Fdiv>\n\n <div class=\"orch-note reveal\">\n <span class=\"rt\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 12h12m0 0l-4-4m4 4l-4 4\"><\u002Fpath><path d=\"M20 5v14\"><\u002Fpath><\u002Fsvg><\u002Fspan>\n <p data-en=\"<b>The orchestrator routes only.</b> The main session never implements or debugs, and subagents can never address the user directly.\" data-th=\"<b>orchestrator ทำหน้าที่ส่งต่องานเท่านั้น</b> เซสชันหลักไม่เขียนโค้ดหรือดีบัก และ subagent ไม่สามารถพูดกับผู้ใช้ได้โดยตรง\">The orchestrator routes only. The main session never implements or debugs, and subagents can never address the user directly.<\u002Fp>\n <\u002Fdiv>\n <\u002Fdiv>\n<\u002Fsection>\n\n<!-- ───────── INSTALL ───────── -->\n<section class=\"block\" id=\"workflow\">\n <div class=\"wrap\">\n <div class=\"sec-head reveal\">\n <span class=\"eyebrow\" data-en=\"Workflow\" data-th=\"วงจรการทำงาน\">Workflow<\u002Fspan>\n <h2 data-en=\"One phase at a time, tests before logic.\" data-th=\"ทำทีละเฟส เขียนเทสต์ก่อนตรรกะ\">One phase at a time, tests before logic.<\u002Fh2>\n <p data-en=\"You build the project as a sequence of vertical-slice phases. A phase that adds a public callable surface runs the test-first loop below; pure infra or doc phases run RESEARCH → IMPLEMENT → CLOSE instead. Either way, the regression guard and the ENDPOINTS coverage gate run at CLOSE.\" data-th=\"คุณสร้างโปรเจกต์เป็นลำดับเฟสแบบ vertical-slice เฟสที่เพิ่มพื้นผิวที่เรียกใช้ได้แบบสาธารณะจะวิ่งตามลูปเขียนเทสต์ก่อนด้านล่าง ส่วนเฟส infra หรือเอกสารล้วน ๆ จะวิ่ง RESEARCH → IMPLEMENT → CLOSE แทน ไม่ว่าทางใด ตัวคุมถดถอยและประตูตรวจความครอบคลุม ENDPOINTS จะทำงานตอน CLOSE\">คุณสร้างโปรเจกต์ด้วยการตัดเฟสแบบ vertical-slice เฟสที่เพิ่มการใช้งานที่เรียกใช้ได้แบบสาธารณะจะวิ่งตามลูปโดยเขียนการทดสอบก่อน ส่วนเฟส infra หรือเฟสเอกสารล้วน ๆ จะวิ่ง RESEARCH → IMPLEMENT → CLOSE แทน แต่ไม่ว่าทางใด การตรวจสอบอย่างเข้มข้นและประตูตรวจสอบการเชื่อมต่อ จะทำงานตอนปิดเฟสเสมอ<\u002Fp>\n <\u002Fdiv>\n\n <div class=\"loop reveal\">\n <div class=\"loop-node\"><div class=\"st-no\">STEP 1<\u002Fdiv><div class=\"st-name\">RESEARCH<\u002Fdiv><div class=\"st-desc\" data-en=\"The researcher checks the shared KB first, investigates the codebase & services, and returns a terse summary.\" data-th=\"researcher ตรวจ KB ที่แชร์ก่อน สำรวจโค้ดเบสและบริการ แล้วส่งกลับสรุปสั้น ๆ\">The researcher checks the shared KB first, investigates the codebase & services, and returns a terse summary.<\u002Fdiv><\u002Fdiv>\n <div class=\"loop-node\"><div class=\"st-no\">STEP 2<\u002Fdiv><div class=\"st-name\">SCAFFOLD<\u002Fdiv><div class=\"st-desc\" data-en=\"The implementer writes interface stubs only — no logic. There is nothing to peek at.\" data-th=\"implementer เขียนแค่โครงอินเทอร์เฟซ ไม่มีตรรกะ จึงไม่มีอะไรให้แอบดู\">implementer เขียนแค่โครงอินเทอร์เฟซ ไม่มีตรรกะ จึงไม่มีอะไรให้การเขียน testcase แอบดูได้<\u002Fdiv><\u002Fdiv>\n <div class=\"loop-node red\"><div class=\"st-no\">STEP 3<\u002Fdiv><div class=\"st-name\">RED<\u002Fdiv><div class=\"st-desc\" data-en=\"The test-author writes the suite blind against the stubs. Every test must collect and FAIL. Green-on-stubs is rejected.\" data-th=\"test-author เขียนชุดทดสอบแบบปิดตากับโครงนั้น ทุกเทสต์ต้องรันได้และ FAIL ถ้าผ่านทั้งที่ยังเป็นโครงจะถูกปฏิเสธ\">The test-author writes the suite blind against the stubs. Every test must collect and FAIL. Green-on-stubs is rejected.<\u002Fdiv><\u002Fdiv>\n <div class=\"loop-node green\"><div class=\"st-no\">STEP 4<\u002Fdiv><div class=\"st-name\">GREEN<\u002Fdiv><div class=\"st-desc\" data-en=\"The implementer fills the logic until the real API suite passes — and only that closes the gate.\" data-th=\"implementer เติมตรรกะจนชุดทดสอบ API จริงผ่าน เท่านั้นจึงผ่านประตู\">The implementer fills the logic until the real API suite passes — and only that closes the gate.<\u002Fdiv><\u002Fdiv>\n <div class=\"loop-node\"><div class=\"st-no\">STEP 5<\u002Fdiv><div class=\"st-name\">CLOSE<\u002Fdiv><div class=\"st-desc\" data-en=\"The regression corpus runs and every endpoint must have ≥ 1 test. A green mock suite alone never closes a phase.\" data-th=\"ชุดถดถอยทำงาน และทุก endpoint ต้องมีเทสต์ ≥ 1 ชุด mock ที่ผ่านอย่างเดียวไม่ปิดเฟส\">ชุดประเมินงานทำงาน และทุก endpoint จะต้องมีเทสต์ ≥ 1 ชุดเสมอ การ mock อย่างเดียวไม่สามารถปิดเฟสได้<\u002Fdiv><\u002Fdiv>\n <\u002Fdiv>\n <div class=\"loop-foot reveal\">\n <span data-en=\"Test-first is structural, not honor-system — the tests are written before any logic exists.\" data-th=\"การเขียนเทสต์ก่อนเป็นเชิงโครงสร้าง ไม่ใช่ระบบความซื่อสัตย์ — เทสต์ถูกเขียนก่อนที่ตรรกะจะมีอยู่\">การเขียนเทสต์ก่อนเป็นเชิงโครงสร้าง ไม่ใช่ระบบความซื่อสัตย์ — เทสต์ถูกเขียนก่อนที่จะเขียนโปรแกรมเสมอ<\u002Fspan>\n <span data-en=\"Context stays small on its own — the synthesizer and the <code>pre-compact.sh</code> hook compress and dedup state between phases.\" data-th=\"คอนเท็กซ์เล็กอยู่ได้เอง — synthesizer และ hook <code>pre-compact.sh</code> บีบอัดและตัดข้อมูลซ้ำระหว่างเฟส\">บริบทมีขนาดเล็กอยู่เสมอ — ด้วย synthesizer และ hook pre-compact.sh บีบอัดและตัดข้อมูลซ้ำระหว่างเฟส<\u002Fspan>\n <\u002Fdiv>\n\n <div class=\"cmd-lead reveal\">\n <div class=\"big\" data-en=\"Day to day, you drive with <b>a handful of commands</b>. The bookkeeping — compaction, issue logging, decision logging — runs on its own.\" data-th=\"ในแต่ละวัน คุณขับด้วย <b>คำสั่งเพียงไม่กี่ตัว</b> ส่วนงานจดบันทึก — การบีบคอนเท็กซ์ การบันทึกปัญหา การบันทึกการตัดสินใจ — ทำงานเอง\">ในแต่ละงาน คุณใช้งานด้วย คำสั่งเพียงไม่กี่ตัว ส่วนงานจดบันทึก — การบีบบริบท การบันทึกปัญหา การบันทึกการตัดสินใจ — ระบบจัดการให้<\u002Fdiv>\n <div class=\"chips\"><span>/overview<\u002Fspan><span>/infra<\u002Fspan><span>/phase<\u002Fspan><\u002Fdiv>\n <\u002Fdiv>\n\n <!-- Commands you run -->\n <div class=\"cmd-block reveal\">\n <div class=\"cmd-grouptitle\">\n <span class=\"gt\" data-en=\"Commands you run\" data-th=\"คำสั่งที่คุณสั่งเอง\">Commands you run<\u002Fspan>\n <span class=\"gh\" data-en=\"The straight-line path from idea to deploy\" data-th=\"เส้นทางตรงจากไอเดียสู่การดีพลอย\">The straight-line path from idea to deploy<\u002Fspan>\n <\u002Fdiv>\n <div class=\"cmd-grid\">\n <div class=\"cmd-card\">\n <div class=\"cname\">/overview<\u002Fdiv>\n <div class=\"cdesc\" data-en=\"Design-research, then a double grill, then <code>OVERVIEW.md</code> and a vertical-slice <code>PLAN.md</code>. Phase 0 (infra) is always first.\" data-th=\"วิจัยดีไซน์ แล้วซักไซ้สองรอบ จากนั้นออก <code>OVERVIEW.md</code> และ <code>PLAN.md</code> แบบ vertical-slice เฟส 0 (infra) มาก่อนเสมอ\">Design-research, then a double grill, then <code>OVERVIEW.md<\u002Fcode> and a vertical-slice <code>PLAN.md<\u002Fcode>. Phase 0 (infra) is always first.<\u002Fdiv>\n <\u002Fdiv>\n <div class=\"cmd-card\">\n <div class=\"cname\">/infra<\u002Fdiv>\n <div class=\"cdesc\" data-en=\"Provision Azure: Bicep → <code>what-if</code> diff → <b>your review gate</b> → apply → <code>INFRA.md</code> + <code>.env.test</code>.\" data-th=\"จัดเตรียม Azure: Bicep → diff <code>what-if</code> → <b>ประตูรีวิวของคุณ</b> → apply → <code>INFRA.md</code> + <code>.env.test</code>\">Provision Azure: Bicep → <code>what-if<\u002Fcode> diff → <b>your review gate<\u002Fb> → apply → <code>INFRA.md<\u002Fcode> + <code>.env.test<\u002Fcode>.<\u002Fdiv>\n <\u002Fdiv>\n <div class=\"cmd-card\">\n <div class=\"cname\">/phase [n]<\u002Fdiv>\n <div class=\"cdesc\" data-en=\"Run one phase end-to-end. It picks the TDD or direct order at research time and runs the coverage gate at CLOSE.\" data-th=\"วิ่งหนึ่งเฟสตั้งแต่ต้นจนจบ เลือกลำดับ TDD หรือแบบตรงตอนวิจัย แล้ววิ่งประตูความครอบคลุมตอน CLOSE\">Run one phase end-to-end. It picks the TDD or direct order at research time and runs the coverage gate at CLOSE.<\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n\n <!-- When you need them -->\n <div class=\"cmd-block reveal\">\n <div class=\"cmd-grouptitle\">\n <span class=\"gt\" data-en=\"When you need them\" data-th=\"เมื่อจำเป็นต้องใช้\">When you need them<\u002Fspan>\n <span class=\"gh\" data-en=\"The escape hatches the loop is built around\" data-th=\"ทางออกที่ลูปออกแบบเผื่อไว้\">The escape hatches the loop is built around<\u002Fspan>\n <\u002Fdiv>\n <div class=\"cmd-grid\">\n <div class=\"cmd-card accent\">\n <div class=\"cname\">/quick [change]<\u002Fdiv>\n <div class=\"cdesc\" data-en=\"For a small, obvious change that doesn't deserve a phase. A direct edit with no agent chain — then the mock regression corpus runs so nothing silently breaks.\" data-th=\"สำหรับการเปลี่ยนเล็ก ๆ ชัดเจน ที่ไม่คุ้มจะตั้งเป็นเฟส แก้ตรง ๆ ไม่มีสาย agent แล้วชุดถดถอย mock จะวิ่ง เพื่อไม่ให้มีอะไรพังเงียบ ๆ\">สำหรับการเปลี่ยนเล็ก ๆ ชัดเจน ที่ไม่คุ้มจะตั้งเป็นเฟส แก้ตรง ๆ ไม่มีสาย agent แล้วชุดตรวจสอบ mock จะวิ่ง เพื่อไม่ให้มีอะไรพังเงียบ ๆ<\u002Fdiv>\n <\u002Fdiv>\n <div class=\"cmd-card accent\">\n <div class=\"cname\">/replan [change]<\u002Fdiv>\n <div class=\"cdesc\" data-en=\"When the plan drifts. Revises <code>PLAN.md</code> while keeping finished phases untouched, re-runs design-research if a new service appears, and preserves the deploy block in the last pending phase.\" data-th=\"เมื่อแผนเริ่มเพี้ยน แก้ <code>PLAN.md</code> โดยไม่แตะเฟสที่เสร็จแล้ว วิจัยดีไซน์ใหม่ถ้ามีบริการใหม่ และคงบล็อกดีพลอยในเฟสสุดท้ายที่ยังค้าง\">เมื่อแผนเริ่มเปลี่ยนไป แก้ PLAN.md โดยไม่แตะเฟสที่เสร็จแล้ว วิจัยดีไซน์ใหม่ถ้ามีบริการใหม่ และคงบล็อกดีพลอยในเฟสสุดท้ายเสมอ<\u002Fdiv>\n <\u002Fdiv>\n <div class=\"cmd-card accent\">\n <div class=\"cname\">/unstuck<\u002Fdiv>\n <div class=\"cdesc\" data-en=\"Deep re-research after the circuit breaker fires. The debug cap is hard: warn at 2, <b>STOP at 3</b> and ask you — the kit never auto-unsticks. You trigger this when you're ready.\" data-th=\"วิจัยเชิงลึกใหม่หลังตัวตัดวงจรทำงาน เพดานดีบักเข้มงวด: เตือนครั้งที่ 2 <b>หยุดครั้งที่ 3</b> แล้วถามคุณ — ชุดนี้ไม่แก้เองอัตโนมัติ คุณเป็นคนสั่งเมื่อพร้อม\">วิจัยเชิงลึกใหม่หลังตัวตัดวงจรทำงาน(หรือคุณสั่ง) เพดานดีบัก: เตือนในครั้งที่ 2 หยุดในครั้งที่ 3 แล้วถามคุณ — มันจะไม่แก้ต่อเองอัตโนมัติ คุณเป็นคนสั่งเมื่อพร้อม<\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n\n <!-- Runs automatically -->\n <div class=\"cmd-block reveal\">\n <div class=\"cmd-grouptitle\">\n <span class=\"gt\" data-en=\"Runs automatically\" data-th=\"ทำงานเองอัตโนมัติ\">Runs automatically<\u002Fspan>\n <span class=\"gh\" data-en=\"Hooks and the orchestrator fire these for you — you rarely type them\" data-th=\"hooks และ orchestrator สั่งให้เอง — คุณแทบไม่ต้องพิมพ์\">Hooks and the orchestrator fire these for you — you rarely type them<\u002Fspan>\n <\u002Fdiv>\n <div class=\"auto-grid\">\n <div class=\"auto-card\">\n <div class=\"ac-top\"><span class=\"cname\">/synthesize<\u002Fspan><span class=\"auto-badge\">AUTO<\u002Fspan><\u002Fdiv>\n <div class=\"cdesc\" data-en=\"Compresses <code>STATE.md</code>, dedups <code>ISSUES.md</code>, and prunes snapshots — triggered around compaction so handoff files never bloat.\" data-th=\"บีบอัด <code>STATE.md</code> ตัดซ้ำ <code>ISSUES.md</code> และตัดสแนปช็อต — ทำงานช่วงบีบคอนเท็กซ์ ไฟล์ส่งต่อจึงไม่บวม\">Compresses <code>STATE.md<\u002Fcode>, dedups <code>ISSUES.md<\u002Fcode>, and prunes snapshots — triggered around compaction so handoff files never bloat.<\u002Fdiv>\n <\u002Fdiv>\n <div class=\"auto-card\">\n <div class=\"ac-top\"><span class=\"cname\">/log-issue<\u002Fspan><span class=\"auto-badge\">AUTO<\u002Fspan><\u002Fdiv>\n <div class=\"cdesc\" data-en=\"Every resolved error is appended to <code>ISSUES.md</code> with its root cause and failed attempts — so the same dead end is never repeated.\" data-th=\"ทุกข้อผิดพลาดที่แก้แล้วถูกบันทึกใน <code>ISSUES.md</code> พร้อมต้นเหตุและความพยายามที่ล้มเหลว — ทางตันเดิมจึงไม่เกิดซ้ำ\">Every resolved error is appended to <code>ISSUES.md<\u002Fcode> with its root cause and failed attempts — so the same dead end is never repeated.<\u002Fdiv>\n <\u002Fdiv>\n <div class=\"auto-card\">\n <div class=\"ac-top\"><span class=\"cname\">/log-decision<\u002Fspan><span class=\"auto-badge\">AUTO<\u002Fspan><\u002Fdiv>\n <div class=\"cdesc\" data-en=\"Any architectural change is recorded in <code>DESIGN_LOG.md</code> before a phase closes — logging never depends on you remembering. <code>/store-wisdom</code> promotes wisdom to the shared KB on your approval.\" data-th=\"การเปลี่ยนสถาปัตยกรรมทุกครั้งถูกบันทึกใน <code>DESIGN_LOG.md</code> ก่อนปิดเฟส — การบันทึกไม่ขึ้นกับว่าคุณจะจำได้ไหม <code>/store-wisdom</code> ส่งความรู้ขึ้น KB ที่แชร์เมื่อคุณอนุมัติ\">Any architectural change is recorded in <code>DESIGN_LOG.md<\u002Fcode> before a phase closes — logging never depends on you remembering. <code>/store-wisdom<\u002Fcode> promotes wisdom to the shared KB on your approval.<\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n<\u002Fsection>\n\n<!-- ───────── FEATURES ───────── -->\n<section class=\"block tint\" id=\"features\">\n <div class=\"wrap\">\n <div class=\"sec-head reveal\">\n <span class=\"eyebrow\" data-en=\"Everything in the kit\" data-th=\"ทุกอย่างในชุดเครื่องมือ\">Everything in the kit<\u002Fspan>\n <h2 data-en=\"Every feature, release by release.\" data-th=\"ทุกฟีเจอร์ ทีละรุ่น\">Every feature, release by release.<\u002Fh2>\n <p data-en=\"The complete capability set — not just v2.0. Each feature landed when it solved a specific, documented production failure, from the v1.0 foundation through to v2.0.2. Nothing was bolted on for show.\" data-th=\"ชุดความสามารถทั้งหมด ไม่ใช่แค่ v2.0 แต่ละฟีเจอร์เกิดขึ้นเมื่อมันแก้ความล้มเหลวจริงที่บันทึกไว้ ตั้งแต่รากฐาน v1.0 จนถึง v2.0.2 ไม่มีอะไรถูกแปะเพิ่มเพื่อความสวยงาม\">The complete capability set — not just v2.0. Each feature landed when it solved a specific, documented production failure, from the v1.0 foundation through to v2.0.2. Nothing was bolted on for show.<\u002Fp>\n <\u002Fdiv>\n\n <div class=\"ver-rail reveal\">\n <div class=\"ver-step\"><span class=\"vn\">v1.0<\u002Fspan><span class=\"vl\" data-en=\"Foundation — hooks, agents, test stack\" data-th=\"รากฐาน — hooks, agents, ชุดทดสอบ\">Foundation — hooks, agents, test stack<\u002Fspan><\u002Fdiv>\n <div class=\"ver-step\"><span class=\"vn\">v1.1<\u002Fspan><span class=\"vl\" data-en=\"Azure infra as Phase 0\" data-th=\"โครงสร้าง Azure เป็นเฟส 0\">Azure infra as Phase 0<\u002Fspan><\u002Fdiv>\n <div class=\"ver-step\"><span class=\"vn\">v1.3<\u002Fspan><span class=\"vl\" data-en=\"setup.sh distribution\" data-th=\"กระจายผ่าน setup.sh\">setup.sh distribution<\u002Fspan><\u002Fdiv>\n <div class=\"ver-step\"><span class=\"vn\">v1.4<\u002Fspan><span class=\"vl\" data-en=\"ENDPOINTS coverage gate\" data-th=\"ประตูความครอบคลุม ENDPOINTS\">ENDPOINTS coverage gate<\u002Fspan><\u002Fdiv>\n <div class=\"ver-step\"><span class=\"vn\">v1.5<\u002Fspan><span class=\"vl\" data-en=\"Shared knowledge base\" data-th=\"ฐานความรู้ที่แชร์\">Shared knowledge base<\u002Fspan><\u002Fdiv>\n <div class=\"ver-step\"><span class=\"vn\">v2.0<\u002Fspan><span class=\"vl\" data-en=\"Five locked features\" data-th=\"ห้าฟีเจอร์ที่ล็อกไว้\">Five locked features<\u002Fspan><\u002Fdiv>\n <div class=\"ver-step cur\"><span class=\"vn\">v2.0.2<\u002Fspan><span class=\"vl\" data-en=\"Path-first KB · current\" data-th=\"KB แบบระบุพาธ · ปัจจุบัน\">Path-first KB · current<\u002Fspan><\u002Fdiv>\n <\u002Fdiv>\n\n <div class=\"feat-grid\">\n\n <!-- Guardrails -->\n <div class=\"feat-card reveal\">\n <div class=\"feat-head\">\n <div class=\"feat-ico\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 3l7 3v5c0 4-3 7-7 8-4-1-7-4-7-8V6z\"><\u002Fpath><path d=\"M9 12l2 2 4-4\"><\u002Fpath><\u002Fsvg><\u002Fdiv>\n <h3 data-en=\"Deterministic guardrails\" data-th=\"ตัวกั้นเชิงกำหนดแน่นอน\">Deterministic guardrails<\u002Fh3>\n <\u002Fdiv>\n <ul class=\"feat-list\">\n <li><span class=\"ftx\" data-en=\"<b>Three lifecycle hooks</b> — session-start, pre-compact, subagent-stop.\" data-th=\"<b>hook วงจรชีวิต 3 ตัว</b> — session-start, pre-compact, subagent-stop\"><b>Three lifecycle hooks<\u002Fb> — session-start, pre-compact, subagent-stop.<\u002Fspan><span class=\"fver\">v1.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Eleven hard rules</b> injected into every single session.\" data-th=\"<b>กฎเหล็ก 11 ข้อ</b> ถูกแทรกในทุกเซสชัน\"><b>Eleven hard rules<\u002Fb> injected into every single session.<\u002Fspan><span class=\"fver\">v1.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Debug circuit breaker</b> — a hard cap at three attempts.\" data-th=\"<b>ตัวตัดวงจรการดีบัก</b> — จำกัดไม่เกิน 3 ครั้ง\"><b>Debug circuit breaker<\u002Fb> — a hard cap at three attempts.<\u002Fspan><span class=\"fver\">v1.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Loud-fail verifiers</b> for install, hook wiring & the AGENTS sentinel.\" data-th=\"<b>ตัวตรวจที่พังให้ดัง</b> สำหรับการติดตั้ง การต่อ hook และ sentinel ของ AGENTS\"><b>Loud-fail verifiers<\u002Fb> for install, hook wiring & the AGENTS sentinel.<\u002Fspan><span class=\"fver\">v1.3+<\u002Fspan><\u002Fli>\n <\u002Ful>\n <\u002Fdiv>\n\n <!-- Test discipline -->\n <div class=\"feat-card reveal\">\n <div class=\"feat-head\">\n <div class=\"feat-ico\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 3h6\"><\u002Fpath><path d=\"M10 3v5L5.5 16.5A2 2 0 007.3 19.5h9.4a2 2 0 001.8-3L14 8V3\"><\u002Fpath><path d=\"M8 14h8\"><\u002Fpath><\u002Fsvg><\u002Fdiv>\n <h3 data-en=\"Test discipline\" data-th=\"วินัยการทดสอบ\">Test discipline<\u002Fh3>\n <\u002Fdiv>\n <ul class=\"feat-list\">\n <li><span class=\"ftx\" data-en=\"<b>Three-layer test stack</b> — mock → real API → browser E2E.\" data-th=\"<b>ชุดทดสอบ 3 ชั้น</b> — mock → API จริง → E2E บนเบราว์เซอร์\"><b>Three-layer test stack<\u002Fb> — mock → real API → browser E2E.<\u002Fspan><span class=\"fver\">v1.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Blind test authoring</b> — the author never reads the code.\" data-th=\"<b>เขียนเทสต์แบบปิดตา</b> — ผู้เขียนไม่อ่านโค้ดจริง\"><b>Blind test authoring<\u002Fb> — the author never reads the code.<\u002Fspan><span class=\"fver\">v1.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>TDD-first</b> via a SCAFFOLD → RED → GREEN gate.\" data-th=\"<b>เขียนเทสต์ก่อน</b> ผ่านประตู SCAFFOLD → RED → GREEN\"><b>TDD-first<\u002Fb> via a SCAFFOLD → RED → GREEN gate.<\u002Fspan><span class=\"fver\">v2.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Regression corpus</b> + an ENDPOINTS coverage gate at CLOSE.\" data-th=\"<b>คลังทดสอบถดถอย</b> + ประตูตรวจความครอบคลุม ENDPOINTS ตอน CLOSE\"><b>Regression corpus<\u002Fb> + an ENDPOINTS coverage gate at CLOSE.<\u002Fspan><span class=\"fver\">v1.4<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Browser E2E</b> with ROPC auth — never scripts the login UI.\" data-th=\"<b>E2E บนเบราว์เซอร์</b> ด้วย ROPC — ไม่สคริปต์หน้าจอล็อกอิน\"><b>Browser E2E<\u002Fb> with ROPC auth — never scripts the login UI.<\u002Fspan><span class=\"fver\">v1.x<\u002Fspan><\u002Fli>\n <\u002Ful>\n <\u002Fdiv>\n\n <!-- Orchestration -->\n <div class=\"feat-card reveal\">\n <div class=\"feat-head\">\n <div class=\"feat-ico\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"6\" r=\"2.5\"><\u002Fcircle><circle cx=\"5\" cy=\"18\" r=\"2.5\"><\u002Fcircle><circle cx=\"19\" cy=\"18\" r=\"2.5\"><\u002Fcircle><path d=\"M12 8.5v2.5m0 0l-5.4 4.2M12 11l5.4 4.2\"><\u002Fpath><\u002Fsvg><\u002Fdiv>\n <h3 data-en=\"Subagent orchestration\" data-th=\"การจัดวง subagent\">Subagent orchestration<\u002Fh3>\n <\u002Fdiv>\n <ul class=\"feat-list\">\n <li><span class=\"ftx\" data-en=\"<b>Eight scoped subagents</b>, each running in its own context.\" data-th=\"<b>subagent เฉพาะทาง 8 ตัว</b> แต่ละตัวรันในคอนเท็กซ์ของตัวเอง\"><b>Eight scoped subagents<\u002Fb>, each running in its own context.<\u002Fspan><span class=\"fver\">v1.0→2.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Orchestrator routes only</b> — it never implements or debugs.\" data-th=\"<b>orchestrator ส่งต่องานอย่างเดียว</b> ไม่เขียนโค้ดหรือดีบักเอง\"><b>Orchestrator routes only<\u002Fb> — it never implements or debugs.<\u002Fspan><span class=\"fver\">v1.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Isolated debugger</b> — noise to a file, a terse summary back.\" data-th=\"<b>debugger แยกคอนเท็กซ์</b> — เก็บเสียงรบกวนลงไฟล์ ส่งกลับสรุปสั้น\"><b>Isolated debugger<\u002Fb> — noise to a file, a terse summary back.<\u002Fspan><span class=\"fver\">v1.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Synthesizer compaction</b> — compresses & dedups state.\" data-th=\"<b>synthesizer บีบอัด</b> — ย่อและตัดสถานะที่ซ้ำ\"><b>Synthesizer compaction<\u002Fb> — compresses & dedups state.<\u002Fspan><span class=\"fver\">v1.0<\u002Fspan><\u002Fli>\n <\u002Ful>\n <\u002Fdiv>\n\n <!-- State & memory -->\n <div class=\"feat-card reveal\">\n <div class=\"feat-head\">\n <div class=\"feat-ico\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14 3H7a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 002-2V8z\"><\u002Fpath><path d=\"M14 3v5h5M9 13h6M9 17h4\"><\u002Fpath><\u002Fsvg><\u002Fdiv>\n <h3 data-en=\"State & memory\" data-th=\"สถานะและความจำ\">State & memory<\u002Fh3>\n <\u002Fdiv>\n <ul class=\"feat-list\">\n <li><span class=\"ftx\" data-en=\"<b>Declarative document layer</b> — every rule lives in one place.\" data-th=\"<b>ชั้นเอกสารแบบประกาศ</b> — ทุกกฎอยู่ในที่เดียว\"><b>Declarative document layer<\u002Fb> — every rule lives in one place.<\u002Fspan><span class=\"fver\">v1.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Pre-compact snapshots</b>, auto-pruned before any context loss.\" data-th=\"<b>สแนปช็อตก่อนบีบ</b> ตัดอัตโนมัติก่อนสูญเสียคอนเท็กซ์\"><b>Pre-compact snapshots<\u002Fb>, auto-pruned before any context loss.<\u002Fspan><span class=\"fver\">v1.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Append-only DESIGN_LOG</b> — rationale & rejected paths.\" data-th=\"<b>DESIGN_LOG ต่อท้ายอย่างเดียว</b> — เหตุผลและทางที่ถูกปฏิเสธ\"><b>Append-only DESIGN_LOG<\u002Fb> — rationale & rejected paths.<\u002Fspan><span class=\"fver\">v1.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Shared KB</b> across projects via <code>/store-wisdom</code>.\" data-th=\"<b>KB ที่แชร์</b> ข้ามโปรเจกต์ผ่าน <code>/store-wisdom</code>\"><b>Shared KB<\u002Fb> across projects via <code>/store-wisdom<\u002Fcode>.<\u002Fspan><span class=\"fver\">v1.5<\u002Fspan><\u002Fli>\n <\u002Ful>\n <\u002Fdiv>\n\n <!-- Workflow & commands -->\n <div class=\"feat-card reveal\">\n <div class=\"feat-head\">\n <div class=\"feat-ico\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\"><\u002Frect><path d=\"M7 9l3 3-3 3M13 15h4\"><\u002Fpath><\u002Fsvg><\u002Fdiv>\n <h3 data-en=\"Workflow & commands\" data-th=\"วงจรการทำงานและคำสั่ง\">Workflow & commands<\u002Fh3>\n <\u002Fdiv>\n <ul class=\"feat-list\">\n <li><span class=\"ftx\" data-en=\"<b>Eleven commands</b> spanning the path from idea to deploy.\" data-th=\"<b>คำสั่ง 11 ตัว</b> ครอบคลุมเส้นทางจากไอเดียถึงดีพลอย\"><b>Eleven commands<\u002Fb> spanning the path from idea to deploy.<\u002Fspan><span class=\"fver\">v1.0→2.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Vertical-slice phases</b> — infra is always Phase 0.\" data-th=\"<b>เฟสแบบ vertical-slice</b> — infra เป็นเฟส 0 เสมอ\"><b>Vertical-slice phases<\u002Fb> — infra is always Phase 0.<\u002Fspan><span class=\"fver\">v1.1<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b><code>/overview</code></b> design-research plus a double grill.\" data-th=\"<b><code>/overview</code></b> วิจัยดีไซน์ พร้อมซักไซ้สองรอบ\"><b><code>/overview<\u002Fcode><\u002Fb> design-research plus a double grill.<\u002Fspan><span class=\"fver\">v1.4<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Escape hatches</b> — <code>/quick</code>, <code>/unstuck</code>, <code>/replan</code>.\" data-th=\"<b>ทางออกฉุกเฉิน</b> — <code>/quick</code>, <code>/unstuck</code>, <code>/replan</code>\"><b>Escape hatches<\u002Fb> — <code>/quick<\u002Fcode>, <code>/unstuck<\u002Fcode>, <code>/replan<\u002Fcode>.<\u002Fspan><span class=\"fver\">v1.x<\u002Fspan><\u002Fli>\n <\u002Ful>\n <\u002Fdiv>\n\n <!-- Infra & portability -->\n <div class=\"feat-card reveal\">\n <div class=\"feat-head\">\n <div class=\"feat-ico\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M7 18a4 4 0 01-.5-7.97A5.5 5.5 0 0117.5 9.5 4 4 0 0117 18z\"><\u002Fpath><\u002Fsvg><\u002Fdiv>\n <h3 data-en=\"Infra & portability\" data-th=\"โครงสร้างพื้นฐานและการพกพา\">Infra & portability<\u002Fh3>\n <\u002Fdiv>\n <ul class=\"feat-list\">\n <li><span class=\"ftx\" data-en=\"<b>Azure Phase 0</b> — Bicep → what-if → your review gate.\" data-th=\"<b>Azure เฟส 0</b> — Bicep → what-if → ประตูรีวิวของคุณ\"><b>Azure Phase 0<\u002Fb> — Bicep → what-if → your review gate.<\u002Fspan><span class=\"fver\">v1.1<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>AGENTS.md / CLAUDE.md split</b> on the open standard.\" data-th=\"<b>แยก AGENTS.md / CLAUDE.md</b> ตามมาตรฐานเปิด\"><b>AGENTS.md / CLAUDE.md split<\u002Fb> on the open standard.<\u002Fspan><span class=\"fver\">v2.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Multi-tool adapters</b> — Claude Code & Cursor.\" data-th=\"<b>อะแดปเตอร์หลายเครื่องมือ</b> — Claude Code และ Cursor\"><b>Multi-tool adapters<\u002Fb> — Claude Code & Cursor.<\u002Fspan><span class=\"fver\">v2.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Upgrade-safe npx installer</b> — manifest + sha256, non-destructive.\" data-th=\"<b>ตัวติดตั้ง npx ปลอดภัยต่อการอัปเกรด</b> — manifest + sha256 ไม่ทำลายไฟล์\"><b>Upgrade-safe npx installer<\u002Fb> — manifest + sha256, non-destructive.<\u002Fspan><span class=\"fver\">v2.0<\u002Fspan><\u002Fli>\n <li><span class=\"ftx\" data-en=\"<b>Windows Git Bash support</b> — derived path, no WSL relay.\" data-th=\"<b>รองรับ Windows Git Bash</b> — หาพาธเอง ไม่ผ่าน WSL\"><b>Windows Git Bash support<\u002Fb> — derived path, no WSL relay.<\u002Fspan><span class=\"fver\">v2.0.1<\u002Fspan><\u002Fli>\n <\u002Ful>\n <\u002Fdiv>\n\n <\u002Fdiv>\n <\u002Fdiv>\n<\u002Fsection>\n\n<!-- ───────── INSTALL ───────── -->\n<section class=\"block\" id=\"install\">\n <div class=\"wrap\">\n <div class=\"sec-head reveal\" style=\"margin-bottom:36px;\">\n <span class=\"eyebrow\" data-en=\"Install\" data-th=\"ติดตั้ง\">Install<\u002Fspan>\n <h2 data-en=\"Up and running in one command.\" data-th=\"พร้อมใช้งานด้วยคำสั่งเดียว\">Up and running in one command.<\u002Fh2>\n <\u002Fdiv>\n <div class=\"install-band reveal\">\n <div>\n <p data-en=\"Run it in any project. The installer is idempotent and non-destructive — it never clobbers your files, and re-running it upgrades safely.\" data-th=\"รันในโปรเจกต์ใดก็ได้ ตัวติดตั้งทำงานซ้ำได้และไม่ทำลายไฟล์เดิม ไม่เขียนทับไฟล์ของคุณ และรันซ้ำเพื่ออัปเกรดได้อย่างปลอดภัย\" style=\"font-size:18px;color:var(--fg2);line-height:1.6;max-width:42ch;\">Run it in any project. The installer is idempotent and non-destructive — it never clobbers your files, and re-running it upgrades safely.<\u002Fp>\n <div class=\"ib-cmd\">\n <span class=\"prompt\">$<\u002Fspan>\n <code id=\"install-cmd2\">npx create-anpunkit<\u002Fcode>\n <button class=\"copy\" id=\"copyBtn2\" data-en=\"COPY\" data-th=\"คัดลอก\">COPY<\u002Fbutton>\n <\u002Fdiv>\n <div class=\"ib-note\" data-en=\"Requires Node ≥ 18 and bash on your PATH (Git Bash on Windows).\" data-th=\"ต้องใช้ Node ≥ 18 และมี bash ใน PATH (Git Bash บน Windows)\">Requires Node ≥ 18 and bash on your PATH (Git Bash on Windows).<\u002Fdiv>\n <\u002Fdiv>\n <div class=\"ib-flags\">\n <div class=\"ib-flag\"><span class=\"fk\">--dry-run<\u002Fspan><span class=\"fv\" data-en=\"Print the plan and write nothing.\" data-th=\"แสดงแผนโดยไม่เขียนอะไร\">Print the plan and write nothing.<\u002Fspan><\u002Fdiv>\n <div class=\"ib-flag\"><span class=\"fk\">--kb-path ~/kb<\u002Fspan><span class=\"fv\" data-en=\"Connect a shared knowledge base you cloned first.\" data-th=\"เชื่อมฐานความรู้ที่แชร์กันซึ่งคุณ clone ไว้ก่อน\">Connect a shared knowledge base you cloned first.<\u002Fspan><\u002Fdiv>\n <div class=\"ib-flag\"><span class=\"fk\">--no-kb<\u002Fspan><span class=\"fv\" data-en=\"Skip KB setup entirely.\" data-th=\"ข้ามการตั้งค่า KB ทั้งหมด\">Skip KB setup entirely.<\u002Fspan><\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n<\u002Fsection>\n\n<!-- ───────── FOOTER ───────── -->\n<footer>\n <div class=\"wrap\">\n <div class=\"foot-top\">\n <div class=\"foot-tag\"><span class=\"q\">“<\u002Fspan><span data-en=\"Worry-free or fail hard, nothing in between.\" data-th=\"ไร้กังวล หรือพังให้ดังไปเลย ไม่มีอะไรอยู่ตรงกลาง\">Worry-free or fail hard, nothing in between.<\u002Fspan><span class=\"q\">”<\u002Fspan><\u002Fdiv>\n <div class=\"foot-links\">\n <div class=\"foot-col\">\n <h4 data-en=\"Project\" data-th=\"โปรเจกต์\">Project<\u002Fh4>\n <a href=\"https://github.com/MetheeS/anpunkit\" target=\"_blank\" rel=\"noopener\" class=\"mono\">github.com/MetheeS/anpunkit<\u002Fa>\n <a href=\"https://www.npmjs.com/package/create-anpunkit\" target=\"_blank\" rel=\"noopener\" class=\"mono\">npm: create-anpunkit<\u002Fa>\n <span class=\"mono\">v2.0.2<\u002Fspan>\n <\u002Fdiv>\n <div class=\"foot-col\">\n <h4 data-en=\"Requirements\" data-th=\"ความต้องการระบบ\">Requirements<\u002Fh4>\n <span class=\"mono\">Node ≥ 18<\u002Fspan>\n <span class=\"mono\">bash on PATH<\u002Fspan>\n <span data-en=\"Claude Code · Cursor\" data-th=\"Claude Code · Cursor\">Claude Code · Cursor<\u002Fspan>\n <\u002Fdiv>\n <\u002Fdiv>\n <\u002Fdiv>\n <div class=\"foot-bottom\">\n <p data-en=\"anpunkit — an engineering methodology kit for AI coding agents.\" data-th=\"anpunkit — ชุดเครื่องมือระเบียบวิธีทางวิศวกรรมสำหรับ AI coding agents\">anpunkit — an engineering methodology kit for AI coding agents.<\u002Fp>\n <span class=\"reqs\">npx create-anpunkit<\u002Fspan>\n <\u002Fdiv>\n <\u002Fdiv>\n<\u002Ffooter>\n\n<script>\n(function(){\n var STORE='anpunkit-lang';\n function pickInitial(){\n var saved=null; try{saved=localStorage.getItem(STORE);}catch(e){}\n if(saved==='en'||saved==='th') return saved;\n var nav=(navigator.language||'en').toLowerCase();\n return nav.indexOf('th')===0 ? 'th' : 'en';\n }\n var current=null;\n // Persist any in-place edits made in the active language before switching away,\n // so inline edits in the preview survive a language toggle.\n function syncBack(){\n if(!current) return;\n document.querySelectorAll('[data-en]').forEach(function(el){\n el.setAttribute('data-'+current, el.innerHTML);\n });\n }\n function apply(lang){\n if(lang===current) return;\n syncBack();\n document.documentElement.setAttribute('lang',lang);\n document.querySelectorAll('[data-en]').forEach(function(el){\n var val=el.getAttribute('data-'+lang);\n if(val!=null) el.innerHTML=val;\n });\n document.querySelectorAll('.lang-toggle button').forEach(function(b){\n b.classList.toggle('active', b.getAttribute('data-lang')===lang);\n });\n current=lang;\n try{localStorage.setItem(STORE,lang);}catch(e){}\n }\n document.querySelectorAll('.lang-toggle button').forEach(function(b){\n b.addEventListener('click',function(){apply(b.getAttribute('data-lang'));});\n });\n apply(pickInitial());\n\n // copy install command\n document.querySelectorAll('.copy').forEach(function(cb){\n cb.addEventListener('click',function(){\n var holder=cb.previousElementSibling;\n var txt=holder?holder.textContent:'npx create-anpunkit';\n var done=function(){\n var lang=document.documentElement.getAttribute('lang');\n cb.textContent = lang==='th' ? 'คัดลอกแล้ว' : 'COPIED';\n setTimeout(function(){ cb.textContent = cb.getAttribute('data-'+lang); },1600);\n };\n if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(txt).then(done,done);}\n else{done();}\n });\n });\n\n // reveal on scroll\n var io=new IntersectionObserver(function(entries){\n entries.forEach(function(e){ if(e.isIntersecting){ e.target.classList.add('in'); io.unobserve(e.target);} });\n },{threshold:.12,rootMargin:'0px 0px -8% 0px'});\n document.querySelectorAll('.reveal').forEach(function(el){io.observe(el);});\n})();\n<\u002Fscript>\n\n\n<\u002Fbody><\u002Fhtml>"
|
|
338
|
+
</script>
|
|
339
|
+
</body>
|
|
340
|
+
</html>
|