ts-procedures 10.1.2 → 10.2.1

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 (272) hide show
  1. package/CHANGELOG.md +33 -86
  2. package/README.md +11 -6
  3. package/agent_config/bin/postinstall.mjs +20 -89
  4. package/agent_config/bin/setup.mjs +51 -264
  5. package/agent_config/lib/install-skills.mjs +108 -0
  6. package/agent_config/{claude-code/skills → skills}/ts-procedures/anti-patterns.md +8 -0
  7. package/agent_config/{claude-code/skills → skills}/ts-procedures/api-reference.md +25 -22
  8. package/agent_config/{claude-code/skills → skills}/ts-procedures/patterns.md +24 -13
  9. package/build/adapters/hono/envelope-parity.test.js +6 -17
  10. package/build/adapters/hono/envelope-parity.test.js.map +1 -1
  11. package/build/adapters/hono/handlers/http-stream.d.ts +2 -1
  12. package/build/adapters/hono/handlers/http-stream.js +9 -2
  13. package/build/adapters/hono/handlers/http-stream.js.map +1 -1
  14. package/build/adapters/hono/handlers/http.d.ts +2 -1
  15. package/build/adapters/hono/handlers/http.js +9 -2
  16. package/build/adapters/hono/handlers/http.js.map +1 -1
  17. package/build/adapters/hono/handlers/rpc.d.ts +2 -1
  18. package/build/adapters/hono/handlers/rpc.js +9 -2
  19. package/build/adapters/hono/handlers/rpc.js.map +1 -1
  20. package/build/adapters/hono/handlers/stream.d.ts +2 -1
  21. package/build/adapters/hono/handlers/stream.js +9 -2
  22. package/build/adapters/hono/handlers/stream.js.map +1 -1
  23. package/build/adapters/hono/index.d.ts +14 -7
  24. package/build/adapters/hono/index.js +45 -5
  25. package/build/adapters/hono/index.js.map +1 -1
  26. package/build/adapters/hono/index.test.js +55 -0
  27. package/build/adapters/hono/index.test.js.map +1 -1
  28. package/build/adapters/hono/request.d.ts +7 -0
  29. package/build/adapters/hono/request.js +15 -0
  30. package/build/adapters/hono/request.js.map +1 -1
  31. package/build/adapters/hono/types.d.ts +31 -0
  32. package/build/adapters/hono/types.js.map +1 -1
  33. package/build/codegen/bin/cli.js +1 -1
  34. package/build/codegen/bin/cli.js.map +1 -1
  35. package/build/codegen/constants.js +2 -2
  36. package/build/codegen/constants.js.map +1 -1
  37. package/build/codegen/emit/api-route.js +1 -1
  38. package/build/codegen/emit/api-route.js.map +1 -1
  39. package/build/codegen/emit/scope-file.js +1 -1
  40. package/build/codegen/emit/scope-file.js.map +1 -1
  41. package/build/codegen/emit-errors.integration.test.js +2 -2
  42. package/build/codegen/emit-errors.integration.test.js.map +1 -1
  43. package/build/codegen/resolve-envelope.js +1 -1
  44. package/build/codegen/resolve-envelope.js.map +1 -1
  45. package/build/codegen/test-helpers/run-tsc.js +1 -1
  46. package/build/codegen/test-helpers/run-tsc.js.map +1 -1
  47. package/build/core/create-http-stream.d.ts +3 -14
  48. package/build/core/create-http-stream.js +4 -2
  49. package/build/core/create-http-stream.js.map +1 -1
  50. package/build/core/create-http.d.ts +2 -9
  51. package/build/core/create-http.js +3 -2
  52. package/build/core/create-http.js.map +1 -1
  53. package/build/core/create-http.test-d.js +104 -0
  54. package/build/core/create-http.test-d.js.map +1 -0
  55. package/build/core/create-stream.js +6 -2
  56. package/build/core/create-stream.js.map +1 -1
  57. package/build/core/create.js +5 -4
  58. package/build/core/create.js.map +1 -1
  59. package/build/core/create.test.js +63 -0
  60. package/build/core/create.test.js.map +1 -1
  61. package/build/core/internal.d.ts +16 -0
  62. package/build/core/internal.js +22 -0
  63. package/build/core/internal.js.map +1 -1
  64. package/build/core/procedures.d.ts +9 -16
  65. package/build/core/procedures.js +4 -0
  66. package/build/core/procedures.js.map +1 -1
  67. package/build/core/types.d.ts +85 -4
  68. package/build/errors.d.ts +33 -0
  69. package/build/errors.js +91 -0
  70. package/build/errors.js.map +1 -0
  71. package/build/errors.test.d.ts +1 -0
  72. package/build/errors.test.js +122 -0
  73. package/build/errors.test.js.map +1 -0
  74. package/build/exports.d.ts +1 -2
  75. package/build/exports.js +0 -4
  76. package/build/exports.js.map +1 -1
  77. package/build/implementations/http/doc-registry.d.ts +28 -0
  78. package/build/implementations/http/doc-registry.js +96 -0
  79. package/build/implementations/http/doc-registry.js.map +1 -0
  80. package/build/implementations/http/doc-registry.test.d.ts +1 -0
  81. package/build/implementations/http/doc-registry.test.js +516 -0
  82. package/build/implementations/http/doc-registry.test.js.map +1 -0
  83. package/build/implementations/http/error-taxonomy.d.ts +249 -0
  84. package/build/implementations/http/error-taxonomy.js +252 -0
  85. package/build/implementations/http/error-taxonomy.js.map +1 -0
  86. package/build/implementations/http/error-taxonomy.test.d.ts +1 -0
  87. package/build/implementations/http/error-taxonomy.test.js +399 -0
  88. package/build/implementations/http/error-taxonomy.test.js.map +1 -0
  89. package/build/implementations/http/express-rpc/error-taxonomy.test.d.ts +1 -0
  90. package/build/implementations/http/express-rpc/error-taxonomy.test.js +83 -0
  91. package/build/implementations/http/express-rpc/error-taxonomy.test.js.map +1 -0
  92. package/build/implementations/http/express-rpc/index.d.ts +125 -0
  93. package/build/implementations/http/express-rpc/index.js +216 -0
  94. package/build/implementations/http/express-rpc/index.js.map +1 -0
  95. package/build/implementations/http/express-rpc/index.test.d.ts +1 -0
  96. package/build/implementations/http/express-rpc/index.test.js +684 -0
  97. package/build/implementations/http/express-rpc/index.test.js.map +1 -0
  98. package/build/implementations/http/express-rpc/types.d.ts +11 -0
  99. package/build/implementations/http/express-rpc/types.js +2 -0
  100. package/build/implementations/http/express-rpc/types.js.map +1 -0
  101. package/build/implementations/http/hono-api/error-taxonomy.test.d.ts +1 -0
  102. package/build/implementations/http/hono-api/error-taxonomy.test.js +137 -0
  103. package/build/implementations/http/hono-api/error-taxonomy.test.js.map +1 -0
  104. package/build/implementations/http/hono-api/index.d.ts +151 -0
  105. package/build/implementations/http/hono-api/index.js +344 -0
  106. package/build/implementations/http/hono-api/index.js.map +1 -0
  107. package/build/implementations/http/hono-api/index.test.d.ts +1 -0
  108. package/build/implementations/http/hono-api/index.test.js +992 -0
  109. package/build/implementations/http/hono-api/index.test.js.map +1 -0
  110. package/build/implementations/http/hono-api/types.d.ts +13 -0
  111. package/build/implementations/http/hono-api/types.js +2 -0
  112. package/build/implementations/http/hono-api/types.js.map +1 -0
  113. package/build/implementations/http/hono-rpc/error-taxonomy.test.d.ts +1 -0
  114. package/build/implementations/http/hono-rpc/error-taxonomy.test.js +64 -0
  115. package/build/implementations/http/hono-rpc/error-taxonomy.test.js.map +1 -0
  116. package/build/implementations/http/hono-rpc/index.d.ts +130 -0
  117. package/build/implementations/http/hono-rpc/index.js +209 -0
  118. package/build/implementations/http/hono-rpc/index.js.map +1 -0
  119. package/build/implementations/http/hono-rpc/index.test.d.ts +1 -0
  120. package/build/implementations/http/hono-rpc/index.test.js +828 -0
  121. package/build/implementations/http/hono-rpc/index.test.js.map +1 -0
  122. package/build/implementations/http/hono-rpc/types.d.ts +11 -0
  123. package/build/implementations/http/hono-rpc/types.js +2 -0
  124. package/build/implementations/http/hono-rpc/types.js.map +1 -0
  125. package/build/implementations/http/hono-stream/error-taxonomy.test.d.ts +1 -0
  126. package/build/implementations/http/hono-stream/error-taxonomy.test.js +159 -0
  127. package/build/implementations/http/hono-stream/error-taxonomy.test.js.map +1 -0
  128. package/build/implementations/http/hono-stream/index.d.ts +171 -0
  129. package/build/implementations/http/hono-stream/index.js +415 -0
  130. package/build/implementations/http/hono-stream/index.js.map +1 -0
  131. package/build/implementations/http/hono-stream/index.test.d.ts +1 -0
  132. package/build/implementations/http/hono-stream/index.test.js +1383 -0
  133. package/build/implementations/http/hono-stream/index.test.js.map +1 -0
  134. package/build/implementations/http/hono-stream/types.d.ts +15 -0
  135. package/build/implementations/http/hono-stream/types.js +2 -0
  136. package/build/implementations/http/hono-stream/types.js.map +1 -0
  137. package/build/implementations/http/on-request-error.test.d.ts +1 -0
  138. package/build/implementations/http/on-request-error.test.js +173 -0
  139. package/build/implementations/http/on-request-error.test.js.map +1 -0
  140. package/build/implementations/http/route-errors.test.d.ts +1 -0
  141. package/build/implementations/http/route-errors.test.js +139 -0
  142. package/build/implementations/http/route-errors.test.js.map +1 -0
  143. package/build/implementations/types.d.ts +192 -0
  144. package/build/implementations/types.js +2 -0
  145. package/build/implementations/types.js.map +1 -0
  146. package/build/index.d.ts +165 -0
  147. package/build/index.js +258 -0
  148. package/build/index.js.map +1 -0
  149. package/build/index.test.d.ts +1 -0
  150. package/build/index.test.js +920 -0
  151. package/build/index.test.js.map +1 -0
  152. package/build/schema/compute-schema.js +1 -5
  153. package/build/schema/compute-schema.js.map +1 -1
  154. package/build/schema/compute-schema.test.js +0 -13
  155. package/build/schema/compute-schema.test.js.map +1 -1
  156. package/build/schema/extract-json-schema.d.ts +2 -0
  157. package/build/schema/extract-json-schema.js +12 -0
  158. package/build/schema/extract-json-schema.js.map +1 -0
  159. package/build/schema/extract-json-schema.test.d.ts +1 -0
  160. package/build/schema/extract-json-schema.test.js +23 -0
  161. package/build/schema/extract-json-schema.test.js.map +1 -0
  162. package/build/schema/parser.d.ts +28 -0
  163. package/build/schema/parser.js +170 -0
  164. package/build/schema/parser.js.map +1 -0
  165. package/build/schema/parser.test.d.ts +1 -0
  166. package/build/schema/parser.test.js +120 -0
  167. package/build/schema/parser.test.js.map +1 -0
  168. package/build/schema/resolve-schema-lib.d.ts +12 -0
  169. package/build/schema/resolve-schema-lib.js +11 -0
  170. package/build/schema/resolve-schema-lib.js.map +1 -0
  171. package/build/schema/resolve-schema-lib.test.d.ts +1 -0
  172. package/build/schema/resolve-schema-lib.test.js +17 -0
  173. package/build/schema/resolve-schema-lib.test.js.map +1 -0
  174. package/build/schema/typebox.d.ts +1 -1
  175. package/build/schema/typebox.js +2 -20
  176. package/build/schema/typebox.js.map +1 -1
  177. package/build/schema/typebox.test.js +0 -10
  178. package/build/schema/typebox.test.js.map +1 -1
  179. package/build/schema/types.d.ts +8 -0
  180. package/build/schema/types.js +2 -0
  181. package/build/schema/types.js.map +1 -0
  182. package/build/server/context.d.ts +12 -2
  183. package/build/server/context.js +49 -4
  184. package/build/server/context.js.map +1 -1
  185. package/build/server/context.test.js +32 -1
  186. package/build/server/context.test.js.map +1 -1
  187. package/build/server/doc-registry.js +0 -3
  188. package/build/server/doc-registry.js.map +1 -1
  189. package/build/server/doc-registry.test.js +0 -31
  190. package/build/server/doc-registry.test.js.map +1 -1
  191. package/build/server/index.d.ts +0 -1
  192. package/build/server/index.js +0 -1
  193. package/build/server/index.js.map +1 -1
  194. package/build/server/types.d.ts +6 -27
  195. package/build/stack-utils.d.ts +25 -0
  196. package/build/stack-utils.js +95 -0
  197. package/build/stack-utils.js.map +1 -0
  198. package/build/stack-utils.test.d.ts +1 -0
  199. package/build/stack-utils.test.js +80 -0
  200. package/build/stack-utils.test.js.map +1 -0
  201. package/docs/ai-agent-setup.md +19 -27
  202. package/docs/client-error-handling.md +1 -1
  203. package/docs/core.md +8 -0
  204. package/docs/http-integrations.md +42 -28
  205. package/package.json +8 -8
  206. package/src/adapters/astro/README.md +2 -0
  207. package/src/adapters/hono/envelope-parity.test.ts +6 -18
  208. package/src/adapters/hono/handlers/http-stream.ts +12 -3
  209. package/src/adapters/hono/handlers/http.ts +12 -3
  210. package/src/adapters/hono/handlers/rpc.ts +12 -3
  211. package/src/adapters/hono/handlers/stream.ts +12 -3
  212. package/src/adapters/hono/index.test.ts +70 -0
  213. package/src/adapters/hono/index.ts +53 -11
  214. package/src/adapters/hono/request.ts +18 -0
  215. package/src/adapters/hono/types.ts +37 -0
  216. package/src/codegen/bin/cli.ts +1 -1
  217. package/src/codegen/constants.ts +2 -2
  218. package/src/codegen/emit/api-route.ts +1 -1
  219. package/src/codegen/emit/scope-file.ts +1 -2
  220. package/src/codegen/emit-errors.integration.test.ts +2 -2
  221. package/src/codegen/resolve-envelope.ts +1 -1
  222. package/src/codegen/test-helpers/run-tsc.ts +0 -1
  223. package/src/core/create-http-stream.ts +20 -13
  224. package/src/core/create-http.test-d.ts +154 -0
  225. package/src/core/create-http.ts +19 -8
  226. package/src/core/create-stream.ts +12 -4
  227. package/src/core/create.test.ts +74 -0
  228. package/src/core/create.ts +10 -5
  229. package/src/core/internal.ts +35 -0
  230. package/src/core/procedures.ts +4 -0
  231. package/src/core/types.ts +99 -4
  232. package/src/exports.ts +6 -4
  233. package/src/schema/compute-schema.test.ts +0 -16
  234. package/src/schema/compute-schema.ts +1 -5
  235. package/src/schema/typebox.test.ts +0 -12
  236. package/src/schema/typebox.ts +2 -21
  237. package/src/server/context.test.ts +39 -1
  238. package/src/server/context.ts +54 -4
  239. package/src/server/doc-registry.test.ts +0 -34
  240. package/src/server/doc-registry.ts +0 -3
  241. package/src/server/index.ts +0 -1
  242. package/src/server/types.ts +8 -27
  243. package/agent_config/claude-code/.claude-plugin/plugin.json +0 -5
  244. package/agent_config/claude-code/agents/ts-procedures-architect.md +0 -138
  245. package/agent_config/copilot/copilot-instructions.md +0 -521
  246. package/agent_config/cursor/cursorrules +0 -521
  247. package/agent_config/lib/install-claude.mjs +0 -57
  248. package/build/schema/strip-internal-keywords.d.ts +0 -18
  249. package/build/schema/strip-internal-keywords.js +0 -33
  250. package/build/schema/strip-internal-keywords.js.map +0 -1
  251. package/build/schema/strip-internal-keywords.test.js +0 -38
  252. package/build/schema/strip-internal-keywords.test.js.map +0 -1
  253. package/build/server/spec-version.d.ts +0 -24
  254. package/build/server/spec-version.js +0 -26
  255. package/build/server/spec-version.js.map +0 -1
  256. package/build/version.d.ts +0 -9
  257. package/build/version.js +0 -11
  258. package/build/version.js.map +0 -1
  259. package/docs/doc-envelope-spec-v1.md +0 -296
  260. package/docs/doc-envelope-v1.schema.json +0 -253
  261. package/src/schema/strip-internal-keywords.test.ts +0 -43
  262. package/src/schema/strip-internal-keywords.ts +0 -36
  263. package/src/server/spec-version.ts +0 -27
  264. package/src/version.ts +0 -11
  265. /package/agent_config/{claude-code/skills → skills}/ts-procedures/SKILL.md +0 -0
  266. /package/agent_config/{claude-code/skills → skills}/ts-procedures/checklist.md +0 -0
  267. /package/agent_config/{claude-code/skills → skills}/ts-procedures/templates/astro-catchall.md +0 -0
  268. /package/agent_config/{claude-code/skills → skills}/ts-procedures/templates/client.md +0 -0
  269. /package/agent_config/{claude-code/skills → skills}/ts-procedures/templates/hono.md +0 -0
  270. /package/agent_config/{claude-code/skills → skills}/ts-procedures/templates/procedure.md +0 -0
  271. /package/agent_config/{claude-code/skills → skills}/ts-procedures/templates/stream-procedure.md +0 -0
  272. /package/build/{schema/strip-internal-keywords.test.d.ts → core/create-http.test-d.d.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -4,111 +4,58 @@ All notable changes to **ts-procedures** are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## 10.1.2 — 2026-06-24
7
+ ## Unreleased
8
8
 
9
- Fixes a registration crash and output leak when TypeBox internal markers escape
10
- as enumerable properties. Complements the detection fix in 10.1.1.
9
+ ## 10.2.1 2026-06-26
11
10
 
12
- ### Fixed
13
-
14
- - **`Type.Optional(Type.Unsafe({...}))` no longer crashes registration or leaks
15
- markers.** Most TypeBox `~`-prefixed markers are non-enumerable and stay out of
16
- the JSON Schema, but some compositions — notably `Type.Optional` wrapping
17
- `Type.Unsafe` — emit `~unsafe`/`~optional` as **enumerable** own properties.
18
- Those then (a) made strict AJV throw `unknown keyword: "~unsafe"` at
19
- registration and (b) leaked into serialized DocEnvelope/codegen output. JSON
20
- Schema extracted from any channel is now deep-stripped of `~`-prefixed keys
21
- (`stripInternalKeywords`) at the single extraction chokepoint, so both runtime
22
- validation and emitted docs/codegen are clean. Stripping by the `~`-prefix
23
- convention (not a fixed name list) stays robust against future markers.
24
-
25
- ### Notes
26
-
27
- - AJV stays strict (no `strictSchema: false`) — stripping cleans the output too
28
- and keeps AJV's unknown-keyword safety net for genuine schema typos.
29
- - 10.1.1 fixed *recognition* of a bare top-level `Type.Unsafe(...)`; this release
30
- fixes the distinct *enumerable-marker leak* in nested compositions. Schemas
31
- built only from `Type.Object`/primitives were never affected by either.
32
-
33
- ## 10.1.1 — 2026-06-24
11
+ ### Changed
34
12
 
35
- Fixes TypeBox schema detection for `Type.Unsafe(...)` on typebox 1.2.0. No
36
- change to generated output, docs, or validation behavior.
13
+ - **AI agent config is now a single spec-compliant [Agent Skill](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills).** Replaced the per-tool config directories (`claude-code/`, `cursor/`, `copilot/`) with one source of truth at `agent_config/skills/ts-procedures/`. `npx ts-procedures-setup` now installs that one skill into the standard agent roots — `.agents/skills/ts-procedures/` (read by Codex, Cursor, GitHub Copilot, Gemini CLI, and 30+ tools) and `.claude/skills/ts-procedures/` (Claude Code native) — eliminating the hand-synced duplicate `.cursorrules` / `.github/copilot-instructions.md` files and their marker-merge machinery.
14
+ - `ts-procedures-setup` CLI simplified to `--dry-run` / `--check` / `--help` (per-tool `targets` and `--force` removed; the install is a force-overwrite copy). The postinstall auto-update remains opt-in (only refreshes once the skill is already installed).
37
15
 
38
- ### Fixed
16
+ ### Removed
39
17
 
40
- - **Top-level `Type.Unsafe(...)` channel schemas are recognized again.** TypeBox
41
- 1.2.0 tags `Type.Unsafe(...)` with a `~unsafe` marker *instead of* `~kind`, so
42
- the built-in TypeBox adapter — which only looked for `~kind` — failed to detect
43
- a bare top-level `Type.Unsafe(...)` used as a channel schema (e.g.
44
- `schema: { params: Type.Unsafe(...) }`), throwing a `ProcedureRegistrationError`
45
- at registration. Detection now keys off TypeBox v1's `~`-prefix marker
46
- *convention* (via `Reflect.ownKeys`, since the markers are non-enumerable)
47
- rather than a fixed list of names, so current and future `~*` markers are
48
- recognized without further changes. `Type.Unsafe` nested inside a
49
- `Type.Object(...)` was never affected.
18
+ - The Claude-only `ts-procedures-architect` agent and the Claude Code plugin manifest (`.claude-plugin/plugin.json`). The unified `ts-procedures` skill covers reference, scaffold, and review modes.
19
+ - Downstream cleanup note: a previously installed `.claude/agents/ts-procedures-architect.md` is now orphaned and can be deleted manually.
50
20
 
51
- ### Notes
21
+ ## 10.2.0 — 2026-06-25
52
22
 
53
- - Serialized JSON Schema was never affected: TypeBox's `~` markers are
54
- non-enumerable, so they never reached `JSON.stringify` (DocEnvelope, codegen)
55
- or AJV. The frozen codegen goldens and DocEnvelope parity fixtures are
56
- unchanged. App devs only need to upgrade; no code changes required.
23
+ ### Added
57
24
 
58
- ## 10.1.0 2026-06-24
25
+ - **Factory middleware via `Procedures({ middleware })`.** Declare middleware at the factory level that runs with the *final* resolved context (your `TContext` + injected `error()` + `signal`) and the validated incoming call data. Enables "parse the incoming call" patterns for authorization, tenancy enforcement, enrichment, etc.
59
26
 
60
- Adds explicit versioning to the DocEnvelope so downstream consumers (docs
61
- viewers, codegen, dashboards) can detect the wire format and stay forward
62
- compatible. Purely additive existing envelopes and consumers are unaffected.
27
+ ```ts
28
+ const Protected = Procedures<Ctx>({
29
+ middleware: [({ ctx, input, next }) => {
30
+ // ctx is fully resolved; input is schema-validated
31
+ if (input && 'orgId' in input && !ctx.user.tenants.includes(input.orgId)) {
32
+ throw ctx.error('tenant violation')
33
+ }
34
+ return next({ ctx: { ...ctx, checked: true } }) // optional augmentation
35
+ }]
36
+ })
37
+ ```
63
38
 
64
- ### Added
39
+ Works for `Create*` / `CreateHttp*`, direct calls, and all adapters. Complements (runs after) builder-level middleware.
65
40
 
66
- - **`DocEnvelope.specVersion`** a small monotonic integer (currently `1`)
67
- identifying the envelope wire format, emitted on every envelope produced by
68
- `DocRegistry.toJSON()` / `builder.toDocEnvelope()`. Bumped **only** when the
69
- envelope shape changes — never on routine package releases — so consumers get
70
- a stable signal to branch their parser on. An absent `specVersion` MUST be
71
- treated as `1`. Exported as `DOC_ENVELOPE_SPEC_VERSION` from `ts-procedures`
72
- and `ts-procedures/server`.
73
- - **`DocEnvelope.generatorVersion`** — the ts-procedures package version that
74
- produced the envelope (e.g. `"10.1.0"`), for telemetry/debugging only; not a
75
- parse-compatibility signal. Exported as `DOC_ENVELOPE_GENERATOR_VERSION` from
76
- `ts-procedures/server`. Strippable via the `transform` hook for servers that
77
- don't want to advertise the version on a public docs endpoint.
78
- - **DocEnvelope wire-contract docs** — `docs/doc-envelope-spec-v1.md` plus a
79
- validatable JSON Schema at `docs/doc-envelope-v1.schema.json` (filenames pinned
80
- to the envelope `specVersion`), for building third-party tools against the
81
- served envelope.
41
+ - **HonoAppBuilder middleware & context composition.** First-class support on the builder:
82
42
 
83
- ### Internal
84
-
85
- - Single source of truth for the package version (`src/version.ts`), consumed by
86
- both the codegen file header and the doc-envelope metadata.
43
+ - `new HonoAppBuilder({ middleware: [mw...] })`
44
+ - `builder.use(mw)` (or `use(pattern, mw)`)
45
+ - `.register(factory, ctx, { middleware: [...], context?: extraLayer })`
87
46
 
88
- ## 10.0.0 2026-06-24
47
+ Added `HonoRequestMiddleware`, `RegisterOptions`, and improved server `resolveFactoryContext` / `composeContextInits` to support arrays of context initializers (left-to-right shallow merge).
89
48
 
90
- A maintenance major: the toolchain and supported peer ranges move up a major
91
- across the board. There are **no source API changes** — every public export,
92
- the codegen output (byte-identical against the frozen goldens), the client
93
- runtime, and the DocEnvelope shape are unchanged. The version is bumped to major
94
- purely to signal the dependency floor shift below.
49
+ - Updated documentation in `docs/core.md`, `docs/http-integrations.md`, `README.md`, and agent guidance to cover both layers and the recommended patterns (replacing manual `app.use` + stashing for protected surfaces).
95
50
 
96
51
  ### Changed
97
52
 
98
- - **Toolchain upgraded a major.** `typescript` 5 6, `eslint` & `@eslint/js`
99
- 9 → 10, `globals` 15 → 17. The full gate (typecheck + 1251 tests including the
100
- frozen codegen goldens + lint) passes on the new versions. The only code touched
101
- was lint conformance for ESLint 10's newly-recommended rules
102
- (`preserve-caught-error`, `no-useless-assignment`) — internal, no behavior change.
103
- - **Astro optional peer: 6 → 7.** The optional peer range remains
104
- `6.x.x || 7.x.x`, so existing Astro 6 consumers are unaffected; Astro 7 is now
105
- tested and supported.
53
+ - Agent config now recommends "Prefer middleware + layered context for access levels" instead of always using separate factories.
106
54
 
107
- ### Migration
55
+ ## 9.3.0 — 2026-06-25
108
56
 
109
- Nothing to do for consumers staying on their current peer versions. If you build
110
- against this package's types with a local TypeScript, note the dev/CI toolchain
111
- now targets TypeScript 6.
57
+ ### Improved
58
+ - **DX: autocomplete for HTTP procedure schema channels.** `CreateHttp` and `CreateHttpStream` now surface `THttpReqInput` (and its alias `APIInput`) / `THttpResInput` inside `TCreateHttpConfig` so IDEs offer `pathParams | query | body | headers` (and `body | headers` for res) when writing `schema: { req: { ... }, res: { ... } }`. A small `HttpSchemaInput` helper keeps the public bag generics (`TReq`/`TRes`) while making literal keys visible. `satisfies APIInput` continues to work for extra checking. Same shapes are reused by `CreateHttpStream` for consistency.
112
59
 
113
60
  ## 9.2.0 — 2026-06-19
114
61
 
package/README.md CHANGED
@@ -155,6 +155,8 @@ for await (const event of api.users.WatchUsers({})) { ... } // TypedStream
155
155
  still fail fast at registration).
156
156
  - Plug in another schema library with a 3-line `SchemaAdapter`
157
157
  (`Procedures({ schema: { adapters: [zodAdapter] } })`).
158
+ - Factory middleware (ctx + input aware, runs for all procedures):
159
+ `Procedures({ middleware: [({ ctx, input, next }) => ...] })`.
158
160
 
159
161
  ## Streaming
160
162
 
@@ -189,17 +191,20 @@ implementation.
189
191
 
190
192
  ## AI assistant setup
191
193
 
192
- The package ships rules and skills for Claude Code, Cursor, and GitHub
193
- Copilot so your AI tooling knows the framework's patterns and footguns:
194
+ The package ships a single spec-compliant [Agent Skill](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills)
195
+ so your AI tooling knows the framework's patterns and footguns. One skill is
196
+ installed into the standard agent directories (`.agents/skills/` — read by
197
+ Codex, Cursor, Copilot, Gemini CLI and 30+ others — plus `.claude/skills/` for
198
+ Claude Code):
194
199
 
195
200
  ```bash
196
- npx ts-procedures-setup # all targets
197
- npx ts-procedures-setup claude # Claude Code only
201
+ npx ts-procedures-setup # install the skill
198
202
  npx ts-procedures-setup --dry-run # preview
203
+ npx ts-procedures-setup --check # CI gate: fail if outdated
199
204
  ```
200
205
 
201
- Installed files auto-update on subsequent `npm install` via the package's
202
- postinstall hook.
206
+ The installed skill auto-updates on subsequent `npm install` via the package's
207
+ postinstall hook. See [docs/ai-agent-setup.md](docs/ai-agent-setup.md).
203
208
 
204
209
  ## Migrating from v8
205
210
 
@@ -1,105 +1,36 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { readFileSync, writeFileSync, existsSync } from 'node:fs';
4
- import { resolve, join, dirname } from 'node:path';
3
+ import { resolve, dirname } from 'node:path';
5
4
  import { fileURLToPath } from 'node:url';
5
+ import { installSkills, isInstalled } from '../lib/install-skills.mjs';
6
6
 
7
7
  const __filename = fileURLToPath(import.meta.url);
8
8
  const __dirname = dirname(__filename);
9
- const AGENT_CONFIG_DIR = resolve(__dirname, '..');
10
9
 
11
- const MARKER_BEGIN = '<!-- BEGIN ts-procedures -->';
12
- const MARKER_END = '<!-- END ts-procedures -->';
10
+ // ─── Per-project config (keep in sync with bin/setup.mjs) ────────────────────
11
+ const CONFIG = {
12
+ packageName: 'ts-procedures',
13
+ sourceDir: resolve(__dirname, '..', 'skills'),
14
+ skills: ['ts-procedures'],
15
+ targetRoots: ['.agents/skills', '.claude/skills'],
16
+ };
13
17
 
14
- // INIT_CWD is set by npm/yarn/pnpm during lifecycle scripts — points to the project root
18
+ // INIT_CWD is set by npm/yarn/pnpm during lifecycle scripts — the project root.
15
19
  const projectRoot = process.env.INIT_CWD;
20
+ if (!projectRoot) process.exit(0);
16
21
 
17
- if (!projectRoot) {
18
- process.exit(0);
19
- }
20
-
21
- // Detect self-install (developing ts-procedures itself) — skip
22
- const ownPackageRoot = resolve(__dirname, '../..');
23
- if (resolve(projectRoot) === ownPackageRoot) {
24
- process.exit(0);
25
- }
26
-
27
- // ─── Helpers ──────────────────────────────────────────────
28
-
29
- function readSourceFile(relativePath) {
30
- return readFileSync(join(AGENT_CONFIG_DIR, relativePath), 'utf-8');
31
- }
32
-
33
- function wrapWithMarkers(content) {
34
- return `${MARKER_BEGIN}\n${content.trim()}\n${MARKER_END}`;
35
- }
36
-
37
- function replaceMarkerSection(existingContent, newContent) {
38
- const beginIdx = existingContent.indexOf(MARKER_BEGIN);
39
- const endIdx = existingContent.indexOf(MARKER_END);
40
-
41
- if (beginIdx !== -1 && endIdx !== -1) {
42
- const before = existingContent.slice(0, beginIdx);
43
- const after = existingContent.slice(endIdx + MARKER_END.length);
44
- return before + wrapWithMarkers(newContent) + after;
45
- }
46
-
47
- return null; // No existing markers — don't modify
48
- }
22
+ // Skip when developing this package itself.
23
+ if (resolve(projectRoot) === resolve(__dirname, '..', '..')) process.exit(0);
49
24
 
50
- function updateMarkedFile(targetPath, sourceRelativePath) {
51
- if (!existsSync(targetPath)) return false;
25
+ const cfg = { ...CONFIG, projectRoot };
52
26
 
53
- const existing = readFileSync(targetPath, 'utf-8');
54
- if (!existing.includes(MARKER_BEGIN)) return false;
55
-
56
- const sourceContent = readSourceFile(sourceRelativePath);
57
- const updated = replaceMarkerSection(existing, sourceContent);
58
- if (updated) {
59
- writeFileSync(targetPath, updated, 'utf-8');
60
- return true;
61
- }
62
- return false;
63
- }
64
-
65
- // ─── Auto-update ──────────────────────────────────────────
66
-
67
- const claudeSkillDir = join(projectRoot, '.claude', 'skills', 'ts-procedures');
68
- const cursorFile = join(projectRoot, '.cursorrules');
69
- const copilotFile = join(projectRoot, '.github', 'copilot-instructions.md');
70
-
71
- // Check if any target was previously set up
72
- const hasAnySetup = existsSync(claudeSkillDir) || existsSync(cursorFile) || existsSync(copilotFile);
73
-
74
- if (hasAnySetup) {
27
+ // Opt-in only: refresh skills the user has already installed; never create on
28
+ // first install (rely on README / `npx <pkg>-setup`).
29
+ if (isInstalled(cfg)) {
75
30
  try {
76
- const updated = [];
77
-
78
- // Auto-update Claude Code files
79
- if (existsSync(claudeSkillDir)) {
80
- const { installClaude } = await import('../lib/install-claude.mjs');
81
- installClaude(projectRoot);
82
- updated.push('Claude Code');
83
- }
84
-
85
- // Auto-update Cursor rules (only if markers present)
86
- if (updateMarkedFile(cursorFile, 'cursor/cursorrules')) {
87
- updated.push('Cursor');
88
- }
89
-
90
- // Auto-update Copilot instructions (only if markers present)
91
- if (updateMarkedFile(copilotFile, 'copilot/copilot-instructions.md')) {
92
- updated.push('Copilot');
93
- }
94
-
95
- if (updated.length > 0) {
96
- console.log('');
97
- console.log(` ts-procedures — AI agent rules updated (${updated.join(', ')})`);
98
- console.log('');
99
- }
31
+ installSkills(cfg);
32
+ console.log(`\n ${CONFIG.packageName} — Agent Skill updated\n`);
100
33
  } catch {
101
- // Never break npm install
34
+ // Never break npm install.
102
35
  }
103
36
  }
104
-
105
- // No invitation message if not opted in — rely on README / npx ts-procedures-setup --help
@@ -1,297 +1,84 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync } from 'node:fs';
4
- import { join, dirname, resolve } from 'node:path';
3
+ import { existsSync } from 'node:fs';
4
+ import { dirname, resolve } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
- import { createInterface } from 'node:readline';
7
- import { installClaude } from '../lib/install-claude.mjs';
6
+ import { installSkills, planSkills, outdatedSkillFiles } from '../lib/install-skills.mjs';
8
7
 
9
8
  const __filename = fileURLToPath(import.meta.url);
10
9
  const __dirname = dirname(__filename);
11
- const AGENT_CONFIG_DIR = resolve(__dirname, '..');
12
- const PROJECT_DIR = process.cwd();
13
-
14
- const MARKER_BEGIN = '<!-- BEGIN ts-procedures -->';
15
- const MARKER_END = '<!-- END ts-procedures -->';
16
10
 
17
- const VALID_TARGETS = ['claude', 'cursor', 'copilot', 'all'];
11
+ // ─── Per-project config (the only thing a consuming library customizes) ──────
12
+ const CONFIG = {
13
+ packageName: 'ts-procedures',
14
+ sourceDir: resolve(__dirname, '..', 'skills'),
15
+ skills: ['ts-procedures'],
16
+ // `.agents/skills` is read by 30+ agent tools (Codex, Cursor, Copilot,
17
+ // Gemini CLI, …); `.claude/skills` is Claude Code's native project location.
18
+ // One source, fanned out to both.
19
+ targetRoots: ['.agents/skills', '.claude/skills'],
20
+ };
18
21
 
19
- // ─── Helpers ──────────────────────────────────────────────
22
+ const PROJECT_DIR = process.cwd();
20
23
 
21
24
  function printUsage() {
22
25
  console.log(`
23
- Usage: npx ts-procedures-setup [targets...] [options]
26
+ Usage: npx ${CONFIG.packageName}-setup [options]
24
27
 
25
- Targets:
26
- claude Install Claude Code skills and architect agent into .claude/
27
- cursor Copy ts-procedures rules to .cursorrules
28
- copilot Copy ts-procedures instructions to .github/copilot-instructions.md
29
- all Set up all targets (default)
28
+ Installs the ${CONFIG.packageName} Agent Skill into the standard agent
29
+ directories so any compatible AI coding tool picks it up automatically:
30
+
31
+ ${CONFIG.targetRoots.map((r) => `${r}/${CONFIG.skills.join(', ')}`).join('\n ')}
30
32
 
31
33
  Options:
32
34
  --help Show this help message
33
- --force Overwrite existing files without prompting
34
35
  --dry-run Show what would be created/updated without writing files
35
- --check Exit with code 1 if any files are outdated (useful in CI)
36
+ --check Exit with code 1 if any installed files are outdated (CI gate)
36
37
 
37
- Examples:
38
- npx ts-procedures-setup # Set up all targets
39
- npx ts-procedures-setup cursor # Set up Cursor only
40
- npx ts-procedures-setup cursor copilot # Set up Cursor and Copilot
41
- npx ts-procedures-setup --force # Overwrite without prompting
42
- npx ts-procedures-setup --dry-run # Preview changes
43
- npx ts-procedures-setup --check # Verify files are up to date
38
+ The files auto-update on every \`npm install\` once installed.
44
39
  `);
45
40
  }
46
41
 
47
- function readSourceFile(relativePath) {
48
- const fullPath = join(AGENT_CONFIG_DIR, relativePath);
49
- return readFileSync(fullPath, 'utf-8');
50
- }
51
-
52
- function wrapWithMarkers(content) {
53
- return `${MARKER_BEGIN}\n${content.trim()}\n${MARKER_END}`;
54
- }
55
-
56
- function replaceMarkerSection(existingContent, newContent) {
57
- const beginIdx = existingContent.indexOf(MARKER_BEGIN);
58
- const endIdx = existingContent.indexOf(MARKER_END);
59
-
60
- if (beginIdx !== -1 && endIdx !== -1) {
61
- const before = existingContent.slice(0, beginIdx);
62
- const after = existingContent.slice(endIdx + MARKER_END.length);
63
- return before + wrapWithMarkers(newContent) + after;
64
- }
65
-
66
- const separator = existingContent.trim() ? '\n\n' : '';
67
- return existingContent.trim() + separator + wrapWithMarkers(newContent) + '\n';
68
- }
69
-
70
- async function confirm(message) {
71
- const rl = createInterface({ input: process.stdin, output: process.stdout });
72
- return new Promise((resolve) => {
73
- rl.question(`${message} (y/N) `, (answer) => {
74
- rl.close();
75
- resolve(answer.toLowerCase() === 'y');
76
- });
77
- });
78
- }
79
-
80
- // ─── Check Mode ───────────────────────────────────────────
81
-
82
- function isFileOutdated(targetPath, expectedContent) {
83
- if (!existsSync(targetPath)) return true;
84
- const current = readFileSync(targetPath, 'utf-8');
85
- return current !== expectedContent;
86
- }
87
-
88
- // ─── Target Handlers ──────────────────────────────────────
89
-
90
- function setupClaude({ dryRun, check } = {}) {
91
- if (dryRun) {
92
- // Mirror install-claude.mjs: copy every file under each source skill dir, plus the agent.
93
- const claudeFiles = [];
94
- const skillsSrc = join(AGENT_CONFIG_DIR, 'claude-code', 'skills');
95
- for (const skill of ['ts-procedures']) {
96
- const walk = (dir, prefix) => {
97
- for (const entry of readdirSync(dir)) {
98
- const full = join(dir, entry);
99
- if (statSync(full).isDirectory()) {
100
- walk(full, `${prefix}/${entry}`);
101
- } else {
102
- claudeFiles.push(`${prefix}/${entry}`);
103
- }
104
- }
105
- };
106
- walk(join(skillsSrc, skill), `.claude/skills/${skill}`);
107
- }
108
- claudeFiles.push('.claude/agents/ts-procedures-architect.md');
109
-
110
- console.log('\n Claude Code (dry run)');
111
- console.log(' ─────────────────────');
112
- for (const f of claudeFiles) {
113
- const status = existsSync(join(PROJECT_DIR, f)) ? 'update' : 'create';
114
- console.log(` [${status}] ${f}`);
115
- }
116
- console.log('');
117
- return false;
118
- }
119
-
120
- const { files } = installClaude(PROJECT_DIR);
121
-
122
- if (check) return false; // installClaude always writes — check mode handled at main level
123
-
124
- console.log('\n Claude Code');
125
- console.log(' ───────────');
126
- console.log(' Installed (updates automatically on npm install/update):\n');
127
- for (const f of files) {
128
- console.log(` ${f}`);
129
- }
130
- console.log('');
131
- console.log(' Skills: ts-procedures (use `/ts-procedures review <path>` and `/ts-procedures scaffold <type> <Name>`; Kotlin/Swift codegen reference included)');
132
- console.log(' Agent: ts-procedures-architect (architecture planning)\n');
133
- return false;
134
- }
135
-
136
- async function setupCursor(force, { dryRun, check } = {}) {
137
- const targetPath = join(PROJECT_DIR, '.cursorrules');
138
- const sourceContent = readSourceFile('cursor/cursorrules');
139
-
140
- if (dryRun || check) {
141
- const exists = existsSync(targetPath);
142
- const hasMarkers = exists && readFileSync(targetPath, 'utf-8').includes(MARKER_BEGIN);
143
- const label = dryRun ? 'dry run' : 'check';
144
- const status = !exists ? 'create' : hasMarkers ? 'update' : 'append';
145
- console.log(`\n Cursor (${label})`);
146
- console.log(` ──────`);
147
- console.log(` [${status}] .cursorrules\n`);
148
- return check && (status === 'create' || status === 'update');
149
- }
150
-
151
- console.log('\n Cursor');
152
- console.log(' ──────');
153
-
154
- if (existsSync(targetPath)) {
155
- const existing = readFileSync(targetPath, 'utf-8');
156
-
157
- if (existing.includes(MARKER_BEGIN)) {
158
- const updated = replaceMarkerSection(existing, sourceContent);
159
- writeFileSync(targetPath, updated, 'utf-8');
160
- console.log(' Updated ts-procedures section in .cursorrules\n');
161
- return false;
162
- }
163
-
164
- if (!force) {
165
- const ok = await confirm(' .cursorrules already exists. Append ts-procedures rules?');
166
- if (!ok) {
167
- console.log(' Skipped.\n');
168
- return false;
169
- }
170
- }
171
-
172
- const updated = replaceMarkerSection(existing, sourceContent);
173
- writeFileSync(targetPath, updated, 'utf-8');
174
- console.log(' Appended ts-procedures rules to .cursorrules\n');
175
- } else {
176
- writeFileSync(targetPath, wrapWithMarkers(sourceContent) + '\n', 'utf-8');
177
- console.log(' Created .cursorrules\n');
178
- }
179
- return false;
180
- }
181
-
182
- async function setupCopilot(force, { dryRun, check } = {}) {
183
- const githubDir = join(PROJECT_DIR, '.github');
184
- const targetPath = join(githubDir, 'copilot-instructions.md');
185
- const sourceContent = readSourceFile('copilot/copilot-instructions.md');
186
-
187
- if (dryRun || check) {
188
- const exists = existsSync(targetPath);
189
- const hasMarkers = exists && readFileSync(targetPath, 'utf-8').includes(MARKER_BEGIN);
190
- const label = dryRun ? 'dry run' : 'check';
191
- const status = !exists ? 'create' : hasMarkers ? 'update' : 'append';
192
- console.log(`\n GitHub Copilot (${label})`);
193
- console.log(` ──────────────`);
194
- console.log(` [${status}] .github/copilot-instructions.md\n`);
195
- return check && (status === 'create' || status === 'update');
196
- }
197
-
198
- console.log('\n GitHub Copilot');
199
- console.log(' ──────────────');
200
-
201
- if (!existsSync(githubDir)) {
202
- mkdirSync(githubDir, { recursive: true });
203
- }
204
-
205
- if (existsSync(targetPath)) {
206
- const existing = readFileSync(targetPath, 'utf-8');
207
-
208
- if (existing.includes(MARKER_BEGIN)) {
209
- const updated = replaceMarkerSection(existing, sourceContent);
210
- writeFileSync(targetPath, updated, 'utf-8');
211
- console.log(' Updated ts-procedures section in .github/copilot-instructions.md\n');
212
- return false;
213
- }
214
-
215
- if (!force) {
216
- const ok = await confirm(' .github/copilot-instructions.md already exists. Append ts-procedures instructions?');
217
- if (!ok) {
218
- console.log(' Skipped.\n');
219
- return false;
220
- }
221
- }
222
-
223
- const updated = replaceMarkerSection(existing, sourceContent);
224
- writeFileSync(targetPath, updated, 'utf-8');
225
- console.log(' Appended ts-procedures instructions to .github/copilot-instructions.md\n');
226
- } else {
227
- writeFileSync(targetPath, wrapWithMarkers(sourceContent) + '\n', 'utf-8');
228
- console.log(' Created .github/copilot-instructions.md\n');
229
- }
230
- return false;
231
- }
232
-
233
- // ─── Main ─────────────────────────────────────────────────
234
-
235
- async function main() {
42
+ function main() {
236
43
  const args = process.argv.slice(2);
237
- const force = args.includes('--force');
238
- const dryRun = args.includes('--dry-run');
239
- const check = args.includes('--check');
240
- const help = args.includes('--help') || args.includes('-h');
241
- const targets = args.filter(a => !a.startsWith('--') && !a.startsWith('-'));
242
-
243
- if (help) {
44
+ if (args.includes('--help') || args.includes('-h')) {
244
45
  printUsage();
245
46
  process.exit(0);
246
47
  }
247
-
248
- for (const t of targets) {
249
- if (!VALID_TARGETS.includes(t)) {
250
- console.error(`Unknown target: "${t}". Valid targets: ${VALID_TARGETS.join(', ')}`);
251
- process.exit(1);
252
- }
253
- }
254
-
255
- const selectedTargets = targets.length === 0 || targets.includes('all')
256
- ? ['claude', 'cursor', 'copilot']
257
- : targets;
48
+ const dryRun = args.includes('--dry-run');
49
+ const check = args.includes('--check');
50
+ const cfg = { ...CONFIG, projectRoot: PROJECT_DIR };
258
51
 
259
52
  const mode = dryRun ? ' (dry run)' : check ? ' (check)' : '';
260
- console.log(`\nts-procedures AI Agent Setup${mode}`);
261
- console.log('════════════════════════════');
262
-
263
- const opts = { dryRun, check };
264
- let outdated = false;
265
-
266
- if (selectedTargets.includes('claude')) {
267
- outdated = setupClaude(opts) || outdated;
268
- }
269
-
270
- if (selectedTargets.includes('cursor')) {
271
- outdated = (await setupCursor(force, opts)) || outdated;
272
- }
273
-
274
- if (selectedTargets.includes('copilot')) {
275
- outdated = (await setupCopilot(force, opts)) || outdated;
276
- }
53
+ console.log(`\n${CONFIG.packageName} Agent Skill setup${mode}`);
54
+ console.log(''.repeat(40));
277
55
 
278
56
  if (check) {
279
- if (outdated) {
280
- console.log(' Files are outdated. Run: npx ts-procedures-setup\n');
57
+ const outdated = outdatedSkillFiles(cfg);
58
+ if (outdated.length) {
59
+ console.log('\n Outdated or missing:');
60
+ for (const f of outdated) console.log(` ${f}`);
61
+ console.log(`\n Run: npx ${CONFIG.packageName}-setup\n`);
281
62
  process.exit(1);
282
- } else {
283
- console.log(' All files are up to date.\n');
284
63
  }
285
- } else if (dryRun) {
286
- console.log(' No files were modified (dry run).\n');
287
- } else {
288
- console.log(' Tip: Auto-generated .claude/ files can be added to .gitignore');
289
- console.log(' (they are regenerated on npm install).\n');
290
- console.log('Done!\n');
64
+ console.log('\n All files are up to date.\n');
65
+ return;
66
+ }
67
+
68
+ if (dryRun) {
69
+ console.log('');
70
+ for (const { rel, dest } of planSkills(cfg)) {
71
+ console.log(` [${existsSync(dest) ? 'update' : 'create'}] ${rel}`);
72
+ }
73
+ console.log('\n No files were modified (dry run).\n');
74
+ return;
291
75
  }
76
+
77
+ const { files } = installSkills(cfg);
78
+ console.log('\n Installed (auto-updates on npm install):\n');
79
+ for (const f of files) console.log(` ${f}`);
80
+ console.log('\n Tip: these are generated — you may add the install roots to .gitignore.\n');
81
+ console.log('Done!\n');
292
82
  }
293
83
 
294
- main().catch((err) => {
295
- console.error('Error:', err.message);
296
- process.exit(1);
297
- });
84
+ main();