@ontrails/trails 1.0.0-beta.2 → 1.0.0-beta.22

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 (150) hide show
  1. package/CHANGELOG.md +647 -0
  2. package/README.md +26 -0
  3. package/package.json +28 -7
  4. package/src/app.ts +86 -2
  5. package/src/clack.ts +22 -0
  6. package/src/cli.ts +330 -11
  7. package/src/completions.ts +240 -0
  8. package/src/lifecycle-source-io.ts +33 -0
  9. package/src/load-app-mirror.ts +202 -0
  10. package/src/local-state-io.ts +153 -0
  11. package/src/mcp-app.ts +30 -0
  12. package/src/mcp-options.ts +77 -0
  13. package/src/mcp.ts +8 -0
  14. package/src/project-writes.ts +377 -0
  15. package/src/release/bindings.ts +39 -0
  16. package/src/release/check.ts +818 -0
  17. package/src/release/config.ts +63 -0
  18. package/src/release/contract-facts.ts +425 -0
  19. package/src/release/index.ts +85 -0
  20. package/src/release/native-bun-publish.ts +651 -0
  21. package/src/release/native-bun-registry.ts +350 -0
  22. package/src/release/packed-artifacts-smoke.ts +236 -0
  23. package/src/release/smoke.ts +46 -0
  24. package/src/release/wayfinder-dogfood-smoke.ts +226 -0
  25. package/src/retired-topo-command.ts +36 -0
  26. package/src/run-adapter-check.ts +76 -0
  27. package/src/run-collision.ts +126 -0
  28. package/src/run-completions-install.ts +179 -0
  29. package/src/run-example.ts +149 -0
  30. package/src/run-examples.ts +148 -0
  31. package/src/run-quiet.ts +75 -0
  32. package/src/run-release-check.ts +74 -0
  33. package/src/run-trace.ts +273 -0
  34. package/src/run-warden.ts +39 -0
  35. package/src/run-watch.ts +432 -0
  36. package/src/scaffold-version-sync.ts +183 -0
  37. package/src/scaffold-versions.generated.ts +12 -0
  38. package/src/trails/adapter-check.ts +244 -0
  39. package/src/trails/add-surface.ts +94 -40
  40. package/src/trails/add-trail.ts +79 -41
  41. package/src/trails/add-verify.ts +95 -25
  42. package/src/trails/compile.ts +67 -0
  43. package/src/trails/completions-complete.ts +165 -0
  44. package/src/trails/completions.ts +47 -0
  45. package/src/trails/create-adapter.ts +1084 -0
  46. package/src/trails/create-scaffold.ts +399 -104
  47. package/src/trails/create-versions.ts +62 -0
  48. package/src/trails/create.ts +185 -71
  49. package/src/trails/deprecate.ts +59 -0
  50. package/src/trails/dev-clean.ts +82 -0
  51. package/src/trails/dev-reset.ts +50 -0
  52. package/src/trails/dev-stats.ts +72 -0
  53. package/src/trails/dev-support.ts +340 -0
  54. package/src/trails/doctor.ts +56 -0
  55. package/src/trails/draft-promote.ts +949 -0
  56. package/src/trails/guide.ts +74 -68
  57. package/src/trails/load-app.ts +1143 -15
  58. package/src/trails/project.ts +17 -3
  59. package/src/trails/release-check.ts +104 -0
  60. package/src/trails/release-smoke.ts +48 -0
  61. package/src/trails/revise.ts +53 -0
  62. package/src/trails/root-dir.ts +21 -0
  63. package/src/trails/run-example.ts +491 -0
  64. package/src/trails/run-examples.ts +145 -0
  65. package/src/trails/run.ts +410 -0
  66. package/src/trails/scaffold-json.ts +58 -0
  67. package/src/trails/survey.ts +881 -226
  68. package/src/trails/topo-activation.ts +385 -0
  69. package/src/trails/topo-constants.ts +2 -0
  70. package/src/trails/topo-history.ts +47 -0
  71. package/src/trails/topo-output-schemas.ts +248 -0
  72. package/src/trails/topo-pin.ts +52 -0
  73. package/src/trails/topo-read-support.ts +313 -0
  74. package/src/trails/topo-reports.ts +807 -0
  75. package/src/trails/topo-store-support.ts +174 -0
  76. package/src/trails/topo-support.ts +220 -0
  77. package/src/trails/topo-unpin.ts +61 -0
  78. package/src/trails/topo.ts +106 -0
  79. package/src/trails/validate.ts +38 -0
  80. package/src/trails/version-lifecycle-support.ts +945 -0
  81. package/src/trails/warden-guide.ts +129 -0
  82. package/src/trails/warden.ts +165 -58
  83. package/src/versions.ts +31 -0
  84. package/.turbo/turbo-build.log +0 -1
  85. package/.turbo/turbo-lint.log +0 -3
  86. package/.turbo/turbo-typecheck.log +0 -1
  87. package/__tests__/examples.test.ts +0 -6
  88. package/dist/bin/trails.d.ts +0 -3
  89. package/dist/bin/trails.d.ts.map +0 -1
  90. package/dist/bin/trails.js +0 -4
  91. package/dist/bin/trails.js.map +0 -1
  92. package/dist/src/app.d.ts +0 -2
  93. package/dist/src/app.d.ts.map +0 -1
  94. package/dist/src/app.js +0 -11
  95. package/dist/src/app.js.map +0 -1
  96. package/dist/src/clack.d.ts +0 -9
  97. package/dist/src/clack.d.ts.map +0 -1
  98. package/dist/src/clack.js +0 -62
  99. package/dist/src/clack.js.map +0 -1
  100. package/dist/src/cli.d.ts +0 -2
  101. package/dist/src/cli.d.ts.map +0 -1
  102. package/dist/src/cli.js +0 -13
  103. package/dist/src/cli.js.map +0 -1
  104. package/dist/src/trails/add-surface.d.ts +0 -13
  105. package/dist/src/trails/add-surface.d.ts.map +0 -1
  106. package/dist/src/trails/add-surface.js +0 -88
  107. package/dist/src/trails/add-surface.js.map +0 -1
  108. package/dist/src/trails/add-trail.d.ts +0 -11
  109. package/dist/src/trails/add-trail.d.ts.map +0 -1
  110. package/dist/src/trails/add-trail.js +0 -85
  111. package/dist/src/trails/add-trail.js.map +0 -1
  112. package/dist/src/trails/add-verify.d.ts +0 -10
  113. package/dist/src/trails/add-verify.d.ts.map +0 -1
  114. package/dist/src/trails/add-verify.js +0 -67
  115. package/dist/src/trails/add-verify.js.map +0 -1
  116. package/dist/src/trails/create-scaffold.d.ts +0 -15
  117. package/dist/src/trails/create-scaffold.d.ts.map +0 -1
  118. package/dist/src/trails/create-scaffold.js +0 -288
  119. package/dist/src/trails/create-scaffold.js.map +0 -1
  120. package/dist/src/trails/create.d.ts +0 -22
  121. package/dist/src/trails/create.d.ts.map +0 -1
  122. package/dist/src/trails/create.js +0 -121
  123. package/dist/src/trails/create.js.map +0 -1
  124. package/dist/src/trails/guide.d.ts +0 -11
  125. package/dist/src/trails/guide.d.ts.map +0 -1
  126. package/dist/src/trails/guide.js +0 -80
  127. package/dist/src/trails/guide.js.map +0 -1
  128. package/dist/src/trails/load-app.d.ts +0 -4
  129. package/dist/src/trails/load-app.d.ts.map +0 -1
  130. package/dist/src/trails/load-app.js +0 -24
  131. package/dist/src/trails/load-app.js.map +0 -1
  132. package/dist/src/trails/project.d.ts +0 -8
  133. package/dist/src/trails/project.d.ts.map +0 -1
  134. package/dist/src/trails/project.js +0 -43
  135. package/dist/src/trails/project.js.map +0 -1
  136. package/dist/src/trails/survey.d.ts +0 -33
  137. package/dist/src/trails/survey.d.ts.map +0 -1
  138. package/dist/src/trails/survey.js +0 -225
  139. package/dist/src/trails/survey.js.map +0 -1
  140. package/dist/src/trails/warden.d.ts +0 -19
  141. package/dist/src/trails/warden.d.ts.map +0 -1
  142. package/dist/src/trails/warden.js +0 -88
  143. package/dist/src/trails/warden.js.map +0 -1
  144. package/dist/tsconfig.tsbuildinfo +0 -1
  145. package/src/__tests__/create.test.ts +0 -349
  146. package/src/__tests__/guide.test.ts +0 -91
  147. package/src/__tests__/load-app.test.ts +0 -15
  148. package/src/__tests__/survey.test.ts +0 -161
  149. package/src/__tests__/warden.test.ts +0 -74
  150. package/tsconfig.json +0 -9
package/README.md ADDED
@@ -0,0 +1,26 @@
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 committed topo artifacts.
17
+ - `trails validate` checks committed topo artifacts for drift.
18
+ - `trails wayfind overview` and adjacent `trails wayfind ...` commands read
19
+ saved topo artifacts through Wayfinder for local graph navigation.
20
+ - `trails warden` runs Trails governance checks for contract and architecture drift.
21
+ - `trails guide` shows available trails and examples from a project.
22
+
23
+ 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.
24
+
25
+ See the main Trails documentation for the full framework guide:
26
+ <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.2",
3
+ "version": "1.0.0-beta.22",
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,23 @@
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.22",
31
+ "@ontrails/cli": "^1.0.0-beta.22",
32
+ "@ontrails/commander": "^1.0.0-beta.22",
33
+ "@ontrails/core": "^1.0.0-beta.22",
34
+ "@ontrails/http": "^1.0.0-beta.22",
35
+ "@ontrails/mcp": "^1.0.0-beta.22",
36
+ "@ontrails/observe": "^1.0.0-beta.22",
37
+ "@ontrails/permits": "^1.0.0-beta.22",
38
+ "@ontrails/topographer": "^1.0.0-beta.22",
39
+ "@ontrails/tracing": "^1.0.0-beta.22",
40
+ "@ontrails/warden": "^1.0.0-beta.22",
41
+ "@ontrails/wayfinder": "^1.0.0-beta.22",
22
42
  "commander": "^14.0.3",
43
+ "typescript": "^5.9.3",
23
44
  "zod": "^4.3.5"
24
45
  },
25
46
  "devDependencies": {
26
- "@ontrails/testing": "^1.0.0-beta.0"
47
+ "@ontrails/testing": "^1.0.0-beta.22"
27
48
  }
28
49
  }
package/src/app.ts CHANGED
@@ -1,22 +1,106 @@
1
1
  import { topo } from '@ontrails/core';
2
+ import {
3
+ wayfindAdaptersTrail,
4
+ wayfindContractTrail,
5
+ wayfindDescribeTrail,
6
+ wayfindErrorsTrail,
7
+ wayfindExamplesTrail,
8
+ wayfindImpactTrail,
9
+ wayfindNearbyTrail,
10
+ wayfindOverviewTrail,
11
+ wayfindSearchTrail,
12
+ wayfindTrailsTrail,
13
+ } from '@ontrails/wayfinder';
2
14
 
3
15
  import * as addSurface from './trails/add-surface.js';
4
16
  import * as addTrail from './trails/add-trail.js';
5
17
  import * as addVerify from './trails/add-verify.js';
18
+ import * as adapterCheck from './trails/adapter-check.js';
19
+ import * as compile from './trails/compile.js';
20
+ import * as completions from './trails/completions.js';
21
+ import * as completionsComplete from './trails/completions-complete.js';
6
22
  import * as create from './trails/create.js';
23
+ import * as createAdapter from './trails/create-adapter.js';
7
24
  import * as createScaffold from './trails/create-scaffold.js';
25
+ import * as createVersions from './trails/create-versions.js';
26
+ import * as deprecate from './trails/deprecate.js';
27
+ import * as devClean from './trails/dev-clean.js';
28
+ import * as devReset from './trails/dev-reset.js';
29
+ import * as devStats from './trails/dev-stats.js';
30
+ import * as doctor from './trails/doctor.js';
31
+ import * as draftPromote from './trails/draft-promote.js';
8
32
  import * as guide from './trails/guide.js';
33
+ import * as releaseCheck from './trails/release-check.js';
34
+ import * as releaseSmoke from './trails/release-smoke.js';
35
+ import * as revise from './trails/revise.js';
36
+ import * as run from './trails/run.js';
37
+ import * as runExample from './trails/run-example.js';
38
+ import * as runExamples from './trails/run-examples.js';
9
39
  import * as survey from './trails/survey.js';
40
+ import * as topoHistory from './trails/topo-history.js';
41
+ import * as topoPin from './trails/topo-pin.js';
42
+ import * as topoCommand from './trails/topo.js';
43
+ import * as topoUnpin from './trails/topo-unpin.js';
44
+ import * as validate from './trails/validate.js';
10
45
  import * as warden from './trails/warden.js';
46
+ import * as wardenGuide from './trails/warden-guide.js';
11
47
 
12
- export const app = topo(
48
+ export const operatorApp = topo(
13
49
  'trails',
50
+ run,
51
+ runExamples,
52
+ runExample,
14
53
  survey,
54
+ topoCommand,
55
+ compile,
56
+ topoHistory,
57
+ topoPin,
58
+ topoUnpin,
59
+ validate,
60
+ revise,
61
+ deprecate,
62
+ doctor,
63
+ devStats,
64
+ devClean,
65
+ devReset,
15
66
  guide,
67
+ releaseCheck,
68
+ releaseSmoke,
69
+ draftPromote,
70
+ adapterCheck,
16
71
  warden,
72
+ wardenGuide,
17
73
  create,
74
+ createAdapter,
18
75
  createScaffold,
76
+ createVersions,
19
77
  addSurface,
20
78
  addVerify,
21
- addTrail
79
+ addTrail,
80
+ completions,
81
+ completionsComplete
22
82
  );
83
+
84
+ const operatorTrails = Object.fromEntries(
85
+ operatorApp.list().map((trailItem) => [trailItem.id, trailItem])
86
+ );
87
+
88
+ const cliWayfinderTrails = {
89
+ wayfindAdaptersTrail,
90
+ wayfindContractTrail,
91
+ wayfindDescribeTrail,
92
+ wayfindErrorsTrail,
93
+ wayfindExamplesTrail,
94
+ wayfindImpactTrail,
95
+ wayfindNearbyTrail,
96
+ wayfindOverviewTrail,
97
+ wayfindSearchTrail,
98
+ wayfindTrailsTrail,
99
+ };
100
+
101
+ export const trailsCliIncludedTrails = [
102
+ ...operatorApp.list().map((trailItem) => trailItem.id),
103
+ ...Object.values(cliWayfinderTrails).map((trailItem) => trailItem.id),
104
+ ];
105
+
106
+ export const app = topo('trails', operatorTrails, cliWayfinderTrails);
package/src/clack.ts CHANGED
@@ -55,8 +55,30 @@ const fieldResolvers: Record<Field['type'], FieldResolver> = {
55
55
  const raw = await clack.text({ message: field.label });
56
56
  return clack.isCancel(raw) ? undefined : Number(raw);
57
57
  },
58
+ 'number[]': async (field) => {
59
+ const raw = await clack.text({
60
+ message: `${field.label} (comma-separated numbers)`,
61
+ });
62
+ if (clack.isCancel(raw)) {
63
+ return;
64
+ }
65
+ return String(raw)
66
+ .split(',')
67
+ .map((s) => Number(s.trim()));
68
+ },
58
69
  string: async (field) =>
59
70
  cancelable(await clack.text({ message: field.label })),
71
+ 'string[]': async (field) => {
72
+ const raw = await clack.text({
73
+ message: `${field.label} (comma-separated)`,
74
+ });
75
+ if (clack.isCancel(raw)) {
76
+ return;
77
+ }
78
+ return String(raw)
79
+ .split(',')
80
+ .map((s) => s.trim());
81
+ },
60
82
  };
61
83
 
62
84
  /** Resolve a single field value with Clack. */
package/src/cli.ts CHANGED
@@ -1,14 +1,333 @@
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
+ outputModePreset,
9
+ permitPreset,
10
+ tokenPreset,
11
+ tracePreset,
12
+ watchPreset,
13
+ } from '@ontrails/cli';
14
+ import type {
15
+ ActionResultContext,
16
+ ResolveCliPermitFromToken,
17
+ } from '@ontrails/cli';
18
+ import { createProgram } from '@ontrails/commander';
19
+ import { resolvePermitFromBearerToken } from '@ontrails/permits';
20
+ import { deriveTopoGraph } from '@ontrails/topographer';
21
+
22
+ import { app, trailsCliIncludedTrails } from './app.js';
5
23
  import { resolveInputWithClack } from './clack.js';
24
+ import { getRetiredTopoCommandDiagnostic } from './retired-topo-command.js';
25
+ import { attachCompletionsInstallCommand } from './run-completions-install.js';
26
+ import {
27
+ applyAdapterCheckExitCode,
28
+ tryAdapterCheckOutput,
29
+ } from './run-adapter-check.js';
30
+ import {
31
+ applyReleaseCheckExitCode,
32
+ tryReleaseCheckOutput,
33
+ } from './run-release-check.js';
34
+ import { tryRecoverFromRunCollision } from './run-collision.js';
35
+ import { tryExampleRunOutput } from './run-example.js';
36
+ import { tryExamplesRunOutput } from './run-examples.js';
37
+ import { tryQuietRunOutput } from './run-quiet.js';
38
+ import {
39
+ argvHasTraceFlag,
40
+ installTraceSink,
41
+ tryTraceJsonOutput,
42
+ writeTraceTreeToStderr,
43
+ } from './run-trace.js';
44
+ import type { TraceSession } from './run-trace.js';
45
+ import {
46
+ argvHasWatchFlag,
47
+ hashTopoGraphEntry,
48
+ readRunTrailId,
49
+ runWatchLoop,
50
+ } from './run-watch.js';
51
+ import { tryWardenOutput } from './run-warden.js';
52
+ import { tryLoadFreshAppLease } from './trails/load-app.js';
53
+ import { resolveRunModulePath } from './trails/run.js';
54
+ import { resolveTrailRootDir } from './trails/root-dir.js';
55
+ import { trailsPackageVersion } from './versions.js';
56
+
57
+ const buildOnResult =
58
+ (session: TraceSession | undefined) =>
59
+ async (ctx: ActionResultContext): Promise<void> => {
60
+ const recovered = await tryRecoverFromRunCollision(ctx, { graph: app });
61
+ const resolvedCtx: ActionResultContext =
62
+ recovered === undefined
63
+ ? ctx
64
+ : {
65
+ ...ctx,
66
+ input: recovered.isOk()
67
+ ? (ctx.trail.input.safeParse(ctx.input).data ?? ctx.input)
68
+ : ctx.input,
69
+ result: recovered,
70
+ };
71
+
72
+ // `--trace --json` (without `--quiet`) emits a single Result-shaped
73
+ // envelope on stdout that includes the captured records under
74
+ // `tracing`. Hand that case off before the regular chain so the
75
+ // existing handlers do not also write to stdout.
76
+ applyAdapterCheckExitCode(resolvedCtx);
77
+ applyReleaseCheckExitCode(resolvedCtx);
78
+ if (session !== undefined && tryTraceJsonOutput(resolvedCtx, session)) {
79
+ return;
80
+ }
81
+
82
+ if (tryExampleRunOutput(resolvedCtx)) {
83
+ return;
84
+ }
85
+ if (tryExamplesRunOutput(resolvedCtx)) {
86
+ return;
87
+ }
88
+ if (await tryQuietRunOutput(resolvedCtx)) {
89
+ return;
90
+ }
91
+ if (tryWardenOutput(resolvedCtx)) {
92
+ return;
93
+ }
94
+ if (tryAdapterCheckOutput(resolvedCtx)) {
95
+ return;
96
+ }
97
+ if (tryReleaseCheckOutput(resolvedCtx)) {
98
+ return;
99
+ }
100
+ await defaultOnResult(resolvedCtx);
101
+ };
102
+
103
+ const traceEnabled = argvHasTraceFlag(process.argv);
104
+ const maybeInstallTraceSession = (): TraceSession | undefined =>
105
+ traceEnabled ? installTraceSink() : undefined;
106
+
107
+ const resolveCliPermitFromToken: ResolveCliPermitFromToken = (input) =>
108
+ resolvePermitFromBearerToken({
109
+ bearerToken: input.token,
110
+ configValues: input.configValues,
111
+ env: process.env as Record<string, string | undefined>,
112
+ graph: input.graph,
113
+ missingAuthResourceMessage:
114
+ '--token requires an auth adapter. Register authResource from @ontrails/permits in your topo.',
115
+ nullPermitMessage: 'Auth adapter did not produce a permit for --token',
116
+ requestId: input.requestId,
117
+ resources: input.resources,
118
+ surface: 'cli',
119
+ });
120
+
121
+ interface WatchRunTarget {
122
+ readonly app?: string | undefined;
123
+ readonly id: string;
124
+ readonly module?: string | undefined;
125
+ readonly rootDir?: string | undefined;
126
+ }
127
+
128
+ const readFlagValue = (
129
+ args: readonly string[],
130
+ flagName: string
131
+ ): string | undefined => {
132
+ const longFlag = `--${flagName}`;
133
+ const prefixedFlag = `${longFlag}=`;
134
+ for (let i = 0; i < args.length; i += 1) {
135
+ const arg = args[i];
136
+ if (arg === longFlag) {
137
+ return args[i + 1];
138
+ }
139
+ if (arg?.startsWith(prefixedFlag)) {
140
+ return arg.slice(prefixedFlag.length);
141
+ }
142
+ }
143
+ return undefined;
144
+ };
145
+
146
+ const resolveWatchRunTarget = (
147
+ argv: readonly string[]
148
+ ): WatchRunTarget | null => {
149
+ const args = argv.slice(2);
150
+ const id = readRunTrailId(args);
151
+ if (id === undefined) {
152
+ return null;
153
+ }
154
+ return {
155
+ app: readFlagValue(args, 'app'),
156
+ id,
157
+ module: readFlagValue(args, 'module'),
158
+ rootDir: readFlagValue(args, 'root-dir'),
159
+ };
160
+ };
161
+
162
+ /**
163
+ * Resolve the directory whose source-file events wake the `--watch` loop.
164
+ * Reruns still depend on the resolved TopoGraph entry hash; this path is
165
+ * only the cheap filesystem event source.
166
+ */
167
+ const toWatchSourcePath = (rootDir: string, modulePath: string): string => {
168
+ if (modulePath.startsWith('file:')) {
169
+ return fileURLToPath(modulePath);
170
+ }
171
+ return isAbsolute(modulePath) ? modulePath : resolve(rootDir, modulePath);
172
+ };
173
+
174
+ const resolveWatchDirectorySourcePath = async (
175
+ target: WatchRunTarget | null
176
+ ): Promise<string> => {
177
+ if (target !== null) {
178
+ const rootDirResult = resolveTrailRootDir(target.rootDir, process.cwd());
179
+ if (rootDirResult.isErr()) {
180
+ throw rootDirResult.error;
181
+ }
182
+ const moduleResult = await resolveRunModulePath(
183
+ rootDirResult.value,
184
+ target.module,
185
+ target.id,
186
+ target.app
187
+ );
188
+ if (moduleResult.isOk()) {
189
+ return toWatchSourcePath(rootDirResult.value, moduleResult.value);
190
+ }
191
+ }
192
+ const cwd = process.cwd();
193
+ const srcDir = join(cwd, 'src');
194
+ if (existsSync(srcDir)) {
195
+ return join(srcDir, 'app.ts');
196
+ }
197
+ return join(cwd, 'app.ts');
198
+ };
199
+
200
+ const readWatchTopoGraphEntryHash = async (
201
+ target: WatchRunTarget | null
202
+ ): Promise<string | null> => {
203
+ if (target === null) {
204
+ return null;
205
+ }
206
+ const rootDirResult = resolveTrailRootDir(target.rootDir, process.cwd());
207
+ if (rootDirResult.isErr()) {
208
+ throw rootDirResult.error;
209
+ }
210
+ const rootDir = rootDirResult.value;
211
+ const moduleResult = await resolveRunModulePath(
212
+ rootDir,
213
+ target.module,
214
+ target.id,
215
+ target.app
216
+ );
217
+ if (moduleResult.isErr()) {
218
+ throw moduleResult.error;
219
+ }
220
+ const leaseResult = await tryLoadFreshAppLease(moduleResult.value, rootDir);
221
+ if (leaseResult.isErr()) {
222
+ throw leaseResult.error;
223
+ }
224
+ const lease = leaseResult.value;
225
+ try {
226
+ const topoGraph = deriveTopoGraph(lease.app);
227
+ const entry = topoGraph.entries.find(
228
+ (candidate) => candidate.kind === 'trail' && candidate.id === target.id
229
+ );
230
+ return entry === undefined ? null : hashTopoGraphEntry(entry);
231
+ } finally {
232
+ lease.release();
233
+ }
234
+ };
235
+
236
+ const wardenValueFlags = new Set([
237
+ '--apps',
238
+ '--config-path',
239
+ '--depth',
240
+ '--drafts',
241
+ '--fail-on',
242
+ '--format',
243
+ '--lock',
244
+ '--root-dir',
245
+ ]);
246
+
247
+ const normalizeWardenArgv = (argv: readonly string[]): string[] => {
248
+ if (argv[2] !== 'warden') {
249
+ return [...argv];
250
+ }
251
+
252
+ const normalized = [...argv];
253
+ let previousFlagConsumesValue = false;
254
+ for (let index = 3; index < normalized.length; index += 1) {
255
+ const arg = normalized[index];
256
+ if (arg === undefined) {
257
+ continue;
258
+ }
259
+
260
+ if (previousFlagConsumesValue) {
261
+ previousFlagConsumesValue = false;
262
+ continue;
263
+ }
264
+
265
+ if (arg === '-a') {
266
+ normalized[index] = '--apps';
267
+ previousFlagConsumesValue = true;
268
+ continue;
269
+ }
270
+
271
+ previousFlagConsumesValue = wardenValueFlags.has(arg);
272
+ }
273
+
274
+ return normalized;
275
+ };
276
+
277
+ /**
278
+ * Invoke `surface()` once with an optional fresh trace session.
279
+ *
280
+ * When `--trace` is set, a fresh {@link TraceSession} is installed for the
281
+ * duration of the call and finalized in the `finally` block. Under
282
+ * `--watch`, this produces a fresh sink (and a fresh stderr tree) per
283
+ * rerun rather than letting records accumulate in a single
284
+ * process-lifetime sink.
285
+ */
286
+ const runSurfaceOnce = async (): Promise<void> => {
287
+ const retiredTopoCommand = getRetiredTopoCommandDiagnostic(process.argv);
288
+ if (retiredTopoCommand !== null) {
289
+ process.stderr.write(`${retiredTopoCommand.message}\n`);
290
+ process.exitCode = 1;
291
+ return;
292
+ }
293
+
294
+ const session = maybeInstallTraceSession();
295
+ try {
296
+ const program = createProgram(app, {
297
+ description: 'Agent-native, contract-first TypeScript framework',
298
+ include: trailsCliIncludedTrails,
299
+ name: 'trails',
300
+ onResult: buildOnResult(session),
301
+ presets: [
302
+ outputModePreset(),
303
+ tracePreset(),
304
+ permitPreset(),
305
+ tokenPreset(),
306
+ devPermitPreset(),
307
+ watchPreset(),
308
+ ],
309
+ resolveInput: resolveInputWithClack,
310
+ resolvePermitFromToken: resolveCliPermitFromToken,
311
+ version: trailsPackageVersion,
312
+ });
313
+ attachCompletionsInstallCommand(program);
314
+ await program.parseAsync(normalizeWardenArgv(process.argv));
315
+ } finally {
316
+ if (session !== undefined) {
317
+ const records = session.finalize();
318
+ writeTraceTreeToStderr(records);
319
+ }
320
+ }
321
+ };
322
+
323
+ const watchTarget = argvHasWatchFlag(process.argv)
324
+ ? resolveWatchRunTarget(process.argv)
325
+ : null;
6
326
 
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
- });
327
+ await (argvHasWatchFlag(process.argv)
328
+ ? runWatchLoop({
329
+ readTopoGraphEntryHash: () => readWatchTopoGraphEntryHash(watchTarget),
330
+ run: runSurfaceOnce,
331
+ sourcePath: await resolveWatchDirectorySourcePath(watchTarget),
332
+ })
333
+ : runSurfaceOnce());