-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
665 lines (615 loc) · 41.4 KB
/
Copy pathindex.html
File metadata and controls
665 lines (615 loc) · 41.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>pstack · a guided tour</title>
<meta name="description" content="An unofficial, interactive reading aid for pstack, poteto's set of engineering skills for coding agents.">
<meta property="og:title" content="pstack · a guided tour">
<meta property="og:description" content="An unofficial, interactive reading aid for pstack, poteto's set of engineering skills for coding agents.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://hustlecoding.github.io/pstack-explained/">
<meta property="og:image" content="https://hustlecoding.github.io/pstack-explained/og.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="640">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://hustlecoding.github.io/pstack-explained/og.png">
<link rel="canonical" href="https://hustlecoding.github.io/pstack-explained/">
<link rel="alternate" type="text/plain" href="llms.txt" title="LLM context for this site">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
<link rel="shortcut icon" href="favicon.ico">
<meta name="theme-color" content="#ffffff">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root{
--bg:#ffffff; --ink:#1a1a1a; --soft:#3d3d3d; --dim:#6b6b6b; --faint:#9b9b9b;
--rule:#ededed; --rule2:#e0e0e0;
--accent:#b45309; --accent-soft:#fdf4ea; --accent-line:#e8c39a;
--core:#b45309; --arch:#1f6f8b; --verif:#4d7c2f; --deleg:#b13a5a; --meta:#7c4d9e;
--maxw:880px; --navh:54px;
--ff:"Hanken Grotesk",system-ui,-apple-system,sans-serif;
--ff-mono:"IBM Plex Mono",ui-monospace,monospace;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth; -webkit-text-size-adjust:100%}
body{
margin:0; background:var(--bg); color:var(--ink);
font-family:var(--ff); font-size:17px; line-height:1.65;
-webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
}
.wrap{max-width:var(--maxw); margin:0 auto; padding:0 clamp(20px,5vw,40px); position:relative}
code{font-family:var(--ff-mono); font-size:.92em; color:var(--accent)}
/* masthead */
header.mast{padding:clamp(56px,9vw,96px) 0 clamp(36px,5vw,52px)}
.mark{display:flex; align-items:center; gap:13px; margin-bottom:22px}
.glyph{width:26px; height:26px; position:relative; flex:none}
.glyph i{position:absolute; left:0; height:20%; background:var(--accent); border-radius:1px}
.glyph i:nth-child(1){bottom:0; width:100%; opacity:.4}
.glyph i:nth-child(2){bottom:40%; width:68%; opacity:.65}
.glyph i:nth-child(3){bottom:80%; width:40%}
h1.wordmark{font-family:var(--ff); font-weight:700; font-size:clamp(36px,6vw,50px); letter-spacing:-.025em; margin:0; line-height:1}
h1.wordmark span{color:var(--accent)}
.count{font-family:var(--ff-mono); font-size:13px; color:var(--faint); margin:0 0 26px; letter-spacing:.02em}
.tagline{font-size:clamp(19px,2.6vw,23px); line-height:1.45; color:var(--soft); max-width:52ch; margin:0 0 18px; font-weight:500; letter-spacing:-.005em}
.tagline em{font-style:normal; color:var(--ink); border-bottom:2px solid var(--accent-line)}
.intro-lead{color:var(--dim); font-size:16px; line-height:1.6; max-width:62ch; margin:0}
/* nav */
nav.bar{position:sticky; top:0; z-index:20; background:rgba(255,255,255,.9); backdrop-filter:blur(8px); border-bottom:1px solid var(--rule)}
nav.bar .wrap{display:flex; gap:2px; padding-top:7px; padding-bottom:7px; overflow-x:auto; scrollbar-width:none}
nav.bar .wrap::-webkit-scrollbar{display:none}
nav.bar a{color:var(--dim); text-decoration:none; font-size:14px; font-weight:500; white-space:nowrap; padding:8px 13px; border-radius:8px; transition:background .15s,color .15s}
nav.bar a:hover,nav.bar a:focus-visible{background:var(--accent-soft); color:var(--accent); outline:none}
section{padding:clamp(52px,8vw,84px) 0; border-bottom:1px solid var(--rule); scroll-margin-top:var(--navh)}
.sh{margin-bottom:clamp(26px,4vw,38px); max-width:64ch}
.sh .num{color:var(--accent); font-family:var(--ff-mono); font-size:12px; letter-spacing:.04em}
.sh h2{font-weight:600; font-size:clamp(24px,3.6vw,31px); margin:7px 0 12px; letter-spacing:-.018em; line-height:1.15}
.sh .lead{color:var(--dim); font-size:16px; line-height:1.6; margin:0; max-width:60ch}
/* routing */
.flow{margin:0 0 28px; font-size:15px; color:var(--dim); line-height:1.9}
.flow .cmd-tok{font-family:var(--ff-mono); color:var(--accent); font-size:14px}
.flow .arr{color:var(--faint); margin:0 2px}
fieldset.chooser{border:none; padding:0; margin:0; max-width:680px}
.chooser label{display:block; font-weight:500; font-size:15px; margin-bottom:9px; color:var(--ink)}
.chooser label .hint{color:var(--faint); font-weight:400; font-size:13.5px}
#task{
width:100%; background:var(--bg); border:1px solid var(--rule2); color:var(--ink);
font-family:var(--ff); font-size:16px; padding:14px 16px; border-radius:10px; outline:none;
transition:border-color .15s, box-shadow .15s;
}
#task:focus{border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft)}
#task::placeholder{color:var(--faint)}
.examples{margin:18px 0 0}
.examples .elbl{font-size:13px; color:var(--faint); margin-bottom:10px}
.exchips{display:flex; gap:8px; flex-wrap:wrap}
.exchip{
background:var(--bg); border:1px solid var(--rule2); color:var(--soft);
font-family:var(--ff); font-size:14px; padding:7px 13px; border-radius:999px;
cursor:pointer; transition:border-color .15s,background .15s,color .15s;
}
.exchip:hover,.exchip:focus-visible{border-color:var(--accent-line); background:var(--accent-soft); color:var(--accent); outline:none}
.verdict{margin:22px 0 0; min-height:26px; font-size:15.5px; color:var(--soft); line-height:1.55}
.verdict .pick{font-weight:600; color:var(--accent)}
.verdict .kw{font-family:var(--ff-mono); font-size:12px; color:var(--accent); background:var(--accent-soft); padding:2px 7px; border-radius:5px; margin:0 2px; white-space:nowrap}
.verdict .why{color:var(--faint); font-size:13.5px}
.verdict .none{color:var(--faint)}
/* grids */
.grid{display:grid; gap:12px}
.pb.grid{grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}
.card{
text-align:left; background:var(--bg); border:1px solid var(--rule); color:var(--ink);
padding:17px 18px; border-radius:12px; cursor:pointer; font-family:var(--ff); font-size:15px;
position:relative; transition:border-color .18s, background .18s, transform .18s; overflow:hidden;
animation:rise .5s both;
}
.card:hover{border-color:var(--rule2); background:#fafafa; transform:translateY(-1px)}
.card:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
.card .idx{display:none}
.card .nm{font-weight:600; font-size:16.5px; line-height:1.25}
.card .tr{color:var(--dim); font-size:14px; margin-top:8px; line-height:1.5}
.card.recommended{border:1.5px solid var(--accent); background:var(--accent-soft)}
.card.recommended .nm{color:var(--accent)}
.card.recommended::after{
content:"match"; position:absolute; top:0; right:0; background:var(--accent); color:#fff;
font-size:10px; letter-spacing:.08em; text-transform:uppercase; padding:3px 8px; font-weight:600;
font-family:var(--ff-mono); border-bottom-left-radius:10px;
}
.card.meta-card{border-style:dashed; grid-column:1/-1; background:transparent}
.card.meta-card .nm{color:var(--dim)}
.sk.grid{grid-template-columns:repeat(auto-fill,minmax(220px,1fr))}
.chip{
text-align:left; background:var(--bg); border:1px solid var(--rule); color:var(--ink);
padding:14px 15px; border-radius:12px; cursor:pointer; font-family:var(--ff);
transition:border-color .18s,background .18s,transform .18s; animation:rise .5s both;
}
.chip:hover{border-color:var(--rule2); background:#fafafa; transform:translateY(-1px)}
.chip:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
.chip .cmd{color:var(--accent); font-family:var(--ff-mono); font-weight:500; font-size:14px}
.chip .when{color:var(--dim); font-size:13.5px; margin-top:7px; line-height:1.5}
/* principles */
.band{margin-bottom:clamp(34px,5vw,52px); animation:rise .5s both}
.band:last-child{margin-bottom:0}
.band-head{display:flex; align-items:center; gap:10px; margin-bottom:8px}
.band-head .tick{width:9px; height:9px; border-radius:2px; flex:none}
.band-head h3{font-weight:600; font-size:18px; margin:0; letter-spacing:-.01em}
.band-head .ct{color:var(--faint); font-family:var(--ff-mono); font-size:12px}
.band-head .ln{flex:1; height:1px; background:var(--rule)}
.band-desc{color:var(--dim); font-size:14.5px; margin:0 0 16px; max-width:60ch}
.bricks{display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:10px}
.brick{
text-align:left; background:var(--bg); border:1px solid var(--rule); border-left:3px solid var(--c);
color:var(--ink); padding:13px 15px; border-radius:3px 12px 12px 3px; cursor:pointer; font-family:var(--ff);
transition:background .18s,transform .18s; animation:rise .5s both;
}
.brick:hover{background:#fafafa; transform:translateY(-1px)}
.brick:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
.brick .pn{font-weight:600; font-size:15px; line-height:1.25}
.brick .pa{color:var(--faint); font-size:12.5px; margin-top:6px; line-height:1.45}
/* models */
.mgrid{display:grid; grid-template-columns:repeat(auto-fill,minmax(250px,1fr)); gap:10px}
.mcard{
background:var(--bg); border:1px solid var(--rule); border-radius:12px; padding:15px 16px;
display:flex; flex-direction:column; gap:7px; animation:rise .5s both;
}
.mrole{font-weight:600; font-size:15px; line-height:1.3; color:var(--ink)}
.kind{align-self:flex-start; font-size:11px; padding:3px 8px; border-radius:999px; border:1px solid var(--rule2); color:var(--dim); font-family:var(--ff-mono)}
.kind.panel{color:var(--arch); border-color:rgba(31,111,139,.35); background:rgba(31,111,139,.06)}
.mowns{color:var(--dim); font-size:13.5px; line-height:1.5}
.models-note{color:var(--faint); font-size:13.5px; margin-top:20px; max-width:60ch; line-height:1.6}
/* modal */
.overlay{position:fixed; inset:0; background:rgba(20,20,20,.45); backdrop-filter:blur(4px); display:none; align-items:center; justify-content:center; z-index:60; padding:clamp(16px,4vw,24px)}
.overlay.open{display:flex; animation:fade .2s}
.dialog{background:var(--bg); border:1px solid var(--rule2); border-radius:14px; max-width:540px; width:100%; padding:clamp(24px,4vw,32px); position:relative; animation:rise .26s both; max-height:86vh; overflow:auto; box-shadow:0 16px 48px -16px rgba(0,0,0,.22)}
.dialog .cat{font-family:var(--ff-mono); font-size:12px; letter-spacing:.04em; text-transform:uppercase; color:var(--accent)}
.dialog h3{font-weight:600; font-size:clamp(22px,3.5vw,27px); margin:8px 0 18px; line-height:1.15; letter-spacing:-.015em}
.dialog .field{margin:15px 0}
.dialog .field .k{color:var(--faint); font-size:11px; letter-spacing:.06em; text-transform:uppercase; margin-bottom:5px; font-family:var(--ff-mono)}
.dialog .field .v{color:var(--ink); font-size:15.5px; line-height:1.6}
.dialog .close{position:absolute; top:13px; right:13px; background:var(--bg); border:1px solid var(--rule2); color:var(--dim); width:34px; height:34px; border-radius:9px; cursor:pointer; font-family:var(--ff); font-size:18px; line-height:1; display:flex; align-items:center; justify-content:center}
.dialog .close:hover{color:var(--accent); border-color:var(--accent-line)}
footer{padding:clamp(40px,6vw,60px) 0 calc(40px + env(safe-area-inset-bottom)); color:var(--faint); font-size:14px; line-height:1.7}
footer p{margin:0 0 8px; max-width:62ch}
footer a{color:var(--accent); text-decoration:none; border-bottom:1px solid var(--accent-line)}
footer a:hover{border-bottom-color:var(--accent)}
@keyframes rise{from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:none}}
@keyframes fade{from{opacity:0} to{opacity:1}}
@media (prefers-reduced-motion:reduce){
*{animation:none !important; transition:none !important}
html{scroll-behavior:auto}
}
@media (max-width:640px){
.pb.grid,.sk.grid,.bricks,.mgrid{grid-template-columns:1fr}
.flow{line-height:2}
}
</style>
</head>
<body>
<header class="mast">
<div class="wrap">
<div class="mark">
<div class="glyph" aria-hidden="true"><i></i><i></i><i></i></div>
<h1 class="wordmark">p<span>stack</span></h1>
</div>
<p class="count">21 principles · 17 playbooks · 19 skills</p>
<p class="tagline">Engineering skills that turn a coding agent into a careful crew. You describe a task, and pstack matches it to a <em>playbook</em>.</p>
<p class="intro-lead">Type <code>/poteto-mode</code> and describe a task. pstack matches it to a playbook and runs the skills as the steps fire. Twenty-one principles apply across every playbook, biasing the work toward small diffs and real verification.</p>
</div>
</header>
<nav class="bar">
<div class="wrap">
<a href="#routing">Routing</a>
<a href="#playbooks">Playbooks</a>
<a href="#skills">Skills</a>
<a href="#principles">Principles</a>
<a href="#models">Models</a>
<a href="agent-templates.html">Agent Templates</a>
<a href="changelog.html">Changelog</a>
</div>
</nav>
<section id="routing">
<div class="wrap">
<div class="sh">
<span class="num">01</span>
<h2>See the routing in action</h2>
<p class="lead">This is the whole idea behind <code>/poteto-mode</code>. Type a task, or tap an example, and watch the matching playbook light up below.</p>
</div>
<p class="flow"><span class="cmd-tok">/poteto-mode</span> <span class="arr">→</span> match a playbook <span class="arr">→</span> skills fire as steps <span class="arr">→</span> clean reply</p>
<fieldset class="chooser">
<label for="task">Describe a task <span class="hint">(keyword matching, not the real router)</span></label>
<input id="task" type="text" autocomplete="off" spellcheck="false" placeholder="Describe a task and watch the playbook light up…" aria-label="Describe a task">
</fieldset>
<div class="examples">
<div class="elbl">Or tap an example</div>
<div class="exchips" id="exchips"></div>
</div>
<div class="verdict" id="verdict" aria-live="polite"></div>
</div>
</section>
<section id="playbooks">
<div class="wrap">
<div class="sh">
<span class="num">02</span>
<h2>Playbooks</h2>
<p class="lead">A playbook is a step-by-step recipe for one kind of task. There are seventeen. <code>/poteto-mode</code> copies the matched one in full before any work starts.</p>
</div>
<div class="grid pb" id="pbgrid"></div>
</div>
</section>
<section id="skills">
<div class="wrap">
<div class="sh">
<span class="num">03</span>
<h2>Skills</h2>
<p class="lead">Skills are the individual moves. Reach for one directly when you want a specific result instead of the full route.</p>
</div>
<div class="grid sk" id="skgrid"></div>
</div>
</section>
<section id="principles">
<div class="wrap">
<div class="sh">
<span class="num">04</span>
<h2>Principles</h2>
<p class="lead">Twenty-one rules in five families. Tap any one to see when it applies and what it says.</p>
</div>
<div id="pstack"></div>
</div>
</section>
<section id="models">
<div class="wrap">
<div class="sh">
<span class="num">05</span>
<h2>Models per role</h2>
<p class="lead">pstack can use a different model for each role. Some roles run several models in parallel so the reviews come from different angles.</p>
</div>
<div class="mgrid" id="mgrid"></div>
<p class="models-note">Run <code>/setup-pstack</code> to detect your models and write the rule. Skills read it and fall back to their own defaults when a line is missing, so you override only what you want.</p>
</div>
</section>
<footer>
<div class="wrap">
<p>pstack is <a href="https://x.com/poteto" target="_blank" rel="noopener">poteto's</a> set of engineering skills for coding agents, installed with <code>/add-plugin pstack</code>. This page is an unofficial reading aid.</p>
<p>Source and deploy: <a href="https://github.com/HustleCoding/pstack-explained" target="_blank" rel="noopener">HustleCoding/pstack-explained</a>, live at this URL via GitHub Actions.</p>
</div>
</footer>
<div class="overlay" id="overlay" role="dialog" aria-modal="true" aria-labelledby="dtitle">
<div class="dialog">
<button class="close" id="close" aria-label="Close">×</button>
<div class="cat" id="dcat"></div>
<h3 id="dtitle"></h3>
<div id="dbody"></div>
</div>
</div>
<script>
const PLAYBOOKS = [
{id:"investigation", n:"Investigation", t:"A read-only question. How does X work, why was Y built this way, are we sure about Z, should we do X or Y.",
detail:"Read-only. The deliverable is a cited answer, not a change. Stay in the evidence instead of building a sketch to settle an empirical fork."},
{id:"bug-fix", n:"Bug fix", t:"A reported defect to reproduce, root-cause, and fix with runtime evidence.",
detail:"Reproduce first on the same surface yourself. Trace each symptom to its root cause. Fix there, then verify against the real artifact."},
{id:"perf-issue", n:"Perf issue", t:"A measured slowness to trace and improve against a baseline.",
detail:"One-off fix against a measured baseline, not a sustained loop. That distinguishes it from Hillclimb."},
{id:"hillclimb", n:"Hillclimb", t:"Sustained, scientific improvement of one metric against a target. Loop hypotheses with before/after measurement and one commit per accepted win.",
detail:"Distinct from Perf issue, which is a one-off. Here you keep looping, logging each accepted win as its own commit."},
{id:"runtime-forensics", n:"Runtime forensics", t:"Diagnose a runtime symptom like a leak, idle-CPU spin, or glitch from live instrumentation.",
detail:"The deliverable is a diagnosis, not a fix. Instrument the live system and read the symptom."},
{id:"trace-forensics", n:"Trace forensics", t:"Diagnose a captured profiling artifact like a cpuprofile, trace, spindump, or heap snapshot handed to you after the fact.",
detail:"The deliverable is a diagnosis, not a fix. The artifact is already captured; you read it."},
{id:"feature", n:"Feature", t:"New or changed behavior, built from a named data shape.",
detail:"Name the data shape first. Build the lever. Verify it works against the real artifact, not a proxy."},
{id:"refactoring", n:"Refactoring", t:"A behavior-preserving change to structure or shape. Rename, extract, inline, dedupe, or move.",
detail:"Behavior must stay identical. Bias to deletion and the smallest diff. Remove dead weight before restructuring."},
{id:"prototype", n:"Prototype", t:"A throwaway sketch to make a design or behavioral decision cheaply, or to settle an empirical fork by observing it instead of asking.",
detail:"Built to decide, not to ship. The throwaway probe answers faster than a question to the human and hands back a result to react to."},
{id:"visual-parity", n:"Visual parity", t:"Pixel-exact UI equivalence. Matching two implementations or migrating a styling system.",
detail:"Reproduce the drift first. Fix until it matches the reference, measured on the real surface."},
{id:"authoring-a-skill", n:"Authoring a skill", t:"Writing or editing a SKILL.md.",
detail:"Shape the trigger, the steps, and the reply contract. Encode lessons in structure rather than more text."},
{id:"eval", n:"Eval", t:"Testing how a skill, structure, or prompt change affects agent behavior before promoting it.",
detail:"Blinded comparison against a baseline. Measure behavior change before you promote the new version."},
{id:"autonomous-run", n:"Autonomous run", t:"A long task to drive to completion without stopping. Run until done, /loop until X, going to bed.",
detail:"Keep a decision trail via show-me-your-work so the human can audit it when they return. Commit the trail when stakes need a record."},
{id:"session-pickup", n:"Session pickup", t:"Resuming or taking over a prior agent's in-flight work from a transcript, cloud-agent URL, or pushed branch.",
detail:"Reconstruct state from the record. Fire a fresh subagent with consolidated scope rather than trusting a done summary."},
{id:"pause-safely", n:"Pause safely", t:"Suspending in-flight work cleanly so it can be resumed. On an explicit pause, going offline, a restart, or imminent compaction.",
detail:"The complement to Session pickup. Leave the work in a state another agent can pick up without guessing."},
{id:"multi-phase-plan", n:"Multi-phase plan", t:"Work that spans phases or stacked PRs.",
detail:"Break the work into verifiable units. Order delivery so the sequence proves itself, each unit green before the next."},
{id:"figure-it-out", n:"Figure it out", t:"No bundled playbook fits. Design a bespoke, rigorous playbook for the task.",
detail:"Used for large or cross-cutting efforts like a migration across many call sites, or work the user steps away from to trust later."},
{id:"opening-a-pr", n:"Opening a PR", t:"Invoked at the end of every other playbook.", meta:true,
detail:"Not a task shape of its own. It fires once the matched playbook's work is done, to package and ship the result."}
];
const SKILLS = [
{cmd:"/poteto-mode", when:"Default entry point for any non-trivial task. Reads your request, picks a playbook, and routes the other skills as the steps fire."},
{cmd:"/how", when:"You want a walkthrough of how a subsystem works."},
{cmd:"/why", when:"You want to know why something was built this way. Discovers MCPs at runtime and queries each evidence category in parallel."},
{cmd:"/recall", when:"You are starting or resuming work and want your recent context on a topic rebuilt from your chat history and the shared record."},
{cmd:"/blast-radius", when:"A small-looking change and you want to know what else it could break, proven by running code rather than asserted."},
{cmd:"/architect", when:"About to write code that crosses a function boundary and want the caller's usage, types, and module shape settled first."},
{cmd:"/arena", when:"You want N parallel attempts at the same thing, then grab the best parts of each."},
{cmd:"/interrogate", when:"You have a diff and want four different models to try to break it, including a strict code-quality lens."},
{cmd:"/automate-me", when:"You want your own -mode skill, drafted from how you have actually worked."},
{cmd:"/setup-pstack", when:"You want to pick which models pstack uses per role. Detects your models and writes a config rule."},
{cmd:"/reflect", when:"A long task landed and you want the recipe captured as a skill edit so the next run does not repeat it."},
{cmd:"/tdd", when:"You are fixing a bug and there is a cheap local test path. Write the failing test first, then the fix."},
{cmd:"/typescript-best-practices", when:"You are reading or editing TypeScript. Grounds the type-system-discipline principle in syntax."},
{cmd:"/figure-it-out", when:"No bundled playbook fits. Designs a rigorous, auditable playbook for the task."},
{cmd:"/show-me-your-work", when:"You want a reviewable decision trail. Logs decisions to a TSV you can commit."},
{cmd:"/unslop", when:"You are cleaning up writing. Removes AI tells."},
{cmd:"/teach", when:"Explain a body of work plainly so a person actually understands it. Runs `how` and `why` and weaves what they find into one clear explanation."},
{cmd:"/create-verification-skill", when:"Generate a project-local verification skill that drives your app the way a user does — any language, framework, or platform."},
{cmd:"/maintain-verification-skill", when:"Periodic pass that keeps a project's verification skill and feature map honest."}
];
const GROUPS = [
{key:"core", label:"Core", color:"var(--core)", desc:"How to sequence work and scope it. The bias to delete before you add."},
{key:"arch", label:"Architecture", color:"var(--arch)", desc:"Boundaries, types, and shared state across concurrent actors."},
{key:"verif", label:"Verification", color:"var(--verif)", desc:"Proving it works against the real artifact, and ordering the work so each step can be checked."},
{key:"deleg", label:"Delegation", color:"var(--deleg)", desc:"Keeping context lean and staying unblocked on the human."},
{key:"meta", label:"Meta", color:"var(--meta)", desc:"Turning repeated corrections into structure that enforces itself."}
];
const PRINCIPLES = [
{g:"core", n:"Laziness Protocol", applies:"Refactoring, sizing a diff, or tempted to add abstractions, layers, or signal threading.", rule:"Bias to deletion and the smallest change that solves the problem. If a human maintainer would find it exhausting, it is a bad solution."},
{g:"core", n:"Foundational Thinking", applies:"Before writing logic. Core types and data structures, scaffold-vs-feature sequencing, what concurrent actors share.", rule:"Get the data shape right before writing logic. The right shape makes downstream code obvious."},
{g:"core", n:"Redesign From First Principles", applies:"Integrating a new requirement into an existing design.", rule:"Do not bolt it on. Redesign as if the requirement had been there from day one."},
{g:"core", n:"Subtract Before You Add", applies:"Sequencing an addition, refactor, or rewrite.", rule:"Remove complexity first, then build. Deletion gives a simpler base, which makes the next addition smaller and less brittle."},
{g:"core", n:"Minimize Reader Load", applies:"Reviewing or shaping code that is hard to trace.", rule:"Count layers and hidden state. Collapse one-caller wrappers and shrink mutable scope."},
{g:"core", n:"Outcome-Oriented Execution", applies:"Planned rewrites and migrations with explicit phase boundaries.", rule:"Optimize for the intended, verifiable end state. Do not preserve smooth intermediate states."},
{g:"core", n:"Experience First", applies:"Product, UX, or feature-scope tradeoffs.", rule:"Choose user delight over implementation convenience. Ship fewer polished features over more rough ones."},
{g:"core", n:"Exhaust the Design Space", applies:"A novel interaction or architectural decision with no precedent.", rule:"Build 2 to 3 competing prototypes and compare before committing."},
{g:"core", n:"Build the Lever", applies:"Any non-trivial work.", rule:"Build the tool that does or proves it. The tool is the artifact a reviewer reruns."},
{g:"arch", n:"Boundary Discipline", applies:"Wiring validation, error handling, or framework adapters.", rule:"Guards at system boundaries. Trust internal types. Keep business logic in pure functions."},
{g:"arch", n:"Type System Discipline", applies:"Designing types or a signature in any typed language.", rule:"Make illegal states unrepresentable. The type checker is a proof assistant."},
{g:"arch", n:"Make Operations Idempotent", applies:"Designing commands, lifecycle steps, or loops that run amid crashes and retries.", rule:"Converge to the same end state regardless of partial prior runs."},
{g:"arch", n:"Migrate Callers Then Delete Legacy APIs", applies:"Introducing a new internal API while old callers exist.", rule:"Migrate callers and remove the old API in the same wave. No compatibility layers."},
{g:"arch", n:"Separate Before Serializing Shared State", applies:"Concurrent actors might write the same file, branch, key, or object.", rule:"Eliminate the sharing first. When sharing is real, enforce serialization structurally."},
{g:"arch", n:"Model the Domain", applies:"When writing stateful logic, or when code branches a lot or repeats a shape assumption across files.", rule:"Encode the real domain in a data structure instead of scattering it across conditionals. Reach for state machines, typed models, lookup tables, reducers, or small module boundaries that gather repeated behavior. Don't force an abstraction if the current shape is already clear and local."},
{g:"verif", n:"Prove It Works", applies:"After a task, before declaring done.", rule:"Verify against the real artifact. Not a proxy. Not that it compiles."},
{g:"verif", n:"Fix Root Causes", applies:"Debugging.", rule:"Trace each symptom to its root cause. Reproduce first. Ask why until you reach it."},
{g:"verif", n:"Sequence Work into Verifiable Units", applies:"Multi-step work and how you stack commits and PRs.", rule:"Order work as small units, each ending in a state you can check. Do not advance until the current one is green."},
{g:"deleg", n:"Guard the Context Window", applies:"Context fills up. Large outputs, long files, repeated reads, fan-out planning.", rule:"Route bulk to subagents. Keep summaries in the main thread, not raw payloads."},
{g:"deleg", n:"Never Block on the Human", applies:"Tempted to ask should I do X on reversible work.", rule:"Proceed. Present the result. Let the human course-correct after the fact."},
{g:"meta", n:"Encode Lessons in Structure", applies:"You catch yourself writing the same instruction a second time.", rule:"Encode it as a lint, metadata flag, runtime check, or script instead of more text."}
];
const MODELS = [
{r:"feature, refactoring", k:"single", o:"Code work that builds or restructures."},
{r:"bug-fix", k:"single", o:"Reproduce, root-cause, and fix with evidence."},
{r:"perf-issue", k:"single", o:"Trace measured slowness against a baseline."},
{r:"hillclimb", k:"single", o:"Loop hypotheses against a target metric."},
{r:"judgment and prose", k:"single", o:"Where judgment and writing quality matter most."},
{r:"how explorer", k:"single", o:"Maps the subsystem inside the /how skill."},
{r:"how explainer", k:"single", o:"Writes the /how walkthrough."},
{r:"how critics", k:"panel", o:"One subagent per model reviews the explanation."},
{r:"why investigators", k:"single", o:"Queries one evidence category inside /why."},
{r:"why synthesizer", k:"single", o:"Combines the evidence into the cited answer."},
{r:"reflect tooling", k:"single", o:"Captures the tooling recipe in /reflect."},
{r:"reflect judgment, divergent, synthesizer", k:"single", o:"Captures the judgment recipe in /reflect."},
{r:"arena runners", k:"panel", o:"One subagent per model produces a competing attempt."},
{r:"architect runners", k:"panel", o:"One subagent per model explores a design."},
{r:"interrogate reviewers", k:"panel", o:"One subagent per model tries to break the diff."}
];
const KEYWORDS = {
investigation:["how does","how do","how is","how are","how can","how should","why was","why is","why do","why are","are we sure","should we","read-only","understand","walkthrough","explain","how x works","how it works","how does x"],
"bug-fix":["bug","defect","reproduc","broken","crash","error","fix","regression","wrong","fails","incorrect","doesn't work","does not work","wrong output"],
"perf-issue":["slow","latency","performance","baseline","takes a second","takes a","takes a second to load","seconds to load","load time","takes forever","laggy","optimize","sluggish","jank","throughput","run a cpu trace","run a trace"],
hillclimb:["improve the metric","against a target","sustained","loop hypotheses","metric","benchmark","hillclimb","keep improving","before/after"],
"runtime-forensics":["leak","idle cpu","cpu spin","spin","glitch","stutter","live instrumentation","diagnose a symptom","memory leak"],
"trace-forensics":["cpuprofile","cpu profile file","spindump","heap snapshot","heap dump","profiling artifact","profile file","flame graph","trace file","captured trace","a cpuprofile"],
feature:["build a feature","new feature","add a","implement","feature flag","new behavior","change behavior","add support","build a small"],
refactoring:["refactor","rename","extract","inline","dedupe","deduplicate","move","restructure","behavior-preserving","clean up the code"],
prototype:["prototype","mock","sketch","compare two","two prototypes","try this layout","mock it up","throwaway","build two"],
"visual-parity":["visual parity","pixel","match the design","spacing","styling system","row spacing","matches","pixel-exact"],
"authoring-a-skill":["write a skill","edit a skill","skill.md","authoring a skill","new skill","modify a skill"],
eval:["eval","evaluate the skill","affects agent behavior","blinded","promote the skill","skill change","prompt change"],
"autonomous-run":["going to bed","run until","overnight","autonomous","don't stop","loop until","many hours","i'm away","while i sleep"],
"session-pickup":["resume","take over","prior agent","in-flight","in flight","transcript","pickup","cloud-agent","pick up where"],
"pause-safely":["pause","suspend","going offline","restart","cleanly so it can be resumed","step away","context compaction"],
"multi-phase-plan":["multi-phase","stacked pr","spans phases","migration across","many call sites","multi-pr","across the codebase"],
"figure-it-out":["no playbook","bespoke","cross-cutting","ambitious","step away","trust it when","trust it was done","when i'm back","migrate every caller","migrate all callers","keeping behavior identical","figure it out","unprecedented"]
};
const EXAMPLES = [
"this PR has a subtle bug where scroll drifts every 750ms when idle. repro first, then fix.",
"a big list takes a second to load even though we virtualize. run a cpu trace and tell me why.",
"build a small feature behind a feature flag. verify it really works.",
"build two prototypes of the markdown renderer so we can compare. spawn an agent for each.",
"the row spacing is too tall when this flag is on. the second image is correct. fix until it matches.",
"i'm going to bed. land the stack even if ci flakes. i want everything merged by morning.",
"how do we cancel runs? do we have an n+1 when we look up every run to cancel?",
"why is this feature flag not on yet?",
"migrate every caller from the synchronous store to the new async one, keeping behavior identical.",
"rename this helper across the whole codebase without changing behavior."
];
const EXAMPLE_CHIPS = [
{label:"Fix a scrolling bug", text:EXAMPLES[0]},
{label:"Why is a list slow?", text:EXAMPLES[1]},
{label:"Build a small feature", text:EXAMPLES[2]},
{label:"Compare two designs", text:EXAMPLES[3]},
{label:"Match a UI exactly", text:EXAMPLES[4]},
{label:"Run overnight", text:EXAMPLES[5]},
{label:"How does this work?", text:EXAMPLES[6]},
{label:"Migrate callers", text:EXAMPLES[8]}
];
const $ = s => document.querySelector(s);
const el = (tag, cls) => { const e = document.createElement(tag); if(cls) e.className = cls; return e; };
function renderPlaybooks(){
const grid = $("#pbgrid");
PLAYBOOKS.forEach((p, i) => {
const b = el("button", "card" + (p.meta ? " meta-card" : ""));
b.dataset.id = p.id;
b.type = "button";
b.style.animationDelay = (i * 30) + "ms";
const idx = el("span", "idx"); idx.textContent = String(i + 1).padStart(2, "0");
const nm = el("div", "nm"); nm.textContent = p.n;
const tr = el("div", "tr"); tr.textContent = p.t;
b.append(idx, nm, tr);
b.addEventListener("click", () => openDialog("playbook", p));
grid.appendChild(b);
});
}
function renderSkills(){
const grid = $("#skgrid");
SKILLS.forEach((s, i) => {
const b = el("button", "chip");
b.type = "button";
b.style.animationDelay = (i * 25) + "ms";
const cmd = el("div", "cmd"); cmd.textContent = s.cmd;
const when = el("div", "when"); when.textContent = s.when;
b.append(cmd, when);
b.addEventListener("click", () => openDialog("skill", s));
grid.appendChild(b);
});
}
function renderPrinciples(){
const root = $("#pstack");
GROUPS.forEach((g, gi) => {
const band = el("div", "band");
band.style.animationDelay = (gi * 60) + "ms";
const head = el("div", "band-head");
const tick = el("span", "tick"); tick.style.background = g.color;
const h3 = el("h3"); h3.textContent = g.label;
const ct = el("span", "ct"); ct.textContent = PRINCIPLES.filter(p => p.g === g.key).length + " rules";
const ln = el("span", "ln");
head.append(tick, h3, ct, ln);
band.appendChild(head);
const desc = el("p", "band-desc"); desc.textContent = g.desc;
band.appendChild(desc);
const bricks = el("div", "bricks");
PRINCIPLES.filter(p => p.g === g.key).forEach((p, i) => {
const bk = el("button", "brick");
bk.type = "button";
bk.style.setProperty("--c", g.color);
bk.style.animationDelay = (i * 35) + "ms";
const pn = el("div", "pn"); pn.textContent = p.n;
const pa = el("div", "pa"); pa.textContent = p.applies;
bk.append(pn, pa);
bk.addEventListener("click", () => openDialog("principle", { ...p, group: g.label }));
bricks.appendChild(bk);
});
band.appendChild(bricks);
root.appendChild(band);
});
}
function renderModels(){
const grid = $("#mgrid");
MODELS.forEach((m, i) => {
const card = el("div", "mcard");
card.style.animationDelay = (i * 25) + "ms";
const role = el("div", "mrole"); role.textContent = m.r;
const badge = el("span", "kind" + (m.k === "panel" ? " panel" : ""));
badge.textContent = m.k === "panel" ? "panel · multi-model" : "single model";
const owns = el("div", "mowns"); owns.textContent = m.o;
card.append(role, badge, owns);
grid.appendChild(card);
});
}
function renderExamples(){
const wrap = $("#exchips");
EXAMPLE_CHIPS.forEach(c => {
const b = el("button", "exchip");
b.type = "button";
b.textContent = c.label;
b.addEventListener("click", () => {
const input = $("#task");
input.value = c.text;
showVerdict(c.text);
input.focus();
const card = document.querySelector("#pbgrid .card.recommended");
if(card) setTimeout(() => card.scrollIntoView({ behavior:"smooth", block:"center" }), 120);
});
wrap.appendChild(b);
});
}
function openDialog(kind, data){
const cat = $("#dcat"), title = $("#dtitle"), body = $("#dbody");
body.innerHTML = "";
if(kind === "playbook"){
cat.textContent = "Playbook";
title.textContent = data.n;
addField(body, "Matches when", data.t);
addField(body, "What it does", data.detail);
} else if(kind === "skill"){
cat.textContent = "Skill";
title.textContent = data.cmd;
addField(body, "Use it when", data.when);
} else {
cat.textContent = data.group + " principle";
title.textContent = data.n;
addField(body, "When it applies", data.applies);
addField(body, "The rule", data.rule);
}
$("#overlay").classList.add("open");
document.body.style.overflow = "hidden";
$("#close").focus();
}
function addField(parent, k, v){
const f = el("div", "field");
const kk = el("div", "k"); kk.textContent = k;
const vv = el("div", "v"); vv.textContent = v;
f.append(kk, vv);
parent.appendChild(f);
}
function closeDialog(){
$("#overlay").classList.remove("open");
document.body.style.overflow = "";
}
function scorePlaybooks(text){
const t = text.toLowerCase();
if(!t.trim()) return [];
const out = [];
for(const id in KEYWORDS){
const hits = KEYWORDS[id].filter(k => t.includes(k));
if(hits.length) out.push({ id, hits, score: hits.length });
}
out.sort((a, b) => b.score - a.score || b.hits.length - a.hits.length);
return out;
}
function clearHighlights(){
document.querySelectorAll("#pbgrid .card").forEach(c => c.classList.remove("recommended"));
}
function showVerdict(text){
const v = $("#verdict");
const results = scorePlaybooks(text);
clearHighlights();
if(!text.trim()){ v.innerHTML = ""; return; }
if(!results.length){
v.innerHTML = '<span class="none">No clear match here. When no playbook fits, pstack uses <span class="pick">Figure it out</span>.</span>';
return;
}
const top = results[0];
const pb = PLAYBOOKS.find(p => p.id === top.id);
const card = document.querySelector('#pbgrid .card[data-id="' + top.id + '"]');
if(card) card.classList.add("recommended");
const kwHtml = top.hits.slice(0, 5).map(h => '<span class="kw">' + h + '</span>').join("");
const tail = results.length > 1 ? ' <span class="why">also in range: ' + results.slice(1, 3).map(r => PLAYBOOKS.find(p => p.id === r.id).n).join(", ") + "</span>" : "";
v.innerHTML = 'pstack would route this to the <span class="pick">' + pb.n + '</span> playbook. <span class="why">matched ' + kwHtml + tail + "</span>";
}
let exIdx = 0, exFrozen = false;
function rotateExample(){
if(exFrozen) return;
const input = $("#task");
if(document.activeElement === input || input.value) return;
input.placeholder = EXAMPLES[exIdx];
exIdx = (exIdx + 1) % EXAMPLES.length;
}
function init(){
renderPlaybooks();
renderSkills();
renderPrinciples();
renderModels();
renderExamples();
const input = $("#task");
input.addEventListener("input", () => { showVerdict(input.value); });
input.addEventListener("focus", () => { exFrozen = true; if(input.placeholder.startsWith("Describe")) input.placeholder = "e.g. " + EXAMPLES[0]; });
input.addEventListener("blur", () => { exFrozen = false; if(!input.value) input.placeholder = "Describe a task and watch the playbook light up…"; });
$("#close").addEventListener("click", closeDialog);
$("#overlay").addEventListener("click", e => { if(e.target === $("#overlay")) closeDialog(); });
document.addEventListener("keydown", e => { if(e.key === "Escape") closeDialog(); });
setInterval(rotateExample, 3400);
rotateExample();
}
init();
</script>
</body>
</html>