@stacksjs/defaults 0.72.103 → 0.73.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/ai/AGENTS.md +25 -4
  2. package/ai/README.md +26 -4
  3. package/ai/skills/stacks-actions/SKILL.md +1 -1
  4. package/ai/skills/stacks-ai/SKILL.md +1 -1
  5. package/ai/skills/stacks-alias/SKILL.md +1 -1
  6. package/ai/skills/stacks-analytics/SKILL.md +1 -1
  7. package/ai/skills/stacks-api/SKILL.md +1 -1
  8. package/ai/skills/stacks-arrays/SKILL.md +1 -1
  9. package/ai/skills/stacks-auto-imports/SKILL.md +1 -1
  10. package/ai/skills/stacks-browse/SKILL.md +1 -1
  11. package/ai/skills/stacks-browser/SKILL.md +1 -1
  12. package/ai/skills/stacks-buddy/SKILL.md +1 -1
  13. package/ai/skills/stacks-build/SKILL.md +79 -5
  14. package/ai/skills/stacks-cache/SKILL.md +1 -1
  15. package/ai/skills/stacks-calendar/SKILL.md +1 -1
  16. package/ai/skills/stacks-chat/SKILL.md +1 -1
  17. package/ai/skills/stacks-cli/SKILL.md +1 -1
  18. package/ai/skills/stacks-cloud/SKILL.md +1 -1
  19. package/ai/skills/stacks-cms/SKILL.md +1 -1
  20. package/ai/skills/stacks-codebase-design/DEEPENING.md +79 -0
  21. package/ai/skills/stacks-codebase-design/DESIGN-IT-TWICE.md +72 -0
  22. package/ai/skills/stacks-codebase-design/SKILL.md +180 -0
  23. package/ai/skills/stacks-collections/SKILL.md +1 -1
  24. package/ai/skills/stacks-commerce/SKILL.md +141 -35
  25. package/ai/skills/stacks-composables/SKILL.md +1 -1
  26. package/ai/skills/stacks-config/SKILL.md +1 -1
  27. package/ai/skills/stacks-configuration/SKILL.md +1 -1
  28. package/ai/skills/stacks-cron/SKILL.md +1 -1
  29. package/ai/skills/stacks-crosswind/SKILL.md +1 -1
  30. package/ai/skills/stacks-database/SKILL.md +1 -1
  31. package/ai/skills/stacks-datetime/SKILL.md +1 -1
  32. package/ai/skills/stacks-dependencies/SKILL.md +1 -1
  33. package/ai/skills/stacks-deploy/SKILL.md +1 -1
  34. package/ai/skills/stacks-desktop/SKILL.md +1 -1
  35. package/ai/skills/stacks-development/SKILL.md +1 -1
  36. package/ai/skills/stacks-dns/SKILL.md +1 -1
  37. package/ai/skills/stacks-docs/SKILL.md +1 -1
  38. package/ai/skills/stacks-domain-modeling/FORMATS.md +123 -0
  39. package/ai/skills/stacks-domain-modeling/SKILL.md +109 -0
  40. package/ai/skills/stacks-enums/SKILL.md +1 -1
  41. package/ai/skills/stacks-error-handling/SKILL.md +1 -1
  42. package/ai/skills/stacks-events/SKILL.md +1 -1
  43. package/ai/skills/stacks-faker/SKILL.md +1 -1
  44. package/ai/skills/stacks-flow/PHASE-BOUNDARIES.md +91 -0
  45. package/ai/skills/stacks-flow/SKILL.md +117 -0
  46. package/ai/skills/stacks-git/SKILL.md +37 -9
  47. package/ai/skills/stacks-grilling/SKILL.md +85 -0
  48. package/ai/skills/stacks-guard/SKILL.md +86 -11
  49. package/ai/skills/stacks-guard/scripts/block-destructive.sh +67 -0
  50. package/ai/skills/stacks-handoff/SKILL.md +70 -0
  51. package/ai/skills/stacks-health/SKILL.md +1 -1
  52. package/ai/skills/stacks-http/SKILL.md +1 -1
  53. package/ai/skills/stacks-i18n/SKILL.md +1 -1
  54. package/ai/skills/stacks-investigate/SKILL.md +234 -106
  55. package/ai/skills/stacks-investigate/scripts/hitl-loop.template.sh +47 -0
  56. package/ai/skills/stacks-jobs/SKILL.md +1 -1
  57. package/ai/skills/stacks-listeners/SKILL.md +1 -1
  58. package/ai/skills/stacks-logging/SKILL.md +1 -1
  59. package/ai/skills/stacks-mail/SKILL.md +1 -1
  60. package/ai/skills/stacks-middleware/SKILL.md +1 -1
  61. package/ai/skills/stacks-migrations/SKILL.md +1 -1
  62. package/ai/skills/stacks-models/SKILL.md +1 -1
  63. package/ai/skills/stacks-new-feature/SKILL.md +65 -5
  64. package/ai/skills/stacks-notifications/SKILL.md +1 -1
  65. package/ai/skills/stacks-objects/SKILL.md +1 -1
  66. package/ai/skills/stacks-office-hours/SKILL.md +16 -2
  67. package/ai/skills/stacks-orm/SKILL.md +1 -1
  68. package/ai/skills/stacks-path/SKILL.md +1 -1
  69. package/ai/skills/stacks-payments/SKILL.md +35 -1
  70. package/ai/skills/stacks-plan-review/SKILL.md +27 -6
  71. package/ai/skills/stacks-plugins/SKILL.md +1 -1
  72. package/ai/skills/stacks-prototype/LOGIC.md +103 -0
  73. package/ai/skills/stacks-prototype/SKILL.md +66 -0
  74. package/ai/skills/stacks-prototype/UI.md +112 -0
  75. package/ai/skills/stacks-push/SKILL.md +1 -1
  76. package/ai/skills/stacks-query-builder/SKILL.md +1 -1
  77. package/ai/skills/stacks-queue/SKILL.md +1 -1
  78. package/ai/skills/stacks-realtime/SKILL.md +1 -1
  79. package/ai/skills/stacks-registry/SKILL.md +1 -1
  80. package/ai/skills/stacks-repl/SKILL.md +1 -1
  81. package/ai/skills/stacks-retro/SKILL.md +121 -75
  82. package/ai/skills/stacks-review/SKILL.md +182 -74
  83. package/ai/skills/stacks-router/SKILL.md +1 -1
  84. package/ai/skills/stacks-routes/SKILL.md +1 -1
  85. package/ai/skills/stacks-scaffolding/SKILL.md +1 -1
  86. package/ai/skills/stacks-scheduler/SKILL.md +1 -1
  87. package/ai/skills/stacks-search-engine/SKILL.md +1 -1
  88. package/ai/skills/stacks-security/SKILL.md +1 -1
  89. package/ai/skills/stacks-security-audit/SKILL.md +1 -1
  90. package/ai/skills/stacks-server/SKILL.md +1 -1
  91. package/ai/skills/stacks-shell/SKILL.md +1 -1
  92. package/ai/skills/stacks-slug/SKILL.md +1 -1
  93. package/ai/skills/stacks-sms/SKILL.md +1 -1
  94. package/ai/skills/stacks-socials/SKILL.md +1 -1
  95. package/ai/skills/stacks-storage/SKILL.md +1 -1
  96. package/ai/skills/stacks-strings/SKILL.md +1 -1
  97. package/ai/skills/stacks-stx/SKILL.md +1 -1
  98. package/ai/skills/stacks-tdd/EXAMPLES.md +136 -0
  99. package/ai/skills/stacks-tdd/SKILL.md +125 -0
  100. package/ai/skills/stacks-testing/SKILL.md +13 -3
  101. package/ai/skills/stacks-tunnel/SKILL.md +1 -1
  102. package/ai/skills/stacks-types/SKILL.md +1 -1
  103. package/ai/skills/stacks-ui/SKILL.md +1 -1
  104. package/ai/skills/stacks-utils/SKILL.md +1 -1
  105. package/ai/skills/stacks-validation/SKILL.md +1 -1
  106. package/ai/skills/stacks-whois/SKILL.md +1 -1
  107. package/ai/skills/stacks-wizard/SKILL.md +127 -0
  108. package/ai/skills/stacks-wizard/scripts/template.sh +208 -0
  109. package/ai/skills/stacks-writing-for-agents/MECHANICS.md +125 -0
  110. package/ai/skills/stacks-writing-for-agents/SKILL.md +218 -0
  111. package/app/Actions/Auth/GenerateTwoFactorSecretAction.ts +12 -2
  112. package/app/Actions/Commerce/Shipping/{DriverDestroyAction.ts → CourierDestroyAction.ts} +6 -6
  113. package/app/Actions/Commerce/Shipping/{DriverIndexAction.ts → CourierIndexAction.ts} +3 -3
  114. package/app/Actions/Commerce/Shipping/CourierPingStoreAction.ts +61 -0
  115. package/app/Actions/Commerce/Shipping/{DriverShowAction.ts → CourierShowAction.ts} +5 -5
  116. package/app/Actions/Commerce/Shipping/{DriverStoreAction.ts → CourierStoreAction.ts} +4 -4
  117. package/app/Actions/Commerce/Shipping/{DriverUpdateAction.ts → CourierUpdateAction.ts} +6 -6
  118. package/app/Actions/Commerce/Shipping/DeliveryRouteStartAction.ts +38 -0
  119. package/app/Actions/Commerce/Shipping/DeliveryStopCompleteAction.ts +40 -0
  120. package/app/Actions/Commerce/Shipping/DeliveryStopFailAction.ts +44 -0
  121. package/app/Actions/Commerce/Shipping/DeliveryStopStartAction.ts +39 -0
  122. package/app/Actions/Commerce/Shipping/courier-session.ts +75 -0
  123. package/app/Actions/Commerce/commerce-action.test.ts +5 -5
  124. package/app/Actions/Dashboard/Commerce/CommerceDeliveryAction.ts +5 -5
  125. package/app/Actions/Dashboard/Commerce/CourierIndexAction.ts +24 -0
  126. package/app/Actions/Dashboard/Commerce/DeliveryRouteIndexAction.ts +7 -7
  127. package/app/Actions/Dashboard/Commerce/commerce-delivery.test.ts +11 -11
  128. package/app/Actions/Dashboard/Commerce/commerce-delivery.ts +29 -29
  129. package/app/Actions/Dashboard/Commerce/{driver-records.test.ts → courier-records.test.ts} +9 -9
  130. package/app/Actions/Dashboard/Commerce/{driver-records.ts → courier-records.ts} +14 -14
  131. package/app/Actions/Dashboard/Commerce/delivery-route-records.test.ts +13 -13
  132. package/app/Actions/Dashboard/Commerce/delivery-route-records.ts +25 -25
  133. package/app/Models/User.ts +1 -1
  134. package/app/Models/commerce/{Driver.ts → Courier.ts} +7 -7
  135. package/app/Models/commerce/{DriverPing.ts → CourierPing.ts} +7 -7
  136. package/app/Models/commerce/DeliveryRoute.ts +6 -6
  137. package/app/Models/commerce/DeliveryStop.ts +31 -8
  138. package/bootstrap.ts +7 -0
  139. package/functions/commerce/shippings/couriers.ts +19 -0
  140. package/ide/vscode/package.json +1 -1
  141. package/package.json +4 -3
  142. package/resources/components/Dashboard/Commerce/Delivery/{DriverDeleteDialog.stx → CourierDeleteDialog.stx} +5 -5
  143. package/resources/components/Dashboard/Commerce/Delivery/{DriverDialog.stx → CourierDialog.stx} +10 -10
  144. package/resources/components/Dashboard/Commerce/Delivery/{DriversDashboard.stx → CouriersDashboard.stx} +43 -43
  145. package/resources/components/Dashboard/Commerce/Delivery/{DriversTable.stx → CouriersTable.stx} +21 -21
  146. package/resources/components/Dashboard/Commerce/Delivery/DeliveryOverviewDashboard.stx +18 -18
  147. package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDeleteDialog.stx +2 -2
  148. package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDialog.stx +22 -22
  149. package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesDashboard.stx +24 -24
  150. package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesTable.stx +8 -8
  151. package/resources/components/Dashboard/Commerce/Delivery/TabNavigation.stx +1 -1
  152. package/resources/functions/dashboard/data.ts +1 -1
  153. package/resources/functions/dashboard/sidebar.ts +2 -2
  154. package/routes/dashboard-api.ts +6 -6
  155. package/routes/dashboard.ts +7 -7
  156. package/routes/delivery.ts +24 -0
  157. package/types/defaults.ts +3 -3
  158. package/views/dashboard/.discovered-models.json +18 -18
  159. package/views/dashboard/AUDIT.md +1 -1
  160. package/views/dashboard/commerce/delivery/{drivers.stx → couriers.stx} +2 -2
  161. package/views/dashboard/composables/useChart.ts +16 -2
  162. package/app/Actions/Dashboard/Commerce/DriverIndexAction.ts +0 -24
  163. package/functions/commerce/shippings/drivers.ts +0 -19
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # A wizard walks a human through a manual procedure, step by step.
4
+ # Generated by the stacks-wizard skill.
5
+ #
6
+ # Library adapted from Matt Pocock's `wizard` skill (MIT),
7
+ # https://github.com/mattpocock/skills
8
+ #
9
+ # Everything above the "STAGES" marker is the wizard library: do not hand-edit
10
+ # it. Author the per-step stages below the marker.
11
+
12
+ set -euo pipefail
13
+
14
+ # ──────────────────────────────────────────────────────────────────────────
15
+ # Wizard library: delightful, consistent UX, identical across every wizard.
16
+ # ──────────────────────────────────────────────────────────────────────────
17
+
18
+ if [[ -t 1 ]] && command -v tput >/dev/null 2>&1 && [[ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]]; then
19
+ BOLD=$(tput bold); DIM=$(tput dim); RESET=$(tput sgr0)
20
+ BLUE=$(tput setaf 4); GREEN=$(tput setaf 2); YELLOW=$(tput setaf 3); RED=$(tput setaf 1)
21
+ else
22
+ BOLD=""; DIM=""; RESET=""; BLUE=""; GREEN=""; YELLOW=""; RED=""
23
+ fi
24
+
25
+ # Author sets this at the top of the stages section.
26
+ TOTAL_STAGES=0
27
+
28
+ _STAGE_INDEX=0
29
+ ENV_FILE="${ENV_FILE:-.env}"
30
+ WRITTEN_ENV=() # KEYs written to ENV_FILE this run
31
+ WRITTEN_SECRET=() # secret NAMEs set this run
32
+ SKIPPED=() # things we couldn't do (e.g. gh missing)
33
+
34
+ # _clear wipes the terminal so only the current step is on screen. No-op when
35
+ # output isn't a terminal, so piped logs stay readable.
36
+ _clear() {
37
+ [[ -t 1 ]] || return 0
38
+ if command -v tput >/dev/null 2>&1; then tput clear; else printf '\033[2J\033[3J\033[H'; fi
39
+ }
40
+
41
+ # banner "Title" shows the opening frame: what this wizard does.
42
+ banner() {
43
+ _clear
44
+ printf '\n%s%s %s%s\n' "$BOLD" "$BLUE" "$1" "$RESET"
45
+ printf '%s %s stages%s\n\n' "$DIM" "$TOTAL_STAGES" "$RESET"
46
+ printf '%s You drive the browser; this wizard tells you exactly what to do and\n' "$DIM"
47
+ printf ' captures the values you copy back. Stop any time with Ctrl-C and re-run\n'
48
+ printf ' later, since it remembers values already saved.%s\n' "$RESET"
49
+ pause "Ready to start?"
50
+ }
51
+
52
+ # stage "Name" clears the screen, then announces a stage and shows progress.
53
+ # Clearing keeps only the current step on screen.
54
+ stage() {
55
+ _clear
56
+ _STAGE_INDEX=$((_STAGE_INDEX + 1))
57
+ printf '\n%s%s▸ Stage %s/%s · %s%s\n' \
58
+ "$BOLD" "$BLUE" "$_STAGE_INDEX" "$TOTAL_STAGES" "$1" "$RESET"
59
+ }
60
+
61
+ # say "..." prints a plain instruction line.
62
+ say() { printf ' %s\n' "$1"; }
63
+ # step "..." is a numbered-feeling action the human takes in the browser.
64
+ step() { printf ' %s•%s %s\n' "$BLUE" "$RESET" "$1"; }
65
+ note() { printf ' %s%s%s\n' "$DIM" "$1" "$RESET"; }
66
+ warn() { printf ' %s⚠ %s%s\n' "$YELLOW" "$1" "$RESET"; }
67
+
68
+ # open_url URL opens it in the human's browser, cross-platform incl. WSL.
69
+ open_url() {
70
+ local url="$1"
71
+ printf ' %s↗ opening%s %s\n' "$GREEN" "$RESET" "$url"
72
+ { if command -v wslview >/dev/null 2>&1; then wslview "$url"
73
+ elif command -v explorer.exe >/dev/null 2>&1; then explorer.exe "$url"
74
+ elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$url"
75
+ elif command -v open >/dev/null 2>&1; then open "$url"
76
+ else warn "couldn't open a browser; visit it manually: $url"; fi
77
+ } >/dev/null 2>&1 || warn "couldn't open a browser, so visit it manually: $url"
78
+ }
79
+
80
+ # pause "msg" waits for the human to confirm they've done the manual part.
81
+ pause() {
82
+ printf ' %s%s%s ' "$DIM" "${1:-Press Enter to continue}" "$RESET"
83
+ read -r _ || true
84
+ }
85
+
86
+ # confirm "question" is a y/N gate; returns success on yes.
87
+ confirm() {
88
+ local reply=""
89
+ printf ' %s? %s [y/N] ' "$YELLOW" "$1"
90
+ read -r reply || true
91
+ [[ "$reply" =~ ^[Yy] ]]
92
+ }
93
+
94
+ # _existing KEY: current value of KEY in ENV_FILE, if any.
95
+ _existing() {
96
+ [[ -f "$ENV_FILE" ]] || return 1
97
+ local line; line=$(grep -E "^${1}=" "$ENV_FILE" | tail -n1) || return 1
98
+ printf '%s' "${line#*=}"
99
+ }
100
+
101
+ # ask KEY "Prompt" reads a value into $KEY. Offers the existing .env value as
102
+ # a default on re-runs (Enter keeps it). Visible input (non-secret).
103
+ ask() {
104
+ local key="$1" prompt="$2" current input
105
+ current=$(_existing "$key" || true)
106
+ if [[ -n "$current" ]]; then
107
+ printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
108
+ else
109
+ printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
110
+ fi
111
+ read -r input || true
112
+ [[ -z "$input" && -n "$current" ]] && input="$current"
113
+ printf -v "$key" '%s' "$input"
114
+ }
115
+
116
+ # ask_secret KEY "Prompt" is like ask, but input is hidden.
117
+ ask_secret() {
118
+ local key="$1" prompt="$2" current input
119
+ current=$(_existing "$key" || true)
120
+ if [[ -n "$current" ]]; then
121
+ printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
122
+ else
123
+ printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
124
+ fi
125
+ read -rs input || true
126
+ printf '\n'
127
+ [[ -z "$input" && -n "$current" ]] && input="$current"
128
+ printf -v "$key" '%s' "$input"
129
+ }
130
+
131
+ # write_env KEY VALUE upserts KEY=VALUE into ENV_FILE (creates it; replaces
132
+ # any existing line). Idempotent.
133
+ write_env() {
134
+ local key="$1" value="$2" tmp
135
+ touch "$ENV_FILE"
136
+ tmp=$(mktemp)
137
+ grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true
138
+ printf '%s=%s\n' "$key" "$value" >> "$tmp"
139
+ mv "$tmp" "$ENV_FILE"
140
+ WRITTEN_ENV+=("$key")
141
+ printf ' %s✓ wrote%s %s → %s\n' "$GREEN" "$RESET" "$key" "$ENV_FILE"
142
+ }
143
+
144
+ # set_secret NAME VALUE sets a GitHub Actions repo secret via gh. Falls back
145
+ # to a warning (and records it) if gh is unavailable or unauthenticated.
146
+ set_secret() {
147
+ local name="$1" value="$2"
148
+ if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
149
+ if printf '%s' "$value" | gh secret set "$name" >/dev/null 2>&1; then
150
+ WRITTEN_SECRET+=("$name")
151
+ printf ' %s✓ set%s GitHub secret %s\n' "$GREEN" "$RESET" "$name"
152
+ return
153
+ fi
154
+ fi
155
+ SKIPPED+=("GitHub secret $name (set it manually: gh secret set $name)")
156
+ warn "skipped GitHub secret $name: gh not ready; set it later"
157
+ }
158
+
159
+ # set_var NAME VALUE sets a GitHub Actions repo variable (non-secret).
160
+ set_var() {
161
+ local name="$1" value="$2"
162
+ if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
163
+ if gh variable set "$name" --body "$value" >/dev/null 2>&1; then
164
+ printf ' %s✓ set%s GitHub variable %s\n' "$GREEN" "$RESET" "$name"
165
+ return
166
+ fi
167
+ fi
168
+ SKIPPED+=("GitHub variable $name")
169
+ warn "skipped GitHub variable $name, gh not ready; set it later"
170
+ }
171
+
172
+ # finish clears, then shows a closing summary of everything configured.
173
+ finish() {
174
+ _clear
175
+ printf '\n%s%s ✓ Setup complete%s\n' "$BOLD" "$GREEN" "$RESET"
176
+ (( ${#WRITTEN_ENV[@]} )) && note "wrote ${#WRITTEN_ENV[@]} value(s) to $ENV_FILE: ${WRITTEN_ENV[*]}"
177
+ (( ${#WRITTEN_SECRET[@]} )) && note "set ${#WRITTEN_SECRET[@]} GitHub secret(s): ${WRITTEN_SECRET[*]}"
178
+ if (( ${#SKIPPED[@]} )); then
179
+ printf '\n'; warn "still to do by hand:"
180
+ for s in "${SKIPPED[@]}"; do note " - $s"; done
181
+ fi
182
+ printf '\n'
183
+ }
184
+
185
+ # ──────────────────────────────────────────────────────────────────────────
186
+ # STAGES: author this section. One stage() per step the human takes.
187
+ # Replace the example below. Set TOTAL_STAGES to match the stages you write.
188
+ # ──────────────────────────────────────────────────────────────────────────
189
+
190
+ TOTAL_STAGES=1
191
+
192
+ banner "AWS credentials for buddy deploy"
193
+
194
+ # -- Example stage: replace with the real steps ---------------------------
195
+ stage "AWS: access keys"
196
+ say "We'll capture an AWS access key pair so 'buddy deploy' can reach your account."
197
+ open_url "https://console.aws.amazon.com/iam/home#/security_credentials"
198
+ step "Open Access keys, then Create access key, and pick 'Command Line Interface'."
199
+ ask AWS_ACCESS_KEY_ID "Paste the access key ID:"
200
+ step "Copy the secret access key before you close the dialog. It is shown once."
201
+ ask_secret AWS_SECRET_ACCESS_KEY "Paste the secret access key:"
202
+ write_env AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
203
+ write_env AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
204
+ set_secret AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" # CI needs this one
205
+ note "Encrypt the file when every stage is done: ./buddy env:encrypt"
206
+ # -------------------------------------------------------------------------
207
+
208
+ finish
@@ -0,0 +1,125 @@
1
+ # Skill mechanics in Stacks
2
+
3
+ The skill-specific branch of [SKILL.md](SKILL.md): what changes when the
4
+ document is a `SKILL.md`. Everything else about writing it is the universal
5
+ reference next door.
6
+
7
+ ## Where a skill lives
8
+
9
+ `@stacksjs/skills` resolves a skill name against two sources, in order:
10
+
11
+ 1. `app/Skills/<name>/SKILL.md` - the project's own skills
12
+ 2. `storage/framework/defaults/ai/skills/<name>/SKILL.md` - the bundled ones
13
+
14
+ First hit wins, which is the same app-overrides-defaults model as
15
+ `app/Actions/` and `app/Models/`. To shadow a bundled skill, create a directory
16
+ with **the same name** under `app/Skills/`. To add a new one, pick a name no
17
+ bundled skill uses.
18
+
19
+ `buddy setup:ai <agent>` materializes the merged set into whatever directory the
20
+ agent reads (`.claude/skills` for Claude Code), symlinking by default so an
21
+ upgrade keeps them in sync, or copying with `--copy` when you want to edit them
22
+ per project. The generated directories are gitignored. Only `AGENTS.md` is
23
+ committed, because which agent you use is a personal choice.
24
+
25
+ Nothing writes into `storage/framework/defaults/ai/skills`. A project skill goes
26
+ in `app/Skills/`. Edit the bundled directory only when you are working on the
27
+ framework itself.
28
+
29
+ ## Frontmatter
30
+
31
+ ```yaml
32
+ ---
33
+ name: stacks-cache
34
+ description: Use when implementing caching in Stacks - memory cache, Redis cache, cache-aside (getOrSet), TTL management, or cache stats. Covers @stacksjs/cache and config/cache.ts.
35
+ license: MIT
36
+ compatibility: Bun >= 1.3.0, TypeScript
37
+ allowed-tools: Read Edit Write Bash Grep Glob
38
+ ---
39
+ ```
40
+
41
+ `validateSkill()` in `@stacksjs/skills` enforces four rules, and
42
+ `storage/framework/core/skills/tests/skills.test.ts` runs it over every bundled
43
+ skill, so a broken one fails the suite:
44
+
45
+ - `name` is required, 1 to 64 characters, lowercase letters, numbers and hyphens
46
+ only.
47
+ - `name` must equal the directory name.
48
+ - `description` is required and at most 1024 characters.
49
+
50
+ Two more rules the validator does not catch but the parsers do:
51
+
52
+ - **Keep `: ` out of the description.** The frontmatter parser splits each line
53
+ on its first colon and takes the rest verbatim, and a bare `: ` inside an
54
+ unquoted YAML scalar is invalid YAML besides. Use a hyphen or a comma.
55
+ - **Keep the em-dash out of everything.** The project-wide rule in `AGENTS.md`
56
+ applies to skills as much as to UI copy. Older bundled skills still carry them
57
+ in their descriptions, which is drift, not licence.
58
+
59
+ Optional fields `SkillMetadata` understands, beyond the five above:
60
+ `disable-model-invocation`, `user-invocable`, `argument-hint`, `context: fork`,
61
+ `agent`, `model`, `effort`, and a free-form `metadata` map.
62
+
63
+ ## Supporting files
64
+
65
+ `getSkill()` reports three well-known subdirectories beside `SKILL.md`:
66
+
67
+ - `scripts/` - runnable helpers the skill invokes (`stacks-browse` and
68
+ `stacks-wizard` both ship one)
69
+ - `references/` - disclosed reference the body points at
70
+ - `assets/` - templates, images, fixtures
71
+
72
+ A sibling `.md` file with no directory works too, and is the lightest form of
73
+ progressive disclosure: `stacks-technical-diagrams` keeps 36 of them.
74
+
75
+ ## Invocation
76
+
77
+ Two choices, trading the two loads:
78
+
79
+ - A **model-invoked** skill keeps its `description`, so the agent can fire it on
80
+ its own and other skills can reach it. The description is the skill's top-level
81
+ context pointer, forced to stay loaded at all times: permanent context load in
82
+ exchange for discoverability. A model-invoked skill whose content is all
83
+ reference is also the one home for shared reference, because another skill can
84
+ invoke it. Mechanics: omit `disable-model-invocation` and write a model-facing
85
+ description carrying the trigger branches. This is the default for the bundled
86
+ set, and the right choice for every subsystem reference.
87
+ - A **user-invoked** skill strips the description from the agent's reach: only
88
+ the human typing its name can invoke it, and no other skill can. Zero context
89
+ load, but it spends cognitive load, because you are the index that must
90
+ remember it exists. Mechanics: set `disable-model-invocation: true`, and the
91
+ `description` becomes human-facing, a one-line summary with the trigger list
92
+ stripped. `stacks-flow` and `stacks-handoff` are the two bundled examples.
93
+
94
+ Pick model-invocation only when the agent must reach the skill on its own, or
95
+ another skill must. If it only ever fires by hand, make it user-invoked and pay
96
+ no context load.
97
+
98
+ Shared reference that two user-invoked skills both need can live in neither:
99
+ with no descriptions, neither can fire the other. Push it to a plain file
100
+ outside the skill system, or to a model-invoked reference skill both can reach.
101
+
102
+ ## Splitting by invocation
103
+
104
+ The invocation cut, as opposed to the sequence cut in [SKILL.md](SKILL.md):
105
+ split off a model-invoked skill when you have a distinct leading word that
106
+ should trigger it on its own (a trigger word you actually type), or when another
107
+ skill must reach it. `stacks-grilling` exists as its own skill for exactly that
108
+ reason: `stacks-office-hours`, `stacks-plan-review` and `stacks-redesign` all
109
+ call it. You pay context load for the new always-loaded description, so that
110
+ independent reach has to be worth it.
111
+
112
+ ## Router skills
113
+
114
+ When user-invoked skills multiply past what you can remember, that piled-up
115
+ cognitive load is cured by a **router skill**: one user-invoked skill that names
116
+ the others and when to reach for each, so the human has one skill to remember
117
+ instead of many. `stacks-flow` is it. A router can only hint, never fire:
118
+ user-invoked skills have no description, so nothing but the human can reach them.
119
+
120
+ ## Registering a project skill
121
+
122
+ Nothing to register. `listSkills()` reads the directories, so a new
123
+ `app/Skills/<name>/SKILL.md` is live the moment it exists. Re-run
124
+ `buddy setup:ai <agent>` to link it into your agent's directory, and add a row to
125
+ the feature-to-skill table in `AGENTS.md` so a human can find it too.
@@ -0,0 +1,218 @@
1
+ ---
2
+ name: stacks-writing-for-agents
3
+ description: Use when writing or editing any document an agent reads - a SKILL.md under app/Skills or storage/framework/defaults/ai/skills, the project AGENTS.md, or a reference file a skill points at. Covers context pointers, the information hierarchy, completion criteria, leading words, pruning, and the skill mechanics behind app/Skills and buddy setup:ai.
4
+ license: MIT
5
+ compatibility: Bun >= 1.3.0, TypeScript
6
+ allowed-tools: Read Edit Write Bash Grep Glob
7
+ ---
8
+
9
+ # Writing for agents
10
+
11
+ Reference for every document an agent consumes in a Stacks project: a skill, the
12
+ project `AGENTS.md`, a doc reached by a pointer. The packaging differs, the
13
+ writing does not. The same levers make each one predictable, because the agent
14
+ takes the same *process* every run rather than producing the same output.
15
+
16
+ Stacks ships 100+ skills and expects projects to add their own under
17
+ `app/Skills/`, so this is the skill that keeps that set from turning to sludge.
18
+ The Stacks-specific mechanics (frontmatter, invocation, the override model,
19
+ what `buddy setup:ai` does with the result) are in
20
+ [MECHANICS.md](MECHANICS.md). Everything below is universal.
21
+
22
+ Credit: the model in this skill is adapted from Matt Pocock's `writing-for-agents`
23
+ skill (MIT), <https://github.com/mattpocock/skills>.
24
+
25
+ ## Context pointers
26
+
27
+ A **context pointer** is a reference held in the agent's context that names some
28
+ out-of-context material and encodes the condition for reaching it. A skill's
29
+ `description` is one. A line in `AGENTS.md` naming a doc is the same object. The
30
+ pointer's *wording*, not its target, decides when the agent reaches the material
31
+ and how reliably. A must-have target behind a weakly worded pointer is a variance
32
+ bug: sharpen the wording first, and inline the material only if sharpening fails.
33
+
34
+ A pointer does two jobs: state what the material is, and list the **branches**
35
+ that should trigger reaching it (a branch is a distinct case the document
36
+ handles, so different runs take different paths through it). Every word of an
37
+ always-loaded pointer costs on every turn, so it earns harder pruning than the
38
+ body:
39
+
40
+ - **Front-load the leading word.** The pointer is where it does its triggering work.
41
+ - **One trigger per branch.** Synonyms that rename a single branch are one branch
42
+ written twice. Collapse them, keep only genuinely distinct branches.
43
+ - **Cut identity the body already carries.** `stacks-queue`'s description does not
44
+ need to explain what a queue is.
45
+
46
+ The bundled skills follow one shape, and yours should too: `Use when <situation>
47
+ - <branch>, <branch>, <branch>. Covers <package> and <config file>.` The
48
+ trailing `Covers` clause is doing pointer work, not decoration: it is how an
49
+ agent holding a package name (`@stacksjs/cache`) or a path (`config/queue.ts`)
50
+ finds the skill that owns it.
51
+
52
+ ## The two loads
53
+
54
+ Every document and pointer you add spends one of two budgets:
55
+
56
+ - **Context load** is the cost of always-loaded material on the agent's window:
57
+ an `AGENTS.md` line, a skill description, anything sitting in context every
58
+ turn, spending tokens and attention whether or not it fires.
59
+ - **Cognitive load** is the cost on the human: which documents exist and when to
60
+ reach for each. The human is the index. Not a cost to minimise, it is the
61
+ price of human agency. Spend it where human judgement matters, remove it where
62
+ it does not.
63
+
64
+ Material reached only through a pointer escapes context load at the price of the
65
+ pointer's own line. Material with no pointer at all rides entirely on cognitive
66
+ load.
67
+
68
+ ## Information hierarchy
69
+
70
+ A document is built from two content types: **steps** (the ordered actions the
71
+ agent performs) and **reference** (definitions, rules, facts consulted on
72
+ demand). The two mix freely: all steps (`stacks-new-feature`), all reference
73
+ (`stacks-orm`), or both (`stacks-investigate`). The core decision is where each
74
+ piece sits on the **information hierarchy**, a ladder ranked by how immediately
75
+ the agent needs the material:
76
+
77
+ 1. **In-file step** is the primary tier: what the agent does, in order.
78
+ 2. **In-file reference** is consulted on demand. Often a legitimately flat
79
+ peer-set (every rule of a review on one rung), which is a fine arrangement,
80
+ not a smell.
81
+ 3. **Disclosed reference** is pushed into a separate file, reached by a context
82
+ pointer, loaded only when the pointer fires. Spans a sibling file in the same
83
+ skill directory through fully external reference any document can point at.
84
+
85
+ Push too little down and the top bloats. Push too much and you hide material the
86
+ agent actually needs. That tension is the whole decision.
87
+
88
+ **Progressive disclosure** is the move down the ladder so the top stays legible.
89
+ Not primarily a token optimisation: it is how the hierarchy is protected.
90
+ Branching is the cleanest disclosure test: inline what every branch needs, push
91
+ behind a pointer what only some branches reach. `stacks-technical-diagrams`
92
+ keeps 36 reference files beside one `SKILL.md` for exactly this reason.
93
+
94
+ **Co-location** is the within-file companion. Where the ladder decides *how far
95
+ down* a piece sits, co-location decides *what sits beside it* once there. Keep a
96
+ concept's definition, rules and caveats under one heading rather than scattered,
97
+ so reading one part brings its neighbours with it. The test: the document should
98
+ read like documentation written for the agent.
99
+
100
+ **Sprawl** is the failure mode: a document simply too long, even when every line
101
+ is live and unique. Attention thins across the excess, and every extra line is
102
+ one more to keep relevant. The cure is the ladder.
103
+
104
+ ## Steps and completion criteria
105
+
106
+ Every step ends on a **completion criterion**, the condition that tells the agent
107
+ the work is done. Two properties make it a lever:
108
+
109
+ - **Clarity.** Can the agent tell done from not-done? A vague bound
110
+ ("understanding reached") invites **premature completion**: ending the step
111
+ before it is genuinely done, attention slipping to *being done*. The visible
112
+ steps still ahead supply the pull, the criterion's clarity is the resistance.
113
+ Defend in order: sharpen the bound first (local and cheap), and only if it is
114
+ irreducibly fuzzy *and* you observe the rush, hide the later steps by splitting
115
+ the sequence. Hiding only works across a real context boundary (a hand-off or a
116
+ subagent dispatch). An inline call leaves the later steps in context and clears
117
+ nothing.
118
+ - **Demand.** How much the criterion requires. "Every changed model accounted
119
+ for" forces thorough work where "produce a change list" does not. Demand drives
120
+ **legwork**, the digging the agent does within the work, latent in the wording
121
+ rather than written as its own step. It is not step-bound: "every rule applied"
122
+ binds a body of flat reference just as "every step done" binds a sequence,
123
+ which is how an all-reference document still carries an exhaustiveness bar.
124
+
125
+ The strongest criteria are both checkable and exhaustive. `stacks-investigate`
126
+ Phase 1 is the model to copy: one command, already run once, output shown.
127
+
128
+ ## When to split
129
+
130
+ Splitting one document into two spends one of the two loads, so split only when
131
+ the cut earns it:
132
+
133
+ - **By sequence.** Split a run of steps where the post-completion steps tempt the
134
+ agent to rush the one in front of it. Keeping them out of view drives more
135
+ legwork on the current task. Beware the reverse: merging sequences exposes each
136
+ step's later steps to what follows, inviting premature completion.
137
+ - **By invocation.** Skill-specific, see [MECHANICS.md](MECHANICS.md).
138
+
139
+ ## Leading words
140
+
141
+ A **leading word** is a compact concept already living in the model's
142
+ pretraining that the agent thinks with while running the document (*lesson*,
143
+ *fog of war*, *tracer bullet*, *seam*). Repeated as a token, never as a
144
+ sentence, it accumulates a distributed definition and anchors a whole region of
145
+ behaviour in the fewest tokens by recruiting priors the model already holds.
146
+ Coining your own works if you define it clearly, but a made-up word recruits no
147
+ priors: you pay in definition tokens what a pretrained word gives free. Reach
148
+ for an existing word first.
149
+
150
+ It anchors twice. In the body, *execution*: the agent reaches for the same
151
+ behaviour every time the word appears. In a pointer, *invocation*: when the same
152
+ word lives in your prompts, your docs and your codebase, the agent links that
153
+ shared language to the material and reaches it more reliably. This is why the
154
+ Stacks skills insist on `trait`, `driver`, `action`, `seam` and `tracer bullet`
155
+ rather than paraphrasing them.
156
+
157
+ Hunt for opportunities to refactor with leading words. A triad spelled out at
158
+ three sites, a pointer spending a sentence to gesture at one idea. Each is a
159
+ passage begging to collapse into a single token:
160
+
161
+ - "fast, deterministic, low-overhead" becomes *tight* (a *tight* loop).
162
+ - "a loop you believe in" becomes *red*, turning a fuzzy gate into a binary
163
+ observable state (the loop goes *red* on the bug, or it does not).
164
+
165
+ **Negation** is the failure mode beside this lever. Steering by prohibition drags
166
+ the forbidden behaviour into context and makes it *more* available, not less.
167
+ *Do not think of an elephant*, and the elephant is all there is. Prompt the
168
+ **positive**: state the target behaviour ("use signals and composables in stx
169
+ templates") so the banned one is never spoken. A prohibition earns its place only
170
+ as a hard guardrail you cannot phrase positively, and even then, pair it with the
171
+ positive target so attention lands on what to do.
172
+
173
+ ## Pruning
174
+
175
+ - Keep each meaning in a **single source of truth**: one authoritative place, so
176
+ changing the behaviour is a one-place edit. **Duplication** costs maintenance
177
+ and tokens, and inflates a meaning's prominence on the ladder past its real
178
+ rank. It is the accidental inverse of a leading word, which repeats a token on
179
+ purpose, never the meaning.
180
+ - The **environment** is a source of truth too, and in a Stacks project it is a
181
+ rich one: `buddy list`, `buddy <command> --help`, `config/*.ts`, the
182
+ `storage/framework/*-auto-imports.json` manifests, the generated
183
+ `storage/framework/types/*.d.ts`. A document that restates it is a **cache**: a
184
+ copy of a lookup, earning its load only when the lookup is expensive. Cache
185
+ what the agent cannot find by looking (the unwritten convention, the reason
186
+ behind a choice, the gotcha no config confesses) and leave the one-command
187
+ lookups to the environment, where they cannot go stale. Every bundled skill's
188
+ `## Gotchas` section is this rule applied.
189
+ - Check every line for **relevance**: does it still bear on what the document
190
+ does? A line loses relevance by never bearing on the task, or by going stale as
191
+ the code it describes changes. Without a pruning discipline the default fate is
192
+ **sediment**: stale layers that settle because adding feels safe and removing
193
+ feels risky.
194
+ - Hunt **no-ops** sentence by sentence: an instruction the model already obeys by
195
+ default pays load to say nothing. The test (does it change behaviour versus the
196
+ default?) is model-relative, not reader-relative. Two people disagreeing about
197
+ a no-op disagree about the default, and settle it by running the document, not
198
+ by debate. When a sentence fails, delete the whole sentence rather than trim
199
+ words from it. The test also grades leading words: a word too weak to beat the
200
+ default (*be thorough*, when the agent is already thorough-ish) is a no-op, and
201
+ the fix is a stronger word (*relentless*), not a different technique.
202
+
203
+ ## Before you finish
204
+
205
+ - Every line changes behaviour versus the default.
206
+ - Every meaning lives in exactly one place.
207
+ - The description names distinct branches, front-loads the trigger, and carries
208
+ the `Covers` clause.
209
+ - Nothing in the body restates what `buddy <command> --help` or a config file
210
+ already says.
211
+ - No em-dash anywhere in the file (the project-wide rule in `AGENTS.md`).
212
+ - `bunx --bun pickier .` is clean if you touched code alongside it.
213
+
214
+ ## Downstream
215
+
216
+ > Reach for `stacks-retro` after a session to find which documents actually
217
+ > failed, and `stacks-flow` when the problem is that nobody remembers a skill
218
+ > exists.
@@ -2,10 +2,11 @@ import { Action } from '@stacksjs/actions'
2
2
  import { generateTwoFactorSetup, stashPendingTwoFactorSecret } from '@stacksjs/auth'
3
3
  import { config } from '@stacksjs/config'
4
4
  import { response } from '@stacksjs/router'
5
+ import { toSvg } from 'ts-qr-codes'
5
6
 
6
7
  export default new Action({
7
8
  name: 'GenerateTwoFactorSecretAction',
8
- description: 'Generate a new TOTP secret + otpauth URI for the authenticated user to scan',
9
+ description: 'Generate a new TOTP secret, otpauth URI and scannable QR code for the authenticated user',
9
10
  method: 'POST',
10
11
  async handle(request: RequestInstance) {
11
12
  const user = await request.user()
@@ -21,6 +22,15 @@ export default new Action({
21
22
  // two-factor.ts's doc comment for why.
22
23
  await stashPendingTwoFactorSecret(user.id as number, secret)
23
24
 
24
- return response.json({ secret, uri })
25
+ // Rendered here rather than left to the client. Every authenticator flow
26
+ // needs the URI as a QR code, and a caller that has to find its own
27
+ // encoder either ships one to the browser or, more often, falls back to
28
+ // asking the user to type a 32-character secret by hand.
29
+ //
30
+ // SVG so it stays sharp at any size and can be inlined into a page or an
31
+ // email; the URI is still returned for clients that render their own.
32
+ const qr = toSvg(uri, { size: 240, title: 'Two-factor authentication setup' })
33
+
34
+ return response.json({ secret, uri, qr })
25
35
  },
26
36
  })
@@ -4,19 +4,19 @@ import { response } from '@stacksjs/router'
4
4
  import { commerceIdentifier, commerceNotFound } from '../commerce-action'
5
5
 
6
6
  export default new Action({
7
- name: 'Driver Destroy',
8
- description: 'Driver Destroy ORM Action',
7
+ name: 'Courier Destroy',
8
+ description: 'Courier Destroy ORM Action',
9
9
  method: 'DELETE',
10
10
  async handle(request: RequestInstance) {
11
- const identifier = commerceIdentifier(request, 'Driver')
11
+ const identifier = commerceIdentifier(request, 'Courier')
12
12
  if (identifier.error)
13
13
  return identifier.error
14
14
  const { id } = identifier
15
15
 
16
- const deleted = await shippings.drivers.destroy(id)
16
+ const deleted = await shippings.couriers.destroy(id)
17
17
  if (!deleted)
18
- return commerceNotFound('Driver', id)
18
+ return commerceNotFound('Courier', id)
19
19
 
20
- return response.json({ message: 'Driver deleted successfully' })
20
+ return response.json({ message: 'Courier deleted successfully' })
21
21
  },
22
22
  })
@@ -5,11 +5,11 @@ import { shippings } from '@stacksjs/commerce'
5
5
  import { response } from '@stacksjs/router'
6
6
 
7
7
  export default new Action({
8
- name: 'Driver Index',
9
- description: 'Driver Index ORM Action',
8
+ name: 'Courier Index',
9
+ description: 'Courier Index ORM Action',
10
10
  method: 'GET',
11
11
  async handle() {
12
- const results = await shippings.drivers.fetchAll()
12
+ const results = await shippings.couriers.fetchAll()
13
13
 
14
14
  return response.json(results)
15
15
  },