@ontrails/trails 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +1906 -0
  2. package/README.md +48 -0
  3. package/bin/trails.ts +3 -0
  4. package/package.json +57 -0
  5. package/src/app.ts +167 -0
  6. package/src/clack.ts +111 -0
  7. package/src/cli.ts +308 -0
  8. package/src/completions.ts +431 -0
  9. package/src/lifecycle-source-io.ts +33 -0
  10. package/src/load-app-mirror.ts +202 -0
  11. package/src/local-state-io.ts +129 -0
  12. package/src/mcp-app.ts +42 -0
  13. package/src/mcp-options.ts +92 -0
  14. package/src/mcp.ts +8 -0
  15. package/src/project-writes.ts +377 -0
  16. package/src/regrade/audit.ts +571 -0
  17. package/src/regrade/config.ts +152 -0
  18. package/src/regrade/history.ts +636 -0
  19. package/src/regrade/lifecycle.ts +76 -0
  20. package/src/regrade/live-api-preserve.ts +123 -0
  21. package/src/regrade/plan-artifact.ts +515 -0
  22. package/src/regrade/plan-derivation.ts +301 -0
  23. package/src/regrade/prepared-run.ts +259 -0
  24. package/src/regrade/receipt-history.ts +446 -0
  25. package/src/regrade/source-transaction.ts +185 -0
  26. package/src/release/bindings.ts +58 -0
  27. package/src/release/changeset-packages.ts +99 -0
  28. package/src/release/check.ts +1191 -0
  29. package/src/release/cli-bundle.ts +575 -0
  30. package/src/release/config.ts +73 -0
  31. package/src/release/contract-facts.ts +425 -0
  32. package/src/release/homebrew.ts +221 -0
  33. package/src/release/index.ts +180 -0
  34. package/src/release/lock-roundtrip-smoke.ts +255 -0
  35. package/src/release/lock-roundtrip-workspace.ts +107 -0
  36. package/src/release/native-bun-publish.ts +964 -0
  37. package/src/release/native-bun-registry.ts +848 -0
  38. package/src/release/notes-cli.ts +171 -0
  39. package/src/release/notes.ts +390 -0
  40. package/src/release/pack-coherence.ts +455 -0
  41. package/src/release/package-route-facts.ts +146 -0
  42. package/src/release/packed-artifacts-smoke.ts +236 -0
  43. package/src/release/policy.ts +1780 -0
  44. package/src/release/semver.ts +104 -0
  45. package/src/release/smoke.ts +56 -0
  46. package/src/release/stable-version-release.ts +80 -0
  47. package/src/release/wayfinder-dogfood-smoke.ts +762 -0
  48. package/src/release/zero-line-transition.ts +68 -0
  49. package/src/retired-topo-command.ts +36 -0
  50. package/src/run-adapter-check.ts +76 -0
  51. package/src/run-argv.ts +133 -0
  52. package/src/run-collision.ts +126 -0
  53. package/src/run-completions-install.ts +179 -0
  54. package/src/run-example.ts +149 -0
  55. package/src/run-examples.ts +148 -0
  56. package/src/run-quiet.ts +75 -0
  57. package/src/run-regrade-progress.ts +47 -0
  58. package/src/run-release-check.ts +74 -0
  59. package/src/run-schema.ts +74 -0
  60. package/src/run-trace.ts +273 -0
  61. package/src/run-warden.ts +39 -0
  62. package/src/run-watch-project.ts +52 -0
  63. package/src/run-watch.ts +381 -0
  64. package/src/run-wayfind-outline.ts +170 -0
  65. package/src/scaffold-version-sync.ts +183 -0
  66. package/src/scaffold-versions.generated.ts +12 -0
  67. package/src/trails/adapter-check.ts +244 -0
  68. package/src/trails/add-surface.ts +816 -0
  69. package/src/trails/add-trail.ts +141 -0
  70. package/src/trails/add-verify.ts +252 -0
  71. package/src/trails/compile.ts +118 -0
  72. package/src/trails/completions-complete.ts +236 -0
  73. package/src/trails/completions.ts +47 -0
  74. package/src/trails/config-explain.ts +43 -0
  75. package/src/trails/create-adapter.ts +785 -0
  76. package/src/trails/create-scaffold.ts +1215 -0
  77. package/src/trails/create-versions.ts +62 -0
  78. package/src/trails/create.ts +652 -0
  79. package/src/trails/deprecate.ts +59 -0
  80. package/src/trails/dev-clean.ts +80 -0
  81. package/src/trails/dev-reset.ts +48 -0
  82. package/src/trails/dev-stats.ts +71 -0
  83. package/src/trails/dev-support.ts +360 -0
  84. package/src/trails/doctor.ts +77 -0
  85. package/src/trails/draft-promote.ts +949 -0
  86. package/src/trails/guide.ts +106 -0
  87. package/src/trails/load-app.ts +1145 -0
  88. package/src/trails/operator-context.ts +66 -0
  89. package/src/trails/project-context-output.ts +304 -0
  90. package/src/trails/project-context.ts +613 -0
  91. package/src/trails/project.ts +65 -0
  92. package/src/trails/regrade.ts +4951 -0
  93. package/src/trails/release-check.ts +113 -0
  94. package/src/trails/release-smoke.ts +49 -0
  95. package/src/trails/revise.ts +53 -0
  96. package/src/trails/root-dir.ts +21 -0
  97. package/src/trails/run-example.ts +592 -0
  98. package/src/trails/run-examples.ts +149 -0
  99. package/src/trails/run.ts +496 -0
  100. package/src/trails/scaffold-json.ts +60 -0
  101. package/src/trails/scaffold-topo-identity.ts +479 -0
  102. package/src/trails/survey.ts +990 -0
  103. package/src/trails/topo-activation.ts +14 -0
  104. package/src/trails/topo-constants.ts +2 -0
  105. package/src/trails/topo-history.ts +47 -0
  106. package/src/trails/topo-output-schemas.ts +259 -0
  107. package/src/trails/topo-pin.ts +38 -0
  108. package/src/trails/topo-read-support.ts +368 -0
  109. package/src/trails/topo-reports.ts +809 -0
  110. package/src/trails/topo-store-support.ts +323 -0
  111. package/src/trails/topo-support.ts +247 -0
  112. package/src/trails/topo-unpin.ts +61 -0
  113. package/src/trails/topo.ts +92 -0
  114. package/src/trails/validate.ts +348 -0
  115. package/src/trails/version-lifecycle-support.ts +936 -0
  116. package/src/trails/warden-guide.ts +134 -0
  117. package/src/trails/warden.ts +598 -0
  118. package/src/trails/wayfind-diff.ts +716 -0
  119. package/src/trails/wayfind-outline.ts +876 -0
  120. package/src/trails/wayfind.ts +1319 -0
  121. package/src/versions.ts +31 -0
package/src/cli.ts ADDED
@@ -0,0 +1,308 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { isAbsolute, join, resolve } from 'node:path';
4
+
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 { createTrailContext } from '@ontrails/core';
22
+ import { resolvePermitFromBearerToken } from '@ontrails/permits';
23
+
24
+ import { app, trailsCliIncludedTrails, trailsOverlays } from './app.js';
25
+ import { resolveInputWithClack } from './clack.js';
26
+ import { getRetiredTopoCommandDiagnostic } from './retired-topo-command.js';
27
+ import { attachCompletionsInstallCommand } from './run-completions-install.js';
28
+ import { usesStructuredCliOutput } from './run-regrade-progress.js';
29
+ import { attachSchemaCommand } from './run-schema.js';
30
+ import {
31
+ applyAdapterCheckExitCode,
32
+ tryAdapterCheckOutput,
33
+ } from './run-adapter-check.js';
34
+ import {
35
+ applyReleaseCheckExitCode,
36
+ tryReleaseCheckOutput,
37
+ } from './run-release-check.js';
38
+ import { tryRecoverFromRunCollision } from './run-collision.js';
39
+ import { tryExampleRunOutput } from './run-example.js';
40
+ import { tryExamplesRunOutput } from './run-examples.js';
41
+ import { tryQuietRunOutput } from './run-quiet.js';
42
+ import {
43
+ argvHasTraceFlag,
44
+ installTraceSink,
45
+ tryTraceJsonOutput,
46
+ writeTraceTreeToStderr,
47
+ } from './run-trace.js';
48
+ import type { TraceSession } from './run-trace.js';
49
+ import { argvHasWatchFlag, readRunTrailId, runWatchLoop } from './run-watch.js';
50
+ import { readWatchTopoGraphEntryHash } from './run-watch-project.js';
51
+ import type { WatchRunTarget } from './run-watch-project.js';
52
+ import { tryWardenOutput } from './run-warden.js';
53
+ import { tryWayfindOutlineOutput } from './run-wayfind-outline.js';
54
+ import { resolveRunTargetProject } from './trails/run.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
+ if (tryWayfindOutlineOutput(resolvedCtx)) {
101
+ return;
102
+ }
103
+ await defaultOnResult(resolvedCtx);
104
+ };
105
+
106
+ const traceEnabled = argvHasTraceFlag(process.argv);
107
+ const maybeInstallTraceSession = (): TraceSession | undefined =>
108
+ traceEnabled ? installTraceSink() : undefined;
109
+
110
+ const resolveCliPermitFromToken: ResolveCliPermitFromToken = (input) =>
111
+ resolvePermitFromBearerToken({
112
+ bearerToken: input.token,
113
+ configValues: input.configValues,
114
+ env: process.env as Record<string, string | undefined>,
115
+ graph: input.graph,
116
+ missingAuthResourceMessage:
117
+ '--token requires an auth adapter. Register authResource from @ontrails/permits in your topo.',
118
+ nullPermitMessage: 'Auth adapter did not produce a permit for --token',
119
+ requestId: input.requestId,
120
+ resources: input.resources,
121
+ surface: 'cli',
122
+ });
123
+
124
+ const readFlagValue = (
125
+ args: readonly string[],
126
+ flagName: string
127
+ ): string | undefined => {
128
+ const longFlag = `--${flagName}`;
129
+ const prefixedFlag = `${longFlag}=`;
130
+ for (let i = 0; i < args.length; i += 1) {
131
+ const arg = args[i];
132
+ if (arg === longFlag) {
133
+ return args[i + 1];
134
+ }
135
+ if (arg?.startsWith(prefixedFlag)) {
136
+ return arg.slice(prefixedFlag.length);
137
+ }
138
+ }
139
+ return undefined;
140
+ };
141
+
142
+ const resolveWatchRunTarget = (
143
+ argv: readonly string[]
144
+ ): WatchRunTarget | null => {
145
+ const args = argv.slice(2);
146
+ const id = readRunTrailId(args);
147
+ if (id === undefined) {
148
+ return null;
149
+ }
150
+ return {
151
+ app: readFlagValue(args, 'app'),
152
+ id,
153
+ module: readFlagValue(args, 'module'),
154
+ rootDir: readFlagValue(args, 'root-dir'),
155
+ };
156
+ };
157
+
158
+ /**
159
+ * Resolve the directory whose source-file events wake the `--watch` loop.
160
+ * Reruns still depend on the resolved TopoGraph entry hash; this path is
161
+ * only the cheap filesystem event source.
162
+ */
163
+ const toWatchSourcePath = (rootDir: string, modulePath: string): string => {
164
+ if (modulePath.startsWith('file:')) {
165
+ return fileURLToPath(modulePath);
166
+ }
167
+ return isAbsolute(modulePath) ? modulePath : resolve(rootDir, modulePath);
168
+ };
169
+
170
+ const resolveWatchDirectorySourcePath = async (
171
+ target: WatchRunTarget | null
172
+ ): Promise<string> => {
173
+ if (target !== null) {
174
+ const targetResult = await resolveRunTargetProject(target, target.id, {
175
+ cwd: process.cwd(),
176
+ });
177
+ if (targetResult.isOk()) {
178
+ return toWatchSourcePath(
179
+ targetResult.value.rootDir,
180
+ targetResult.value.modulePath
181
+ );
182
+ }
183
+ }
184
+ const cwd = process.cwd();
185
+ const srcDir = join(cwd, 'src');
186
+ if (existsSync(srcDir)) {
187
+ return join(srcDir, 'app.ts');
188
+ }
189
+ return join(cwd, 'app.ts');
190
+ };
191
+
192
+ const wardenValueFlags = new Set([
193
+ '--apps',
194
+ '--config-path',
195
+ '--depth',
196
+ '--drafts',
197
+ '--fail-on',
198
+ '--format',
199
+ '--scope-exclude',
200
+ '--lock',
201
+ '--root-dir',
202
+ ]);
203
+
204
+ const normalizeWardenArgv = (argv: readonly string[]): string[] => {
205
+ if (argv[2] !== 'warden') {
206
+ return [...argv];
207
+ }
208
+
209
+ const normalized = [...argv];
210
+ let previousFlagConsumesValue = false;
211
+ for (let index = 3; index < normalized.length; index += 1) {
212
+ const arg = normalized[index];
213
+ if (arg === undefined) {
214
+ continue;
215
+ }
216
+
217
+ if (previousFlagConsumesValue) {
218
+ previousFlagConsumesValue = false;
219
+ continue;
220
+ }
221
+
222
+ if (arg === '-a') {
223
+ normalized[index] = '--apps';
224
+ previousFlagConsumesValue = true;
225
+ continue;
226
+ }
227
+
228
+ previousFlagConsumesValue = wardenValueFlags.has(arg);
229
+ }
230
+
231
+ return normalized;
232
+ };
233
+
234
+ /**
235
+ * Invoke `surface()` once with an optional fresh trace session.
236
+ *
237
+ * When `--trace` is set, a fresh {@link TraceSession} is installed for the
238
+ * duration of the call and finalized in the `finally` block. Under
239
+ * `--watch`, this produces a fresh sink (and a fresh stderr tree) per
240
+ * rerun rather than letting records accumulate in a single
241
+ * process-lifetime sink.
242
+ */
243
+ const runSurfaceOnce = async (): Promise<void> => {
244
+ const retiredTopoCommand = getRetiredTopoCommandDiagnostic(process.argv);
245
+ if (retiredTopoCommand !== null) {
246
+ process.stderr.write(`${retiredTopoCommand.message}\n`);
247
+ process.exitCode = 1;
248
+ return;
249
+ }
250
+
251
+ const session = maybeInstallTraceSession();
252
+ try {
253
+ const surfaceOptions = {
254
+ createContext: () =>
255
+ createTrailContext({
256
+ progress: (event) => {
257
+ if (
258
+ !usesStructuredCliOutput(process.argv, process.env) &&
259
+ event.message?.startsWith('Regrade:') === true
260
+ ) {
261
+ process.stderr.write(`${event.message}\n`);
262
+ }
263
+ },
264
+ }),
265
+ description: 'Agent-native, contract-first TypeScript framework',
266
+ include: trailsCliIncludedTrails,
267
+ name: 'trails',
268
+ onResult: buildOnResult(session),
269
+ overlays: trailsOverlays,
270
+ presets: [
271
+ outputModePreset(),
272
+ tracePreset(),
273
+ permitPreset(),
274
+ tokenPreset(),
275
+ devPermitPreset(),
276
+ watchPreset(),
277
+ ],
278
+ resolveInput: resolveInputWithClack,
279
+ resolvePermitFromToken: resolveCliPermitFromToken,
280
+ version: trailsPackageVersion,
281
+ } satisfies CreateProgramOptions;
282
+ const program = createProgram(app, surfaceOptions);
283
+ const schemaCommands = deriveCliCommands(app, surfaceOptions);
284
+ if (schemaCommands.isErr()) {
285
+ throw schemaCommands.error;
286
+ }
287
+ attachSchemaCommand(program, schemaCommands.value);
288
+ attachCompletionsInstallCommand(program);
289
+ await program.parseAsync(normalizeWardenArgv(process.argv));
290
+ } finally {
291
+ if (session !== undefined) {
292
+ const records = session.finalize();
293
+ writeTraceTreeToStderr(records);
294
+ }
295
+ }
296
+ };
297
+
298
+ const watchTarget = argvHasWatchFlag(process.argv)
299
+ ? resolveWatchRunTarget(process.argv)
300
+ : null;
301
+
302
+ await (argvHasWatchFlag(process.argv)
303
+ ? runWatchLoop({
304
+ readTopoGraphEntryHash: () => readWatchTopoGraphEntryHash(watchTarget),
305
+ run: runSurfaceOnce,
306
+ sourcePath: await resolveWatchDirectorySourcePath(watchTarget),
307
+ })
308
+ : runSurfaceOnce());