kimaki 0.26.0 → 0.27.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 (214) hide show
  1. package/dist/agent-model.e2e.test.js +25 -122
  2. package/dist/anthropic-account-identity.js +1 -0
  3. package/dist/anthropic-auth-plugin.js +1 -0
  4. package/dist/anthropic-auth-state.js +1 -0
  5. package/dist/bin.js +73 -19
  6. package/dist/cli-commands/maintenance.js +46 -7
  7. package/dist/cli-commands/send.js +24 -0
  8. package/dist/cli-commands/session.js +183 -8
  9. package/dist/cli-commands/task.js +13 -1
  10. package/dist/cli-parsing.test.js +59 -3
  11. package/dist/cli-runner.js +28 -4
  12. package/dist/cli-runner.test.js +27 -0
  13. package/dist/cli-send-thread.e2e.test.js +1 -1
  14. package/dist/cli.js +32 -0
  15. package/dist/commands/abort.js +5 -1
  16. package/dist/commands/agent.js +6 -8
  17. package/dist/commands/ask-question.js +21 -32
  18. package/dist/commands/ask-question.test.js +43 -1
  19. package/dist/commands/login.js +6 -0
  20. package/dist/commands/merge-worktree.js +36 -10
  21. package/dist/commands/model-variant.js +7 -1
  22. package/dist/commands/model.js +14 -8
  23. package/dist/commands/multioauth.js +1 -0
  24. package/dist/commands/new-worktree.js +170 -102
  25. package/dist/commands/user-command.js +6 -8
  26. package/dist/commands/worktrees.js +5 -13
  27. package/dist/config.js +16 -1
  28. package/dist/database.js +124 -3
  29. package/dist/db.js +22 -0
  30. package/dist/db.test.js +167 -1
  31. package/dist/discord-bot.js +34 -32
  32. package/dist/discord-command-registration.js +9 -1
  33. package/dist/discord-utils.js +13 -5
  34. package/dist/discord-utils.test.js +17 -1
  35. package/dist/errors.js +5 -0
  36. package/dist/exec-async.js +17 -5
  37. package/dist/external-directory-permissions.e2e.test.js +2 -2
  38. package/dist/file-edit-log.js +197 -0
  39. package/dist/file-edit-log.test.js +258 -0
  40. package/dist/format-tables.js +52 -0
  41. package/dist/format-tables.test.js +93 -0
  42. package/dist/gateway-proxy-reconnect.e2e.test.js +28 -8
  43. package/dist/gateway-proxy.e2e.test.js +5 -5
  44. package/dist/git-worktree-core.js +182 -17
  45. package/dist/ipc-tools-plugin.js +63 -3
  46. package/dist/kimaki-opencode-plugin-loading.e2e.test.js +122 -58
  47. package/dist/kimaki-opencode-plugin.js +7 -3
  48. package/dist/kimaki-workspace-adaptor.js +33 -10
  49. package/dist/logger.js +10 -1
  50. package/dist/markdown.js +3 -1
  51. package/dist/markdown.test.js +11 -3
  52. package/dist/message-formatting.js +18 -7
  53. package/dist/message-formatting.test.js +56 -9
  54. package/dist/oauth-rotation-shared.js +10 -0
  55. package/dist/openai-auth-plugin.js +1 -0
  56. package/dist/openai-auth-state.js +1 -0
  57. package/dist/opencode-command.test.js +60 -0
  58. package/dist/opencode.js +53 -12
  59. package/dist/question-abort-clears-dropdown.e2e.test.js +89 -0
  60. package/dist/question-text-before-dropdown.e2e.test.js +52 -0
  61. package/dist/queue-advanced-abort.e2e.test.js +1 -1
  62. package/dist/queue-advanced-action-buttons.e2e.test.js +3 -3
  63. package/dist/queue-advanced-e2e-setup.js +78 -5
  64. package/dist/queue-advanced-footer.e2e.test.js +40 -246
  65. package/dist/queue-advanced-model-switch.e2e.test.js +2 -2
  66. package/dist/queue-advanced-permissions-typing.e2e.test.js +7 -9
  67. package/dist/queue-advanced-question.e2e.test.js +31 -26
  68. package/dist/queue-advanced-typing-interrupt.e2e.test.js +2 -2
  69. package/dist/queue-delete-message.e2e.test.js +1 -1
  70. package/dist/queue-drain-after-interactive-ui.e2e.test.js +2 -2
  71. package/dist/queue-interrupt-drain.e2e.test.js +112 -2
  72. package/dist/queue-question-select-drain.e2e.test.js +30 -128
  73. package/dist/runtime-lifecycle.e2e.test.js +10 -37
  74. package/dist/schema.js +34 -0
  75. package/dist/session-handler/event-stream-state.js +267 -50
  76. package/dist/session-handler/event-stream-state.test.js +415 -8
  77. package/dist/session-handler/model-utils.js +159 -5
  78. package/dist/session-handler/model-utils.test.js +345 -0
  79. package/dist/session-handler/thread-session-runtime.js +252 -182
  80. package/dist/session-sleep.test.js +224 -0
  81. package/dist/session-title-rename.test.js +7 -39
  82. package/dist/sleep.e2e.test.js +491 -0
  83. package/dist/startup-time.e2e.test.js +2 -1
  84. package/dist/store.js +3 -0
  85. package/dist/system-message.js +59 -6
  86. package/dist/system-message.test.js +76 -6
  87. package/dist/task-runner.js +94 -4
  88. package/dist/task-schedule.js +56 -0
  89. package/dist/task-schedule.test.js +71 -1
  90. package/dist/thread-message-queue.e2e.test.js +10 -169
  91. package/dist/undo-redo.e2e.test.js +2 -2
  92. package/dist/voice-attachment.js +23 -1
  93. package/dist/voice-message.e2e.test.js +10 -10
  94. package/dist/voice.test.js +55 -29
  95. package/dist/worktree-clone-isolation.e2e.test.js +188 -0
  96. package/dist/worktree-lifecycle.e2e.test.js +29 -10
  97. package/dist/worktrees.js +226 -443
  98. package/dist/worktrees.test.js +435 -58
  99. package/dist/xai-auth-plugin.js +1 -0
  100. package/dist/xai-auth-state.js +1 -0
  101. package/package.json +8 -6
  102. package/skills/errore/SKILL.md +18 -0
  103. package/skills/goke/SKILL.md +150 -10
  104. package/skills/holocron/SKILL.md +37 -0
  105. package/skills/npm-package/SKILL.md +55 -35
  106. package/skills/profano/SKILL.md +17 -2
  107. package/skills/spiceflow/SKILL.md +39 -3
  108. package/skills/strada/SKILL.md +39 -0
  109. package/skills/zele/SKILL.md +19 -0
  110. package/src/agent-model.e2e.test.ts +25 -151
  111. package/src/anthropic-account-identity.ts +1 -0
  112. package/src/anthropic-auth-plugin.ts +1 -0
  113. package/src/anthropic-auth-state.ts +1 -0
  114. package/src/bin.ts +78 -26
  115. package/src/cli-commands/maintenance.ts +59 -13
  116. package/src/cli-commands/send.ts +25 -0
  117. package/src/cli-commands/session.ts +203 -9
  118. package/src/cli-commands/task.ts +13 -1
  119. package/src/cli-parsing.test.ts +69 -3
  120. package/src/cli-runner.test.ts +49 -0
  121. package/src/cli-runner.ts +28 -4
  122. package/src/cli-send-thread.e2e.test.ts +1 -1
  123. package/src/cli.ts +53 -0
  124. package/src/commands/abort.ts +6 -1
  125. package/src/commands/agent.ts +6 -6
  126. package/src/commands/ask-question.test.ts +48 -1
  127. package/src/commands/ask-question.ts +21 -32
  128. package/src/commands/login.ts +6 -0
  129. package/src/commands/merge-worktree.ts +43 -12
  130. package/src/commands/model-variant.ts +8 -1
  131. package/src/commands/model.ts +18 -8
  132. package/src/commands/multioauth.ts +1 -0
  133. package/src/commands/new-worktree.ts +138 -55
  134. package/src/commands/user-command.ts +6 -6
  135. package/src/commands/worktrees.ts +5 -12
  136. package/src/config.ts +18 -1
  137. package/src/database.ts +195 -3
  138. package/src/db.test.ts +188 -0
  139. package/src/db.ts +26 -0
  140. package/src/discord-bot.ts +35 -29
  141. package/src/discord-command-registration.ts +15 -1
  142. package/src/discord-utils.test.ts +19 -0
  143. package/src/discord-utils.ts +21 -6
  144. package/src/errors.ts +5 -0
  145. package/src/exec-async.ts +20 -11
  146. package/src/external-directory-permissions.e2e.test.ts +2 -2
  147. package/src/file-edit-log.test.ts +287 -0
  148. package/src/file-edit-log.ts +283 -0
  149. package/src/format-tables.test.ts +101 -0
  150. package/src/format-tables.ts +75 -0
  151. package/src/gateway-proxy-reconnect.e2e.test.ts +38 -8
  152. package/src/gateway-proxy.e2e.test.ts +5 -5
  153. package/src/git-worktree-core.ts +247 -20
  154. package/src/ipc-tools-plugin.ts +75 -5
  155. package/src/kimaki-opencode-plugin-loading.e2e.test.ts +188 -97
  156. package/src/kimaki-opencode-plugin.ts +7 -3
  157. package/src/kimaki-workspace-adaptor.ts +40 -11
  158. package/src/logger.ts +13 -4
  159. package/src/markdown.test.ts +13 -3
  160. package/src/markdown.ts +4 -1
  161. package/src/message-formatting.test.ts +86 -12
  162. package/src/message-formatting.ts +19 -7
  163. package/src/oauth-rotation-shared.ts +10 -0
  164. package/src/openai-auth-plugin.ts +1 -0
  165. package/src/openai-auth-state.ts +1 -0
  166. package/src/opencode-command.test.ts +81 -0
  167. package/src/opencode.ts +71 -11
  168. package/src/question-abort-clears-dropdown.e2e.test.ts +109 -0
  169. package/src/question-text-before-dropdown.e2e.test.ts +67 -0
  170. package/src/queue-advanced-abort.e2e.test.ts +1 -1
  171. package/src/queue-advanced-action-buttons.e2e.test.ts +3 -3
  172. package/src/queue-advanced-e2e-setup.ts +83 -4
  173. package/src/queue-advanced-footer.e2e.test.ts +47 -294
  174. package/src/queue-advanced-model-switch.e2e.test.ts +2 -2
  175. package/src/queue-advanced-permissions-typing.e2e.test.ts +7 -9
  176. package/src/queue-advanced-question.e2e.test.ts +35 -29
  177. package/src/queue-advanced-typing-interrupt.e2e.test.ts +2 -2
  178. package/src/queue-delete-message.e2e.test.ts +1 -1
  179. package/src/queue-drain-after-interactive-ui.e2e.test.ts +2 -2
  180. package/src/queue-interrupt-drain.e2e.test.ts +123 -2
  181. package/src/queue-question-select-drain.e2e.test.ts +32 -157
  182. package/src/runtime-lifecycle.e2e.test.ts +10 -41
  183. package/src/schema.sql +14 -1
  184. package/src/schema.ts +36 -0
  185. package/src/session-handler/event-stream-state.test.ts +437 -7
  186. package/src/session-handler/event-stream-state.ts +389 -53
  187. package/src/session-handler/model-utils.test.ts +415 -0
  188. package/src/session-handler/model-utils.ts +249 -5
  189. package/src/session-handler/thread-session-runtime.ts +300 -215
  190. package/src/session-sleep.test.ts +294 -0
  191. package/src/session-title-rename.test.ts +9 -48
  192. package/src/sleep.e2e.test.ts +562 -0
  193. package/src/startup-time.e2e.test.ts +5 -1
  194. package/src/store.ts +21 -0
  195. package/src/system-message.test.ts +86 -6
  196. package/src/system-message.ts +66 -7
  197. package/src/task-runner.ts +136 -2
  198. package/src/task-schedule.test.ts +76 -0
  199. package/src/task-schedule.ts +95 -0
  200. package/src/thread-message-queue.e2e.test.ts +10 -202
  201. package/src/undo-redo.e2e.test.ts +2 -2
  202. package/src/voice-attachment.ts +29 -1
  203. package/src/voice-message.e2e.test.ts +10 -10
  204. package/src/voice.test.ts +63 -29
  205. package/src/worktree-clone-isolation.e2e.test.ts +213 -0
  206. package/src/worktree-lifecycle.e2e.test.ts +29 -9
  207. package/src/worktrees.test.ts +505 -64
  208. package/src/worktrees.ts +257 -603
  209. package/src/xai-auth-plugin.ts +1 -0
  210. package/src/xai-auth-state.ts +1 -0
  211. package/dist/message-finish-field.e2e.test.js +0 -169
  212. package/dist/subagent-rate-limit-plugin.js +0 -184
  213. package/src/message-finish-field.e2e.test.ts +0 -196
  214. package/src/subagent-rate-limit-plugin.ts +0 -228
@@ -5,8 +5,9 @@ description: >
5
5
  with Standard Schema support (Zod, Valibot, ArkType). Use goke when building CLI
6
6
  tools — it handles commands, subcommands, options, type coercion, help generation,
7
7
  and more. Schema-based options give you automatic type inference, coercion from
8
- strings, and help text generation. ALWAYS read this skill when a repo uses goke
9
- for its CLI.
8
+ strings, and help text generation. Pair with `@goke/mcp` to generate CLI
9
+ commands automatically from an MCP server. ALWAYS read this skill when a repo
10
+ uses goke for its CLI.
10
11
  version: 0.0.1
11
12
  ---
12
13
 
@@ -30,14 +31,51 @@ npm install goke # or bun, pnpm, etc
30
31
 
31
32
  ## Quick Notes
32
33
 
33
- - Core APIs: `cli.option`, `cli.use`, `cli.version`, `cli.help`, `cli.completions`, `cli.parse`
34
+ - Core APIs: `cli.option`, `cli.use`, `cli.section`, `cli.version`, `cli.help`, `cli.completions`, `cli.parse`
34
35
  - Prefer injected `{ fs, console, process }` over globals
35
36
  - Use relative paths with injected `fs`; if a helper needs current-cwd semantics, pass injected `process.cwd` into that helper
36
37
  - For JustBash compatibility tests, import the existing CLI from app code instead of defining a new CLI inside the test
37
38
  - **Never install `picocolors`, `chalk`, `kleur`, or any color library.** Use `import { colors } from 'goke'` instead. It's a vendored picocolors with zero extra dependencies.
39
+ - Use [`@goke/mcp`](https://github.com/remorses/goke/tree/main/mcp) to generate CLI commands from an MCP server. `addMcpCommands({ cli, getMcpUrl, loadCache, saveCache })` discovers tools and registers a typed command for each one.
38
40
 
39
41
  The README is the source of truth for rules, examples, testing patterns, JustBash integration, and API details.
40
42
 
43
+ ## Reporting goke bugs
44
+
45
+ If you find a bug in goke, **always open a GitHub issue with the `gh` CLI**. Do not only mention it in chat.
46
+
47
+ ```bash
48
+ gh issue create --repo remorses/goke --title "Short bug title" --body "$(cat <<'EOF'
49
+ **Model:** grok-4.6
50
+ **Harness:** OpenCode / Kimaki
51
+
52
+ **What happened**
53
+ Describe the unexpected behavior.
54
+
55
+ **Relevant goke code**
56
+ Paste the CLI definition or goke API usage from this package that triggered the bug.
57
+
58
+ **Reproduction**
59
+ Minimal example that fails:
60
+
61
+ ```ts
62
+ import { goke } from 'goke'
63
+
64
+ const cli = goke('mycli')
65
+ cli.command('deploy', 'Deploy').action(() => {})
66
+ cli.parse()
67
+ ```
68
+ EOF
69
+ )"
70
+ ```
71
+
72
+ Always include in the issue body:
73
+
74
+ - the **model** used (for example `grok-4.6`, `claude-opus-4-6`)
75
+ - the **harness** (for example OpenCode, Claude Code, Cursor, Codex)
76
+ - the **relevant goke code** from this package that hit the bug
77
+ - a **minimal reproduction** example
78
+
41
79
  If the README or this skill mentions a `goke` export that is missing from the installed package, upgrade `goke` to latest first before adding workarounds or custom local detection code:
42
80
 
43
81
  ```bash
@@ -72,15 +110,30 @@ if (isAgent) {
72
110
  }
73
111
  ```
74
112
 
75
- When guarding interactive prompts, check `isAgent` alongside `!process.stdin.isTTY`:
113
+ `isAgent` is for **skipping interactive prompts that have a CLI flag alternative** (e.g. `--env staging` instead of a clack select). Use it to require the flag and fail with a usage hint instead of hanging on stdin:
76
114
 
77
115
  ```ts
78
- if (isAgent || !process.stdin.isTTY) {
79
- console.error('Missing --env. Usage: deploy --env staging|production')
116
+ if (!env) {
117
+ if (isAgent || !process.stdin.isTTY) {
118
+ console.error('Missing --env. Usage: deploy --env staging|production')
119
+ process.exit(1)
120
+ }
121
+ // ... clack prompt fallback
122
+ }
123
+ ```
124
+
125
+ **Never use `isAgent` to block commands that have no flag alternative**, like device-flow login. Those commands don't read stdin at all; they open a browser, print a code, and poll. The only real requirement is a TTY for spinner output. Use `!process.stdin.isTTY` or `!process.stdout.isTTY` alone for those:
126
+
127
+ ```ts
128
+ // login command — no flag alternative, agent can run it in tuistory
129
+ if (!process.stdin.isTTY) {
130
+ console.error('Login requires a terminal. Run in tuistory or tmux.')
80
131
  process.exit(1)
81
132
  }
82
133
  ```
83
134
 
135
+ Agents can run login commands in a PTY via tuistory and complete the browser flow with playwriter. Blocking them with `isAgent` makes login impossible from agent contexts.
136
+
84
137
  Supported agents: `cursor`, `claude`, `devin`, `replit`, `gemini`, `codex`, `auggie`, `opencode`, `kiro`, `goose`, `pi`. Set `AI_AGENT` env var to override.
85
138
 
86
139
  ## Long-Running Interactive Commands
@@ -96,16 +149,22 @@ cli
96
149
  .command('login', 'Authenticate with browser login')
97
150
  .action(async (options, ctx) => {
98
151
  if (ctx.daemon.isDaemon) {
99
- // ── DAEMON: run the long-running flow in background ──
100
- const result = await startOAuthFlow({ /* ... */ })
152
+ // ── DAEMON: create the URL, hand it to the foreground, then wait ──
153
+ const flow = await startOAuthFlow({ /* ... */ })
154
+ ctx.daemon.publishStartupMessage(`Authorize: ${flow.authorizationUrl}`)
155
+ ctx.daemon.ready()
156
+ const result = await flow.waitForApproval()
101
157
  if (result.success) saveAuth(result)
102
158
  return // daemon exits, PID file is cleaned up
103
159
  }
104
160
 
105
161
  // ── CLIENT: decide foreground vs background ──
106
162
  if (isAgent) {
107
- // Agent mode: start daemon and return immediately
108
- await ctx.daemon.start({ timeoutMs: 10 * 60 * 1000 })
163
+ // Agent mode: return after the daemon publishes its authorization URL
164
+ await ctx.daemon.start({
165
+ waitForStartup: true,
166
+ timeoutMs: 10 * 60 * 1000,
167
+ })
109
168
  ctx.console.log('Login running in background.')
110
169
  ctx.console.log('After approving in browser, verify with: mycli me')
111
170
  return
@@ -117,6 +176,8 @@ cli
117
176
  })
118
177
  ```
119
178
 
179
+ Use `waitForStartup: true` when the daemon creates an OAuth URL, device code, or other value that the agent must see before the foreground command returns. Publish ordered messages with `publishStartupMessage(message, { stream: 'stdout' | 'stderr' })`, then call `ready()`. Detached stdio remains ignored. Attached mode still receives the messages directly and waits for the daemon to exit.
180
+
120
181
  Add a `me` command (exits 0 if logged in, 1 if not) so agents can poll for completion. Use `ctx.daemon.forCommand('login')` to check the login daemon status from other commands. See the **Background Daemons** section in the goke README for the full pattern, including `env` passthrough and PID file safety.
121
182
 
122
183
  ## Shell Completions
@@ -220,6 +281,17 @@ project remove
220
281
 
221
282
  Pick **singular or plural** for the noun and stick with it across the entire CLI. If `project list` exists, don't add `projects add`.
222
283
 
284
+ **Always group namespaced commands with `.section()`.** Commands that share a parent word (`get pods`, `get services`, `auth login`) must sit under a named heading in root help. Call `cli.section('Get')` before registering that group. Use `.command(...).section('Name')` when one command belongs in a different group.
285
+
286
+ ```ts
287
+ cli.section('Get')
288
+ cli.command('get pods', 'List pods')
289
+ cli.command('get services', 'List services')
290
+
291
+ cli.section('Describe')
292
+ cli.command('describe pod <name>', 'Describe a pod')
293
+ ```
294
+
223
295
  ### Consistent verbs
224
296
 
225
297
  Choose one verb per action and reuse it everywhere:
@@ -233,6 +305,16 @@ Choose one verb per action and reuse it everywhere:
233
305
 
234
306
  Check which verbs the CLI already uses and match them. If existing commands use `add`, every new "create something" command should also use `add`.
235
307
 
308
+ ### Never mix different ID kinds in positionals
309
+
310
+ All positionals of a command must identify the same kind of thing. Never give the first positional a different meaning than the rest (e.g. `events <recordingId> [...eventIds]`): a call like `events 1 4 7` is unreadable because the first number silently refers to a different entity. Move the parent/scoping ID to an optional flag with a sensible default and keep positionals homogeneous:
311
+
312
+ ```ts
313
+ cli
314
+ .command('recorder events [...eventIds]', 'Print recorded events')
315
+ .option('-r, --recording <id>', 'Recording ID (defaults to the latest recording)')
316
+ ```
317
+
236
318
  ## Prefer Optional Flags Over Required Flags
237
319
 
238
320
  **Never make a flag required when it can be optional with an interactive fallback.** Required flags force users to read `--help` before they can run anything. Optional flags let them run the bare command and discover options progressively through prompts.
@@ -301,6 +383,19 @@ if (!options.name) {
301
383
  }
302
384
  ```
303
385
 
386
+ ## Error Messages with Recovery Hints
387
+
388
+ **Every error message should include a hint showing how to fix or rerun the command.** When a CLI exits with an error, the user (or agent) should be able to copy-paste a corrected command immediately instead of reading `--help`.
389
+
390
+ ```ts
391
+ console.error('Failed to detect local port.')
392
+ console.error('If your server prints the port in an unusual format, specify it explicitly:')
393
+ console.error(` mycli serve -p <port> -- ${shellQuote(args)}`)
394
+ process.exit(1)
395
+ ```
396
+
397
+ Include the user's original arguments in the suggested command when possible, so the hint is directly copy-pasteable. For timeouts, show how long it waited so the user knows whether to wait longer or try a different approach.
398
+
304
399
  ## Interactive Prompts with @clack/prompts
305
400
 
306
401
  Use `@clack/prompts` for interactive CLI prompts like `select`, `confirm`, and text input.
@@ -553,3 +648,48 @@ cli
553
648
  - Developers can point to `http://localhost:3000` during development without losing their production token
554
649
  - Switching servers is just `--api-url` or setting an env var; no re-login needed if the server was used before
555
650
  ```
651
+
652
+ ## MCP CLIs (`@goke/mcp`)
653
+
654
+ Use `addMcpCommands` from `@goke/mcp`. Do not hide `--help` behind a token. Put token steps on a `config` command. Never write `isSetupCmd` or a first-run prompt that blocks no-args.
655
+
656
+ Use `getMcpUrl` plus `getHeaders` for HTTP Bearer tokens. Use `getMcpTransport` for stdio or any custom transport.
657
+
658
+ ```ts
659
+ await addMcpCommands({
660
+ cli,
661
+ getMcpUrl: () => MCP_URL,
662
+ getHeaders: () => {
663
+ const token = process.env.TOKEN || loadConfig().token
664
+ if (!token) return
665
+ return { Authorization: `Bearer ${token}` }
666
+ },
667
+ loadCache: () => loadConfig().cache,
668
+ saveCache: (cache) => saveConfig({ cache }),
669
+ })
670
+ ```
671
+
672
+ ## Writing a Skill for a goke CLI
673
+
674
+ When creating a SKILL.md for a CLI built with goke, keep it **thin**. The skill is a bridge between the agent and the canonical docs, not a copy of them.
675
+
676
+ ### Structure
677
+
678
+ 1. **Always start with `--help`.** The CLI self-documents commands, options, and examples via goke's help generator. Make agents read it first, in full, every session. Add a "Read the help first" section with `mycli --help` and a rule to never truncate with `head`, `tail`, or `sed -n`.
679
+
680
+ 2. **Point at canonical docs.** If the CLI has a docs site with `llms-full.txt` (e.g. built with Holocron), tell agents to `curl -s https://mycli.dev/llms-full.txt` as the single source of truth. For OSS repos without a docs site, curl the raw README from GitHub: `curl -s https://raw.githubusercontent.com/owner/repo/main/README.md`. Add a "never truncate" rule next to the curl command. Optionally list 3-4 individual page URLs for the most common quick lookups.
681
+
682
+ 3. **Install and auth.** Just the commands, one per line, minimal prose. Start with `which mycli` so agents check before installing. Then `npm install -g mycli`, `mycli auth check`, `mycli auth login`, and `mycli auth set --key` for CI.
683
+
684
+ 4. **Gotchas.** Agent-specific knowledge that is not obvious from `--help` or the docs. Things like browser flows that need user interaction, remote vs local file path restrictions, implicit defaults that cause silent failures, and subscription requirements.
685
+
686
+ 5. **MCP alternative.** If the CLI has an MCP server, include the one-liner install command.
687
+
688
+ ### What NOT to put in the skill
689
+
690
+ - **Command reference** — already in `--help` output
691
+ - **Workflow examples** — belong in the docs site (quickstart, guides)
692
+ - **Option descriptions** — already in Zod `.describe()` strings that goke renders in help
693
+ - **Output format details** — covered by `--help` and docs
694
+
695
+ If a section in the skill duplicates content from `--help` or the docs, delete it. The agent will fetch the canonical source.
@@ -182,6 +182,7 @@ README as the index page:
182
182
 
183
183
  ```mdx
184
184
  ---
185
+ $schema: https://holocron.so/frontmatter.json
185
186
  title: My Project
186
187
  ---
187
188
 
@@ -304,9 +305,42 @@ title: Quickstart
304
305
  description: Build and deploy your first Holocron docs site.
305
306
  icon: lucide:rocket
306
307
  tag: NEW
308
+ prompt: |
309
+ Write the Quickstart from @/cli/src/create.ts.
310
+ ---
311
+ ```
312
+
313
+ ### Record the generation prompt
314
+
315
+ Every documentation page you create must include a **`prompt`** field that
316
+ records the instruction and sources used to generate that page. When editing an
317
+ older page without one, add it when you can identify the real sources. Write the
318
+ original generation recipe, not a maintenance instruction.
319
+
320
+ Use **`@/path`** for source files and folders from the **repository root**.
321
+ Use `@./path` or `@../path` only when the source sits next to the MDX page.
322
+ Prefix remote sources with **`@https://`**. Bare URLs are not references. Do
323
+ not invent sources, and update the prompt when source locations or page scope
324
+ change.
325
+
326
+ ```yaml
327
+ ---
328
+ $schema: https://holocron.so/frontmatter.json
329
+ title: Authentication
330
+ prompt: |
331
+ Write the authentication guide from @/src/auth/ and
332
+ @/src/middleware/session.ts.
333
+ Use @https://github.com/example/project/releases for recent behavior.
334
+
335
+ Explain sessions, API keys, and GitHub Actions OIDC.
336
+ Include a complete TypeScript example for every method.
307
337
  ---
308
338
  ```
309
339
 
340
+ Holocron Maintain extracts these references and reruns the generation prompt
341
+ when a source changes. A folder reference matches changes to any tracked file
342
+ inside that folder.
343
+
310
344
  **Title** should be **50-60 characters** max. It appears in the OG image at large
311
345
  font size, in the browser tab, and in search results. Keep it concise and
312
346
  descriptive.
@@ -319,6 +353,7 @@ the full title still appears in the page heading, browser tab, and OG image.
319
353
 
320
354
  ```mdx
321
355
  ---
356
+ $schema: https://holocron.so/frontmatter.json
322
357
  title: Configuring Authentication Providers
323
358
  sidebarTitle: Auth Providers
324
359
  ---
@@ -342,6 +377,7 @@ sidebar and often wraps or looks verbose.
342
377
 
343
378
  ```mdx
344
379
  ---
380
+ $schema: https://holocron.so/frontmatter.json
345
381
  title: Open-source browser automation for AI agents
346
382
  sidebarTitle: Home
347
383
  description: Automate any browser with a simple TypeScript API.
@@ -369,6 +405,7 @@ In both modes the content can feel too wide. Use **`maxWidth`** to constrain it:
369
405
 
370
406
  ```yaml
371
407
  ---
408
+ $schema: https://holocron.so/frontmatter.json
372
409
  mode: "custom"
373
410
  maxWidth: 700
374
411
  ---
@@ -3,8 +3,9 @@ name: npm-package
3
3
  description: >
4
4
  Opinionated TypeScript npm package template for ESM packages. Enforces
5
5
  src→dist builds with tsc, strict TypeScript defaults, explicit exports, and
6
- publish-safe package metadata. Use this when creating or updating any npm
7
- package in this repo.
6
+ publish-safe package metadata. Relative source imports must use .ts or .tsx
7
+ (never .js); tsc rewrites them via rewriteRelativeImportExtensions. Use this
8
+ when creating or updating any npm package in this repo.
8
9
  version: 0.0.1
9
10
  ---
10
11
 
@@ -45,19 +46,36 @@ Use this skill when scaffolding or fixing npm packages.
45
46
  README at the repository root. Package READMEs don't get read by anyone.
46
47
  The root README is the single source of truth for the whole project.
47
48
  10. `scripts.build` should be `tsc && chmod +x dist/cli.js` (skip the chmod if
48
- the package has no bin). No bundling. Do not delete `dist/` in `build` by
49
- default because forcing a clean build on every local build can cause
50
- issues. Optionally include running scripts with `tsx` if needed to
51
- generate build artifacts.
52
- 11. `prepublishOnly` must always do the cleanup before `build` (optionally run
53
- generation before build when required). Always add this script:
49
+ the package has no bin). No bundling. Optionally include running scripts
50
+ with `tsx` if needed to generate build artifacts.
51
+
52
+ **Never put `rm -rf`, `rimraf`, or any delete in `build`.** `build` runs on
53
+ every edit, watch loop, and CI test job. Deleting `dist/` there breaks live
54
+ dev servers that hold it in their module graph (Bun fails with `Failed to
55
+ load bundled module '<pkg>/dist/index.js'` until restarted) and throws away
56
+ incremental compilation. A stale file only matters in a published tarball,
57
+ which `prepublishOnly` already handles.
58
+
59
+ 11. `prepublishOnly` is the **only** place a delete belongs. It keeps a file
60
+ deleted from `src/` out of the published package:
54
61
  ```json
55
62
  { "prepublishOnly": "rimraf dist \"*.tsbuildinfo\" && pnpm build" }
56
63
  ```
57
- This ensures `dist/` is fresh before every `npm publish`, so deleted files
58
- do not accidentally stay in the published package. Use `rimraf` here
59
- instead of bare shell globs so the script behaves the same in zsh, bash,
60
- and Windows shells even when no `.tsbuildinfo` file exists.
64
+ **Always use `rimraf`, never `rm -rf`.** This holds for every package.json
65
+ script, not just this one. `rm -rf` is not portable to Windows, and with no
66
+ `.tsbuildinfo` present zsh aborts `rm -rf dist *.tsbuildinfo` with `no
67
+ matches found`, so the build after `&&` never runs and you publish a stale
68
+ `dist/`. `sh` and `bash` only survive because `rm -f` ignores the literal
69
+ unmatched name. Install `rimraf` as a devDependency; never rely on
70
+ `npx`/`bunx` at publish time.
71
+
72
+ When the package also has a publish guard, chain it first so the guard
73
+ still runs before anything is deleted:
74
+ ```json
75
+ {
76
+ "prepublishOnly": "node -e \"if (!process.env.CI) process.exit(1)\" && rimraf dist \"*.tsbuildinfo\" && pnpm build"
77
+ }
78
+ ```
61
79
 
62
80
  ## bin field
63
81
 
@@ -175,33 +193,33 @@ Use Node ESM-compatible compiler settings:
175
193
 
176
194
  - Always use "rootDir": "src"
177
195
  - Add `"DOM"` to `lib` only when browser globals are needed.
178
- - Use `.ts` and `.tsx` extensions in source imports. `tsc` rewrites them to
179
- `.js` in the emitted `dist/` output automatically via
180
- `rewriteRelativeImportExtensions`. This means source code works directly in
181
- runtimes like `tsx`, `bun`, and frameworks like Next.js that expect `.ts`
182
- extensions, while the published `dist/` has correct `.js` imports that Node.js
183
- and other consumers resolve without issues.
184
-
185
- ```ts
186
- // source (src/index.ts) — use .ts/.tsx extensions
187
- import { helper } from "./utils.ts";
188
- import { Button } from "./button.tsx";
189
-
190
- // emitted output (dist/index.js) — tsc rewrites to .js
191
- // import { helper } from './utils.js'
192
- // import { Button } from './button.js'
193
- ```
194
-
195
- - Only relative imports are rewritten. Path aliases (`paths` in tsconfig) are
196
- not supported by `rewriteRelativeImportExtensions` — this is fine since npm
197
- packages should use relative imports anyway.
198
- - Requires TypeScript 5.7+.
196
+ - Requires TypeScript 5.7+ (`rewriteRelativeImportExtensions`).
199
197
  - Install `@types/node` as a dev dependency whenever Node APIs are used.
200
198
  - If generation is required, keep generators in `scripts/*.ts` and invoke them
201
199
  from package scripts before build/publish.
202
200
 
203
201
  > IMPORTANT! always use rootDir src. if there are other root level folders that should be type checked you should create other tsconfig.json files inside those folder. DO NOT add other folders inside src or the dist/ will contain dist/src, dist/other-folder. which breaks imports. the tsconfig.json inside these other folders can be minimal, using noEmit true, declaration false. Because usually these folders do not need to be emitted or compiled. just type checked. tests should still be put inside src. other folders can be things like `scripts` or `fixtures`.
204
202
 
203
+ ## Relative imports
204
+
205
+ Always write `.ts` or `.tsx` in relative source imports. Never write `.js`.
206
+
207
+ TypeScript 5.7+ rewrites those extensions to `.js` in `dist/` when
208
+ `rewriteRelativeImportExtensions` is on. Source then runs as-is in `tsx`,
209
+ `bun`, Next.js, and Node type-stripping. Published JS still resolves.
210
+
211
+ ```ts
212
+ import { helper } from "./utils.ts";
213
+ import { Button } from "./button.tsx";
214
+ ```
215
+
216
+ `tsc` emits `./utils.js` and `./button.js`. Only relative paths (`./`, `../`)
217
+ are rewritten. Path aliases, `#` imports, and bare specifiers are not. npm
218
+ packages should use relative imports anyway.
219
+
220
+ Docs: [rewriteRelativeImportExtensions](https://www.typescriptlang.org/tsconfig/#rewriteRelativeImportExtensions),
221
+ [TypeScript 5.7 path rewriting](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-7.html#path-rewriting-for-relative-paths).
222
+
205
223
  ## Preferred exports template
206
224
 
207
225
  ```json
@@ -355,7 +373,7 @@ Source of truth: [TypeScript Modules Reference — package.json "imports" and se
355
373
 
356
374
  ## tests location
357
375
 
358
- test files should be close with the associated source files. for example if you have an utils.ts file you will create utils.test.ts file next to it. with tests, importing from utils. preferred testing framework is vitest (or bun if project already using `bun test` or depends on bun APIs, rare)
376
+ test files should be close with the associated source files. for example if you have an utils.ts file you will create utils.test.ts file next to it. with tests, importing from `./utils.ts`. preferred testing framework is vitest (or bun if project already using `bun test` or depends on bun APIs, rare)
359
377
 
360
378
  ## Agent skill
361
379
 
@@ -665,8 +683,10 @@ To make peer dependencies optional, add `peerDependenciesMeta`:
665
683
 
666
684
  ## common mistakes
667
685
 
686
+ - never write `.js` in relative source imports. use `.ts` or `.tsx`. tsc rewrites them in `dist/`
668
687
  - if you need to use zod always use latest version
669
688
  - always install packages as dev dependencies if used only for scripts, testing or types only
670
- - if the package uses `rimraf` in scripts, install it as a dev dependency instead of relying on platform-specific shell behavior
689
+ - never use `rm -rf` in a package.json script. always use `rimraf`, installed as a dev dependency. `rm -rf` is not portable to Windows and zsh aborts the whole `&&` chain when a glob matches nothing
690
+ - never delete anything in `build`. deletes belong in `prepublishOnly` only
671
691
  - never use em-dashes (—) or dashes as inline separators (like `word - word`) in README or documentation. instead restructure the sentence: use periods to split into two sentences, colons, commas, or parentheses
672
692
  - never add badge images (shields.io, etc.) or any images you don't have locally in the repo. don't invent image URLs
@@ -1,6 +1,13 @@
1
1
  ---
2
2
  name: profano
3
- description: CLI tool to analyze V8 .cpuprofile files and Chrome Performance traces and print top functions by self-time or total-time in the terminal. ALWAYS load this skill when CPU profiling JavaScript or TypeScript programs (Node, Vitest, Bun, Chrome DevTools exports) — it shows how to generate .cpuprofile files and how to inspect them from the terminal without opening Chrome DevTools.
3
+ repo: remorses/profano
4
+ description: >
5
+ CLI tool to analyze V8 .cpuprofile files and Chrome Performance traces and
6
+ print top functions by self-time or total-time in the terminal. ALWAYS load
7
+ this skill when CPU profiling JavaScript or TypeScript programs (Node, Vitest,
8
+ Bun, Chrome DevTools exports, Cloudflare Workers startup or request CPU).
9
+ It shows how to generate .cpuprofile files and how to inspect them from the
10
+ terminal without opening Chrome DevTools.
4
11
  ---
5
12
 
6
13
  # profano
@@ -13,6 +20,14 @@ Every time you use profano, you MUST fetch the latest README and read it in full
13
20
  curl -s https://raw.githubusercontent.com/remorses/profano/main/README.md # NEVER pipe to head/tail, read in full
14
21
  ```
15
22
 
16
- The README covers generating `.cpuprofile` files (Node, Vitest, Bun, Chrome Performance traces, browser pages via playwriter, React component profiling), all CLI options, and how to read the output columns.
23
+ The README covers generating `.cpuprofile` files (Node, Vitest, Bun, **Cloudflare Workers** startup and request, Chrome Performance traces, browser pages via playwriter, React component profiling), all CLI options, and how to read the output columns.
17
24
 
18
25
  Chrome Performance **Save profile** writes a trace JSON. Pass it to `profano Trace.json` directly. Do not convert it first.
26
+
27
+ ## Cloudflare Workers
28
+
29
+ A Worker needs **two** profiles. Startup is `wrangler check startup`. A request is `wrangler dev --inspector-port 9230` plus `profano workers request <url>`. `wrangler tail` `cpuTime` is only a total. Never paste a one-off CDP script; use the CLI command.
30
+
31
+ Do **not** copy `dist/` to a temp folder unless `wrangler check startup` errors about two wrangler configs with different base paths. That copy is an exception, not the default.
32
+
33
+ Trust the **function mix** in a local profile. Do not treat local milliseconds as Cloudflare CPU.
@@ -64,6 +64,28 @@ Always import and use `Link` from `spiceflow/react` for navigational links in Sp
64
64
 
65
65
  **`Link` auto-prepends the Vite `base` path.** Never manually prepend the base path to `Link` href values. `<Link href="/dashboard" />` automatically renders as `<a href="/my-app/dashboard">` when the Vite base is `/my-app/`. Manually prepending causes double-prefixing. This only applies to `Link`; raw `fetch()` calls, `Response.redirect()`, and other non-Link URL construction still need manual base path handling.
66
66
 
67
+ ## `<Head>` is server-only
68
+
69
+ `<Head>` renders nothing. It records its children during the RSC render, and spiceflow reads them back to build the document head. A `'use client'` module never runs in that render, so a `<Head>` inside one contributes nothing at all.
70
+
71
+ Always put `<Head>` in the `.page()` or `.layout()` handler, never in a `'use client'` component:
72
+
73
+ ```tsx
74
+ .page('/', async () => {
75
+ return (
76
+ <>
77
+ <Head>
78
+ <Head.Title>Dashboard | My App</Head.Title>
79
+ <Head.Meta name="description" content="Your account overview." />
80
+ </Head>
81
+ <InteractiveDashboard />
82
+ </>
83
+ )
84
+ })
85
+ ```
86
+
87
+ Importing `Head` into a `'use client'` module fails the build, and rendering one throws. To change the title from the browser after load, set `document.title` in an effect instead.
88
+
67
89
  ## OpenTelemetry instrumentation
68
90
 
69
91
  Spiceflow supports automatic route instrumentation when you pass an OpenTelemetry-compatible tracer to the constructor:
@@ -150,14 +172,28 @@ export async function deleteProject(id: string) {
150
172
 
151
173
  Never assume a server action is only reachable through your own UI. Treat every server action like a public API endpoint.
152
174
 
175
+ ## Always `throw redirect(...)`, never `return redirect(...)`
176
+
177
+ Both work at runtime, but `throw` is safer: it prevents the redirect from contributing to the handler's inferred return type, avoiding circular TS7022 errors with `SpiceflowRegister`. It also short-circuits the handler immediately, making control flow explicit. This applies to `.page()`, `.layout()`, `.loader()`, `.get()`, `.post()`, server actions, and middleware.
178
+
153
179
  ## Never `router.refresh()` after server actions
154
180
 
155
181
  Successful server actions re-run matching loaders and reconcile the current page. Do not call `router.refresh()` afterward. Use it only when data changes outside a server action.
156
182
 
157
183
  `router.refresh()` is fire-and-forget. Do not await a custom refresh or navigation commit helper inside a React form action because the transition can deadlock.
158
184
 
159
- ## Router usage in app entry handlers
185
+ ## Circular types in app entry handlers
186
+
187
+ Circular TypeScript errors (TS7022) happen when any API that reads from `SpiceflowRegister` (i.e. `typeof app`) appears in a handler's **return value**. This affects `router.href()`, `router.getLoaderData()`, `createSpiceflowFetch()`, `useLoaderData()`, `Link`, and any future API typed against the registered app.
188
+
189
+ **Safe** (no circular):
190
+ - Any registered API in **JSX children, attributes, or event handlers** (`.page()`, `.layout()`)
191
+ - `throw` expressions: `throw redirect(router.href(...))` in any handler
192
+ - Any registered API in **client components, server components, and separate files**
160
193
 
161
- `router` from `spiceflow/react` is typed from the globally registered `typeof app`. Do **not** use `router` inside `.loader()`, `.get()`, `.post()`, or `.route()` handlers in the same file that initializes `export const app = new Spiceflow()`. Those handlers feed return types back into `typeof app` through loader data or typed API responses, so `router.href()` can create recursive circular TypeScript errors such as TS7022.
194
+ **Circular** (causes TS7022):
195
+ - Any registered API in a `.loader()` **return value**
196
+ - Any registered API in a `.get()` or `.post()` **return value**
197
+ - `return redirect(router.href(...))` inside `.page()` on paths **with loaders**
162
198
 
163
- `router.href()` is okay in components, other modules, and for JSX links inside `.page()` / `.layout()` handlers because rendered page/layout JSX is not part of the app metadata. If a loader-heavy app still hits a circular `typeof app` error from page/layout usage, move the link UI into a component module. Context `redirect()` intentionally accepts a plain `string`; do not pass `router.href()` into redirects inside app-entry handlers because redirect return values participate in handler return inference and can reintroduce the cycle.
199
+ The rule: circular happens when a `RegisteredApp`-typed expression reaches a **return value** that feeds `typeof app` inference. JSX, `throw`, and event handler callbacks don't feed return types. See `spiceflow/src/type-repros/registered-app-circular.test.ts` for the exact boundaries.
@@ -98,6 +98,16 @@ Rules to never break:
98
98
  - **Browser project id needs a public prefix** or the bundler will strip it.
99
99
  - In **RSC / server-rendered** apps, run browser `initStrada()` from a side-effect-only `"use client"` module rendered once in the root layout (a component that returns `null`). A bare `import` runs on the server and gets tree-shaken from the client bundle. See `website/src/docs/quickstart.mdx` for the pattern.
100
100
  - If the framework exposes an OTel tracer hook (e.g. Spiceflow `new Spiceflow({ tracer })`), pass `trace.getTracer("my-app")` from the SDK so request spans flow to the same project.
101
+ - In **Cloudflare Workers**, import `env` from `cloudflare:workers` and call
102
+ `initStrada()` at module scope. Never initialize in per-request middleware.
103
+ See the Cloudflare Workers section of `website/src/sdk/README.mdx`.
104
+ - **Email and user id belong on errors, logs, and product events.** That is how
105
+ you open an issue and see who hit it. Use `strataBetterAuth()` (default
106
+ `includeUserDetails: true`), `identifyUser()`, or `tags` / event properties
107
+ with `user.id` and `user.email`. Do not strip them.
108
+ - Never attach API keys, prompts, or raw user content (the text they typed)
109
+ to tags or events. Use stable route, handler, service, and environment
110
+ identifiers for those.
101
111
 
102
112
  ## Terminal UI
103
113
 
@@ -233,4 +243,33 @@ FROM otel_traces
233
243
  WHERE Duration > 1000000000
234
244
  ORDER BY Duration DESC
235
245
  LIMIT 20
246
+
247
+ -- HTTP route performance: top routes by RPS and latency
248
+ -- ParentSpanId = '' filters to root spans (one per HTTP request)
249
+ -- Duration is nanoseconds, divide by 1e6 for milliseconds
250
+ SELECT
251
+ SpanName AS path,
252
+ count() AS total_requests,
253
+ round(count() / 3600, 2) AS rps,
254
+ round(avg(Duration) / 1e6, 1) AS avg_ms,
255
+ round(quantile(0.95)(Duration) / 1e6, 1) AS p95_ms,
256
+ round(max(Duration) / 1e6, 1) AS max_ms
257
+ FROM otel_traces
258
+ WHERE Timestamp >= now() - INTERVAL 1 HOUR
259
+ AND ParentSpanId = ''
260
+ GROUP BY path
261
+ ORDER BY total_requests DESC
262
+ LIMIT 30
263
+
264
+ -- Per-minute traffic pattern for a specific route
265
+ SELECT
266
+ toStartOfMinute(Timestamp) AS minute,
267
+ count() AS requests
268
+ FROM otel_traces
269
+ WHERE Timestamp >= now() - INTERVAL 1 HOUR
270
+ AND ParentSpanId = ''
271
+ AND SpanName = 'POST /api/my-route'
272
+ GROUP BY minute
273
+ ORDER BY minute DESC
274
+ LIMIT 60
236
275
  ```
@@ -60,3 +60,22 @@ The README and `zele --help` output are the source of truth for commands, option
60
60
  zele mail watch --filter "is:unread from:bob@example.com subject:Re:Question" --timeout 600
61
61
  ```
62
62
  If the matched email wasn't the expected one, call `zele mail watch` again with a more specific filter. Exit code 0 means a match was found, exit code 1 means timeout.
63
+ 8. **Check reply recipients before sending** with `zele mail reply <thread-id> --dry-run`. Recipients are inferred from the thread, not from the sender of the last message, so a thread whose last message you sent still replies to the other person. If a reply would only reach the account's own address, zele refuses to send:
64
+ ```bash
65
+ # see to / cc / subject / In-Reply-To without sending
66
+ zele mail reply <thread-id> --dry-run
67
+
68
+ # override the inferred recipient
69
+ zele mail reply <thread-id> --to paul@acme.com --body "..."
70
+
71
+ # deliberately reply to yourself (normally refused)
72
+ zele mail reply <thread-id> --allow-self --body "..."
73
+ ```
74
+ Never work around a `SelfRecipientError` by switching to `zele mail send`; pass `--to` to `zele mail reply` instead, so threading headers stay correct.
75
+ 9. **Read a thread before replying.** `zele mail reply` and `zele mail send --thread-id` fail with `UnseenLatestError` unless `zele mail read <thread-id>` already showed the live last message. `mail watch` and `mail list` do **not** count. If a new reply arrives after you read, read again before sending:
76
+ ```bash
77
+ zele mail read <thread-id>
78
+ zele mail reply <thread-id> --body "..."
79
+ ```
80
+ Never pass `--force` to skip this unless the user explicitly asks to send without reading.
81
+ 10. **Send into an existing thread** with `zele mail send --thread-id <thread-id>` when you need full control of recipients and subject but still want correct `In-Reply-To`/`References` headers. Recipients and subject are inferred from the thread when omitted. The same read-before-reply rule applies.