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

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 +1104 -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
@@ -0,0 +1,360 @@
1
+ import { existsSync, statSync } from 'node:fs';
2
+ import { isAbsolute, join } from 'node:path';
3
+
4
+ import {
5
+ openReadTrailsDb,
6
+ openWriteTrailsDb,
7
+ deriveTrailsDbPath,
8
+ } from '@ontrails/core';
9
+ import {
10
+ countPinnedSnapshots,
11
+ countPrunableSnapshots,
12
+ countTopoSnapshots,
13
+ pruneUnpinnedSnapshots,
14
+ } from '@ontrails/topography/backend-support';
15
+ import {
16
+ DEFAULT_MAX_AGE,
17
+ DEFAULT_MAX_RECORDS,
18
+ applyTraceCleanup,
19
+ countTraceRecords,
20
+ previewTraceCleanup,
21
+ } from '@ontrails/tracing';
22
+
23
+ import {
24
+ removeFileIfPresent,
25
+ removeRootRelativeFileIfPresent,
26
+ } from '../local-state-io.js';
27
+
28
+ import { requireTrailRootDir } from './root-dir.js';
29
+
30
+ export const DEFAULT_TOPO_SNAPSHOT_RETENTION = 50;
31
+
32
+ const deriveRootDir = (cwd?: string): string => requireTrailRootDir(cwd);
33
+
34
+ const removeResetFileIfPresent = (
35
+ rootDir: string,
36
+ resetPath: string
37
+ ): boolean => {
38
+ if (isAbsolute(resetPath)) {
39
+ const removed = removeFileIfPresent(resetPath);
40
+ if (removed.isErr()) {
41
+ throw removed.error;
42
+ }
43
+ return removed.value;
44
+ }
45
+
46
+ const removed = removeRootRelativeFileIfPresent(rootDir, resetPath);
47
+ if (removed.isErr()) {
48
+ throw removed.error;
49
+ }
50
+ return removed.value;
51
+ };
52
+
53
+ export interface DevStatsReport {
54
+ readonly db: {
55
+ readonly exists: boolean;
56
+ readonly fileSizeBytes: number;
57
+ readonly path: string;
58
+ };
59
+ readonly lock: {
60
+ readonly exists: boolean;
61
+ readonly fileSizeBytes: number;
62
+ readonly path: string;
63
+ };
64
+ readonly retention: {
65
+ readonly snapshots: number;
66
+ readonly traceAgeMs: number;
67
+ readonly traces: number;
68
+ };
69
+ readonly topo: {
70
+ readonly pinnedCount: number;
71
+ readonly prunableSnapshotCount: number;
72
+ readonly snapshotCount: number;
73
+ };
74
+ readonly tracing: {
75
+ readonly recordCount: number;
76
+ };
77
+ }
78
+
79
+ export interface DevCleanReport {
80
+ readonly dryRun: boolean;
81
+ readonly remaining: {
82
+ readonly pinnedCount: number;
83
+ readonly snapshotCount: number;
84
+ readonly traceCount: number;
85
+ };
86
+ readonly removed: {
87
+ readonly topoSnapshots: number;
88
+ readonly traceRecords: number;
89
+ };
90
+ readonly retention: {
91
+ readonly snapshots: number;
92
+ readonly traceAgeMs: number;
93
+ readonly traces: number;
94
+ };
95
+ }
96
+
97
+ export interface DevResetReport {
98
+ readonly dryRun: boolean;
99
+ readonly removedCount: number;
100
+ readonly removedFiles: readonly string[];
101
+ }
102
+
103
+ interface DevRetentionOptions {
104
+ readonly maxAge?: number;
105
+ readonly maxRecords?: number;
106
+ readonly rootDir?: string;
107
+ readonly snapshotRetention?: number;
108
+ }
109
+
110
+ interface DevCleanupContext {
111
+ readonly dbPath: string;
112
+ readonly dryRun: boolean;
113
+ readonly retention: DevCleanReport['retention'];
114
+ readonly rootDir: string;
115
+ }
116
+
117
+ const buildRetention = (options?: DevRetentionOptions) => ({
118
+ snapshots: options?.snapshotRetention ?? DEFAULT_TOPO_SNAPSHOT_RETENTION,
119
+ traceAgeMs: options?.maxAge ?? DEFAULT_MAX_AGE,
120
+ traces: options?.maxRecords ?? DEFAULT_MAX_RECORDS,
121
+ });
122
+
123
+ const emptyDevClean = (
124
+ retention: DevCleanReport['retention'],
125
+ dryRun: boolean
126
+ ): DevCleanReport => ({
127
+ dryRun,
128
+ remaining: {
129
+ pinnedCount: 0,
130
+ snapshotCount: 0,
131
+ traceCount: 0,
132
+ },
133
+ removed: {
134
+ topoSnapshots: 0,
135
+ traceRecords: 0,
136
+ },
137
+ retention,
138
+ });
139
+
140
+ const buildLockStats = (lockPath: string): DevStatsReport['lock'] => ({
141
+ exists: existsSync(lockPath),
142
+ fileSizeBytes: existsSync(lockPath) ? statSync(lockPath).size : 0,
143
+ path: lockPath,
144
+ });
145
+
146
+ const buildDbStats = (
147
+ dbPath: string,
148
+ exists: boolean
149
+ ): DevStatsReport['db'] => ({
150
+ exists,
151
+ fileSizeBytes: exists ? statSync(dbPath).size : 0,
152
+ path: dbPath,
153
+ });
154
+
155
+ const emptyDevStats = (
156
+ dbPath: string,
157
+ lockPath: string,
158
+ retention: DevStatsReport['retention']
159
+ ): DevStatsReport => ({
160
+ db: buildDbStats(dbPath, false),
161
+ lock: buildLockStats(lockPath),
162
+ retention,
163
+ topo: {
164
+ pinnedCount: 0,
165
+ prunableSnapshotCount: 0,
166
+ snapshotCount: 0,
167
+ },
168
+ tracing: {
169
+ recordCount: 0,
170
+ },
171
+ });
172
+
173
+ const liveDevStats = (
174
+ db: Parameters<typeof countPinnedSnapshots>[0],
175
+ dbPath: string,
176
+ lockPath: string,
177
+ retention: DevStatsReport['retention']
178
+ ): DevStatsReport => ({
179
+ db: buildDbStats(dbPath, true),
180
+ lock: buildLockStats(lockPath),
181
+ retention,
182
+ topo: {
183
+ pinnedCount: countPinnedSnapshots(db),
184
+ prunableSnapshotCount: countPrunableSnapshots(db, {
185
+ keep: retention.snapshots,
186
+ }),
187
+ snapshotCount: countTopoSnapshots(db),
188
+ },
189
+ tracing: {
190
+ recordCount: countTraceRecords(db),
191
+ },
192
+ });
193
+
194
+ const deriveDevStatsContext = (options?: DevRetentionOptions) => {
195
+ const rootDir = deriveRootDir(options?.rootDir);
196
+ const dbPath = deriveTrailsDbPath({ rootDir });
197
+ const lockPath = join(rootDir, 'trails.lock');
198
+ return {
199
+ dbExists: existsSync(dbPath),
200
+ dbPath,
201
+ lockPath,
202
+ retention: buildRetention(options),
203
+ rootDir,
204
+ };
205
+ };
206
+
207
+ const deriveDevCleanupContext = (
208
+ options?: DevRetentionOptions & { readonly dryRun?: boolean }
209
+ ): DevCleanupContext => {
210
+ const rootDir = deriveRootDir(options?.rootDir);
211
+ return {
212
+ dbPath: deriveTrailsDbPath({ rootDir }),
213
+ dryRun: options?.dryRun ?? false,
214
+ retention: buildRetention(options),
215
+ rootDir,
216
+ };
217
+ };
218
+
219
+ const cleanupTraces = (
220
+ db: Parameters<typeof countPinnedSnapshots>[0],
221
+ context: DevCleanupContext
222
+ ) =>
223
+ context.dryRun
224
+ ? previewTraceCleanup(db, {
225
+ maxAge: context.retention.traceAgeMs,
226
+ maxRecords: context.retention.traces,
227
+ })
228
+ : applyTraceCleanup(db, {
229
+ maxAge: context.retention.traceAgeMs,
230
+ maxRecords: context.retention.traces,
231
+ });
232
+
233
+ const cleanupTopoSnapshots = (
234
+ db: Parameters<typeof countPinnedSnapshots>[0],
235
+ context: DevCleanupContext
236
+ ): number =>
237
+ context.dryRun
238
+ ? countPrunableSnapshots(db, { keep: context.retention.snapshots })
239
+ : pruneUnpinnedSnapshots(db, { keep: context.retention.snapshots });
240
+
241
+ const buildCleanReport = (
242
+ db: Parameters<typeof countPinnedSnapshots>[0],
243
+ context: DevCleanupContext
244
+ ): DevCleanReport => {
245
+ const traceReport = cleanupTraces(db, context);
246
+ const topoRemoved = cleanupTopoSnapshots(db, context);
247
+ const snapshotCount = countTopoSnapshots(db);
248
+
249
+ return {
250
+ dryRun: context.dryRun,
251
+ remaining: {
252
+ pinnedCount: countPinnedSnapshots(db),
253
+ snapshotCount: context.dryRun
254
+ ? snapshotCount - topoRemoved
255
+ : snapshotCount,
256
+ traceCount: context.dryRun
257
+ ? traceReport.remaining - traceReport.removedTotal
258
+ : traceReport.remaining,
259
+ },
260
+ removed: {
261
+ topoSnapshots: topoRemoved,
262
+ traceRecords: traceReport.removedTotal,
263
+ },
264
+ retention: context.retention,
265
+ };
266
+ };
267
+
268
+ const dbSidecarPaths = (basePath: string): readonly string[] => [
269
+ basePath,
270
+ `${basePath}-shm`,
271
+ `${basePath}-wal`,
272
+ ];
273
+
274
+ const legacyRepoPath = (...segments: readonly string[]) => segments.join('/');
275
+ const legacyDbName = (...segments: readonly string[]) => segments.join('.');
276
+
277
+ const legacyResetFiles = [
278
+ // Legacy paths (pre-state migration) cleaned for one cycle so upgrading
279
+ // workspaces do not leave stale DB sidecars at old repo-local locations.
280
+ ...dbSidecarPaths(legacyRepoPath('.trails', 'state', 'trails.db')),
281
+ ...dbSidecarPaths(legacyRepoPath('.trails', legacyDbName('trails', 'db'))),
282
+ ...dbSidecarPaths(legacyRepoPath('.trails', 'dev', 'tracing.db')),
283
+ ] as const;
284
+
285
+ const currentResetFiles = (rootDir: string): readonly string[] => {
286
+ const dbPath = deriveTrailsDbPath({ rootDir });
287
+ return dbSidecarPaths(dbPath);
288
+ };
289
+
290
+ const resetFileExists = (rootDir: string, resetPath: string): boolean =>
291
+ existsSync(isAbsolute(resetPath) ? resetPath : join(rootDir, resetPath));
292
+
293
+ const presentResetFiles = (rootDir: string): readonly string[] =>
294
+ [...currentResetFiles(rootDir), ...legacyResetFiles].filter((resetPath) =>
295
+ resetFileExists(rootDir, resetPath)
296
+ );
297
+
298
+ export const buildDevStats = (
299
+ options?: DevRetentionOptions
300
+ ): DevStatsReport => {
301
+ const { dbExists, dbPath, lockPath, retention, rootDir } =
302
+ deriveDevStatsContext(options);
303
+
304
+ if (!dbExists) {
305
+ return emptyDevStats(dbPath, lockPath, retention);
306
+ }
307
+
308
+ const db = openReadTrailsDb({ rootDir });
309
+
310
+ try {
311
+ return liveDevStats(db, dbPath, lockPath, retention);
312
+ } finally {
313
+ db.close();
314
+ }
315
+ };
316
+
317
+ export const cleanDevState = (
318
+ options?: DevRetentionOptions & { readonly dryRun?: boolean }
319
+ ): DevCleanReport => {
320
+ const context = deriveDevCleanupContext(options);
321
+ if (!existsSync(context.dbPath)) {
322
+ return emptyDevClean(context.retention, context.dryRun);
323
+ }
324
+
325
+ const db = context.dryRun
326
+ ? openReadTrailsDb({ rootDir: context.rootDir })
327
+ : openWriteTrailsDb({ rootDir: context.rootDir });
328
+
329
+ try {
330
+ return buildCleanReport(db, context);
331
+ } finally {
332
+ db.close();
333
+ }
334
+ };
335
+
336
+ export const resetDevState = (options?: {
337
+ readonly dryRun?: boolean;
338
+ readonly rootDir?: string;
339
+ }): DevResetReport => {
340
+ const rootDir = deriveRootDir(options?.rootDir);
341
+ const files = presentResetFiles(rootDir);
342
+
343
+ if (options?.dryRun === true) {
344
+ return {
345
+ dryRun: true,
346
+ removedCount: files.length,
347
+ removedFiles: files,
348
+ };
349
+ }
350
+
351
+ const removedFiles = files.filter((relativePath) =>
352
+ removeResetFileIfPresent(rootDir, relativePath)
353
+ );
354
+
355
+ return {
356
+ dryRun: false,
357
+ removedCount: removedFiles.length,
358
+ removedFiles,
359
+ };
360
+ };
@@ -0,0 +1,77 @@
1
+ import {
2
+ deriveTrailsDir,
3
+ InternalError,
4
+ isTrailsError,
5
+ Result,
6
+ trail,
7
+ } from '@ontrails/core';
8
+ import { readTopoGraph } from '@ontrails/topography';
9
+ import { z } from 'zod';
10
+
11
+ import {
12
+ deriveDoctorSummary,
13
+ withLifecycleApp,
14
+ } from './version-lifecycle-support.js';
15
+
16
+ const readDoctorForceGraph = async (
17
+ rootDir: string
18
+ ): Promise<Awaited<ReturnType<typeof readTopoGraph>> | undefined> => {
19
+ try {
20
+ return await readTopoGraph({ dir: deriveTrailsDir({ rootDir }) });
21
+ } catch {
22
+ // Force audit details are supplemental; stale topo locks should not block doctor counts.
23
+ return undefined;
24
+ }
25
+ };
26
+
27
+ const toDoctorSummaryError = (error: unknown): InternalError =>
28
+ error instanceof Error
29
+ ? new InternalError('Unable to derive doctor summary', { cause: error })
30
+ : new InternalError(`Unable to derive doctor summary: ${String(error)}`);
31
+
32
+ export const doctorTrail = trail('doctor', {
33
+ description: 'Diagnose trail versioning lifecycle state',
34
+ implementation: async (input, ctx) =>
35
+ withLifecycleApp(input, ctx.cwd, async (app, rootDir) => {
36
+ const forceGraph = await readDoctorForceGraph(rootDir);
37
+ try {
38
+ return Result.ok(deriveDoctorSummary(app, { forceGraph }));
39
+ } catch (error) {
40
+ // deriveTopoGraph throws ValidationError on invalid topos; doctor must
41
+ // report that diagnosis instead of letting the pipeline redact it to a
42
+ // generic internal error.
43
+ if (isTrailsError(error)) {
44
+ return Result.err(error);
45
+ }
46
+ return Result.err(toDoctorSummaryError(error));
47
+ }
48
+ }),
49
+ input: z.object({
50
+ module: z.string().optional().describe('Path to the app module'),
51
+ rootDir: z.string().optional().describe('Workspace root directory'),
52
+ }),
53
+ intent: 'read',
54
+ output: z.object({
55
+ archived: z.number(),
56
+ deprecated: z.number(),
57
+ forceDetails: z.array(
58
+ z
59
+ .object({
60
+ acceptedAt: z.string(),
61
+ change: z.enum(['modified', 'removed']),
62
+ detail: z.string(),
63
+ id: z.string(),
64
+ kind: z.enum(['entity', 'trail', 'signal', 'resource']),
65
+ reason: z.string().optional(),
66
+ scope: z.enum(['entry', 'graph']),
67
+ severity: z.literal('breaking'),
68
+ source: z.literal('trails compile --force'),
69
+ })
70
+ .strict()
71
+ ),
72
+ forceEvents: z.number(),
73
+ mode: z.literal('doctor'),
74
+ trails: z.number(),
75
+ versioned: z.number(),
76
+ }),
77
+ });