@mannyc1/ts-release 0.0.1 → 0.0.3

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 (162) hide show
  1. package/ARCHITECTURE.md +93 -0
  2. package/README.md +282 -49
  3. package/SPEC.md +46 -9
  4. package/dist/config/schema.d.ts +4 -0
  5. package/dist/config/schema.d.ts.map +1 -1
  6. package/dist/config/schema.js +13 -0
  7. package/dist/config/schema.js.map +1 -1
  8. package/dist/domain/evidence.d.ts +4 -0
  9. package/dist/domain/evidence.d.ts.map +1 -1
  10. package/dist/domain/evidence.js +4 -0
  11. package/dist/domain/evidence.js.map +1 -1
  12. package/dist/domain/operation.d.ts +1 -0
  13. package/dist/domain/operation.d.ts.map +1 -1
  14. package/dist/domain/operation.js +147 -1
  15. package/dist/domain/operation.js.map +1 -1
  16. package/dist/domain/release.d.ts +1 -0
  17. package/dist/domain/release.d.ts.map +1 -1
  18. package/dist/domain/release.js +1 -0
  19. package/dist/domain/release.js.map +1 -1
  20. package/dist/domain/remote-state.d.ts +90 -0
  21. package/dist/domain/remote-state.d.ts.map +1 -0
  22. package/dist/domain/remote-state.js +85 -0
  23. package/dist/domain/remote-state.js.map +1 -0
  24. package/dist/domain/status.d.ts +43 -0
  25. package/dist/domain/status.d.ts.map +1 -0
  26. package/dist/domain/status.js +50 -0
  27. package/dist/domain/status.js.map +1 -0
  28. package/dist/domain/target.d.ts +32 -2
  29. package/dist/domain/target.d.ts.map +1 -1
  30. package/dist/domain/target.js +35 -2
  31. package/dist/domain/target.js.map +1 -1
  32. package/dist/host/host.d.ts +7 -23
  33. package/dist/host/host.d.ts.map +1 -1
  34. package/dist/host/host.js +3 -22
  35. package/dist/host/host.js.map +1 -1
  36. package/dist/host/http-live.d.ts +1 -2
  37. package/dist/host/http-live.d.ts.map +1 -1
  38. package/dist/host/http-live.js +18 -9
  39. package/dist/host/http-live.js.map +1 -1
  40. package/dist/host/http.d.ts +3 -1
  41. package/dist/host/http.d.ts.map +1 -1
  42. package/dist/host/http.js +3 -1
  43. package/dist/host/http.js.map +1 -1
  44. package/dist/host/platform.d.ts +10 -0
  45. package/dist/host/platform.d.ts.map +1 -0
  46. package/dist/host/platform.js +121 -0
  47. package/dist/host/platform.js.map +1 -0
  48. package/dist/host/test.d.ts +7 -3
  49. package/dist/host/test.d.ts.map +1 -1
  50. package/dist/host/test.js +128 -25
  51. package/dist/host/test.js.map +1 -1
  52. package/dist/internal/workspace-path.d.ts +16 -0
  53. package/dist/internal/workspace-path.d.ts.map +1 -0
  54. package/dist/internal/workspace-path.js +33 -0
  55. package/dist/internal/workspace-path.js.map +1 -0
  56. package/dist/planner/artifact-inventory.d.ts +9 -0
  57. package/dist/planner/artifact-inventory.d.ts.map +1 -0
  58. package/dist/planner/artifact-inventory.js +85 -0
  59. package/dist/planner/artifact-inventory.js.map +1 -0
  60. package/dist/planner/create-release-plan.d.ts +1 -1
  61. package/dist/planner/create-release-plan.d.ts.map +1 -1
  62. package/dist/planner/errors.d.ts +37 -1
  63. package/dist/planner/errors.d.ts.map +1 -1
  64. package/dist/planner/errors.js +30 -0
  65. package/dist/planner/errors.js.map +1 -1
  66. package/dist/planner/evidence-recorder.d.ts +13 -8
  67. package/dist/planner/evidence-recorder.d.ts.map +1 -1
  68. package/dist/planner/evidence-recorder.js +116 -18
  69. package/dist/planner/evidence-recorder.js.map +1 -1
  70. package/dist/planner/executor.d.ts +31 -19
  71. package/dist/planner/executor.d.ts.map +1 -1
  72. package/dist/planner/executor.js +52 -7
  73. package/dist/planner/executor.js.map +1 -1
  74. package/dist/planner/normalize-release.d.ts +2 -2
  75. package/dist/planner/normalize-release.d.ts.map +1 -1
  76. package/dist/planner/normalize-release.js +50 -52
  77. package/dist/planner/normalize-release.js.map +1 -1
  78. package/dist/planner/reconcile.d.ts +17 -0
  79. package/dist/planner/reconcile.d.ts.map +1 -0
  80. package/dist/planner/reconcile.js +337 -0
  81. package/dist/planner/reconcile.js.map +1 -0
  82. package/dist/planner/release-eligibility.d.ts +34 -0
  83. package/dist/planner/release-eligibility.d.ts.map +1 -0
  84. package/dist/planner/release-eligibility.js +179 -0
  85. package/dist/planner/release-eligibility.js.map +1 -0
  86. package/dist/planner/render-plan.d.ts +10 -0
  87. package/dist/planner/render-plan.d.ts.map +1 -1
  88. package/dist/planner/render-plan.js +179 -0
  89. package/dist/planner/render-plan.js.map +1 -1
  90. package/dist/planner/status.d.ts +40 -0
  91. package/dist/planner/status.d.ts.map +1 -0
  92. package/dist/planner/status.js +342 -0
  93. package/dist/planner/status.js.map +1 -0
  94. package/dist/targets/github.d.ts.map +1 -1
  95. package/dist/targets/github.js +28 -13
  96. package/dist/targets/github.js.map +1 -1
  97. package/dist/targets/npm.d.ts +2 -2
  98. package/dist/targets/npm.d.ts.map +1 -1
  99. package/dist/targets/npm.js +74 -14
  100. package/dist/targets/npm.js.map +1 -1
  101. package/dist/workflows/config.d.ts +153 -0
  102. package/dist/workflows/config.d.ts.map +1 -0
  103. package/dist/workflows/config.js +301 -0
  104. package/dist/workflows/config.js.map +1 -0
  105. package/dist/workflows/diagnostics.d.ts +62 -0
  106. package/dist/workflows/diagnostics.d.ts.map +1 -0
  107. package/dist/workflows/diagnostics.js +474 -0
  108. package/dist/workflows/diagnostics.js.map +1 -0
  109. package/dist/workflows/evidence.d.ts +21 -0
  110. package/dist/workflows/evidence.d.ts.map +1 -0
  111. package/dist/workflows/evidence.js +45 -0
  112. package/dist/workflows/evidence.js.map +1 -0
  113. package/dist/workflows/index.d.ts +7 -0
  114. package/dist/workflows/index.d.ts.map +1 -0
  115. package/dist/workflows/index.js +6 -0
  116. package/dist/workflows/index.js.map +1 -0
  117. package/dist/workflows/init.d.ts +71 -0
  118. package/dist/workflows/init.d.ts.map +1 -0
  119. package/dist/workflows/init.js +384 -0
  120. package/dist/workflows/init.js.map +1 -0
  121. package/dist/workflows/live.d.ts +9 -0
  122. package/dist/workflows/live.d.ts.map +1 -0
  123. package/dist/workflows/live.js +7 -0
  124. package/dist/workflows/live.js.map +1 -0
  125. package/dist/workflows/options.d.ts +30 -0
  126. package/dist/workflows/options.d.ts.map +1 -0
  127. package/dist/workflows/options.js +16 -0
  128. package/dist/workflows/options.js.map +1 -0
  129. package/examples/README.md +16 -9
  130. package/examples/github-release/release.config.json +1 -0
  131. package/examples/homebrew-tap/release.config.json +1 -0
  132. package/examples/multi-target/release.config.json +10 -1
  133. package/examples/non-strict-skips/release.config.json +1 -0
  134. package/examples/npm-first-publish/release.config.json +33 -0
  135. package/examples/npm-only/release.config.json +10 -1
  136. package/examples/pypi-registry/release.config.json +1 -0
  137. package/examples/scoop-bucket/release.config.json +1 -0
  138. package/package.json +56 -27
  139. package/templates/README.md +54 -0
  140. package/templates/github-actions/plan-and-approved-execute.yml +53 -0
  141. package/templates/github-actions/plan-only.yml +30 -0
  142. package/templates/github-actions/trusted-publishing.yml +59 -0
  143. package/templates/multi-target-homebrew/release.config.json +70 -0
  144. package/templates/multi-target-scoop/release.config.json +72 -0
  145. package/templates/npm-github/release.config.json +50 -0
  146. package/templates/npm-only/release.config.json +39 -0
  147. package/dist/cli/command.d.ts +0 -4
  148. package/dist/cli/command.d.ts.map +0 -1
  149. package/dist/cli/command.js +0 -125
  150. package/dist/cli/command.js.map +0 -1
  151. package/dist/cli/main.d.ts +0 -3
  152. package/dist/cli/main.d.ts.map +0 -1
  153. package/dist/cli/main.js +0 -15
  154. package/dist/cli/main.js.map +0 -1
  155. package/dist/cli/programmatic.d.ts +0 -25
  156. package/dist/cli/programmatic.d.ts.map +0 -1
  157. package/dist/cli/programmatic.js +0 -53
  158. package/dist/cli/programmatic.js.map +0 -1
  159. package/dist/host/bun.d.ts +0 -9
  160. package/dist/host/bun.d.ts.map +0 -1
  161. package/dist/host/bun.js +0 -156
  162. package/dist/host/bun.js.map +0 -1
@@ -0,0 +1,93 @@
1
+ # Architecture
2
+
3
+ `@mannyc1/ts-release` is library-first and CLI-second.
4
+
5
+ The package turns release intent into typed data, plans, evidence, and approved operations. The CLI and GitHub Action are adapters over those workflows.
6
+
7
+ ## Target Boundary
8
+
9
+ The intended repository shape separates reusable library code from the
10
+ first-party release application:
11
+
12
+ ```text
13
+ src/ reusable TypeScript release library
14
+ apps/release-ts/ official CLI app, Bun runtime shell, and self-release dogfood
15
+ apps/ts-release-action/
16
+ official JavaScript action app and Node runtime shell
17
+ scripts/ repo-wide maintenance gates only
18
+ examples/ reusable release config examples
19
+ ```
20
+
21
+ `src/` contains generic library code only. It may require platform services
22
+ such as `FileSystem`, `Path`, `ReleaseCommandRunner`, `ReleaseHttp`, or
23
+ `HttpClient`, but it must not provide the concrete Bun runtime for the official
24
+ CLI.
25
+
26
+ `apps/release-ts/` owns argv parsing, terminal output, Bun runtime assembly,
27
+ standalone CLI compilation, and self-release policy/config. A module consumed
28
+ only by the official CLI or self-release dogfood belongs in `apps/release-ts/`
29
+ unless it is made generic and documented as public library API.
30
+
31
+ `apps/ts-release-action/` owns GitHub Action input parsing, GitHub step-summary
32
+ and output adapters, evidence artifact upload, and the Node runtime assembly
33
+ used by the bundled action. Action code should call public workflow APIs rather
34
+ than reaching into CLI modules.
35
+
36
+ ## Current Module Taxonomy
37
+
38
+ - `domain/` contains durable schema-backed data models and errors.
39
+ - `config/` parses and validates release config into domain values.
40
+ - `planner/` normalizes release intent, builds plans, renders plans, executes operation data through injected services, records evidence, reports status, resumes safe work, and reconciles remote state.
41
+ - `targets/` models ecosystem-specific target semantics and produces operation data. Target modules may describe commands and HTTP checks, but they do not execute them.
42
+ - `host/` defines injectable command and HTTP services plus live or test implementations.
43
+ - `workflows/` contains reusable application workflows over config files, init/scaffolding plans, diagnostics, evidence files, and live target/HTTP composition. This is the high-level programmatic surface.
44
+ - `apps/release-ts/src/runtime/` contains the Bun runtime shell for the official CLI app.
45
+ - `apps/release-ts/src/cli/` parses command-line flags, calls workflows, prints terminal output, and writes user-requested CLI output files.
46
+ - `apps/ts-release-action/src/runtime/` contains the Node runtime shell for the bundled GitHub Action.
47
+ - `apps/ts-release-action/src/` adapts GitHub Action inputs, outputs, step summaries, and artifact uploads to workflow calls.
48
+ - `scripts/` contains repository maintenance checks. Scripts may use app runtime layers, but they are not package library code.
49
+
50
+ ## Dependency Direction
51
+
52
+ Library modules must not import from `cli/`.
53
+
54
+ The normal flow is:
55
+
56
+ ```text
57
+ domain <- config
58
+ domain <- planner <- targets
59
+ domain <- host
60
+ workflows -> config/planner/host/targets
61
+ apps/release-ts runtime -> host/workflows/platform layers
62
+ apps/release-ts cli -> workflows/runtime boundary
63
+ apps/ts-release-action runtime -> host/workflows/platform layers
64
+ apps/ts-release-action action -> workflows/runtime boundary
65
+ ```
66
+
67
+ `src/index.ts` intentionally stays empty. Public API is the explicit subpath list in `package.json`, checked by `scripts/check-package-exports.ts` and `scripts/check-tree-shaking.ts`. The only workflow aggregate is the opt-in `./workflows` facade.
68
+
69
+ ## Public Workflow Surface
70
+
71
+ There is no public `./api` facade, and the root package export stays empty. The public workflow modules are named after the work they own:
72
+
73
+ - `./workflows` for the curated opt-in `Config`, `Init`, `Diagnostics`, `Evidence`, and `Live` namespaces.
74
+ - `./workflows/config` for config-file release workflows.
75
+ - `./workflows/init` for data-first scaffolding previews and approved writes.
76
+ - `./workflows/diagnostics` for static config, auth, and CI readiness reports.
77
+ - `./workflows/evidence` for reusable evidence persistence.
78
+ - `./workflows/live` for runtime-neutral live target and HTTP services.
79
+
80
+ The `./workflows` facade lives at `src/workflows/index.ts` and should only re-export workflow namespaces. Use exact workflow leaf imports for maximum tree-shaking or direct option-class access, and lower-level `domain/`, `config/`, `planner/`, `host/`, and `targets/` subpaths when a caller needs more control than the workflow modules provide.
81
+
82
+ ## Boundary Rules
83
+
84
+ - Publish operations are data until execution is explicitly approved.
85
+ - `Effect.run*` belongs at true runtime boundaries.
86
+ - Reusable effectful operations use `Effect.fn`; inline orchestration bodies use `Effect.gen`.
87
+ - Durable models, options, target variants, and typed errors use `Schema.Class`, `Schema.TaggedClass`, and `Schema.TaggedErrorClass`.
88
+ - Layers are provided at CLI, action, runtime, script, application, and test boundaries.
89
+ - Config parsing, evidence persistence, status, resume, reconciliation, and release execution are library workflows, not CLI behavior.
90
+ - Terminal formatting, argv parsing, and `--out` file writing belong in
91
+ `apps/release-ts/src/cli/`.
92
+ - GitHub Action input parsing, output names, step summaries, and evidence artifact
93
+ uploads belong in `apps/ts-release-action/src/`.
package/README.md CHANGED
@@ -2,93 +2,184 @@
2
2
 
3
3
  `@mannyc1/ts-release` turns release intent into explicit, inspectable, repeatable publishing operations.
4
4
 
5
+ The root package is the reusable TypeScript release library. The official Bun
6
+ CLI app lives in `apps/release-ts`, and the root `cli` script delegates to that
7
+ private first-party app.
8
+
5
9
  The default workflow is plan-first:
6
10
 
7
11
  ```sh
12
+ bun run cli validate-config --config release.config.json
8
13
  bun run cli plan --config release.config.json --format text
9
14
  bun run cli run --config release.config.json --execute --approve-irreversible
10
15
  ```
11
16
 
17
+ On GitHub Actions, the primary CI integration is the JavaScript action:
18
+
19
+ ```yaml
20
+ - uses: mannyc2/ts-release-action@v1
21
+ with:
22
+ command: plan
23
+ config: release.config.json
24
+ format: markdown
25
+ ```
26
+
27
+ The action calls the TypeScript workflow APIs directly and keeps target-native
28
+ operations visible in the plan. It defaults to `runtime: bundled`; workspace
29
+ runtime mode is deferred until a same-module-graph Node platform setup can be
30
+ required without surprising users. The source currently lives in
31
+ `apps/ts-release-action`.
32
+
33
+ The action supports the review commands `plan`, `validate-config`, `status`,
34
+ `doctor`, `check-auth`, and `check-ci`, plus the approved workflow commands
35
+ `validate`, `run`, `resume`, and `reconcile`. Use `upload-evidence: true` when
36
+ an action job should publish collected `.release/evidence` JSON files even after
37
+ a command fails.
38
+
12
39
  The `run` command is the recommended release path: it renders generated files,
13
40
  validates every preflight, executes approved publish operations, and verifies
14
41
  remote state in order. The primitive commands remain available for review and
15
42
  debug flows:
16
43
 
17
44
  ```sh
45
+ bun run cli schema --out release-config.schema.json
46
+ bun run cli init --template npm-github --package @scope/pkg --repo owner/repo
47
+ bun run cli validate-config --config release.config.json --format text
18
48
  bun run cli plan --config release.config.json --format text
49
+ bun run cli plan --config release.config.json --format summary
50
+ bun run cli explain npm:npm-publish --config release.config.json
51
+ bun run cli doctor --config release.config.json --format text
52
+ bun run cli check-auth --config release.config.json --target npm --format text
53
+ bun run cli check-ci --config release.config.json --workflow .github/workflows/release.yml --format markdown
19
54
  bun run cli render --config release.config.json --execute
20
55
  bun run cli validate --config release.config.json
21
56
  bun run cli print --config release.config.json
22
57
  bun run cli execute --config release.config.json --execute --approve-irreversible
23
58
  bun run cli verify --config release.config.json
59
+ bun run cli eligibility --config release.config.json --format text
60
+ bun run cli reconcile --config release.config.json --execute
24
61
  ```
25
62
 
26
63
  Rendering writes generated target files locally and records `render.json` evidence. `execute` is a lower-level primitive that runs publish operations only. Publishing is blocked unless execution is explicitly approved. Irreversible operations require a second approval flag.
27
64
 
65
+ Status and resume commands use existing `.release/evidence` files to report progress and continue conservative unfinished work:
66
+
67
+ ```sh
68
+ bun run cli status --config release.config.json --format text
69
+ bun run cli resume --config release.config.json --execute --approve-irreversible
70
+ ```
71
+
72
+ Resume skips operations with successful matching evidence, reruns safe read-only failures, and blocks failed publish operations until remote state is reconciled manually.
73
+ `eligibility` checks npm and GitHub remote state before the self-release workflow decides whether to run.
74
+ `reconcile` is separate from resume: it inspects GitHub release state through the API and can publish a matching draft release with explicit `--execute` without republishing immutable npm versions.
75
+
76
+ The executable is an argv and console adapter over TypeScript workflows. Release workflows are modeled as typed functions first, then exposed through the CLI for terminal and CI usage.
77
+
28
78
  ## Imports
29
79
 
30
- The package intentionally avoids aggregate library barrels. The root `release` export is intentionally empty; import the exact module you need from an explicit subpath.
80
+ The package root export is intentionally empty. For onboarding and application workflow code, import the opt-in workflow facade from `@mannyc1/ts-release/workflows`; for maximum tree-shaking or target-author control, import the exact module you need from an explicit subpath.
31
81
 
32
82
  ```ts
33
83
  import * as Effect from "effect/Effect"
34
- import * as Layer from "effect/Layer"
35
84
  import { type ReleaseIntent } from "@mannyc1/ts-release/domain/release"
36
- import { BunReleaseHostLayer } from "@mannyc1/ts-release/host/bun"
37
85
  import { createReleasePlan } from "@mannyc1/ts-release/planner/create-release-plan"
38
86
  import { validatePlan } from "@mannyc1/ts-release/planner/executor"
39
- import { LiveTargetRegistryLayer } from "@mannyc1/ts-release/targets/live"
40
87
 
41
- const planAndValidate = (intent: ReleaseIntent) =>
42
- Effect.gen(function*() {
43
- const plan = yield* createReleasePlan(intent)
44
- const evidence = yield* validatePlan(plan)
45
- return { plan, evidence }
46
- }).pipe(
47
- Effect.provide(Layer.mergeAll(BunReleaseHostLayer, LiveTargetRegistryLayer))
48
- )
88
+ export const planAndValidate = Effect.fn("docs.planAndValidate")(function*(intent: ReleaseIntent) {
89
+ const plan = yield* createReleasePlan(intent)
90
+ const evidence = yield* validatePlan(plan)
91
+ return { plan, evidence }
92
+ })
49
93
  ```
50
94
 
51
- `createReleasePlan` needs a `TargetRegistry` layer. `validatePlan` needs `ReleaseHost` and `TargetRegistry` layers. Workflows that verify HTTP evidence, such as `verifyPlan`, `runApprovedReleaseWorkflow`, or direct `VerifyHttpOperation` execution, also need a `ReleaseHttp` layer. Bun callers can import `LiveReleaseHttpLayer` from `@mannyc1/ts-release/host/http-live` and provide it with a `ReleaseHost` layer plus an Effect HTTP client such as `@effect/platform-bun/BunHttpClient`; tests can import `makeTestReleaseHttpLayer` from `@mannyc1/ts-release/host/http`. Internal Effect imports use deep module paths such as `effect/Effect` and `effect/Layer` to keep bundlers from depending on broad root-package analysis.
95
+ `createReleasePlan` needs a `TargetRegistry` layer. Command execution needs a `ReleaseCommandRunner` layer, while artifact checks and checksum generation use Effect Platform `FileSystem`, `Path`, and `Crypto` services directly. High-level config-file workflows, render writes, and evidence writes also need Effect Platform `FileSystem` and `Path` services. Workflows that verify HTTP evidence, such as `verifyPlan`, `runApprovedReleaseWorkflow`, or direct `VerifyHttpOperation` execution, also need a `ReleaseHttp` layer. Applications can compose `@mannyc1/ts-release/workflows` and their platform services at the edge, or use exact lower-level imports from `@mannyc1/ts-release/workflows/live`, `@mannyc1/ts-release/host/platform`, and `@mannyc1/ts-release/host/http`. Tests can import `makeTestReleaseHttpLayer` from `@mannyc1/ts-release/host/http`. Internal Effect imports use deep module paths such as `effect/Effect` and `effect/Layer` to keep bundlers from depending on broad root-package analysis. See `ARCHITECTURE.md` for the module taxonomy.
52
96
 
53
- ## Programmatic CLI
97
+ Reusable operations in docs and examples should use `Effect.fn`; workflow bodies use `Effect.gen`. Durable data, options, tagged target variants, and typed errors use `Schema.Class`, `Schema.TaggedClass`, and `Schema.TaggedErrorClass`, with `.make(...)` for construction. Runtime layers are provided once at CLI, action, script, application, or test boundaries.
54
98
 
55
- Applications can run the CLI command path without spawning the executable:
99
+ ## TypeScript Workflows
100
+
101
+ Applications can call high-level release workflows without argv arrays or CLI command names:
56
102
 
57
103
  ```ts
104
+ import * as BunHttpClient from "@effect/platform-bun/BunHttpClient"
105
+ import * as BunServices from "@effect/platform-bun/BunServices"
58
106
  import * as Effect from "effect/Effect"
59
- import {
60
- PlanReleaseConfigOptions,
61
- planReleaseConfig,
62
- ReleaseCliOptions,
63
- runReleaseCli
64
- } from "@mannyc1/ts-release/cli/programmatic"
65
-
66
- await Effect.runPromise(
67
- runReleaseCli([
68
- "plan",
69
- "--config",
70
- "release.config.json",
71
- "--format",
72
- "text"
73
- ], ReleaseCliOptions.make({ root: "/path/to/release-workspace" }))
107
+ import * as Layer from "effect/Layer"
108
+ import { Config, Live } from "@mannyc1/ts-release/workflows"
109
+
110
+ const root = "/path/to/release-workspace"
111
+ const RuntimeLayer = Live.makeLayer({ root }).pipe(
112
+ Layer.provideMerge(BunServices.layer),
113
+ Layer.provideMerge(BunHttpClient.layer)
114
+ )
115
+
116
+ const textPlan = await Effect.runPromise(
117
+ Config.renderPlan({ root, configPath: "release.config.json", format: "text" }).pipe(
118
+ Effect.provide(RuntimeLayer)
119
+ )
74
120
  )
75
121
 
76
122
  const plan = await Effect.runPromise(
77
- planReleaseConfig(
78
- PlanReleaseConfigOptions.make({
79
- root: "/path/to/release-workspace",
80
- configPath: "release.config.json"
81
- })
123
+ Config.plan({ root, configPath: "release.config.json" }).pipe(
124
+ Effect.provide(RuntimeLayer)
125
+ )
126
+ )
127
+
128
+ const evidence = await Effect.runPromise(
129
+ Config.run({
130
+ root,
131
+ configPath: "release.config.json",
132
+ execute: true,
133
+ approveIrreversible: true
134
+ }).pipe(
135
+ Effect.provide(RuntimeLayer)
136
+ )
137
+ )
138
+
139
+ const status = await Effect.runPromise(
140
+ Config.status({ root, configPath: "release.config.json", format: "json" }).pipe(
141
+ Effect.provide(RuntimeLayer)
142
+ )
143
+ )
144
+
145
+ const resumedEvidence = await Effect.runPromise(
146
+ Config.resume({
147
+ root,
148
+ configPath: "release.config.json",
149
+ execute: true,
150
+ approveIrreversible: true
151
+ }).pipe(
152
+ Effect.provide(RuntimeLayer)
153
+ )
154
+ )
155
+
156
+ const eligibility = await Effect.runPromise(
157
+ Config.checkEligibility({
158
+ root,
159
+ configPath: "release.config.json"
160
+ }).pipe(
161
+ Effect.provide(RuntimeLayer)
162
+ )
163
+ )
164
+
165
+ const reconciliationEvidence = await Effect.runPromise(
166
+ Config.reconcile({
167
+ root,
168
+ configPath: "release.config.json",
169
+ execute: true
170
+ }).pipe(
171
+ Effect.provide(RuntimeLayer)
82
172
  )
83
173
  )
84
174
  ```
85
175
 
86
- The helper provides the Bun host and live target registry internally, so callers do not need to import `effect/unstable/cli/Command` or assemble CLI layers by hand.
176
+ Use `@mannyc1/ts-release/workflows` for the curated `Config`, `Init`, `Diagnostics`, `Evidence`, and `Live` namespaces. Exact leaf imports such as `@mannyc1/ts-release/workflows/config`, `@mannyc1/ts-release/workflows/init`, `@mannyc1/ts-release/workflows/diagnostics`, `@mannyc1/ts-release/workflows/evidence`, and `@mannyc1/ts-release/workflows/live` remain stable and are preferred when an application needs maximum tree-shaking or direct access to option classes. Applications provide platform services at the edge, such as `FileSystem`, `Path`, `HttpClient`, and command execution. Use explicit lower-level planner, config, target, host, and domain subpaths when an application needs finer control over planning, execution, or test layers.
87
177
 
88
178
  ## Example Config
89
179
 
90
180
  ```json
91
181
  {
182
+ "$schema": "https://mannyc2.github.io/ts-release/schema/release-config.schema.json",
92
183
  "identity": {
93
184
  "name": "@mannyc1/ts-release",
94
185
  "version": "0.1.0",
@@ -115,8 +206,14 @@ The helper provides the Bun host and live target registry internally, so callers
115
206
  "_tag": "NpmRegistryTarget",
116
207
  "id": "npm",
117
208
  "registry": "https://registry.npmjs.org",
209
+ "packageName": "@mannyc1/ts-release",
118
210
  "packagePath": ".",
119
- "tokenEnv": "NPM_TOKEN",
211
+ "trustedPublishing": {
212
+ "provider": "github-actions",
213
+ "workflow": "release.yml",
214
+ "packageExists": true,
215
+ "verifyPackageExists": true
216
+ },
120
217
  "access": "public",
121
218
  "provenance": true,
122
219
  "dryRunSupport": "native",
@@ -135,11 +232,13 @@ The helper provides the Bun host and live target registry internally, so callers
135
232
  }
136
233
  ],
137
234
  "strict": true,
138
- "evidenceDirectory": ".release/evidence"
235
+ "evidenceDirectory": ".release/evidence/{version}"
139
236
  }
140
237
  ```
141
238
 
142
- Paths are release-workspace relative and may not be absolute or contain parent traversal.
239
+ The optional `$schema` key powers editor completion and does not change release behavior. Print the derived schema with `bun run cli schema`, and use `bun run cli validate-config` to check JSON syntax and release config shape without running target validators.
240
+
241
+ Paths are release-workspace relative and may not be absolute or contain parent traversal. `evidenceDirectory` may include the literal `{version}` placeholder, which is resolved during planning so each release version can use its own evidence directory.
143
242
 
144
243
  Homebrew tap targets model catalog updates as generated files plus an approval-gated push:
145
244
 
@@ -205,49 +304,110 @@ Text plans include the release identity, evidence directory, artifact inventory,
205
304
  ```text
206
305
  @mannyc1/ts-release@0.1.0
207
306
  commit: abc123
208
- evidence: .release/evidence
307
+ evidence: .release/evidence/0.1.0
209
308
  artifacts: 2
210
309
  targets: 2
211
310
  operations: 9
212
311
 
213
312
  targets:
214
313
  - github [GitHubReleaseTarget] auth=env-token dry-run=simulated strategy=simulated-plan mutability=mutable-release recovery=delete-and-recreate
215
- - npm [NpmRegistryTarget] auth=env-token dry-run=native strategy=native-command mutability=immutable recovery=publish-new-version
314
+ - npm [NpmRegistryTarget] auth=trusted-publishing runs-in=ci provider=github-actions workflow=release.yml required-permission=id-token:write package-prerequisite=exists dry-run=native strategy=native-command mutability=immutable recovery=publish-new-version
315
+ ```
316
+
317
+ Summary plans provide a compact human first pass over risk, execution gates, target auth setup, and gated operation IDs:
318
+
319
+ ```sh
320
+ bun run cli plan --config release.config.json --format summary
321
+ ```
322
+
323
+ Markdown plans are intended for CI artifacts and PR review:
324
+
325
+ ```sh
326
+ bun run cli plan --config release.config.json --format markdown > release-plan.md
327
+ ```
328
+
329
+ Use `explain` when one operation needs a focused review without executing anything:
330
+
331
+ ```sh
332
+ bun run cli explain npm:npm-publish --config release.config.json
216
333
  ```
217
334
 
218
335
  JSON plans include the same data in a stable, CI-artifact-friendly shape, including `targetCapabilities`.
219
336
 
220
337
  GitHub release verification uses the GitHub REST API to check the release tag, title, draft flag, prerelease flag, and each uploaded artifact name.
221
338
 
339
+ ## Status and Resume
340
+
341
+ `status` reads local phase evidence and reports each current operation as pending, passed, failed, blocked, or complete without executing anything. The JSON format is schema-backed for CI or dashboards; the text format is intended for terminal review.
342
+
343
+ `resume` is intentionally conservative. It skips successful matching evidence, can rerun missing work and failed read-only validation or verification operations, and never reruns a failed publish operation. A failed publish command can still have changed the outside world, so resume blocks until a maintainer reconciles npm, GitHub, or any other remote state manually.
344
+
345
+ `reconcile` is the narrow remote repair path for GitHub Releases. It reads the GitHub release by tag, blocks on mismatched metadata or assets, skips an already matching published release, and can run `gh release edit <tag> --draft=false` for a matching draft when the target expects a public release. It does not run `npm publish`.
346
+
222
347
  ## Public API
223
348
 
224
- The intentional public API is the explicit subpath list in `package.json`. `release/cli` and `release/cli/command` are public so applications can embed the CLI command, while the executable remains available through the `release` binary.
349
+ The intentional public API is the explicit subpath list in `package.json`. The root package export remains empty. Programmatic callers can use the opt-in `@mannyc1/ts-release/workflows` facade for happy-path workflow APIs, or exact leaf subpaths such as `@mannyc1/ts-release/workflows/config`, `@mannyc1/ts-release/workflows/init`, and `@mannyc1/ts-release/workflows/diagnostics` for maximum tree-shaking and direct option-class access. Lower-level planner/config/target/status subpaths remain available for finer control. The official CLI command adapter lives in the private `apps/release-ts` app rather than the reusable root package API.
225
350
 
226
351
  The package export checker fails if a new export is added without being added to the intentional API list.
227
352
 
353
+ ## Templates
354
+
355
+ Copyable starter configs live in `templates/`. They are authoring starting
356
+ points with placeholder package, repository, tap, and bucket names. Runnable
357
+ fixtures live in `examples/`.
358
+
359
+ Config templates are intentionally narrow:
360
+
361
+ - `npm-only` for an existing npm package using GitHub Actions trusted publishing.
362
+ - `npm-github` for npm plus GitHub Releases.
363
+ - `multi-target-homebrew` for npm, GitHub Releases, and a Homebrew tap.
364
+ - `multi-target-scoop` for npm, GitHub Releases, and a Scoop bucket.
365
+
366
+ ```sh
367
+ bun run cli init --template npm-github --package @scope/pkg --repo owner/repo
368
+ bun run cli init --template npm-github --package @scope/pkg --repo owner/repo --write
369
+ bun run cli plan --config release.config.json --format text
370
+ ```
371
+
372
+ The npm templates enable provenance and set `verifyPackageExists: true`, which
373
+ adds a read-only `npm view <package>` validation before trusted publishing.
374
+ Add `--github-actions` to include the action-first trusted-publishing workflow
375
+ template in the preview or write set. Existing files are not overwritten unless
376
+ `--overwrite` is also passed.
377
+
378
+ Action-first GitHub templates live under `templates/github-actions/`.
379
+
380
+ Use `doctor`, `check-auth`, and `check-ci` after writing a template to inspect
381
+ static readiness before any publish operation is approved.
382
+
228
383
  ## Examples
229
384
 
230
385
  Runnable example configs live in `examples/`:
231
386
 
232
387
  - `examples/multi-target`
233
388
  - `examples/npm-only`
389
+ - `examples/npm-first-publish`
234
390
  - `examples/github-release`
235
391
  - `examples/homebrew-tap`
236
392
  - `examples/pypi-registry`
237
393
  - `examples/scoop-bucket`
238
394
  - `examples/non-strict-skips`
239
395
 
240
- `examples/multi-target` demonstrates one release coordinated across a release host, a package registry, and an installer catalog. Build the package first, then plan an example from its directory:
396
+ `examples/multi-target` demonstrates one release coordinated across GitHub Releases, npm, and a Homebrew tap. The focused fixtures cover PyPI, Scoop, npm-only trusted publishing, token-based first npm publish, GitHub-only releases, and non-strict skipped validators. Build the package first, then plan an example from its directory:
241
397
 
242
398
  ```sh
243
399
  bun run build
244
400
  cd examples/multi-target
245
- bun ../../dist/cli/main.js plan --config release.config.json --format text
401
+ bun ../../apps/release-ts/src/cli/main.ts plan --config release.config.json --format text
246
402
  ```
247
403
 
404
+ Trusted-publishing npm examples use provenance and `verifyPackageExists`. The
405
+ `npm-first-publish` example intentionally stays token-based because npm trusted
406
+ publishing can only be configured after the package already exists.
407
+
248
408
  ## Evidence
249
409
 
250
- Render, validation, execution, and verification evidence is written as JSON bundles. Failed commands still preserve partial evidence before the command failure is returned.
410
+ Render, validation, execution, and verification evidence is written as JSON bundles. Failed commands still preserve partial evidence before the command failure is returned. Use `evidenceDirectory` such as `.release/evidence/{version}` when older local evidence should not collide with the current release version.
251
411
 
252
412
  ```json
253
413
  {
@@ -258,6 +418,7 @@ Render, validation, execution, and verification evidence is written as JSON bund
258
418
  {
259
419
  "id": "npm:npm-pack-dry-run:command",
260
420
  "operationId": "npm:npm-pack-dry-run",
421
+ "operationFingerprint": "{\"_tag\":\"ValidateCommandOperation\",\"id\":\"npm:npm-pack-dry-run\",\"targetId\":\"npm\",\"approval\":{\"requiresExecute\":false,\"requiresIrreversibleApproval\":false},\"command\":{\"executable\":\"npm\",\"args\":[\"pack\",\"--dry-run\",\"--json\"],\"requiredEnv\":[],\"redactedEnv\":[]}}",
261
422
  "status": "passed",
262
423
  "severity": "info",
263
424
  "exitCode": 0
@@ -287,20 +448,92 @@ RELEASE_INTEGRATION_GITHUB=1 bun run test:integration:tools
287
448
 
288
449
  The first command validates npm adapter operations against the real `npm` CLI. The second also validates GitHub adapter readiness checks against the real `gh` CLI and requires `gh auth status` to succeed. GitHub release creation itself has no native dry-run; release validation is simulated from the deterministic plan before publish and verified against GitHub only after publish.
289
450
 
290
- Example configs are checked through the same programmatic CLI command path:
451
+ Example configs and templates are checked through the TypeScript workflow path:
291
452
 
292
453
  ```sh
293
454
  bun run check:examples
294
455
  ```
295
456
 
296
- This repository also includes a first release config at `release.config.json` that targets both npm and GitHub for the scoped `@mannyc1/ts-release` package. The self-release config must pass `bun run check:self-release-config` before release checks proceed. Its `identity.commit` may be the explicit current short commit, or `HEAD` as a stored-config convenience. Generated plans resolve `HEAD` to the current short commit, and the self-release guard requires a committed Git checkout with clean tracked files.
457
+ This repository also includes a self-release config at `apps/release-ts/release.config.json` that targets both npm and GitHub for the scoped `@mannyc1/ts-release` package. The app-owned self-release scripts live under `apps/release-ts/scripts`, with root package scripts delegating to them. The self-release config must pass `bun run check:self-release-config` before release checks proceed. Its `identity.commit` may be the explicit current short commit, or `HEAD` as a stored-config convenience. Generated plans resolve `HEAD` to the current short commit, and the self-release guard requires a committed Git checkout with clean tracked files.
297
458
 
298
459
  ```sh
299
460
  bun run check:self-release-config
300
461
  ```
301
462
 
463
+ ### Self Release
464
+
465
+ The local non-publish gates for this package are:
466
+
467
+ ```sh
468
+ bun run check:release
469
+ bun run release:artifacts
470
+ bun run cli plan --config apps/release-ts/release.config.json --format text
471
+ ```
472
+
473
+ `release:artifacts` delegates to `apps/release-ts/scripts/build-release-artifacts.ts` and writes ignored files under `.release/artifacts`: the npm package tarball and standalone CLI executables for Linux, macOS, and Windows. GitHub Actions runs the approved release workflow on protected `main` when the package version has not already been published. The workflow uses npm trusted publishing with GitHub Actions OIDC instead of an npm token, runs the app CLI with `--config apps/release-ts/release.config.json`, and uploads `.release/evidence/**` for audit.
474
+
302
475
  ### Local Release Auth
303
476
 
304
- Use `.env.example` as the local credential contract. Export `NPM_TOKEN` and `GH_TOKEN`, or copy `.env.example` to `.env` and fill in the tokens locally. `.env` and `.npmrc` are ignored intentionally; keep token values out of commits. `.npmrc.example` shows npm's `${NPM_TOKEN}` interpolation form for local setup.
477
+ Use `.env.example` as the local credential contract. Export `GH_TOKEN`, or copy
478
+ `.env.example` to `.env` and fill in token values locally. `.env` and `.npmrc`
479
+ are ignored intentionally; keep token values out of commits. `.npmrc.example`
480
+ shows npm's `${NPM_TOKEN}` interpolation form for token-based npm targets.
481
+ `TWINE_USERNAME` and `TWINE_PASSWORD` are only needed for PyPI/Twine examples.
482
+
483
+ For npmjs releases from GitHub Actions, prefer structured `trustedPublishing` on
484
+ the npm target:
485
+
486
+ ```json
487
+ {
488
+ "packageName": "@mannyc1/ts-release",
489
+ "trustedPublishing": {
490
+ "provider": "github-actions",
491
+ "workflow": "release.yml",
492
+ "packageExists": true,
493
+ "verifyPackageExists": true
494
+ },
495
+ "access": "public",
496
+ "provenance": true
497
+ }
498
+ ```
499
+
500
+ Trusted publishing authenticates during `npm publish` with CI OIDC, so
501
+ `ts-release` records that mode in validation evidence instead of running
502
+ `npm whoami`, which does not validate OIDC publishing. Configure npmjs trusted
503
+ publishing for an existing package and use a GitHub-hosted runner with
504
+ `id-token: write`, Node 22.14+ and npm 11.5.1+. The `packageExists` field must be
505
+ `true` as a precondition acknowledgement, not first-publish support. Setting
506
+ `verifyPackageExists` to `true` adds a read-only `npm view <package>` validation
507
+ check. Trusted publishing does not use `NPM_TOKEN` for `npm publish`; token-based
508
+ npm targets may still use `.npmrc.example` and `NPM_TOKEN`.
509
+
510
+ Diagnostics stay static by default and report confidence instead of pretending
511
+ local checks prove provider setup:
512
+
513
+ ```sh
514
+ bun run cli check-auth --config release.config.json --format text
515
+ bun run cli check-ci --config release.config.json --workflow .github/workflows/release.yml --format markdown
516
+ bun run cli doctor --config release.config.json --format json
517
+ ```
518
+
519
+ The reusable GitHub Actions trusted-publishing workflow template lives at
520
+ `templates/github-actions/trusted-publishing.yml`. It uses
521
+ `mannyc2/ts-release-action@v1` to record a Markdown plan, uploads review
522
+ artifacts, and requires a protected `release` environment before running
523
+ approved execution. npm trusted publishing uses OIDC, not `NPM_TOKEN`;
524
+ `GH_TOKEN` is for GitHub Releases and API verification.
525
+
526
+ ### First npm Publish Bootstrap
527
+
528
+ Trusted publishing is the preferred steady state for GitHub Actions, but npm
529
+ requires the package to exist before trusted publishing can be configured. For a
530
+ new package, use a temporary token-based npm target with `tokenEnv: "NPM_TOKEN"`
531
+ and no `trustedPublishing` object. After the first version exists on npm,
532
+ configure npm trusted publishing for owner `mannyc2`, repository `ts-release`,
533
+ and workflow filename `release.yml`, then switch the target to
534
+ `trustedPublishing`.
535
+
536
+ Do not commit token values. Keep `.env.example` and `.npmrc.example` as
537
+ placeholder contracts only.
305
538
 
306
- The first-release GitHub target uses `GH_TOKEN` for both `gh` command authentication and read-only REST API verification. Draft release verification requires authenticated API access. Enable npm provenance for CI-based publishes where the registry can generate provenance.
539
+ The first-release GitHub target uses `GH_TOKEN` for both `gh` command authentication and read-only REST API verification. The release workflow sets up a current Node/npm toolchain for trusted publishing and enables npm provenance for CI-based publishes where the registry can generate provenance.