@ontrails/trails 1.0.0-beta.4 → 1.0.0-beta.42

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 (165) hide show
  1. package/CHANGELOG.md +1106 -4
  2. package/README.md +27 -0
  3. package/package.json +31 -7
  4. package/src/app.ts +147 -3
  5. package/src/cli.ts +345 -11
  6. package/src/completions.ts +240 -0
  7. package/src/lifecycle-source-io.ts +33 -0
  8. package/src/load-app-mirror.ts +202 -0
  9. package/src/local-state-io.ts +173 -0
  10. package/src/mcp-app.ts +42 -0
  11. package/src/mcp-options.ts +91 -0
  12. package/src/mcp.ts +8 -0
  13. package/src/project-writes.ts +377 -0
  14. package/src/regrade/config.ts +152 -0
  15. package/src/regrade/history.ts +430 -0
  16. package/src/regrade/live-api-preserve.ts +8 -0
  17. package/src/regrade/plan-artifact.ts +285 -0
  18. package/src/release/bindings.ts +39 -0
  19. package/src/release/check.ts +844 -0
  20. package/src/release/config.ts +63 -0
  21. package/src/release/contract-facts.ts +425 -0
  22. package/src/release/index.ts +142 -0
  23. package/src/release/lock-roundtrip-smoke.ts +230 -0
  24. package/src/release/native-bun-publish.ts +651 -0
  25. package/src/release/native-bun-registry.ts +765 -0
  26. package/src/release/notes-cli.ts +171 -0
  27. package/src/release/notes.ts +390 -0
  28. package/src/release/pack-coherence.ts +455 -0
  29. package/src/release/packed-artifacts-smoke.ts +236 -0
  30. package/src/release/policy.ts +1687 -0
  31. package/src/release/semver.ts +104 -0
  32. package/src/release/smoke.ts +56 -0
  33. package/src/release/wayfinder-dogfood-smoke.ts +760 -0
  34. package/src/retired-topo-command.ts +36 -0
  35. package/src/run-adapter-check.ts +76 -0
  36. package/src/run-collision.ts +126 -0
  37. package/src/run-completions-install.ts +179 -0
  38. package/src/run-example.ts +149 -0
  39. package/src/run-examples.ts +148 -0
  40. package/src/run-quiet.ts +75 -0
  41. package/src/run-release-check.ts +74 -0
  42. package/src/run-schema.ts +74 -0
  43. package/src/run-trace.ts +273 -0
  44. package/src/run-warden.ts +39 -0
  45. package/src/run-watch.ts +432 -0
  46. package/src/run-wayfind-outline.ts +170 -0
  47. package/src/scaffold-version-sync.ts +183 -0
  48. package/src/scaffold-versions.generated.ts +12 -0
  49. package/src/trails/adapter-check.ts +244 -0
  50. package/src/trails/add-surface.ts +99 -45
  51. package/src/trails/add-trail.ts +84 -37
  52. package/src/trails/add-verify.ts +100 -30
  53. package/src/trails/compile.ts +58 -0
  54. package/src/trails/completions-complete.ts +165 -0
  55. package/src/trails/completions.ts +47 -0
  56. package/src/trails/create-adapter.ts +785 -0
  57. package/src/trails/create-scaffold.ts +401 -106
  58. package/src/trails/create-versions.ts +62 -0
  59. package/src/trails/create.ts +186 -72
  60. package/src/trails/deprecate.ts +59 -0
  61. package/src/trails/dev-clean.ts +82 -0
  62. package/src/trails/dev-reset.ts +50 -0
  63. package/src/trails/dev-stats.ts +72 -0
  64. package/src/trails/dev-support.ts +360 -0
  65. package/src/trails/doctor.ts +77 -0
  66. package/src/trails/draft-promote.ts +949 -0
  67. package/src/trails/guide.ts +70 -68
  68. package/src/trails/load-app.ts +1123 -15
  69. package/src/trails/operator-context.ts +66 -0
  70. package/src/trails/project.ts +17 -3
  71. package/src/trails/regrade.ts +3091 -0
  72. package/src/trails/release-check.ts +105 -0
  73. package/src/trails/release-smoke.ts +49 -0
  74. package/src/trails/revise.ts +53 -0
  75. package/src/trails/root-dir.ts +21 -0
  76. package/src/trails/run-example.ts +475 -0
  77. package/src/trails/run-examples.ts +129 -0
  78. package/src/trails/run.ts +434 -0
  79. package/src/trails/scaffold-json.ts +58 -0
  80. package/src/trails/survey.ts +877 -214
  81. package/src/trails/topo-activation.ts +14 -0
  82. package/src/trails/topo-constants.ts +2 -0
  83. package/src/trails/topo-history.ts +47 -0
  84. package/src/trails/topo-output-schemas.ts +259 -0
  85. package/src/trails/topo-pin.ts +38 -0
  86. package/src/trails/topo-read-support.ts +368 -0
  87. package/src/trails/topo-reports.ts +809 -0
  88. package/src/trails/topo-store-support.ts +325 -0
  89. package/src/trails/topo-support.ts +220 -0
  90. package/src/trails/topo-unpin.ts +61 -0
  91. package/src/trails/topo.ts +92 -0
  92. package/src/trails/validate.ts +27 -0
  93. package/src/trails/version-lifecycle-support.ts +936 -0
  94. package/src/trails/warden-guide.ts +134 -0
  95. package/src/trails/warden.ts +198 -58
  96. package/src/trails/wayfind-outline.ts +876 -0
  97. package/src/trails/wayfind.ts +1053 -0
  98. package/src/versions.ts +31 -0
  99. package/.turbo/turbo-build.log +0 -1
  100. package/.turbo/turbo-lint.log +0 -3
  101. package/.turbo/turbo-typecheck.log +0 -1
  102. package/__tests__/examples.test.ts +0 -6
  103. package/dist/bin/trails.d.ts +0 -3
  104. package/dist/bin/trails.d.ts.map +0 -1
  105. package/dist/bin/trails.js +0 -4
  106. package/dist/bin/trails.js.map +0 -1
  107. package/dist/src/app.d.ts +0 -2
  108. package/dist/src/app.d.ts.map +0 -1
  109. package/dist/src/app.js +0 -11
  110. package/dist/src/app.js.map +0 -1
  111. package/dist/src/clack.d.ts +0 -9
  112. package/dist/src/clack.d.ts.map +0 -1
  113. package/dist/src/clack.js +0 -84
  114. package/dist/src/clack.js.map +0 -1
  115. package/dist/src/cli.d.ts +0 -2
  116. package/dist/src/cli.d.ts.map +0 -1
  117. package/dist/src/cli.js +0 -13
  118. package/dist/src/cli.js.map +0 -1
  119. package/dist/src/trails/add-surface.d.ts +0 -13
  120. package/dist/src/trails/add-surface.d.ts.map +0 -1
  121. package/dist/src/trails/add-surface.js +0 -88
  122. package/dist/src/trails/add-surface.js.map +0 -1
  123. package/dist/src/trails/add-trail.d.ts +0 -10
  124. package/dist/src/trails/add-trail.d.ts.map +0 -1
  125. package/dist/src/trails/add-trail.js +0 -77
  126. package/dist/src/trails/add-trail.js.map +0 -1
  127. package/dist/src/trails/add-verify.d.ts +0 -10
  128. package/dist/src/trails/add-verify.d.ts.map +0 -1
  129. package/dist/src/trails/add-verify.js +0 -67
  130. package/dist/src/trails/add-verify.js.map +0 -1
  131. package/dist/src/trails/create-scaffold.d.ts +0 -15
  132. package/dist/src/trails/create-scaffold.d.ts.map +0 -1
  133. package/dist/src/trails/create-scaffold.js +0 -288
  134. package/dist/src/trails/create-scaffold.js.map +0 -1
  135. package/dist/src/trails/create.d.ts +0 -22
  136. package/dist/src/trails/create.d.ts.map +0 -1
  137. package/dist/src/trails/create.js +0 -121
  138. package/dist/src/trails/create.js.map +0 -1
  139. package/dist/src/trails/guide.d.ts +0 -11
  140. package/dist/src/trails/guide.d.ts.map +0 -1
  141. package/dist/src/trails/guide.js +0 -80
  142. package/dist/src/trails/guide.js.map +0 -1
  143. package/dist/src/trails/load-app.d.ts +0 -4
  144. package/dist/src/trails/load-app.d.ts.map +0 -1
  145. package/dist/src/trails/load-app.js +0 -24
  146. package/dist/src/trails/load-app.js.map +0 -1
  147. package/dist/src/trails/project.d.ts +0 -8
  148. package/dist/src/trails/project.d.ts.map +0 -1
  149. package/dist/src/trails/project.js +0 -43
  150. package/dist/src/trails/project.js.map +0 -1
  151. package/dist/src/trails/survey.d.ts +0 -31
  152. package/dist/src/trails/survey.d.ts.map +0 -1
  153. package/dist/src/trails/survey.js +0 -221
  154. package/dist/src/trails/survey.js.map +0 -1
  155. package/dist/src/trails/warden.d.ts +0 -19
  156. package/dist/src/trails/warden.d.ts.map +0 -1
  157. package/dist/src/trails/warden.js +0 -88
  158. package/dist/src/trails/warden.js.map +0 -1
  159. package/dist/tsconfig.tsbuildinfo +0 -1
  160. package/src/__tests__/create.test.ts +0 -349
  161. package/src/__tests__/guide.test.ts +0 -91
  162. package/src/__tests__/load-app.test.ts +0 -15
  163. package/src/__tests__/survey.test.ts +0 -159
  164. package/src/__tests__/warden.test.ts +0 -74
  165. package/tsconfig.json +0 -9
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # Trails CLI
2
+
3
+ Command-line tools for working with Trails projects.
4
+
5
+ Use the CLI to scaffold a Trails app, add surfaces, inspect the current topo, run warden checks, manage draft state, and keep local Trails project state tidy.
6
+
7
+ ```bash
8
+ bunx @ontrails/trails create
9
+ ```
10
+
11
+ Common workflows:
12
+
13
+ - `trails create` starts a new Trails project with generated trail, topo, surface, and verification files.
14
+ - `trails add surface` adds another surface entrypoint to an existing project.
15
+ - `trails topo` inspects topo state and manages pins/history.
16
+ - `trails compile` writes root `trails.lock`.
17
+ - `trails validate` checks root `trails.lock` for drift.
18
+ - `trails wayfind`, `trails wayfind --trails --intent read`, `trails wayfind <id> --contract`, `trails wayfind <id> --deps`, `trails wayfind <id> --impact`, `trails wayfind pattern "wayfind.*"`, `trails wayfind query "release drift"`, and `trails wayfind diff ...` read graph artifacts through Wayfinder. `trails wayfind file <file> --outline` is the operator-owned live-source exception, assembled with `@ontrails/source` and enriched with saved graph context when available.
19
+ - `trails schema <command...>` shows accepted CLI routes, aliases, flags, and schemas for an operator command or command namespace.
20
+ - `trails warden` runs Trails governance checks for contract and architecture drift. Use `--scope-exclude <glob>` or project `warden.scope.exclude` config when local notes, scratch space, or generated state should not be governed by Warden.
21
+ - `trails regrade plan <from> <to> --root-dir <path> --json` writes an active Regrade plan, `trails regrade plan --expand` stages wide-net review candidates in that plan, `trails regrade preview` reruns it without writing, and `trails regrade apply` consumes the plan and writes history. Use `plan --expand --dry-run` to inspect staged candidates without writing the active plan, and add `--dry-run` to `apply` to prove the apply path without mutating source. Use `trails regrade plans` and `trails regrade check` when a workspace has active plans that need inspection before apply. MCP exposes the same lifecycle as `trails_plan_regrade`, `trails_list_regrades`, `trails_check_regrade`, `trails_preview_regrade`, and `trails_apply_regrade`.
22
+ - `trails guide` remains available for compatibility; prefer `trails wayfind --source live --module <app-module>` or saved-artifact Wayfinder reads for agent navigation.
23
+
24
+ Trails is contract-first: define trails once with typed input, Result output, examples, and meta; the framework derives CLI, MCP, HTTP, and future surfaces from the same contracts.
25
+
26
+ See the main Trails documentation for the full framework guide:
27
+ <https://github.com/outfitter-dev/trails>
package/package.json CHANGED
@@ -1,10 +1,23 @@
1
1
  {
2
2
  "name": "@ontrails/trails",
3
- "version": "1.0.0-beta.4",
3
+ "version": "1.0.0-beta.42",
4
4
  "bin": {
5
5
  "trails": "./bin/trails.ts"
6
6
  },
7
+ "files": [
8
+ "bin/**/*.ts",
9
+ "src/**/*.ts",
10
+ "!src/**/__tests__/**",
11
+ "!src/**/*.test.ts",
12
+ "!src/**/*.test-d.ts",
13
+ "README.md",
14
+ "CHANGELOG.md"
15
+ ],
7
16
  "type": "module",
17
+ "exports": {
18
+ "./release": "./src/release/index.ts",
19
+ "./package.json": "./package.json"
20
+ },
8
21
  "scripts": {
9
22
  "build": "tsc -b",
10
23
  "test": "bun test",
@@ -14,15 +27,26 @@
14
27
  },
15
28
  "dependencies": {
16
29
  "@clack/prompts": "^1.1.0",
17
- "@ontrails/cli": "^1.0.0-beta.0",
18
- "@ontrails/core": "^1.0.0-beta.0",
19
- "@ontrails/logging": "^1.0.0-beta.0",
20
- "@ontrails/schema": "^1.0.0-beta.0",
21
- "@ontrails/warden": "^1.0.0-beta.0",
30
+ "@ontrails/adapter-kit": "^1.0.0-beta.42",
31
+ "@ontrails/cli": "^1.0.0-beta.42",
32
+ "@ontrails/commander": "^1.0.0-beta.42",
33
+ "@ontrails/config": "^1.0.0-beta.42",
34
+ "@ontrails/core": "^1.0.0-beta.42",
35
+ "@ontrails/http": "^1.0.0-beta.42",
36
+ "@ontrails/mcp": "^1.0.0-beta.42",
37
+ "@ontrails/observe": "^1.0.0-beta.42",
38
+ "@ontrails/permits": "^1.0.0-beta.42",
39
+ "@ontrails/regrade": "^1.0.0-beta.42",
40
+ "@ontrails/source": "^1.0.0-beta.42",
41
+ "@ontrails/topography": "^1.0.0-beta.42",
42
+ "@ontrails/tracing": "^1.0.0-beta.42",
43
+ "@ontrails/warden": "^1.0.0-beta.42",
22
44
  "commander": "^14.0.3",
45
+ "typescript": "^5.9.3",
23
46
  "zod": "^4.3.5"
24
47
  },
25
48
  "devDependencies": {
26
- "@ontrails/testing": "^1.0.0-beta.0"
49
+ "@ontrails/cloudflare": "^1.0.0-beta.42",
50
+ "@ontrails/testing": "^1.0.0-beta.42"
27
51
  }
28
52
  }
package/src/app.ts CHANGED
@@ -1,22 +1,166 @@
1
- import { topo } from '@ontrails/core';
1
+ import { surfaceOverlay, topo } from '@ontrails/core';
2
+ import {
3
+ wayfindAdaptersTrail,
4
+ wayfindEntitiesTrail,
5
+ wayfindContractTrail,
6
+ wayfindDescribeTrail,
7
+ wayfindDiffTrail,
8
+ wayfindErrorsTrail,
9
+ wayfindExamplesTrail,
10
+ wayfindOverlayTrail,
11
+ wayfindTrailheadsTrail,
12
+ wayfindImpactTrail,
13
+ wayfindNearbyTrail,
14
+ wayfindOverviewTrail,
15
+ wayfindResourcesTrail,
16
+ wayfindSearchTrail,
17
+ wayfindSignalsTrail,
18
+ wayfindSurfacesTrail,
19
+ wayfindTrailsTrail,
20
+ wayfindVersionsTrail,
21
+ } from '@ontrails/topography';
2
22
 
3
23
  import * as addSurface from './trails/add-surface.js';
4
24
  import * as addTrail from './trails/add-trail.js';
5
25
  import * as addVerify from './trails/add-verify.js';
26
+ import * as adapterCheck from './trails/adapter-check.js';
27
+ import * as compile from './trails/compile.js';
28
+ import * as completions from './trails/completions.js';
29
+ import * as completionsComplete from './trails/completions-complete.js';
6
30
  import * as create from './trails/create.js';
31
+ import * as createAdapter from './trails/create-adapter.js';
7
32
  import * as createScaffold from './trails/create-scaffold.js';
33
+ import * as createVersions from './trails/create-versions.js';
34
+ import * as deprecate from './trails/deprecate.js';
35
+ import * as devClean from './trails/dev-clean.js';
36
+ import * as devReset from './trails/dev-reset.js';
37
+ import * as devStats from './trails/dev-stats.js';
38
+ import * as doctor from './trails/doctor.js';
39
+ import * as draftPromote from './trails/draft-promote.js';
8
40
  import * as guide from './trails/guide.js';
41
+ import * as regrade from './trails/regrade.js';
42
+ import * as releaseCheck from './trails/release-check.js';
43
+ import * as releaseSmoke from './trails/release-smoke.js';
44
+ import * as revise from './trails/revise.js';
45
+ import * as run from './trails/run.js';
46
+ import * as runExample from './trails/run-example.js';
47
+ import * as runExamples from './trails/run-examples.js';
9
48
  import * as survey from './trails/survey.js';
49
+ import * as topoHistory from './trails/topo-history.js';
50
+ import * as topoPin from './trails/topo-pin.js';
51
+ import * as topoCommand from './trails/topo.js';
52
+ import * as topoUnpin from './trails/topo-unpin.js';
53
+ import * as validate from './trails/validate.js';
10
54
  import * as warden from './trails/warden.js';
55
+ import * as wardenGuide from './trails/warden-guide.js';
56
+ import * as wayfind from './trails/wayfind.js';
57
+ import { wayfindOutlineTrail } from './trails/wayfind-outline.js';
11
58
 
12
- export const app = topo(
59
+ export const operatorApp = topo(
13
60
  'trails',
61
+ run,
62
+ runExamples,
63
+ runExample,
14
64
  survey,
65
+ topoCommand,
66
+ compile,
67
+ topoHistory,
68
+ topoPin,
69
+ topoUnpin,
70
+ validate,
71
+ revise,
72
+ deprecate,
73
+ doctor,
74
+ devStats,
75
+ devClean,
76
+ devReset,
15
77
  guide,
78
+ regrade,
79
+ releaseCheck,
80
+ releaseSmoke,
81
+ draftPromote,
82
+ adapterCheck,
16
83
  warden,
84
+ wardenGuide,
17
85
  create,
86
+ createAdapter,
18
87
  createScaffold,
88
+ createVersions,
19
89
  addSurface,
20
90
  addVerify,
21
- addTrail
91
+ addTrail,
92
+ completions,
93
+ completionsComplete
22
94
  );
95
+
96
+ const operatorTrails = Object.fromEntries(
97
+ operatorApp.list().map((trailItem) => [trailItem.id, trailItem])
98
+ );
99
+
100
+ const cliWayfinderCommandTrails = {
101
+ wayfindDiffTrail,
102
+ wayfindFileTrail: wayfind.wayfindFileTrail,
103
+ wayfindPatternTrail: wayfind.wayfindPatternTrail,
104
+ wayfindQueryTrail: wayfind.wayfindQueryTrail,
105
+ wayfindTrail: wayfind.wayfindTrail,
106
+ };
107
+
108
+ const cliWayfinderSupportTrails = {
109
+ wayfindAdaptersTrail,
110
+ wayfindContractTrail,
111
+ wayfindDescribeTrail,
112
+ wayfindEntitiesTrail,
113
+ wayfindErrorsTrail,
114
+ wayfindExamplesTrail,
115
+ wayfindImpactTrail,
116
+ wayfindNearbyTrail,
117
+ wayfindOutlineTrail,
118
+ wayfindOverlayTrail,
119
+ wayfindOverviewTrail,
120
+ wayfindResourcesTrail,
121
+ wayfindSearchTrail,
122
+ wayfindSignalsTrail,
123
+ wayfindSurfacesTrail,
124
+ wayfindTrailheadsTrail,
125
+ wayfindTrailsTrail,
126
+ wayfindVersionsTrail,
127
+ };
128
+
129
+ const cliWayfinderTrails = {
130
+ ...cliWayfinderCommandTrails,
131
+ ...cliWayfinderSupportTrails,
132
+ };
133
+
134
+ export const trailsCliIncludedTrails = [
135
+ ...new Set([
136
+ ...operatorApp.list().map((trailItem) => trailItem.id),
137
+ ...Object.values(cliWayfinderCommandTrails).map(
138
+ (trailItem) => trailItem.id
139
+ ),
140
+ ]),
141
+ ];
142
+
143
+ export const trailsOverlays = [
144
+ surfaceOverlay({
145
+ cli: { diff: 'survey.diff' },
146
+ // Authored, lockable default for the operator MCP `inspect` trailhead.
147
+ // The call-site map in `mcp-options.ts` overrides it at runtime with
148
+ // richer metadata (description, deferred loading) over the same members.
149
+ mcp: {
150
+ inspect: [
151
+ 'survey',
152
+ 'topo',
153
+ 'guide',
154
+ 'survey.brief',
155
+ 'survey.diff',
156
+ 'survey.resource',
157
+ 'survey.signal',
158
+ 'survey.surfaces',
159
+ 'survey.trail',
160
+ 'topo.history',
161
+ ],
162
+ },
163
+ }),
164
+ ];
165
+
166
+ export const app = topo('trails', operatorTrails, cliWayfinderTrails);
package/src/cli.ts CHANGED
@@ -1,14 +1,348 @@
1
- import { outputModePreset } from '@ontrails/cli';
2
- import { blaze } from '@ontrails/cli/commander';
1
+ import { existsSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { isAbsolute, join, resolve } from 'node:path';
3
4
 
4
- import { app } from './app.js';
5
+ import {
6
+ defaultOnResult,
7
+ devPermitPreset,
8
+ deriveCliCommands,
9
+ outputModePreset,
10
+ permitPreset,
11
+ tokenPreset,
12
+ tracePreset,
13
+ watchPreset,
14
+ } from '@ontrails/cli';
15
+ import type {
16
+ ActionResultContext,
17
+ ResolveCliPermitFromToken,
18
+ } from '@ontrails/cli';
19
+ import { createProgram } from '@ontrails/commander';
20
+ import type { CreateProgramOptions } from '@ontrails/commander';
21
+ import { resolvePermitFromBearerToken } from '@ontrails/permits';
22
+ import { deriveTopoGraph } from '@ontrails/topography';
23
+
24
+ import { app, trailsCliIncludedTrails, trailsOverlays } from './app.js';
5
25
  import { resolveInputWithClack } from './clack.js';
26
+ import { getRetiredTopoCommandDiagnostic } from './retired-topo-command.js';
27
+ import { attachCompletionsInstallCommand } from './run-completions-install.js';
28
+ import { attachSchemaCommand } from './run-schema.js';
29
+ import {
30
+ applyAdapterCheckExitCode,
31
+ tryAdapterCheckOutput,
32
+ } from './run-adapter-check.js';
33
+ import {
34
+ applyReleaseCheckExitCode,
35
+ tryReleaseCheckOutput,
36
+ } from './run-release-check.js';
37
+ import { tryRecoverFromRunCollision } from './run-collision.js';
38
+ import { tryExampleRunOutput } from './run-example.js';
39
+ import { tryExamplesRunOutput } from './run-examples.js';
40
+ import { tryQuietRunOutput } from './run-quiet.js';
41
+ import {
42
+ argvHasTraceFlag,
43
+ installTraceSink,
44
+ tryTraceJsonOutput,
45
+ writeTraceTreeToStderr,
46
+ } from './run-trace.js';
47
+ import type { TraceSession } from './run-trace.js';
48
+ import {
49
+ argvHasWatchFlag,
50
+ hashTopoGraphEntry,
51
+ readRunTrailId,
52
+ runWatchLoop,
53
+ } from './run-watch.js';
54
+ import { tryWardenOutput } from './run-warden.js';
55
+ import { tryWayfindOutlineOutput } from './run-wayfind-outline.js';
56
+ import { tryLoadFreshAppLease } from './trails/load-app.js';
57
+ import { resolveRunModulePath } from './trails/run.js';
58
+ import { resolveTrailRootDir } from './trails/root-dir.js';
59
+ import { trailsPackageVersion } from './versions.js';
60
+
61
+ const buildOnResult =
62
+ (session: TraceSession | undefined) =>
63
+ async (ctx: ActionResultContext): Promise<void> => {
64
+ const recovered = await tryRecoverFromRunCollision(ctx, { graph: app });
65
+ const resolvedCtx: ActionResultContext =
66
+ recovered === undefined
67
+ ? ctx
68
+ : {
69
+ ...ctx,
70
+ input: recovered.isOk()
71
+ ? (ctx.trail.input.safeParse(ctx.input).data ?? ctx.input)
72
+ : ctx.input,
73
+ result: recovered,
74
+ };
75
+
76
+ // `--trace --json` (without `--quiet`) emits a single Result-shaped
77
+ // envelope on stdout that includes the captured records under
78
+ // `tracing`. Hand that case off before the regular chain so the
79
+ // existing handlers do not also write to stdout.
80
+ applyAdapterCheckExitCode(resolvedCtx);
81
+ applyReleaseCheckExitCode(resolvedCtx);
82
+ if (session !== undefined && tryTraceJsonOutput(resolvedCtx, session)) {
83
+ return;
84
+ }
85
+
86
+ if (tryExampleRunOutput(resolvedCtx)) {
87
+ return;
88
+ }
89
+ if (tryExamplesRunOutput(resolvedCtx)) {
90
+ return;
91
+ }
92
+ if (await tryQuietRunOutput(resolvedCtx)) {
93
+ return;
94
+ }
95
+ if (tryWardenOutput(resolvedCtx)) {
96
+ return;
97
+ }
98
+ if (tryAdapterCheckOutput(resolvedCtx)) {
99
+ return;
100
+ }
101
+ if (tryReleaseCheckOutput(resolvedCtx)) {
102
+ return;
103
+ }
104
+ if (tryWayfindOutlineOutput(resolvedCtx)) {
105
+ return;
106
+ }
107
+ await defaultOnResult(resolvedCtx);
108
+ };
109
+
110
+ const traceEnabled = argvHasTraceFlag(process.argv);
111
+ const maybeInstallTraceSession = (): TraceSession | undefined =>
112
+ traceEnabled ? installTraceSink() : undefined;
113
+
114
+ const resolveCliPermitFromToken: ResolveCliPermitFromToken = (input) =>
115
+ resolvePermitFromBearerToken({
116
+ bearerToken: input.token,
117
+ configValues: input.configValues,
118
+ env: process.env as Record<string, string | undefined>,
119
+ graph: input.graph,
120
+ missingAuthResourceMessage:
121
+ '--token requires an auth adapter. Register authResource from @ontrails/permits in your topo.',
122
+ nullPermitMessage: 'Auth adapter did not produce a permit for --token',
123
+ requestId: input.requestId,
124
+ resources: input.resources,
125
+ surface: 'cli',
126
+ });
127
+
128
+ interface WatchRunTarget {
129
+ readonly app?: string | undefined;
130
+ readonly id: string;
131
+ readonly module?: string | undefined;
132
+ readonly rootDir?: string | undefined;
133
+ }
134
+
135
+ const readFlagValue = (
136
+ args: readonly string[],
137
+ flagName: string
138
+ ): string | undefined => {
139
+ const longFlag = `--${flagName}`;
140
+ const prefixedFlag = `${longFlag}=`;
141
+ for (let i = 0; i < args.length; i += 1) {
142
+ const arg = args[i];
143
+ if (arg === longFlag) {
144
+ return args[i + 1];
145
+ }
146
+ if (arg?.startsWith(prefixedFlag)) {
147
+ return arg.slice(prefixedFlag.length);
148
+ }
149
+ }
150
+ return undefined;
151
+ };
152
+
153
+ const resolveWatchRunTarget = (
154
+ argv: readonly string[]
155
+ ): WatchRunTarget | null => {
156
+ const args = argv.slice(2);
157
+ const id = readRunTrailId(args);
158
+ if (id === undefined) {
159
+ return null;
160
+ }
161
+ return {
162
+ app: readFlagValue(args, 'app'),
163
+ id,
164
+ module: readFlagValue(args, 'module'),
165
+ rootDir: readFlagValue(args, 'root-dir'),
166
+ };
167
+ };
168
+
169
+ /**
170
+ * Resolve the directory whose source-file events wake the `--watch` loop.
171
+ * Reruns still depend on the resolved TopoGraph entry hash; this path is
172
+ * only the cheap filesystem event source.
173
+ */
174
+ const toWatchSourcePath = (rootDir: string, modulePath: string): string => {
175
+ if (modulePath.startsWith('file:')) {
176
+ return fileURLToPath(modulePath);
177
+ }
178
+ return isAbsolute(modulePath) ? modulePath : resolve(rootDir, modulePath);
179
+ };
180
+
181
+ const resolveWatchDirectorySourcePath = async (
182
+ target: WatchRunTarget | null
183
+ ): Promise<string> => {
184
+ if (target !== null) {
185
+ const rootDirResult = resolveTrailRootDir(target.rootDir, process.cwd());
186
+ if (rootDirResult.isErr()) {
187
+ throw rootDirResult.error;
188
+ }
189
+ const moduleResult = await resolveRunModulePath(
190
+ rootDirResult.value,
191
+ target.module,
192
+ target.id,
193
+ target.app
194
+ );
195
+ if (moduleResult.isOk()) {
196
+ return toWatchSourcePath(rootDirResult.value, moduleResult.value);
197
+ }
198
+ }
199
+ const cwd = process.cwd();
200
+ const srcDir = join(cwd, 'src');
201
+ if (existsSync(srcDir)) {
202
+ return join(srcDir, 'app.ts');
203
+ }
204
+ return join(cwd, 'app.ts');
205
+ };
206
+
207
+ const readWatchTopoGraphEntryHash = async (
208
+ target: WatchRunTarget | null
209
+ ): Promise<string | null> => {
210
+ if (target === null) {
211
+ return null;
212
+ }
213
+ const rootDirResult = resolveTrailRootDir(target.rootDir, process.cwd());
214
+ if (rootDirResult.isErr()) {
215
+ throw rootDirResult.error;
216
+ }
217
+ const rootDir = rootDirResult.value;
218
+ const moduleResult = await resolveRunModulePath(
219
+ rootDir,
220
+ target.module,
221
+ target.id,
222
+ target.app
223
+ );
224
+ if (moduleResult.isErr()) {
225
+ throw moduleResult.error;
226
+ }
227
+ const leaseResult = await tryLoadFreshAppLease(moduleResult.value, rootDir);
228
+ if (leaseResult.isErr()) {
229
+ throw leaseResult.error;
230
+ }
231
+ const lease = leaseResult.value;
232
+ try {
233
+ const topoGraph = deriveTopoGraph(lease.app);
234
+ const entry = topoGraph.entries.find(
235
+ (candidate) => candidate.kind === 'trail' && candidate.id === target.id
236
+ );
237
+ return entry === undefined ? null : hashTopoGraphEntry(entry);
238
+ } finally {
239
+ lease.release();
240
+ }
241
+ };
242
+
243
+ const wardenValueFlags = new Set([
244
+ '--apps',
245
+ '--config-path',
246
+ '--depth',
247
+ '--drafts',
248
+ '--fail-on',
249
+ '--format',
250
+ '--scope-exclude',
251
+ '--lock',
252
+ '--root-dir',
253
+ ]);
254
+
255
+ const normalizeWardenArgv = (argv: readonly string[]): string[] => {
256
+ if (argv[2] !== 'warden') {
257
+ return [...argv];
258
+ }
259
+
260
+ const normalized = [...argv];
261
+ let previousFlagConsumesValue = false;
262
+ for (let index = 3; index < normalized.length; index += 1) {
263
+ const arg = normalized[index];
264
+ if (arg === undefined) {
265
+ continue;
266
+ }
267
+
268
+ if (previousFlagConsumesValue) {
269
+ previousFlagConsumesValue = false;
270
+ continue;
271
+ }
272
+
273
+ if (arg === '-a') {
274
+ normalized[index] = '--apps';
275
+ previousFlagConsumesValue = true;
276
+ continue;
277
+ }
278
+
279
+ previousFlagConsumesValue = wardenValueFlags.has(arg);
280
+ }
281
+
282
+ return normalized;
283
+ };
284
+
285
+ /**
286
+ * Invoke `surface()` once with an optional fresh trace session.
287
+ *
288
+ * When `--trace` is set, a fresh {@link TraceSession} is installed for the
289
+ * duration of the call and finalized in the `finally` block. Under
290
+ * `--watch`, this produces a fresh sink (and a fresh stderr tree) per
291
+ * rerun rather than letting records accumulate in a single
292
+ * process-lifetime sink.
293
+ */
294
+ const runSurfaceOnce = async (): Promise<void> => {
295
+ const retiredTopoCommand = getRetiredTopoCommandDiagnostic(process.argv);
296
+ if (retiredTopoCommand !== null) {
297
+ process.stderr.write(`${retiredTopoCommand.message}\n`);
298
+ process.exitCode = 1;
299
+ return;
300
+ }
301
+
302
+ const session = maybeInstallTraceSession();
303
+ try {
304
+ const surfaceOptions = {
305
+ description: 'Agent-native, contract-first TypeScript framework',
306
+ include: trailsCliIncludedTrails,
307
+ name: 'trails',
308
+ onResult: buildOnResult(session),
309
+ overlays: trailsOverlays,
310
+ presets: [
311
+ outputModePreset(),
312
+ tracePreset(),
313
+ permitPreset(),
314
+ tokenPreset(),
315
+ devPermitPreset(),
316
+ watchPreset(),
317
+ ],
318
+ resolveInput: resolveInputWithClack,
319
+ resolvePermitFromToken: resolveCliPermitFromToken,
320
+ version: trailsPackageVersion,
321
+ } satisfies CreateProgramOptions;
322
+ const program = createProgram(app, surfaceOptions);
323
+ const schemaCommands = deriveCliCommands(app, surfaceOptions);
324
+ if (schemaCommands.isErr()) {
325
+ throw schemaCommands.error;
326
+ }
327
+ attachSchemaCommand(program, schemaCommands.value);
328
+ attachCompletionsInstallCommand(program);
329
+ await program.parseAsync(normalizeWardenArgv(process.argv));
330
+ } finally {
331
+ if (session !== undefined) {
332
+ const records = session.finalize();
333
+ writeTraceTreeToStderr(records);
334
+ }
335
+ }
336
+ };
337
+
338
+ const watchTarget = argvHasWatchFlag(process.argv)
339
+ ? resolveWatchRunTarget(process.argv)
340
+ : null;
6
341
 
7
- // oxlint-disable-next-line require-hook -- CLI entry point
8
- blaze(app, {
9
- description: 'Agent-native, contract-first TypeScript framework',
10
- name: 'trails',
11
- presets: [outputModePreset()],
12
- resolveInput: resolveInputWithClack,
13
- version: '0.1.0',
14
- });
342
+ await (argvHasWatchFlag(process.argv)
343
+ ? runWatchLoop({
344
+ readTopoGraphEntryHash: () => readWatchTopoGraphEntryHash(watchTarget),
345
+ run: runSurfaceOnce,
346
+ sourcePath: await resolveWatchDirectorySourcePath(watchTarget),
347
+ })
348
+ : runSurfaceOnce());