Compare commits
632 Commits
6f1350b527
...
Iteration/
| Author | SHA1 | Date | |
|---|---|---|---|
| d022cc8788 | |||
| bcf3e31db6 | |||
| c4f430ccb3 | |||
| 1a9db9328e | |||
| 2e130b98f5 | |||
| 5cdcdad534 | |||
| d2e08dbbec | |||
| 026d4908d8 | |||
| 31232ea899 | |||
| b38df737e1 | |||
| 346156ee9b | |||
| 0d79130e07 | |||
| b3fb8c7a82 | |||
| 44fb21eb6a | |||
| 8f738ffbe8 | |||
| 6db152bb2f | |||
| fc6af43baa | |||
| 52bdbbae25 | |||
| 76f657c986 | |||
| a2793f7bec | |||
| b5b7f9a41e | |||
| b930662817 | |||
| ab9da7bb33 | |||
| 781e82441d | |||
| f4b6a55b27 | |||
| 8cf921f11c | |||
| c5871b59a1 | |||
| c0b9604515 | |||
| 5c2ef97c24 | |||
| 2885b503b3 | |||
| 062f5a436f | |||
| ba435dd6a6 | |||
| 3f21f7a7d6 | |||
| 5f960daf78 | |||
| 3b7b856e48 | |||
| cf36f1447f | |||
|
|
1f634eb465 | ||
|
|
84ab3bad99 | ||
|
|
6c8594633a | ||
|
|
064961471b | ||
|
|
e37aad9e1d | ||
|
|
7ec84fbc0f | ||
|
|
2f0b24ce88 | ||
|
|
e56649e5be | ||
|
|
1c492fe8db | ||
|
|
e139f5e227 | ||
|
|
016e7bee79 | ||
|
|
c437593a8c | ||
|
|
134021c91e | ||
|
|
5c779cb6e0 | ||
|
|
32c9859b38 | ||
|
|
283e3e3eb3 | ||
|
|
6a70713b30 | ||
|
|
4ccbf13197 | ||
|
|
0ae8148689 | ||
|
|
9a802c04e5 | ||
|
|
4d419a1966 | ||
|
|
fe2041bcf5 | ||
|
|
6ac19d6565 | ||
|
|
cce27975a1 | ||
|
|
d86ac46761 | ||
|
|
420f3d83f9 | ||
|
|
27fba9160c | ||
|
|
5089a71764 | ||
|
|
5f57429fb0 | ||
|
|
46ede81aef | ||
|
|
36e68e6672 | ||
|
|
a6cd550b94 | ||
|
|
c6e65fde9b | ||
|
|
1b47fb8f32 | ||
|
|
c5126c583b | ||
|
|
918b9b4f25 | ||
|
|
944526d9ef | ||
|
|
6c8352491c | ||
|
|
1ee554daec | ||
|
|
34a7cf0f3c | ||
|
|
6a3fffc46c | ||
|
|
872769e69e | ||
|
|
1550ef3cb1 | ||
|
|
58863e8ec5 | ||
|
|
0d96a94e5f | ||
|
|
cf689beceb | ||
|
|
f4d92f99a2 | ||
|
|
e131d6ba77 | ||
|
|
bcbc290bad | ||
| 4c1e4f428a | |||
| 20b5d70af9 | |||
| 226474b434 | |||
| 860f589b9a | |||
| 2adbc87a52 | |||
| 6e564d1d1a | |||
| 3d28e29eaa | |||
| 0948494b1c | |||
| 599289a94e | |||
| b988f99a95 | |||
| 56adc30409 | |||
| 53e95751b1 | |||
| 58bdb2f18e | |||
| a03ba5d396 | |||
| 2768deb0b6 | |||
| e2301d5f3b | |||
| 9f5c5c3680 | |||
| 5e7e68cce7 | |||
| ca939ff617 | |||
| 8de9c59b1c | |||
| 631c5392b6 | |||
| af5a21ed1e | |||
| 480d4c4583 | |||
| ed071918ed | |||
| d597a25c69 | |||
| 81aa3c2b11 | |||
| a5388446d3 | |||
| 8f3a68a673 | |||
| 95fc0b0a1b | |||
| b7369a9c71 | |||
| 05a5694a5f | |||
| 1350a9ed79 | |||
| 93200a9074 | |||
| 02d522564f | |||
| b6d21b81e3 | |||
| 98ff88d5c3 | |||
| a9eaf1d697 | |||
| 97851c2595 | |||
| ebaf112c80 | |||
| 09cf0be86e | |||
| 6a6672d0e4 | |||
| 0b86720534 | |||
| a93acc0784 | |||
| 7c85a8cf29 | |||
| 348cb4e670 | |||
| e77bb0d09b | |||
| 73a3a04204 | |||
| 573e887ca5 | |||
| 29d3d48dbe | |||
| d42fe2b0ca | |||
| eb74dd7849 | |||
| 3a2e3f2571 | |||
| b0bd37ec12 | |||
| 001f8caf35 | |||
| 21b73a750d | |||
| ee7bfb81f0 | |||
| b4a9e0c1c9 | |||
| 948243b13d | |||
| 5425e2ce19 | |||
| 70fb7dadef | |||
| 8a3c45c577 | |||
| 9238eeb56e | |||
| 9674e0d0d9 | |||
| 248ed55f4e | |||
| 66e12e9629 | |||
| 4f4e42f27e | |||
| 6b3b33b2f0 | |||
| 74c7b27327 | |||
| 37b4483183 | |||
| 516a7f5bdf | |||
| fe4c545308 | |||
| bb33232b1b | |||
| c3ae7fcfbc | |||
| 66cec7515a | |||
| cb75b5668b | |||
| 95104100c9 | |||
| 02ccf57aa3 | |||
| 60debb7505 | |||
| 531de3c760 | |||
| a887f91686 | |||
| 7efcea2b54 | |||
| 85bef83752 | |||
| bc344f892c | |||
| 41db5b56af | |||
| 15cd26c81a | |||
| 3f2cf31543 | |||
| ba73913a41 | |||
| a44c88005d | |||
| f177c26016 | |||
| 4bde09837a | |||
| c250a47651 | |||
| a7f2c4480a | |||
| 37e113bbf4 | |||
| 5442dcbeda | |||
| b9fc828567 | |||
| 327c72ca74 | |||
| 32f03cd2c1 | |||
| 8d2e8faa84 | |||
| 1f1f31a7cb | |||
| 52d883d8a0 | |||
| 6b1fbf4a92 | |||
| 10a35625a5 | |||
| a207c51bfb | |||
| 6eb6d381f7 | |||
| e27758cce3 | |||
| 7137ed087f | |||
| a9690a49db | |||
| 0f58886454 | |||
| 9f8173e124 | |||
| 1d930cc82d | |||
| 1b21d4dafa | |||
| e573a82120 | |||
| e049080f6c | |||
| 9942bbc74e | |||
| fc9f819787 | |||
| 33a4d25ca9 | |||
| e9862a67ba | |||
| 868f5a8ef3 | |||
| fd795d8742 | |||
| 2b3a9cb33f | |||
| 4b8784d5e7 | |||
| 2413c2fe14 | |||
| e52671eaed | |||
| 716e9f5971 | |||
| 28ac58ea18 | |||
| 8e61cb1a54 | |||
| 4aab0bcbf2 | |||
| 6caf0f6141 | |||
| 6e15e1b853 | |||
| ad30f5d41b | |||
| b538e45bd9 | |||
| 1033d7666b | |||
| 0d2baabcf1 | |||
| 9d8e3926ff | |||
| c5ed040359 | |||
| 363e0efb34 | |||
| 1363797378 | |||
| 71498883b5 | |||
| 1d75a4c31a | |||
| 89eec1406a | |||
| 9a625f51ef | |||
| 2d6456c4a3 | |||
| 9bd1d60e85 | |||
| 908c5fa1de | |||
| 505b30aa8f | |||
| e0a37f4a11 | |||
| 4c284c422c | |||
| 44e4f03957 | |||
| 0a2961ecd6 | |||
| fa554c3930 | |||
| 1125701329 | |||
| ed1facc1b8 | |||
| 0cba6fd6d5 | |||
| 94f20ce8c7 | |||
| aef20d2ab1 | |||
| 0ec16d4afa | |||
| 7e4c61e6e9 | |||
| 2d0b4e9bc0 | |||
| 520b126ecf | |||
| 5d9be1d7e4 | |||
| 5065d925ad | |||
| 97d6319b64 | |||
| 0a27a78d97 | |||
| fc995187df | |||
| 8bf1282378 | |||
| 854330a4b4 | |||
| e40ad462e1 | |||
| 647d78309d | |||
| c7f486ae09 | |||
| 71559547b6 | |||
| 6292443f69 | |||
| 97c196b7c6 | |||
| cb328f3ec2 | |||
| 9992e82a3c | |||
| e2003eb9e3 | |||
| 71048e31d4 | |||
| 4a1e44f9e1 | |||
| dd408fcdca | |||
| 516bb92a16 | |||
| 023a4309eb | |||
| b972a776d9 | |||
| d26010b29d | |||
| 8706247436 | |||
| 8154a61453 | |||
| d9de704d73 | |||
| 4ea4a1e53f | |||
| 041856dc8c | |||
| 7cdb66cbe4 | |||
| 5410181e77 | |||
| 548ec0cd6b | |||
| 5e9e41db21 | |||
| c35542f911 | |||
| 42c5ec912f | |||
| c0b64c9e30 | |||
| 37706bc7ce | |||
| 7308afe801 | |||
| c1456f3c54 | |||
| 640ea8ec99 | |||
| 8569873690 | |||
| 5196472d7e | |||
| 814a2956b3 | |||
| 4879d9bf07 | |||
| 8d8d426e2e | |||
| 246ae5e123 | |||
| 7e613a0d2b | |||
| 510a5bfb21 | |||
| decb7a10ec | |||
| 5588c7c762 | |||
| eebc7194d8 | |||
| e945a672ed | |||
| 41d3667df2 | |||
| 8f66ca7bcb | |||
| e9ff14df0e | |||
| c807b99a91 | |||
| 677d6239ce | |||
| d0989c66bb | |||
| 67f3286e09 | |||
| a8b52f8ae1 | |||
| 5496cb58aa | |||
| 3cb16804a4 | |||
| 2a7ac3f86e | |||
| ef8ec025bd | |||
| 23e12d8a73 | |||
| 4a18aac6ce | |||
| 51fa027eb8 | |||
| afc6d7fc76 | |||
| cd807e58e3 | |||
| ff7e749bf2 | |||
| 0627ffec42 | |||
| 1d6535b81b | |||
| 0f6fd42aff | |||
| c1cec0aede | |||
| 6dfc5c0301 | |||
| fce35e8a2d | |||
| 81ba84cf05 | |||
| 384a54164b | |||
| a7dfe858c7 | |||
| e9df1e7ded | |||
| cc56e27c01 | |||
| 78d6aded9b | |||
| 14a8ea5a2c | |||
| 38ef73fd7f | |||
| 303bffbe84 | |||
| 80c6f6c756 | |||
| 7e489a19fb | |||
| 434a8b0349 | |||
| 10fcc0b3c9 | |||
| c0c32c62f4 | |||
| a8a91fe04e | |||
| 8980e6d999 | |||
| 322ded0012 | |||
| ecfa417c15 | |||
| 4770e053ee | |||
| 138f08ddd7 | |||
| 950c0bfb8f | |||
| 724967b8b8 | |||
| 1c6f8ed07b | |||
| 58eb047433 | |||
| 045aa4fa3a | |||
| 69ee754c19 | |||
| 9d50424edc | |||
| 302664404b | |||
| 619f029a5a | |||
| 31e147495c | |||
| ad095d73f3 | |||
| 8b5cd50576 | |||
| 8227ab2f20 | |||
| 6457ffa6f9 | |||
| 8d69858413 | |||
| 49e8ebed60 | |||
| bb5a5ec6ef | |||
| cf602ab5be | |||
| d7ade821f4 | |||
| 7f42d198eb | |||
| ebc3e78ba9 | |||
| 00cae60fe1 | |||
| 67e0840695 | |||
| bc2dbcb416 | |||
| dc4f3cb7ba | |||
| 121462c00f | |||
| 5b283285a2 | |||
| 4e65cf95d1 | |||
| 2b408230a6 | |||
| 48ad0844ae | |||
| 2ef8b8a705 | |||
| 33e7f99fdc | |||
| 06ee422e34 | |||
| 30d6743310 | |||
| a97462dd69 | |||
| d56b841443 | |||
| 8296742cce | |||
| 0c82ae2b73 | |||
| f339fb1987 | |||
| 40809d11c5 | |||
| 50dbc52432 | |||
| cebcada950 | |||
| f5dd2ce4ab | |||
| 02b10f87cf | |||
| 5e64c64ce3 | |||
| a2fd1dfd62 | |||
| 623a622298 | |||
| 65e461eff7 | |||
| e81d3a58ad | |||
| da8aedc96b | |||
| 9995dcd0a3 | |||
| f0f21e52f1 | |||
| a8becf55ff | |||
| f9da1a937f | |||
| a202b2da8a | |||
| 3b54850f1b | |||
| 00dba42a63 | |||
| 3450c20236 | |||
| debe8f6edc | |||
| d0ef3bb712 | |||
| 8826d95516 | |||
| 8af5d1a045 | |||
| ff62ea457a | |||
| 71b948f22b | |||
| 129a38f942 | |||
| 15dbf8dc66 | |||
| ba1886c314 | |||
| 8819c94322 | |||
| 825def88a4 | |||
| 42b884aaf8 | |||
| 8a4c6f3cee | |||
| ce6aa62fd6 | |||
| 4ff5dcf55e | |||
| d3eaac0eb0 | |||
| 8ace759200 | |||
| 5432fb0513 | |||
| e1ec024904 | |||
| 754bbcdf3b | |||
| 47aa5f8d25 | |||
| facc766993 | |||
| 8553a46143 | |||
| 4be473c248 | |||
| 8a531237c3 | |||
| 922a3d0b49 | |||
| 60b43bb29e | |||
| bdac4ab7b2 | |||
| 0ebb3d75f3 | |||
| 7dd55630dc | |||
| 9ee2f0b466 | |||
| 244a2742c1 | |||
| 8f62496250 | |||
| 344850fe1d | |||
| 2a92ab64af | |||
| 62eb43a1cf | |||
| 84aca2fd63 | |||
| c083596ef6 | |||
| fb14c51fc2 | |||
| dbeed92ed9 | |||
| 030425be33 | |||
| 0ed804c4b3 | |||
| 20b4b6d6bf | |||
| 9a4d87a0d4 | |||
| 0df992c98a | |||
| f74b7da25f | |||
| 6de830fcc4 | |||
| da50a35a62 | |||
| 1fd0072f69 | |||
| bfcea1c18f | |||
| db166807c7 | |||
| 809cb2b8a8 | |||
| f48f11beb4 | |||
| 5862018e7c | |||
| dc9bf0c9b7 | |||
| 7f2e978288 | |||
| 3078bc71c8 | |||
| cf59380784 | |||
| 876151ec1c | |||
| c10b70757f | |||
| 4d1e714366 | |||
| d2b765327c | |||
| 7dfcf41b41 | |||
| ed334b946b | |||
| 95b2334658 | |||
| da66e673fe | |||
| 284f6c15c7 | |||
| 55918a0b88 | |||
| d2494798aa | |||
| b9733c4913 | |||
| 9bd55a1695 | |||
| e78f5794b9 | |||
| df76e33105 | |||
| ec86dbf463 | |||
| 817d0d6e04 | |||
| b44363b335 | |||
| 3e8f613475 | |||
| 242e0b1f40 | |||
| 060d8fd65e | |||
| f3297f0529 | |||
| 63ca12393b | |||
| 429edf0d19 | |||
| 7c64e433e8 | |||
| 269769bfe4 | |||
| 1980c846f2 | |||
| 89f9875a97 | |||
| 30c56e66dd | |||
| c86afbfa8f | |||
| aa41a5ed5e | |||
| a308ee228b | |||
| b0da71bd25 | |||
| 7f18765911 | |||
| 876c92095c | |||
| e45610661e | |||
| d85d7bffd6 | |||
| fe77d9ca72 | |||
| 9b83f92fb6 | |||
| 2248558bd3 | |||
| 2aae31ac5f | |||
| 5031bf15b9 | |||
| 9c768e0719 | |||
| b6c379265d | |||
| 4156bfc9dd | |||
| 0ef136f008 | |||
| b1d6355a7d | |||
| 907e500ffb | |||
| 275debdd38 | |||
| b9c3875c08 | |||
| b5147d1acb | |||
| 86f8d0b644 | |||
| a83dca2eb2 | |||
| 51ee38bc2e | |||
| 9417179161 | |||
| b52cb9a078 | |||
| de9eacd273 | |||
| f40abaf93c | |||
| e0cb4498e6 | |||
| c7b8ecfebf | |||
| 2ca33b7172 | |||
| 769f6b8709 | |||
| dd68d0a62b | |||
| c5018f110f | |||
| ad3a7a770a | |||
| beed9d25e0 | |||
| 163d01dae5 | |||
| e7d52db270 | |||
| 672274f9fd | |||
| b52744b149 | |||
| 61155952a7 | |||
| 8efe79526a | |||
| a625462205 | |||
| c5429e7287 | |||
| e661b59bb9 | |||
| 5bb0ff0ddf | |||
| 8ed3d9da93 | |||
| c5bf85c8de | |||
| f5000f2bfc | |||
| 4189dbe98f | |||
| bc60886aea | |||
| 6ecc0b5adb | |||
| 1d602ad1f9 | |||
| 03a0960c4d | |||
| 4ba1f5b99d | |||
| 1382cbbf47 | |||
| c1eec5d4f1 | |||
| efe8a362aa | |||
| 6dc6afece0 | |||
| 037595c22e | |||
| 25e9749564 | |||
| 18daeae65a | |||
| f32d32cd36 | |||
| c665f32976 | |||
| 655c9ce7a6 | |||
| 353621d923 | |||
| 804145332b | |||
| 931e140e8e | |||
| b11edde720 | |||
| 8ab5ebc3af | |||
| dc84cef2ce | |||
| b18ecfeb55 | |||
| fba8e9e76b | |||
| ad6d43e0cd | |||
| 5a90caa619 | |||
| bc7e5d6f6d | |||
| 0b82f30f86 | |||
| 301eb6158e | |||
| 6c83087319 | |||
| 2ae585225b | |||
| 543c454f16 | |||
| 246ea6e287 | |||
| 80f560df33 | |||
| 5851cc6403 | |||
| 76b539e867 | |||
| b47f7b4f46 | |||
| 37f43d2e2d | |||
| a30b3036bb | |||
| d81bd242a4 | |||
| d309951493 | |||
| 62708892ec | |||
| b8dda7e62a | |||
| 5891e9db8d | |||
| 902ddb3687 | |||
| 760b3db1df | |||
| 001eb81e5e | |||
| 1ec7de4ec4 | |||
| 113b3edd69 | |||
| 22f19377a5 | |||
| c7bf43f306 | |||
| 1036b5979e | |||
| cb0835cd94 | |||
| 526d9c62b7 | |||
| 116355835a | |||
| f6a0f0f39c | |||
| e461791a0e | |||
| 109ae688d2 | |||
| 65b4127b84 | |||
| bf591095a2 | |||
| accf7cb293 | |||
| ffeb0417c0 | |||
| 32beac4424 | |||
| 3f63fffbb1 | |||
| 4856a88d41 | |||
| 1cf17e8f14 | |||
| 409a68d60b | |||
| 1290160728 | |||
| 2b0f79be81 | |||
| d977000a66 | |||
| c9fee7f2f6 | |||
| b02175271a | |||
| e87513541b | |||
| dfcf16f548 | |||
| a945a4f554 | |||
| 1da680a790 | |||
| 23eb0307bb | |||
| 5fefe9d0cb | |||
| 79c061b6fa | |||
| 30a0717316 | |||
| e2e6a64ba4 | |||
| d104d297ca | |||
| 5a179ba16b | |||
| 477a9fc98d | |||
| ce0783f96e | |||
| fdcff33058 | |||
| 45aa7deb87 | |||
| 194078674a | |||
| a924e63e68 | |||
| 6821e5abcf | |||
| b68e7ec013 | |||
| 46e4e5f4f1 | |||
| 23be0a7d3e | |||
| 8677a54370 | |||
| 91c9bbfeb8 | |||
| 1489abe668 | |||
| 3b1fd91709 | |||
| 2291f7740d |
30
.agents/rules/ponytail.md
Normal file
30
.agents/rules/ponytail.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Ponytail, lazy senior dev mode
|
||||
|
||||
You are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written.
|
||||
|
||||
Before writing any code, stop at the first rung that holds:
|
||||
|
||||
1. Does this need to be built at all? (YAGNI)
|
||||
2. Does it already exist in this codebase? Reuse the helper, util, or pattern that's already here, don't re-write it.
|
||||
3. Does the standard library already do this? Use it.
|
||||
4. Does a native platform feature cover it? Use it.
|
||||
5. Does an already-installed dependency solve it? Use it.
|
||||
6. Can this be one line? Make it one line.
|
||||
7. Only then: write the minimum code that works.
|
||||
|
||||
The ladder runs after you understand the problem, not instead of it: read the task and the code it touches, trace the real flow end to end, then climb.
|
||||
|
||||
Bug fix = root cause, not symptom: a report names a symptom. Grep every caller of the function you touch and fix the shared function once — one guard there is a smaller diff than one per caller, and patching only the path the ticket names leaves a sibling caller still broken.
|
||||
|
||||
Rules:
|
||||
|
||||
- No abstractions that weren't explicitly requested.
|
||||
- No new dependency if it can be avoided.
|
||||
- No boilerplate nobody asked for.
|
||||
- Deletion over addition. Boring over clever. Fewest files possible.
|
||||
- Shortest working diff wins, but only once you understand the problem. The smallest change in the wrong place isn't lazy, it's a second bug.
|
||||
- Question complex requests: "Do you actually need X, or does Y cover it?"
|
||||
- Pick the edge-case-correct option when two stdlib approaches are the same size, lazy means less code, not the flimsier algorithm.
|
||||
- Mark intentional simplifications with a `ponytail:` comment. If the shortcut has a known ceiling (global lock, O(n²) scan, naive heuristic), the comment names the ceiling and the upgrade path.
|
||||
|
||||
Not lazy about: understanding the problem (read it fully and trace the real flow before picking a rung, a small diff you don't understand is just laziness dressed up as efficiency), input validation at trust boundaries, error handling that prevents data loss, security, accessibility, the calibration real hardware needs (the platform is never the spec ideal, a clock drifts, a sensor reads off), anything explicitly requested. Lazy code without its check is unfinished: non-trivial logic leaves ONE runnable check behind, the smallest thing that fails if the logic breaks (an assert-based demo/self-check or one small test file; no frameworks, no fixtures). Trivial one-liners need no test.
|
||||
49
.agents/skills/caveman/SKILL.md
Normal file
49
.agents/skills/caveman/SKILL.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
name: caveman
|
||||
description: >
|
||||
Ultra-compressed communication mode. Cuts token usage ~75% by dropping
|
||||
filler, articles, and pleasantries while keeping full technical accuracy.
|
||||
Use when user says "caveman mode", "talk like caveman", "use caveman",
|
||||
"less tokens", "be brief", or invokes /caveman.
|
||||
---
|
||||
|
||||
Respond terse like smart caveman. All technical substance stay. Only fluff die.
|
||||
|
||||
## Persistence
|
||||
|
||||
ACTIVE EVERY RESPONSE once triggered. No revert after many turns. No filler drift. Still active if unsure. Off only when user says "stop caveman" or "normal mode".
|
||||
|
||||
## Rules
|
||||
|
||||
Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Fragments OK. Short synonyms (big not extensive, fix not "implement a solution for"). Abbreviate common terms (DB/auth/config/req/res/fn/impl). Strip conjunctions. Use arrows for causality (X -> Y). One word when one word enough.
|
||||
|
||||
Technical terms stay exact. Code blocks unchanged. Errors quoted exact.
|
||||
|
||||
Pattern: `[thing] [action] [reason]. [next step].`
|
||||
|
||||
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
|
||||
Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:"
|
||||
|
||||
### Examples
|
||||
|
||||
**"Why React component re-render?"**
|
||||
|
||||
> Inline obj prop -> new ref -> re-render. `useMemo`.
|
||||
|
||||
**"Explain database connection pooling."**
|
||||
|
||||
> Pool = reuse DB conn. Skip handshake -> fast under load.
|
||||
|
||||
## Auto-Clarity Exception
|
||||
|
||||
Drop caveman temporarily for: security warnings, irreversible action confirmations, multi-step sequences where fragment order risks misread, user asks to clarify or repeats question. Resume caveman after clear part done.
|
||||
|
||||
Example -- destructive op:
|
||||
|
||||
> **Warning:** This will permanently delete all rows in the `users` table and cannot be undone.
|
||||
>
|
||||
> ```sql
|
||||
> DROP TABLE users;
|
||||
> ```
|
||||
>
|
||||
> Caveman resume. Verify backup exist first.
|
||||
117
.agents/skills/diagnose/SKILL.md
Normal file
117
.agents/skills/diagnose/SKILL.md
Normal file
@@ -0,0 +1,117 @@
|
||||
---
|
||||
name: diagnose
|
||||
description: Disciplined diagnosis loop for hard bugs and performance regressions. Reproduce → minimise → hypothesise → instrument → fix → regression-test. Use when user says "diagnose this" / "debug this", reports a bug, says something is broken/throwing/failing, or describes a performance regression.
|
||||
---
|
||||
|
||||
# Diagnose
|
||||
|
||||
A discipline for hard bugs. Skip phases only when explicitly justified.
|
||||
|
||||
When exploring the codebase, use the project's domain glossary to get a clear mental model of the relevant modules, and check ADRs in the area you're touching.
|
||||
|
||||
## Phase 1 — Build a feedback loop
|
||||
|
||||
**This is the skill.** Everything else is mechanical. If you have a fast, deterministic, agent-runnable pass/fail signal for the bug, you will find the cause — bisection, hypothesis-testing, and instrumentation all just consume that signal. If you don't have one, no amount of staring at code will save you.
|
||||
|
||||
Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.**
|
||||
|
||||
### Ways to construct one — try them in roughly this order
|
||||
|
||||
1. **Failing test** at whatever seam reaches the bug — unit, integration, e2e.
|
||||
2. **Curl / HTTP script** against a running dev server.
|
||||
3. **CLI invocation** with a fixture input, diffing stdout against a known-good snapshot.
|
||||
4. **Headless browser script** (Playwright / Puppeteer) — drives the UI, asserts on DOM/console/network.
|
||||
5. **Replay a captured trace.** Save a real network request / payload / event log to disk; replay it through the code path in isolation.
|
||||
6. **Throwaway harness.** Spin up a minimal subset of the system (one service, mocked deps) that exercises the bug code path with a single function call.
|
||||
7. **Property / fuzz loop.** If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode.
|
||||
8. **Bisection harness.** If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can `git bisect run` it.
|
||||
9. **Differential loop.** Run the same input through old-version vs new-version (or two configs) and diff outputs.
|
||||
10. **HITL bash script.** Last resort. If a human must click, drive _them_ with `scripts/hitl-loop.template.sh` so the loop is still structured. Captured output feeds back to you.
|
||||
|
||||
Build the right feedback loop, and the bug is 90% fixed.
|
||||
|
||||
### Iterate on the loop itself
|
||||
|
||||
Treat the loop as a product. Once you have _a_ loop, ask:
|
||||
|
||||
- Can I make it faster? (Cache setup, skip unrelated init, narrow the test scope.)
|
||||
- Can I make the signal sharper? (Assert on the specific symptom, not "didn't crash".)
|
||||
- Can I make it more deterministic? (Pin time, seed RNG, isolate filesystem, freeze network.)
|
||||
|
||||
A 30-second flaky loop is barely better than no loop. A 2-second deterministic loop is a debugging superpower.
|
||||
|
||||
### Non-deterministic bugs
|
||||
|
||||
The goal is not a clean repro but a **higher reproduction rate**. Loop the trigger 100×, parallelise, add stress, narrow timing windows, inject sleeps. A 50%-flake bug is debuggable; 1% is not — keep raising the rate until it's debuggable.
|
||||
|
||||
### When you genuinely cannot build a loop
|
||||
|
||||
Stop and say so explicitly. List what you tried. Ask the user for: (a) access to whatever environment reproduces it, (b) a captured artifact (HAR file, log dump, core dump, screen recording with timestamps), or (c) permission to add temporary production instrumentation. Do **not** proceed to hypothesise without a loop.
|
||||
|
||||
Do not proceed to Phase 2 until you have a loop you believe in.
|
||||
|
||||
## Phase 2 — Reproduce
|
||||
|
||||
Run the loop. Watch the bug appear.
|
||||
|
||||
Confirm:
|
||||
|
||||
- [ ] The loop produces the failure mode the **user** described — not a different failure that happens to be nearby. Wrong bug = wrong fix.
|
||||
- [ ] The failure is reproducible across multiple runs (or, for non-deterministic bugs, reproducible at a high enough rate to debug against).
|
||||
- [ ] You have captured the exact symptom (error message, wrong output, slow timing) so later phases can verify the fix actually addresses it.
|
||||
|
||||
Do not proceed until you reproduce the bug.
|
||||
|
||||
## Phase 3 — Hypothesise
|
||||
|
||||
Generate **3–5 ranked hypotheses** before testing any of them. Single-hypothesis generation anchors on the first plausible idea.
|
||||
|
||||
Each hypothesis must be **falsifiable**: state the prediction it makes.
|
||||
|
||||
> Format: "If <X> is the cause, then <changing Y> will make the bug disappear / <changing Z> will make it worse."
|
||||
|
||||
If you cannot state the prediction, the hypothesis is a vibe — discard or sharpen it.
|
||||
|
||||
**Show the ranked list to the user before testing.** They often have domain knowledge that re-ranks instantly ("we just deployed a change to #3"), or know hypotheses they've already ruled out. Cheap checkpoint, big time saver. Don't block on it — proceed with your ranking if the user is AFK.
|
||||
|
||||
## Phase 4 — Instrument
|
||||
|
||||
Each probe must map to a specific prediction from Phase 3. **Change one variable at a time.**
|
||||
|
||||
Tool preference:
|
||||
|
||||
1. **Debugger / REPL inspection** if the env supports it. One breakpoint beats ten logs.
|
||||
2. **Targeted logs** at the boundaries that distinguish hypotheses.
|
||||
3. Never "log everything and grep".
|
||||
|
||||
**Tag every debug log** with a unique prefix, e.g. `[DEBUG-a4f2]`. Cleanup at the end becomes a single grep. Untagged logs survive; tagged logs die.
|
||||
|
||||
**Perf branch.** For performance regressions, logs are usually wrong. Instead: establish a baseline measurement (timing harness, `performance.now()`, profiler, query plan), then bisect. Measure first, fix second.
|
||||
|
||||
## Phase 5 — Fix + regression test
|
||||
|
||||
Write the regression test **before the fix** — but only if there is a **correct seam** for it.
|
||||
|
||||
A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site. If the only available seam is too shallow (single-caller test when the bug needs multiple callers, unit test that can't replicate the chain that triggered the bug), a regression test there gives false confidence.
|
||||
|
||||
**If no correct seam exists, that itself is the finding.** Note it. The codebase architecture is preventing the bug from being locked down. Flag this for the next phase.
|
||||
|
||||
If a correct seam exists:
|
||||
|
||||
1. Turn the minimised repro into a failing test at that seam.
|
||||
2. Watch it fail.
|
||||
3. Apply the fix.
|
||||
4. Watch it pass.
|
||||
5. Re-run the Phase 1 feedback loop against the original (un-minimised) scenario.
|
||||
|
||||
## Phase 6 — Cleanup + post-mortem
|
||||
|
||||
Required before declaring done:
|
||||
|
||||
- [ ] Original repro no longer reproduces (re-run the Phase 1 loop)
|
||||
- [ ] Regression test passes (or absence of seam is documented)
|
||||
- [ ] All `[DEBUG-...]` instrumentation removed (`grep` the prefix)
|
||||
- [ ] Throwaway prototypes deleted (or moved to a clearly-marked debug location)
|
||||
- [ ] The hypothesis that turned out correct is stated in the commit / PR message — so the next debugger learns
|
||||
|
||||
**Then ask: what would have prevented this bug?** If the answer involves architectural change (no good test seam, tangled callers, hidden coupling) hand off to the `/improve-codebase-architecture` skill with the specifics. Make the recommendation **after** the fix is in, not before — you have more information now than when you started.
|
||||
41
.agents/skills/diagnose/scripts/hitl-loop.template.sh
Normal file
41
.agents/skills/diagnose/scripts/hitl-loop.template.sh
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
# Human-in-the-loop reproduction loop.
|
||||
# Copy this file, edit the steps below, and run it.
|
||||
# The agent runs the script; the user follows prompts in their terminal.
|
||||
#
|
||||
# Usage:
|
||||
# bash hitl-loop.template.sh
|
||||
#
|
||||
# Two helpers:
|
||||
# step "<instruction>" → show instruction, wait for Enter
|
||||
# capture VAR "<question>" → show question, read response into VAR
|
||||
#
|
||||
# At the end, captured values are printed as KEY=VALUE for the agent to parse.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
step() {
|
||||
printf '\n>>> %s\n' "$1"
|
||||
read -r -p " [Enter when done] " _
|
||||
}
|
||||
|
||||
capture() {
|
||||
local var="$1" question="$2" answer
|
||||
printf '\n>>> %s\n' "$question"
|
||||
read -r -p " > " answer
|
||||
printf -v "$var" '%s' "$answer"
|
||||
}
|
||||
|
||||
# --- edit below ---------------------------------------------------------
|
||||
|
||||
step "Open the app at http://localhost:3000 and sign in."
|
||||
|
||||
capture ERRORED "Click the 'Export' button. Did it throw an error? (y/n)"
|
||||
|
||||
capture ERROR_MSG "Paste the error message (or 'none'):"
|
||||
|
||||
# --- edit above ---------------------------------------------------------
|
||||
|
||||
printf '\n--- Captured ---\n'
|
||||
printf 'ERRORED=%s\n' "$ERRORED"
|
||||
printf 'ERROR_MSG=%s\n' "$ERROR_MSG"
|
||||
7
.agents/skills/grill-me/SKILL.md
Normal file
7
.agents/skills/grill-me/SKILL.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
name: grill-me
|
||||
description: A relentless interview to sharpen a plan or design.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Run a `/grilling` session.
|
||||
7
.agents/skills/grill-with-docs/SKILL.md
Normal file
7
.agents/skills/grill-with-docs/SKILL.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
name: grill-with-docs
|
||||
description: A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Run a `/grilling` session, using the `/domain-modeling` skill.
|
||||
16
.agents/skills/handoff/SKILL.md
Normal file
16
.agents/skills/handoff/SKILL.md
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: handoff
|
||||
description: Compact the current conversation into a handoff document for another agent to pick up.
|
||||
argument-hint: "What will the next session be used for?"
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS - not the current workspace.
|
||||
|
||||
Include a "suggested skills" section in the document, which suggests skills that the agent should invoke.
|
||||
|
||||
Do not duplicate content already captured in other artifacts (PRDs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead.
|
||||
|
||||
Redact any sensitive information, such as API keys, passwords, or personally identifiable information.
|
||||
|
||||
If the user passed arguments, treat them as a description of what the next session will focus on and tailor the doc accordingly.
|
||||
123
.agents/skills/improve-codebase-architecture/HTML-REPORT.md
Normal file
123
.agents/skills/improve-codebase-architecture/HTML-REPORT.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# HTML Report Format
|
||||
|
||||
The architectural review is rendered as a single self-contained HTML file in the OS temp directory. Tailwind and Mermaid both come from CDNs. Mermaid handles graph-shaped diagrams reliably; hand-built divs and inline SVG handle the more editorial visuals (mass diagrams, cross-sections). Mix the two — don't lean on Mermaid for everything, it'll start to look generic.
|
||||
|
||||
## Scaffold
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Architecture review — {{repo name}}</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script type="module">
|
||||
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
|
||||
mermaid.initialize({ startOnLoad: true, theme: "neutral", securityLevel: "loose" });
|
||||
</script>
|
||||
<style>
|
||||
/* small custom layer for things Tailwind doesn't cover cleanly:
|
||||
dashed seam lines, hand-drawn-feeling arrow heads, etc. */
|
||||
.seam { stroke-dasharray: 4 4; }
|
||||
.leak { stroke: #dc2626; }
|
||||
.deep { background: linear-gradient(135deg, #0f172a, #1e293b); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-stone-50 text-slate-900 font-sans">
|
||||
<main class="max-w-5xl mx-auto px-6 py-12 space-y-12">
|
||||
<header>...</header>
|
||||
<section id="candidates" class="space-y-10">...</section>
|
||||
<section id="top-recommendation">...</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Header
|
||||
|
||||
Repo name, date, and a compact legend: solid box = module, dashed line = seam, red arrow = leakage, thick dark box = deep module. No introduction paragraph — straight into the candidates.
|
||||
|
||||
## Candidate card
|
||||
|
||||
The diagrams carry the weight. Prose is sparse, plain, and uses the glossary terms (from the `/codebase-design` skill) without ceremony.
|
||||
|
||||
Each candidate is one `<article>`:
|
||||
|
||||
- **Title** — short, names the deepening (e.g. "Collapse the Order intake pipeline").
|
||||
- **Badge row** — recommendation strength (`Strong` = emerald, `Worth exploring` = amber, `Speculative` = slate), plus a tag for the dependency category (`in-process`, `local-substitutable`, `ports & adapters`, `mock`).
|
||||
- **Files** — monospaced list, `font-mono text-sm`.
|
||||
- **Before / After diagram** — the centrepiece. Two columns, side by side. See patterns below.
|
||||
- **Problem** — one sentence. What hurts.
|
||||
- **Solution** — one sentence. What changes.
|
||||
- **Wins** — bullets, ≤6 words each. e.g. "Tests hit one interface", "Pricing logic stops leaking", "Delete 4 shallow wrappers".
|
||||
- **ADR callout** (if applicable) — one line in an amber-tinted box.
|
||||
|
||||
No paragraphs of explanation. If the diagram needs a paragraph to be understood, redraw the diagram.
|
||||
|
||||
## Diagram patterns
|
||||
|
||||
Pick the pattern that fits the candidate. Mix them. Don't make every diagram look the same — variety is part of the point.
|
||||
|
||||
### Mermaid graph (the workhorse for dependencies / call flow)
|
||||
|
||||
Use a Mermaid `flowchart` or `graph` when the point is "X calls Y calls Z, and look at the mess." Wrap it in a Tailwind-styled card so it doesn't feel parachuted in. Style with classDef to colour leakage edges red and the deep module dark. Sequence diagrams work well for "before: 6 round-trips; after: 1."
|
||||
|
||||
```html
|
||||
<div class="rounded-lg border border-slate-200 bg-white p-4">
|
||||
<pre class="mermaid">
|
||||
flowchart LR
|
||||
A[OrderHandler] --> B[OrderValidator]
|
||||
B --> C[OrderRepo]
|
||||
C -.leak.-> D[PricingClient]
|
||||
classDef leak stroke:#dc2626,stroke-width:2px;
|
||||
class C,D leak
|
||||
</pre>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Hand-built boxes-and-arrows (when Mermaid's layout fights you)
|
||||
|
||||
Modules as `<div>`s with borders and labels. Arrows as inline SVG `<line>` or `<path>` elements positioned absolutely over a relative container. Reach for this when you want the "after" diagram to feel like one thick-bordered deep module with greyed-out internals — Mermaid won't render that with the right weight.
|
||||
|
||||
### Cross-section (good for layered shallowness)
|
||||
|
||||
Stack horizontal bands (`h-12 border-l-4`) to show layers a call passes through. Before: 6 thin layers each doing nothing. After: 1 thick band labelled with the consolidated responsibility.
|
||||
|
||||
### Mass diagram (good for "interface as wide as implementation")
|
||||
|
||||
Two rectangles per module — one for interface surface area, one for implementation. Before: interface rectangle is nearly as tall as the implementation rectangle (shallow). After: interface rectangle is short, implementation rectangle is tall (deep).
|
||||
|
||||
### Call-graph collapse
|
||||
|
||||
Before: a tree of function calls rendered as nested boxes. After: the same tree collapsed into one box, with the now-internal calls shown faded inside it.
|
||||
|
||||
## Style guidance
|
||||
|
||||
- Lean editorial, not corporate-dashboard. Generous whitespace. Serif optional for headings (`font-serif` works well with stone/slate).
|
||||
- Colour sparingly: one accent (emerald or indigo) plus red for leakage and amber for warnings.
|
||||
- Keep diagrams ~320px tall so before/after sits comfortably side by side without scrolling.
|
||||
- Use `text-xs uppercase tracking-wider` for module labels inside diagrams — they should read as schematic, not as UI.
|
||||
- The only scripts are the Tailwind CDN and the Mermaid ESM import. The report is otherwise static — no app code, no interactivity beyond Mermaid's own rendering.
|
||||
|
||||
## Top recommendation section
|
||||
|
||||
One larger card. Candidate name, one sentence on why, anchor link to its card. That's it.
|
||||
|
||||
## Tone
|
||||
|
||||
Plain English, concise — but the architectural nouns and verbs come straight from the `/codebase-design` skill. Concision is not an excuse to drift.
|
||||
|
||||
**Use exactly:** module, interface, implementation, depth, deep, shallow, seam, adapter, leverage, locality.
|
||||
|
||||
**Never substitute:** component, service, unit (for module) · API, signature (for interface) · boundary (for seam) · layer, wrapper (for module, when you mean module).
|
||||
|
||||
**Phrasings that fit the style:**
|
||||
|
||||
- "Order intake module is shallow — interface nearly matches the implementation."
|
||||
- "Pricing leaks across the seam."
|
||||
- "Deepen: one interface, one place to test."
|
||||
- "Two adapters justify the seam: HTTP in prod, in-memory in tests."
|
||||
|
||||
**Wins bullets** name the gain in glossary terms: *"locality: bugs concentrate in one module"*, *"leverage: one interface, N call sites"*, *"interface shrinks; implementation absorbs the wrappers"*. Don't write *"easier to maintain"* or *"cleaner code"* — those terms aren't in the glossary and don't earn their place.
|
||||
|
||||
No hedging, no throat-clearing, no "it's worth noting that…". If a sentence could be a bullet, make it a bullet. If a bullet could be cut, cut it. If a term isn't in the `/codebase-design` glossary, reach for one that is before inventing a new one.
|
||||
66
.agents/skills/improve-codebase-architecture/SKILL.md
Normal file
66
.agents/skills/improve-codebase-architecture/SKILL.md
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: improve-codebase-architecture
|
||||
description: Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Improve Codebase Architecture
|
||||
|
||||
Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability.
|
||||
|
||||
This command is _informed_ by the project's domain model and built on a shared design vocabulary:
|
||||
|
||||
- Run the `/codebase-design` skill for the architecture vocabulary (**module**, **interface**, **depth**, **seam**, **adapter**, **leverage**, **locality**) and its principles (the deletion test, "the interface is the test surface", "one adapter = hypothetical seam, two = real"). Use these terms exactly in every suggestion — don't drift into "component," "service," "API," or "boundary."
|
||||
- The domain language in `CONTEXT.md` gives names to good seams; ADRs in `docs/adr/` record decisions this command should not re-litigate.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Explore
|
||||
|
||||
Read the project's domain glossary (`CONTEXT.md`) and any ADRs in the area you're touching first.
|
||||
|
||||
Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction:
|
||||
|
||||
- Where does understanding one concept require bouncing between many small modules?
|
||||
- Where are modules **shallow** — interface nearly as complex as the implementation?
|
||||
- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)?
|
||||
- Where do tightly-coupled modules leak across their seams?
|
||||
- Which parts of the codebase are untested, or hard to test through their current interface?
|
||||
|
||||
Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want.
|
||||
|
||||
### 2. Present candidates as an HTML report
|
||||
|
||||
Write a self-contained HTML file to the OS temp directory so nothing lands in the repo. Resolve the temp dir from `$TMPDIR`, falling back to `/tmp` (or `%TEMP%` on Windows), and write to `<tmpdir>/architecture-review-<timestamp>.html` so each run gets a fresh file. Open it for the user — `xdg-open <path>` on Linux, `open <path>` on macOS, `start <path>` on Windows — and tell them the absolute path.
|
||||
|
||||
The report uses **Tailwind via CDN** for layout and styling, and **Mermaid via CDN** for diagrams where a graph/flow/sequence reliably communicates the structure. Mix Mermaid with hand-crafted CSS/SVG visuals — use Mermaid when relationships are graph-shaped (call graphs, dependencies, sequences), and hand-built divs/SVG when you want something more editorial (mass diagrams, cross-sections, collapse animations). Each candidate gets a **before/after visualisation**. Be visual.
|
||||
|
||||
For each candidate, render a card with:
|
||||
|
||||
- **Files** — which files/modules are involved
|
||||
- **Problem** — why the current architecture is causing friction
|
||||
- **Solution** — plain English description of what would change
|
||||
- **Benefits** — explained in terms of locality and leverage, and how tests would improve
|
||||
- **Before / After diagram** — side-by-side, custom-drawn, illustrating the shallowness and the deepening
|
||||
- **Recommendation strength** — one of `Strong`, `Worth exploring`, `Speculative`, rendered as a badge
|
||||
|
||||
End the report with a **Top recommendation** section: which candidate you'd tackle first and why.
|
||||
|
||||
**Use CONTEXT.md vocabulary for the domain, and the `/codebase-design` vocabulary for the architecture.** If `CONTEXT.md` defines "Order," talk about "the Order intake module" — not "the FooBarHandler," and not "the Order service."
|
||||
|
||||
**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly in the card (e.g. a warning callout: _"contradicts ADR-0007 — but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids.
|
||||
|
||||
See [HTML-REPORT.md](HTML-REPORT.md) for the full HTML scaffold, diagram patterns, and styling guidance.
|
||||
|
||||
Do NOT propose interfaces yet. After the file is written, ask the user: "Which of these would you like to explore?"
|
||||
|
||||
### 3. Grilling loop
|
||||
|
||||
Once the user picks a candidate, run the `/grilling` skill to walk the design tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive.
|
||||
|
||||
Side effects happen inline as decisions crystallize — run the `/domain-modeling` skill to keep the domain model current as you go:
|
||||
|
||||
- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md`. Create the file lazily if it doesn't exist.
|
||||
- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there.
|
||||
- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones.
|
||||
- **Want to explore alternative interfaces for the deepened module?** Run the `/codebase-design` skill and use its design-it-twice parallel sub-agent pattern.
|
||||
79
.agents/skills/prototype/LOGIC.md
Normal file
79
.agents/skills/prototype/LOGIC.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Logic Prototype
|
||||
|
||||
A tiny interactive terminal app that lets the user drive a state model by hand. Use this when the question is about **business logic, state transitions, or data shape** — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases.
|
||||
|
||||
## When this is the right shape
|
||||
|
||||
- "I'm not sure if this state machine handles the edge case where X then Y."
|
||||
- "Does this data model actually let me represent the case where..."
|
||||
- "I want to feel out what the API should look like before writing it."
|
||||
- Anything where the user wants to **press buttons and watch state change**.
|
||||
|
||||
If the question is "what should this look like" — wrong branch. Use [UI.md](UI.md).
|
||||
|
||||
## Process
|
||||
|
||||
### 1. State the question
|
||||
|
||||
Before writing code, write down what state model and what question you're prototyping. One paragraph, in the prototype's README or a comment at the top of the file. A logic prototype that answers the wrong question is pure waste — make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK.
|
||||
|
||||
### 2. Pick the language
|
||||
|
||||
Use whatever the host project uses. If the project has no obvious runtime (e.g. a docs repo), ask.
|
||||
|
||||
Match the project's existing conventions for tooling — don't add a new package manager or runtime just for the prototype.
|
||||
|
||||
### 3. Isolate the logic in a portable module
|
||||
|
||||
Put the actual logic — the bit that's answering the question — behind a small, pure interface that could be lifted out and dropped into the real codebase later. The TUI around it is throwaway; the logic module shouldn't be.
|
||||
|
||||
The right shape depends on the question:
|
||||
|
||||
- **A pure reducer** — `(state, action) => state`. Good when actions are discrete events and state is a single value.
|
||||
- **A state machine** — explicit states and transitions. Good when "which actions are even legal right now" is part of the question.
|
||||
- **A small set of pure functions** over a plain data type. Good when there's no implicit current state — just transformations.
|
||||
- **A class or module with a clear method surface** when the logic genuinely owns ongoing internal state.
|
||||
|
||||
Pick whichever shape best fits the question being asked, *not* whichever is easiest to wire to a TUI. Keep it pure: no I/O, no terminal code, no `console.log` for control flow. The TUI imports it and calls into it; nothing flows the other direction.
|
||||
|
||||
This is what makes the prototype useful past its own lifetime. When the question's been answered, the validated reducer / machine / function set can be lifted into the real module — the TUI shell gets deleted.
|
||||
|
||||
### 4. Build the smallest TUI that exposes the state
|
||||
|
||||
Build it as a **lightweight TUI** — on every tick, clear the screen (`console.clear()` / `print("\033[2J\033[H")` / equivalent) and re-render the whole frame. The user should always see one stable view, not an ever-growing scrollback.
|
||||
|
||||
Each frame has two parts, in this order:
|
||||
|
||||
1. **Current state**, pretty-printed and diff-friendly (one field per line, or formatted JSON). Use **bold** for field names or section headers and **dim** for less important context (timestamps, IDs, derived values). Native ANSI escape codes are fine — `\x1b[1m` bold, `\x1b[2m` dim, `\x1b[0m` reset. No need to pull in a styling library unless one is already in the project.
|
||||
2. **Keyboard shortcuts**, listed at the bottom: `[a] add user [d] delete user [t] tick clock [q] quit`. Bold the key, dim the description, or vice-versa — whatever reads cleanly.
|
||||
|
||||
Behaviour:
|
||||
|
||||
1. **Initialise state** — a single in-memory object/struct. Render the first frame on start.
|
||||
2. **Read one keystroke (or one line)** at a time, dispatch to a handler that mutates state.
|
||||
3. **Re-render** the full frame after every action — don't append, replace.
|
||||
4. **Loop until quit.**
|
||||
|
||||
The whole frame should fit on one screen.
|
||||
|
||||
### 5. Make it runnable in one command
|
||||
|
||||
Add a script to the project's existing task runner (`package.json` scripts, `Makefile`, `justfile`, `pyproject.toml`). The user should run `pnpm run <prototype-name>` or equivalent — never need to remember a path.
|
||||
|
||||
If the host project has no task runner, just put the command at the top of the prototype's README.
|
||||
|
||||
### 6. Hand it over
|
||||
|
||||
Give the user the run command. They'll drive it themselves; the interesting moments are when they say "wait, that shouldn't be possible" or "huh, I assumed X would be different" — those are the bugs in the _idea_, which is the whole point. If they want new actions added, add them. Prototypes evolve.
|
||||
|
||||
### 7. Capture the answer
|
||||
|
||||
When the prototype has done its job, the answer to the question is the only thing worth keeping. If the user is around, ask what it taught them. If not, leave a `NOTES.md` next to the prototype so the answer can be filled in (or filled in by you, if you've watched the session) before the prototype gets deleted.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- **Don't add tests.** A prototype that needs tests is no longer a prototype.
|
||||
- **Don't wire it to the real database.** Use an in-memory store unless the question is specifically about persistence.
|
||||
- **Don't generalise.** No "what if we wanted to support X later." The prototype answers one question.
|
||||
- **Don't blur the logic and the TUI together.** If the reducer / state machine references `console.log`, prompts, or terminal escape codes, it's no longer portable. Keep the TUI as a thin shell over a pure module.
|
||||
- **Don't ship the TUI shell into production.** The shell is optimised for being driven by hand from a terminal. The logic module behind it is the bit worth keeping.
|
||||
30
.agents/skills/prototype/SKILL.md
Normal file
30
.agents/skills/prototype/SKILL.md
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
name: prototype
|
||||
description: Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.
|
||||
---
|
||||
|
||||
# Prototype
|
||||
|
||||
A prototype is **throwaway code that answers a question**. The question decides the shape.
|
||||
|
||||
## Pick a branch
|
||||
|
||||
Identify which question is being answered — from the user's prompt, the surrounding code, or by asking if the user is around:
|
||||
|
||||
- **"Does this logic / state model feel right?"** → [LOGIC.md](LOGIC.md). Build a tiny interactive terminal app that pushes the state machine through cases that are hard to reason about on paper.
|
||||
- **"What should this look like?"** → [UI.md](UI.md). Generate several radically different UI variations on a single route, switchable via a URL search param and a floating bottom bar.
|
||||
|
||||
The two branches produce very different artifacts — getting this wrong wastes the whole prototype. If the question is genuinely ambiguous and the user isn't reachable, default to whichever branch better matches the surrounding code (a backend module → logic; a page or component → UI) and state the assumption at the top of the prototype.
|
||||
|
||||
## Rules that apply to both
|
||||
|
||||
1. **Throwaway from day one, and clearly marked as such.** Locate the prototype code close to where it will actually be used (next to the module or page it's prototyping for) so context is obvious — but name it so a casual reader can see it's a prototype, not production. For throwaway UI routes, obey whatever routing convention the project already uses; don't invent a new top-level structure.
|
||||
2. **One command to run.** Whatever the project's existing task runner supports — `pnpm <name>`, `python <path>`, `bun <path>`, etc. The user must be able to start it without thinking.
|
||||
3. **No persistence by default.** State lives in memory. Persistence is the thing the prototype is _checking_, not something it should depend on. If the question explicitly involves a database, hit a scratch DB or a local file with a clear "PROTOTYPE — wipe me" name.
|
||||
4. **Skip the polish.** No tests, no error handling beyond what makes the prototype _runnable_, no abstractions. The point is to learn something fast and then delete it.
|
||||
5. **Surface the state.** After every action (logic) or on every variant switch (UI), print or render the full relevant state so the user can see what changed.
|
||||
6. **Delete or absorb when done.** When the prototype has answered its question, either delete it or fold the validated decision into the real code — don't leave it rotting in the repo.
|
||||
|
||||
## When done
|
||||
|
||||
The _answer_ is the only thing worth keeping from a prototype. Capture it somewhere durable (commit message, ADR, issue, or a `NOTES.md` next to the prototype) along with the question it was answering. If the user is around, that capture is a quick conversation; if not, leave the placeholder so they (or you, on the next pass) can fill in the verdict before deleting the prototype.
|
||||
112
.agents/skills/prototype/UI.md
Normal file
112
.agents/skills/prototype/UI.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# UI Prototype
|
||||
|
||||
Generate **several radically different UI variations** on a single route, switchable from a floating bottom bar. The user flips between variants in the browser, picks one (or steals bits from each), then throws the rest away.
|
||||
|
||||
If the question is about logic/state rather than what something looks like — wrong branch. Use [LOGIC.md](LOGIC.md).
|
||||
|
||||
## When this is the right shape
|
||||
|
||||
- "What should this page look like?"
|
||||
- "I want to see a few options for this dashboard before committing."
|
||||
- "Try a different layout for the settings screen."
|
||||
- Any time the user would otherwise spend a day picking between three vague mockups in their head.
|
||||
|
||||
## Two sub-shapes — strongly prefer sub-shape A
|
||||
|
||||
A UI prototype is much easier to judge when it's **butting up against the rest of the app** — real header, real sidebar, real data, real density. A throwaway route on its own is a vacuum: every variant looks fine in isolation. Default to sub-shape A whenever there's a plausible existing page to host the variants. Only reach for sub-shape B if the prototype genuinely has no nearby home.
|
||||
|
||||
### Sub-shape A — adjustment to an existing page (preferred)
|
||||
|
||||
The route already exists. Variants are rendered **on the same route**, gated by a `?variant=` URL search param. The existing data fetching, params, and auth all stay — only the rendering swaps. This is the default; pick it unless there's a specific reason not to.
|
||||
|
||||
If the prototype is for something that doesn't yet have a page but *would naturally live inside one* (a new section of the dashboard, a new card on the settings screen, a new step in an existing flow) — that's still sub-shape A. Mount the variants inside the host page.
|
||||
|
||||
### Sub-shape B — a new page (last resort)
|
||||
|
||||
Only use this when the thing being prototyped genuinely has no existing page to live inside — e.g. an entirely new top-level surface, or a flow that can't be embedded anywhere sensible.
|
||||
|
||||
Create a **throwaway route** following whatever routing convention the project already uses — don't invent a new top-level structure. Name it so it's obviously a prototype (e.g. include the word `prototype` in the path or filename). Same `?variant=` pattern.
|
||||
|
||||
Before committing to sub-shape B, sanity-check: is there really no existing page this could be embedded in? An empty route hides design problems that a populated one would expose.
|
||||
|
||||
In both sub-shapes the floating bottom bar is identical.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. State the question and pick N
|
||||
|
||||
Default to **3 variants**. More than 5 stops being radically different and starts being noise — cap there.
|
||||
|
||||
Write down the plan in one line, in the prototype's location or a top-of-file comment:
|
||||
|
||||
> "Three variants of the settings page, switchable via `?variant=`, on the existing `/settings` route."
|
||||
|
||||
This works whether the user is here to push back or not.
|
||||
|
||||
### 2. Generate radically different variants
|
||||
|
||||
Draft each variant. Hold each one to:
|
||||
|
||||
- The page's purpose and the data it has access to.
|
||||
- The project's component library / styling system (TailwindCSS, shadcn, MUI, plain CSS, whatever).
|
||||
- A clear exported component name, e.g. `VariantA`, `VariantB`, `VariantC`.
|
||||
|
||||
Variants must be **structurally different** — different layout, different information hierarchy, different primary affordance, not just different colours. Three slightly-tweaked card grids isn't a UI prototype, it's wallpaper. If two drafts come out too similar, redo one with explicit "do not use a card grid" guidance.
|
||||
|
||||
### 3. Wire them together
|
||||
|
||||
Create a single switcher component on the route:
|
||||
|
||||
```tsx
|
||||
// pseudo-code — adapt to the project's framework
|
||||
const variant = searchParams.get('variant') ?? 'A';
|
||||
return (
|
||||
<>
|
||||
{variant === 'A' && <VariantA {...data} />}
|
||||
{variant === 'B' && <VariantB {...data} />}
|
||||
{variant === 'C' && <VariantC {...data} />}
|
||||
<PrototypeSwitcher variants={['A','B','C']} current={variant} />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
For sub-shape A (existing page): keep all the existing data fetching above the switcher; only the rendered subtree changes per variant.
|
||||
|
||||
For sub-shape B (new page): the throwaway route under `/prototype/<name>` mounts the same switcher.
|
||||
|
||||
### 4. Build the floating switcher
|
||||
|
||||
A small fixed-position bar at the bottom-centre of the screen with three pieces:
|
||||
|
||||
- **Left arrow** — cycles to the previous variant (wraps around).
|
||||
- **Variant label** — shows the current variant key and, if the variant exports a name, that name too. e.g. `B — Sidebar layout`.
|
||||
- **Right arrow** — cycles forward (wraps around).
|
||||
|
||||
Behaviour:
|
||||
|
||||
- Clicking an arrow updates the URL search param (use the framework's router — `router.replace` on Next, `navigate` on React Router, etc) so the variant is shareable and reload-stable.
|
||||
- Keyboard: `←` and `→` arrow keys also cycle. Don't intercept arrow keys when an `<input>`, `<textarea>`, or `[contenteditable]` is focused.
|
||||
- Visually distinct from the page (e.g. high-contrast pill, subtle shadow) so it's obviously not part of the design being evaluated.
|
||||
- Hidden in production builds — gate on `process.env.NODE_ENV !== 'production'` or an equivalent check, so a stray prototype merge can't ship the bar to users.
|
||||
|
||||
Put the switcher in a single shared component so both sub-shapes can reuse it. Locate it wherever shared UI lives in the project.
|
||||
|
||||
### 5. Hand it over
|
||||
|
||||
Surface the URL (and the `?variant=` keys). The user will flip through whenever they get to it. The interesting feedback is usually **"I want the header from B with the sidebar from C"** — that's the actual design they want.
|
||||
|
||||
### 6. Capture the answer and clean up
|
||||
|
||||
Once a variant has won, write down which one and why (commit message, ADR, issue, or a `NOTES.md` next to the prototype if running AFK and the user hasn't responded yet). Then:
|
||||
|
||||
- **Sub-shape A** — delete the losing variants and the switcher; fold the winner into the existing page.
|
||||
- **Sub-shape B** — promote the winning variant to a real route, delete the throwaway route and the switcher.
|
||||
|
||||
Don't leave variant components or the switcher lying around. They rot fast and confuse the next reader.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- **Variants that differ only in colour or copy.** That's a tweak, not a prototype. Real variants disagree about structure.
|
||||
- **Sharing too much code between variants.** A shared `<Header>` is fine; a shared `<Layout>` defeats the point. Each variant should be free to throw out the layout.
|
||||
- **Wiring variants to real mutations.** Read-only prototypes are fine. If a variant needs to mutate, point it at a stub — the question is "what should this look like", not "does the backend work".
|
||||
- **Promoting the prototype directly to production.** The variant code was written under prototype constraints (no tests, minimal error handling). Rewrite it properly when you fold it in.
|
||||
127
.agents/skills/setup-matt-pocock-skills/SKILL.md
Normal file
127
.agents/skills/setup-matt-pocock-skills/SKILL.md
Normal file
@@ -0,0 +1,127 @@
|
||||
---
|
||||
name: setup-matt-pocock-skills
|
||||
description: Configure this repo for the engineering skills — set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Setup Matt Pocock's Skills
|
||||
|
||||
Scaffold the per-repo configuration that the engineering skills assume:
|
||||
|
||||
- **Issue tracker** — where issues live (GitHub by default; local markdown is also supported out of the box)
|
||||
- **Triage labels** — the strings used for the five canonical triage roles
|
||||
- **Domain docs** — where `CONTEXT.md` and ADRs live, and the consumer rules for reading them
|
||||
|
||||
This is a prompt-driven skill, not a deterministic script. Explore, present what you found, confirm with the user, then write.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Explore
|
||||
|
||||
Look at the current repo to understand its starting state. Read whatever exists; don't assume:
|
||||
|
||||
- `git remote -v` and `.git/config` — is this a GitHub repo? Which one?
|
||||
- `AGENTS.md` and `CLAUDE.md` at the repo root — does either exist? Is there already an `## Agent skills` section in either?
|
||||
- `CONTEXT.md` and `CONTEXT-MAP.md` at the repo root
|
||||
- `docs/adr/` and any `src/*/docs/adr/` directories
|
||||
- `docs/agents/` — does this skill's prior output already exist?
|
||||
- `.scratch/` — sign that a local-markdown issue tracker convention is already in use
|
||||
|
||||
### 2. Present findings and ask
|
||||
|
||||
Summarise what's present and what's missing. Then walk the user through the three decisions **one at a time** — present a section, get the user's answer, then move to the next. Don't dump all three at once.
|
||||
|
||||
Assume the user does not know what these terms mean. Each section starts with a short explainer (what it is, why these skills need it, what changes if they pick differently). Then show the choices and the default.
|
||||
|
||||
**Section A — Issue tracker.**
|
||||
|
||||
> Explainer: The "issue tracker" is where issues live for this repo. Skills like `to-issues`, `triage`, `to-prd`, and `qa` read from and write to it — they need to know whether to call `gh issue create`, write a markdown file under `.scratch/`, or follow some other workflow you describe. Pick the place you actually track work for this repo.
|
||||
|
||||
Default posture: these skills were designed for GitHub. If a `git remote` points at GitHub, propose that. If a `git remote` points at GitLab (`gitlab.com` or a self-hosted host), propose GitLab. Otherwise (or if the user prefers), offer:
|
||||
|
||||
- **GitHub** — issues live in the repo's GitHub Issues (uses the `gh` CLI)
|
||||
- **GitLab** — issues live in the repo's GitLab Issues (uses the [`glab`](https://gitlab.com/gitlab-org/cli) CLI)
|
||||
- **Local markdown** — issues live as files under `.scratch/<feature>/` in this repo (good for solo projects or repos without a remote)
|
||||
- **Other** (Jira, Linear, etc.) — ask the user to describe the workflow in one paragraph; the skill will record it as freeform prose
|
||||
|
||||
If — and only if — the user picked **GitHub** or **GitLab**, ask one follow-up:
|
||||
|
||||
> Explainer: Open-source repos often receive feature requests as pull requests, not just issues — a PR is an issue with attached code. If you turn this on, `/triage` pulls *external* PRs into the same queue and runs them through the same labels and states as issues (collaborators' in-flight PRs are left alone). Leave it off if PRs aren't a request surface for you.
|
||||
|
||||
- **PRs as a request surface** — yes / no (default: no). Record the answer in `docs/agents/issue-tracker.md`. For local-markdown and other trackers, skip this question — there are no PRs.
|
||||
|
||||
**Section B — Triage label vocabulary.**
|
||||
|
||||
> Explainer: When the `triage` skill processes an incoming issue, it moves it through a state machine — needs evaluation, waiting on reporter, ready for an AFK agent to pick up, ready for a human, or won't fix. To do that, it needs to apply labels (or the equivalent in your issue tracker) that match strings *you've actually configured*. If your repo already uses different label names (e.g. `bug:triage` instead of `needs-triage`), map them here so the skill applies the right ones instead of creating duplicates.
|
||||
|
||||
The five canonical roles:
|
||||
|
||||
- `needs-triage` — maintainer needs to evaluate
|
||||
- `needs-info` — waiting on reporter
|
||||
- `ready-for-agent` — fully specified, AFK-ready (an agent can pick it up with no human context)
|
||||
- `ready-for-human` — needs human implementation
|
||||
- `wontfix` — will not be actioned
|
||||
|
||||
Default: each role's string equals its name. Ask the user if they want to override any. If their issue tracker has no existing labels, the defaults are fine.
|
||||
|
||||
**Section C — Domain docs.**
|
||||
|
||||
> Explainer: Some skills (`improve-codebase-architecture`, `diagnosing-bugs`, `tdd`) read a `CONTEXT.md` file to learn the project's domain language, and `docs/adr/` for past architectural decisions. They need to know whether the repo has one global context or multiple (e.g. a monorepo with separate frontend/backend contexts) so they look in the right place.
|
||||
|
||||
Confirm the layout:
|
||||
|
||||
- **Single-context** — one `CONTEXT.md` + `docs/adr/` at the repo root. Most repos are this.
|
||||
- **Multi-context** — `CONTEXT-MAP.md` at the root pointing to per-context `CONTEXT.md` files (typically a monorepo).
|
||||
|
||||
### 3. Confirm and edit
|
||||
|
||||
Show the user a draft of:
|
||||
|
||||
- The `## Agent skills` block to add to whichever of `CLAUDE.md` / `AGENTS.md` is being edited (see step 4 for selection rules)
|
||||
- The contents of `docs/agents/issue-tracker.md`, `docs/agents/triage-labels.md`, `docs/agents/domain.md`
|
||||
|
||||
Let them edit before writing.
|
||||
|
||||
### 4. Write
|
||||
|
||||
**Pick the file to edit:**
|
||||
|
||||
- If `CLAUDE.md` exists, edit it.
|
||||
- Else if `AGENTS.md` exists, edit it.
|
||||
- If neither exists, ask the user which one to create — don't pick for them.
|
||||
|
||||
Never create `AGENTS.md` when `CLAUDE.md` already exists (or vice versa) — always edit the one that's already there.
|
||||
|
||||
If an `## Agent skills` block already exists in the chosen file, update its contents in-place rather than appending a duplicate. Don't overwrite user edits to the surrounding sections.
|
||||
|
||||
The block:
|
||||
|
||||
```markdown
|
||||
## Agent skills
|
||||
|
||||
### Issue tracker
|
||||
|
||||
[one-line summary of where issues are tracked, plus whether external PRs are a triage surface]. See `docs/agents/issue-tracker.md`.
|
||||
|
||||
### Triage labels
|
||||
|
||||
[one-line summary of the label vocabulary]. See `docs/agents/triage-labels.md`.
|
||||
|
||||
### Domain docs
|
||||
|
||||
[one-line summary of layout — "single-context" or "multi-context"]. See `docs/agents/domain.md`.
|
||||
```
|
||||
|
||||
Then write the three docs files using the seed templates in this skill folder as a starting point:
|
||||
|
||||
- [issue-tracker-github.md](./issue-tracker-github.md) — GitHub issue tracker
|
||||
- [issue-tracker-gitlab.md](./issue-tracker-gitlab.md) — GitLab issue tracker
|
||||
- [issue-tracker-local.md](./issue-tracker-local.md) — local-markdown issue tracker
|
||||
- [triage-labels.md](./triage-labels.md) — label mapping
|
||||
- [domain.md](./domain.md) — domain doc consumer rules + layout
|
||||
|
||||
For "other" issue trackers, write `docs/agents/issue-tracker.md` from scratch using the user's description.
|
||||
|
||||
### 5. Done
|
||||
|
||||
Tell the user the setup is complete and which engineering skills will now read from these files. Mention they can edit `docs/agents/*.md` directly later — re-running this skill is only necessary if they want to switch issue trackers or restart from scratch.
|
||||
51
.agents/skills/setup-matt-pocock-skills/domain.md
Normal file
51
.agents/skills/setup-matt-pocock-skills/domain.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Domain Docs
|
||||
|
||||
How the engineering skills should consume this repo's domain documentation when exploring the codebase.
|
||||
|
||||
## Before exploring, read these
|
||||
|
||||
- **`CONTEXT.md`** at the repo root, or
|
||||
- **`CONTEXT-MAP.md`** at the repo root if it exists — it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
|
||||
- **`docs/adr/`** — read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.
|
||||
|
||||
If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill (reached via `/grill-with-docs` and `/improve-codebase-architecture`) creates them lazily when terms or decisions actually get resolved.
|
||||
|
||||
## File structure
|
||||
|
||||
Single-context repo (most repos):
|
||||
|
||||
```
|
||||
/
|
||||
├── CONTEXT.md
|
||||
├── docs/adr/
|
||||
│ ├── 0001-event-sourced-orders.md
|
||||
│ └── 0002-postgres-for-write-model.md
|
||||
└── src/
|
||||
```
|
||||
|
||||
Multi-context repo (presence of `CONTEXT-MAP.md` at the root):
|
||||
|
||||
```
|
||||
/
|
||||
├── CONTEXT-MAP.md
|
||||
├── docs/adr/ ← system-wide decisions
|
||||
└── src/
|
||||
├── ordering/
|
||||
│ ├── CONTEXT.md
|
||||
│ └── docs/adr/ ← context-specific decisions
|
||||
└── billing/
|
||||
├── CONTEXT.md
|
||||
└── docs/adr/
|
||||
```
|
||||
|
||||
## Use the glossary's vocabulary
|
||||
|
||||
When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
|
||||
|
||||
If the concept you need isn't in the glossary yet, that's a signal — either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).
|
||||
|
||||
## Flag ADR conflicts
|
||||
|
||||
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
|
||||
|
||||
> _Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_
|
||||
@@ -0,0 +1,34 @@
|
||||
# Issue tracker: GitHub
|
||||
|
||||
Issues and PRDs for this repo live as GitHub issues. Use the `gh` CLI for all operations.
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Create an issue**: `gh issue create --title "..." --body "..."`. Use a heredoc for multi-line bodies.
|
||||
- **Read an issue**: `gh issue view <number> --comments`, filtering comments by `jq` and also fetching labels.
|
||||
- **List issues**: `gh issue list --state open --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'` with appropriate `--label` and `--state` filters.
|
||||
- **Comment on an issue**: `gh issue comment <number> --body "..."`
|
||||
- **Apply / remove labels**: `gh issue edit <number> --add-label "..."` / `--remove-label "..."`
|
||||
- **Close**: `gh issue close <number> --comment "..."`
|
||||
|
||||
Infer the repo from `git remote -v` — `gh` does this automatically when run inside a clone.
|
||||
|
||||
## Pull requests as a triage surface
|
||||
|
||||
**PRs as a request surface: no.** _(Set to `yes` if this repo treats external PRs as feature requests; `/triage` reads this flag.)_
|
||||
|
||||
When set to `yes`, PRs run through the same labels and states as issues, using the `gh pr` equivalents:
|
||||
|
||||
- **Read a PR**: `gh pr view <number> --comments` and `gh pr diff <number>` for the diff.
|
||||
- **List external PRs for triage**: `gh pr list --state open --json number,title,body,labels,author,authorAssociation,comments` then keep only `authorAssociation` of `CONTRIBUTOR`, `FIRST_TIME_CONTRIBUTOR`, or `NONE` (drop `OWNER`/`MEMBER`/`COLLABORATOR`).
|
||||
- **Comment / label / close**: `gh pr comment`, `gh pr edit --add-label`/`--remove-label`, `gh pr close`.
|
||||
|
||||
GitHub shares one number space across issues and PRs, so a bare `#42` may be either — resolve with `gh pr view 42` and fall back to `gh issue view 42`.
|
||||
|
||||
## When a skill says "publish to the issue tracker"
|
||||
|
||||
Create a GitHub issue.
|
||||
|
||||
## When a skill says "fetch the relevant ticket"
|
||||
|
||||
Run `gh issue view <number> --comments`.
|
||||
@@ -0,0 +1,35 @@
|
||||
# Issue tracker: GitLab
|
||||
|
||||
Issues and PRDs for this repo live as GitLab issues. Use the [`glab`](https://gitlab.com/gitlab-org/cli) CLI for all operations.
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Create an issue**: `glab issue create --title "..." --description "..."`. Use a heredoc for multi-line descriptions. Pass `--description -` to open an editor.
|
||||
- **Read an issue**: `glab issue view <number> --comments`. Use `-F json` for machine-readable output.
|
||||
- **List issues**: `glab issue list -F json` with appropriate `--label` filters.
|
||||
- **Comment on an issue**: `glab issue note <number> --message "..."`. GitLab calls comments "notes".
|
||||
- **Apply / remove labels**: `glab issue update <number> --label "..."` / `--unlabel "..."`. Multiple labels can be comma-separated or by repeating the flag.
|
||||
- **Close**: `glab issue close <number>`. `glab issue close` does not accept a closing comment, so post the explanation first with `glab issue note <number> --message "..."`, then close.
|
||||
- **Merge requests**: GitLab calls PRs "merge requests". Use `glab mr create`, `glab mr view`, `glab mr note`, etc. — the same shape as `gh pr ...` with `mr` in place of `pr` and `note`/`--message` in place of `comment`/`--body`.
|
||||
|
||||
Infer the repo from `git remote -v` — `glab` does this automatically when run inside a clone.
|
||||
|
||||
## Merge requests as a triage surface
|
||||
|
||||
**MRs as a request surface: no.** _(Set to `yes` if this repo treats external merge requests as feature requests; `/triage` reads this flag.)_
|
||||
|
||||
When set to `yes`, MRs run through the same labels and states as issues, using the `glab mr` equivalents:
|
||||
|
||||
- **Read an MR**: `glab mr view <number> --comments` and `glab mr diff <number>` for the diff.
|
||||
- **List external MRs for triage**: `glab mr list -F json`, then keep only MRs whose author is not a project member/owner (a contributor's MR, not a maintainer's in-flight work).
|
||||
- **Comment / label / close**: `glab mr note`, `glab mr update --label`/`--unlabel`, `glab mr close`.
|
||||
|
||||
Unlike GitHub, GitLab numbers issues and MRs separately, so `#42` is unambiguous once you know which surface the maintainer means.
|
||||
|
||||
## When a skill says "publish to the issue tracker"
|
||||
|
||||
Create a GitLab issue.
|
||||
|
||||
## When a skill says "fetch the relevant ticket"
|
||||
|
||||
Run `glab issue view <number> --comments`.
|
||||
@@ -0,0 +1,19 @@
|
||||
# Issue tracker: Local Markdown
|
||||
|
||||
Issues and PRDs for this repo live as markdown files in `.scratch/`.
|
||||
|
||||
## Conventions
|
||||
|
||||
- One feature per directory: `.scratch/<feature-slug>/`
|
||||
- The PRD is `.scratch/<feature-slug>/PRD.md`
|
||||
- Implementation issues are `.scratch/<feature-slug>/issues/<NN>-<slug>.md`, numbered from `01`
|
||||
- Triage state is recorded as a `Status:` line near the top of each issue file (see `triage-labels.md` for the role strings)
|
||||
- Comments and conversation history append to the bottom of the file under a `## Comments` heading
|
||||
|
||||
## When a skill says "publish to the issue tracker"
|
||||
|
||||
Create a new file under `.scratch/<feature-slug>/` (creating the directory if needed).
|
||||
|
||||
## When a skill says "fetch the relevant ticket"
|
||||
|
||||
Read the file at the referenced path. The user will normally pass the path or the issue number directly.
|
||||
15
.agents/skills/setup-matt-pocock-skills/triage-labels.md
Normal file
15
.agents/skills/setup-matt-pocock-skills/triage-labels.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Triage Labels
|
||||
|
||||
The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
|
||||
|
||||
| Label in mattpocock/skills | Label in our tracker | Meaning |
|
||||
| -------------------------- | -------------------- | ---------------------------------------- |
|
||||
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
|
||||
| `needs-info` | `needs-info` | Waiting on reporter for more information |
|
||||
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
|
||||
| `ready-for-human` | `ready-for-human` | Requires human implementation |
|
||||
| `wontfix` | `wontfix` | Will not be actioned |
|
||||
|
||||
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
|
||||
|
||||
Edit the right-hand column to match whatever vocabulary you actually use.
|
||||
111
.agents/skills/tdd/SKILL.md
Normal file
111
.agents/skills/tdd/SKILL.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
name: tdd
|
||||
description: Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
|
||||
---
|
||||
|
||||
# Test-Driven Development
|
||||
|
||||
## Philosophy
|
||||
|
||||
**Core principle**: Tests should verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't.
|
||||
|
||||
**Good tests** are integration-style: they exercise real code paths through public APIs. They describe _what_ the system does, not _how_ it does it. A good test reads like a specification - "user can checkout with valid cart" tells you exactly what capability exists. These tests survive refactors because they don't care about internal structure.
|
||||
|
||||
**Bad tests** are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (like querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behavior hasn't changed. If you rename an internal function and tests fail, those tests were testing implementation, not behavior.
|
||||
|
||||
**Tautological tests** restate the implementation inside the assertion, so they pass by construction and give zero confidence. When the expected value is computed the way the code computes it — `expect(add(a, b)).toBe(a + b)`, snapshotting a figure you derived by hand the same way the code does, asserting a constant equals itself — the test can never disagree with the code: break the code wrong and the assertion breaks wrong with it. The expected value must come from an independent source of truth — a known-good literal, a worked example, the spec.
|
||||
|
||||
See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.
|
||||
|
||||
## Anti-Pattern: Horizontal Slices
|
||||
|
||||
**DO NOT write all tests first, then all implementation.** This is "horizontal slicing" - treating RED as "write all tests" and GREEN as "write all code."
|
||||
|
||||
This produces **crap tests**:
|
||||
|
||||
- Tests written in bulk test _imagined_ behavior, not _actual_ behavior
|
||||
- You end up testing the _shape_ of things (data structures, function signatures) rather than user-facing behavior
|
||||
- Tests become insensitive to real changes - they pass when behavior breaks, fail when behavior is fine
|
||||
- You outrun your headlights, committing to test structure before understanding the implementation
|
||||
|
||||
**Correct approach**: Vertical slices via tracer bullets. One test → one implementation → repeat. Each test responds to what you learned from the previous cycle. Because you just wrote the code, you know exactly what behavior matters and how to verify it.
|
||||
|
||||
```
|
||||
WRONG (horizontal):
|
||||
RED: test1, test2, test3, test4, test5
|
||||
GREEN: impl1, impl2, impl3, impl4, impl5
|
||||
|
||||
RIGHT (vertical):
|
||||
RED→GREEN: test1→impl1
|
||||
RED→GREEN: test2→impl2
|
||||
RED→GREEN: test3→impl3
|
||||
...
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Planning
|
||||
|
||||
When exploring the codebase, read `CONTEXT.md` (if it exists) so that test names and interface vocabulary match the project's domain language, and respect ADRs in the area you're touching.
|
||||
|
||||
Before writing any code:
|
||||
|
||||
- [ ] Confirm with user what interface changes are needed
|
||||
- [ ] Confirm with user which behaviors to test (prioritize)
|
||||
- [ ] Identify opportunities for deep modules (small interface, deep implementation) — run the `/codebase-design` skill for the vocabulary and the testability checks
|
||||
- [ ] List the behaviors to test (not implementation steps)
|
||||
- [ ] Get user approval on the plan
|
||||
|
||||
Ask: "What should the public interface look like? Which behaviors are most important to test?"
|
||||
|
||||
**You can't test everything.** Confirm with the user exactly which behaviors matter most. Focus testing effort on critical paths and complex logic, not every possible edge case.
|
||||
|
||||
### 2. Tracer Bullet
|
||||
|
||||
Write ONE test that confirms ONE thing about the system:
|
||||
|
||||
```
|
||||
RED: Write test for first behavior → test fails
|
||||
GREEN: Write minimal code to pass → test passes
|
||||
```
|
||||
|
||||
This is your tracer bullet - proves the path works end-to-end.
|
||||
|
||||
### 3. Incremental Loop
|
||||
|
||||
For each remaining behavior:
|
||||
|
||||
```
|
||||
RED: Write next test → fails
|
||||
GREEN: Minimal code to pass → passes
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- One test at a time
|
||||
- Only enough code to pass current test
|
||||
- Don't anticipate future tests
|
||||
- Keep tests focused on observable behavior
|
||||
|
||||
### 4. Refactor
|
||||
|
||||
After all tests pass, look for [refactor candidates](refactoring.md):
|
||||
|
||||
- [ ] Extract duplication
|
||||
- [ ] Deepen modules (move complexity behind simple interfaces)
|
||||
- [ ] Apply SOLID principles where natural
|
||||
- [ ] Consider what new code reveals about existing code
|
||||
- [ ] Run tests after each refactor step
|
||||
|
||||
**Never refactor while RED.** Get to GREEN first.
|
||||
|
||||
## Checklist Per Cycle
|
||||
|
||||
```
|
||||
[ ] Test describes behavior, not implementation
|
||||
[ ] Test uses public interface only
|
||||
[ ] Test would survive internal refactor
|
||||
[ ] Expected values are independent literals, not recomputed from the code
|
||||
[ ] Code is minimal for this test
|
||||
[ ] No speculative features added
|
||||
```
|
||||
59
.agents/skills/tdd/mocking.md
Normal file
59
.agents/skills/tdd/mocking.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# When to Mock
|
||||
|
||||
Mock at **system boundaries** only:
|
||||
|
||||
- External APIs (payment, email, etc.)
|
||||
- Databases (sometimes - prefer test DB)
|
||||
- Time/randomness
|
||||
- File system (sometimes)
|
||||
|
||||
Don't mock:
|
||||
|
||||
- Your own classes/modules
|
||||
- Internal collaborators
|
||||
- Anything you control
|
||||
|
||||
## Designing for Mockability
|
||||
|
||||
At system boundaries, design interfaces that are easy to mock:
|
||||
|
||||
**1. Use dependency injection**
|
||||
|
||||
Pass external dependencies in rather than creating them internally:
|
||||
|
||||
```typescript
|
||||
// Easy to mock
|
||||
function processPayment(order, paymentClient) {
|
||||
return paymentClient.charge(order.total);
|
||||
}
|
||||
|
||||
// Hard to mock
|
||||
function processPayment(order) {
|
||||
const client = new StripeClient(process.env.STRIPE_KEY);
|
||||
return client.charge(order.total);
|
||||
}
|
||||
```
|
||||
|
||||
**2. Prefer SDK-style interfaces over generic fetchers**
|
||||
|
||||
Create specific functions for each external operation instead of one generic function with conditional logic:
|
||||
|
||||
```typescript
|
||||
// GOOD: Each function is independently mockable
|
||||
const api = {
|
||||
getUser: (id) => fetch(`/users/${id}`),
|
||||
getOrders: (userId) => fetch(`/users/${userId}/orders`),
|
||||
createOrder: (data) => fetch('/orders', { method: 'POST', body: data }),
|
||||
};
|
||||
|
||||
// BAD: Mocking requires conditional logic inside the mock
|
||||
const api = {
|
||||
fetch: (endpoint, options) => fetch(endpoint, options),
|
||||
};
|
||||
```
|
||||
|
||||
The SDK approach means:
|
||||
- Each mock returns one specific shape
|
||||
- No conditional logic in test setup
|
||||
- Easier to see which endpoints a test exercises
|
||||
- Type safety per endpoint
|
||||
10
.agents/skills/tdd/refactoring.md
Normal file
10
.agents/skills/tdd/refactoring.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Refactor Candidates
|
||||
|
||||
After TDD cycle, look for:
|
||||
|
||||
- **Duplication** → Extract function/class
|
||||
- **Long methods** → Break into private helpers (keep tests on public interface)
|
||||
- **Shallow modules** → Combine or deepen
|
||||
- **Feature envy** → Move logic to where data lives
|
||||
- **Primitive obsession** → Introduce value objects
|
||||
- **Existing code** the new code reveals as problematic
|
||||
77
.agents/skills/tdd/tests.md
Normal file
77
.agents/skills/tdd/tests.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Good and Bad Tests
|
||||
|
||||
## Good Tests
|
||||
|
||||
**Integration-style**: Test through real interfaces, not mocks of internal parts.
|
||||
|
||||
```typescript
|
||||
// GOOD: Tests observable behavior
|
||||
test("user can checkout with valid cart", async () => {
|
||||
const cart = createCart();
|
||||
cart.add(product);
|
||||
const result = await checkout(cart, paymentMethod);
|
||||
expect(result.status).toBe("confirmed");
|
||||
});
|
||||
```
|
||||
|
||||
Characteristics:
|
||||
|
||||
- Tests behavior users/callers care about
|
||||
- Uses public API only
|
||||
- Survives internal refactors
|
||||
- Describes WHAT, not HOW
|
||||
- One logical assertion per test
|
||||
|
||||
## Bad Tests
|
||||
|
||||
**Implementation-detail tests**: Coupled to internal structure.
|
||||
|
||||
```typescript
|
||||
// BAD: Tests implementation details
|
||||
test("checkout calls paymentService.process", async () => {
|
||||
const mockPayment = jest.mock(paymentService);
|
||||
await checkout(cart, payment);
|
||||
expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
|
||||
});
|
||||
```
|
||||
|
||||
Red flags:
|
||||
|
||||
- Mocking internal collaborators
|
||||
- Testing private methods
|
||||
- Asserting on call counts/order
|
||||
- Test breaks when refactoring without behavior change
|
||||
- Test name describes HOW not WHAT
|
||||
- Verifying through external means instead of interface
|
||||
|
||||
```typescript
|
||||
// BAD: Bypasses interface to verify
|
||||
test("createUser saves to database", async () => {
|
||||
await createUser({ name: "Alice" });
|
||||
const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
|
||||
expect(row).toBeDefined();
|
||||
});
|
||||
|
||||
// GOOD: Verifies through interface
|
||||
test("createUser makes user retrievable", async () => {
|
||||
const user = await createUser({ name: "Alice" });
|
||||
const retrieved = await getUser(user.id);
|
||||
expect(retrieved.name).toBe("Alice");
|
||||
});
|
||||
```
|
||||
|
||||
**Tautological tests**: Expected value restates the implementation, so the test passes by construction.
|
||||
|
||||
```typescript
|
||||
// BAD: Expected value is recomputed the way the code computes it
|
||||
test("calculateTotal sums line items", () => {
|
||||
const items = [{ price: 10 }, { price: 5 }];
|
||||
const expected = items.reduce((sum, i) => sum + i.price, 0);
|
||||
expect(calculateTotal(items)).toBe(expected);
|
||||
});
|
||||
|
||||
// GOOD: Expected value is an independent, known literal
|
||||
test("calculateTotal sums line items", () => {
|
||||
expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15);
|
||||
});
|
||||
```
|
||||
35
.agents/skills/teach/GLOSSARY-FORMAT.md
Normal file
35
.agents/skills/teach/GLOSSARY-FORMAT.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# GLOSSARY.md Format
|
||||
|
||||
`GLOSSARY.md` is the canonical language for this teaching workspace. All explainers, exercises, and learning records should adhere to its terminology. Building it is itself part of learning: compressing a concept into a tight definition is evidence the user understands it.
|
||||
|
||||
## Structure
|
||||
|
||||
```md
|
||||
# {Topic} Glossary
|
||||
|
||||
{One or two sentence description of the topic this glossary covers.}
|
||||
|
||||
## Terms
|
||||
|
||||
**Hypertrophy**:
|
||||
Muscle growth driven by mechanical tension and metabolic stress over repeated training sessions.
|
||||
_Avoid_: Bulking, getting big
|
||||
|
||||
**Progressive overload**:
|
||||
Systematically increasing the demand on a muscle over time — via load, volume, or intensity.
|
||||
_Avoid_: Pushing harder, levelling up
|
||||
|
||||
**RPE (Rate of Perceived Exertion)**:
|
||||
A 1–10 self-rating of how hard a set felt, where 10 is failure and 8 means two reps left in the tank.
|
||||
_Avoid_: Effort score, intensity rating
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- **Add a term only when the user understands it.** The glossary is a record of compressed knowledge, not a dictionary the user reads to learn. If the user has just been introduced to a concept, wait until they can use it correctly before promoting it here.
|
||||
- **Be opinionated.** When several words exist for the same concept, pick the best one and list the rest as aliases to avoid. This is how language compresses.
|
||||
- **Keep definitions tight.** One or two sentences. Define what the term IS, not what it does or how to do it.
|
||||
- **Use the glossary's own terms inside definitions.** Once a term is in the glossary, prefer it everywhere — including inside other definitions. This is what makes complex terms easier to grasp later.
|
||||
- **Group under subheadings** when natural clusters emerge (e.g. `## Anatomy`, `## Programming`). A flat list is fine when terms cohere.
|
||||
- **Flag ambiguities explicitly.** If a term is used loosely in the wider field, note the resolution: "In this workspace, 'set' always means a working set — warm-ups are tracked separately."
|
||||
- **Revise as understanding deepens.** A definition the user wrote in week one may be wrong by week six. Update in place; do not leave stale entries.
|
||||
46
.agents/skills/teach/LEARNING-RECORD-FORMAT.md
Normal file
46
.agents/skills/teach/LEARNING-RECORD-FORMAT.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Learning Record Format
|
||||
|
||||
Learning records live in `./learning-records/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc. Create the directory lazily — only when the first record is written.
|
||||
|
||||
They are the teaching equivalent of ADRs: they capture non-obvious lessons, key insights, and stated prior knowledge that will steer future sessions. They are used to calculate the zone of proximal development.
|
||||
|
||||
## Template
|
||||
|
||||
```md
|
||||
# {Short title of what was learned or established}
|
||||
|
||||
{1-3 sentences: what was learned (or what prior knowledge was established), and why it matters for future sessions.}
|
||||
```
|
||||
|
||||
That is the whole format. A learning record can be a single paragraph. The value is recording _that_ this is now known and _why_ it changes what to teach next — not in filling out sections.
|
||||
|
||||
## Optional sections
|
||||
|
||||
Only include these when they add genuine value. Most records won't need them.
|
||||
|
||||
- **Status** frontmatter (`active | superseded by LR-NNNN`) — useful when an earlier understanding turns out to be wrong and is replaced.
|
||||
- **Evidence** — how the user demonstrated the understanding (a question answered, an exercise completed, prior experience cited). Useful when the claim might be revisited.
|
||||
- **Implications** — what this unlocks or rules out for future sessions. Worth recording when non-obvious.
|
||||
|
||||
## Numbering
|
||||
|
||||
Scan `./learning-records/` for the highest existing number and increment by one.
|
||||
|
||||
## When to write a learning record
|
||||
|
||||
Write one when any of these is true:
|
||||
|
||||
1. **The user demonstrated genuine understanding of something non-trivial** — not just exposure, but evidence they can use the concept correctly. This sets a new floor for what to teach next.
|
||||
2. **The user disclosed prior knowledge** — "I already know X." Record it so future sessions don't re-teach it. Also record the _depth_ claimed.
|
||||
3. **A misconception was corrected** — the user previously believed something wrong and now sees why. These are high-value: they predict future stumbling blocks for related topics.
|
||||
4. **The mission shifted in response to learning** — the user discovered they cared about something different than they thought. Cross-link to [[MISSION.md]] and update it.
|
||||
|
||||
### What does _not_ qualify
|
||||
|
||||
- Material that was merely covered. Coverage is not learning. Wait for evidence.
|
||||
- Anything already captured tersely in [[GLOSSARY.md]] as a term definition. Don't duplicate.
|
||||
- Session-by-session activity logs. Learning records are not a journal — they are decision-grade insights.
|
||||
|
||||
## Supersession
|
||||
|
||||
When a later record contradicts an earlier one (the user's understanding deepened or corrected), mark the old record `Status: superseded by LR-NNNN` rather than deleting it. The history of how understanding evolved is itself useful signal.
|
||||
31
.agents/skills/teach/MISSION-FORMAT.md
Normal file
31
.agents/skills/teach/MISSION-FORMAT.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# MISSION.md Format
|
||||
|
||||
`MISSION.md` lives at the workspace root. It captures the _reason_ the user is learning this topic. Every teaching decision — what to teach next, which resources to surface, which exercises to design — should trace back to this document.
|
||||
|
||||
## Template
|
||||
|
||||
```md
|
||||
# Mission: {Topic}
|
||||
|
||||
## Why
|
||||
{1-3 sentences. The concrete real-world goal the user is chasing. What changes in their life or work when they have this skill? Avoid abstract framings like "to understand X" — push for the underlying outcome.}
|
||||
|
||||
## Success looks like
|
||||
- {A specific, observable thing the user will be able to do}
|
||||
- {Another specific thing}
|
||||
- {…}
|
||||
|
||||
## Constraints
|
||||
- {Time, budget, prior commitments, learning preferences, anything that bounds the approach}
|
||||
|
||||
## Out of scope
|
||||
- {Adjacent topics the user explicitly does not want to chase right now — protects the zone of proximal development}
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- **One mission per workspace.** If the user wants to learn two unrelated things, that is two workspaces.
|
||||
- **Concrete over abstract.** "Run a half marathon by October" beats "get fitter." "Ship a Rust CLI to my team" beats "learn Rust."
|
||||
- **Push back on vagueness.** If the user cannot articulate why, interview them before writing anything. A bad mission is worse than no mission.
|
||||
- **Revise when reality shifts.** Missions change. When the user's goal moves, update this file — don't leave a stale mission steering future sessions.
|
||||
- **Keep it short.** If `MISSION.md` runs past a screen, it has stopped being a compass and started being a plan.
|
||||
32
.agents/skills/teach/RESOURCES-FORMAT.md
Normal file
32
.agents/skills/teach/RESOURCES-FORMAT.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# RESOURCES.md Format
|
||||
|
||||
`RESOURCES.md` is the curated set of trusted sources for this topic. Knowledge for explainers should be drawn from here, not from parametric guesses. Wisdom comes from the communities listed here.
|
||||
|
||||
## Structure
|
||||
|
||||
```md
|
||||
# {Topic} Resources
|
||||
|
||||
## Knowledge
|
||||
|
||||
- [Book: _The Science and Practice of Strength Training_ — Zatsiorsky & Kraemer](https://example.com)
|
||||
Foundational text on programming and adaptation. Use for: anything to do with periodisation, recovery, intensity zones.
|
||||
- [Article: "How Much Should I Train?" — Greg Nuckols (Stronger By Science)](https://example.com)
|
||||
Evidence-based review of volume landmarks. Use for: weekly set targets per muscle group.
|
||||
|
||||
## Wisdom (Communities)
|
||||
|
||||
- [r/weightroom](https://reddit.com/r/weightroom)
|
||||
High-signal subreddit, moderated against bro-science. Use for: programme critique, plateau troubleshooting.
|
||||
- Local: Tuesday strength class at {gym name}
|
||||
Use for: real-time coaching feedback on lifts.
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- **High-trust only.** Prefer primary sources, recognised experts, peer-reviewed work, and communities with strong moderation. If a resource is marketing dressed as education, leave it out.
|
||||
- **Annotate every entry.** A bare link is useless in three months. Add one line: what it covers and when to reach for it.
|
||||
- **Group by Knowledge / Wisdom.** Mirrors the philosophy in [SKILL.md](./SKILL.md). It is fine for a resource to appear in only one group.
|
||||
- **Surface gaps explicitly.** If no good resource exists for an area the mission needs, write a `## Gaps` section listing what is missing. This drives future search.
|
||||
- **Prune ruthlessly.** A resource that turned out to be wrong, shallow, or off-mission should be removed, not buried. Better five sharp sources than thirty mediocre ones.
|
||||
- **Record community preferences.** If the user has opted out of joining communities, note it here so future sessions don't keep proposing them.
|
||||
140
.agents/skills/teach/SKILL.md
Normal file
140
.agents/skills/teach/SKILL.md
Normal file
@@ -0,0 +1,140 @@
|
||||
---
|
||||
name: teach
|
||||
description: Teach the user a new skill or concept, within this workspace.
|
||||
disable-model-invocation: true
|
||||
argument-hint: "What would you like to learn about?"
|
||||
---
|
||||
|
||||
The user has asked you to teach them something. This is a stateful request - they intend to learn the topic over multiple sessions.
|
||||
|
||||
## Teaching Workspace
|
||||
|
||||
Treat the current directory as a teaching workspace. The state of their learning is captured in this directory in several files:
|
||||
|
||||
- `MISSION.md`: A document capturing the _reason_ the user is interested in the topic. This should be used to ground all teaching. Use the format in [MISSION-FORMAT.md](./MISSION-FORMAT.md).
|
||||
- `./reference/*.html`: A directory of reference materials. These are the compressed learnings from the lessons - cheat sheets, reference algorithms, syntax, yoga poses, glossaries. They are the raw units of learning. They should be beautiful documents which print out well, and are designed for quick reference.
|
||||
- `RESOURCES.md`: A list of resources which can be explored to ground your teaching in contextual knowledge, or to acquire knowledge and wisdom. Use the format in [RESOURCES-FORMAT.md](./RESOURCES-FORMAT.md).
|
||||
- `./learning-records/*.md`: A directory of learning records, which capture what the user has learned. These are loosely equivalent to architectural decision records in software development - they capture non-obvious lessons and key insights that may need to be revised later, or drive future sessions. These should be used to calculate the zone of proximal development. They are titled `0001-<dash-case-name>.md`, where the number increments each time. Use the format in [LEARNING-RECORD-FORMAT.md](./LEARNING-RECORD-FORMAT.md).
|
||||
- `./lessons/*.html`: A directory of lessons. A **lesson** is a single, self-contained HTML output that teaches one tightly-scoped thing tied to the mission. This is the primary unit of teaching in this workspace.
|
||||
- `./assets/*`: Reusable **components** shared across lessons. See [Assets](#assets).
|
||||
- `NOTES.md`: A scratchpad for you to jot down user preferences, or working notes.
|
||||
|
||||
## Philosophy
|
||||
|
||||
To learn at a deep level, the user needs three things:
|
||||
|
||||
- **Knowledge**, captured from high-quality, high-trust resources
|
||||
- **Skills**, acquired through highly-relevant interactive lessons devised by you, based on the knowledge
|
||||
- **Wisdom**, which comes from interacting with other learners and practitioners
|
||||
|
||||
Before the `RESOURCES.md` is well-populated, your focus should be to find high-quality resources which will help the user acquire knowledge. Never trust your parametric knowledge.
|
||||
|
||||
Some topics may require more skills than knowledge. Learning more about theoretical physics might be more knowledge-based. For yoga, more skills-based.
|
||||
|
||||
### Fluency vs Storage Strength
|
||||
|
||||
You should be careful to split between two types of learning:
|
||||
|
||||
- **Fluency strength**: in-the-moment retrieval of knowledge
|
||||
- **Storage strength**: long-term retention of knowledge
|
||||
|
||||
Fluency can give the user an illusory sense of mastery, but storage strength is the real goal. Try to design lessons which build long-term retention by desirable difficulty:
|
||||
|
||||
- Using retrieval practice (recall from memory)
|
||||
- Spacing (distributing practice over time)
|
||||
- Interleaving (mixing up different but related topics in practice - for skills practice only)
|
||||
|
||||
## Lessons
|
||||
|
||||
A lesson is the main thing you produce — the unit in which knowledge and skills reach the user. Each lesson is one self-contained HTML file, saved to `./lessons/` and titled `0001-<dash-case-name>.html` where the number increments each time.
|
||||
|
||||
A lesson should be **beautiful** — clean, readable typography and layout — since the user will return to these later to review. Think Tufte.
|
||||
|
||||
The lesson should be short, and completable very quickly. Learners' working memory is very small, and we need to stay within it. But each lesson should give the user a single tangible win that they can build on. It should be directly tied to the mission, and should be in the user's zone of proximal development.
|
||||
|
||||
If possible, open the lesson file for the user by running a CLI command.
|
||||
|
||||
Each lesson should link via HTML anchors to other lessons and reference documents.
|
||||
|
||||
Each lesson should recommend a primary source for the user to read or watch. This should be the most high-quality, high-trust resource you found on the topic.
|
||||
|
||||
Each lesson should contain a reminder to ask followup questions to the agent. The agent is their teacher, and can assist with anything that's unclear.
|
||||
|
||||
## Assets
|
||||
|
||||
Lessons are built from reusable **components**, stored in `./assets/`: stylesheets, quiz widgets, simulators, diagram helpers — anything a second lesson could reuse.
|
||||
|
||||
Reuse is the default, not the exception. Before authoring a lesson, read `./assets/` and build from the components already there. When a lesson needs something new and reusable, write it as a component in `./assets/` and link to it — never inline code a future lesson would duplicate.
|
||||
|
||||
A shared stylesheet is the first component every workspace earns: every lesson links it, so the lessons look like one consistent course rather than a pile of one-offs. As the workspace grows, so should the component library.
|
||||
|
||||
## The Mission
|
||||
|
||||
Every lesson should be tied into the mission - the reason that the user is interested in learning about the topic.
|
||||
|
||||
If the user is unclear about the mission, or the `MISSION.md` is not populated, your first job should be to question the user on why they want to learn this.
|
||||
|
||||
Failing to understand the mission will mean knowledge acquisition is not grounded in real-world goals. Lessons will feel too abstract. You will have no way of judging what the user should do next.
|
||||
|
||||
Missions may change as the user develops more skills and knowledge. This is normal - make sure to update the `MISSION.md` and add a learning record to capture the change. Confirm with the user before changing the mission.
|
||||
|
||||
## Zone Of Proximal Development
|
||||
|
||||
Each lesson, the user should always feel as if they are being challenged 'just enough'.
|
||||
|
||||
The user may specify an exact thing they want to learn. If they don't, figure out their zone of proximal development by:
|
||||
|
||||
- Reading their `learning-records`
|
||||
- Figuring out the right thing to teach them based on their mission
|
||||
- Teach the most relevant thing that fits in their zone of proximal development
|
||||
|
||||
## Knowledge
|
||||
|
||||
Lessons should be designed around a skill the user is going to learn. The knowledge in the lesson should be only what's required to acquire that skill. You teach the knowledge first, then get the user to practice the skills via an interactive feedback loop.
|
||||
|
||||
Knowledge should first be gathered from trusted resources. Use `RESOURCES.md` to keep track of them. Lessons should be littered with citations - links to external resources to back up any claim made. This increases the trustworthiness of the lesson.
|
||||
|
||||
For acquiring knowledge, difficulty is the enemy. It eats working memory you need for understanding.
|
||||
|
||||
## Skills
|
||||
|
||||
If knowledge is all about acquisition, skills are about durability and flexibility. Make the knowledge stick.
|
||||
|
||||
For skill acquisition, difficulty is the tool. Effortful retrieval is what builds storage strength. Skills should be taught through interactive lessons. There are several tools at your disposal:
|
||||
|
||||
- Interactive lessons, using quizzes and light in-browser tasks
|
||||
- Lessons which guide the user through a list of real-world steps to take (for instance, yoga poses)
|
||||
|
||||
Each of these should be based on a **feedback loop**, where the user receives feedback on their performance. This feedback loop should be as tight as possible, giving feedback immediately - and ideally automatically.
|
||||
|
||||
For quizzes, each answer should be exactly the same number of words (and characters, if possible). Don't give the user any clues about the answer through formatting.
|
||||
|
||||
## Acquiring Wisdom
|
||||
|
||||
Wisdom comes from true real-world interaction - testing your skills outside the learning environment.
|
||||
|
||||
When the user asks a question that appears to require wisdom, your default posture should be to attempt to answer - but to ultimately delegate to a **community**.
|
||||
|
||||
A community is a place (online or offline) where the user can test their skills in the real world. This might be a forum, a subreddit, a real-world class (budget permitting) or a local interest group.
|
||||
|
||||
You should attempt to find high-reputation communities the user can join. If the user expresses a preference that they don't want to join a community, respect it.
|
||||
|
||||
## Reference Documents
|
||||
|
||||
While creating lessons, you should also create reference documents. Lessons can reference these documents - they are useful for tracking raw units of knowledge useful across lessons.
|
||||
|
||||
Lessons will rarely be revisited later - reference documents will be. They should be the compressed essence of the lesson, in a format designed for quick reference.
|
||||
|
||||
Some learning topics lend themselves to reference:
|
||||
|
||||
- Syntax and code snippets for programming
|
||||
- Algorithms and flowcharts for processes
|
||||
- Yoga poses and sequences for yoga
|
||||
- Exercises and routines for fitness
|
||||
- Glossaries for any topic with its own nomenclature
|
||||
|
||||
Glossaries, in particular, are an essential reference. Once one is created, it should be adhered to in every lesson.
|
||||
|
||||
## `NOTES.md`
|
||||
|
||||
The user will sometimes express preferences of how they want to be taught, or things you should keep in mind. This is the place to record those preferences, so you can refer back to them when designing lessons or working with the user.
|
||||
84
.agents/skills/to-issues/SKILL.md
Normal file
84
.agents/skills/to-issues/SKILL.md
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
name: to-issues
|
||||
description: Break a plan, spec, or PRD into independently-grabbable issues on the project issue tracker using tracer-bullet vertical slices.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# To Issues
|
||||
|
||||
Break a plan into independently-grabbable issues using vertical slices (tracer bullets).
|
||||
|
||||
The issue tracker and triage label vocabulary should have been provided to you — run `/setup-matt-pocock-skills` if not.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Gather context
|
||||
|
||||
Work from whatever is already in the conversation context. If the user passes an issue reference (issue number, URL, or path) as an argument, fetch it from the issue tracker and read its full body and comments.
|
||||
|
||||
### 2. Explore the codebase (optional)
|
||||
|
||||
If you have not already explored the codebase, do so to understand the current state of the code. Issue titles and descriptions should use the project's domain glossary vocabulary, and respect ADRs in the area you're touching.
|
||||
|
||||
Look for opportunities to prefactor the code to make the implementation easier. "Make the change easy, then make the easy change."
|
||||
|
||||
### 3. Draft vertical slices
|
||||
|
||||
Break the plan into **tracer bullet** issues. Each issue is a thin vertical slice that cuts through ALL integration layers end-to-end, NOT a horizontal slice of one layer.
|
||||
|
||||
<vertical-slice-rules>
|
||||
|
||||
- Each slice delivers a narrow but COMPLETE path through every layer (schema, API, UI, tests)
|
||||
- A completed slice is demoable or verifiable on its own
|
||||
- Any prefactoring should be done first
|
||||
|
||||
</vertical-slice-rules>
|
||||
|
||||
### 4. Quiz the user
|
||||
|
||||
Present the proposed breakdown as a numbered list. For each slice, show:
|
||||
|
||||
- **Title**: short descriptive name
|
||||
- **Blocked by**: which other slices (if any) must complete first
|
||||
- **User stories covered**: which user stories this addresses (if the source material has them)
|
||||
|
||||
Ask the user:
|
||||
|
||||
- Does the granularity feel right? (too coarse / too fine)
|
||||
- Are the dependency relationships correct?
|
||||
- Should any slices be merged or split further?
|
||||
|
||||
Iterate until the user approves the breakdown.
|
||||
|
||||
### 5. Publish the issues to the issue tracker
|
||||
|
||||
For each approved slice, publish a new issue to the issue tracker. Use the issue body template below. These issues are considered ready for AFK agents, so publish them with the correct triage label unless instructed otherwise.
|
||||
|
||||
Publish issues in dependency order (blockers first) so you can reference real issue identifiers in the "Blocked by" field.
|
||||
|
||||
<issue-template>
|
||||
## Parent
|
||||
|
||||
A reference to the parent issue on the issue tracker (if the source was an existing issue, otherwise omit this section).
|
||||
|
||||
## What to build
|
||||
|
||||
A concise description of this vertical slice. Describe the end-to-end behavior, not layer-by-layer implementation.
|
||||
|
||||
Avoid specific file paths or code snippets — they go stale fast. Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it here and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Criterion 1
|
||||
- [ ] Criterion 2
|
||||
- [ ] Criterion 3
|
||||
|
||||
## Blocked by
|
||||
|
||||
- A reference to the blocking ticket (if any)
|
||||
|
||||
Or "None - can start immediately" if no blockers.
|
||||
|
||||
</issue-template>
|
||||
|
||||
Do NOT close or modify any parent issue.
|
||||
75
.agents/skills/to-prd/SKILL.md
Normal file
75
.agents/skills/to-prd/SKILL.md
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
name: to-prd
|
||||
description: Turn the current conversation into a PRD and publish it to the project issue tracker — no interview, just synthesis of what you've already discussed.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
This skill takes the current conversation context and codebase understanding and produces a PRD. Do NOT interview the user — just synthesize what you already know.
|
||||
|
||||
The issue tracker and triage label vocabulary should have been provided to you — run `/setup-matt-pocock-skills` if not.
|
||||
|
||||
## Process
|
||||
|
||||
1. Explore the repo to understand the current state of the codebase, if you haven't already. Use the project's domain glossary vocabulary throughout the PRD, and respect any ADRs in the area you're touching.
|
||||
|
||||
2. Sketch out the seams at which you're going to test the feature. Existing seams should be preferred to new ones. Use the highest seam possible. If new seams are needed, propose them at the highest point you can. The fewer seams across the codebase, the better - the ideal number is one.
|
||||
|
||||
Check with the user that these seams match their expectations.
|
||||
|
||||
3. Write the PRD using the template below, then publish it to the project issue tracker. Apply the `ready-for-agent` triage label - no need for additional triage.
|
||||
|
||||
<prd-template>
|
||||
|
||||
## Problem Statement
|
||||
|
||||
The problem that the user is facing, from the user's perspective.
|
||||
|
||||
## Solution
|
||||
|
||||
The solution to the problem, from the user's perspective.
|
||||
|
||||
## User Stories
|
||||
|
||||
A LONG, numbered list of user stories. Each user story should be in the format of:
|
||||
|
||||
1. As an <actor>, I want a <feature>, so that <benefit>
|
||||
|
||||
<user-story-example>
|
||||
1. As a mobile bank customer, I want to see balance on my accounts, so that I can make better informed decisions about my spending
|
||||
</user-story-example>
|
||||
|
||||
This list of user stories should be extremely extensive and cover all aspects of the feature.
|
||||
|
||||
## Implementation Decisions
|
||||
|
||||
A list of implementation decisions that were made. This can include:
|
||||
|
||||
- The modules that will be built/modified
|
||||
- The interfaces of those modules that will be modified
|
||||
- Technical clarifications from the developer
|
||||
- Architectural decisions
|
||||
- Schema changes
|
||||
- API contracts
|
||||
- Specific interactions
|
||||
|
||||
Do NOT include specific file paths or code snippets. They may end up being outdated very quickly.
|
||||
|
||||
Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it within the relevant decision and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
|
||||
|
||||
## Testing Decisions
|
||||
|
||||
A list of testing decisions that were made. Include:
|
||||
|
||||
- A description of what makes a good test (only test external behavior, not implementation details)
|
||||
- Which modules will be tested
|
||||
- Prior art for the tests (i.e. similar types of tests in the codebase)
|
||||
|
||||
## Out of Scope
|
||||
|
||||
A description of the things that are out of scope for this PRD.
|
||||
|
||||
## Further Notes
|
||||
|
||||
Any further notes about the feature.
|
||||
|
||||
</prd-template>
|
||||
207
.agents/skills/triage/AGENT-BRIEF.md
Normal file
207
.agents/skills/triage/AGENT-BRIEF.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Writing Agent Briefs
|
||||
|
||||
An agent brief is a structured comment posted on a GitHub issue or PR when it moves to `ready-for-agent`. It is the authoritative specification that an AFK agent will work from. The original body and discussion are context — the agent brief is the contract.
|
||||
|
||||
The brief states **what the agent should do**, which stretches to both surfaces: for an issue, that's building the change from nothing; for a PR, it's what's left to do *to the existing diff* — finish it, close gaps, address review points. Same principles either way; the PR example below shows the difference.
|
||||
|
||||
## Principles
|
||||
|
||||
### Durability over precision
|
||||
|
||||
The issue may sit in `ready-for-agent` for days or weeks. The codebase will change in the meantime. Write the brief so it stays useful even as files are renamed, moved, or refactored.
|
||||
|
||||
- **Do** describe interfaces, types, and behavioral contracts
|
||||
- **Do** name specific types, function signatures, or config shapes that the agent should look for or modify
|
||||
- **Don't** reference file paths — they go stale
|
||||
- **Don't** reference line numbers
|
||||
- **Don't** assume the current implementation structure will remain the same
|
||||
|
||||
### Behavioral, not procedural
|
||||
|
||||
Describe **what** the system should do, not **how** to implement it. The agent will explore the codebase fresh and make its own implementation decisions.
|
||||
|
||||
- **Good:** "The `SkillConfig` type should accept an optional `schedule` field of type `CronExpression`"
|
||||
- **Bad:** "Open src/types/skill.ts and add a schedule field on line 42"
|
||||
- **Good:** "When a user runs `/triage` with no arguments, they should see a summary of issues needing attention"
|
||||
- **Bad:** "Add a switch statement in the main handler function"
|
||||
|
||||
### Complete acceptance criteria
|
||||
|
||||
The agent needs to know when it's done. Every agent brief must have concrete, testable acceptance criteria. Each criterion should be independently verifiable.
|
||||
|
||||
- **Good:** "Running `gh issue list --label needs-triage` returns issues that have been through initial classification"
|
||||
- **Bad:** "Triage should work correctly"
|
||||
|
||||
### Explicit scope boundaries
|
||||
|
||||
State what is out of scope. This prevents the agent from gold-plating or making assumptions about adjacent features.
|
||||
|
||||
## Template
|
||||
|
||||
```markdown
|
||||
## Agent Brief
|
||||
|
||||
**Category:** bug / enhancement
|
||||
**Summary:** one-line description of what needs to happen
|
||||
|
||||
**Current behavior:**
|
||||
Describe what happens now. For bugs, this is the broken behavior.
|
||||
For enhancements, this is the status quo the feature builds on.
|
||||
|
||||
**Desired behavior:**
|
||||
Describe what should happen after the agent's work is complete.
|
||||
Be specific about edge cases and error conditions.
|
||||
|
||||
**Key interfaces:**
|
||||
- `TypeName` — what needs to change and why
|
||||
- `functionName()` return type — what it currently returns vs what it should return
|
||||
- Config shape — any new configuration options needed
|
||||
|
||||
**Acceptance criteria:**
|
||||
- [ ] Specific, testable criterion 1
|
||||
- [ ] Specific, testable criterion 2
|
||||
- [ ] Specific, testable criterion 3
|
||||
|
||||
**Out of scope:**
|
||||
- Thing that should NOT be changed or addressed in this issue
|
||||
- Adjacent feature that might seem related but is separate
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Good agent brief (bug)
|
||||
|
||||
```markdown
|
||||
## Agent Brief
|
||||
|
||||
**Category:** bug
|
||||
**Summary:** Skill description truncation drops mid-word, producing broken output
|
||||
|
||||
**Current behavior:**
|
||||
When a skill description exceeds 1024 characters, it is truncated at exactly
|
||||
1024 characters regardless of word boundaries. This produces descriptions
|
||||
that end mid-word (e.g. "Use when the user wants to confi").
|
||||
|
||||
**Desired behavior:**
|
||||
Truncation should break at the last word boundary before 1024 characters
|
||||
and append "..." to indicate truncation.
|
||||
|
||||
**Key interfaces:**
|
||||
- The `SkillMetadata` type's `description` field — no type change needed,
|
||||
but the validation/processing logic that populates it needs to respect
|
||||
word boundaries
|
||||
- Any function that reads SKILL.md frontmatter and extracts the description
|
||||
|
||||
**Acceptance criteria:**
|
||||
- [ ] Descriptions under 1024 chars are unchanged
|
||||
- [ ] Descriptions over 1024 chars are truncated at the last word boundary
|
||||
before 1024 chars
|
||||
- [ ] Truncated descriptions end with "..."
|
||||
- [ ] The total length including "..." does not exceed 1024 chars
|
||||
|
||||
**Out of scope:**
|
||||
- Changing the 1024 char limit itself
|
||||
- Multi-line description support
|
||||
```
|
||||
|
||||
### Good agent brief (enhancement)
|
||||
|
||||
```markdown
|
||||
## Agent Brief
|
||||
|
||||
**Category:** enhancement
|
||||
**Summary:** Add `.out-of-scope/` directory support for tracking rejected feature requests
|
||||
|
||||
**Current behavior:**
|
||||
When a feature request is rejected, the issue is closed with a `wontfix` label
|
||||
and a comment. There is no persistent record of the decision or reasoning.
|
||||
Future similar requests require the maintainer to recall or search for the
|
||||
prior discussion.
|
||||
|
||||
**Desired behavior:**
|
||||
Rejected feature requests should be documented in `.out-of-scope/<concept>.md`
|
||||
files that capture the decision, reasoning, and links to all issues that
|
||||
requested the feature. When triaging new issues, these files should be
|
||||
checked for matches.
|
||||
|
||||
**Key interfaces:**
|
||||
- Markdown file format in `.out-of-scope/` — each file should have a
|
||||
`# Concept Name` heading, a `**Decision:**` line, a `**Reason:**` line,
|
||||
and a `**Prior requests:**` list with issue links
|
||||
- The triage workflow should read all `.out-of-scope/*.md` files early
|
||||
and match incoming issues against them by concept similarity
|
||||
|
||||
**Acceptance criteria:**
|
||||
- [ ] Closing a feature as wontfix creates/updates a file in `.out-of-scope/`
|
||||
- [ ] The file includes the decision, reasoning, and link to the closed issue
|
||||
- [ ] If a matching `.out-of-scope/` file already exists, the new issue is
|
||||
appended to its "Prior requests" list rather than creating a duplicate
|
||||
- [ ] During triage, existing `.out-of-scope/` files are checked and surfaced
|
||||
when a new issue matches a prior rejection
|
||||
|
||||
**Out of scope:**
|
||||
- Automated matching (human confirms the match)
|
||||
- Reopening previously rejected features
|
||||
- Bug reports (only enhancement rejections go to `.out-of-scope/`)
|
||||
```
|
||||
|
||||
### Good agent brief (PR)
|
||||
|
||||
For a PR, "Current behavior" describes the state of the diff, and the brief asks the agent to finish or fix it rather than build from scratch.
|
||||
|
||||
```markdown
|
||||
## Agent Brief
|
||||
|
||||
**Category:** enhancement
|
||||
**Summary:** Finish the contributor's `--json` output flag for `triage list`
|
||||
|
||||
**Current behavior:**
|
||||
The PR adds a `--json` flag that serializes the issue list to JSON. The happy
|
||||
path works and the diff matches the project's command structure. Two gaps
|
||||
remain: errors are still printed as human text (not JSON), and the new flag has
|
||||
no test coverage.
|
||||
|
||||
**Desired behavior:**
|
||||
With `--json`, all output — including errors — is well-formed JSON on stdout,
|
||||
and the command's exit codes are unchanged. The existing human-readable output
|
||||
is untouched when the flag is absent.
|
||||
|
||||
**Key interfaces:**
|
||||
- The command's error path should emit `{ "error": string }` under `--json`
|
||||
instead of the plain-text error
|
||||
- Reuse the existing serializer the PR already added; don't introduce a second
|
||||
|
||||
**Acceptance criteria:**
|
||||
- [ ] `triage list --json` emits valid JSON for both success and error cases
|
||||
- [ ] Exit codes match the non-JSON command
|
||||
- [ ] A test covers the `--json` success output and one error case
|
||||
- [ ] Default (non-JSON) output is byte-for-byte unchanged
|
||||
|
||||
**Out of scope:**
|
||||
- Adding `--json` to any other command
|
||||
- Changing the JSON shape of the success payload the PR already defined
|
||||
```
|
||||
|
||||
### Bad agent brief
|
||||
|
||||
```markdown
|
||||
## Agent Brief
|
||||
|
||||
**Summary:** Fix the triage bug
|
||||
|
||||
**What to do:**
|
||||
The triage thing is broken. Look at the main file and fix it.
|
||||
The function around line 150 has the issue.
|
||||
|
||||
**Files to change:**
|
||||
- src/triage/handler.ts (line 150)
|
||||
- src/types.ts (line 42)
|
||||
```
|
||||
|
||||
This is bad because:
|
||||
- No category
|
||||
- Vague description ("the triage thing is broken")
|
||||
- References file paths and line numbers that will go stale
|
||||
- No acceptance criteria
|
||||
- No scope boundaries
|
||||
- No description of current vs desired behavior
|
||||
105
.agents/skills/triage/OUT-OF-SCOPE.md
Normal file
105
.agents/skills/triage/OUT-OF-SCOPE.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Out-of-Scope Knowledge Base
|
||||
|
||||
The `.out-of-scope/` directory in a repo stores persistent records of rejected feature requests. It serves two purposes:
|
||||
|
||||
1. **Institutional memory** — why a feature was rejected, so the reasoning isn't lost when the issue is closed
|
||||
2. **Deduplication** — when a new issue comes in that matches a prior rejection, the skill can surface the previous decision instead of re-litigating it
|
||||
|
||||
## Directory structure
|
||||
|
||||
```
|
||||
.out-of-scope/
|
||||
├── dark-mode.md
|
||||
├── plugin-system.md
|
||||
└── graphql-api.md
|
||||
```
|
||||
|
||||
One file per **concept**, not per issue. Multiple issues requesting the same thing are grouped under one file.
|
||||
|
||||
## File format
|
||||
|
||||
The file should be written in a relaxed, readable style — more like a short design document than a database entry. Use paragraphs, code samples, and examples to make the reasoning clear and useful to someone encountering it for the first time.
|
||||
|
||||
```markdown
|
||||
# Dark Mode
|
||||
|
||||
This project does not support dark mode or user-facing theming.
|
||||
|
||||
## Why this is out of scope
|
||||
|
||||
The rendering pipeline assumes a single color palette defined in
|
||||
`ThemeConfig`. Supporting multiple themes would require:
|
||||
|
||||
- A theme context provider wrapping the entire component tree
|
||||
- Per-component theme-aware style resolution
|
||||
- A persistence layer for user theme preferences
|
||||
|
||||
This is a significant architectural change that doesn't align with the
|
||||
project's focus on content authoring. Theming is a concern for downstream
|
||||
consumers who embed or redistribute the output.
|
||||
|
||||
```ts
|
||||
// The current ThemeConfig interface is not designed for runtime switching:
|
||||
interface ThemeConfig {
|
||||
colors: ColorPalette; // single palette, resolved at build time
|
||||
fonts: FontStack;
|
||||
}
|
||||
```
|
||||
|
||||
## Prior requests
|
||||
|
||||
- #42 — "Add dark mode support"
|
||||
- #87 — "Night theme for accessibility"
|
||||
- #134 — "Dark theme option"
|
||||
```
|
||||
|
||||
### Naming the file
|
||||
|
||||
Use a short, descriptive kebab-case name for the concept: `dark-mode.md`, `plugin-system.md`, `graphql-api.md`. The name should be recognizable enough that someone browsing the directory understands what was rejected without opening the file.
|
||||
|
||||
### Writing the reason
|
||||
|
||||
The reason should be substantive — not "we don't want this" but why. Good reasons reference:
|
||||
|
||||
- Project scope or philosophy ("This project focuses on X; theming is a downstream concern")
|
||||
- Technical constraints ("Supporting this would require Y, which conflicts with our Z architecture")
|
||||
- Strategic decisions ("We chose to use A instead of B because...")
|
||||
|
||||
The reason should be durable. Avoid referencing temporary circumstances ("we're too busy right now") — those aren't real rejections, they're deferrals.
|
||||
|
||||
## When to check `.out-of-scope/`
|
||||
|
||||
During triage (Step 1: Gather context), read all files in `.out-of-scope/`. When evaluating a new issue:
|
||||
|
||||
- Check if the request matches an existing out-of-scope concept
|
||||
- Matching is by concept similarity, not keyword — "night theme" matches `dark-mode.md`
|
||||
- If there's a match, surface it to the maintainer: "This is similar to `.out-of-scope/dark-mode.md` — we rejected this before because [reason]. Do you still feel the same way?"
|
||||
|
||||
The maintainer may:
|
||||
|
||||
- **Confirm** — the new issue gets added to the existing file's "Prior requests" list, then closed
|
||||
- **Reconsider** — the out-of-scope file gets deleted or updated, and the issue proceeds through normal triage
|
||||
- **Disagree** — the issues are related but distinct, proceed with normal triage
|
||||
|
||||
## When to write to `.out-of-scope/`
|
||||
|
||||
Only when an **enhancement** (not a bug) is *rejected* as `wontfix`. This applies to enhancement PRs exactly as it does to issues — a rejected PR is recorded here so the same request doesn't return as fresh code.
|
||||
|
||||
Do **not** write here when something is closed as `wontfix` because it's **already implemented**. That's a built feature, not a rejected one; recording it would poison the dedup checks with false rejections. Instead, the closing comment points to where the feature already lives.
|
||||
|
||||
The flow:
|
||||
|
||||
1. Maintainer decides a feature request is out of scope
|
||||
2. Check if a matching `.out-of-scope/` file already exists
|
||||
3. If yes: append the new issue to the "Prior requests" list
|
||||
4. If no: create a new file with the concept name, decision, reason, and first prior request
|
||||
5. Post a comment on the issue explaining the decision and mentioning the `.out-of-scope/` file
|
||||
6. Close the issue with the `wontfix` label
|
||||
|
||||
## Updating or removing out-of-scope files
|
||||
|
||||
If the maintainer changes their mind about a previously rejected concept:
|
||||
|
||||
- Delete the `.out-of-scope/` file
|
||||
- The skill does not need to reopen old issues — they're historical records
|
||||
- The new issue that triggered the reconsideration proceeds through normal triage
|
||||
112
.agents/skills/triage/SKILL.md
Normal file
112
.agents/skills/triage/SKILL.md
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
name: triage
|
||||
description: Move issues and external PRs through a state machine of triage roles — categorise, verify, grill if needed, and write agent-ready briefs.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Triage
|
||||
|
||||
Move issues on the project issue tracker through a small state machine of triage roles.
|
||||
|
||||
If this repo treats external pull requests as a request surface (see the issue-tracker config), triage covers them too: **a PR is an issue with attached code** — same roles, same states, same machine, with a few deltas marked "for a PR" below. Resolve a bare `#42` to an issue or PR per the tracker config.
|
||||
|
||||
Every comment or issue posted to the issue tracker during triage **must** start with this disclaimer:
|
||||
|
||||
```
|
||||
> *This was generated by AI during triage.*
|
||||
```
|
||||
|
||||
## Reference docs
|
||||
|
||||
- [AGENT-BRIEF.md](AGENT-BRIEF.md) — how to write durable agent briefs
|
||||
- [OUT-OF-SCOPE.md](OUT-OF-SCOPE.md) — how the `.out-of-scope/` knowledge base works
|
||||
|
||||
## Roles
|
||||
|
||||
Two **category** roles:
|
||||
|
||||
- `bug` — something is broken
|
||||
- `enhancement` — new feature or improvement
|
||||
|
||||
Five **state** roles:
|
||||
|
||||
- `needs-triage` — maintainer needs to evaluate
|
||||
- `needs-info` — waiting on reporter for more information
|
||||
- `ready-for-agent` — fully specified, ready for an AFK agent
|
||||
- `ready-for-human` — needs human implementation
|
||||
- `wontfix` — will not be actioned
|
||||
|
||||
For a PR, the same states read against the attached code: `ready-for-agent` means a brief is attached and an agent should take the next step on the diff; `ready-for-human` means it's ready for a human to merge.
|
||||
|
||||
Every triaged issue should carry exactly one category role and one state role. If state roles conflict, flag it and ask the maintainer before doing anything else.
|
||||
|
||||
These are canonical role names — the actual label strings used in the issue tracker may differ. The mapping should have been provided to you - run `/setup-matt-pocock-skills` if not.
|
||||
|
||||
State transitions: an unlabeled issue normally goes to `needs-triage` first; from there it moves to `needs-info`, `ready-for-agent`, `ready-for-human`, or `wontfix`. `needs-info` returns to `needs-triage` once the reporter replies. The maintainer can override at any time — flag transitions that look unusual and ask before proceeding.
|
||||
|
||||
## Invocation
|
||||
|
||||
The maintainer invokes `/triage` and describes what they want in natural language. Interpret the request and act. Examples:
|
||||
|
||||
- "Show me anything that needs my attention"
|
||||
- "Let's look at #42" (issue or PR)
|
||||
- "Move #42 to ready-for-agent"
|
||||
- "What's ready for agents to pick up?"
|
||||
|
||||
## Show what needs attention
|
||||
|
||||
Query the issue tracker and present three buckets, oldest first:
|
||||
|
||||
1. **Unlabeled** — never triaged.
|
||||
2. **`needs-triage`** — evaluation in progress.
|
||||
3. **`needs-info` with reporter activity since the last triage notes** — needs re-evaluation.
|
||||
|
||||
When PRs are in scope, include external PRs in these buckets and tag each line `[PR]` or `[issue]`. Discovery surfaces only *external* PRs (the tracker config defines who counts as external) — a collaborator's in-flight PR is not triage work. This filter is discovery-only; an explicitly named PR is always triaged regardless of author.
|
||||
|
||||
Show counts and a one-line summary per item. Let the maintainer pick.
|
||||
|
||||
## Triage a specific issue or PR
|
||||
|
||||
1. **Gather context.** Read the full issue or PR (body, comments, labels, author, dates; for a PR, the diff too). Parse any prior triage notes so you don't re-ask resolved questions. Explore the codebase using the project's domain glossary, respecting ADRs in the area. Run two checks against the codebase: (a) **redundancy** — search for an existing implementation of the requested behavior by domain concept (not just the request's wording), and report where you looked. If found, it's an already-implemented `wontfix` (step 5). (b) **prior rejection** — read `.out-of-scope/*.md` and surface any that resembles this request.
|
||||
|
||||
2. **Recommend.** Tell the maintainer your category and state recommendation with reasoning, plus a brief codebase summary relevant to the request — including whether it's already implemented. Wait for direction.
|
||||
|
||||
3. **Verify the claim.** Before any grilling, check that the claim holds up. For a bug, reproduce it from the reporter's steps. For a PR, confirm the diff does what it claims — check it out, run the relevant tests or commands. Report what happened: confirmed (with code path), failed, or insufficient detail (a strong `needs-info` signal). A confirmed verification makes a much stronger agent brief.
|
||||
|
||||
4. **Grill (if needed).** If the request needs fleshing out, run the `/grilling` and `/domain-modeling` skills together — grill it into shape one question at a time, sharpening domain terms and updating `CONTEXT.md`/ADRs inline as decisions land.
|
||||
|
||||
5. **Apply the outcome:**
|
||||
- `ready-for-agent` — post an agent brief comment ([AGENT-BRIEF.md](AGENT-BRIEF.md)).
|
||||
- `ready-for-human` — same structure as an agent brief, but note why it can't be delegated (judgment calls, external access, design decisions, manual testing).
|
||||
- `needs-info` — post triage notes (template below).
|
||||
- `wontfix` — close, with the comment depending on *why*:
|
||||
- **Already implemented** — the change already exists in the codebase. Point to where it lives; do **not** write to `.out-of-scope/` (that KB is for *rejected* requests, not built ones).
|
||||
- **Rejected (bug)** — polite explanation, then close.
|
||||
- **Rejected (enhancement)** — write to `.out-of-scope/`, link to it from a comment, then close ([OUT-OF-SCOPE.md](OUT-OF-SCOPE.md)).
|
||||
- `needs-triage` — apply the role. Optional comment if there's partial progress.
|
||||
|
||||
## Quick state override
|
||||
|
||||
If the maintainer says "move #42 to ready-for-agent", trust them and apply the role directly. Confirm what you're about to do (role changes, comment, close), then act. Skip grilling. If moving to `ready-for-agent` without a grilling session, ask whether they want to write an agent brief.
|
||||
|
||||
## Needs-info template
|
||||
|
||||
```markdown
|
||||
## Triage Notes
|
||||
|
||||
**What we've established so far:**
|
||||
|
||||
- point 1
|
||||
- point 2
|
||||
|
||||
**What we still need from you (@reporter):**
|
||||
|
||||
- question 1
|
||||
- question 2
|
||||
```
|
||||
|
||||
Capture everything resolved during grilling under "established so far" so the work isn't lost. Questions must be specific and actionable, not "please provide more info".
|
||||
|
||||
## Resuming a previous session
|
||||
|
||||
If prior triage notes exist on the issue or PR, read them, check whether the reporter has answered any outstanding questions, and present an updated picture before continuing. Don't re-ask resolved questions.
|
||||
117
.agents/skills/write-a-skill/SKILL.md
Normal file
117
.agents/skills/write-a-skill/SKILL.md
Normal file
@@ -0,0 +1,117 @@
|
||||
---
|
||||
name: write-a-skill
|
||||
description: Create new agent skills with proper structure, progressive disclosure, and bundled resources. Use when user wants to create, write, or build a new skill.
|
||||
---
|
||||
|
||||
# Writing Skills
|
||||
|
||||
## Process
|
||||
|
||||
1. **Gather requirements** - ask user about:
|
||||
- What task/domain does the skill cover?
|
||||
- What specific use cases should it handle?
|
||||
- Does it need executable scripts or just instructions?
|
||||
- Any reference materials to include?
|
||||
|
||||
2. **Draft the skill** - create:
|
||||
- SKILL.md with concise instructions
|
||||
- Additional reference files if content exceeds 500 lines
|
||||
- Utility scripts if deterministic operations needed
|
||||
|
||||
3. **Review with user** - present draft and ask:
|
||||
- Does this cover your use cases?
|
||||
- Anything missing or unclear?
|
||||
- Should any section be more/less detailed?
|
||||
|
||||
## Skill Structure
|
||||
|
||||
```
|
||||
skill-name/
|
||||
├── SKILL.md # Main instructions (required)
|
||||
├── REFERENCE.md # Detailed docs (if needed)
|
||||
├── EXAMPLES.md # Usage examples (if needed)
|
||||
└── scripts/ # Utility scripts (if needed)
|
||||
└── helper.js
|
||||
```
|
||||
|
||||
## SKILL.md Template
|
||||
|
||||
```md
|
||||
---
|
||||
name: skill-name
|
||||
description: Brief description of capability. Use when [specific triggers].
|
||||
---
|
||||
|
||||
# Skill Name
|
||||
|
||||
## Quick start
|
||||
|
||||
[Minimal working example]
|
||||
|
||||
## Workflows
|
||||
|
||||
[Step-by-step processes with checklists for complex tasks]
|
||||
|
||||
## Advanced features
|
||||
|
||||
[Link to separate files: See [REFERENCE.md](REFERENCE.md)]
|
||||
```
|
||||
|
||||
## Description Requirements
|
||||
|
||||
The description is **the only thing your agent sees** when deciding which skill to load. It's surfaced in the system prompt alongside all other installed skills. Your agent reads these descriptions and picks the relevant skill based on the user's request.
|
||||
|
||||
**Goal**: Give your agent just enough info to know:
|
||||
|
||||
1. What capability this skill provides
|
||||
2. When/why to trigger it (specific keywords, contexts, file types)
|
||||
|
||||
**Format**:
|
||||
|
||||
- Max 1024 chars
|
||||
- Write in third person
|
||||
- First sentence: what it does
|
||||
- Second sentence: "Use when [specific triggers]"
|
||||
|
||||
**Good example**:
|
||||
|
||||
```
|
||||
Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when user mentions PDFs, forms, or document extraction.
|
||||
```
|
||||
|
||||
**Bad example**:
|
||||
|
||||
```
|
||||
Helps with documents.
|
||||
```
|
||||
|
||||
The bad example gives your agent no way to distinguish this from other document skills.
|
||||
|
||||
## When to Add Scripts
|
||||
|
||||
Add utility scripts when:
|
||||
|
||||
- Operation is deterministic (validation, formatting)
|
||||
- Same code would be generated repeatedly
|
||||
- Errors need explicit handling
|
||||
|
||||
Scripts save tokens and improve reliability vs generated code.
|
||||
|
||||
## When to Split Files
|
||||
|
||||
Split into separate files when:
|
||||
|
||||
- SKILL.md exceeds 100 lines
|
||||
- Content has distinct domains (finance vs sales schemas)
|
||||
- Advanced features are rarely needed
|
||||
|
||||
## Review Checklist
|
||||
|
||||
After drafting, verify:
|
||||
|
||||
- [ ] Description includes triggers ("Use when...")
|
||||
- [ ] SKILL.md under 100 lines
|
||||
- [ ] No time-sensitive info
|
||||
- [ ] Consistent terminology
|
||||
- [ ] Concrete examples included
|
||||
- [ ] References one level deep
|
||||
7
.agents/skills/zoom-out/SKILL.md
Normal file
7
.agents/skills/zoom-out/SKILL.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
name: zoom-out
|
||||
description: Tell the agent to zoom out and give broader context or a higher-level perspective. Use when you're unfamiliar with a section of code or need to understand how it fits into the bigger picture.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
I don't know this area of code well. Go up a layer of abstraction. Give me a map of all the relevant modules and callers, using the project's domain glossary vocabulary.
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
name: OpenSpec: Apply
|
||||
description: Implement an approved OpenSpec change and keep tasks in sync.
|
||||
category: OpenSpec
|
||||
tags: [openspec, apply]
|
||||
---
|
||||
<!-- OPENSPEC:START -->
|
||||
**Guardrails**
|
||||
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
||||
- Keep changes tightly scoped to the requested outcome.
|
||||
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
||||
|
||||
**Steps**
|
||||
Track these steps as TODOs and complete them one by one.
|
||||
1. Read `changes/<id>/proposal.md`, `design.md` (if present), and `tasks.md` to confirm scope and acceptance criteria.
|
||||
2. Work through tasks sequentially, keeping edits minimal and focused on the requested change.
|
||||
3. Confirm completion before updating statuses—make sure every item in `tasks.md` is finished.
|
||||
4. Update the checklist after all work is done so each task is marked `- [x]` and reflects reality.
|
||||
5. Reference `openspec list` or `openspec show <item>` when additional context is required.
|
||||
|
||||
**Reference**
|
||||
- Use `openspec show <id> --json --deltas-only` if you need additional context from the proposal while implementing.
|
||||
<!-- OPENSPEC:END -->
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: OpenSpec: Archive
|
||||
description: Archive a deployed OpenSpec change and update specs.
|
||||
category: OpenSpec
|
||||
tags: [openspec, archive]
|
||||
---
|
||||
<!-- OPENSPEC:START -->
|
||||
**Guardrails**
|
||||
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
||||
- Keep changes tightly scoped to the requested outcome.
|
||||
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
||||
|
||||
**Steps**
|
||||
1. Determine the change ID to archive:
|
||||
- If this prompt already includes a specific change ID (for example inside a `<ChangeId>` block populated by slash-command arguments), use that value after trimming whitespace.
|
||||
- If the conversation references a change loosely (for example by title or summary), run `openspec list` to surface likely IDs, share the relevant candidates, and confirm which one the user intends.
|
||||
- Otherwise, review the conversation, run `openspec list`, and ask the user which change to archive; wait for a confirmed change ID before proceeding.
|
||||
- If you still cannot identify a single change ID, stop and tell the user you cannot archive anything yet.
|
||||
2. Validate the change ID by running `openspec list` (or `openspec show <id>`) and stop if the change is missing, already archived, or otherwise not ready to archive.
|
||||
3. Run `openspec archive <id> --yes` so the CLI moves the change and applies spec updates without prompts (use `--skip-specs` only for tooling-only work).
|
||||
4. Review the command output to confirm the target specs were updated and the change landed in `changes/archive/`.
|
||||
5. Validate with `openspec validate --strict` and inspect with `openspec show <id>` if anything looks off.
|
||||
|
||||
**Reference**
|
||||
- Use `openspec list` to confirm change IDs before archiving.
|
||||
- Inspect refreshed specs with `openspec list --specs` and address any validation issues before handing off.
|
||||
<!-- OPENSPEC:END -->
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
name: OpenSpec: Proposal
|
||||
description: Scaffold a new OpenSpec change and validate strictly.
|
||||
category: OpenSpec
|
||||
tags: [openspec, change]
|
||||
---
|
||||
<!-- OPENSPEC:START -->
|
||||
**Guardrails**
|
||||
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
||||
- Keep changes tightly scoped to the requested outcome.
|
||||
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
||||
- Identify any vague or ambiguous details and ask the necessary follow-up questions before editing files.
|
||||
- Do not write any code during the proposal stage. Only create design documents (proposal.md, tasks.md, design.md, and spec deltas). Implementation happens in the apply stage after approval.
|
||||
|
||||
**Steps**
|
||||
1. Review `openspec/project.md`, run `openspec list` and `openspec list --specs`, and inspect related code or docs (e.g., via `rg`/`ls`) to ground the proposal in current behaviour; note any gaps that require clarification.
|
||||
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, and `design.md` (when needed) under `openspec/changes/<id>/`.
|
||||
3. Map the change into concrete capabilities or requirements, breaking multi-scope efforts into distinct spec deltas with clear relationships and sequencing.
|
||||
4. Capture architectural reasoning in `design.md` when the solution spans multiple systems, introduces new patterns, or demands trade-off discussion before committing to specs.
|
||||
5. Draft spec deltas in `changes/<id>/specs/<capability>/spec.md` (one folder per capability) using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement and cross-reference related capabilities when relevant.
|
||||
6. Draft `tasks.md` as an ordered list of small, verifiable work items that deliver user-visible progress, include validation (tests, tooling), and highlight dependencies or parallelizable work.
|
||||
7. Validate with `openspec validate <id> --strict` and resolve every issue before sharing the proposal.
|
||||
|
||||
**Reference**
|
||||
- Use `openspec show <id> --json --deltas-only` or `openspec show <spec> --type spec` to inspect details when validation fails.
|
||||
- Search existing requirements with `rg -n "Requirement:|Scenario:" openspec/specs` before writing new ones.
|
||||
- Explore the codebase with `rg <keyword>`, `ls`, or direct file reads so proposals align with current implementation realities.
|
||||
<!-- OPENSPEC:END -->
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: OPSX: Apply
|
||||
name: "OPSX: Apply"
|
||||
description: Implement tasks from an OpenSpec change (Experimental)
|
||||
category: Workflow
|
||||
tags: [workflow, artifacts, experimental]
|
||||
@@ -7,26 +7,25 @@ tags: [workflow, artifacts, experimental]
|
||||
|
||||
Implement tasks from an OpenSpec change.
|
||||
|
||||
**Input**: Optionally specify `--change <name>` after `/opsx:apply`. If omitted, MUST prompt for available changes.
|
||||
**Input**: Optionally specify a change name (e.g., `/opsx:apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
1. **Select the change**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
||||
|
||||
Show changes that are implementation-ready (have tasks artifact).
|
||||
Include the schema used for each change if available.
|
||||
Mark changes with incomplete tasks as "(In Progress)".
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:apply <other>`).
|
||||
|
||||
2. **Check status to understand the schema**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used (e.g., "spec-driven", "tdd")
|
||||
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
||||
- Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
|
||||
|
||||
3. **Get apply instructions**
|
||||
@@ -51,7 +50,6 @@ Implement tasks from an OpenSpec change.
|
||||
Read the files listed in `contextFiles` from the apply instructions output.
|
||||
The files depend on the schema being used:
|
||||
- **spec-driven**: proposal, specs, design, tasks
|
||||
- **tdd**: spec, tests, implementation, docs
|
||||
- Other schemas: follow the contextFiles from CLI output
|
||||
|
||||
5. **Show current progress**
|
||||
@@ -113,7 +111,7 @@ Working on task 4/7: <task description>
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tasks complete! Ready to archive this change.
|
||||
All tasks complete! You can archive this change with `/opsx:archive`.
|
||||
```
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: OPSX: Archive
|
||||
name: "OPSX: Archive"
|
||||
description: Archive a completed change in the experimental workflow
|
||||
category: Workflow
|
||||
tags: [workflow, archive, experimental]
|
||||
@@ -7,7 +7,7 @@ tags: [workflow, archive, experimental]
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
**Input**: Optionally specify `--change <name>` after `/opsx:archive`. If omitted, MUST prompt for available changes.
|
||||
**Input**: Optionally specify a change name after `/opsx:archive` (e.g., `/opsx:archive add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
@@ -46,38 +46,20 @@ Archive a completed change in the experimental workflow.
|
||||
|
||||
**If no tasks file exists:** Proceed without task-related warning.
|
||||
|
||||
4. **Check if delta specs need syncing**
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
Check if `specs/` directory exists in the change with spec files.
|
||||
Check for delta specs at `openspec/changes/<name>/specs/`. If none exist, proceed without sync prompt.
|
||||
|
||||
**If delta specs exist, perform a quick sync check:**
|
||||
**If delta specs exist:**
|
||||
- Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`
|
||||
- Determine what changes would be applied (adds, modifications, removals, renames)
|
||||
- Show a combined summary before prompting
|
||||
|
||||
a. **For each delta spec** at `openspec/changes/<name>/specs/<capability>/spec.md`:
|
||||
- Extract requirement names (lines matching `### Requirement: <name>`)
|
||||
- Note which sections exist (ADDED, MODIFIED, REMOVED)
|
||||
**Prompt options:**
|
||||
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
||||
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
||||
|
||||
b. **Check corresponding main spec** at `openspec/specs/<capability>/spec.md`:
|
||||
- If main spec doesn't exist → needs sync
|
||||
- If main spec exists, check if ADDED requirement names appear in it
|
||||
- If any ADDED requirements are missing from main spec → needs sync
|
||||
|
||||
c. **Report findings:**
|
||||
|
||||
**If sync needed:**
|
||||
```
|
||||
⚠️ Delta specs may not be synced:
|
||||
- specs/auth/spec.md → Main spec missing requirement "Token Refresh"
|
||||
- specs/api/spec.md → Main spec doesn't exist yet
|
||||
|
||||
Would you like to sync now before archiving?
|
||||
```
|
||||
- Use **AskUserQuestion tool** with options: "Sync now", "Archive without syncing"
|
||||
- If user chooses sync, execute `/opsx:sync` logic
|
||||
|
||||
**If already synced (all requirements found):**
|
||||
- Proceed without prompting (specs appear to be in sync)
|
||||
|
||||
**If no delta specs exist:** Proceed without sync-related checks.
|
||||
If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>"). Proceed to archive regardless of choice.
|
||||
|
||||
5. **Perform the archive**
|
||||
|
||||
@@ -102,7 +84,7 @@ Archive a completed change in the experimental workflow.
|
||||
- Change name
|
||||
- Schema that was used
|
||||
- Archive location
|
||||
- Spec sync status (synced / not synced / no delta specs)
|
||||
- Spec sync status (synced / sync skipped / no delta specs)
|
||||
- Note about any warnings (incomplete artifacts/tasks)
|
||||
|
||||
**Output On Success**
|
||||
@@ -139,12 +121,12 @@ All artifacts complete. All tasks complete.
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** ⚠️ Not synced
|
||||
**Specs:** Sync skipped (user chose to skip)
|
||||
|
||||
**Warnings:**
|
||||
- Archived with 2 incomplete artifacts
|
||||
- Archived with 3 incomplete tasks
|
||||
- Delta specs were not synced (user chose to skip)
|
||||
- Delta spec sync was skipped (user chose to skip)
|
||||
|
||||
Review the archive if this was not intentional.
|
||||
```
|
||||
@@ -170,6 +152,6 @@ Target archive directory already exists.
|
||||
- Use artifact graph (openspec status --json) for completion checking
|
||||
- Don't block archive on warnings - just inform and confirm
|
||||
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
||||
- Quick sync check: look for requirement names in delta specs, verify they exist in main specs
|
||||
- Show clear summary of what happened
|
||||
- If sync is requested, use /opsx:sync approach (agent-driven)
|
||||
- If sync is requested, use the Skill tool to invoke `openspec-sync-specs` (agent-driven)
|
||||
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
||||
|
||||
173
.claude/commands/opsx/explore.md
Normal file
173
.claude/commands/opsx/explore.md
Normal file
@@ -0,0 +1,173 @@
|
||||
---
|
||||
name: "OPSX: Explore"
|
||||
description: "Enter explore mode - think through ideas, investigate problems, clarify requirements"
|
||||
category: Workflow
|
||||
tags: [workflow, explore, experimental, thinking]
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
|
||||
|
||||
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
||||
|
||||
**Input**: The argument after `/opsx:explore` is whatever the user wants to think about. Could be:
|
||||
- A vague idea: "real-time collaboration"
|
||||
- A specific problem: "the auth system is getting unwieldy"
|
||||
- A change name: "add-dark-mode" (to explore in context of that change)
|
||||
- A comparison: "postgres vs sqlite for this"
|
||||
- Nothing (just enter explore mode)
|
||||
|
||||
---
|
||||
|
||||
## The Stance
|
||||
|
||||
- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script
|
||||
- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.
|
||||
- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking
|
||||
- **Adaptive** - Follow interesting threads, pivot when new information emerges
|
||||
- **Patient** - Don't rush to conclusions, let the shape of the problem emerge
|
||||
- **Grounded** - Explore the actual codebase when relevant, don't just theorize
|
||||
|
||||
---
|
||||
|
||||
## What You Might Do
|
||||
|
||||
Depending on what the user brings, you might:
|
||||
|
||||
**Explore the problem space**
|
||||
- Ask clarifying questions that emerge from what they said
|
||||
- Challenge assumptions
|
||||
- Reframe the problem
|
||||
- Find analogies
|
||||
|
||||
**Investigate the codebase**
|
||||
- Map existing architecture relevant to the discussion
|
||||
- Find integration points
|
||||
- Identify patterns already in use
|
||||
- Surface hidden complexity
|
||||
|
||||
**Compare options**
|
||||
- Brainstorm multiple approaches
|
||||
- Build comparison tables
|
||||
- Sketch tradeoffs
|
||||
- Recommend a path (if asked)
|
||||
|
||||
**Visualize**
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Use ASCII diagrams liberally │
|
||||
├─────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────┐ ┌────────┐ │
|
||||
│ │ State │────────▶│ State │ │
|
||||
│ │ A │ │ B │ │
|
||||
│ └────────┘ └────────┘ │
|
||||
│ │
|
||||
│ System diagrams, state machines, │
|
||||
│ data flows, architecture sketches, │
|
||||
│ dependency graphs, comparison tables │
|
||||
│ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Surface risks and unknowns**
|
||||
- Identify what could go wrong
|
||||
- Find gaps in understanding
|
||||
- Suggest spikes or investigations
|
||||
|
||||
---
|
||||
|
||||
## OpenSpec Awareness
|
||||
|
||||
You have full context of the OpenSpec system. Use it naturally, don't force it.
|
||||
|
||||
### Check for context
|
||||
|
||||
At the start, quickly check what exists:
|
||||
```bash
|
||||
openspec list --json
|
||||
```
|
||||
|
||||
This tells you:
|
||||
- If there are active changes
|
||||
- Their names, schemas, and status
|
||||
- What the user might be working on
|
||||
|
||||
If the user mentioned a specific change name, read its artifacts for context.
|
||||
|
||||
### When no change exists
|
||||
|
||||
Think freely. When insights crystallize, you might offer:
|
||||
|
||||
- "This feels solid enough to start a change. Want me to create a proposal?"
|
||||
- Or keep exploring - no pressure to formalize
|
||||
|
||||
### When a change exists
|
||||
|
||||
If the user mentions a change or you detect one is relevant:
|
||||
|
||||
1. **Read existing artifacts for context**
|
||||
- `openspec/changes/<name>/proposal.md`
|
||||
- `openspec/changes/<name>/design.md`
|
||||
- `openspec/changes/<name>/tasks.md`
|
||||
- etc.
|
||||
|
||||
2. **Reference them naturally in conversation**
|
||||
- "Your design mentions using Redis, but we just realized SQLite fits better..."
|
||||
- "The proposal scopes this to premium users, but we're now thinking everyone..."
|
||||
|
||||
3. **Offer to capture when decisions are made**
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|--------------|------------------|
|
||||
| New requirement discovered | `specs/<capability>/spec.md` |
|
||||
| Requirement changed | `specs/<capability>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
- "This is a new requirement. Add it to specs?"
|
||||
- "This changes scope. Update the proposal?"
|
||||
|
||||
4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.
|
||||
|
||||
---
|
||||
|
||||
## What You Don't Have To Do
|
||||
|
||||
- Follow a script
|
||||
- Ask the same questions every time
|
||||
- Produce a specific artifact
|
||||
- Reach a conclusion
|
||||
- Stay on topic if a tangent is valuable
|
||||
- Be brief (this is thinking time)
|
||||
|
||||
---
|
||||
|
||||
## Ending Discovery
|
||||
|
||||
There's no required ending. Discovery might:
|
||||
|
||||
- **Flow into a proposal**: "Ready to start? I can create a change proposal."
|
||||
- **Result in artifact updates**: "Updated design.md with these decisions"
|
||||
- **Just provide clarity**: User has what they need, moves on
|
||||
- **Continue later**: "We can pick this up anytime"
|
||||
|
||||
When things crystallize, you might offer a summary - but it's optional. Sometimes the thinking IS the value.
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.
|
||||
- **Don't fake understanding** - If something is unclear, dig deeper
|
||||
- **Don't rush** - Discovery is thinking time, not task time
|
||||
- **Don't force structure** - Let patterns emerge naturally
|
||||
- **Don't auto-capture** - Offer to save insights, don't just do it
|
||||
- **Do visualize** - A good diagram is worth many paragraphs
|
||||
- **Do explore the codebase** - Ground discussions in reality
|
||||
- **Do question assumptions** - Including the user's and your own
|
||||
106
.claude/commands/opsx/propose.md
Normal file
106
.claude/commands/opsx/propose.md
Normal file
@@ -0,0 +1,106 @@
|
||||
---
|
||||
name: "OPSX: Propose"
|
||||
description: Propose a new change - create it and generate all artifacts in one step
|
||||
category: Workflow
|
||||
tags: [workflow, artifacts, experimental]
|
||||
---
|
||||
|
||||
Propose a new change - create the change and generate all artifacts in one step.
|
||||
|
||||
I'll create a change with artifacts:
|
||||
- proposal.md (what & why)
|
||||
- design.md (how)
|
||||
- tasks.md (implementation steps)
|
||||
|
||||
When ready to implement, run /opsx:apply
|
||||
|
||||
---
|
||||
|
||||
**Input**: The argument after `/opsx:propose` is the change name (kebab-case), OR a description of what the user wants to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no input provided, ask what they want to build**
|
||||
|
||||
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
2. **Create the change directory**
|
||||
```bash
|
||||
openspec new change "<name>"
|
||||
```
|
||||
This creates a scaffolded change at `openspec/changes/<name>/` with `.openspec.yaml`.
|
||||
|
||||
3. **Get the artifact build order**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to get:
|
||||
- `applyRequires`: array of artifact IDs needed before implementation (e.g., `["tasks"]`)
|
||||
- `artifacts`: list of all artifacts with their status and dependencies
|
||||
|
||||
4. **Create artifacts in sequence until apply-ready**
|
||||
|
||||
Use the **TodoWrite tool** to track progress through the artifacts.
|
||||
|
||||
Loop through artifacts in dependency order (artifacts with no pending dependencies first):
|
||||
|
||||
a. **For each artifact that is `ready` (dependencies satisfied)**:
|
||||
- Get instructions:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- The instructions JSON includes:
|
||||
- `context`: Project background (constraints for you - do NOT include in output)
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance for this artifact type
|
||||
- `outputPath`: Where to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- Read any completed dependency files for context
|
||||
- Create the artifact file using `template` as the structure
|
||||
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
||||
- Show brief progress: "Created <artifact-id>"
|
||||
|
||||
b. **Continue until all `applyRequires` artifacts are complete**
|
||||
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
||||
- Check if every artifact ID in `applyRequires` has `status: "done"` in the artifacts array
|
||||
- Stop when all `applyRequires` artifacts are done
|
||||
|
||||
c. **If an artifact requires user input** (unclear context):
|
||||
- Use **AskUserQuestion tool** to clarify
|
||||
- Then continue with creation
|
||||
|
||||
5. **Show final status**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
|
||||
**Output**
|
||||
|
||||
After completing all artifacts, summarize:
|
||||
- Change name and location
|
||||
- List of artifacts created with brief descriptions
|
||||
- What's ready: "All artifacts created! Ready for implementation."
|
||||
- Prompt: "Run `/opsx:apply` to start implementing."
|
||||
|
||||
**Artifact Creation Guidelines**
|
||||
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type
|
||||
- The schema defines what each artifact should contain - follow it
|
||||
- Read dependency artifacts for context before creating new ones
|
||||
- Use `template` as the structure for your output file - fill in its sections
|
||||
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
||||
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
||||
- These guide what you write, but should never appear in the output
|
||||
|
||||
**Guardrails**
|
||||
- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)
|
||||
- Always read dependency artifacts before creating a new one
|
||||
- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum
|
||||
- If a change with that name already exists, ask if user wants to continue it or create a new one
|
||||
- Verify each artifact file exists after writing before proceeding to next
|
||||
5
.claude/settings.json
Normal file
5
.claude/settings.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"enabledPlugins": {
|
||||
"ralph-loop@claude-plugins-official": true
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: api-routing
|
||||
description: API 路由注册规范。注册新 API 路由时使用。包含 Register() 函数用法、RouteSpec 必填项等规范。
|
||||
description: API 路由注册规范。注册新 API 路由、添加新 Handler 时使用。包含 Register() 函数用法、RouteSpec 必填项、文档生成器更新等规范。
|
||||
---
|
||||
|
||||
# API 路由注册规范
|
||||
@@ -12,7 +12,29 @@ description: API 路由注册规范。注册新 API 路由时使用。包含 Reg
|
||||
在以下情况下必须遵守本规范:
|
||||
- 注册新的 API 路由
|
||||
- 修改现有路由配置
|
||||
- 添加新的 Handler 函数
|
||||
- **添加新的 Handler(必须同步更新文档生成器!)**
|
||||
|
||||
## 新增 Handler 检查清单(⚠️ 最容易遗漏)
|
||||
|
||||
新增 Handler 时,必须完成以下 **4 个步骤**,否则接口不会出现在 OpenAPI 文档中:
|
||||
|
||||
| 步骤 | 文件 | 操作 |
|
||||
|------|------|------|
|
||||
| 1️⃣ | `internal/bootstrap/types.go` | 添加 Handler 字段 |
|
||||
| 2️⃣ | `internal/bootstrap/handlers.go` | 实例化 Handler |
|
||||
| 3️⃣ | `internal/routes/admin.go` | 调用路由注册函数 |
|
||||
| 4️⃣ | `cmd/api/docs.go` + `cmd/gendocs/main.go` | **添加到文档生成器** |
|
||||
|
||||
### 步骤 4 详解(最常遗漏!)
|
||||
|
||||
```go
|
||||
// cmd/api/docs.go 和 cmd/gendocs/main.go 都要改!
|
||||
handlers := &bootstrap.Handlers{
|
||||
// ... 现有 Handler
|
||||
IotCard: admin.NewIotCardHandler(nil), // 添加
|
||||
IotCardImport: admin.NewIotCardImportHandler(nil), // 添加
|
||||
}
|
||||
```
|
||||
|
||||
## 核心规则
|
||||
|
||||
@@ -108,13 +130,22 @@ Register(router, doc, basePath, "GET", "/health", handler.Health, RouteSpec{
|
||||
|
||||
## AI 助手检查清单
|
||||
|
||||
注册路由后必须检查:
|
||||
### 注册路由时
|
||||
|
||||
1. ✅ 是否使用 `Register()` 函数而非直接注册
|
||||
2. ✅ `Summary` 是否使用中文简短描述
|
||||
3. ✅ `Tags` 是否正确分组
|
||||
4. ✅ `Input` 和 `Output` 是否指向正确的 DTO
|
||||
5. ✅ `Auth` 是否根据业务需求正确设置
|
||||
|
||||
### 新增 Handler 时(⚠️ 必查)
|
||||
|
||||
1. ✅ `internal/bootstrap/types.go` 添加了 Handler 字段
|
||||
2. ✅ `internal/bootstrap/handlers.go` 实例化了 Handler
|
||||
3. ✅ `internal/routes/admin.go` 调用了路由注册函数
|
||||
4. ✅ **`cmd/api/docs.go` 添加了 Handler**
|
||||
5. ✅ **`cmd/gendocs/main.go` 添加了 Handler**
|
||||
6. ✅ 运行 `go run cmd/gendocs/main.go` 验证文档生成
|
||||
7. ✅ 运行 `grep "接口路径" docs/admin-openapi.yaml` 确认接口存在
|
||||
|
||||
**完整指南**: 参见 [`docs/api-documentation-guide.md`](docs/api-documentation-guide.md)
|
||||
|
||||
1
.claude/skills/caveman
Symbolic link
1
.claude/skills/caveman
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/caveman
|
||||
139
.claude/skills/comment-standards/SKILL.md
Normal file
139
.claude/skills/comment-standards/SKILL.md
Normal file
@@ -0,0 +1,139 @@
|
||||
---
|
||||
name: comment-standards
|
||||
description: Go 注释规范。编写 Go 代码注释、文档注释时使用。包含包注释、结构体注释、接口注释、函数注释、内联注释的完整规范与示例。
|
||||
---
|
||||
|
||||
# Go 注释规范
|
||||
|
||||
**基本原则**:
|
||||
- **所有注释使用中文**
|
||||
- **导出符号必须有文档注释**(包、函数、方法、类型、接口、常量、变量)
|
||||
- **复杂逻辑必须有实现注释**(解释"为什么",而不是"做了什么")
|
||||
- **禁止废话注释**(不要用注释复述代码本身)
|
||||
- **修改代码时必须同步更新注释**
|
||||
|
||||
---
|
||||
|
||||
## 包注释
|
||||
|
||||
每个包的入口文件(通常是主文件或 `doc.go`)必须有包注释:
|
||||
|
||||
```go
|
||||
// Package account 提供账号管理的业务逻辑服务
|
||||
// 包含账号创建、修改、删除、权限分配等功能
|
||||
package account
|
||||
```
|
||||
|
||||
## 结构体注释
|
||||
|
||||
所有导出结构体必须有文档注释,说明该结构体代表什么:
|
||||
|
||||
```go
|
||||
// Service 账号业务服务
|
||||
// 负责账号的 CRUD、角色分配、密码管理等业务逻辑
|
||||
type Service struct {
|
||||
store *Store
|
||||
auditService AuditServiceInterface
|
||||
}
|
||||
```
|
||||
|
||||
## 接口注释
|
||||
|
||||
导出接口必须注释接口用途,每个方法必须说明契约:
|
||||
|
||||
```go
|
||||
// PermissionChecker 权限检查器接口
|
||||
// 用于查询用户的权限列表
|
||||
type PermissionChecker interface {
|
||||
// CheckPermission 检查用户是否拥有指定权限
|
||||
// userID: 用户ID
|
||||
// permCode: 权限编码(格式: module:action)
|
||||
// platform: 端口类型 (all/web/h5)
|
||||
CheckPermission(ctx context.Context, userID uint, permCode string, platform string) (bool, error)
|
||||
}
|
||||
```
|
||||
|
||||
## 函数和方法注释
|
||||
|
||||
**导出函数/方法**必须以函数名开头,说明功能:
|
||||
|
||||
```go
|
||||
// Create 创建账号
|
||||
// POST /api/admin/accounts
|
||||
func (h *AccountHandler) Create(c *fiber.Ctx) error {
|
||||
```
|
||||
|
||||
**复杂方法**(超过 30 行或包含复杂业务逻辑)必须额外说明实现思路:
|
||||
|
||||
```go
|
||||
// ActivateByRealname 首次实名激活套餐
|
||||
// 当用户完成实名认证后,自动激活处于"囤货待实名"状态的套餐:
|
||||
// 1. 查找该卡所有 status=3(待实名激活)的套餐
|
||||
// 2. 按创建时间排序,第一个主套餐立即激活(status=1)
|
||||
// 3. 其余主套餐进入排队状态(status=4)
|
||||
// 4. 加油包如果绑定了已激活的主套餐则一并激活
|
||||
func (s *UsageService) ActivateByRealname(ctx context.Context, cardID uint) error {
|
||||
```
|
||||
|
||||
**未导出函数/方法**:
|
||||
- 简单逻辑(< 15 行):可以不加注释
|
||||
- 复杂逻辑(≥ 15 行)或非显而易见的算法:必须加注释
|
||||
|
||||
```go
|
||||
// buildPermissionTree 递归构建权限树
|
||||
// 采用 map 索引 + 单次遍历算法,时间复杂度 O(n)
|
||||
func (s *Service) buildPermissionTree(permissions []*model.Permission) []*dto.PermissionTreeNode {
|
||||
```
|
||||
|
||||
## 常量和枚举注释
|
||||
|
||||
分组常量必须有组注释,每个值必须有行内注释:
|
||||
|
||||
```go
|
||||
// 用户类型常量
|
||||
const (
|
||||
UserTypeSuperAdmin = 1 // 超级管理员
|
||||
UserTypePlatform = 2 // 平台用户
|
||||
UserTypeAgent = 3 // 代理账号
|
||||
UserTypeEnterprise = 4 // 企业账号
|
||||
)
|
||||
```
|
||||
|
||||
## 内联注释规范
|
||||
|
||||
**必须添加内联注释的场景**:
|
||||
|
||||
| 场景 | 要求 |
|
||||
|------|------|
|
||||
| 复杂条件判断 | 解释判断的业务含义 |
|
||||
| 多步骤业务流程 | 用编号注释标明每一步 |
|
||||
| 非显而易见的设计决策 | 解释"为什么这样做"而不是"做了什么" |
|
||||
| 缓存/事务/并发处理 | 说明策略和原因 |
|
||||
| 临时方案/兼容逻辑 | 标注 TODO 或说明背景 |
|
||||
|
||||
**✅ 好的内联注释(解释为什么)**:
|
||||
|
||||
```go
|
||||
// 使用 Redis 分布式锁防止并发重复创建,锁超时 10 秒
|
||||
if !s.acquireLock(ctx, lockKey, 10*time.Second) {
|
||||
return errors.New(errors.CodeTooManyRequests, "操作过于频繁,请稍后重试")
|
||||
}
|
||||
|
||||
// 先冻结佣金再扣款,保证资金安全(失败时佣金自动解冻)
|
||||
if err := s.freezeCommission(ctx, tx, orderID); err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
**❌ 废话注释(禁止)**:
|
||||
|
||||
```go
|
||||
// 获取用户ID ← 禁止:代码本身已经很清楚
|
||||
userID := middleware.GetUserIDFromContext(ctx)
|
||||
|
||||
// 创建账号 ← 禁止:变量名已说明意图
|
||||
account := &model.Account{}
|
||||
|
||||
// 返回错误 ← 禁止:return err 不需要注释
|
||||
return err
|
||||
```
|
||||
1
.claude/skills/diagnose
Symbolic link
1
.claude/skills/diagnose
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/diagnose
|
||||
@@ -14,6 +14,8 @@ description: DTO 数据传输对象规范。创建或修改 DTO 文件、请求/
|
||||
- 创建 `XXXRequest`、`XXXResponse`、`XXXReq`、`XXXResp` 结构体
|
||||
- 添加或修改 API 接口的输入输出参数
|
||||
|
||||
---
|
||||
|
||||
## 必须项(MUST)
|
||||
|
||||
### 1. Description 标签规范
|
||||
@@ -36,59 +38,150 @@ type CreateUserRequest struct {
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 枚举字段必须列出所有可能值(中文)
|
||||
### 2. 枚举字段:int vs string 选择
|
||||
|
||||
**所有枚举类型字段必须在 `description` 中列出所有可能值和对应的中文含义**
|
||||
**必须按以下规则选择类型,禁止混用:**
|
||||
|
||||
| 场景 | 类型 | 示例 |
|
||||
|------|------|------|
|
||||
| 状态类(生命周期阶段) | `int` | 待支付→已完成→已关闭 |
|
||||
| 布尔状态(启用/禁用) | `int` | `0=禁用, 1=启用` |
|
||||
| 类型/方式类(种类) | `string` | `"wechat"`, `"single_card"` |
|
||||
| 平台/标识符类 | `string` | `"web"`, `"h5"`, `"all"` |
|
||||
|
||||
```go
|
||||
// 用户类型
|
||||
UserType int `json:"user_type" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"`
|
||||
// ✅ 状态 → int
|
||||
Status int `json:"status"`
|
||||
PaymentStatus int `json:"payment_status"`
|
||||
|
||||
// 角色类型
|
||||
RoleType int `json:"role_type" description:"角色类型 (1:平台角色, 2:客户角色)"`
|
||||
|
||||
// 权限类型
|
||||
PermType int `json:"perm_type" description:"权限类型 (1:菜单, 2:按钮)"`
|
||||
|
||||
// 状态字段
|
||||
Status int `json:"status" description:"状态 (0:禁用, 1:启用)"`
|
||||
|
||||
// 适用端口
|
||||
Platform string `json:"platform" description:"适用端口 (all:全部, web:Web后台, h5:H5端)"`
|
||||
// ✅ 类型/方式 → string
|
||||
PaymentMethod string `json:"payment_method" validate:"required,oneof=wechat offline"`
|
||||
OrderType string `json:"order_type" validate:"required,oneof=single_card device"`
|
||||
```
|
||||
|
||||
❌ **禁止使用英文枚举值**:
|
||||
### 3. Int 状态值约定
|
||||
|
||||
#### 3.1 通用禁用/启用
|
||||
|
||||
**必须用全局常量,禁止自定义(尤其禁止 1=启用 2=禁用 这种反向写法)**:
|
||||
|
||||
```go
|
||||
UserType int `json:"user_type" description:"用户类型 (1:SuperAdmin, 2:Platform)"` // 错误!
|
||||
// pkg/constants/constants.go 已定义,直接使用
|
||||
StatusDisabled = 0 // 禁用
|
||||
StatusEnabled = 1 // 启用
|
||||
```
|
||||
|
||||
### 3. 验证标签与 OpenAPI 标签一致
|
||||
✅ 正确:`description:"状态 (0:禁用, 1:启用)"`
|
||||
❌ 禁止:`description:"状态 (1:启用, 2:禁用)"`
|
||||
|
||||
**所有验证约束必须同时在 `validate` 和 OpenAPI 标签中声明**
|
||||
#### 3.2 生命周期状态
|
||||
|
||||
从 **1** 开始递增,0 不使用(避免与 Go 零值混淆):
|
||||
|
||||
```go
|
||||
const (
|
||||
RechargeStatusPending = 1 // 待支付
|
||||
RechargeStatusPaid = 2 // 已支付
|
||||
RechargeStatusCompleted = 3 // 已完成
|
||||
RechargeStatusClosed = 4 // 已关闭
|
||||
)
|
||||
```
|
||||
|
||||
### 4. 枚举列表必须从 constants 原文抄写
|
||||
|
||||
**DTO description 的枚举列表必须与 `pkg/constants/` 定义完全一致,不可凭记忆填写。**
|
||||
|
||||
操作步骤:
|
||||
1. 先查/定义 `pkg/constants/` 中的枚举常量
|
||||
2. 将常量注释**原文抄写**到 description
|
||||
|
||||
```go
|
||||
// constants.go 中:
|
||||
RechargeStatusPending = 1 // 待支付
|
||||
RechargeStatusPaid = 2 // 已支付
|
||||
RechargeStatusCompleted = 3 // 已完成
|
||||
RechargeStatusClosed = 4 // 已关闭
|
||||
RechargeStatusRefunded = 5 // 已退款
|
||||
|
||||
// DTO description 从上面抄:
|
||||
Status int `json:"status" description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款)"`
|
||||
```
|
||||
|
||||
❌ 禁止(description 与 constants 不一致,是历史 bug 的根因):
|
||||
```go
|
||||
// constants 说 3=已完成,description 却写 3:已取消
|
||||
Status int `json:"status" description:"状态 (1:待支付, 2:已完成, 3:已取消)"`
|
||||
```
|
||||
|
||||
### 5. description 格式标准
|
||||
|
||||
**统一格式**:`字段含义 (值1:中文含义1, 值2:中文含义2)`
|
||||
|
||||
- 值与含义之间用**冒号** `:`(禁止用等号 `=`)
|
||||
- 多个值之间用**逗号加空格** `, `
|
||||
- 含义必须是**中文**
|
||||
|
||||
```go
|
||||
// ✅ 统一格式
|
||||
Status int `description:"状态 (1:待支付, 2:已支付, 3:已完成)"`
|
||||
Platform string `description:"适用端口 (all:全部, web:Web后台, h5:H5端)"`
|
||||
|
||||
// ❌ 格式混乱
|
||||
Status int `description:"状态 (0=禁用, 1=启用)"` // 用等号
|
||||
Status int `description:"0=禁用 1=启用"` // 无括号无逗号
|
||||
```
|
||||
|
||||
### 6. Response DTO 的状态字段必须同时返回 int 和 text
|
||||
|
||||
**所有 Response DTO 中的 int 状态字段,必须同时提供对应的 `_name` 文字字段。**
|
||||
|
||||
原因:防止前端维护映射表出错(历史上已有因此产生 bug 的案例)。
|
||||
|
||||
```go
|
||||
// ✅ Response DTO 标准写法
|
||||
type XxxResponse struct {
|
||||
Status int `json:"status" description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款)"`
|
||||
StatusName string `json:"status_name" description:"状态名称(中文)"`
|
||||
}
|
||||
|
||||
// toResponse 函数中赋值
|
||||
func rechargeStatusName(status int) string {
|
||||
switch status {
|
||||
case constants.RechargeStatusPending:
|
||||
return "待支付"
|
||||
case constants.RechargeStatusCompleted:
|
||||
return "已完成"
|
||||
// ...
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
字段命名约定:`status` → `status_name`,`payment_status` → `payment_status_name`
|
||||
|
||||
**例外**:Request DTO(查询过滤、创建请求)不需要 `_name` 字段。
|
||||
|
||||
### 7. 验证标签与 OpenAPI 标签一致
|
||||
|
||||
```go
|
||||
Username string `json:"username" validate:"required,min=3,max=50" required:"true" minLength:"3" maxLength:"50" description:"用户名"`
|
||||
```
|
||||
|
||||
**标签对照表**:
|
||||
| validate 标签 | OpenAPI 标签 |
|
||||
|--------------|--------------|
|
||||
| `required` | `required:"true"` |
|
||||
| `min=N,max=M`(数值) | `minimum:"N" maximum:"M"` |
|
||||
| `min=N,max=M`(字符串) | `minLength:"N" maxLength:"M"` |
|
||||
| `oneof=A B C` | description 中说明枚举值 |
|
||||
|
||||
| validate 标签 | OpenAPI 标签 | 说明 |
|
||||
|--------------|--------------|------|
|
||||
| `required` | `required:"true"` | 必填字段 |
|
||||
| `min=N,max=M` | `minimum:"N" maximum:"M"` | 数值范围 |
|
||||
| `min=N,max=M` (字符串) | `minLength:"N" maxLength:"M"` | 字符串长度 |
|
||||
| `len=N` | `minLength:"N" maxLength:"N"` | 固定长度 |
|
||||
| `oneof=A B C` | `description` 中说明 | 枚举值 |
|
||||
|
||||
### 4. 请求参数类型标签
|
||||
|
||||
**Query 参数和 Path 参数必须添加对应标签**
|
||||
### 8. 请求参数类型标签
|
||||
|
||||
```go
|
||||
// Query 参数
|
||||
type ListRequest struct {
|
||||
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
|
||||
UserType *int `json:"user_type" query:"user_type" validate:"omitempty,min=1,max=4" minimum:"1" maximum:"4" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"`
|
||||
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
|
||||
Status *int `json:"status" query:"status" validate:"omitempty,min=1,max=4" minimum:"1" maximum:"4" description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭)"`
|
||||
}
|
||||
|
||||
// Path 参数
|
||||
@@ -97,43 +190,50 @@ type IDReq struct {
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 响应 DTO 完整性
|
||||
|
||||
**所有响应 DTO 的字段都必须有完整的 `description` 标签**
|
||||
### 9. 响应 DTO 完整性
|
||||
|
||||
```go
|
||||
type AccountResponse struct {
|
||||
ID uint `json:"id" description:"账号ID"`
|
||||
Username string `json:"username" description:"用户名"`
|
||||
UserType int `json:"user_type" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"`
|
||||
Status int `json:"status" description:"状态 (0:禁用, 1:启用)"`
|
||||
CreatedAt string `json:"created_at" description:"创建时间"`
|
||||
UpdatedAt string `json:"updated_at" description:"更新时间"`
|
||||
ID uint `json:"id" description:"账号ID"`
|
||||
Username string `json:"username" description:"用户名"`
|
||||
UserType int `json:"user_type" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"`
|
||||
Status int `json:"status" description:"状态 (0:禁用, 1:启用)"`
|
||||
StatusName string `json:"status_name" description:"状态名称(中文)"`
|
||||
CreatedAt string `json:"created_at" description:"创建时间"`
|
||||
UpdatedAt string `json:"updated_at" description:"更新时间"`
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AI 助手必须执行的检查
|
||||
|
||||
**在创建或修改任何 DTO 文件后,必须执行以下检查:**
|
||||
|
||||
1. ✅ 检查所有字段是否有 `description` 标签
|
||||
2. ✅ 检查枚举字段是否列出了所有可能值(中文)
|
||||
3. ✅ 检查状态字段是否说明了 0 和 1 的含义
|
||||
4. ✅ 检查 validate 标签与 OpenAPI 标签是否一致
|
||||
5. ✅ 检查是否禁止使用行内注释替代 description
|
||||
6. ✅ 检查枚举值是否使用中文而非英文
|
||||
7. ✅ 重新生成 OpenAPI 文档验证:`go run cmd/gendocs/main.go`
|
||||
1. ✅ 所有字段有 `description` 标签(无行内注释)
|
||||
2. ✅ 枚举类型选择正确(状态用 int,类型/方式用 string)
|
||||
3. ✅ 禁用/启用使用 `0=禁用, 1=启用`(禁止 1=启用 2=禁用)
|
||||
4. ✅ description 枚举列表已从 `pkg/constants/` 原文抄写,无遗漏
|
||||
5. ✅ description 格式统一(冒号 `:`,括号,逗号)
|
||||
6. ✅ Response DTO 有 `_name` 伴生字段
|
||||
7. ✅ validate 标签与 OpenAPI 标签一致
|
||||
8. ✅ 重新生成 OpenAPI 文档验证:`go run cmd/gendocs/main.go`
|
||||
|
||||
**详细检查清单**: 参见 `docs/code-review-checklist.md`
|
||||
**完整枚举规范**: 参见 [`docs/enum-status-standards.md`](../../docs/enum-status-standards.md)
|
||||
|
||||
---
|
||||
|
||||
## 常见枚举字段标准值
|
||||
|
||||
```go
|
||||
// 用户类型
|
||||
// 用户类型(从 constants.UserType* 抄)
|
||||
description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"
|
||||
|
||||
// 角色类型
|
||||
description:"角色类型 (1:平台角色, 2:客户角色)"
|
||||
// 通用启用/禁用(从 constants.StatusEnabled/Disabled 抄)
|
||||
description:"状态 (0:禁用, 1:启用)"
|
||||
|
||||
// 充值状态(从 constants.RechargeStatus* 抄)
|
||||
description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款)"
|
||||
|
||||
// 权限类型
|
||||
description:"权限类型 (1:菜单, 2:按钮)"
|
||||
@@ -141,9 +241,6 @@ description:"权限类型 (1:菜单, 2:按钮)"
|
||||
// 适用端口
|
||||
description:"适用端口 (all:全部, web:Web后台, h5:H5端)"
|
||||
|
||||
// 状态
|
||||
description:"状态 (0:禁用, 1:启用)"
|
||||
|
||||
// 店铺层级
|
||||
description:"店铺层级 (1-7级)"
|
||||
```
|
||||
|
||||
1
.claude/skills/grill-me
Symbolic link
1
.claude/skills/grill-me
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/grill-me
|
||||
1
.claude/skills/grill-with-docs
Symbolic link
1
.claude/skills/grill-with-docs
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/grill-with-docs
|
||||
1
.claude/skills/handoff
Symbolic link
1
.claude/skills/handoff
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/handoff
|
||||
777
.claude/skills/hurl-test/SKILL.md
Normal file
777
.claude/skills/hurl-test/SKILL.md
Normal file
@@ -0,0 +1,777 @@
|
||||
---
|
||||
name: hurl-test
|
||||
description: Hurl 接口测试生成器。用户描述要测试的接口或业务流程,自动探索代码、确认需求、生成完整的 .hurl 测试文件(含 DTO 驱动的字段完整性断言)。触发词:测试、hurl、写测试、接口测试。
|
||||
---
|
||||
|
||||
# Hurl 接口测试生成器
|
||||
|
||||
**用户描述要测试什么,你来读代码、问确认、生成 .hurl 文件。**
|
||||
|
||||
适用于任何后端项目(Go / Python / Node / Java 等),不预设框架和目录结构。
|
||||
|
||||
---
|
||||
|
||||
## 触发条件
|
||||
|
||||
以下情况必须使用本 Skill:
|
||||
- 用户说"测试 XX 接口"、"写 hurl 测试"、"给 XX 加测试"
|
||||
- 用户说"测试 XX 流程"、"测试 XX 的业务逻辑"
|
||||
- 用户说"验证 XX 接口的字段"、"测试接口契约"
|
||||
- 用户提到 hurl、.hurl、接口测试、集成测试、冒烟测试
|
||||
|
||||
---
|
||||
|
||||
## 四阶段工作流(必须按顺序执行)
|
||||
|
||||
```
|
||||
Phase 1: 探索 → Phase 2: 确认 → Phase 3: 生成 → Phase 4: 验证
|
||||
读代码搞清楚 展示给用户确认 输出 .hurl 文件 语法检查 + 试跑
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 1: 探索(Explore)
|
||||
|
||||
**目标:读代码,搞清楚项目约定 + 涉及的接口 + 字段 + 依赖。**
|
||||
|
||||
#### 1.1 项目画像(首次使用时必须执行,后续复用)
|
||||
|
||||
首次为项目生成 Hurl 测试时,先回答以下问题(通过读代码,不要猜):
|
||||
|
||||
| 问题 | 怎么找 |
|
||||
|------|--------|
|
||||
| **语言/框架** | 看 go.mod / package.json / requirements.txt / pom.xml |
|
||||
| **路由注册在哪** | 搜索 `router`、`app.Get`、`@GetMapping`、`@app.route` 等关键词 |
|
||||
| **请求/响应 schema 定义在哪** | 搜索 DTO / schema / serializer / model 目录,看 json tag 或装饰器 |
|
||||
| **统一响应格式是什么** | 找 response helper 文件(如 `response.go`、`response.py`),记录 JSON 结构 |
|
||||
| **认证方式是什么** | 找 auth middleware,确定是 Bearer Token / Cookie / API Key / Basic Auth |
|
||||
| **登录接口是什么** | 找登录 handler,记录路径、请求体、响应中 token 的位置 |
|
||||
| **分页格式是什么** | 找列表接口的响应结构,记录 items/total/page 等字段名 |
|
||||
| **已有 hurl 测试吗** | 搜索 `*.hurl` 文件,复用已有的约定 |
|
||||
|
||||
将画像结果**写入 `tests/hurl/.project-profile.md` 文件持久化保存**。
|
||||
|
||||
#### 画像持久化(关键机制)
|
||||
|
||||
**首次使用时**:完成 1.1 探索后,将画像写入 `tests/hurl/.project-profile.md`,格式如下:
|
||||
|
||||
```markdown
|
||||
# 项目画像(Hurl 测试自动生成用)
|
||||
<!-- 由 hurl-test skill 自动生成,请勿手动修改 -->
|
||||
<!-- 如需刷新,删除此文件后重新运行 skill -->
|
||||
|
||||
## 技术栈
|
||||
- 语言: Go 1.25
|
||||
- 框架: Fiber v2
|
||||
- ORM: GORM
|
||||
|
||||
## 路由定义位置
|
||||
- 路由注册入口: internal/routes/routes.go
|
||||
- 按模块拆分: internal/routes/{module}.go
|
||||
- 路由注册函数: Register(router, doc, basePath, method, path, handler, spec)
|
||||
|
||||
## Schema 定义位置
|
||||
- DTO 目录: internal/model/dto/
|
||||
- 命名规则: {module}_dto.go
|
||||
- 字段标签: json / validate / description
|
||||
|
||||
## 统一响应格式
|
||||
{code: int, msg: string, data: any, timestamp: string(RFC3339)}
|
||||
- 成功: code=0, msg="success"
|
||||
- 错误: code!=0
|
||||
|
||||
## 分页格式
|
||||
{items: [], total: int, page: int, size: int}
|
||||
- 包裹在 data 字段内: $.data.items / $.data.total
|
||||
|
||||
## 认证方式
|
||||
- 后台: POST /api/auth/admin-login → $.data.access_token → Authorization: Bearer {token}
|
||||
- C端: JWT → Authorization: Bearer {token}
|
||||
|
||||
## 默认测试账号
|
||||
- 用户名: admin
|
||||
- 密码: Admin@123456
|
||||
|
||||
## 服务端口
|
||||
- 默认: 3000
|
||||
```
|
||||
|
||||
**后续使用时**:检查 `tests/hurl/.project-profile.md` 是否存在:
|
||||
- **存在** → 直接读取,跳过 1.1 的探索步骤,节省时间
|
||||
- **不存在** → 执行 1.1 完整探索,然后生成此文件
|
||||
- **用户说"刷新画像"** → 删除旧文件,重新执行 1.1
|
||||
|
||||
#### 1.2 找接口定义
|
||||
|
||||
根据用户要测的模块,定位路由注册代码,提取:
|
||||
|
||||
- **HTTP 方法**(GET / POST / PUT / DELETE / PATCH)
|
||||
- **路由路径**(含路径参数格式,如 `/users/:id` 或 `/users/{id}`)
|
||||
- **接口说明**(注释、Summary、装饰器描述)
|
||||
- **是否需要认证**
|
||||
- **请求 schema 类型名**(Input / Request DTO)
|
||||
- **响应 schema 类型名**(Output / Response DTO)
|
||||
|
||||
#### 1.3 读 schema 定义(DTO / struct / class / type)
|
||||
|
||||
定位请求和响应的 schema 定义文件,提取每个字段的:
|
||||
|
||||
- **字段名**:JSON 序列化后的名称(json tag / @JsonProperty / serializer field)
|
||||
- **语言类型**:string / int / bool / 数组 / 嵌套对象 / 可空等
|
||||
- **是否必填**:validate tag / required 装饰器 / 非空标注
|
||||
- **是否可空**:指针类型 / Optional / nullable
|
||||
- **是否参与序列化**:`json:"-"` / @JsonIgnore / exclude
|
||||
- **是否 omitempty**:`json:",omitempty"` / 条件序列化
|
||||
- **字段描述**:description tag / docstring / 注释
|
||||
|
||||
#### 1.4 识别业务依赖
|
||||
|
||||
读 service / business logic 层,识别:
|
||||
|
||||
- 创建操作需要哪些前置数据(如创建订单需要先有商品和用户)
|
||||
- 是否有唯一性约束(如用户名不能重复)
|
||||
- 是否依赖外部服务(支付网关、短信、OAuth 等)
|
||||
- 业务流转逻辑(状态机、级联操作)
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: 确认(Clarify)
|
||||
|
||||
**目标:向用户展示发现的内容,确认模糊点。不要闷头生成。**
|
||||
|
||||
#### 2.1 必须展示的内容
|
||||
|
||||
```
|
||||
我梳理了相关代码,发现以下信息:
|
||||
|
||||
📋 涉及接口:
|
||||
- [方法] [路径] - [说明](认证: 是/否)
|
||||
- ...
|
||||
|
||||
📦 响应字段(基于 {SchemaName}):
|
||||
- [字段名]: [类型] - [说明]
|
||||
- ...(共 N 个字段,将全部生成断言)
|
||||
|
||||
🔗 依赖关系:
|
||||
- [创建 X 需要先创建 Y]
|
||||
- ...
|
||||
|
||||
⚠️ 特殊情况:
|
||||
- [涉及外部服务 / 文件上传 / 特殊认证等]
|
||||
```
|
||||
|
||||
#### 2.2 按需确认(只问有歧义的)
|
||||
|
||||
| 场景 | 要问的 |
|
||||
|------|--------|
|
||||
| 流程范围不明确 | "要测到哪一步?" |
|
||||
| 多种用户角色 | "用哪种身份测?" |
|
||||
| 是否测异常 | "需要包含异常 case 吗?(参数校验失败、权限不足等)" |
|
||||
| 是否测数据隔离 | "需要验证不同用户间数据不可见吗?" |
|
||||
| 涉及第三方 | "XX 部分怎么处理?绕过 / 模拟回调 / 跳过?" |
|
||||
| 前置数据来源 | "XX 依赖数据是通过 API 创建还是假设已存在?" |
|
||||
|
||||
**如果用户说"越完整越好"或"都要"→ 默认全部包含,不再追问。**
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: 生成(Generate)
|
||||
|
||||
**目标:生成完整的 .hurl 文件,字段断言基于 schema 代码,不能编造。**
|
||||
|
||||
#### 3.1 文件头注释
|
||||
|
||||
```hurl
|
||||
# ============================================================
|
||||
# 测试:{测试名称}
|
||||
# 生成时间:{日期}
|
||||
# 涉及模块:{module1, module2, ...}
|
||||
# 涉及接口:{N} 个
|
||||
# 断言数量:{N} 条
|
||||
# 前置条件:{服务运行 + 必要的前置条件}
|
||||
# ============================================================
|
||||
# 流程:
|
||||
# 1. {步骤描述}
|
||||
# 2. {步骤描述}
|
||||
# ...
|
||||
# ============================================================
|
||||
```
|
||||
|
||||
#### 3.2 请求生成规则
|
||||
|
||||
**认证**:
|
||||
|
||||
- 根据 Phase 1 画像中的登录接口和 token 位置生成
|
||||
- token 必须通过 `[Captures]` 捕获,后续请求引用
|
||||
- 如果是 Cookie 认证,用 `[Cookies]` 或 cookie capture
|
||||
|
||||
**CRUD 标准模式**:
|
||||
|
||||
| 操作 | 生成要求 |
|
||||
|------|---------|
|
||||
| **创建(POST)** | capture 返回的 ID;唯一字段用 `{{newUuid}}` 防冲突 |
|
||||
| **查询详情(GET)** | **逐字段断言**(类型 + 值,见 3.3) |
|
||||
| **查询列表(GET)** | 分页结构断言 + items[0] 逐字段断言 |
|
||||
| **修改(PUT/PATCH)** | 修改后**紧跟一个 GET 验证修改生效** |
|
||||
| **删除(DELETE)** | 删除后**紧跟一个 GET 验证已删除** |
|
||||
|
||||
**业务流程模式**:
|
||||
|
||||
- 按用户描述的流程顺序编排请求
|
||||
- 上一步的输出(ID、状态等)通过 `[Captures]` 传给下一步
|
||||
- 关键步骤加中间状态验证(如创建订单后验证状态为"待支付")
|
||||
|
||||
#### 3.3 schema 到断言的映射
|
||||
|
||||
读到 schema 字段后,按以下规则生成 jsonpath 断言:
|
||||
|
||||
**通用类型映射(所有语言)**:
|
||||
|
||||
| Schema 类型特征 | Hurl 断言 |
|
||||
|----------------|-----------|
|
||||
| 字符串(string / str / String) | `isString` |
|
||||
| 整数(int / integer / long / Int) | `isInteger` |
|
||||
| 浮点(float / double / decimal / Float) | `isNumber` |
|
||||
| 布尔(bool / boolean / Boolean) | `isBoolean` |
|
||||
| 数组 / 列表([] / List / Array) | `isList` |
|
||||
| 嵌套对象(struct / class / dict / object) | `isObject`,并递归检查子字段 |
|
||||
| 可空类型(指针 / Optional / nullable) | `exists`(不强制类型,因为可能是 null) |
|
||||
| 不参与序列化(json:"-" / @JsonIgnore / exclude=True) | **跳过,不生成断言** |
|
||||
| 条件序列化(omitempty / if not None) | `exists` 或不生成(取决于场景) |
|
||||
|
||||
**Go 特定映射**:
|
||||
|
||||
| Go 类型 | Hurl 断言 |
|
||||
|---------|-----------|
|
||||
| `string` | `isString` |
|
||||
| `int`, `int8/16/32/64`, `uint`, `uint8/16/32/64` | `isInteger` |
|
||||
| `float32`, `float64` | `isNumber` |
|
||||
| `bool` | `isBoolean` |
|
||||
| `[]T` | `isList` |
|
||||
| `*string`, `*int`, `*uint` 等指针 | `exists` |
|
||||
| `time.Time` | `isString`(通常序列化为字符串) |
|
||||
| `map[string]any` | `isObject` |
|
||||
|
||||
**Python 特定映射(Pydantic / Django / FastAPI)**:
|
||||
|
||||
| Python 类型 | Hurl 断言 |
|
||||
|------------|-----------|
|
||||
| `str` | `isString` |
|
||||
| `int` | `isInteger` |
|
||||
| `float`, `Decimal` | `isNumber` |
|
||||
| `bool` | `isBoolean` |
|
||||
| `list[T]`, `List[T]` | `isList` |
|
||||
| `Optional[T]`, `T | None` | `exists` |
|
||||
| `dict`, `Dict` | `isObject` |
|
||||
| `datetime`, `date` | `isString` |
|
||||
|
||||
**TypeScript/JavaScript 特定映射**:
|
||||
|
||||
| TS/JS 类型 | Hurl 断言 |
|
||||
|-----------|-----------|
|
||||
| `string` | `isString` |
|
||||
| `number`(整数上下文) | `isInteger` |
|
||||
| `number`(通用) | `isNumber` |
|
||||
| `boolean` | `isBoolean` |
|
||||
| `T[]`, `Array<T>` | `isList` |
|
||||
| `T \| null`, `T \| undefined` | `exists` |
|
||||
| `object`, `Record<>` | `isObject` |
|
||||
| `Date` | `isString` |
|
||||
|
||||
**Java 特定映射**:
|
||||
|
||||
| Java 类型 | Hurl 断言 |
|
||||
|----------|-----------|
|
||||
| `String` | `isString` |
|
||||
| `Integer`, `Long`, `int`, `long` | `isInteger` |
|
||||
| `Double`, `Float`, `BigDecimal` | `isNumber` |
|
||||
| `Boolean`, `boolean` | `isBoolean` |
|
||||
| `List<T>` | `isList` |
|
||||
| `@Nullable`, `Optional<T>` | `exists` |
|
||||
| `Map<K,V>` | `isObject` |
|
||||
| `LocalDateTime`, `Instant` | `isString` |
|
||||
|
||||
#### 3.4 统一响应格式断言
|
||||
|
||||
根据 Phase 1 画像中发现的统一响应格式,为**每个成功响应**添加格式断言。
|
||||
|
||||
示例:如果项目的统一格式是 `{code, msg, data, timestamp}`:
|
||||
|
||||
```hurl
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.msg" == "success"
|
||||
jsonpath "$.timestamp" isIsoDate
|
||||
```
|
||||
|
||||
示例:如果项目的格式是 `{status, message, result}`:
|
||||
|
||||
```hurl
|
||||
[Asserts]
|
||||
jsonpath "$.status" == "ok"
|
||||
jsonpath "$.message" isString
|
||||
```
|
||||
|
||||
示例:如果项目无统一包装,直接返回数据:
|
||||
|
||||
```hurl
|
||||
[Asserts]
|
||||
# 直接断言业务字段
|
||||
jsonpath "$.id" isInteger
|
||||
jsonpath "$.name" isString
|
||||
```
|
||||
|
||||
**不要假设响应格式,必须从代码中确认。**
|
||||
|
||||
#### 3.5 分页断言
|
||||
|
||||
根据 Phase 1 画像中发现的分页结构生成。
|
||||
|
||||
示例:如果是 `{items, total, page, size}` 格式:
|
||||
|
||||
```hurl
|
||||
jsonpath "$.data.items" isList
|
||||
jsonpath "$.data.total" isInteger
|
||||
jsonpath "$.data.total" >= 1
|
||||
jsonpath "$.data.page" isInteger
|
||||
jsonpath "$.data.size" isInteger
|
||||
# items 内元素逐字段断言
|
||||
jsonpath "$.data.items[0].{field}" {type_assert}
|
||||
```
|
||||
|
||||
示例:如果是 `{results, count, next, previous}` 格式(Django 风格):
|
||||
|
||||
```hurl
|
||||
jsonpath "$.results" isList
|
||||
jsonpath "$.count" isInteger
|
||||
jsonpath "$.count" >= 1
|
||||
# results 内元素逐字段断言
|
||||
jsonpath "$.results[0].{field}" {type_assert}
|
||||
```
|
||||
|
||||
**根据实际代码调整字段名,不硬编码。**
|
||||
|
||||
#### 3.6 异常 Case 模板
|
||||
|
||||
**参数校验失败**:
|
||||
|
||||
```hurl
|
||||
# ── 异常:参数校验失败 ──
|
||||
POST {{base_url}}/{path}
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"required_field": ""
|
||||
}
|
||||
HTTP {expected_error_status}
|
||||
[Asserts]
|
||||
# 断言错误响应格式(根据项目约定调整)
|
||||
```
|
||||
|
||||
> HTTP 状态码根据项目实际返回确定:有的项目错误也返回 200 + 业务错误码,有的返回 400/422。
|
||||
|
||||
**未认证访问**:
|
||||
|
||||
```hurl
|
||||
# ── 异常:未认证访问 ──
|
||||
GET {{base_url}}/{protected_path}
|
||||
HTTP {expected_unauth_status}
|
||||
```
|
||||
|
||||
**越权访问**(如果用户要求):
|
||||
|
||||
```hurl
|
||||
# ── 异常:用户 B 不能访问用户 A 的资源 ──
|
||||
GET {{base_url}}/{path}/{{user_a_resource_id}}
|
||||
Authorization: Bearer {{user_b_token}}
|
||||
HTTP {expected_forbidden_status}
|
||||
```
|
||||
|
||||
#### 3.7 特殊场景处理
|
||||
|
||||
| 场景 | 处理策略 |
|
||||
|------|---------|
|
||||
| **短信/邮件验证码** | 建议服务端加 test_mode 开关,固定验证码写入 env 文件;注释提醒用户 |
|
||||
| **第三方支付** | 优先用项目内部支付方式(如钱包支付);如需测回调,直接 POST 回调接口模拟 |
|
||||
| **OAuth 登录(微信/Google/GitHub)** | 建议服务端加 test_mode 支持直接传 openid/email;注释提醒用户 |
|
||||
| **文件上传** | 用 Hurl 的 `[Multipart]` 语法 + testdata 目录下的样本文件 |
|
||||
| **外部 API 依赖** | 只测参数校验和错误响应格式,不断言业务结果;注释说明依赖 |
|
||||
| **WebSocket** | Hurl 不支持,注释说明跳过 |
|
||||
| **异步任务结果** | 用 Hurl 的 `retry` + `retry-interval` 轮询直到状态变更 |
|
||||
|
||||
异步轮询示例:
|
||||
|
||||
```hurl
|
||||
# 等待异步任务完成(最多重试 10 次,间隔 500ms)
|
||||
GET {{base_url}}/{path}/{{task_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
[Options]
|
||||
retry: 10
|
||||
retry-interval: 500ms
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.data.status" == "completed"
|
||||
```
|
||||
|
||||
#### 3.8 文件输出
|
||||
|
||||
**目录结构**(首次使用时创建,如不存在):
|
||||
|
||||
```
|
||||
tests/hurl/
|
||||
├── env/
|
||||
│ └── dev.env # 环境变量
|
||||
├── testdata/ # 测试用的样本文件
|
||||
├── flows/ # 业务流程测试
|
||||
├── modules/ # 按模块的接口测试
|
||||
│ └── {module}/
|
||||
│ └── 01-crud.hurl
|
||||
├── negative/ # 异常/边界测试
|
||||
├── contract/ # 接口契约验证
|
||||
└── Makefile # 快捷命令
|
||||
```
|
||||
|
||||
文件放置规则:
|
||||
|
||||
| 用户描述 | 输出路径 |
|
||||
|---------|---------|
|
||||
| 测试 XX 流程 / 业务流程 | `tests/hurl/flows/{flow-name}.hurl` |
|
||||
| 测试 XX 模块的 CRUD / 接口 | `tests/hurl/modules/{module}/01-crud.hurl` |
|
||||
| 测试异常/边界/权限 | `tests/hurl/negative/{name}.hurl` |
|
||||
| 测试接口契约/字段对齐 | `tests/hurl/contract/{name}.hurl` |
|
||||
|
||||
**如果项目已有 hurl 测试目录结构,沿用已有约定,不要另起炉灶。**
|
||||
|
||||
#### 3.9 env 和 Makefile
|
||||
|
||||
**env/dev.env**(首次创建时生成,内容基于 Phase 1 画像):
|
||||
|
||||
```properties
|
||||
# 服务地址
|
||||
base_url=http://localhost:{port}
|
||||
|
||||
# 认证信息(根据项目实际填写)
|
||||
admin_username={默认用户名}
|
||||
admin_password={默认密码}
|
||||
|
||||
# 测试模式变量(如果有特殊场景)
|
||||
# test_sms_code=888888
|
||||
# test_openid=test_openid_001
|
||||
```
|
||||
|
||||
**Makefile**(首次创建时生成):
|
||||
|
||||
```makefile
|
||||
SHELL := /bin/bash
|
||||
ENV ?= dev
|
||||
HURL_OPTS := --variables-file env/$(ENV).env --test
|
||||
|
||||
.PHONY: test test-flows test-modules test-negative report
|
||||
|
||||
test: ## 运行所有测试
|
||||
hurl $(HURL_OPTS) .
|
||||
|
||||
test-flows: ## 运行业务流程测试
|
||||
hurl $(HURL_OPTS) flows/
|
||||
|
||||
test-modules: ## 运行模块接口测试
|
||||
hurl $(HURL_OPTS) modules/
|
||||
|
||||
test-negative: ## 运行异常测试
|
||||
hurl $(HURL_OPTS) negative/
|
||||
|
||||
report: ## 生成 HTML 报告
|
||||
hurl $(HURL_OPTS) --report-html build/report/ .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: 验证(Verify)
|
||||
|
||||
**目标:确保生成的 .hurl 文件语法正确、可运行。**
|
||||
|
||||
#### 4.1 语法自检
|
||||
|
||||
- [ ] 每个请求之间有空行分隔
|
||||
- [ ] `[Captures]` 和 `[Asserts]` 拼写正确(大小写敏感)
|
||||
- [ ] 所有 `{{变量}}` 引用都有来源(env 文件定义 或 上游 `[Captures]`)
|
||||
- [ ] JSON body 无尾逗号、格式正确
|
||||
- [ ] `HTTP {status}` 在请求之后、`[Captures]` / `[Asserts]` 之前
|
||||
- [ ] 请求和断言之间没有多余空行(`HTTP` 行必须紧跟请求)
|
||||
- [ ] 文件上传路径相对于 hurl 文件位置正确
|
||||
|
||||
#### 4.2 运行测试
|
||||
|
||||
```bash
|
||||
hurl --variables-file tests/hurl/env/dev.env --test tests/hurl/{生成的文件}
|
||||
```
|
||||
|
||||
- 服务在跑 → 执行,如有失败分析修正
|
||||
- 服务没跑 → 跳过,告知用户手动验证命令
|
||||
|
||||
---
|
||||
|
||||
## 红线规则
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| **不跳过 Phase 1** | 必须读代码确认接口路径和字段,不能凭记忆或猜测 |
|
||||
| **不跳过 Phase 2** | 必须向用户展示发现的接口和字段,确认后再生成 |
|
||||
| **不编造字段** | 所有断言的字段名必须来自实际 schema 代码 |
|
||||
| **不编造路径** | 所有接口路径必须来自实际路由代码 |
|
||||
| **不遗漏字段** | schema 中每个参与序列化的字段都必须有对应断言 |
|
||||
| **不硬编码 ID** | 所有依赖的 ID 通过 `[Captures]` 从上游请求获取 |
|
||||
| **不假设响应格式** | 统一响应结构必须从代码中确认,不同项目格式不同 |
|
||||
| **唯一值防冲突** | 创建类请求的唯一字段使用 `{{newUuid}}` 或 `{{newDate}}` |
|
||||
| **自给自足** | 每个 .hurl 文件自己创建测试数据,不依赖外部数据准备 |
|
||||
|
||||
---
|
||||
|
||||
## AI 助手检查清单
|
||||
|
||||
生成 .hurl 文件后自检:
|
||||
|
||||
1. ✅ 文件头注释包含流程描述和前置条件
|
||||
2. ✅ 认证步骤正确 capture 了 token / cookie
|
||||
3. ✅ 所有依赖数据通过 API 链式创建(自给自足)
|
||||
4. ✅ 每个成功响应断言了项目的统一响应格式
|
||||
5. ✅ 查询详情接口**逐字段断言**(类型 + 值,基于 schema)
|
||||
6. ✅ 分页接口断言了分页结构 + 第一条记录的字段
|
||||
7. ✅ 修改操作后紧跟 GET 验证修改生效
|
||||
8. ✅ 删除操作后紧跟 GET 验证已删除
|
||||
9. ✅ 唯一字段使用了 `{{newUuid}}`
|
||||
10. ✅ `{{变量}}` 引用无悬空(都有 env 或 capture 来源)
|
||||
11. ✅ 文件放在了正确的目录位置
|
||||
12. ✅ 特殊场景有明确的处理策略和注释提醒
|
||||
|
||||
---
|
||||
|
||||
## 附录:Hurl 语法速查
|
||||
|
||||
**生成 .hurl 文件时必须参照本速查,不可凭记忆编造语法。**
|
||||
|
||||
### 文件结构
|
||||
|
||||
一个 .hurl 文件由多个 entry 组成,每个 entry = 请求 + 可选响应:
|
||||
|
||||
```
|
||||
请求1
|
||||
响应1(可选)
|
||||
|
||||
请求2
|
||||
响应2(可选)
|
||||
```
|
||||
|
||||
entry 之间用空行分隔。
|
||||
|
||||
### 请求格式
|
||||
|
||||
```hurl
|
||||
METHOD URL
|
||||
Header1: value1
|
||||
Header2: value2
|
||||
[Options]
|
||||
key: value
|
||||
[Query]
|
||||
param1: value1
|
||||
[Form]
|
||||
field1: value1
|
||||
[Multipart]
|
||||
file1: file,path/to/file;
|
||||
[BasicAuth]
|
||||
username: password
|
||||
[Cookies]
|
||||
name: value
|
||||
BODY(JSON / XML / multiline string / file)
|
||||
```
|
||||
|
||||
**规则**:
|
||||
- Method + URL 是第一行,必须
|
||||
- Headers 紧跟 URL 之后(无 section 标记)
|
||||
- Sections(`[Query]`、`[Form]`、`[Options]` 等)顺序任意
|
||||
- Body 必须在最后
|
||||
- JSON body 直接写 `{ }` 即可,自动设置 Content-Type: application/json
|
||||
|
||||
### 响应格式
|
||||
|
||||
```hurl
|
||||
HTTP {status_code}
|
||||
Header1: expected_value1
|
||||
[Captures]
|
||||
var_name: jsonpath "$.path"
|
||||
[Asserts]
|
||||
jsonpath "$.field" == "value"
|
||||
```
|
||||
|
||||
**规则**:
|
||||
- `HTTP {status}` 紧跟请求之后(中间不能有空行)
|
||||
- `HTTP *` 表示不检查状态码
|
||||
- Headers 检查紧跟 HTTP 行之后
|
||||
- `[Captures]` 和 `[Asserts]` 顺序任意
|
||||
|
||||
### 变量和模板
|
||||
|
||||
```hurl
|
||||
# 引用变量(从 env 文件、命令行或上游 capture 获取)
|
||||
GET {{base_url}}/api/users/{{user_id}}
|
||||
|
||||
# 内置函数
|
||||
POST {{base_url}}/api/users
|
||||
{
|
||||
"email": "{{newUuid}}@test.com",
|
||||
"created_at": "{{newDate}}"
|
||||
}
|
||||
```
|
||||
|
||||
可用函数:
|
||||
- `{{newUuid}}` — 生成 UUID v4
|
||||
- `{{newDate}}` — 生成 RFC 3339 UTC 时间戳
|
||||
|
||||
### Capture 语法
|
||||
|
||||
```hurl
|
||||
[Captures]
|
||||
# JSONPath
|
||||
token: jsonpath "$.data.access_token"
|
||||
user_id: jsonpath "$.data.id"
|
||||
first_item: jsonpath "$.items[0].name"
|
||||
|
||||
# Header
|
||||
location: header "Location"
|
||||
|
||||
# Cookie
|
||||
session: cookie "SESSIONID"
|
||||
|
||||
# Body(整个响应体作为字符串)
|
||||
full_body: body
|
||||
|
||||
# Status code
|
||||
code: status
|
||||
|
||||
# 正则表达式
|
||||
csrf: regex "name=\"csrf\" value=\"([^\"]+)\""
|
||||
|
||||
# 响应时间(毫秒)
|
||||
response_time: duration
|
||||
```
|
||||
|
||||
### Assert 语法
|
||||
|
||||
```hurl
|
||||
[Asserts]
|
||||
# ── 状态码 ──
|
||||
status == 200
|
||||
status >= 200
|
||||
status < 300
|
||||
|
||||
# ── JSONPath 断言 ──
|
||||
jsonpath "$.name" == "Alice" # 等于
|
||||
jsonpath "$.name" != "Bob" # 不等于
|
||||
jsonpath "$.age" > 18 # 大于
|
||||
jsonpath "$.age" >= 18 # 大于等于
|
||||
jsonpath "$.count" < 100 # 小于
|
||||
jsonpath "$.items" count == 5 # 集合长度
|
||||
jsonpath "$.name" startsWith "Al" # 前缀
|
||||
jsonpath "$.name" endsWith "ce" # 后缀
|
||||
jsonpath "$.name" contains "lic" # 包含
|
||||
jsonpath "$.date" matches /\\d{4}-\\d{2}-\\d{2}/ # 正则
|
||||
|
||||
# ── 类型断言 ──
|
||||
jsonpath "$.name" isString
|
||||
jsonpath "$.age" isInteger
|
||||
jsonpath "$.score" isFloat
|
||||
jsonpath "$.count" isNumber # 整数或浮点
|
||||
jsonpath "$.active" isBoolean
|
||||
jsonpath "$.items" isList
|
||||
jsonpath "$.meta" isObject
|
||||
jsonpath "$.id" isUuid
|
||||
jsonpath "$.created_at" isIsoDate # RFC 3339 格式
|
||||
jsonpath "$.field" isEmpty # 空集合
|
||||
|
||||
# ── 存在性 ──
|
||||
jsonpath "$.field" exists
|
||||
jsonpath "$.field" not exists
|
||||
|
||||
# ── 否定 ──
|
||||
jsonpath "$.name" not contains "Bob"
|
||||
jsonpath "$.status" not == "deleted"
|
||||
|
||||
# ── Header 断言 ──
|
||||
header "Content-Type" contains "application/json"
|
||||
header "X-Request-Id" exists
|
||||
|
||||
# ── 性能 ──
|
||||
duration < 1000 # 响应时间(毫秒)
|
||||
|
||||
# ── Body 断言 ──
|
||||
body contains "Hello"
|
||||
bytes count == 1024
|
||||
```
|
||||
|
||||
### Options(逐请求配置)
|
||||
|
||||
```hurl
|
||||
GET {{base_url}}/api/task/{{task_id}}
|
||||
[Options]
|
||||
retry: 10 # 最大重试次数(-1 = 无限)
|
||||
retry-interval: 500ms # 重试间隔
|
||||
delay: 2s # 请求前等待
|
||||
location: true # 跟随重定向
|
||||
insecure: true # 允许不安全 SSL
|
||||
verbose: true # 输出详细日志
|
||||
very-verbose: true # 输出更详细日志
|
||||
skip: true # 跳过此请求
|
||||
variable: key=value # 定义变量
|
||||
HTTP 200
|
||||
```
|
||||
|
||||
### Multipart 文件上传
|
||||
|
||||
```hurl
|
||||
POST {{base_url}}/api/upload
|
||||
[Multipart]
|
||||
file: file,testdata/sample.xlsx;
|
||||
field1: value1
|
||||
# 指定 Content-Type
|
||||
file2: file,testdata/data.bin; application/octet-stream
|
||||
```
|
||||
|
||||
### 运行命令
|
||||
|
||||
```bash
|
||||
# 运行单个文件
|
||||
hurl --test file.hurl
|
||||
|
||||
# 带变量文件
|
||||
hurl --variables-file env/dev.env --test file.hurl
|
||||
|
||||
# 运行目录下所有 .hurl
|
||||
hurl --test tests/hurl/
|
||||
|
||||
# 生成 HTML 报告
|
||||
hurl --test --report-html build/report/ tests/hurl/
|
||||
|
||||
# 生成 JUnit 报告(CI 用)
|
||||
hurl --test --report-junit build/report.xml tests/hurl/
|
||||
|
||||
# 并行执行(--test 默认并行,同文件内串行)
|
||||
hurl --test --jobs 4 tests/hurl/
|
||||
|
||||
# 指定单个变量
|
||||
hurl --variable base_url=http://localhost:3000 --test file.hurl
|
||||
|
||||
# 失败后继续执行
|
||||
hurl --test --continue-on-error tests/hurl/
|
||||
```
|
||||
|
||||
### 常见错误
|
||||
|
||||
| 错误 | 原因 | 修正 |
|
||||
|------|------|------|
|
||||
| `HTTP 200` 和请求之间有空行 | 空行会被当作 entry 分隔符 | 删除空行,HTTP 行紧跟请求 |
|
||||
| JSON body 有尾逗号 | Hurl 严格解析 JSON | 删除最后一个逗号 |
|
||||
| `jsonpath` 写成 `json_path` 或 `JsonPath` | 关键字大小写敏感 | 必须小写 `jsonpath` |
|
||||
| `[Captures]` 写成 `[Capture]` | 必须是复数 | `[Captures]`、`[Asserts]`、`[Options]` |
|
||||
| 变量 `{{ var }}` 有空格 | 允许,但建议统一 | `{{var}}` 或 `{{ var }}` 都可以 |
|
||||
| `isIsoDate` 用在非 RFC 3339 格式 | 只认 `YYYY-MM-DDTHH:mm:ss` 格式 | 如果是其他格式用 `matches` |
|
||||
| `file,path;` 路径含 `..` | Hurl 禁止相对父目录 | 用 `--file-root` 或调整路径 |
|
||||
1
.claude/skills/improve-codebase-architecture
Symbolic link
1
.claude/skills/improve-codebase-architecture
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/improve-codebase-architecture
|
||||
97
.claude/skills/openspec-api-contract/SKILL.md
Normal file
97
.claude/skills/openspec-api-contract/SKILL.md
Normal file
@@ -0,0 +1,97 @@
|
||||
---
|
||||
name: openspec-api-contract
|
||||
description: OpenSpec API 契约规范。创建涉及接口的 OpenSpec 提案时使用。探索阶段提供业务与契约引导清单,提案文档要求 API 契约设计、错误码与完成标准等必填章节。
|
||||
---
|
||||
|
||||
# OpenSpec API 契约规范
|
||||
|
||||
**适用场景**:创建涉及 API/接口的 OpenSpec 提案时,探索和提案两个阶段均须遵守本规范。
|
||||
|
||||
---
|
||||
|
||||
## 一、探索阶段引导清单
|
||||
|
||||
在 `openspec-explore` 阶段,当内容涉及接口时,讨论必须覆盖以下所有维度。
|
||||
|
||||
### 业务与契约确认
|
||||
|
||||
**输入与输出**
|
||||
- 请求方是谁?用户类型(SuperAdmin/Platform/Agent/Enterprise/Personal)?
|
||||
- 输入参数:必填/选填字段、格式约束、参数来源(路径/查询/Body)?
|
||||
- 输出结构:哪些字段必须返回?是否需要分页?
|
||||
|
||||
**业务规则**
|
||||
- 核心业务规则与边界条件?
|
||||
- 是否涉及状态流转?状态机的完整定义?
|
||||
- 依赖外部服务时,外部异常的降级行为?
|
||||
|
||||
**权限与资源所有权**
|
||||
- 哪些用户类型可以访问?
|
||||
- 是否涉及跨用户/跨店铺/跨企业的资源访问?(需三层越权防护)
|
||||
- 资源所有权校验方式:`CanManageShop` / `CanManageEnterprise` / 自有资源?
|
||||
|
||||
**幂等性**
|
||||
- 操作类型:查询(天然幂等)/ 创建 / 更新 / 删除?
|
||||
- 写操作幂等策略:状态条件更新 / Redis 业务键防重 + 分布式锁 / 乐观锁(version)?
|
||||
- 异步任务是否需要任务锁?
|
||||
|
||||
**数据模型变更**
|
||||
- 是否需要新建表、修改现有表或数据回填?
|
||||
- 迁移策略:上线顺序、兼容旧数据的方式?
|
||||
- 是否影响 GORM Callback 自动数据权限过滤?
|
||||
|
||||
**错误码与异常语义**
|
||||
- 预期错误场景及对应错误码?
|
||||
- 错误响应是否泄露敏感信息?(参数校验失败统一返回 `CodeInvalidParam`)
|
||||
|
||||
---
|
||||
|
||||
## 二、提案文档必填章节
|
||||
|
||||
在 `openspec-propose` 生成的提案(`proposal.md` / `design.md`)中,涉及接口时以下内容**不可缺失**。
|
||||
|
||||
### API 契约设计
|
||||
|
||||
**接口定义**
|
||||
|
||||
| 项 | 内容 |
|
||||
|---|---|
|
||||
| Endpoint | `METHOD /api/{scope}/{resource}[/:id]` |
|
||||
| 请求参数 | 字段名、类型、必填/选填、说明 |
|
||||
| 响应结构 | `data` 字段的完整结构定义 |
|
||||
| 鉴权要求 | 允许的用户类型 |
|
||||
| 资源所有权 | 所有权校验方式 |
|
||||
|
||||
**列表接口额外要求**
|
||||
- 分页:`page` + `page_size`(默认 20,最大 100)
|
||||
- 排序:默认排序字段与方向
|
||||
- 过滤:支持的过滤条件列表
|
||||
|
||||
**错误码清单**
|
||||
|
||||
| 场景 | 错误码 | 说明 |
|
||||
|---|---|---|
|
||||
| 参数校验失败 | `CodeInvalidParam` | 统一返回,不泄露细节 |
|
||||
| 资源不存在/越权 | `CodeForbidden` | 不区分两者,防止信息泄露 |
|
||||
| (业务错误场景...) | (对应错误码) | (说明) |
|
||||
|
||||
### 完成标准
|
||||
|
||||
**最小验证步骤**(按顺序列出可操作的验证步骤)
|
||||
|
||||
1. (例:调用创建接口,验证返回 `code=0`)
|
||||
2. (例:查询接口确认数据存在且字段正确)
|
||||
3. (例:PostgreSQL MCP 查询确认数据库记录符合预期)
|
||||
|
||||
**影响范围说明**
|
||||
- 新增/修改的表:
|
||||
- 影响的现有接口:
|
||||
- 影响的权限与数据过滤范围:
|
||||
|
||||
---
|
||||
|
||||
## 约束(必须遵守)
|
||||
|
||||
- **优先复用现有架构与库**:不引入新依赖,错误码优先复用已有定义
|
||||
- **不做顺手重构**:提案范围严格限定在目标功能;发现可优化点,记录到 backlog 但不执行
|
||||
- **数据库设计**:禁止外键约束,禁止 GORM 关联标签,关联通过 ID 字段手动维护
|
||||
@@ -1,30 +1,35 @@
|
||||
---
|
||||
name: openspec-apply-change
|
||||
description: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.2.0"
|
||||
---
|
||||
|
||||
Implement tasks from an OpenSpec change.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, MUST prompt for available changes.
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
1. **Select the change**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
||||
|
||||
Show changes that are implementation-ready (have tasks artifact).
|
||||
Include the schema used for each change if available.
|
||||
Mark changes with incomplete tasks as "(In Progress)".
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:apply <other>`).
|
||||
|
||||
2. **Check status to understand the schema**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used (e.g., "spec-driven", "tdd")
|
||||
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
||||
- Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
|
||||
|
||||
3. **Get apply instructions**
|
||||
@@ -49,7 +54,6 @@ Implement tasks from an OpenSpec change.
|
||||
Read the files listed in `contextFiles` from the apply instructions output.
|
||||
The files depend on the schema being used:
|
||||
- **spec-driven**: proposal, specs, design, tasks
|
||||
- **tdd**: spec, tests, implementation, docs
|
||||
- Other schemas: follow the contextFiles from CLI output
|
||||
|
||||
5. **Show current progress**
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
---
|
||||
name: openspec-archive-change
|
||||
description: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.2.0"
|
||||
---
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, MUST prompt for available changes.
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
@@ -44,38 +50,20 @@ Archive a completed change in the experimental workflow.
|
||||
|
||||
**If no tasks file exists:** Proceed without task-related warning.
|
||||
|
||||
4. **Check if delta specs need syncing**
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
Check if `specs/` directory exists in the change with spec files.
|
||||
Check for delta specs at `openspec/changes/<name>/specs/`. If none exist, proceed without sync prompt.
|
||||
|
||||
**If delta specs exist, perform a quick sync check:**
|
||||
**If delta specs exist:**
|
||||
- Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`
|
||||
- Determine what changes would be applied (adds, modifications, removals, renames)
|
||||
- Show a combined summary before prompting
|
||||
|
||||
a. **For each delta spec** at `openspec/changes/<name>/specs/<capability>/spec.md`:
|
||||
- Extract requirement names (lines matching `### Requirement: <name>`)
|
||||
- Note which sections exist (ADDED, MODIFIED, REMOVED)
|
||||
**Prompt options:**
|
||||
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
||||
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
||||
|
||||
b. **Check corresponding main spec** at `openspec/specs/<capability>/spec.md`:
|
||||
- If main spec doesn't exist → needs sync
|
||||
- If main spec exists, check if ADDED requirement names appear in it
|
||||
- If any ADDED requirements are missing from main spec → needs sync
|
||||
|
||||
c. **Report findings:**
|
||||
|
||||
**If sync needed:**
|
||||
```
|
||||
⚠️ Delta specs may not be synced:
|
||||
- specs/auth/spec.md → Main spec missing requirement "Token Refresh"
|
||||
- specs/api/spec.md → Main spec doesn't exist yet
|
||||
|
||||
Would you like to sync now before archiving?
|
||||
```
|
||||
- Use **AskUserQuestion tool** with options: "Sync now", "Archive without syncing"
|
||||
- If user chooses sync, execute /opsx:sync logic (use the openspec-sync-specs skill)
|
||||
|
||||
**If already synced (all requirements found):**
|
||||
- Proceed without prompting (specs appear to be in sync)
|
||||
|
||||
**If no delta specs exist:** Proceed without sync-related checks.
|
||||
If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>"). Proceed to archive regardless of choice.
|
||||
|
||||
5. **Perform the archive**
|
||||
|
||||
@@ -111,7 +99,7 @@ Archive a completed change in the experimental workflow.
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** ✓ Synced to main specs (or "No delta specs" or "⚠️ Not synced")
|
||||
**Specs:** ✓ Synced to main specs (or "No delta specs" or "Sync skipped")
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
```
|
||||
@@ -123,4 +111,4 @@ All artifacts complete. All tasks complete.
|
||||
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
||||
- Show clear summary of what happened
|
||||
- If sync is requested, use openspec-sync-specs approach (agent-driven)
|
||||
- Quick sync check: look for requirement names in delta specs, verify they exist in main specs
|
||||
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
---
|
||||
name: openspec-continue-change
|
||||
description: Continue working on an OpenSpec change by creating the next artifact. Use when the user wants to progress their change, create the next artifact, or continue their workflow.
|
||||
---
|
||||
|
||||
Continue working on a change by creating the next artifact.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to work on.
|
||||
|
||||
Present the top 3-4 most recently modified changes as options, showing:
|
||||
- Change name
|
||||
- Schema (from `schema` field if present, otherwise "spec-driven")
|
||||
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
||||
- How recently it was modified (from `lastModified` field)
|
||||
|
||||
Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to continue.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Check current status**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand current state. The response includes:
|
||||
- `schemaName`: The workflow schema being used (e.g., "spec-driven", "tdd")
|
||||
- `artifacts`: Array of artifacts with their status ("done", "ready", "blocked")
|
||||
- `isComplete`: Boolean indicating if all artifacts are complete
|
||||
|
||||
3. **Act based on status**:
|
||||
|
||||
---
|
||||
|
||||
**If all artifacts are complete (`isComplete: true`)**:
|
||||
- Congratulate the user
|
||||
- Show final status including the schema used
|
||||
- Suggest: "All artifacts created! You can now implement this change or archive it."
|
||||
- STOP
|
||||
|
||||
---
|
||||
|
||||
**If artifacts are ready to create** (status shows artifacts with `status: "ready"`):
|
||||
- Pick the FIRST artifact with `status: "ready"` from the status output
|
||||
- Get its instructions:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- Parse the JSON to get template, dependencies, and what it unlocks
|
||||
- **Create the artifact file** using the template as a starting point:
|
||||
- Read any completed dependency files for context
|
||||
- Fill in the template based on context and user's goals
|
||||
- Write to the output path specified in instructions
|
||||
- Show what was created and what's now unlocked
|
||||
- STOP after creating ONE artifact
|
||||
|
||||
---
|
||||
|
||||
**If no artifacts are ready (all blocked)**:
|
||||
- This shouldn't happen with a valid schema
|
||||
- Show status and suggest checking for issues
|
||||
|
||||
4. **After creating an artifact, show progress**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
|
||||
**Output**
|
||||
|
||||
After each invocation, show:
|
||||
- Which artifact was created
|
||||
- Schema workflow being used
|
||||
- Current progress (N/M complete)
|
||||
- What artifacts are now unlocked
|
||||
- Prompt: "Want to continue? Just ask me to continue or tell me what to do next."
|
||||
|
||||
**Artifact Creation Guidelines**
|
||||
|
||||
The artifact types and their purpose depend on the schema. Use the `instruction` field from the instructions output to understand what to create.
|
||||
|
||||
Common artifact patterns:
|
||||
|
||||
**spec-driven schema** (proposal → specs → design → tasks):
|
||||
- **proposal.md**: Ask user about the change if not clear. Fill in Why, What Changes, Capabilities, Impact.
|
||||
- The Capabilities section is critical - each capability listed will need a spec file.
|
||||
- **specs/*.md**: Create one spec per capability listed in the proposal.
|
||||
- **design.md**: Document technical decisions, architecture, and implementation approach.
|
||||
- **tasks.md**: Break down implementation into checkboxed tasks.
|
||||
|
||||
**tdd schema** (spec → tests → implementation → docs):
|
||||
- **spec.md**: Feature specification defining what to build.
|
||||
- **tests/*.test.ts**: Write tests BEFORE implementation (TDD red phase).
|
||||
- **src/*.ts**: Implement to make tests pass (TDD green phase).
|
||||
- **docs/*.md**: Document the implemented feature.
|
||||
|
||||
For other schemas, follow the `instruction` field from the CLI output.
|
||||
|
||||
**Guardrails**
|
||||
- Create ONE artifact per invocation
|
||||
- Always read dependency artifacts before creating a new one
|
||||
- Never skip artifacts or create out of order
|
||||
- If context is unclear, ask the user before creating
|
||||
- Verify the artifact file exists after writing before marking progress
|
||||
- Use the schema's artifact sequence, don't assume specific artifact names
|
||||
288
.claude/skills/openspec-explore/SKILL.md
Normal file
288
.claude/skills/openspec-explore/SKILL.md
Normal file
@@ -0,0 +1,288 @@
|
||||
---
|
||||
name: openspec-explore
|
||||
description: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.2.0"
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
|
||||
|
||||
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
||||
|
||||
---
|
||||
|
||||
## The Stance
|
||||
|
||||
- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script
|
||||
- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.
|
||||
- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking
|
||||
- **Adaptive** - Follow interesting threads, pivot when new information emerges
|
||||
- **Patient** - Don't rush to conclusions, let the shape of the problem emerge
|
||||
- **Grounded** - Explore the actual codebase when relevant, don't just theorize
|
||||
|
||||
---
|
||||
|
||||
## What You Might Do
|
||||
|
||||
Depending on what the user brings, you might:
|
||||
|
||||
**Explore the problem space**
|
||||
- Ask clarifying questions that emerge from what they said
|
||||
- Challenge assumptions
|
||||
- Reframe the problem
|
||||
- Find analogies
|
||||
|
||||
**Investigate the codebase**
|
||||
- Map existing architecture relevant to the discussion
|
||||
- Find integration points
|
||||
- Identify patterns already in use
|
||||
- Surface hidden complexity
|
||||
|
||||
**Compare options**
|
||||
- Brainstorm multiple approaches
|
||||
- Build comparison tables
|
||||
- Sketch tradeoffs
|
||||
- Recommend a path (if asked)
|
||||
|
||||
**Visualize**
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Use ASCII diagrams liberally │
|
||||
├─────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────┐ ┌────────┐ │
|
||||
│ │ State │────────▶│ State │ │
|
||||
│ │ A │ │ B │ │
|
||||
│ └────────┘ └────────┘ │
|
||||
│ │
|
||||
│ System diagrams, state machines, │
|
||||
│ data flows, architecture sketches, │
|
||||
│ dependency graphs, comparison tables │
|
||||
│ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Surface risks and unknowns**
|
||||
- Identify what could go wrong
|
||||
- Find gaps in understanding
|
||||
- Suggest spikes or investigations
|
||||
|
||||
---
|
||||
|
||||
## OpenSpec Awareness
|
||||
|
||||
You have full context of the OpenSpec system. Use it naturally, don't force it.
|
||||
|
||||
### Check for context
|
||||
|
||||
At the start, quickly check what exists:
|
||||
```bash
|
||||
openspec list --json
|
||||
```
|
||||
|
||||
This tells you:
|
||||
- If there are active changes
|
||||
- Their names, schemas, and status
|
||||
- What the user might be working on
|
||||
|
||||
### When no change exists
|
||||
|
||||
Think freely. When insights crystallize, you might offer:
|
||||
|
||||
- "This feels solid enough to start a change. Want me to create a proposal?"
|
||||
- Or keep exploring - no pressure to formalize
|
||||
|
||||
### When a change exists
|
||||
|
||||
If the user mentions a change or you detect one is relevant:
|
||||
|
||||
1. **Read existing artifacts for context**
|
||||
- `openspec/changes/<name>/proposal.md`
|
||||
- `openspec/changes/<name>/design.md`
|
||||
- `openspec/changes/<name>/tasks.md`
|
||||
- etc.
|
||||
|
||||
2. **Reference them naturally in conversation**
|
||||
- "Your design mentions using Redis, but we just realized SQLite fits better..."
|
||||
- "The proposal scopes this to premium users, but we're now thinking everyone..."
|
||||
|
||||
3. **Offer to capture when decisions are made**
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|--------------|------------------|
|
||||
| New requirement discovered | `specs/<capability>/spec.md` |
|
||||
| Requirement changed | `specs/<capability>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
- "This is a new requirement. Add it to specs?"
|
||||
- "This changes scope. Update the proposal?"
|
||||
|
||||
4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.
|
||||
|
||||
---
|
||||
|
||||
## What You Don't Have To Do
|
||||
|
||||
- Follow a script
|
||||
- Ask the same questions every time
|
||||
- Produce a specific artifact
|
||||
- Reach a conclusion
|
||||
- Stay on topic if a tangent is valuable
|
||||
- Be brief (this is thinking time)
|
||||
|
||||
---
|
||||
|
||||
## Handling Different Entry Points
|
||||
|
||||
**User brings a vague idea:**
|
||||
```
|
||||
User: I'm thinking about adding real-time collaboration
|
||||
|
||||
You: Real-time collab is a big space. Let me think about this...
|
||||
|
||||
COLLABORATION SPECTRUM
|
||||
════════════════════════════════════════════
|
||||
|
||||
Awareness Coordination Sync
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌────────┐ ┌────────┐ ┌────────┐
|
||||
│Presence│ │Cursors │ │ CRDT │
|
||||
│ "3 │ │ Multi │ │Conflict│
|
||||
│online" │ │ select │ │ free │
|
||||
└────────┘ └────────┘ └────────┘
|
||||
│ │ │
|
||||
trivial moderate complex
|
||||
|
||||
Where's your head at?
|
||||
```
|
||||
|
||||
**User brings a specific problem:**
|
||||
```
|
||||
User: The auth system is a mess
|
||||
|
||||
You: [reads codebase]
|
||||
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ CURRENT AUTH FLOW │
|
||||
└─────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────┼─────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ Google │ │ GitHub │ │ Email │
|
||||
│ OAuth │ │ OAuth │ │ Magic │
|
||||
└────┬────┘ └────┬────┘ └────┬────┘
|
||||
│ │ │
|
||||
└─────────────┼─────────────┘
|
||||
▼
|
||||
┌───────────┐
|
||||
│ Session │
|
||||
└─────┬─────┘
|
||||
│
|
||||
▼
|
||||
┌───────────┐
|
||||
│ Perms │
|
||||
└───────────┘
|
||||
|
||||
I see three tangles. Which one's burning?
|
||||
```
|
||||
|
||||
**User is stuck mid-implementation:**
|
||||
```
|
||||
User: /opsx:explore add-auth-system
|
||||
The OAuth integration is more complex than expected
|
||||
|
||||
You: [reads change artifacts]
|
||||
|
||||
You're on task 4: "Implement OAuth flow"
|
||||
|
||||
Let me trace what's involved...
|
||||
|
||||
[draws diagram, explores options, suggests paths]
|
||||
|
||||
Want to update the design to reflect this?
|
||||
Or add a spike task to investigate?
|
||||
```
|
||||
|
||||
**User wants to compare options:**
|
||||
```
|
||||
User: Should we use Postgres or SQLite?
|
||||
|
||||
You: Generic answer is boring. What's the context?
|
||||
|
||||
User: A CLI tool that tracks local dev environments
|
||||
|
||||
You: That changes everything.
|
||||
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ CLI TOOL DATA STORAGE │
|
||||
└─────────────────────────────────────────────────┘
|
||||
|
||||
Key constraints:
|
||||
• No daemon running
|
||||
• Must work offline
|
||||
• Single user
|
||||
|
||||
SQLite Postgres
|
||||
Deployment embedded ✓ needs server ✗
|
||||
Offline yes ✓ no ✗
|
||||
Single file yes ✓ no ✗
|
||||
|
||||
SQLite. Not even close.
|
||||
|
||||
Unless... is there a sync component?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ending Discovery
|
||||
|
||||
There's no required ending. Discovery might:
|
||||
|
||||
- **Flow into a proposal**: "Ready to start? I can create a change proposal."
|
||||
- **Result in artifact updates**: "Updated design.md with these decisions"
|
||||
- **Just provide clarity**: User has what they need, moves on
|
||||
- **Continue later**: "We can pick this up anytime"
|
||||
|
||||
When it feels like things are crystallizing, you might summarize:
|
||||
|
||||
```
|
||||
## What We Figured Out
|
||||
|
||||
**The problem**: [crystallized understanding]
|
||||
|
||||
**The approach**: [if one emerged]
|
||||
|
||||
**Open questions**: [if any remain]
|
||||
|
||||
**Next steps** (if ready):
|
||||
- Create a change proposal
|
||||
- Keep exploring: just keep talking
|
||||
```
|
||||
|
||||
But this summary is optional. Sometimes the thinking IS the value.
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.
|
||||
- **Don't fake understanding** - If something is unclear, dig deeper
|
||||
- **Don't rush** - Discovery is thinking time, not task time
|
||||
- **Don't force structure** - Let patterns emerge naturally
|
||||
- **Don't auto-capture** - Offer to save insights, don't just do it
|
||||
- **Do visualize** - A good diagram is worth many paragraphs
|
||||
- **Do explore the codebase** - Ground discussions in reality
|
||||
- **Do question assumptions** - Including the user's and your own
|
||||
281
.claude/skills/openspec-lock-consensus/SKILL.md
Normal file
281
.claude/skills/openspec-lock-consensus/SKILL.md
Normal file
@@ -0,0 +1,281 @@
|
||||
---
|
||||
name: openspec-lock-consensus
|
||||
description: 锁定共识 - 在探索讨论后,将讨论结果锁定为正式共识文档。防止后续提案偏离讨论内容。
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: junhong
|
||||
version: "1.1"
|
||||
---
|
||||
|
||||
# 共识锁定 Skill
|
||||
|
||||
在 `/opsx:explore` 讨论后,使用此 skill 将讨论结果锁定为正式共识。共识文档是后续所有 artifact 的基础约束。
|
||||
|
||||
## 触发方式
|
||||
|
||||
```
|
||||
/opsx:lock <change-name>
|
||||
```
|
||||
|
||||
或在探索结束后,AI 主动提议:
|
||||
> "讨论已经比较清晰了,要锁定共识吗?"
|
||||
|
||||
---
|
||||
|
||||
## 工作流程
|
||||
|
||||
### Step 1: 整理讨论要点
|
||||
|
||||
从对话中提取以下四个维度的共识:
|
||||
|
||||
| 维度 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| **要做什么** | 明确的功能范围 | "支持批量导入 IoT 卡" |
|
||||
| **不做什么** | 明确排除的内容 | "不支持实时同步,仅定时批量" |
|
||||
| **关键约束** | 技术/业务限制 | "必须使用 Asynq 异步任务" |
|
||||
| **验收标准** | 如何判断完成 | "导入 1000 张卡 < 30s" |
|
||||
|
||||
### Step 2: 使用 Question_tool 逐维度确认
|
||||
|
||||
**必须使用 Question_tool 进行结构化确认**,每个维度一个问题:
|
||||
|
||||
```typescript
|
||||
// 示例:确认"要做什么"
|
||||
Question_tool({
|
||||
questions: [{
|
||||
header: "确认:要做什么",
|
||||
question: "以下是整理的功能范围,请确认:\n\n" +
|
||||
"1. 功能点 A\n" +
|
||||
"2. 功能点 B\n" +
|
||||
"3. 功能点 C\n\n" +
|
||||
"是否准确完整?",
|
||||
options: [
|
||||
{ label: "确认无误", description: "以上内容准确完整" },
|
||||
{ label: "需要补充", description: "有遗漏的功能点" },
|
||||
{ label: "需要删减", description: "有不应该包含的内容" }
|
||||
],
|
||||
multiple: false
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
**如果用户选择"需要补充"或"需要删减"**:
|
||||
- 用户会通过自定义输入提供修改意见
|
||||
- 根据反馈更新列表,再次使用 Question_tool 确认
|
||||
|
||||
**确认流程**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Question_tool: 确认"要做什么" │
|
||||
│ ├── 用户选择"确认无误" → 进入下一维度 │
|
||||
│ └── 用户选择其他/自定义 → 修改后重新确认 │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ Question_tool: 确认"不做什么" │
|
||||
│ ├── 用户选择"确认无误" → 进入下一维度 │
|
||||
│ └── 用户选择其他/自定义 → 修改后重新确认 │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ Question_tool: 确认"关键约束" │
|
||||
│ ├── 用户选择"确认无误" → 进入下一维度 │
|
||||
│ └── 用户选择其他/自定义 → 修改后重新确认 │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ Question_tool: 确认"验收标准" │
|
||||
│ ├── 用户选择"确认无误" → 生成 consensus.md │
|
||||
│ └── 用户选择其他/自定义 → 修改后重新确认 │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Step 3: 生成 consensus.md
|
||||
|
||||
所有维度确认后,创建文件:
|
||||
|
||||
```bash
|
||||
# 检查 change 是否存在
|
||||
openspec list --json
|
||||
|
||||
# 如果 change 不存在,先创建
|
||||
# openspec new <change-name>
|
||||
|
||||
# 写入 consensus.md
|
||||
```
|
||||
|
||||
**文件路径**: `openspec/changes/<change-name>/consensus.md`
|
||||
|
||||
---
|
||||
|
||||
## Question_tool 使用规范
|
||||
|
||||
### 每个维度的问题模板
|
||||
|
||||
**1. 要做什么**
|
||||
```typescript
|
||||
{
|
||||
header: "确认:要做什么",
|
||||
question: "以下是整理的【功能范围】:\n\n" +
|
||||
items.map((item, i) => `${i+1}. ${item}`).join('\n') +
|
||||
"\n\n请确认是否准确完整?",
|
||||
options: [
|
||||
{ label: "确认无误", description: "功能范围准确完整" },
|
||||
{ label: "需要补充", description: "有遗漏的功能点" },
|
||||
{ label: "需要删减", description: "有不应该包含的内容" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**2. 不做什么**
|
||||
```typescript
|
||||
{
|
||||
header: "确认:不做什么",
|
||||
question: "以下是明确【排除的内容】:\n\n" +
|
||||
items.map((item, i) => `${i+1}. ${item}`).join('\n') +
|
||||
"\n\n请确认是否正确?",
|
||||
options: [
|
||||
{ label: "确认无误", description: "排除范围正确" },
|
||||
{ label: "需要补充", description: "还有其他需要排除的" },
|
||||
{ label: "需要删减", description: "有些不应该排除" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**3. 关键约束**
|
||||
```typescript
|
||||
{
|
||||
header: "确认:关键约束",
|
||||
question: "以下是【关键约束】:\n\n" +
|
||||
items.map((item, i) => `${i+1}. ${item}`).join('\n') +
|
||||
"\n\n请确认是否正确?",
|
||||
options: [
|
||||
{ label: "确认无误", description: "约束条件正确" },
|
||||
{ label: "需要补充", description: "还有其他约束" },
|
||||
{ label: "需要修改", description: "约束描述不准确" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**4. 验收标准**
|
||||
```typescript
|
||||
{
|
||||
header: "确认:验收标准",
|
||||
question: "以下是【验收标准】(必须可测量):\n\n" +
|
||||
items.map((item, i) => `${i+1}. ${item}`).join('\n') +
|
||||
"\n\n请确认是否正确?",
|
||||
options: [
|
||||
{ label: "确认无误", description: "验收标准清晰可测量" },
|
||||
{ label: "需要补充", description: "还有其他验收标准" },
|
||||
{ label: "需要修改", description: "标准不够清晰或无法测量" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 处理用户反馈
|
||||
|
||||
当用户选择非"确认无误"选项或提供自定义输入时:
|
||||
|
||||
1. 解析用户的修改意见
|
||||
2. 更新对应维度的内容
|
||||
3. 再次使用 Question_tool 确认更新后的内容
|
||||
4. 重复直到用户选择"确认无误"
|
||||
|
||||
---
|
||||
|
||||
## consensus.md 模板
|
||||
|
||||
```markdown
|
||||
# 共识文档
|
||||
|
||||
**Change**: <change-name>
|
||||
**确认时间**: <timestamp>
|
||||
**确认人**: 用户
|
||||
|
||||
---
|
||||
|
||||
## 1. 要做什么
|
||||
|
||||
- [x] 功能点 A(已确认)
|
||||
- [x] 功能点 B(已确认)
|
||||
- [x] 功能点 C(已确认)
|
||||
|
||||
## 2. 不做什么
|
||||
|
||||
- [x] 排除项 A(已确认)
|
||||
- [x] 排除项 B(已确认)
|
||||
|
||||
## 3. 关键约束
|
||||
|
||||
- [x] 技术约束 A(已确认)
|
||||
- [x] 业务约束 B(已确认)
|
||||
|
||||
## 4. 验收标准
|
||||
|
||||
- [x] 验收标准 A(已确认)
|
||||
- [x] 验收标准 B(已确认)
|
||||
|
||||
---
|
||||
|
||||
## 讨论背景
|
||||
|
||||
<简要总结讨论的核心问题和解决方向>
|
||||
|
||||
## 关键决策记录
|
||||
|
||||
| 决策点 | 选择 | 原因 |
|
||||
|--------|------|------|
|
||||
| 决策 1 | 选项 A | 理由... |
|
||||
| 决策 2 | 选项 B | 理由... |
|
||||
|
||||
---
|
||||
|
||||
**签字确认**: 用户已通过 Question_tool 逐条确认以上内容
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 后续流程绑定
|
||||
|
||||
### Proposal 生成时
|
||||
|
||||
`/opsx:continue` 生成 proposal 时,**必须**:
|
||||
|
||||
1. 读取 `consensus.md`
|
||||
2. 确保 proposal 的 Capabilities 覆盖"要做什么"中的每一项
|
||||
3. 确保 proposal 不包含"不做什么"中的内容
|
||||
4. 确保 proposal 遵守"关键约束"
|
||||
|
||||
### 验证机制
|
||||
|
||||
如果 proposal 与 consensus 不一致,输出警告:
|
||||
|
||||
```
|
||||
⚠️ Proposal 验证警告:
|
||||
|
||||
共识中"要做什么"但 Proposal 未提及:
|
||||
- 功能点 C
|
||||
|
||||
共识中"不做什么"但 Proposal 包含:
|
||||
- 排除项 A
|
||||
|
||||
建议修正 Proposal 或更新共识。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **必须使用 Question_tool** - 不要用纯文本确认
|
||||
- **逐维度确认** - 四个维度分开确认,不要合并
|
||||
- **不要跳过确认** - 每个维度都必须让用户明确确认
|
||||
- **不要自作主张** - 只整理讨论中明确提到的内容
|
||||
- **避免模糊表述** - "尽量"、"可能"、"考虑"等词汇需要明确化
|
||||
- **验收标准必须可测量** - 避免"性能要好"这类无法验证的标准
|
||||
|
||||
---
|
||||
|
||||
## 与其他 Skills 的关系
|
||||
|
||||
| Skill | 关系 |
|
||||
|-------|------|
|
||||
| `openspec-explore` | 探索结束后触发 lock |
|
||||
| `openspec-new-change` | lock 后触发 new(如果 change 不存在)|
|
||||
| `openspec-continue-change` | 生成 proposal 时读取 consensus 验证 |
|
||||
| `openspec-generate-acceptance-tests` | 从 consensus 的验收标准生成测试骨架 |
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
name: openspec-new-change
|
||||
description: Start a new OpenSpec change using the experimental artifact workflow. Use when the user wants to create a new feature, fix, or modification with a structured step-by-step approach.
|
||||
---
|
||||
|
||||
Start a new change using the experimental artifact-driven approach.
|
||||
|
||||
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no clear input provided, ask what they want to build**
|
||||
|
||||
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
2. **Select a workflow schema**
|
||||
|
||||
Run `openspec schemas --json` to get available schemas with descriptions.
|
||||
|
||||
Use the **AskUserQuestion tool** to let the user choose a workflow:
|
||||
- Present each schema with its description
|
||||
- Mark `spec-driven` as "(default)" if it's available
|
||||
- Example options: "spec-driven - proposal → specs → design → tasks (default)", "tdd - tests → implementation → docs"
|
||||
|
||||
If user doesn't have a preference, default to `spec-driven`.
|
||||
|
||||
3. **Create the change directory**
|
||||
```bash
|
||||
openspec new change "<name>" --schema "<selected-schema>"
|
||||
```
|
||||
This creates a scaffolded change at `openspec/changes/<name>/` with the selected schema.
|
||||
|
||||
4. **Show the artifact status**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
This shows which artifacts need to be created and which are ready (dependencies satisfied).
|
||||
|
||||
5. **Get instructions for the first artifact**
|
||||
The first artifact depends on the schema (e.g., `proposal` for spec-driven, `spec` for tdd).
|
||||
Check the status output to find the first artifact with status "ready".
|
||||
```bash
|
||||
openspec instructions <first-artifact-id> --change "<name>"
|
||||
```
|
||||
This outputs the template and context for creating the first artifact.
|
||||
|
||||
6. **STOP and wait for user direction**
|
||||
|
||||
**Output**
|
||||
|
||||
After completing the steps, summarize:
|
||||
- Change name and location
|
||||
- Selected schema/workflow and its artifact sequence
|
||||
- Current status (0/N artifacts complete)
|
||||
- The template for the first artifact
|
||||
- Prompt: "Ready to create the first artifact? Just describe what this change is about and I'll draft it, or ask me to continue."
|
||||
|
||||
**Guardrails**
|
||||
- Do NOT create any artifacts yet - just show the instructions
|
||||
- Do NOT advance beyond showing the first artifact template
|
||||
- If the name is invalid (not kebab-case), ask for a valid name
|
||||
- If a change with that name already exists, suggest continuing that change instead
|
||||
- Always pass --schema to preserve the user's workflow choice
|
||||
@@ -1,9 +1,24 @@
|
||||
---
|
||||
name: openspec-ff-change
|
||||
description: Fast-forward through OpenSpec artifact creation. Use when the user wants to quickly create all artifacts needed for implementation without stepping through each one individually.
|
||||
name: openspec-propose
|
||||
description: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.2.0"
|
||||
---
|
||||
|
||||
Fast-forward through artifact creation - generate everything needed to start implementation in one go.
|
||||
Propose a new change - create the change and generate all artifacts in one step.
|
||||
|
||||
I'll create a change with artifacts:
|
||||
- proposal.md (what & why)
|
||||
- design.md (how)
|
||||
- tasks.md (implementation steps)
|
||||
|
||||
When ready to implement, run /opsx:apply
|
||||
|
||||
---
|
||||
|
||||
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
||||
|
||||
@@ -22,7 +37,7 @@ Fast-forward through artifact creation - generate everything needed to start imp
|
||||
```bash
|
||||
openspec new change "<name>"
|
||||
```
|
||||
This creates a scaffolded change at `openspec/changes/<name>/`.
|
||||
This creates a scaffolded change at `openspec/changes/<name>/` with `.openspec.yaml`.
|
||||
|
||||
3. **Get the artifact build order**
|
||||
```bash
|
||||
@@ -44,13 +59,16 @@ Fast-forward through artifact creation - generate everything needed to start imp
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- The instructions JSON includes:
|
||||
- `template`: The template content to use
|
||||
- `context`: Project background (constraints for you - do NOT include in output)
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance for this artifact type
|
||||
- `outputPath`: Where to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- Read any completed dependency files for context
|
||||
- Create the artifact file following the schema's `instruction`
|
||||
- Show brief progress: "✓ Created <artifact-id>"
|
||||
- Create the artifact file using `template` as the structure
|
||||
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
||||
- Show brief progress: "Created <artifact-id>"
|
||||
|
||||
b. **Continue until all `applyRequires` artifacts are complete**
|
||||
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
||||
@@ -79,11 +97,14 @@ After completing all artifacts, summarize:
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type
|
||||
- The schema defines what each artifact should contain - follow it
|
||||
- Read dependency artifacts for context before creating new ones
|
||||
- Use the `template` as a starting point, filling in based on context
|
||||
- Use `template` as the structure for your output file - fill in its sections
|
||||
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
||||
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
||||
- These guide what you write, but should never appear in the output
|
||||
|
||||
**Guardrails**
|
||||
- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)
|
||||
- Always read dependency artifacts before creating a new one
|
||||
- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum
|
||||
- If a change with that name already exists, suggest continuing that change instead
|
||||
- If a change with that name already exists, ask if user wants to continue it or create a new one
|
||||
- Verify each artifact file exists after writing before proceeding to next
|
||||
@@ -1,132 +0,0 @@
|
||||
---
|
||||
name: openspec-sync-specs
|
||||
description: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.
|
||||
---
|
||||
|
||||
Sync delta specs from a change to main specs.
|
||||
|
||||
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
|
||||
Show changes that have delta specs (under `specs/` directory).
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Find delta specs**
|
||||
|
||||
Look for delta spec files in `openspec/changes/<name>/specs/*/spec.md`.
|
||||
|
||||
Each delta spec file contains sections like:
|
||||
- `## ADDED Requirements` - New requirements to add
|
||||
- `## MODIFIED Requirements` - Changes to existing requirements
|
||||
- `## REMOVED Requirements` - Requirements to remove
|
||||
- `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)
|
||||
|
||||
If no delta specs found, inform user and stop.
|
||||
|
||||
3. **For each delta spec, apply changes to main specs**
|
||||
|
||||
For each capability with a delta spec at `openspec/changes/<name>/specs/<capability>/spec.md`:
|
||||
|
||||
a. **Read the delta spec** to understand the intended changes
|
||||
|
||||
b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)
|
||||
|
||||
c. **Apply changes intelligently**:
|
||||
|
||||
**ADDED Requirements:**
|
||||
- If requirement doesn't exist in main spec → add it
|
||||
- If requirement already exists → update it to match (treat as implicit MODIFIED)
|
||||
|
||||
**MODIFIED Requirements:**
|
||||
- Find the requirement in main spec
|
||||
- Apply the changes - this can be:
|
||||
- Adding new scenarios (don't need to copy existing ones)
|
||||
- Modifying existing scenarios
|
||||
- Changing the requirement description
|
||||
- Preserve scenarios/content not mentioned in the delta
|
||||
|
||||
**REMOVED Requirements:**
|
||||
- Remove the entire requirement block from main spec
|
||||
|
||||
**RENAMED Requirements:**
|
||||
- Find the FROM requirement, rename to TO
|
||||
|
||||
d. **Create new main spec** if capability doesn't exist yet:
|
||||
- Create `openspec/specs/<capability>/spec.md`
|
||||
- Add Purpose section (can be brief, mark as TBD)
|
||||
- Add Requirements section with the ADDED requirements
|
||||
|
||||
4. **Show summary**
|
||||
|
||||
After applying all changes, summarize:
|
||||
- Which capabilities were updated
|
||||
- What changes were made (requirements added/modified/removed/renamed)
|
||||
|
||||
**Delta Spec Format Reference**
|
||||
|
||||
```markdown
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: New Feature
|
||||
The system SHALL do something new.
|
||||
|
||||
#### Scenario: Basic case
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Existing Feature
|
||||
#### Scenario: New scenario to add
|
||||
- **WHEN** user does A
|
||||
- **THEN** system does B
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: Deprecated Feature
|
||||
|
||||
## RENAMED Requirements
|
||||
|
||||
- FROM: `### Requirement: Old Name`
|
||||
- TO: `### Requirement: New Name`
|
||||
```
|
||||
|
||||
**Key Principle: Intelligent Merging**
|
||||
|
||||
Unlike programmatic merging, you can apply **partial updates**:
|
||||
- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios
|
||||
- The delta represents *intent*, not a wholesale replacement
|
||||
- Use your judgment to merge changes sensibly
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
## Specs Synced: <change-name>
|
||||
|
||||
Updated main specs:
|
||||
|
||||
**<capability-1>**:
|
||||
- Added requirement: "New Feature"
|
||||
- Modified requirement: "Existing Feature" (added 1 scenario)
|
||||
|
||||
**<capability-2>**:
|
||||
- Created new spec file
|
||||
- Added requirement: "Another Feature"
|
||||
|
||||
Main specs are now updated. The change remains active - archive when implementation is complete.
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Read both delta and main specs before making changes
|
||||
- Preserve existing content not mentioned in delta
|
||||
- If something is unclear, ask for clarification
|
||||
- Show what you're changing as you go
|
||||
- The operation should be idempotent - running twice should give same result
|
||||
1
.claude/skills/prototype
Symbolic link
1
.claude/skills/prototype
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/prototype
|
||||
1
.claude/skills/setup-matt-pocock-skills
Symbolic link
1
.claude/skills/setup-matt-pocock-skills
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/setup-matt-pocock-skills
|
||||
1
.claude/skills/tdd
Symbolic link
1
.claude/skills/tdd
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/tdd
|
||||
1
.claude/skills/teach
Symbolic link
1
.claude/skills/teach
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/teach
|
||||
1
.claude/skills/to-issues
Symbolic link
1
.claude/skills/to-issues
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/to-issues
|
||||
1
.claude/skills/to-prd
Symbolic link
1
.claude/skills/to-prd
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/to-prd
|
||||
1
.claude/skills/triage
Symbolic link
1
.claude/skills/triage
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/triage
|
||||
1
.claude/skills/write-a-skill
Symbolic link
1
.claude/skills/write-a-skill
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/write-a-skill
|
||||
1
.claude/skills/zoom-out
Symbolic link
1
.claude/skills/zoom-out
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/zoom-out
|
||||
150
.codex/prompts/opsx-apply.md
Normal file
150
.codex/prompts/opsx-apply.md
Normal file
@@ -0,0 +1,150 @@
|
||||
---
|
||||
description: Implement tasks from an OpenSpec change (Experimental)
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Implement tasks from an OpenSpec change.
|
||||
|
||||
**Input**: Optionally specify a change name (e.g., `/opsx:apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:apply <other>`).
|
||||
|
||||
2. **Check status to understand the schema**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
||||
- Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
|
||||
|
||||
3. **Get apply instructions**
|
||||
|
||||
```bash
|
||||
openspec instructions apply --change "<name>" --json
|
||||
```
|
||||
|
||||
This returns:
|
||||
- Context file paths (varies by schema)
|
||||
- Progress (total, complete, remaining)
|
||||
- Task list with status
|
||||
- Dynamic instruction based on current state
|
||||
|
||||
**Handle states:**
|
||||
- If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx:continue`
|
||||
- If `state: "all_done"`: congratulate, suggest archive
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
4. **Read context files**
|
||||
|
||||
Read the files listed in `contextFiles` from the apply instructions output.
|
||||
The files depend on the schema being used:
|
||||
- **spec-driven**: proposal, specs, design, tasks
|
||||
- Other schemas: follow the contextFiles from CLI output
|
||||
|
||||
5. **Show current progress**
|
||||
|
||||
Display:
|
||||
- Schema being used
|
||||
- Progress: "N/M tasks complete"
|
||||
- Remaining tasks overview
|
||||
- Dynamic instruction from CLI
|
||||
|
||||
6. **Implement tasks (loop until done or blocked)**
|
||||
|
||||
For each pending task:
|
||||
- Show which task is being worked on
|
||||
- Make the code changes required
|
||||
- Keep changes minimal and focused
|
||||
- Mark task complete in the tasks file: `- [ ]` → `- [x]`
|
||||
- Continue to next task
|
||||
|
||||
**Pause if:**
|
||||
- Task is unclear → ask for clarification
|
||||
- Implementation reveals a design issue → suggest updating artifacts
|
||||
- Error or blocker encountered → report and wait for guidance
|
||||
- User interrupts
|
||||
|
||||
7. **On completion or pause, show status**
|
||||
|
||||
Display:
|
||||
- Tasks completed this session
|
||||
- Overall progress: "N/M tasks complete"
|
||||
- If all done: suggest archive
|
||||
- If paused: explain why and wait for guidance
|
||||
|
||||
**Output During Implementation**
|
||||
|
||||
```
|
||||
## Implementing: <change-name> (schema: <schema-name>)
|
||||
|
||||
Working on task 3/7: <task description>
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
|
||||
Working on task 4/7: <task description>
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
```
|
||||
|
||||
**Output On Completion**
|
||||
|
||||
```
|
||||
## Implementation Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Progress:** 7/7 tasks complete ✓
|
||||
|
||||
### Completed This Session
|
||||
- [x] Task 1
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tasks complete! Ready to archive this change.
|
||||
```
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
|
||||
```
|
||||
## Implementation Paused
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Progress:** 4/7 tasks complete
|
||||
|
||||
### Issue Encountered
|
||||
<description of the issue>
|
||||
|
||||
**Options:**
|
||||
1. <option 1>
|
||||
2. <option 2>
|
||||
3. Other approach
|
||||
|
||||
What would you like to do?
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Keep going through tasks until done or blocked
|
||||
- Always read context files before starting (from the apply instructions output)
|
||||
- If task is ambiguous, pause and ask before implementing
|
||||
- If implementation reveals issues, pause and suggest artifact updates
|
||||
- Keep code changes minimal and scoped to each task
|
||||
- Update task checkbox immediately after completing each task
|
||||
- Pause on errors, blockers, or unclear requirements - don't guess
|
||||
- Use contextFiles from CLI output, don't assume specific file names
|
||||
|
||||
**Fluid Workflow Integration**
|
||||
|
||||
This skill supports the "actions on a change" model:
|
||||
|
||||
- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions
|
||||
- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly
|
||||
155
.codex/prompts/opsx-archive.md
Normal file
155
.codex/prompts/opsx-archive.md
Normal file
@@ -0,0 +1,155 @@
|
||||
---
|
||||
description: Archive a completed change in the experimental workflow
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
**Input**: Optionally specify a change name after `/opsx:archive` (e.g., `/opsx:archive add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
|
||||
Show only active changes (not already archived).
|
||||
Include the schema used for each change if available.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Check artifact completion status**
|
||||
|
||||
Run `openspec status --change "<name>" --json` to check artifact completion.
|
||||
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used
|
||||
- `artifacts`: List of artifacts with their status (`done` or other)
|
||||
|
||||
**If any artifacts are not `done`:**
|
||||
- Display warning listing incomplete artifacts
|
||||
- Prompt user for confirmation to continue
|
||||
- Proceed if user confirms
|
||||
|
||||
3. **Check task completion status**
|
||||
|
||||
Read the tasks file (typically `tasks.md`) to check for incomplete tasks.
|
||||
|
||||
Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).
|
||||
|
||||
**If incomplete tasks found:**
|
||||
- Display warning showing count of incomplete tasks
|
||||
- Prompt user for confirmation to continue
|
||||
- Proceed if user confirms
|
||||
|
||||
**If no tasks file exists:** Proceed without task-related warning.
|
||||
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
Check for delta specs at `openspec/changes/<name>/specs/`. If none exist, proceed without sync prompt.
|
||||
|
||||
**If delta specs exist:**
|
||||
- Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`
|
||||
- Determine what changes would be applied (adds, modifications, removals, renames)
|
||||
- Show a combined summary before prompting
|
||||
|
||||
**Prompt options:**
|
||||
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
||||
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
||||
|
||||
If user chooses sync, execute `/opsx:sync` logic. Proceed to archive regardless of choice.
|
||||
|
||||
5. **Perform the archive**
|
||||
|
||||
Create the archive directory if it doesn't exist:
|
||||
```bash
|
||||
mkdir -p openspec/changes/archive
|
||||
```
|
||||
|
||||
Generate target name using current date: `YYYY-MM-DD-<change-name>`
|
||||
|
||||
**Check if target already exists:**
|
||||
- If yes: Fail with error, suggest renaming existing archive or using different date
|
||||
- If no: Move the change directory to archive
|
||||
|
||||
```bash
|
||||
mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
|
||||
```
|
||||
|
||||
6. **Display summary**
|
||||
|
||||
Show archive completion summary including:
|
||||
- Change name
|
||||
- Schema that was used
|
||||
- Archive location
|
||||
- Spec sync status (synced / sync skipped / no delta specs)
|
||||
- Note about any warnings (incomplete artifacts/tasks)
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** ✓ Synced to main specs
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
```
|
||||
|
||||
**Output On Success (No Delta Specs)**
|
||||
|
||||
```
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** No delta specs
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
```
|
||||
|
||||
**Output On Success With Warnings**
|
||||
|
||||
```
|
||||
## Archive Complete (with warnings)
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** Sync skipped (user chose to skip)
|
||||
|
||||
**Warnings:**
|
||||
- Archived with 2 incomplete artifacts
|
||||
- Archived with 3 incomplete tasks
|
||||
- Delta spec sync was skipped (user chose to skip)
|
||||
|
||||
Review the archive if this was not intentional.
|
||||
```
|
||||
|
||||
**Output On Error (Archive Exists)**
|
||||
|
||||
```
|
||||
## Archive Failed
|
||||
|
||||
**Change:** <change-name>
|
||||
**Target:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
|
||||
Target archive directory already exists.
|
||||
|
||||
**Options:**
|
||||
1. Rename the existing archive
|
||||
2. Delete the existing archive if it's a duplicate
|
||||
3. Wait until a different date to archive
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Always prompt for change selection if not provided
|
||||
- Use artifact graph (openspec status --json) for completion checking
|
||||
- Don't block archive on warnings - just inform and confirm
|
||||
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
||||
- Show clear summary of what happened
|
||||
- If sync is requested, use /opsx:sync approach (agent-driven)
|
||||
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
||||
240
.codex/prompts/opsx-bulk-archive.md
Normal file
240
.codex/prompts/opsx-bulk-archive.md
Normal file
@@ -0,0 +1,240 @@
|
||||
---
|
||||
description: Archive multiple completed changes at once
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Archive multiple completed changes in a single operation.
|
||||
|
||||
This skill allows you to batch-archive changes, handling spec conflicts intelligently by checking the codebase to determine what's actually implemented.
|
||||
|
||||
**Input**: None required (prompts for selection)
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Get active changes**
|
||||
|
||||
Run `openspec list --json` to get all active changes.
|
||||
|
||||
If no active changes exist, inform user and stop.
|
||||
|
||||
2. **Prompt for change selection**
|
||||
|
||||
Use **AskUserQuestion tool** with multi-select to let user choose changes:
|
||||
- Show each change with its schema
|
||||
- Include an option for "All changes"
|
||||
- Allow any number of selections (1+ works, 2+ is the typical use case)
|
||||
|
||||
**IMPORTANT**: Do NOT auto-select. Always let the user choose.
|
||||
|
||||
3. **Batch validation - gather status for all selected changes**
|
||||
|
||||
For each selected change, collect:
|
||||
|
||||
a. **Artifact status** - Run `openspec status --change "<name>" --json`
|
||||
- Parse `schemaName` and `artifacts` list
|
||||
- Note which artifacts are `done` vs other states
|
||||
|
||||
b. **Task completion** - Read `openspec/changes/<name>/tasks.md`
|
||||
- Count `- [ ]` (incomplete) vs `- [x]` (complete)
|
||||
- If no tasks file exists, note as "No tasks"
|
||||
|
||||
c. **Delta specs** - Check `openspec/changes/<name>/specs/` directory
|
||||
- List which capability specs exist
|
||||
- For each, extract requirement names (lines matching `### Requirement: <name>`)
|
||||
|
||||
4. **Detect spec conflicts**
|
||||
|
||||
Build a map of `capability -> [changes that touch it]`:
|
||||
|
||||
```
|
||||
auth -> [change-a, change-b] <- CONFLICT (2+ changes)
|
||||
api -> [change-c] <- OK (only 1 change)
|
||||
```
|
||||
|
||||
A conflict exists when 2+ selected changes have delta specs for the same capability.
|
||||
|
||||
5. **Resolve conflicts agentically**
|
||||
|
||||
**For each conflict**, investigate the codebase:
|
||||
|
||||
a. **Read the delta specs** from each conflicting change to understand what each claims to add/modify
|
||||
|
||||
b. **Search the codebase** for implementation evidence:
|
||||
- Look for code implementing requirements from each delta spec
|
||||
- Check for related files, functions, or tests
|
||||
|
||||
c. **Determine resolution**:
|
||||
- If only one change is actually implemented -> sync that one's specs
|
||||
- If both implemented -> apply in chronological order (older first, newer overwrites)
|
||||
- If neither implemented -> skip spec sync, warn user
|
||||
|
||||
d. **Record resolution** for each conflict:
|
||||
- Which change's specs to apply
|
||||
- In what order (if both)
|
||||
- Rationale (what was found in codebase)
|
||||
|
||||
6. **Show consolidated status table**
|
||||
|
||||
Display a table summarizing all changes:
|
||||
|
||||
```
|
||||
| Change | Artifacts | Tasks | Specs | Conflicts | Status |
|
||||
|---------------------|-----------|-------|---------|-----------|--------|
|
||||
| schema-management | Done | 5/5 | 2 delta | None | Ready |
|
||||
| project-config | Done | 3/3 | 1 delta | None | Ready |
|
||||
| add-oauth | Done | 4/4 | 1 delta | auth (!) | Ready* |
|
||||
| add-verify-skill | 1 left | 2/5 | None | None | Warn |
|
||||
```
|
||||
|
||||
For conflicts, show the resolution:
|
||||
```
|
||||
* Conflict resolution:
|
||||
- auth spec: Will apply add-oauth then add-jwt (both implemented, chronological order)
|
||||
```
|
||||
|
||||
For incomplete changes, show warnings:
|
||||
```
|
||||
Warnings:
|
||||
- add-verify-skill: 1 incomplete artifact, 3 incomplete tasks
|
||||
```
|
||||
|
||||
7. **Confirm batch operation**
|
||||
|
||||
Use **AskUserQuestion tool** with a single confirmation:
|
||||
|
||||
- "Archive N changes?" with options based on status
|
||||
- Options might include:
|
||||
- "Archive all N changes"
|
||||
- "Archive only N ready changes (skip incomplete)"
|
||||
- "Cancel"
|
||||
|
||||
If there are incomplete changes, make clear they'll be archived with warnings.
|
||||
|
||||
8. **Execute archive for each confirmed change**
|
||||
|
||||
Process changes in the determined order (respecting conflict resolution):
|
||||
|
||||
a. **Sync specs** if delta specs exist:
|
||||
- Use the openspec-sync-specs approach (agent-driven intelligent merge)
|
||||
- For conflicts, apply in resolved order
|
||||
- Track if sync was done
|
||||
|
||||
b. **Perform the archive**:
|
||||
```bash
|
||||
mkdir -p openspec/changes/archive
|
||||
mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
|
||||
```
|
||||
|
||||
c. **Track outcome** for each change:
|
||||
- Success: archived successfully
|
||||
- Failed: error during archive (record error)
|
||||
- Skipped: user chose not to archive (if applicable)
|
||||
|
||||
9. **Display summary**
|
||||
|
||||
Show final results:
|
||||
|
||||
```
|
||||
## Bulk Archive Complete
|
||||
|
||||
Archived 3 changes:
|
||||
- schema-management-cli -> archive/2026-01-19-schema-management-cli/
|
||||
- project-config -> archive/2026-01-19-project-config/
|
||||
- add-oauth -> archive/2026-01-19-add-oauth/
|
||||
|
||||
Skipped 1 change:
|
||||
- add-verify-skill (user chose not to archive incomplete)
|
||||
|
||||
Spec sync summary:
|
||||
- 4 delta specs synced to main specs
|
||||
- 1 conflict resolved (auth: applied both in chronological order)
|
||||
```
|
||||
|
||||
If any failures:
|
||||
```
|
||||
Failed 1 change:
|
||||
- some-change: Archive directory already exists
|
||||
```
|
||||
|
||||
**Conflict Resolution Examples**
|
||||
|
||||
Example 1: Only one implemented
|
||||
```
|
||||
Conflict: specs/auth/spec.md touched by [add-oauth, add-jwt]
|
||||
|
||||
Checking add-oauth:
|
||||
- Delta adds "OAuth Provider Integration" requirement
|
||||
- Searching codebase... found src/auth/oauth.ts implementing OAuth flow
|
||||
|
||||
Checking add-jwt:
|
||||
- Delta adds "JWT Token Handling" requirement
|
||||
- Searching codebase... no JWT implementation found
|
||||
|
||||
Resolution: Only add-oauth is implemented. Will sync add-oauth specs only.
|
||||
```
|
||||
|
||||
Example 2: Both implemented
|
||||
```
|
||||
Conflict: specs/api/spec.md touched by [add-rest-api, add-graphql]
|
||||
|
||||
Checking add-rest-api (created 2026-01-10):
|
||||
- Delta adds "REST Endpoints" requirement
|
||||
- Searching codebase... found src/api/rest.ts
|
||||
|
||||
Checking add-graphql (created 2026-01-15):
|
||||
- Delta adds "GraphQL Schema" requirement
|
||||
- Searching codebase... found src/api/graphql.ts
|
||||
|
||||
Resolution: Both implemented. Will apply add-rest-api specs first,
|
||||
then add-graphql specs (chronological order, newer takes precedence).
|
||||
```
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
## Bulk Archive Complete
|
||||
|
||||
Archived N changes:
|
||||
- <change-1> -> archive/YYYY-MM-DD-<change-1>/
|
||||
- <change-2> -> archive/YYYY-MM-DD-<change-2>/
|
||||
|
||||
Spec sync summary:
|
||||
- N delta specs synced to main specs
|
||||
- No conflicts (or: M conflicts resolved)
|
||||
```
|
||||
|
||||
**Output On Partial Success**
|
||||
|
||||
```
|
||||
## Bulk Archive Complete (partial)
|
||||
|
||||
Archived N changes:
|
||||
- <change-1> -> archive/YYYY-MM-DD-<change-1>/
|
||||
|
||||
Skipped M changes:
|
||||
- <change-2> (user chose not to archive incomplete)
|
||||
|
||||
Failed K changes:
|
||||
- <change-3>: Archive directory already exists
|
||||
```
|
||||
|
||||
**Output When No Changes**
|
||||
|
||||
```
|
||||
## No Changes to Archive
|
||||
|
||||
No active changes found. Use `/opsx:new` to create a new change.
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Allow any number of changes (1+ is fine, 2+ is the typical use case)
|
||||
- Always prompt for selection, never auto-select
|
||||
- Detect spec conflicts early and resolve by checking codebase
|
||||
- When both changes are implemented, apply specs in chronological order
|
||||
- Skip spec sync only when implementation is missing (warn user)
|
||||
- Show clear per-change status before confirming
|
||||
- Use single confirmation for entire batch
|
||||
- Track and report all outcomes (success/skip/fail)
|
||||
- Preserve .openspec.yaml when moving to archive
|
||||
- Archive directory target uses current date: YYYY-MM-DD-<name>
|
||||
- If archive target exists, fail that change but continue with others
|
||||
@@ -1,13 +1,11 @@
|
||||
---
|
||||
name: OPSX: Continue
|
||||
description: Continue working on a change - create the next artifact (Experimental)
|
||||
category: Workflow
|
||||
tags: [workflow, artifacts, experimental]
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Continue working on a change by creating the next artifact.
|
||||
|
||||
**Input**: Optionally specify `--change <name>` after `/opsx:continue`. If omitted, MUST prompt for available changes.
|
||||
**Input**: Optionally specify a change name after `/opsx:continue` (e.g., `/opsx:continue add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
@@ -30,7 +28,7 @@ Continue working on a change by creating the next artifact.
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand current state. The response includes:
|
||||
- `schemaName`: The workflow schema being used (e.g., "spec-driven", "tdd")
|
||||
- `schemaName`: The workflow schema being used (e.g., "spec-driven")
|
||||
- `artifacts`: Array of artifacts with their status ("done", "ready", "blocked")
|
||||
- `isComplete`: Boolean indicating if all artifacts are complete
|
||||
|
||||
@@ -52,10 +50,17 @@ Continue working on a change by creating the next artifact.
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- Parse the JSON to get template, dependencies, and what it unlocks
|
||||
- **Create the artifact file** using the template as a starting point:
|
||||
- Parse the JSON. The key fields are:
|
||||
- `context`: Project background (constraints for you - do NOT include in output)
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance
|
||||
- `outputPath`: Where to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- **Create the artifact file**:
|
||||
- Read any completed dependency files for context
|
||||
- Fill in the template based on context and user's goals
|
||||
- Use `template` as the structure - fill in its sections
|
||||
- Apply `context` and `rules` as constraints when writing - but do NOT copy them into the file
|
||||
- Write to the output path specified in instructions
|
||||
- Show what was created and what's now unlocked
|
||||
- STOP after creating ONE artifact
|
||||
@@ -89,16 +94,10 @@ Common artifact patterns:
|
||||
**spec-driven schema** (proposal → specs → design → tasks):
|
||||
- **proposal.md**: Ask user about the change if not clear. Fill in Why, What Changes, Capabilities, Impact.
|
||||
- The Capabilities section is critical - each capability listed will need a spec file.
|
||||
- **specs/*.md**: Create one spec per capability listed in the proposal.
|
||||
- **specs/<capability>/spec.md**: Create one spec per capability listed in the proposal's Capabilities section (use the capability name, not the change name).
|
||||
- **design.md**: Document technical decisions, architecture, and implementation approach.
|
||||
- **tasks.md**: Break down implementation into checkboxed tasks.
|
||||
|
||||
**tdd schema** (spec → tests → implementation → docs):
|
||||
- **spec.md**: Feature specification defining what to build.
|
||||
- **tests/*.test.ts**: Write tests BEFORE implementation (TDD red phase).
|
||||
- **src/*.ts**: Implement to make tests pass (TDD green phase).
|
||||
- **docs/*.md**: Document the implemented feature.
|
||||
|
||||
For other schemas, follow the `instruction` field from the CLI output.
|
||||
|
||||
**Guardrails**
|
||||
@@ -108,3 +107,6 @@ For other schemas, follow the `instruction` field from the CLI output.
|
||||
- If context is unclear, ask the user before creating
|
||||
- Verify the artifact file exists after writing before marking progress
|
||||
- Use the schema's artifact sequence, don't assume specific artifact names
|
||||
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
||||
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
||||
- These guide what you write, but should never appear in the output
|
||||
172
.codex/prompts/opsx-explore.md
Normal file
172
.codex/prompts/opsx-explore.md
Normal file
@@ -0,0 +1,172 @@
|
||||
---
|
||||
description: Enter explore mode - think through ideas, investigate problems, clarify requirements
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first (e.g., start a change with `/opsx:new` or `/opsx:ff`). You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
|
||||
|
||||
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
||||
|
||||
**Input**: The argument after `/opsx:explore` is whatever the user wants to think about. Could be:
|
||||
- A vague idea: "real-time collaboration"
|
||||
- A specific problem: "the auth system is getting unwieldy"
|
||||
- A change name: "add-dark-mode" (to explore in context of that change)
|
||||
- A comparison: "postgres vs sqlite for this"
|
||||
- Nothing (just enter explore mode)
|
||||
|
||||
---
|
||||
|
||||
## The Stance
|
||||
|
||||
- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script
|
||||
- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.
|
||||
- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking
|
||||
- **Adaptive** - Follow interesting threads, pivot when new information emerges
|
||||
- **Patient** - Don't rush to conclusions, let the shape of the problem emerge
|
||||
- **Grounded** - Explore the actual codebase when relevant, don't just theorize
|
||||
|
||||
---
|
||||
|
||||
## What You Might Do
|
||||
|
||||
Depending on what the user brings, you might:
|
||||
|
||||
**Explore the problem space**
|
||||
- Ask clarifying questions that emerge from what they said
|
||||
- Challenge assumptions
|
||||
- Reframe the problem
|
||||
- Find analogies
|
||||
|
||||
**Investigate the codebase**
|
||||
- Map existing architecture relevant to the discussion
|
||||
- Find integration points
|
||||
- Identify patterns already in use
|
||||
- Surface hidden complexity
|
||||
|
||||
**Compare options**
|
||||
- Brainstorm multiple approaches
|
||||
- Build comparison tables
|
||||
- Sketch tradeoffs
|
||||
- Recommend a path (if asked)
|
||||
|
||||
**Visualize**
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Use ASCII diagrams liberally │
|
||||
├─────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────┐ ┌────────┐ │
|
||||
│ │ State │────────▶│ State │ │
|
||||
│ │ A │ │ B │ │
|
||||
│ └────────┘ └────────┘ │
|
||||
│ │
|
||||
│ System diagrams, state machines, │
|
||||
│ data flows, architecture sketches, │
|
||||
│ dependency graphs, comparison tables │
|
||||
│ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Surface risks and unknowns**
|
||||
- Identify what could go wrong
|
||||
- Find gaps in understanding
|
||||
- Suggest spikes or investigations
|
||||
|
||||
---
|
||||
|
||||
## OpenSpec Awareness
|
||||
|
||||
You have full context of the OpenSpec system. Use it naturally, don't force it.
|
||||
|
||||
### Check for context
|
||||
|
||||
At the start, quickly check what exists:
|
||||
```bash
|
||||
openspec list --json
|
||||
```
|
||||
|
||||
This tells you:
|
||||
- If there are active changes
|
||||
- Their names, schemas, and status
|
||||
- What the user might be working on
|
||||
|
||||
If the user mentioned a specific change name, read its artifacts for context.
|
||||
|
||||
### When no change exists
|
||||
|
||||
Think freely. When insights crystallize, you might offer:
|
||||
|
||||
- "This feels solid enough to start a change. Want me to create one?"
|
||||
→ Can transition to `/opsx:new` or `/opsx:ff`
|
||||
- Or keep exploring - no pressure to formalize
|
||||
|
||||
### When a change exists
|
||||
|
||||
If the user mentions a change or you detect one is relevant:
|
||||
|
||||
1. **Read existing artifacts for context**
|
||||
- `openspec/changes/<name>/proposal.md`
|
||||
- `openspec/changes/<name>/design.md`
|
||||
- `openspec/changes/<name>/tasks.md`
|
||||
- etc.
|
||||
|
||||
2. **Reference them naturally in conversation**
|
||||
- "Your design mentions using Redis, but we just realized SQLite fits better..."
|
||||
- "The proposal scopes this to premium users, but we're now thinking everyone..."
|
||||
|
||||
3. **Offer to capture when decisions are made**
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|--------------|------------------|
|
||||
| New requirement discovered | `specs/<capability>/spec.md` |
|
||||
| Requirement changed | `specs/<capability>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
- "This is a new requirement. Add it to specs?"
|
||||
- "This changes scope. Update the proposal?"
|
||||
|
||||
4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.
|
||||
|
||||
---
|
||||
|
||||
## What You Don't Have To Do
|
||||
|
||||
- Follow a script
|
||||
- Ask the same questions every time
|
||||
- Produce a specific artifact
|
||||
- Reach a conclusion
|
||||
- Stay on topic if a tangent is valuable
|
||||
- Be brief (this is thinking time)
|
||||
|
||||
---
|
||||
|
||||
## Ending Discovery
|
||||
|
||||
There's no required ending. Discovery might:
|
||||
|
||||
- **Flow into action**: "Ready to start? `/opsx:new` or `/opsx:ff`"
|
||||
- **Result in artifact updates**: "Updated design.md with these decisions"
|
||||
- **Just provide clarity**: User has what they need, moves on
|
||||
- **Continue later**: "We can pick this up anytime"
|
||||
|
||||
When things crystallize, you might offer a summary - but it's optional. Sometimes the thinking IS the value.
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.
|
||||
- **Don't fake understanding** - If something is unclear, dig deeper
|
||||
- **Don't rush** - Discovery is thinking time, not task time
|
||||
- **Don't force structure** - Let patterns emerge naturally
|
||||
- **Don't auto-capture** - Offer to save insights, don't just do it
|
||||
- **Do visualize** - A good diagram is worth many paragraphs
|
||||
- **Do explore the codebase** - Ground discussions in reality
|
||||
- **Do question assumptions** - Including the user's and your own
|
||||
@@ -1,8 +1,6 @@
|
||||
---
|
||||
name: OPSX: Fast Forward
|
||||
description: Create a change and generate all artifacts needed for implementation in one go
|
||||
category: Workflow
|
||||
tags: [workflow, artifacts, experimental]
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Fast-forward through artifact creation - generate everything needed to start implementation.
|
||||
@@ -46,12 +44,15 @@ Fast-forward through artifact creation - generate everything needed to start imp
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- The instructions JSON includes:
|
||||
- `template`: The template content to use
|
||||
- `context`: Project background (constraints for you - do NOT include in output)
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance for this artifact type
|
||||
- `outputPath`: Where to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- Read any completed dependency files for context
|
||||
- Create the artifact file following the schema's `instruction`
|
||||
- Create the artifact file using `template` as the structure
|
||||
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
||||
- Show brief progress: "✓ Created <artifact-id>"
|
||||
|
||||
b. **Continue until all `applyRequires` artifacts are complete**
|
||||
@@ -1,8 +1,6 @@
|
||||
---
|
||||
name: OPSX: New
|
||||
description: Start a new change using the experimental artifact workflow (OPSX)
|
||||
category: Workflow
|
||||
tags: [workflow, artifacts, experimental]
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Start a new change using the experimental artifact-driven approach.
|
||||
@@ -20,21 +18,21 @@ Start a new change using the experimental artifact-driven approach.
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
2. **Select a workflow schema**
|
||||
2. **Determine the workflow schema**
|
||||
|
||||
Run `openspec schemas --json` to get available schemas with descriptions.
|
||||
Use the default schema (omit `--schema`) unless the user explicitly requests a different workflow.
|
||||
|
||||
Use the **AskUserQuestion tool** to let the user choose a workflow:
|
||||
- Present each schema with its description
|
||||
- Mark `spec-driven` as "(default)" if it's available
|
||||
- Example options: "spec-driven - proposal → specs → design → tasks (default)", "tdd - tests → implementation → docs"
|
||||
**Use a different schema only if the user mentions:**
|
||||
- A specific schema name → use `--schema <name>`
|
||||
- "show workflows" or "what workflows" → run `openspec schemas --json` and let them choose
|
||||
|
||||
If user doesn't have a preference, default to `spec-driven`.
|
||||
**Otherwise**: Omit `--schema` to use the default.
|
||||
|
||||
3. **Create the change directory**
|
||||
```bash
|
||||
openspec new change "<name>" --schema "<selected-schema>"
|
||||
openspec new change "<name>"
|
||||
```
|
||||
Add `--schema <name>` only if the user requested a specific workflow.
|
||||
This creates a scaffolded change at `openspec/changes/<name>/` with the selected schema.
|
||||
|
||||
4. **Show the artifact status**
|
||||
@@ -56,7 +54,7 @@ Start a new change using the experimental artifact-driven approach.
|
||||
|
||||
After completing the steps, summarize:
|
||||
- Change name and location
|
||||
- Selected schema/workflow and its artifact sequence
|
||||
- Schema/workflow being used and its artifact sequence
|
||||
- Current status (0/N artifacts complete)
|
||||
- The template for the first artifact
|
||||
- Prompt: "Ready to create the first artifact? Run `/opsx:continue` or just describe what this change is about and I'll draft it."
|
||||
@@ -66,4 +64,4 @@ After completing the steps, summarize:
|
||||
- Do NOT advance beyond showing the first artifact template
|
||||
- If the name is invalid (not kebab-case), ask for a valid name
|
||||
- If a change with that name already exists, suggest using `/opsx:continue` instead
|
||||
- Always pass --schema to preserve the user's workflow choice
|
||||
- Pass --schema if using a non-default workflow
|
||||
523
.codex/prompts/opsx-onboard.md
Normal file
523
.codex/prompts/opsx-onboard.md
Normal file
@@ -0,0 +1,523 @@
|
||||
---
|
||||
description: Guided onboarding - walk through a complete OpenSpec workflow cycle with narration
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Guide the user through their first complete OpenSpec workflow cycle. This is a teaching experience—you'll do real work in their codebase while explaining each step.
|
||||
|
||||
---
|
||||
|
||||
## Preflight
|
||||
|
||||
Before starting, check if OpenSpec is initialized:
|
||||
|
||||
```bash
|
||||
openspec status --json 2>&1 || echo "NOT_INITIALIZED"
|
||||
```
|
||||
|
||||
**If not initialized:**
|
||||
> OpenSpec isn't set up in this project yet. Run `openspec init` first, then come back to `/opsx:onboard`.
|
||||
|
||||
Stop here if not initialized.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Welcome
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
## Welcome to OpenSpec!
|
||||
|
||||
I'll walk you through a complete change cycle—from idea to implementation—using a real task in your codebase. Along the way, you'll learn the workflow by doing it.
|
||||
|
||||
**What we'll do:**
|
||||
1. Pick a small, real task in your codebase
|
||||
2. Explore the problem briefly
|
||||
3. Create a change (the container for our work)
|
||||
4. Build the artifacts: proposal → specs → design → tasks
|
||||
5. Implement the tasks
|
||||
6. Archive the completed change
|
||||
|
||||
**Time:** ~15-20 minutes
|
||||
|
||||
Let's start by finding something to work on.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Task Selection
|
||||
|
||||
### Codebase Analysis
|
||||
|
||||
Scan the codebase for small improvement opportunities. Look for:
|
||||
|
||||
1. **TODO/FIXME comments** - Search for `TODO`, `FIXME`, `HACK`, `XXX` in code files
|
||||
2. **Missing error handling** - `catch` blocks that swallow errors, risky operations without try-catch
|
||||
3. **Functions without tests** - Cross-reference `src/` with test directories
|
||||
4. **Type issues** - `any` types in TypeScript files (`: any`, `as any`)
|
||||
5. **Debug artifacts** - `console.log`, `console.debug`, `debugger` statements in non-debug code
|
||||
6. **Missing validation** - User input handlers without validation
|
||||
|
||||
Also check recent git activity:
|
||||
```bash
|
||||
git log --oneline -10 2>/dev/null || echo "No git history"
|
||||
```
|
||||
|
||||
### Present Suggestions
|
||||
|
||||
From your analysis, present 3-4 specific suggestions:
|
||||
|
||||
```
|
||||
## Task Suggestions
|
||||
|
||||
Based on scanning your codebase, here are some good starter tasks:
|
||||
|
||||
**1. [Most promising task]**
|
||||
Location: `src/path/to/file.ts:42`
|
||||
Scope: ~1-2 files, ~20-30 lines
|
||||
Why it's good: [brief reason]
|
||||
|
||||
**2. [Second task]**
|
||||
Location: `src/another/file.ts`
|
||||
Scope: ~1 file, ~15 lines
|
||||
Why it's good: [brief reason]
|
||||
|
||||
**3. [Third task]**
|
||||
Location: [location]
|
||||
Scope: [estimate]
|
||||
Why it's good: [brief reason]
|
||||
|
||||
**4. Something else?**
|
||||
Tell me what you'd like to work on.
|
||||
|
||||
Which task interests you? (Pick a number or describe your own)
|
||||
```
|
||||
|
||||
**If nothing found:** Fall back to asking what the user wants to build:
|
||||
> I didn't find obvious quick wins in your codebase. What's something small you've been meaning to add or fix?
|
||||
|
||||
### Scope Guardrail
|
||||
|
||||
If the user picks or describes something too large (major feature, multi-day work):
|
||||
|
||||
```
|
||||
That's a valuable task, but it's probably larger than ideal for your first OpenSpec run-through.
|
||||
|
||||
For learning the workflow, smaller is better—it lets you see the full cycle without getting stuck in implementation details.
|
||||
|
||||
**Options:**
|
||||
1. **Slice it smaller** - What's the smallest useful piece of [their task]? Maybe just [specific slice]?
|
||||
2. **Pick something else** - One of the other suggestions, or a different small task?
|
||||
3. **Do it anyway** - If you really want to tackle this, we can. Just know it'll take longer.
|
||||
|
||||
What would you prefer?
|
||||
```
|
||||
|
||||
Let the user override if they insist—this is a soft guardrail.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Explore Demo
|
||||
|
||||
Once a task is selected, briefly demonstrate explore mode:
|
||||
|
||||
```
|
||||
Before we create a change, let me quickly show you **explore mode**—it's how you think through problems before committing to a direction.
|
||||
```
|
||||
|
||||
Spend 1-2 minutes investigating the relevant code:
|
||||
- Read the file(s) involved
|
||||
- Draw a quick ASCII diagram if it helps
|
||||
- Note any considerations
|
||||
|
||||
```
|
||||
## Quick Exploration
|
||||
|
||||
[Your brief analysis—what you found, any considerations]
|
||||
|
||||
┌─────────────────────────────────────────┐
|
||||
│ [Optional: ASCII diagram if helpful] │
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
Explore mode (`/opsx:explore`) is for this kind of thinking—investigating before implementing. You can use it anytime you need to think through a problem.
|
||||
|
||||
Now let's create a change to hold our work.
|
||||
```
|
||||
|
||||
**PAUSE** - Wait for user acknowledgment before proceeding.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Create the Change
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Creating a Change
|
||||
|
||||
A "change" in OpenSpec is a container for all the thinking and planning around a piece of work. It lives in `openspec/changes/<name>/` and holds your artifacts—proposal, specs, design, tasks.
|
||||
|
||||
Let me create one for our task.
|
||||
```
|
||||
|
||||
**DO:** Create the change with a derived kebab-case name:
|
||||
```bash
|
||||
openspec new change "<derived-name>"
|
||||
```
|
||||
|
||||
**SHOW:**
|
||||
```
|
||||
Created: `openspec/changes/<name>/`
|
||||
|
||||
The folder structure:
|
||||
```
|
||||
openspec/changes/<name>/
|
||||
├── proposal.md ← Why we're doing this (empty, we'll fill it)
|
||||
├── design.md ← How we'll build it (empty)
|
||||
├── specs/ ← Detailed requirements (empty)
|
||||
└── tasks.md ← Implementation checklist (empty)
|
||||
```
|
||||
|
||||
Now let's fill in the first artifact—the proposal.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Proposal
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## The Proposal
|
||||
|
||||
The proposal captures **why** we're making this change and **what** it involves at a high level. It's the "elevator pitch" for the work.
|
||||
|
||||
I'll draft one based on our task.
|
||||
```
|
||||
|
||||
**DO:** Draft the proposal content (don't save yet):
|
||||
|
||||
```
|
||||
Here's a draft proposal:
|
||||
|
||||
---
|
||||
|
||||
## Why
|
||||
|
||||
[1-2 sentences explaining the problem/opportunity]
|
||||
|
||||
## What Changes
|
||||
|
||||
[Bullet points of what will be different]
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `<capability-name>`: [brief description]
|
||||
|
||||
### Modified Capabilities
|
||||
<!-- If modifying existing behavior -->
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/path/to/file.ts`: [what changes]
|
||||
- [other files if applicable]
|
||||
|
||||
---
|
||||
|
||||
Does this capture the intent? I can adjust before we save it.
|
||||
```
|
||||
|
||||
**PAUSE** - Wait for user approval/feedback.
|
||||
|
||||
After approval, save the proposal:
|
||||
```bash
|
||||
openspec instructions proposal --change "<name>" --json
|
||||
```
|
||||
Then write the content to `openspec/changes/<name>/proposal.md`.
|
||||
|
||||
```
|
||||
Proposal saved. This is your "why" document—you can always come back and refine it as understanding evolves.
|
||||
|
||||
Next up: specs.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Specs
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Specs
|
||||
|
||||
Specs define **what** we're building in precise, testable terms. They use a requirement/scenario format that makes expected behavior crystal clear.
|
||||
|
||||
For a small task like this, we might only need one spec file.
|
||||
```
|
||||
|
||||
**DO:** Create the spec file:
|
||||
```bash
|
||||
mkdir -p openspec/changes/<name>/specs/<capability-name>
|
||||
```
|
||||
|
||||
Draft the spec content:
|
||||
|
||||
```
|
||||
Here's the spec:
|
||||
|
||||
---
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: <Name>
|
||||
|
||||
<Description of what the system should do>
|
||||
|
||||
#### Scenario: <Scenario name>
|
||||
|
||||
- **WHEN** <trigger condition>
|
||||
- **THEN** <expected outcome>
|
||||
- **AND** <additional outcome if needed>
|
||||
|
||||
---
|
||||
|
||||
This format—WHEN/THEN/AND—makes requirements testable. You can literally read them as test cases.
|
||||
```
|
||||
|
||||
Save to `openspec/changes/<name>/specs/<capability>/spec.md`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: Design
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Design
|
||||
|
||||
The design captures **how** we'll build it—technical decisions, tradeoffs, approach.
|
||||
|
||||
For small changes, this might be brief. That's fine—not every change needs deep design discussion.
|
||||
```
|
||||
|
||||
**DO:** Draft design.md:
|
||||
|
||||
```
|
||||
Here's the design:
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
[Brief context about the current state]
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- [What we're trying to achieve]
|
||||
|
||||
**Non-Goals:**
|
||||
- [What's explicitly out of scope]
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision 1: [Key decision]
|
||||
|
||||
[Explanation of approach and rationale]
|
||||
|
||||
---
|
||||
|
||||
For a small task, this captures the key decisions without over-engineering.
|
||||
```
|
||||
|
||||
Save to `openspec/changes/<name>/design.md`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 8: Tasks
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Tasks
|
||||
|
||||
Finally, we break the work into implementation tasks—checkboxes that drive the apply phase.
|
||||
|
||||
These should be small, clear, and in logical order.
|
||||
```
|
||||
|
||||
**DO:** Generate tasks based on specs and design:
|
||||
|
||||
```
|
||||
Here are the implementation tasks:
|
||||
|
||||
---
|
||||
|
||||
## 1. [Category or file]
|
||||
|
||||
- [ ] 1.1 [Specific task]
|
||||
- [ ] 1.2 [Specific task]
|
||||
|
||||
## 2. Verify
|
||||
|
||||
- [ ] 2.1 [Verification step]
|
||||
|
||||
---
|
||||
|
||||
Each checkbox becomes a unit of work in the apply phase. Ready to implement?
|
||||
```
|
||||
|
||||
**PAUSE** - Wait for user to confirm they're ready to implement.
|
||||
|
||||
Save to `openspec/changes/<name>/tasks.md`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 9: Apply (Implementation)
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Implementation
|
||||
|
||||
Now we implement each task, checking them off as we go. I'll announce each one and occasionally note how the specs/design informed the approach.
|
||||
```
|
||||
|
||||
**DO:** For each task:
|
||||
|
||||
1. Announce: "Working on task N: [description]"
|
||||
2. Implement the change in the codebase
|
||||
3. Reference specs/design naturally: "The spec says X, so I'm doing Y"
|
||||
4. Mark complete in tasks.md: `- [ ]` → `- [x]`
|
||||
5. Brief status: "✓ Task N complete"
|
||||
|
||||
Keep narration light—don't over-explain every line of code.
|
||||
|
||||
After all tasks:
|
||||
|
||||
```
|
||||
## Implementation Complete
|
||||
|
||||
All tasks done:
|
||||
- [x] Task 1
|
||||
- [x] Task 2
|
||||
- [x] ...
|
||||
|
||||
The change is implemented! One more step—let's archive it.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 10: Archive
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Archiving
|
||||
|
||||
When a change is complete, we archive it. This moves it from `openspec/changes/` to `openspec/changes/archive/YYYY-MM-DD-<name>/`.
|
||||
|
||||
Archived changes become your project's decision history—you can always find them later to understand why something was built a certain way.
|
||||
```
|
||||
|
||||
**DO:**
|
||||
```bash
|
||||
openspec archive "<name>"
|
||||
```
|
||||
|
||||
**SHOW:**
|
||||
```
|
||||
Archived to: `openspec/changes/archive/YYYY-MM-DD-<name>/`
|
||||
|
||||
The change is now part of your project's history. The code is in your codebase, the decision record is preserved.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 11: Recap & Next Steps
|
||||
|
||||
```
|
||||
## Congratulations!
|
||||
|
||||
You just completed a full OpenSpec cycle:
|
||||
|
||||
1. **Explore** - Thought through the problem
|
||||
2. **New** - Created a change container
|
||||
3. **Proposal** - Captured WHY
|
||||
4. **Specs** - Defined WHAT in detail
|
||||
5. **Design** - Decided HOW
|
||||
6. **Tasks** - Broke it into steps
|
||||
7. **Apply** - Implemented the work
|
||||
8. **Archive** - Preserved the record
|
||||
|
||||
This same rhythm works for any size change—a small fix or a major feature.
|
||||
|
||||
---
|
||||
|
||||
## Command Reference
|
||||
|
||||
| Command | What it does |
|
||||
|---------|--------------|
|
||||
| `/opsx:explore` | Think through problems before/during work |
|
||||
| `/opsx:new` | Start a new change, step through artifacts |
|
||||
| `/opsx:ff` | Fast-forward: create all artifacts at once |
|
||||
| `/opsx:continue` | Continue working on an existing change |
|
||||
| `/opsx:apply` | Implement tasks from a change |
|
||||
| `/opsx:verify` | Verify implementation matches artifacts |
|
||||
| `/opsx:archive` | Archive a completed change |
|
||||
|
||||
---
|
||||
|
||||
## What's Next?
|
||||
|
||||
Try `/opsx:new` or `/opsx:ff` on something you actually want to build. You've got the rhythm now!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Graceful Exit Handling
|
||||
|
||||
### User wants to stop mid-way
|
||||
|
||||
If the user says they need to stop, want to pause, or seem disengaged:
|
||||
|
||||
```
|
||||
No problem! Your change is saved at `openspec/changes/<name>/`.
|
||||
|
||||
To pick up where we left off later:
|
||||
- `/opsx:continue <name>` - Resume artifact creation
|
||||
- `/opsx:apply <name>` - Jump to implementation (if tasks exist)
|
||||
|
||||
The work won't be lost. Come back whenever you're ready.
|
||||
```
|
||||
|
||||
Exit gracefully without pressure.
|
||||
|
||||
### User just wants command reference
|
||||
|
||||
If the user says they just want to see the commands or skip the tutorial:
|
||||
|
||||
```
|
||||
## OpenSpec Quick Reference
|
||||
|
||||
| Command | What it does |
|
||||
|---------|--------------|
|
||||
| `/opsx:explore` | Think through problems (no code changes) |
|
||||
| `/opsx:new <name>` | Start a new change, step by step |
|
||||
| `/opsx:ff <name>` | Fast-forward: all artifacts at once |
|
||||
| `/opsx:continue <name>` | Continue an existing change |
|
||||
| `/opsx:apply <name>` | Implement tasks |
|
||||
| `/opsx:verify <name>` | Verify implementation |
|
||||
| `/opsx:archive <name>` | Archive when done |
|
||||
|
||||
Try `/opsx:new` to start your first change, or `/opsx:ff` if you want to move fast.
|
||||
```
|
||||
|
||||
Exit gracefully.
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Follow the EXPLAIN → DO → SHOW → PAUSE pattern** at key transitions (after explore, after proposal draft, after tasks, after archive)
|
||||
- **Keep narration light** during implementation—teach without lecturing
|
||||
- **Don't skip phases** even if the change is small—the goal is teaching the workflow
|
||||
- **Pause for acknowledgment** at marked points, but don't over-pause
|
||||
- **Handle exits gracefully**—never pressure the user to continue
|
||||
- **Use real codebase tasks**—don't simulate or use fake examples
|
||||
- **Adjust scope gently**—guide toward smaller tasks but respect user choice
|
||||
@@ -1,15 +1,13 @@
|
||||
---
|
||||
name: OPSX: Sync
|
||||
description: Sync delta specs from a change to main specs
|
||||
category: Workflow
|
||||
tags: [workflow, specs, experimental]
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Sync delta specs from a change to main specs.
|
||||
|
||||
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
||||
|
||||
**Input**: Optionally specify `--change <name>` after `/opsx:sync`. If omitted, MUST prompt for available changes.
|
||||
**Input**: Optionally specify a change name after `/opsx:sync` (e.g., `/opsx:sync add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
162
.codex/prompts/opsx-verify.md
Normal file
162
.codex/prompts/opsx-verify.md
Normal file
@@ -0,0 +1,162 @@
|
||||
---
|
||||
description: Verify implementation matches change artifacts before archiving
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Verify that an implementation matches the change artifacts (specs, tasks, design).
|
||||
|
||||
**Input**: Optionally specify a change name after `/opsx:verify` (e.g., `/opsx:verify add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
|
||||
Show changes that have implementation tasks (tasks artifact exists).
|
||||
Include the schema used for each change if available.
|
||||
Mark changes with incomplete tasks as "(In Progress)".
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Check status to understand the schema**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
||||
- Which artifacts exist for this change
|
||||
|
||||
3. **Get the change directory and load artifacts**
|
||||
|
||||
```bash
|
||||
openspec instructions apply --change "<name>" --json
|
||||
```
|
||||
|
||||
This returns the change directory and context files. Read all available artifacts from `contextFiles`.
|
||||
|
||||
4. **Initialize verification report structure**
|
||||
|
||||
Create a report structure with three dimensions:
|
||||
- **Completeness**: Track tasks and spec coverage
|
||||
- **Correctness**: Track requirement implementation and scenario coverage
|
||||
- **Coherence**: Track design adherence and pattern consistency
|
||||
|
||||
Each dimension can have CRITICAL, WARNING, or SUGGESTION issues.
|
||||
|
||||
5. **Verify Completeness**
|
||||
|
||||
**Task Completion**:
|
||||
- If tasks.md exists in contextFiles, read it
|
||||
- Parse checkboxes: `- [ ]` (incomplete) vs `- [x]` (complete)
|
||||
- Count complete vs total tasks
|
||||
- If incomplete tasks exist:
|
||||
- Add CRITICAL issue for each incomplete task
|
||||
- Recommendation: "Complete task: <description>" or "Mark as done if already implemented"
|
||||
|
||||
**Spec Coverage**:
|
||||
- If delta specs exist in `openspec/changes/<name>/specs/`:
|
||||
- Extract all requirements (marked with "### Requirement:")
|
||||
- For each requirement:
|
||||
- Search codebase for keywords related to the requirement
|
||||
- Assess if implementation likely exists
|
||||
- If requirements appear unimplemented:
|
||||
- Add CRITICAL issue: "Requirement not found: <requirement name>"
|
||||
- Recommendation: "Implement requirement X: <description>"
|
||||
|
||||
6. **Verify Correctness**
|
||||
|
||||
**Requirement Implementation Mapping**:
|
||||
- For each requirement from delta specs:
|
||||
- Search codebase for implementation evidence
|
||||
- If found, note file paths and line ranges
|
||||
- Assess if implementation matches requirement intent
|
||||
- If divergence detected:
|
||||
- Add WARNING: "Implementation may diverge from spec: <details>"
|
||||
- Recommendation: "Review <file>:<lines> against requirement X"
|
||||
|
||||
**Scenario Coverage**:
|
||||
- For each scenario in delta specs (marked with "#### Scenario:"):
|
||||
- Check if conditions are handled in code
|
||||
- Check if tests exist covering the scenario
|
||||
- If scenario appears uncovered:
|
||||
- Add WARNING: "Scenario not covered: <scenario name>"
|
||||
- Recommendation: "Add test or implementation for scenario: <description>"
|
||||
|
||||
7. **Verify Coherence**
|
||||
|
||||
**Design Adherence**:
|
||||
- If design.md exists in contextFiles:
|
||||
- Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:")
|
||||
- Verify implementation follows those decisions
|
||||
- If contradiction detected:
|
||||
- Add WARNING: "Design decision not followed: <decision>"
|
||||
- Recommendation: "Update implementation or revise design.md to match reality"
|
||||
- If no design.md: Skip design adherence check, note "No design.md to verify against"
|
||||
|
||||
**Code Pattern Consistency**:
|
||||
- Review new code for consistency with project patterns
|
||||
- Check file naming, directory structure, coding style
|
||||
- If significant deviations found:
|
||||
- Add SUGGESTION: "Code pattern deviation: <details>"
|
||||
- Recommendation: "Consider following project pattern: <example>"
|
||||
|
||||
8. **Generate Verification Report**
|
||||
|
||||
**Summary Scorecard**:
|
||||
```
|
||||
## Verification Report: <change-name>
|
||||
|
||||
### Summary
|
||||
| Dimension | Status |
|
||||
|--------------|------------------|
|
||||
| Completeness | X/Y tasks, N reqs|
|
||||
| Correctness | M/N reqs covered |
|
||||
| Coherence | Followed/Issues |
|
||||
```
|
||||
|
||||
**Issues by Priority**:
|
||||
|
||||
1. **CRITICAL** (Must fix before archive):
|
||||
- Incomplete tasks
|
||||
- Missing requirement implementations
|
||||
- Each with specific, actionable recommendation
|
||||
|
||||
2. **WARNING** (Should fix):
|
||||
- Spec/design divergences
|
||||
- Missing scenario coverage
|
||||
- Each with specific recommendation
|
||||
|
||||
3. **SUGGESTION** (Nice to fix):
|
||||
- Pattern inconsistencies
|
||||
- Minor improvements
|
||||
- Each with specific recommendation
|
||||
|
||||
**Final Assessment**:
|
||||
- If CRITICAL issues: "X critical issue(s) found. Fix before archiving."
|
||||
- If only warnings: "No critical issues. Y warning(s) to consider. Ready for archive (with noted improvements)."
|
||||
- If all clear: "All checks passed. Ready for archive."
|
||||
|
||||
**Verification Heuristics**
|
||||
|
||||
- **Completeness**: Focus on objective checklist items (checkboxes, requirements list)
|
||||
- **Correctness**: Use keyword search, file path analysis, reasonable inference - don't require perfect certainty
|
||||
- **Coherence**: Look for glaring inconsistencies, don't nitpick style
|
||||
- **False Positives**: When uncertain, prefer SUGGESTION over WARNING, WARNING over CRITICAL
|
||||
- **Actionability**: Every issue must have a specific recommendation with file/line references where applicable
|
||||
|
||||
**Graceful Degradation**
|
||||
|
||||
- If only tasks.md exists: verify task completion only, skip spec/design checks
|
||||
- If tasks + specs exist: verify completeness and correctness, skip design
|
||||
- If full artifacts: verify all three dimensions
|
||||
- Always note which checks were skipped and why
|
||||
|
||||
**Output Format**
|
||||
|
||||
Use clear markdown with:
|
||||
- Table for summary scorecard
|
||||
- Grouped lists for issues (CRITICAL/WARNING/SUGGESTION)
|
||||
- Code references in format: `file.ts:123`
|
||||
- Specific, actionable recommendations
|
||||
- No vague suggestions like "consider reviewing"
|
||||
198
.codex/skills/export-datasource/SKILL.md
Normal file
198
.codex/skills/export-datasource/SKILL.md
Normal file
@@ -0,0 +1,198 @@
|
||||
---
|
||||
name: export-datasource
|
||||
description: Project-specific guide for implementing, modifying, or reviewing export data sources in junhong_cmp_fiber. Use when Codex needs to add a new export scene, extend filters or dynamic columns, register an export scene, change export task query behavior, or explain/debug the DataSource-based export system.
|
||||
---
|
||||
|
||||
# Export Datasource
|
||||
|
||||
Use this skill to work on this project's DataSource-based export system. It covers developer-facing export scene implementation, not one-off manual export operations.
|
||||
|
||||
## First Reads
|
||||
|
||||
Before editing export code, read the relevant current files:
|
||||
|
||||
- `internal/exporter/datasource.go`
|
||||
- `internal/exporter/query_params.go`
|
||||
- `internal/exporter/filter_helpers.go`
|
||||
- `internal/exporter/registry.go`
|
||||
- Existing scene closest to the new scene:
|
||||
- `internal/exporter/device_scene.go`
|
||||
- `internal/exporter/iot_card_scene.go`
|
||||
- For request/scene validation:
|
||||
- `internal/model/dto/export_task_dto.go`
|
||||
- `pkg/constants/constants.go`
|
||||
- For behavior across worker stages:
|
||||
- `internal/task/export_dispatch.go`
|
||||
- `internal/task/export_shard.go`
|
||||
- `internal/task/export_finalize.go`
|
||||
|
||||
Read `references/export-scene-template.md` when adding a new scene or when a concrete code skeleton is useful.
|
||||
|
||||
## Mental Model
|
||||
|
||||
The framework owns async execution, sharding, file generation, OSS upload, and download URLs. A scene implementation owns only data semantics:
|
||||
|
||||
```go
|
||||
type DataSource interface {
|
||||
Scene() string
|
||||
Count(ctx context.Context, params ExportParams) (int, error)
|
||||
Headers(ctx context.Context, params ExportParams) ([]string, error)
|
||||
Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error)
|
||||
}
|
||||
```
|
||||
|
||||
Execution flow:
|
||||
|
||||
1. Admin API creates `tb_export_task` and enqueues `export:dispatch`.
|
||||
2. Dispatch parses `query_json.filters` plus permission snapshot into `ExportParams`.
|
||||
3. Dispatch calls `Headers` once and stores `query_json.resolved_headers`.
|
||||
4. Dispatch calls `Count` and creates `tb_export_shard_task` rows with `shard_offset` and `shard_limit`.
|
||||
5. Shard calls `Fetch`, writes headerless CSV shard files, and uploads them.
|
||||
6. Finalize downloads shard CSV files in shard order, writes one header row, uploads final CSV or converts CSV to XLSX.
|
||||
|
||||
Do not reintroduce keyset cursor logic. New scenes must use offset/limit through `Fetch`.
|
||||
|
||||
## Implementation Workflow
|
||||
|
||||
1. Add a scene constant in `pkg/constants/constants.go`.
|
||||
2. Update `internal/model/dto/export_task_dto.go` validation and descriptions for `scene`.
|
||||
3. Implement `internal/exporter/<scene>_scene.go` with `DataSource`.
|
||||
4. Register the source in `NewDefaultRegistry`.
|
||||
5. Update `IsSupportedScene` if it is used by the current code path.
|
||||
6. Add or update migrations only if the exported domain needs schema/index changes. Do not change export task tables unless the framework contract changes.
|
||||
7. Build and manually verify. This repository forbids automated tests unless the user explicitly requests them.
|
||||
|
||||
## DataSource Rules
|
||||
|
||||
- `Scene` must return the constant, not a string literal.
|
||||
- `Count` and `Fetch` must apply the same filters and permission scope.
|
||||
- `Headers` defines the exact column contract for the whole task. Dispatch stores it once; shards and finalize reuse it.
|
||||
- `Fetch` must return rows aligned to `Headers`; the framework pads/truncates as a fallback, but the source should be correct.
|
||||
- `Fetch` must use stable ordering, normally `ORDER BY id ASC`.
|
||||
- Return string values only. Format time as `2006-01-02 15:04:05` unless the surrounding scene establishes another convention.
|
||||
- Use GORM only. Do not use `database/sql`.
|
||||
- Keep SQL parameters bound through GORM placeholders. Do not concatenate user-controlled values into SQL.
|
||||
- Keep comments, logs, errors, and documentation in Chinese per project rules.
|
||||
|
||||
## Filters And Permissions
|
||||
|
||||
Incoming task query shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"filters": {
|
||||
"status": 1,
|
||||
"shop_id": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`ParseExportParams` exposes:
|
||||
|
||||
- `Filters`: `query_json.filters`
|
||||
- `ScopeShopIDs`: shop permission snapshot captured at task creation
|
||||
- `UserType`: creator user type snapshot
|
||||
|
||||
Apply permission scope inside each DataSource:
|
||||
|
||||
```go
|
||||
query = applyExportShopScope(query, params, "shop_id")
|
||||
```
|
||||
|
||||
For aliased queries:
|
||||
|
||||
```go
|
||||
query = applyExportShopScope(query, params, "o.shop_id")
|
||||
```
|
||||
|
||||
Use helpers from `filter_helpers.go`:
|
||||
|
||||
- `filterInt`
|
||||
- `filterUint`
|
||||
- `filterString`
|
||||
- `filterBool`
|
||||
- `filterTime`
|
||||
- `formatOptionalUint`
|
||||
- `formatOptionalTime`
|
||||
|
||||
Do not read live permissions from context inside a DataSource. Export tasks must use the permission snapshot stored at creation time.
|
||||
|
||||
## Dynamic Columns
|
||||
|
||||
Use dynamic headers only when the task parameters or data require it. If `Headers` changes based on filters, `Fetch` must produce the same shape for every shard under the same `ExportParams`.
|
||||
|
||||
Example pattern:
|
||||
|
||||
```go
|
||||
headers := []string{"ID", "ICCID", "状态"}
|
||||
if filterBool(params.Filters, "with_package") {
|
||||
headers = append(headers, "套餐名称", "套餐状态")
|
||||
}
|
||||
return headers, nil
|
||||
```
|
||||
|
||||
## Join Queries
|
||||
|
||||
JOIN-based exports are allowed. Keep these constraints:
|
||||
|
||||
- Preserve one output row per intended exported entity unless the scene explicitly exports detail rows.
|
||||
- If a JOIN can multiply rows, make `Count` match the exported row semantics exactly.
|
||||
- Use table aliases consistently in filters and scope columns.
|
||||
- Prefer explicit `Select` into a local row struct for multi-table exports.
|
||||
- Keep `Order`, `Limit`, and `Offset` on the final query used by `Fetch`.
|
||||
|
||||
## User-Facing API Notes
|
||||
|
||||
Current API group:
|
||||
|
||||
- `POST /api/admin/export-tasks`
|
||||
- `GET /api/admin/export-tasks`
|
||||
- `GET /api/admin/export-tasks/:id`
|
||||
- `POST /api/admin/export-tasks/:id/cancel`
|
||||
|
||||
Creation request:
|
||||
|
||||
```json
|
||||
{
|
||||
"scene": "iot_card",
|
||||
"format": "csv",
|
||||
"query": {
|
||||
"filters": {
|
||||
"shop_id": 1,
|
||||
"with_package": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Formats are `csv` and `xlsx`. Final download URLs are returned from task detail after completion.
|
||||
|
||||
## Verification
|
||||
|
||||
This project forbids automated tests and `_test.go` files unless the user explicitly asks for tests. Use manual verification:
|
||||
|
||||
```bash
|
||||
go build ./internal/exporter/...
|
||||
go build ./internal/task/...
|
||||
go build ./...
|
||||
```
|
||||
|
||||
Then create an export task through the API and inspect:
|
||||
|
||||
- `tb_export_task.query_json` contains original `filters` and generated `resolved_headers`.
|
||||
- `tb_export_task.total_rows` matches the filtered query.
|
||||
- `tb_export_shard_task.shard_offset` and `shard_limit` are filled.
|
||||
- Shards reach success and final task reaches completed status.
|
||||
- Downloaded file has one header row, expected row count, correct filtering, and valid CSV/XLSX format.
|
||||
|
||||
Use PostgreSQL MCP/manual SQL for data validation when needed.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- Updating only `Fetch` and forgetting `Count`, causing wrong shard planning.
|
||||
- Returning dynamic rows whose column count does not match `Headers`.
|
||||
- Filtering by requested `shop_id` without also applying `ScopeShopIDs`.
|
||||
- Using context/user middleware inside worker DataSource logic.
|
||||
- Registering the source but forgetting DTO `oneof`, so API rejects the new scene.
|
||||
- Writing XLSX shard files. Shards should be CSV; finalize handles final format.
|
||||
- Adding automated tests despite the repository ban.
|
||||
4
.codex/skills/export-datasource/agents/openai.yaml
Normal file
4
.codex/skills/export-datasource/agents/openai.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "导出数据源开发"
|
||||
short_description: "指导新增和维护项目导出数据源场景"
|
||||
default_prompt: "Use $export-datasource to add a new export scene for this project."
|
||||
@@ -0,0 +1,222 @@
|
||||
# Export Scene Template
|
||||
|
||||
Use this reference when adding a new export scene.
|
||||
|
||||
## Minimal Checklist
|
||||
|
||||
- Add `ExportTaskSceneXxx` in `pkg/constants/constants.go`.
|
||||
- Add the scene to `CreateExportTaskRequest.Scene` and `ListExportTaskRequest.Scene` validation/description.
|
||||
- Create `internal/exporter/<scene>_scene.go`.
|
||||
- Register `NewXxxDataSource(db)` in `internal/exporter/registry.go`.
|
||||
- Update `IsSupportedScene`.
|
||||
- Run `gofmt` on changed Go files.
|
||||
- Run `go build ./internal/exporter/...`, `go build ./internal/task/...`, and `go build ./...`.
|
||||
- Manually create a task and validate database rows plus downloaded file.
|
||||
|
||||
## Skeleton
|
||||
|
||||
```go
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// XxxDataSource Xxx 导出数据源。
|
||||
type XxxDataSource struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewXxxDataSource 创建 Xxx 导出数据源。
|
||||
func NewXxxDataSource(db *gorm.DB) *XxxDataSource {
|
||||
return &XxxDataSource{db: db}
|
||||
}
|
||||
|
||||
// Scene 返回导出场景编码。
|
||||
func (s *XxxDataSource) Scene() string {
|
||||
return constants.ExportTaskSceneXxx
|
||||
}
|
||||
|
||||
// Count 统计 Xxx 导出行数。
|
||||
func (s *XxxDataSource) Count(ctx context.Context, params ExportParams) (int, error) {
|
||||
var total int64
|
||||
query := s.applyFilters(s.db.WithContext(ctx).Model(&model.Xxx{}), params)
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(total), nil
|
||||
}
|
||||
|
||||
// Headers 返回 Xxx 导出表头。
|
||||
func (s *XxxDataSource) Headers(ctx context.Context, params ExportParams) ([]string, error) {
|
||||
return []string{"ID", "名称", "状态", "店铺ID", "创建时间"}, nil
|
||||
}
|
||||
|
||||
// Fetch 按 offset/limit 查询 Xxx 导出数据。
|
||||
func (s *XxxDataSource) Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error) {
|
||||
if limit <= 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
var items []model.Xxx
|
||||
query := s.applyFilters(s.db.WithContext(ctx).Model(&model.Xxx{}), params).
|
||||
Order("id ASC").
|
||||
Limit(limit).
|
||||
Offset(offset)
|
||||
if err := query.Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([][]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
rows = append(rows, []string{
|
||||
strconv.FormatUint(uint64(item.ID), 10),
|
||||
item.Name,
|
||||
strconv.Itoa(item.Status),
|
||||
formatOptionalUint(item.ShopID),
|
||||
item.CreatedAt.Format(exportTimeLayout),
|
||||
})
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (s *XxxDataSource) applyFilters(query *gorm.DB, params ExportParams) *gorm.DB {
|
||||
query = applyExportShopScope(query, params, "shop_id")
|
||||
|
||||
if status, ok := filterInt(params.Filters, "status"); ok {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
if shopID, ok := filterUint(params.Filters, "shop_id"); ok {
|
||||
query = query.Where("shop_id = ?", shopID)
|
||||
}
|
||||
if start, ok := filterTime(params.Filters, "created_at_start"); ok {
|
||||
query = query.Where("created_at >= ?", start)
|
||||
}
|
||||
if end, ok := filterTime(params.Filters, "created_at_end"); ok {
|
||||
query = query.Where("created_at <= ?", end)
|
||||
}
|
||||
return query
|
||||
}
|
||||
```
|
||||
|
||||
## JOIN Skeleton
|
||||
|
||||
Use this shape for multi-table exports:
|
||||
|
||||
```go
|
||||
type xxxExportRow struct {
|
||||
ID uint
|
||||
Name string
|
||||
Status int
|
||||
ShopID *uint
|
||||
ExtraName string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func (s *XxxDataSource) Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error) {
|
||||
if limit <= 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
var items []xxxExportRow
|
||||
query := s.applyFilters(s.db.WithContext(ctx).Table("tb_xxx AS x"), params, "x.").
|
||||
Select(`
|
||||
x.id,
|
||||
x.name,
|
||||
x.status,
|
||||
x.shop_id,
|
||||
COALESCE(e.name, '') AS extra_name,
|
||||
x.created_at
|
||||
`).
|
||||
Joins("LEFT JOIN tb_extra AS e ON e.xxx_id = x.id AND e.deleted_at IS NULL").
|
||||
Where("x.deleted_at IS NULL").
|
||||
Order("x.id ASC").
|
||||
Limit(limit).
|
||||
Offset(offset)
|
||||
if err := query.Scan(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([][]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
rows = append(rows, []string{
|
||||
strconv.FormatUint(uint64(item.ID), 10),
|
||||
item.Name,
|
||||
strconv.Itoa(item.Status),
|
||||
formatOptionalUint(item.ShopID),
|
||||
item.ExtraName,
|
||||
item.CreatedAt.Format(exportTimeLayout),
|
||||
})
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (s *XxxDataSource) applyFilters(query *gorm.DB, params ExportParams, prefix string) *gorm.DB {
|
||||
query = applyExportShopScope(query, params, prefix+"shop_id")
|
||||
if status, ok := filterInt(params.Filters, "status"); ok {
|
||||
query = query.Where(prefix+"status = ?", status)
|
||||
}
|
||||
return query
|
||||
}
|
||||
```
|
||||
|
||||
If the JOIN multiplies rows, update `Count` to count the same exported row set. Do not count only the base table unless `Fetch` also returns one row per base record.
|
||||
|
||||
## Registry Patch
|
||||
|
||||
```go
|
||||
func NewDefaultRegistry(db *gorm.DB) *Registry {
|
||||
return NewRegistry(
|
||||
NewDeviceDataSource(db),
|
||||
NewIotCardDataSource(db),
|
||||
NewXxxDataSource(db),
|
||||
)
|
||||
}
|
||||
|
||||
func IsSupportedScene(scene string) bool {
|
||||
switch scene {
|
||||
case constants.ExportTaskSceneDevice,
|
||||
constants.ExportTaskSceneIotCard,
|
||||
constants.ExportTaskSceneXxx:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Manual API Smoke
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://localhost:端口/api/admin/export-tasks' \
|
||||
-H 'Authorization: Bearer <token>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"scene": "xxx",
|
||||
"format": "csv",
|
||||
"query": {
|
||||
"filters": {
|
||||
"status": 1
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Check database:
|
||||
|
||||
```sql
|
||||
SELECT id, scene, format, status, total_rows, total_shards, query_json
|
||||
FROM tb_export_task
|
||||
WHERE id = <task_id>;
|
||||
|
||||
SELECT shard_no, status, shard_offset, shard_limit, row_count, file_key
|
||||
FROM tb_export_shard_task
|
||||
WHERE task_id = <task_id>
|
||||
ORDER BY shard_no;
|
||||
```
|
||||
156
.codex/skills/openspec-apply-change/SKILL.md
Normal file
156
.codex/skills/openspec-apply-change/SKILL.md
Normal file
@@ -0,0 +1,156 @@
|
||||
---
|
||||
name: openspec-apply-change
|
||||
description: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.2.0"
|
||||
---
|
||||
|
||||
Implement tasks from an OpenSpec change.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:apply <other>`).
|
||||
|
||||
2. **Check status to understand the schema**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
||||
- Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
|
||||
|
||||
3. **Get apply instructions**
|
||||
|
||||
```bash
|
||||
openspec instructions apply --change "<name>" --json
|
||||
```
|
||||
|
||||
This returns:
|
||||
- Context file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)
|
||||
- Progress (total, complete, remaining)
|
||||
- Task list with status
|
||||
- Dynamic instruction based on current state
|
||||
|
||||
**Handle states:**
|
||||
- If `state: "blocked"` (missing artifacts): show message, suggest using openspec-continue-change
|
||||
- If `state: "all_done"`: congratulate, suggest archive
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
4. **Read context files**
|
||||
|
||||
Read the files listed in `contextFiles` from the apply instructions output.
|
||||
The files depend on the schema being used:
|
||||
- **spec-driven**: proposal, specs, design, tasks
|
||||
- Other schemas: follow the contextFiles from CLI output
|
||||
|
||||
5. **Show current progress**
|
||||
|
||||
Display:
|
||||
- Schema being used
|
||||
- Progress: "N/M tasks complete"
|
||||
- Remaining tasks overview
|
||||
- Dynamic instruction from CLI
|
||||
|
||||
6. **Implement tasks (loop until done or blocked)**
|
||||
|
||||
For each pending task:
|
||||
- Show which task is being worked on
|
||||
- Make the code changes required
|
||||
- Keep changes minimal and focused
|
||||
- Mark task complete in the tasks file: `- [ ]` → `- [x]`
|
||||
- Continue to next task
|
||||
|
||||
**Pause if:**
|
||||
- Task is unclear → ask for clarification
|
||||
- Implementation reveals a design issue → suggest updating artifacts
|
||||
- Error or blocker encountered → report and wait for guidance
|
||||
- User interrupts
|
||||
|
||||
7. **On completion or pause, show status**
|
||||
|
||||
Display:
|
||||
- Tasks completed this session
|
||||
- Overall progress: "N/M tasks complete"
|
||||
- If all done: suggest archive
|
||||
- If paused: explain why and wait for guidance
|
||||
|
||||
**Output During Implementation**
|
||||
|
||||
```
|
||||
## Implementing: <change-name> (schema: <schema-name>)
|
||||
|
||||
Working on task 3/7: <task description>
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
|
||||
Working on task 4/7: <task description>
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
```
|
||||
|
||||
**Output On Completion**
|
||||
|
||||
```
|
||||
## Implementation Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Progress:** 7/7 tasks complete ✓
|
||||
|
||||
### Completed This Session
|
||||
- [x] Task 1
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tasks complete! Ready to archive this change.
|
||||
```
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
|
||||
```
|
||||
## Implementation Paused
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Progress:** 4/7 tasks complete
|
||||
|
||||
### Issue Encountered
|
||||
<description of the issue>
|
||||
|
||||
**Options:**
|
||||
1. <option 1>
|
||||
2. <option 2>
|
||||
3. Other approach
|
||||
|
||||
What would you like to do?
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Keep going through tasks until done or blocked
|
||||
- Always read context files before starting (from the apply instructions output)
|
||||
- If task is ambiguous, pause and ask before implementing
|
||||
- If implementation reveals issues, pause and suggest artifact updates
|
||||
- Keep code changes minimal and scoped to each task
|
||||
- Update task checkbox immediately after completing each task
|
||||
- Pause on errors, blockers, or unclear requirements - don't guess
|
||||
- Use contextFiles from CLI output, don't assume specific file names
|
||||
|
||||
**Fluid Workflow Integration**
|
||||
|
||||
This skill supports the "actions on a change" model:
|
||||
|
||||
- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions
|
||||
- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly
|
||||
114
.codex/skills/openspec-archive-change/SKILL.md
Normal file
114
.codex/skills/openspec-archive-change/SKILL.md
Normal file
@@ -0,0 +1,114 @@
|
||||
---
|
||||
name: openspec-archive-change
|
||||
description: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.2.0"
|
||||
---
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
|
||||
Show only active changes (not already archived).
|
||||
Include the schema used for each change if available.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Check artifact completion status**
|
||||
|
||||
Run `openspec status --change "<name>" --json` to check artifact completion.
|
||||
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used
|
||||
- `artifacts`: List of artifacts with their status (`done` or other)
|
||||
|
||||
**If any artifacts are not `done`:**
|
||||
- Display warning listing incomplete artifacts
|
||||
- Use **AskUserQuestion tool** to confirm user wants to proceed
|
||||
- Proceed if user confirms
|
||||
|
||||
3. **Check task completion status**
|
||||
|
||||
Read the tasks file (typically `tasks.md`) to check for incomplete tasks.
|
||||
|
||||
Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).
|
||||
|
||||
**If incomplete tasks found:**
|
||||
- Display warning showing count of incomplete tasks
|
||||
- Use **AskUserQuestion tool** to confirm user wants to proceed
|
||||
- Proceed if user confirms
|
||||
|
||||
**If no tasks file exists:** Proceed without task-related warning.
|
||||
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
Check for delta specs at `openspec/changes/<name>/specs/`. If none exist, proceed without sync prompt.
|
||||
|
||||
**If delta specs exist:**
|
||||
- Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`
|
||||
- Determine what changes would be applied (adds, modifications, removals, renames)
|
||||
- Show a combined summary before prompting
|
||||
|
||||
**Prompt options:**
|
||||
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
||||
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
||||
|
||||
If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>"). Proceed to archive regardless of choice.
|
||||
|
||||
5. **Perform the archive**
|
||||
|
||||
Create the archive directory if it doesn't exist:
|
||||
```bash
|
||||
mkdir -p openspec/changes/archive
|
||||
```
|
||||
|
||||
Generate target name using current date: `YYYY-MM-DD-<change-name>`
|
||||
|
||||
**Check if target already exists:**
|
||||
- If yes: Fail with error, suggest renaming existing archive or using different date
|
||||
- If no: Move the change directory to archive
|
||||
|
||||
```bash
|
||||
mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
|
||||
```
|
||||
|
||||
6. **Display summary**
|
||||
|
||||
Show archive completion summary including:
|
||||
- Change name
|
||||
- Schema that was used
|
||||
- Archive location
|
||||
- Whether specs were synced (if applicable)
|
||||
- Note about any warnings (incomplete artifacts/tasks)
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** ✓ Synced to main specs (or "No delta specs" or "Sync skipped")
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Always prompt for change selection if not provided
|
||||
- Use artifact graph (openspec status --json) for completion checking
|
||||
- Don't block archive on warnings - just inform and confirm
|
||||
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
||||
- Show clear summary of what happened
|
||||
- If sync is requested, use openspec-sync-specs approach (agent-driven)
|
||||
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
||||
288
.codex/skills/openspec-explore/SKILL.md
Normal file
288
.codex/skills/openspec-explore/SKILL.md
Normal file
@@ -0,0 +1,288 @@
|
||||
---
|
||||
name: openspec-explore
|
||||
description: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.2.0"
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
|
||||
|
||||
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
||||
|
||||
---
|
||||
|
||||
## The Stance
|
||||
|
||||
- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script
|
||||
- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.
|
||||
- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking
|
||||
- **Adaptive** - Follow interesting threads, pivot when new information emerges
|
||||
- **Patient** - Don't rush to conclusions, let the shape of the problem emerge
|
||||
- **Grounded** - Explore the actual codebase when relevant, don't just theorize
|
||||
|
||||
---
|
||||
|
||||
## What You Might Do
|
||||
|
||||
Depending on what the user brings, you might:
|
||||
|
||||
**Explore the problem space**
|
||||
- Ask clarifying questions that emerge from what they said
|
||||
- Challenge assumptions
|
||||
- Reframe the problem
|
||||
- Find analogies
|
||||
|
||||
**Investigate the codebase**
|
||||
- Map existing architecture relevant to the discussion
|
||||
- Find integration points
|
||||
- Identify patterns already in use
|
||||
- Surface hidden complexity
|
||||
|
||||
**Compare options**
|
||||
- Brainstorm multiple approaches
|
||||
- Build comparison tables
|
||||
- Sketch tradeoffs
|
||||
- Recommend a path (if asked)
|
||||
|
||||
**Visualize**
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Use ASCII diagrams liberally │
|
||||
├─────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────┐ ┌────────┐ │
|
||||
│ │ State │────────▶│ State │ │
|
||||
│ │ A │ │ B │ │
|
||||
│ └────────┘ └────────┘ │
|
||||
│ │
|
||||
│ System diagrams, state machines, │
|
||||
│ data flows, architecture sketches, │
|
||||
│ dependency graphs, comparison tables │
|
||||
│ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Surface risks and unknowns**
|
||||
- Identify what could go wrong
|
||||
- Find gaps in understanding
|
||||
- Suggest spikes or investigations
|
||||
|
||||
---
|
||||
|
||||
## OpenSpec Awareness
|
||||
|
||||
You have full context of the OpenSpec system. Use it naturally, don't force it.
|
||||
|
||||
### Check for context
|
||||
|
||||
At the start, quickly check what exists:
|
||||
```bash
|
||||
openspec list --json
|
||||
```
|
||||
|
||||
This tells you:
|
||||
- If there are active changes
|
||||
- Their names, schemas, and status
|
||||
- What the user might be working on
|
||||
|
||||
### When no change exists
|
||||
|
||||
Think freely. When insights crystallize, you might offer:
|
||||
|
||||
- "This feels solid enough to start a change. Want me to create a proposal?"
|
||||
- Or keep exploring - no pressure to formalize
|
||||
|
||||
### When a change exists
|
||||
|
||||
If the user mentions a change or you detect one is relevant:
|
||||
|
||||
1. **Read existing artifacts for context**
|
||||
- `openspec/changes/<name>/proposal.md`
|
||||
- `openspec/changes/<name>/design.md`
|
||||
- `openspec/changes/<name>/tasks.md`
|
||||
- etc.
|
||||
|
||||
2. **Reference them naturally in conversation**
|
||||
- "Your design mentions using Redis, but we just realized SQLite fits better..."
|
||||
- "The proposal scopes this to premium users, but we're now thinking everyone..."
|
||||
|
||||
3. **Offer to capture when decisions are made**
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|--------------|------------------|
|
||||
| New requirement discovered | `specs/<capability>/spec.md` |
|
||||
| Requirement changed | `specs/<capability>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
- "This is a new requirement. Add it to specs?"
|
||||
- "This changes scope. Update the proposal?"
|
||||
|
||||
4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.
|
||||
|
||||
---
|
||||
|
||||
## What You Don't Have To Do
|
||||
|
||||
- Follow a script
|
||||
- Ask the same questions every time
|
||||
- Produce a specific artifact
|
||||
- Reach a conclusion
|
||||
- Stay on topic if a tangent is valuable
|
||||
- Be brief (this is thinking time)
|
||||
|
||||
---
|
||||
|
||||
## Handling Different Entry Points
|
||||
|
||||
**User brings a vague idea:**
|
||||
```
|
||||
User: I'm thinking about adding real-time collaboration
|
||||
|
||||
You: Real-time collab is a big space. Let me think about this...
|
||||
|
||||
COLLABORATION SPECTRUM
|
||||
════════════════════════════════════════════
|
||||
|
||||
Awareness Coordination Sync
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌────────┐ ┌────────┐ ┌────────┐
|
||||
│Presence│ │Cursors │ │ CRDT │
|
||||
│ "3 │ │ Multi │ │Conflict│
|
||||
│online" │ │ select │ │ free │
|
||||
└────────┘ └────────┘ └────────┘
|
||||
│ │ │
|
||||
trivial moderate complex
|
||||
|
||||
Where's your head at?
|
||||
```
|
||||
|
||||
**User brings a specific problem:**
|
||||
```
|
||||
User: The auth system is a mess
|
||||
|
||||
You: [reads codebase]
|
||||
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ CURRENT AUTH FLOW │
|
||||
└─────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────┼─────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ Google │ │ GitHub │ │ Email │
|
||||
│ OAuth │ │ OAuth │ │ Magic │
|
||||
└────┬────┘ └────┬────┘ └────┬────┘
|
||||
│ │ │
|
||||
└─────────────┼─────────────┘
|
||||
▼
|
||||
┌───────────┐
|
||||
│ Session │
|
||||
└─────┬─────┘
|
||||
│
|
||||
▼
|
||||
┌───────────┐
|
||||
│ Perms │
|
||||
└───────────┘
|
||||
|
||||
I see three tangles. Which one's burning?
|
||||
```
|
||||
|
||||
**User is stuck mid-implementation:**
|
||||
```
|
||||
User: /opsx:explore add-auth-system
|
||||
The OAuth integration is more complex than expected
|
||||
|
||||
You: [reads change artifacts]
|
||||
|
||||
You're on task 4: "Implement OAuth flow"
|
||||
|
||||
Let me trace what's involved...
|
||||
|
||||
[draws diagram, explores options, suggests paths]
|
||||
|
||||
Want to update the design to reflect this?
|
||||
Or add a spike task to investigate?
|
||||
```
|
||||
|
||||
**User wants to compare options:**
|
||||
```
|
||||
User: Should we use Postgres or SQLite?
|
||||
|
||||
You: Generic answer is boring. What's the context?
|
||||
|
||||
User: A CLI tool that tracks local dev environments
|
||||
|
||||
You: That changes everything.
|
||||
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ CLI TOOL DATA STORAGE │
|
||||
└─────────────────────────────────────────────────┘
|
||||
|
||||
Key constraints:
|
||||
• No daemon running
|
||||
• Must work offline
|
||||
• Single user
|
||||
|
||||
SQLite Postgres
|
||||
Deployment embedded ✓ needs server ✗
|
||||
Offline yes ✓ no ✗
|
||||
Single file yes ✓ no ✗
|
||||
|
||||
SQLite. Not even close.
|
||||
|
||||
Unless... is there a sync component?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ending Discovery
|
||||
|
||||
There's no required ending. Discovery might:
|
||||
|
||||
- **Flow into a proposal**: "Ready to start? I can create a change proposal."
|
||||
- **Result in artifact updates**: "Updated design.md with these decisions"
|
||||
- **Just provide clarity**: User has what they need, moves on
|
||||
- **Continue later**: "We can pick this up anytime"
|
||||
|
||||
When it feels like things are crystallizing, you might summarize:
|
||||
|
||||
```
|
||||
## What We Figured Out
|
||||
|
||||
**The problem**: [crystallized understanding]
|
||||
|
||||
**The approach**: [if one emerged]
|
||||
|
||||
**Open questions**: [if any remain]
|
||||
|
||||
**Next steps** (if ready):
|
||||
- Create a change proposal
|
||||
- Keep exploring: just keep talking
|
||||
```
|
||||
|
||||
But this summary is optional. Sometimes the thinking IS the value.
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.
|
||||
- **Don't fake understanding** - If something is unclear, dig deeper
|
||||
- **Don't rush** - Discovery is thinking time, not task time
|
||||
- **Don't force structure** - Let patterns emerge naturally
|
||||
- **Don't auto-capture** - Offer to save insights, don't just do it
|
||||
- **Do visualize** - A good diagram is worth many paragraphs
|
||||
- **Do explore the codebase** - Ground discussions in reality
|
||||
- **Do question assumptions** - Including the user's and your own
|
||||
110
.codex/skills/openspec-propose/SKILL.md
Normal file
110
.codex/skills/openspec-propose/SKILL.md
Normal file
@@ -0,0 +1,110 @@
|
||||
---
|
||||
name: openspec-propose
|
||||
description: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.2.0"
|
||||
---
|
||||
|
||||
Propose a new change - create the change and generate all artifacts in one step.
|
||||
|
||||
I'll create a change with artifacts:
|
||||
- proposal.md (what & why)
|
||||
- design.md (how)
|
||||
- tasks.md (implementation steps)
|
||||
|
||||
When ready to implement, run /opsx:apply
|
||||
|
||||
---
|
||||
|
||||
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no clear input provided, ask what they want to build**
|
||||
|
||||
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
2. **Create the change directory**
|
||||
```bash
|
||||
openspec new change "<name>"
|
||||
```
|
||||
This creates a scaffolded change at `openspec/changes/<name>/` with `.openspec.yaml`.
|
||||
|
||||
3. **Get the artifact build order**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to get:
|
||||
- `applyRequires`: array of artifact IDs needed before implementation (e.g., `["tasks"]`)
|
||||
- `artifacts`: list of all artifacts with their status and dependencies
|
||||
|
||||
4. **Create artifacts in sequence until apply-ready**
|
||||
|
||||
Use the **TodoWrite tool** to track progress through the artifacts.
|
||||
|
||||
Loop through artifacts in dependency order (artifacts with no pending dependencies first):
|
||||
|
||||
a. **For each artifact that is `ready` (dependencies satisfied)**:
|
||||
- Get instructions:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- The instructions JSON includes:
|
||||
- `context`: Project background (constraints for you - do NOT include in output)
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance for this artifact type
|
||||
- `outputPath`: Where to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- Read any completed dependency files for context
|
||||
- Create the artifact file using `template` as the structure
|
||||
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
||||
- Show brief progress: "Created <artifact-id>"
|
||||
|
||||
b. **Continue until all `applyRequires` artifacts are complete**
|
||||
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
||||
- Check if every artifact ID in `applyRequires` has `status: "done"` in the artifacts array
|
||||
- Stop when all `applyRequires` artifacts are done
|
||||
|
||||
c. **If an artifact requires user input** (unclear context):
|
||||
- Use **AskUserQuestion tool** to clarify
|
||||
- Then continue with creation
|
||||
|
||||
5. **Show final status**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
|
||||
**Output**
|
||||
|
||||
After completing all artifacts, summarize:
|
||||
- Change name and location
|
||||
- List of artifacts created with brief descriptions
|
||||
- What's ready: "All artifacts created! Ready for implementation."
|
||||
- Prompt: "Run `/opsx:apply` or ask me to implement to start working on the tasks."
|
||||
|
||||
**Artifact Creation Guidelines**
|
||||
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type
|
||||
- The schema defines what each artifact should contain - follow it
|
||||
- Read dependency artifacts for context before creating new ones
|
||||
- Use `template` as the structure for your output file - fill in its sections
|
||||
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
||||
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
||||
- These guide what you write, but should never appear in the output
|
||||
|
||||
**Guardrails**
|
||||
- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)
|
||||
- Always read dependency artifacts before creating a new one
|
||||
- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum
|
||||
- If a change with that name already exists, ask if user wants to continue it or create a new one
|
||||
- Verify each artifact file exists after writing before proceeding to next
|
||||
36
.config/dbhub.toml
Normal file
36
.config/dbhub.toml
Normal file
@@ -0,0 +1,36 @@
|
||||
[[sources]]
|
||||
id = "main"
|
||||
description = "当前项目库"
|
||||
dsn = "postgresql://erp_pgsql:erp_2025@cxd.whcxd.cn:16159/junhong_cmp_test?sslmode=disable"
|
||||
|
||||
[[sources]]
|
||||
id = "legacy_mysql"
|
||||
description = "老系统 MySQL 库,仅用于 AI 查询和迁移脚本准备"
|
||||
type = "mysql"
|
||||
host = "120.79.89.216"
|
||||
port = 3306
|
||||
database = "kyhl"
|
||||
user = "root"
|
||||
password = "XiWaJ9Eu%go9-2>g!xGp"
|
||||
sslmode = "disable"
|
||||
lazy = true
|
||||
|
||||
[[tools]]
|
||||
name = "search_objects"
|
||||
source = "main"
|
||||
|
||||
[[tools]]
|
||||
name = "execute_sql"
|
||||
source = "main"
|
||||
readonly = true # Only allow SELECT, SHOW, DESCRIBE, EXPLAIN
|
||||
max_rows = 1000 # Limit query results
|
||||
|
||||
[[tools]]
|
||||
name = "search_objects"
|
||||
source = "legacy_mysql"
|
||||
|
||||
[[tools]]
|
||||
name = "execute_sql"
|
||||
source = "legacy_mysql"
|
||||
readonly = true # Only allow SELECT, SHOW, DESCRIBE, EXPLAIN
|
||||
max_rows = 5000 # Limit query results for migration exploration
|
||||
11
.env
Normal file
11
.env
Normal file
@@ -0,0 +1,11 @@
|
||||
MIGRATIONS_DIR=migrations
|
||||
DB_HOST=cxd.whcxd.cn
|
||||
DB_PORT=16159
|
||||
DB_USER=erp_pgsql
|
||||
DB_PASSWORD=erp_2025
|
||||
DB_NAME=junhong_cmp_test
|
||||
DB_SSLMODE=disable
|
||||
GOOGLE_GEMINI_BASE_URL="http://45.155.220.179:8317" # 根据实际填写你服务器的ip地址或者域名
|
||||
GEMINI_API_KEY="sk-VoNbvr6aGpjvZX64rvhrwowrZrCgtGuX9oxykIy8F1DBg"
|
||||
GOOGLE_GENAI_USE_GCA="true"
|
||||
GEMINI_MODEL="gemini-3-pro-preview" # 如果你有gemini3权限可以填: gemini-3-pro-preview
|
||||
93
.env.local
Normal file
93
.env.local
Normal file
@@ -0,0 +1,93 @@
|
||||
# ============================================================================
|
||||
# 君鸿卡管系统 - 本地开发环境变量
|
||||
# 生成时间: 2026-01-30 17:45:14
|
||||
# ============================================================================
|
||||
# 使用方法:
|
||||
# source .env.local && go run cmd/api/main.go
|
||||
# 或者:
|
||||
# ./scripts/run-local.sh
|
||||
# ============================================================================
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 数据库配置(必填)
|
||||
# ----------------------------------------------------------------------------
|
||||
export JUNHONG_DATABASE_HOST="cxd.whcxd.cn"
|
||||
export JUNHONG_DATABASE_PORT="16159"
|
||||
export JUNHONG_DATABASE_USER="erp_pgsql"
|
||||
export JUNHONG_DATABASE_PASSWORD="erp_2025"
|
||||
export JUNHONG_DATABASE_DBNAME="junhong_cmp_test"
|
||||
export JUNHONG_DATABASE_SSLMODE="disable"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Redis 配置(必填)
|
||||
# ----------------------------------------------------------------------------
|
||||
export JUNHONG_REDIS_ADDRESS="cxd.whcxd.cn"
|
||||
export JUNHONG_REDIS_PORT="16299"
|
||||
export JUNHONG_REDIS_PASSWORD="cpNbWtAaqgo1YJmbMp3h"
|
||||
export JUNHONG_REDIS_DB="6"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# JWT 配置(必填)
|
||||
# ----------------------------------------------------------------------------
|
||||
export JUNHONG_JWT_SECRET_KEY="dev-secret-key-for-testing-only-32chars!"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 客户端配置(可选)
|
||||
# ----------------------------------------------------------------------------
|
||||
# 是否强制 C 端用户绑定手机号(true: 强制,false: 不强制)
|
||||
export JUNHONG_CLIENT_REQUIRE_PHONE_BINDING="true"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 服务器配置
|
||||
# ----------------------------------------------------------------------------
|
||||
export JUNHONG_SERVER_ADDRESS=":3000"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 日志配置
|
||||
# ----------------------------------------------------------------------------
|
||||
export JUNHONG_LOGGING_LEVEL="debug"
|
||||
export JUNHONG_LOGGING_DEVELOPMENT="true"
|
||||
export JUNHONG_LOGGING_APP_LOG_FILENAME="logs/app.log"
|
||||
export JUNHONG_LOGGING_ACCESS_LOG_FILENAME="logs/access.log"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Gateway 服务配置
|
||||
# ----------------------------------------------------------------------------
|
||||
export JUNHONG_GATEWAY_BASE_URL="https://open.whjhft.com/openapi"
|
||||
export JUNHONG_GATEWAY_APP_ID="LfjL0WjUqpwkItQ0"
|
||||
export JUNHONG_GATEWAY_APP_SECRET="K0DYuWzbRE6zg5bX"
|
||||
export JUNHONG_GATEWAY_TIMEOUT="30"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 对象存储配置
|
||||
# ----------------------------------------------------------------------------
|
||||
export JUNHONG_STORAGE_PROVIDER="s3"
|
||||
export JUNHONG_STORAGE_S3_ENDPOINT="http://obs-helf.cucloud.cn"
|
||||
export JUNHONG_STORAGE_S3_REGION="cn-langfang-2"
|
||||
export JUNHONG_STORAGE_S3_BUCKET="cmp"
|
||||
export JUNHONG_STORAGE_S3_ACCESS_KEY_ID="598F558CF6FF46E79D1CFC607852378C9523"
|
||||
export JUNHONG_STORAGE_S3_SECRET_ACCESS_KEY="8393425DCB2F48F1914FF39DCBC6C7B17325"
|
||||
export JUNHONG_STORAGE_S3_USE_SSL="false"
|
||||
export JUNHONG_STORAGE_S3_PATH_STYLE="true"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 迁移工具兼容配置(用于 scripts/migrate.sh)
|
||||
# ----------------------------------------------------------------------------
|
||||
export MIGRATIONS_DIR="migrations"
|
||||
export DB_HOST="cxd.whcxd.cn"
|
||||
export DB_PORT="16159"
|
||||
export DB_USER="erp_pgsql"
|
||||
export DB_PASSWORD="erp_2025"
|
||||
export DB_NAME="junhong_cmp_test"
|
||||
export DB_SSLMODE="disable"
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 短信服务配置
|
||||
# ----------------------------------------------------------------------------
|
||||
export JUNHONG_SMS_GATEWAY_URL="https://gateway.sms.whjhft.com:8443"
|
||||
export JUNHONG_SMS_USERNAME="JH0001"
|
||||
export JUNHONG_SMS_PASSWORD="wwR8E4qnL6F0"
|
||||
export JUNHONG_SMS_SIGNATURE="【JHFTIOT】"
|
||||
|
||||
JUNHONG_QUEUE_CONCURRENCY=1000
|
||||
236
.env.prod
Normal file
236
.env.prod
Normal file
@@ -0,0 +1,236 @@
|
||||
# ==========================================================================
|
||||
# 君鸿卡管系统 - 本地开发环境变量
|
||||
# 生成时间: 2026-05-07 11:56:19
|
||||
# 配置依据: pkg/config/defaults/config.yaml + pkg/config/loader.go
|
||||
# ==========================================================================
|
||||
# 使用方法:
|
||||
# source .env.prod && go run cmd/api/main.go
|
||||
# 或者:
|
||||
# ./scripts/run-local.sh
|
||||
#
|
||||
# 说明:
|
||||
# - 未注释的 是本地启动常用或必填配置。
|
||||
# - 注释掉的 是当前有默认值、按需启用或当前主流程未使用的配置。
|
||||
# - 取消注释前请先阅读上方说明,避免覆盖嵌入配置默认值。
|
||||
# ==========================================================================
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 数据库配置(必填)
|
||||
# ----------------------------------------------------------------------------
|
||||
# 数据库主机地址;应用启动必填。
|
||||
export JUNHONG_DATABASE_HOST=cxd.whcxd.cn
|
||||
# 数据库端口;默认 5432。
|
||||
export JUNHONG_DATABASE_PORT=16159
|
||||
# 数据库用户名;应用启动必填。
|
||||
export JUNHONG_DATABASE_USER=junhong_cmp
|
||||
# 数据库密码;应用启动必填,敏感信息请勿提交。
|
||||
export JUNHONG_DATABASE_PASSWORD=CtCom1zBzPQbpVNf3rCNxH
|
||||
# 数据库名称;应用启动必填。
|
||||
export JUNHONG_DATABASE_DBNAME=junhong_cmp_prod
|
||||
# 数据库 SSL 模式;本地通常使用 disable。
|
||||
export JUNHONG_DATABASE_SSLMODE=disable
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Redis 配置(必填)
|
||||
# ----------------------------------------------------------------------------
|
||||
# Redis 主机地址;应用启动必填。
|
||||
export JUNHONG_REDIS_ADDRESS=192.168.1.22
|
||||
# Redis 端口;默认 6379。
|
||||
export JUNHONG_REDIS_PORT=6379
|
||||
# Redis 密码;无密码时留空。
|
||||
export JUNHONG_REDIS_PASSWORD=Bm500vXEyLg0RUzw7YcyPjbRifhz
|
||||
# Redis 数据库编号;默认 0。
|
||||
export JUNHONG_REDIS_DB=0
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# JWT 配置(必填)
|
||||
# ----------------------------------------------------------------------------
|
||||
# JWT 签名密钥;必须至少 32 字符,生产环境必须单独生成。
|
||||
export JUNHONG_JWT_SECRET_KEY=a0xzpGhR3zB54JAiEJtoKnjwgAGMLmRw
|
||||
# C 端 JWT Token 有效期;默认 24h,通常无需覆盖。
|
||||
export JUNHONG_JWT_TOKEN_DURATION=24h
|
||||
# B 端访问令牌 TTL;默认 24h。
|
||||
export JUNHONG_JWT_ACCESS_TOKEN_TTL=24h
|
||||
# B 端刷新令牌 TTL;默认 168h。
|
||||
export JUNHONG_JWT_REFRESH_TOKEN_TTL=168h
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 服务器配置
|
||||
# ----------------------------------------------------------------------------
|
||||
# 服务监听地址;本地默认 :3000。
|
||||
export JUNHONG_SERVER_ADDRESS=:3527
|
||||
# 读取请求超时时间;默认 30s,通常无需覆盖。
|
||||
export JUNHONG_SERVER_READ_TIMEOUT=30s
|
||||
# 写响应超时时间;默认 30s,通常无需覆盖。
|
||||
export JUNHONG_SERVER_WRITE_TIMEOUT=30s
|
||||
# 优雅关闭超时时间;默认 30s。
|
||||
export JUNHONG_SERVER_SHUTDOWN_TIMEOUT=30s
|
||||
# Fiber 预分叉模式;本地和 macOS 开发环境通常不要启用。
|
||||
# JUNHONG_SERVER_PREFORK=false
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 日志配置
|
||||
# ----------------------------------------------------------------------------
|
||||
# 日志级别;可选 debug/info/warn/error。
|
||||
export JUNHONG_LOGGING_LEVEL=debug
|
||||
# 本地开发模式日志;生产环境应使用 false。
|
||||
export JUNHONG_LOGGING_DEVELOPMENT=true
|
||||
# 应用日志文件路径;本地写入项目 logs 目录。
|
||||
export JUNHONG_LOGGING_APP_LOG_FILENAME=logs/app.log
|
||||
# 访问日志文件路径;本地写入项目 logs 目录。
|
||||
export JUNHONG_LOGGING_ACCESS_LOG_FILENAME=logs/access.log
|
||||
# 应用日志单文件最大大小(MB);默认 100。
|
||||
export JUNHONG_LOGGING_APP_LOG_MAX_SIZE=100
|
||||
# 应用日志最多保留的旧文件数;默认 3。
|
||||
export JUNHONG_LOGGING_APP_LOG_MAX_BACKUPS=15
|
||||
# 应用日志最多保留天数;默认 7。
|
||||
export JUNHONG_LOGGING_APP_LOG_MAX_AGE=14
|
||||
# 是否压缩应用日志轮转文件;默认 true。
|
||||
export JUNHONG_LOGGING_APP_LOG_COMPRESS=true
|
||||
# 访问日志单文件最大大小(MB);默认 100。
|
||||
export JUNHONG_LOGGING_ACCESS_LOG_MAX_SIZE=100
|
||||
# 访问日志最多保留的旧文件数;默认 3。
|
||||
export JUNHONG_LOGGING_ACCESS_LOG_MAX_BACKUPS=15
|
||||
# 访问日志最多保留天数;默认 7。
|
||||
export JUNHONG_LOGGING_ACCESS_LOG_MAX_AGE=14
|
||||
# 是否压缩访问日志轮转文件;默认 true。
|
||||
export JUNHONG_LOGGING_ACCESS_LOG_COMPRESS=true
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 客户端配置
|
||||
# ----------------------------------------------------------------------------
|
||||
# 是否强制 C 端用户绑定手机号;当前默认 true。
|
||||
export JUNHONG_CLIENT_REQUIRE_PHONE_BINDING=true
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 数据库连接池配置(可选)
|
||||
# ----------------------------------------------------------------------------
|
||||
# 数据库最大打开连接数;默认 25,本地通常不用改。
|
||||
export JUNHONG_DATABASE_MAX_OPEN_CONNS=25
|
||||
# 数据库最大空闲连接数;默认 10。
|
||||
export JUNHONG_DATABASE_MAX_IDLE_CONNS=10
|
||||
# 数据库连接最大生命周期;默认 5m。
|
||||
export JUNHONG_DATABASE_CONN_MAX_LIFETIME=5m
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Redis 连接池配置(可选)
|
||||
# ----------------------------------------------------------------------------
|
||||
# Redis 连接池大小;默认 10。
|
||||
export JUNHONG_REDIS_POOL_SIZE=20
|
||||
# Redis 最小空闲连接数;默认 5。
|
||||
export JUNHONG_REDIS_MIN_IDLE_CONNS=5
|
||||
# Redis 建连超时;默认 5s。
|
||||
export JUNHONG_REDIS_DIAL_TIMEOUT=5s
|
||||
# Redis 读取超时;默认 3s。
|
||||
export JUNHONG_REDIS_READ_TIMEOUT=3s
|
||||
# Redis 写入超时;默认 3s。
|
||||
export JUNHONG_REDIS_WRITE_TIMEOUT=3s
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 任务队列配置(可选)
|
||||
# ----------------------------------------------------------------------------
|
||||
# Worker 并发数;默认 10。
|
||||
# JUNHONG_QUEUE_CONCURRENCY=10
|
||||
# 任务最大重试次数;默认 5。
|
||||
# JUNHONG_QUEUE_RETRY_MAX=5
|
||||
# 单个任务超时时间;默认 10m。
|
||||
# JUNHONG_QUEUE_TIMEOUT=10m
|
||||
# 队列权重 critical/default/low 当前由嵌入配置 queue.queues 管理,未绑定为环境变量。
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 限流中间件配置(可选)
|
||||
# ----------------------------------------------------------------------------
|
||||
# 是否启用全局限流;默认关闭。
|
||||
# JUNHONG_MIDDLEWARE_ENABLE_RATE_LIMITER=false
|
||||
# 限流窗口内最大请求数;默认 100。
|
||||
# JUNHONG_MIDDLEWARE_RATE_LIMITER_MAX=100
|
||||
# 限流时间窗口;默认 1m。
|
||||
# JUNHONG_MIDDLEWARE_RATE_LIMITER_EXPIRATION=1m
|
||||
# 限流存储后端;可选 memory 或 redis。
|
||||
# JUNHONG_MIDDLEWARE_RATE_LIMITER_STORAGE=memory
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 轮询配置(可选)
|
||||
# ----------------------------------------------------------------------------
|
||||
# 是否输出轮询详细日志;默认 false,排查上游数据时再开启。
|
||||
export JUNHONG_POLLING_VERBOSE_LOG=true
|
||||
# 是否启用 C 端实名自动触发;默认 true。
|
||||
# JUNHONG_POLLING_AUTO_TRIGGER_ENABLE_AUTO_TRIGGER=true
|
||||
# 自动触发使用的系统用户 ID;生产建议改成专用平台账号。
|
||||
export JUNHONG_POLLING_AUTO_TRIGGER_AUTO_TRIGGER_SYSTEM_USER_ID=1
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Worker 运行配置(可选)
|
||||
# ----------------------------------------------------------------------------
|
||||
# Worker 角色;单实例默认 all,多实例可用 leader/consumer。
|
||||
# JUNHONG_WORKER_ROLE=all
|
||||
# Worker 实例名称;多实例部署时用于区分日志。
|
||||
# JUNHONG_WORKER_INSTANCE_NAME=''
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Gateway 服务配置
|
||||
# ----------------------------------------------------------------------------
|
||||
# Gateway API 基础 URL;已按本次输入覆盖嵌入默认值。
|
||||
export JUNHONG_GATEWAY_BASE_URL=https://open.whjhft.com/openapi
|
||||
# Gateway 应用 ID;敏感信息请勿提交。
|
||||
export JUNHONG_GATEWAY_APP_ID=LfjL0WjUqpwkItQ0
|
||||
# Gateway 应用密钥;敏感信息请勿提交。
|
||||
export JUNHONG_GATEWAY_APP_SECRET=K0DYuWzbRE6zg5bX
|
||||
# Gateway 请求超时时间(秒);有效范围 5-300。
|
||||
export JUNHONG_GATEWAY_TIMEOUT=30
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 短信服务配置(可选)
|
||||
# ----------------------------------------------------------------------------
|
||||
# 短信服务未配置时系统会跳过短信客户端初始化;需要短信验证码时再取消注释。
|
||||
# 短信网关地址;设置后 username/password/signature 也必须填写。
|
||||
export JUNHONG_SMS_GATEWAY_URL='https://gateway.sms.whjhft.com:8443'
|
||||
# 短信账号用户名。
|
||||
export JUNHONG_SMS_USERNAME='JH0001'
|
||||
# 短信账号密码;敏感信息请勿提交。
|
||||
export JUNHONG_SMS_PASSWORD='wwR8E4qnL6F0'
|
||||
# 短信签名,例如【君鸿】。
|
||||
export JUNHONG_SMS_SIGNATURE='【JHFTIOT】'
|
||||
# 短信请求超时时间;默认 10s。
|
||||
export JUNHONG_SMS_TIMEOUT=10s
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 对象存储配置(可选)
|
||||
# ----------------------------------------------------------------------------
|
||||
# 对象存储提供商;当前仅支持 s3 兼容服务。
|
||||
export JUNHONG_STORAGE_PROVIDER=s3
|
||||
# 临时文件目录;用于导入导出等临时文件。
|
||||
export JUNHONG_STORAGE_TEMP_DIR=/tmp/junhong-storage
|
||||
# S3 端点;配置后会初始化对象存储服务。
|
||||
export JUNHONG_STORAGE_S3_ENDPOINT=https://obs-helf.cucloud.cn
|
||||
# S3 区域。
|
||||
export JUNHONG_STORAGE_S3_REGION=cn-langfang-2
|
||||
# S3 存储桶名称。
|
||||
export JUNHONG_STORAGE_S3_BUCKET=cmp
|
||||
# S3 Access Key ID;敏感信息请勿提交。
|
||||
export JUNHONG_STORAGE_S3_ACCESS_KEY_ID=598F558CF6FF46E79D1CFC607852378C9523
|
||||
# S3 Secret Access Key;敏感信息请勿提交。
|
||||
export JUNHONG_STORAGE_S3_SECRET_ACCESS_KEY=8393425DCB2F48F1914FF39DCBC6C7B17325
|
||||
# 是否使用 SSL;本地兼容服务常用 false。
|
||||
export JUNHONG_STORAGE_S3_USE_SSL=false
|
||||
# 是否使用路径风格;兼容 S3 服务通常使用 true。
|
||||
export JUNHONG_STORAGE_S3_PATH_STYLE=true
|
||||
# 预签名上传 URL 有效期;默认 15m。
|
||||
export JUNHONG_STORAGE_PRESIGN_UPLOAD_EXPIRES=15m
|
||||
# 预签名下载 URL 有效期;默认 24h。
|
||||
export JUNHONG_STORAGE_PRESIGN_DOWNLOAD_EXPIRES=24h
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 默认超级管理员配置(可选)
|
||||
# ----------------------------------------------------------------------------
|
||||
# 不配置时会使用 pkg/constants 中的默认超管账号;生产环境建议在首次启动前覆盖。
|
||||
# 默认超级管理员用户名;留空则使用代码内置默认值 admin。
|
||||
export JUNHONG_DEFAULT_ADMIN_USERNAME='admin'
|
||||
# 默认超级管理员密码;留空则使用代码内置默认值 Admin@123456。
|
||||
export JUNHONG_DEFAULT_ADMIN_PASSWORD='Admin@123456'
|
||||
# 默认超级管理员手机号;留空则使用代码内置默认值 13800000000。
|
||||
# JUNHONG_DEFAULT_ADMIN_PHONE=''
|
||||
|
||||
export JUNHONG_MIDDLEWARE_CORS_ENABLED=true
|
||||
export JUNHONG_MIDDLEWARE_CORS_ALLOW_ORIGINS=https://cmp-admin.xm-iot.cn,https://cmp-agent.xm-iot.cn,https://cmp-c.xm-iot.cn
|
||||
export JUNHONG_MIDDLEWARE_CORS_ALLOW_CREDENTIALS=true
|
||||
@@ -64,26 +64,13 @@ jobs:
|
||||
- name: 部署到本地(仅 main 分支)
|
||||
if: github.ref == 'refs/heads/main'
|
||||
run: |
|
||||
# 确保部署目录存在
|
||||
mkdir -p ${{ env.DEPLOY_DIR }}/{configs,logs}
|
||||
|
||||
# 调试:显示当前目录和文件
|
||||
echo "📍 当前工作目录: $(pwd)"
|
||||
echo "📁 当前目录内容:"
|
||||
ls -la
|
||||
# 确保部署目录存在(仅需日志目录,配置已嵌入二进制文件)
|
||||
mkdir -p ${{ env.DEPLOY_DIR }}/logs
|
||||
|
||||
# 强制更新 docker-compose.prod.yml(确保使用最新配置)
|
||||
echo "📋 更新部署配置文件..."
|
||||
cp -v docker-compose.prod.yml ${{ env.DEPLOY_DIR }}/
|
||||
|
||||
# configs 目录只在不存在时初始化(避免覆盖运行时配置)
|
||||
if [ ! -d ${{ env.DEPLOY_DIR }}/configs ] || [ -z "$(ls -A ${{ env.DEPLOY_DIR }}/configs 2>/dev/null)" ]; then
|
||||
echo "📋 初始化配置目录..."
|
||||
cp -rv configs/* ${{ env.DEPLOY_DIR }}/configs/
|
||||
else
|
||||
echo "✅ 配置目录已存在,保留现有配置"
|
||||
fi
|
||||
|
||||
cd ${{ env.DEPLOY_DIR }}
|
||||
|
||||
echo "📥 拉取最新镜像..."
|
||||
|
||||
40
.gitignore
vendored
40
.gitignore
vendored
@@ -26,8 +26,6 @@ vendor/
|
||||
.cache/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Log files
|
||||
@@ -73,3 +71,41 @@ cmd/api/api
|
||||
ai-gateway.conf
|
||||
__debug_bin1621385388
|
||||
docs/admin-openapi.yaml
|
||||
/api
|
||||
/gendocs
|
||||
/worker
|
||||
.opencode/skills/json-canvas
|
||||
.opencode/skills/obsidian-bases
|
||||
.gitignore
|
||||
.opencode/skills/obsidian-cli
|
||||
.opencode/skills/obsidian-markdown
|
||||
.opencode/skills/kb-sync
|
||||
.gitignore
|
||||
.opencode/skills/defuddle
|
||||
# CocoIndex Code (ccc)
|
||||
/.cocoindex_code/
|
||||
.omx/hud-config.json
|
||||
.omx/metrics.json
|
||||
.omx/setup-scope.json
|
||||
.omx/cache/codebase-map.json
|
||||
.omx/state/current-task-baseline.json
|
||||
.omx/state/notify-fallback-authority-owner.json
|
||||
.omx/state/notify-fallback-state.json
|
||||
.omx/state/notify-fallback.pid
|
||||
.omx/state/session.json
|
||||
.omx/state/subagent-tracking.json
|
||||
.omx/state/team-leader-nudge.json
|
||||
.omx/state/tmux-hook-state.json
|
||||
.omx/state/update-check.json
|
||||
.omx/state/sessions/omx-1777517355305-tzivrd/AGENTS.md
|
||||
.omx/state/sessions/omx-1777517355305-tzivrd/hud-state.json
|
||||
.omx/state/sessions/omx-1777517489966-oo3g37/AGENTS.md
|
||||
.omx/state/sessions/omx-1777517489966-oo3g37/hud-state.json
|
||||
.omx/state/sessions/omx-1777517489966-oo3g37/notify-hook-state.json
|
||||
.omx/state/tmux-extended-keys/private-tmp-tmux-501-default.json
|
||||
.aider*
|
||||
|
||||
# /teach skill 的个人学习工作区,不进入项目提交历史
|
||||
.claude/teach-workspace/
|
||||
scripts/batch_package_purchase/assets.example_购买结果_20260715_115804.csv
|
||||
scripts/batch_package_purchase/assets.example_购买结果_20260715_115814.csv
|
||||
|
||||
15
.mcp.json
Normal file
15
.mcp.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"postgres": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@bytebase/dbhub@latest",
|
||||
"--transport",
|
||||
"stdio",
|
||||
"--config",
|
||||
".config/dbhub.toml"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
149
.opencode/command/opsx-apply.md
Normal file
149
.opencode/command/opsx-apply.md
Normal file
@@ -0,0 +1,149 @@
|
||||
---
|
||||
description: Implement tasks from an OpenSpec change (Experimental)
|
||||
---
|
||||
|
||||
Implement tasks from an OpenSpec change.
|
||||
|
||||
**Input**: Optionally specify a change name (e.g., `/opsx-apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx-apply <other>`).
|
||||
|
||||
2. **Check status to understand the schema**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
||||
- Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
|
||||
|
||||
3. **Get apply instructions**
|
||||
|
||||
```bash
|
||||
openspec instructions apply --change "<name>" --json
|
||||
```
|
||||
|
||||
This returns:
|
||||
- Context file paths (varies by schema)
|
||||
- Progress (total, complete, remaining)
|
||||
- Task list with status
|
||||
- Dynamic instruction based on current state
|
||||
|
||||
**Handle states:**
|
||||
- If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx-continue`
|
||||
- If `state: "all_done"`: congratulate, suggest archive
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
4. **Read context files**
|
||||
|
||||
Read the files listed in `contextFiles` from the apply instructions output.
|
||||
The files depend on the schema being used:
|
||||
- **spec-driven**: proposal, specs, design, tasks
|
||||
- Other schemas: follow the contextFiles from CLI output
|
||||
|
||||
5. **Show current progress**
|
||||
|
||||
Display:
|
||||
- Schema being used
|
||||
- Progress: "N/M tasks complete"
|
||||
- Remaining tasks overview
|
||||
- Dynamic instruction from CLI
|
||||
|
||||
6. **Implement tasks (loop until done or blocked)**
|
||||
|
||||
For each pending task:
|
||||
- Show which task is being worked on
|
||||
- Make the code changes required
|
||||
- Keep changes minimal and focused
|
||||
- Mark task complete in the tasks file: `- [ ]` → `- [x]`
|
||||
- Continue to next task
|
||||
|
||||
**Pause if:**
|
||||
- Task is unclear → ask for clarification
|
||||
- Implementation reveals a design issue → suggest updating artifacts
|
||||
- Error or blocker encountered → report and wait for guidance
|
||||
- User interrupts
|
||||
|
||||
7. **On completion or pause, show status**
|
||||
|
||||
Display:
|
||||
- Tasks completed this session
|
||||
- Overall progress: "N/M tasks complete"
|
||||
- If all done: suggest archive
|
||||
- If paused: explain why and wait for guidance
|
||||
|
||||
**Output During Implementation**
|
||||
|
||||
```
|
||||
## Implementing: <change-name> (schema: <schema-name>)
|
||||
|
||||
Working on task 3/7: <task description>
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
|
||||
Working on task 4/7: <task description>
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
```
|
||||
|
||||
**Output On Completion**
|
||||
|
||||
```
|
||||
## Implementation Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Progress:** 7/7 tasks complete ✓
|
||||
|
||||
### Completed This Session
|
||||
- [x] Task 1
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tasks complete! You can archive this change with `/opsx-archive`.
|
||||
```
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
|
||||
```
|
||||
## Implementation Paused
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Progress:** 4/7 tasks complete
|
||||
|
||||
### Issue Encountered
|
||||
<description of the issue>
|
||||
|
||||
**Options:**
|
||||
1. <option 1>
|
||||
2. <option 2>
|
||||
3. Other approach
|
||||
|
||||
What would you like to do?
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Keep going through tasks until done or blocked
|
||||
- Always read context files before starting (from the apply instructions output)
|
||||
- If task is ambiguous, pause and ask before implementing
|
||||
- If implementation reveals issues, pause and suggest artifact updates
|
||||
- Keep code changes minimal and scoped to each task
|
||||
- Update task checkbox immediately after completing each task
|
||||
- Pause on errors, blockers, or unclear requirements - don't guess
|
||||
- Use contextFiles from CLI output, don't assume specific file names
|
||||
|
||||
**Fluid Workflow Integration**
|
||||
|
||||
This skill supports the "actions on a change" model:
|
||||
|
||||
- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions
|
||||
- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly
|
||||
154
.opencode/command/opsx-archive.md
Normal file
154
.opencode/command/opsx-archive.md
Normal file
@@ -0,0 +1,154 @@
|
||||
---
|
||||
description: Archive a completed change in the experimental workflow
|
||||
---
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
**Input**: Optionally specify a change name after `/opsx-archive` (e.g., `/opsx-archive add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
|
||||
Show only active changes (not already archived).
|
||||
Include the schema used for each change if available.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Check artifact completion status**
|
||||
|
||||
Run `openspec status --change "<name>" --json` to check artifact completion.
|
||||
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used
|
||||
- `artifacts`: List of artifacts with their status (`done` or other)
|
||||
|
||||
**If any artifacts are not `done`:**
|
||||
- Display warning listing incomplete artifacts
|
||||
- Prompt user for confirmation to continue
|
||||
- Proceed if user confirms
|
||||
|
||||
3. **Check task completion status**
|
||||
|
||||
Read the tasks file (typically `tasks.md`) to check for incomplete tasks.
|
||||
|
||||
Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).
|
||||
|
||||
**If incomplete tasks found:**
|
||||
- Display warning showing count of incomplete tasks
|
||||
- Prompt user for confirmation to continue
|
||||
- Proceed if user confirms
|
||||
|
||||
**If no tasks file exists:** Proceed without task-related warning.
|
||||
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
Check for delta specs at `openspec/changes/<name>/specs/`. If none exist, proceed without sync prompt.
|
||||
|
||||
**If delta specs exist:**
|
||||
- Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`
|
||||
- Determine what changes would be applied (adds, modifications, removals, renames)
|
||||
- Show a combined summary before prompting
|
||||
|
||||
**Prompt options:**
|
||||
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
||||
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
||||
|
||||
If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>"). Proceed to archive regardless of choice.
|
||||
|
||||
5. **Perform the archive**
|
||||
|
||||
Create the archive directory if it doesn't exist:
|
||||
```bash
|
||||
mkdir -p openspec/changes/archive
|
||||
```
|
||||
|
||||
Generate target name using current date: `YYYY-MM-DD-<change-name>`
|
||||
|
||||
**Check if target already exists:**
|
||||
- If yes: Fail with error, suggest renaming existing archive or using different date
|
||||
- If no: Move the change directory to archive
|
||||
|
||||
```bash
|
||||
mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
|
||||
```
|
||||
|
||||
6. **Display summary**
|
||||
|
||||
Show archive completion summary including:
|
||||
- Change name
|
||||
- Schema that was used
|
||||
- Archive location
|
||||
- Spec sync status (synced / sync skipped / no delta specs)
|
||||
- Note about any warnings (incomplete artifacts/tasks)
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** ✓ Synced to main specs
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
```
|
||||
|
||||
**Output On Success (No Delta Specs)**
|
||||
|
||||
```
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** No delta specs
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
```
|
||||
|
||||
**Output On Success With Warnings**
|
||||
|
||||
```
|
||||
## Archive Complete (with warnings)
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** Sync skipped (user chose to skip)
|
||||
|
||||
**Warnings:**
|
||||
- Archived with 2 incomplete artifacts
|
||||
- Archived with 3 incomplete tasks
|
||||
- Delta spec sync was skipped (user chose to skip)
|
||||
|
||||
Review the archive if this was not intentional.
|
||||
```
|
||||
|
||||
**Output On Error (Archive Exists)**
|
||||
|
||||
```
|
||||
## Archive Failed
|
||||
|
||||
**Change:** <change-name>
|
||||
**Target:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
|
||||
Target archive directory already exists.
|
||||
|
||||
**Options:**
|
||||
1. Rename the existing archive
|
||||
2. Delete the existing archive if it's a duplicate
|
||||
3. Wait until a different date to archive
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Always prompt for change selection if not provided
|
||||
- Use artifact graph (openspec status --json) for completion checking
|
||||
- Don't block archive on warnings - just inform and confirm
|
||||
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
||||
- Show clear summary of what happened
|
||||
- If sync is requested, use the Skill tool to invoke `openspec-sync-specs` (agent-driven)
|
||||
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
||||
170
.opencode/command/opsx-explore.md
Normal file
170
.opencode/command/opsx-explore.md
Normal file
@@ -0,0 +1,170 @@
|
||||
---
|
||||
description: Enter explore mode - think through ideas, investigate problems, clarify requirements
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
|
||||
|
||||
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
||||
|
||||
**Input**: The argument after `/opsx-explore` is whatever the user wants to think about. Could be:
|
||||
- A vague idea: "real-time collaboration"
|
||||
- A specific problem: "the auth system is getting unwieldy"
|
||||
- A change name: "add-dark-mode" (to explore in context of that change)
|
||||
- A comparison: "postgres vs sqlite for this"
|
||||
- Nothing (just enter explore mode)
|
||||
|
||||
---
|
||||
|
||||
## The Stance
|
||||
|
||||
- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script
|
||||
- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.
|
||||
- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking
|
||||
- **Adaptive** - Follow interesting threads, pivot when new information emerges
|
||||
- **Patient** - Don't rush to conclusions, let the shape of the problem emerge
|
||||
- **Grounded** - Explore the actual codebase when relevant, don't just theorize
|
||||
|
||||
---
|
||||
|
||||
## What You Might Do
|
||||
|
||||
Depending on what the user brings, you might:
|
||||
|
||||
**Explore the problem space**
|
||||
- Ask clarifying questions that emerge from what they said
|
||||
- Challenge assumptions
|
||||
- Reframe the problem
|
||||
- Find analogies
|
||||
|
||||
**Investigate the codebase**
|
||||
- Map existing architecture relevant to the discussion
|
||||
- Find integration points
|
||||
- Identify patterns already in use
|
||||
- Surface hidden complexity
|
||||
|
||||
**Compare options**
|
||||
- Brainstorm multiple approaches
|
||||
- Build comparison tables
|
||||
- Sketch tradeoffs
|
||||
- Recommend a path (if asked)
|
||||
|
||||
**Visualize**
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Use ASCII diagrams liberally │
|
||||
├─────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────┐ ┌────────┐ │
|
||||
│ │ State │────────▶│ State │ │
|
||||
│ │ A │ │ B │ │
|
||||
│ └────────┘ └────────┘ │
|
||||
│ │
|
||||
│ System diagrams, state machines, │
|
||||
│ data flows, architecture sketches, │
|
||||
│ dependency graphs, comparison tables │
|
||||
│ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Surface risks and unknowns**
|
||||
- Identify what could go wrong
|
||||
- Find gaps in understanding
|
||||
- Suggest spikes or investigations
|
||||
|
||||
---
|
||||
|
||||
## OpenSpec Awareness
|
||||
|
||||
You have full context of the OpenSpec system. Use it naturally, don't force it.
|
||||
|
||||
### Check for context
|
||||
|
||||
At the start, quickly check what exists:
|
||||
```bash
|
||||
openspec list --json
|
||||
```
|
||||
|
||||
This tells you:
|
||||
- If there are active changes
|
||||
- Their names, schemas, and status
|
||||
- What the user might be working on
|
||||
|
||||
If the user mentioned a specific change name, read its artifacts for context.
|
||||
|
||||
### When no change exists
|
||||
|
||||
Think freely. When insights crystallize, you might offer:
|
||||
|
||||
- "This feels solid enough to start a change. Want me to create a proposal?"
|
||||
- Or keep exploring - no pressure to formalize
|
||||
|
||||
### When a change exists
|
||||
|
||||
If the user mentions a change or you detect one is relevant:
|
||||
|
||||
1. **Read existing artifacts for context**
|
||||
- `openspec/changes/<name>/proposal.md`
|
||||
- `openspec/changes/<name>/design.md`
|
||||
- `openspec/changes/<name>/tasks.md`
|
||||
- etc.
|
||||
|
||||
2. **Reference them naturally in conversation**
|
||||
- "Your design mentions using Redis, but we just realized SQLite fits better..."
|
||||
- "The proposal scopes this to premium users, but we're now thinking everyone..."
|
||||
|
||||
3. **Offer to capture when decisions are made**
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|--------------|------------------|
|
||||
| New requirement discovered | `specs/<capability>/spec.md` |
|
||||
| Requirement changed | `specs/<capability>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
- "This is a new requirement. Add it to specs?"
|
||||
- "This changes scope. Update the proposal?"
|
||||
|
||||
4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.
|
||||
|
||||
---
|
||||
|
||||
## What You Don't Have To Do
|
||||
|
||||
- Follow a script
|
||||
- Ask the same questions every time
|
||||
- Produce a specific artifact
|
||||
- Reach a conclusion
|
||||
- Stay on topic if a tangent is valuable
|
||||
- Be brief (this is thinking time)
|
||||
|
||||
---
|
||||
|
||||
## Ending Discovery
|
||||
|
||||
There's no required ending. Discovery might:
|
||||
|
||||
- **Flow into a proposal**: "Ready to start? I can create a change proposal."
|
||||
- **Result in artifact updates**: "Updated design.md with these decisions"
|
||||
- **Just provide clarity**: User has what they need, moves on
|
||||
- **Continue later**: "We can pick this up anytime"
|
||||
|
||||
When things crystallize, you might offer a summary - but it's optional. Sometimes the thinking IS the value.
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.
|
||||
- **Don't fake understanding** - If something is unclear, dig deeper
|
||||
- **Don't rush** - Discovery is thinking time, not task time
|
||||
- **Don't force structure** - Let patterns emerge naturally
|
||||
- **Don't auto-capture** - Offer to save insights, don't just do it
|
||||
- **Do visualize** - A good diagram is worth many paragraphs
|
||||
- **Do explore the codebase** - Ground discussions in reality
|
||||
- **Do question assumptions** - Including the user's and your own
|
||||
103
.opencode/command/opsx-propose.md
Normal file
103
.opencode/command/opsx-propose.md
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
description: Propose a new change - create it and generate all artifacts in one step
|
||||
---
|
||||
|
||||
Propose a new change - create the change and generate all artifacts in one step.
|
||||
|
||||
I'll create a change with artifacts:
|
||||
- proposal.md (what & why)
|
||||
- design.md (how)
|
||||
- tasks.md (implementation steps)
|
||||
|
||||
When ready to implement, run /opsx-apply
|
||||
|
||||
---
|
||||
|
||||
**Input**: The argument after `/opsx-propose` is the change name (kebab-case), OR a description of what the user wants to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no input provided, ask what they want to build**
|
||||
|
||||
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
2. **Create the change directory**
|
||||
```bash
|
||||
openspec new change "<name>"
|
||||
```
|
||||
This creates a scaffolded change at `openspec/changes/<name>/` with `.openspec.yaml`.
|
||||
|
||||
3. **Get the artifact build order**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to get:
|
||||
- `applyRequires`: array of artifact IDs needed before implementation (e.g., `["tasks"]`)
|
||||
- `artifacts`: list of all artifacts with their status and dependencies
|
||||
|
||||
4. **Create artifacts in sequence until apply-ready**
|
||||
|
||||
Use the **TodoWrite tool** to track progress through the artifacts.
|
||||
|
||||
Loop through artifacts in dependency order (artifacts with no pending dependencies first):
|
||||
|
||||
a. **For each artifact that is `ready` (dependencies satisfied)**:
|
||||
- Get instructions:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- The instructions JSON includes:
|
||||
- `context`: Project background (constraints for you - do NOT include in output)
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance for this artifact type
|
||||
- `outputPath`: Where to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- Read any completed dependency files for context
|
||||
- Create the artifact file using `template` as the structure
|
||||
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
||||
- Show brief progress: "Created <artifact-id>"
|
||||
|
||||
b. **Continue until all `applyRequires` artifacts are complete**
|
||||
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
||||
- Check if every artifact ID in `applyRequires` has `status: "done"` in the artifacts array
|
||||
- Stop when all `applyRequires` artifacts are done
|
||||
|
||||
c. **If an artifact requires user input** (unclear context):
|
||||
- Use **AskUserQuestion tool** to clarify
|
||||
- Then continue with creation
|
||||
|
||||
5. **Show final status**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
|
||||
**Output**
|
||||
|
||||
After completing all artifacts, summarize:
|
||||
- Change name and location
|
||||
- List of artifacts created with brief descriptions
|
||||
- What's ready: "All artifacts created! Ready for implementation."
|
||||
- Prompt: "Run `/opsx-apply` to start implementing."
|
||||
|
||||
**Artifact Creation Guidelines**
|
||||
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type
|
||||
- The schema defines what each artifact should contain - follow it
|
||||
- Read dependency artifacts for context before creating new ones
|
||||
- Use `template` as the structure for your output file - fill in its sections
|
||||
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
||||
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
||||
- These guide what you write, but should never appear in the output
|
||||
|
||||
**Guardrails**
|
||||
- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)
|
||||
- Always read dependency artifacts before creating a new one
|
||||
- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum
|
||||
- If a change with that name already exists, ask if user wants to continue it or create a new one
|
||||
- Verify each artifact file exists after writing before proceeding to next
|
||||
115
.opencode/package-lock.json
generated
Normal file
115
.opencode/package-lock.json
generated
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"name": ".opencode",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@opencode-ai/plugin": "1.3.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@opencode-ai/plugin": {
|
||||
"version": "1.3.17",
|
||||
"resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.3.17.tgz",
|
||||
"integrity": "sha512-N5lckFtYvEu2R8K1um//MIOTHsJHniF2kHoPIWPCrxKG5Jpismt1ISGzIiU3aKI2ht/9VgcqKPC5oZFLdmpxPw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "1.3.17",
|
||||
"zod": "4.1.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentui/core": ">=0.1.96",
|
||||
"@opentui/solid": ">=0.1.96"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@opentui/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@opentui/solid": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@opencode-ai/sdk": {
|
||||
"version": "1.3.17",
|
||||
"resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.3.17.tgz",
|
||||
"integrity": "sha512-2+MGgu7wynqTBwxezR01VAGhILXlpcHDY/pF7SWB87WOgLt3kD55HjKHNj6PWxyY8n575AZolR95VUC3gtwfmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cross-spawn": "7.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/node-which"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.1.8",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
139
.opencode/skills/comment-standards/SKILL.md
Normal file
139
.opencode/skills/comment-standards/SKILL.md
Normal file
@@ -0,0 +1,139 @@
|
||||
---
|
||||
name: comment-standards
|
||||
description: Go 注释规范。编写 Go 代码注释、文档注释时使用。包含包注释、结构体注释、接口注释、函数注释、内联注释的完整规范与示例。
|
||||
---
|
||||
|
||||
# Go 注释规范
|
||||
|
||||
**基本原则**:
|
||||
- **所有注释使用中文**
|
||||
- **导出符号必须有文档注释**(包、函数、方法、类型、接口、常量、变量)
|
||||
- **复杂逻辑必须有实现注释**(解释"为什么",而不是"做了什么")
|
||||
- **禁止废话注释**(不要用注释复述代码本身)
|
||||
- **修改代码时必须同步更新注释**
|
||||
|
||||
---
|
||||
|
||||
## 包注释
|
||||
|
||||
每个包的入口文件(通常是主文件或 `doc.go`)必须有包注释:
|
||||
|
||||
```go
|
||||
// Package account 提供账号管理的业务逻辑服务
|
||||
// 包含账号创建、修改、删除、权限分配等功能
|
||||
package account
|
||||
```
|
||||
|
||||
## 结构体注释
|
||||
|
||||
所有导出结构体必须有文档注释,说明该结构体代表什么:
|
||||
|
||||
```go
|
||||
// Service 账号业务服务
|
||||
// 负责账号的 CRUD、角色分配、密码管理等业务逻辑
|
||||
type Service struct {
|
||||
store *Store
|
||||
auditService AuditServiceInterface
|
||||
}
|
||||
```
|
||||
|
||||
## 接口注释
|
||||
|
||||
导出接口必须注释接口用途,每个方法必须说明契约:
|
||||
|
||||
```go
|
||||
// PermissionChecker 权限检查器接口
|
||||
// 用于查询用户的权限列表
|
||||
type PermissionChecker interface {
|
||||
// CheckPermission 检查用户是否拥有指定权限
|
||||
// userID: 用户ID
|
||||
// permCode: 权限编码(格式: module:action)
|
||||
// platform: 端口类型 (all/web/h5)
|
||||
CheckPermission(ctx context.Context, userID uint, permCode string, platform string) (bool, error)
|
||||
}
|
||||
```
|
||||
|
||||
## 函数和方法注释
|
||||
|
||||
**导出函数/方法**必须以函数名开头,说明功能:
|
||||
|
||||
```go
|
||||
// Create 创建账号
|
||||
// POST /api/admin/accounts
|
||||
func (h *AccountHandler) Create(c *fiber.Ctx) error {
|
||||
```
|
||||
|
||||
**复杂方法**(超过 30 行或包含复杂业务逻辑)必须额外说明实现思路:
|
||||
|
||||
```go
|
||||
// ActivateByRealname 首次实名激活套餐
|
||||
// 当用户完成实名认证后,自动激活处于"囤货待实名"状态的套餐:
|
||||
// 1. 查找该卡所有 status=3(待实名激活)的套餐
|
||||
// 2. 按创建时间排序,第一个主套餐立即激活(status=1)
|
||||
// 3. 其余主套餐进入排队状态(status=4)
|
||||
// 4. 加油包如果绑定了已激活的主套餐则一并激活
|
||||
func (s *UsageService) ActivateByRealname(ctx context.Context, cardID uint) error {
|
||||
```
|
||||
|
||||
**未导出函数/方法**:
|
||||
- 简单逻辑(< 15 行):可以不加注释
|
||||
- 复杂逻辑(≥ 15 行)或非显而易见的算法:必须加注释
|
||||
|
||||
```go
|
||||
// buildPermissionTree 递归构建权限树
|
||||
// 采用 map 索引 + 单次遍历算法,时间复杂度 O(n)
|
||||
func (s *Service) buildPermissionTree(permissions []*model.Permission) []*dto.PermissionTreeNode {
|
||||
```
|
||||
|
||||
## 常量和枚举注释
|
||||
|
||||
分组常量必须有组注释,每个值必须有行内注释:
|
||||
|
||||
```go
|
||||
// 用户类型常量
|
||||
const (
|
||||
UserTypeSuperAdmin = 1 // 超级管理员
|
||||
UserTypePlatform = 2 // 平台用户
|
||||
UserTypeAgent = 3 // 代理账号
|
||||
UserTypeEnterprise = 4 // 企业账号
|
||||
)
|
||||
```
|
||||
|
||||
## 内联注释规范
|
||||
|
||||
**必须添加内联注释的场景**:
|
||||
|
||||
| 场景 | 要求 |
|
||||
|------|------|
|
||||
| 复杂条件判断 | 解释判断的业务含义 |
|
||||
| 多步骤业务流程 | 用编号注释标明每一步 |
|
||||
| 非显而易见的设计决策 | 解释"为什么这样做"而不是"做了什么" |
|
||||
| 缓存/事务/并发处理 | 说明策略和原因 |
|
||||
| 临时方案/兼容逻辑 | 标注 TODO 或说明背景 |
|
||||
|
||||
**✅ 好的内联注释(解释为什么)**:
|
||||
|
||||
```go
|
||||
// 使用 Redis 分布式锁防止并发重复创建,锁超时 10 秒
|
||||
if !s.acquireLock(ctx, lockKey, 10*time.Second) {
|
||||
return errors.New(errors.CodeTooManyRequests, "操作过于频繁,请稍后重试")
|
||||
}
|
||||
|
||||
// 先冻结佣金再扣款,保证资金安全(失败时佣金自动解冻)
|
||||
if err := s.freezeCommission(ctx, tx, orderID); err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
**❌ 废话注释(禁止)**:
|
||||
|
||||
```go
|
||||
// 获取用户ID ← 禁止:代码本身已经很清楚
|
||||
userID := middleware.GetUserIDFromContext(ctx)
|
||||
|
||||
// 创建账号 ← 禁止:变量名已说明意图
|
||||
account := &model.Account{}
|
||||
|
||||
// 返回错误 ← 禁止:return err 不需要注释
|
||||
return err
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user