@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.
- package/CHANGELOG.md +1106 -4
- package/README.md +27 -0
- package/package.json +31 -7
- package/src/app.ts +147 -3
- package/src/cli.ts +345 -11
- package/src/completions.ts +240 -0
- package/src/lifecycle-source-io.ts +33 -0
- package/src/load-app-mirror.ts +202 -0
- package/src/local-state-io.ts +173 -0
- package/src/mcp-app.ts +42 -0
- package/src/mcp-options.ts +91 -0
- package/src/mcp.ts +8 -0
- package/src/project-writes.ts +377 -0
- package/src/regrade/config.ts +152 -0
- package/src/regrade/history.ts +430 -0
- package/src/regrade/live-api-preserve.ts +8 -0
- package/src/regrade/plan-artifact.ts +285 -0
- package/src/release/bindings.ts +39 -0
- package/src/release/check.ts +844 -0
- package/src/release/config.ts +63 -0
- package/src/release/contract-facts.ts +425 -0
- package/src/release/index.ts +142 -0
- package/src/release/lock-roundtrip-smoke.ts +230 -0
- package/src/release/native-bun-publish.ts +651 -0
- package/src/release/native-bun-registry.ts +765 -0
- package/src/release/notes-cli.ts +171 -0
- package/src/release/notes.ts +390 -0
- package/src/release/pack-coherence.ts +455 -0
- package/src/release/packed-artifacts-smoke.ts +236 -0
- package/src/release/policy.ts +1687 -0
- package/src/release/semver.ts +104 -0
- package/src/release/smoke.ts +56 -0
- package/src/release/wayfinder-dogfood-smoke.ts +760 -0
- package/src/retired-topo-command.ts +36 -0
- package/src/run-adapter-check.ts +76 -0
- package/src/run-collision.ts +126 -0
- package/src/run-completions-install.ts +179 -0
- package/src/run-example.ts +149 -0
- package/src/run-examples.ts +148 -0
- package/src/run-quiet.ts +75 -0
- package/src/run-release-check.ts +74 -0
- package/src/run-schema.ts +74 -0
- package/src/run-trace.ts +273 -0
- package/src/run-warden.ts +39 -0
- package/src/run-watch.ts +432 -0
- package/src/run-wayfind-outline.ts +170 -0
- package/src/scaffold-version-sync.ts +183 -0
- package/src/scaffold-versions.generated.ts +12 -0
- package/src/trails/adapter-check.ts +244 -0
- package/src/trails/add-surface.ts +99 -45
- package/src/trails/add-trail.ts +84 -37
- package/src/trails/add-verify.ts +100 -30
- package/src/trails/compile.ts +58 -0
- package/src/trails/completions-complete.ts +165 -0
- package/src/trails/completions.ts +47 -0
- package/src/trails/create-adapter.ts +785 -0
- package/src/trails/create-scaffold.ts +401 -106
- package/src/trails/create-versions.ts +62 -0
- package/src/trails/create.ts +186 -72
- package/src/trails/deprecate.ts +59 -0
- package/src/trails/dev-clean.ts +82 -0
- package/src/trails/dev-reset.ts +50 -0
- package/src/trails/dev-stats.ts +72 -0
- package/src/trails/dev-support.ts +360 -0
- package/src/trails/doctor.ts +77 -0
- package/src/trails/draft-promote.ts +949 -0
- package/src/trails/guide.ts +70 -68
- package/src/trails/load-app.ts +1123 -15
- package/src/trails/operator-context.ts +66 -0
- package/src/trails/project.ts +17 -3
- package/src/trails/regrade.ts +3091 -0
- package/src/trails/release-check.ts +105 -0
- package/src/trails/release-smoke.ts +49 -0
- package/src/trails/revise.ts +53 -0
- package/src/trails/root-dir.ts +21 -0
- package/src/trails/run-example.ts +475 -0
- package/src/trails/run-examples.ts +129 -0
- package/src/trails/run.ts +434 -0
- package/src/trails/scaffold-json.ts +58 -0
- package/src/trails/survey.ts +877 -214
- package/src/trails/topo-activation.ts +14 -0
- package/src/trails/topo-constants.ts +2 -0
- package/src/trails/topo-history.ts +47 -0
- package/src/trails/topo-output-schemas.ts +259 -0
- package/src/trails/topo-pin.ts +38 -0
- package/src/trails/topo-read-support.ts +368 -0
- package/src/trails/topo-reports.ts +809 -0
- package/src/trails/topo-store-support.ts +325 -0
- package/src/trails/topo-support.ts +220 -0
- package/src/trails/topo-unpin.ts +61 -0
- package/src/trails/topo.ts +92 -0
- package/src/trails/validate.ts +27 -0
- package/src/trails/version-lifecycle-support.ts +936 -0
- package/src/trails/warden-guide.ts +134 -0
- package/src/trails/warden.ts +198 -58
- package/src/trails/wayfind-outline.ts +876 -0
- package/src/trails/wayfind.ts +1053 -0
- package/src/versions.ts +31 -0
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/__tests__/examples.test.ts +0 -6
- package/dist/bin/trails.d.ts +0 -3
- package/dist/bin/trails.d.ts.map +0 -1
- package/dist/bin/trails.js +0 -4
- package/dist/bin/trails.js.map +0 -1
- package/dist/src/app.d.ts +0 -2
- package/dist/src/app.d.ts.map +0 -1
- package/dist/src/app.js +0 -11
- package/dist/src/app.js.map +0 -1
- package/dist/src/clack.d.ts +0 -9
- package/dist/src/clack.d.ts.map +0 -1
- package/dist/src/clack.js +0 -84
- package/dist/src/clack.js.map +0 -1
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js +0 -13
- package/dist/src/cli.js.map +0 -1
- package/dist/src/trails/add-surface.d.ts +0 -13
- package/dist/src/trails/add-surface.d.ts.map +0 -1
- package/dist/src/trails/add-surface.js +0 -88
- package/dist/src/trails/add-surface.js.map +0 -1
- package/dist/src/trails/add-trail.d.ts +0 -10
- package/dist/src/trails/add-trail.d.ts.map +0 -1
- package/dist/src/trails/add-trail.js +0 -77
- package/dist/src/trails/add-trail.js.map +0 -1
- package/dist/src/trails/add-verify.d.ts +0 -10
- package/dist/src/trails/add-verify.d.ts.map +0 -1
- package/dist/src/trails/add-verify.js +0 -67
- package/dist/src/trails/add-verify.js.map +0 -1
- package/dist/src/trails/create-scaffold.d.ts +0 -15
- package/dist/src/trails/create-scaffold.d.ts.map +0 -1
- package/dist/src/trails/create-scaffold.js +0 -288
- package/dist/src/trails/create-scaffold.js.map +0 -1
- package/dist/src/trails/create.d.ts +0 -22
- package/dist/src/trails/create.d.ts.map +0 -1
- package/dist/src/trails/create.js +0 -121
- package/dist/src/trails/create.js.map +0 -1
- package/dist/src/trails/guide.d.ts +0 -11
- package/dist/src/trails/guide.d.ts.map +0 -1
- package/dist/src/trails/guide.js +0 -80
- package/dist/src/trails/guide.js.map +0 -1
- package/dist/src/trails/load-app.d.ts +0 -4
- package/dist/src/trails/load-app.d.ts.map +0 -1
- package/dist/src/trails/load-app.js +0 -24
- package/dist/src/trails/load-app.js.map +0 -1
- package/dist/src/trails/project.d.ts +0 -8
- package/dist/src/trails/project.d.ts.map +0 -1
- package/dist/src/trails/project.js +0 -43
- package/dist/src/trails/project.js.map +0 -1
- package/dist/src/trails/survey.d.ts +0 -31
- package/dist/src/trails/survey.d.ts.map +0 -1
- package/dist/src/trails/survey.js +0 -221
- package/dist/src/trails/survey.js.map +0 -1
- package/dist/src/trails/warden.d.ts +0 -19
- package/dist/src/trails/warden.d.ts.map +0 -1
- package/dist/src/trails/warden.js +0 -88
- package/dist/src/trails/warden.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/__tests__/create.test.ts +0 -349
- package/src/__tests__/guide.test.ts +0 -91
- package/src/__tests__/load-app.test.ts +0 -15
- package/src/__tests__/survey.test.ts +0 -159
- package/src/__tests__/warden.test.ts +0 -74
- package/tsconfig.json +0 -9
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `create.versions` trail -- Sync generated scaffold dependency versions.
|
|
3
|
+
*
|
|
4
|
+
* Derives `apps/trails/src/scaffold-versions.generated.ts` from the root
|
|
5
|
+
* `package.json` catalog and devDependencies. Graduated from
|
|
6
|
+
* `scripts/sync-scaffold-versions.ts`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Result, trail, ValidationError } from '@ontrails/core';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
|
|
12
|
+
import { syncScaffoldVersions } from '../scaffold-version-sync.js';
|
|
13
|
+
import { resolveTrailRootDir } from './root-dir.js';
|
|
14
|
+
|
|
15
|
+
const createVersionsInputSchema = z.object({
|
|
16
|
+
check: z
|
|
17
|
+
.boolean()
|
|
18
|
+
.default(false)
|
|
19
|
+
.describe('Verify the generated file is current instead of writing'),
|
|
20
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const createVersionsOutputSchema = z.object({
|
|
24
|
+
generatedPath: z.string(),
|
|
25
|
+
mode: z.enum(['check', 'write']),
|
|
26
|
+
written: z.boolean(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const createVersionsTrail = trail('create.versions', {
|
|
30
|
+
description: 'Sync generated scaffold dependency versions',
|
|
31
|
+
examples: [
|
|
32
|
+
{
|
|
33
|
+
input: { check: true },
|
|
34
|
+
name: 'Verify generated scaffold versions are current',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
implementation: async (input, ctx) => {
|
|
38
|
+
const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
|
|
39
|
+
if (rootDirResult.isErr()) {
|
|
40
|
+
return rootDirResult;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
return Result.ok(
|
|
45
|
+
await syncScaffoldVersions({
|
|
46
|
+
check: input.check,
|
|
47
|
+
rootDir: rootDirResult.value,
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return Result.err(
|
|
52
|
+
new ValidationError(
|
|
53
|
+
error instanceof Error ? error.message : String(error)
|
|
54
|
+
)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
input: createVersionsInputSchema,
|
|
59
|
+
intent: 'write',
|
|
60
|
+
output: createVersionsOutputSchema,
|
|
61
|
+
permit: { scopes: ['project:write'] },
|
|
62
|
+
});
|
package/src/trails/create.ts
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `create`
|
|
2
|
+
* `create` trail -- Create a new Trails project.
|
|
3
3
|
*
|
|
4
4
|
* Composes create.scaffold, add.surface, and add.verify sub-trails
|
|
5
|
-
* via ctx.
|
|
5
|
+
* via ctx.compose.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
8
|
+
import { InternalError, Result, trail } from '@ontrails/core';
|
|
9
|
+
import type { TrailContext } from '@ontrails/core';
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
|
|
12
|
+
import {
|
|
13
|
+
PROJECT_NAME_MESSAGE,
|
|
14
|
+
PROJECT_NAME_PATTERN,
|
|
15
|
+
projectPathExists,
|
|
16
|
+
writeProjectFile,
|
|
17
|
+
} from '../project-writes.js';
|
|
18
|
+
|
|
12
19
|
// ---------------------------------------------------------------------------
|
|
13
20
|
// Helpers
|
|
14
21
|
// ---------------------------------------------------------------------------
|
|
15
22
|
|
|
16
23
|
type Starter = 'empty' | 'entity' | 'hello';
|
|
17
|
-
type Surface = 'cli' | 'mcp';
|
|
24
|
+
type Surface = 'cli' | 'http' | 'mcp';
|
|
18
25
|
|
|
19
|
-
interface
|
|
26
|
+
interface CreateInput {
|
|
20
27
|
readonly dir?: string | undefined;
|
|
21
28
|
readonly name: string;
|
|
22
29
|
readonly starter: Starter;
|
|
@@ -42,6 +49,18 @@ interface ScaffoldedProject {
|
|
|
42
49
|
readonly name: string;
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
interface SurfaceResult {
|
|
53
|
+
readonly created: string | null;
|
|
54
|
+
readonly dependency: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type TrailContextWithCompose = TrailContext & {
|
|
58
|
+
readonly compose: NonNullable<TrailContext['compose']>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const hasCompose = (ctx: TrailContext): ctx is TrailContextWithCompose =>
|
|
62
|
+
Boolean(ctx.compose);
|
|
63
|
+
|
|
45
64
|
const buildScaffoldInput = (input: ScaffoldRequest) => ({
|
|
46
65
|
...(input.dir === undefined ? {} : { dir: input.dir }),
|
|
47
66
|
name: input.name,
|
|
@@ -58,45 +77,34 @@ const buildVerifyInput = (input: VerifyRequest) => ({
|
|
|
58
77
|
name: input.name,
|
|
59
78
|
});
|
|
60
79
|
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
): Promise<Result<ScaffoldedProject, Error>> =>
|
|
65
|
-
follow('create.scaffold', buildScaffoldInput(input));
|
|
66
|
-
|
|
67
|
-
const addSurfaceFiles = async (
|
|
68
|
-
follow: FollowFn,
|
|
69
|
-
dir: string,
|
|
70
|
-
surfaces: readonly string[]
|
|
80
|
+
const collectSurfaceFiles = async (
|
|
81
|
+
surfaces: readonly string[],
|
|
82
|
+
addSurface: (surface: string) => Promise<Result<SurfaceResult, Error>>
|
|
71
83
|
): Promise<Result<string[], Error>> => {
|
|
72
84
|
const created: string[] = [];
|
|
73
85
|
|
|
74
86
|
for (const surface of surfaces) {
|
|
75
|
-
const result = await
|
|
76
|
-
'add.surface',
|
|
77
|
-
buildSurfaceInput(dir, surface)
|
|
78
|
-
);
|
|
87
|
+
const result = await addSurface(surface);
|
|
79
88
|
if (result.isErr()) {
|
|
80
|
-
return
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
if (result.value.created !== null) {
|
|
92
|
+
created.push(result.value.created);
|
|
81
93
|
}
|
|
82
|
-
created.push(result.value.created);
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
return Result.ok(created);
|
|
86
97
|
};
|
|
87
98
|
|
|
88
99
|
const collectVerifyFiles = async (
|
|
89
|
-
|
|
90
|
-
|
|
100
|
+
shouldVerify: boolean,
|
|
101
|
+
addVerify: () => Promise<Result<{ created: string[] }, Error>>
|
|
91
102
|
): Promise<Result<string[], Error>> => {
|
|
92
|
-
if (!
|
|
103
|
+
if (!shouldVerify) {
|
|
93
104
|
return Result.ok([]);
|
|
94
105
|
}
|
|
95
106
|
|
|
96
|
-
const result = await
|
|
97
|
-
'add.verify',
|
|
98
|
-
buildVerifyInput(input)
|
|
99
|
-
);
|
|
107
|
+
const result = await addVerify();
|
|
100
108
|
return result.isErr()
|
|
101
109
|
? Result.err(result.error)
|
|
102
110
|
: Result.ok(result.value.created);
|
|
@@ -105,48 +113,94 @@ const collectVerifyFiles = async (
|
|
|
105
113
|
const collectCreatedFiles = (
|
|
106
114
|
scaffolded: readonly string[],
|
|
107
115
|
surfaces: readonly string[],
|
|
108
|
-
verify: readonly string[]
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
): Promise<Result<{ created: string[]; dir: string; name: string }, Error>> => {
|
|
115
|
-
const scaffolded = await scaffoldProject(follow, input);
|
|
116
|
-
if (scaffolded.isErr()) {
|
|
117
|
-
return Result.err(scaffolded.error);
|
|
118
|
-
}
|
|
116
|
+
verify: readonly string[],
|
|
117
|
+
readme: string | null
|
|
118
|
+
): string[] =>
|
|
119
|
+
readme === null
|
|
120
|
+
? [...scaffolded, ...surfaces, ...verify]
|
|
121
|
+
: [...scaffolded, ...surfaces, ...verify, readme];
|
|
119
122
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
const surfaceReadmeLines = {
|
|
124
|
+
cli: '- `src/cli.ts` - CLI surface entry point',
|
|
125
|
+
http: '- `src/http.ts` - HTTP surface entry point',
|
|
126
|
+
mcp: '- `src/mcp.ts` - MCP surface entry point',
|
|
127
|
+
} satisfies Record<Surface, string>;
|
|
128
|
+
|
|
129
|
+
const starterReadmeLines = {
|
|
130
|
+
empty:
|
|
131
|
+
'Starts with an empty `src/trails/` directory for authoring from scratch.',
|
|
132
|
+
entity:
|
|
133
|
+
'Includes sample entity trails, a signal, and an in-memory store for exploration.',
|
|
134
|
+
hello: 'Includes a `hello` trail with examples for the first happy path.',
|
|
135
|
+
} satisfies Record<Starter, string>;
|
|
136
|
+
|
|
137
|
+
const generateReadme = (input: CreateInput): string => {
|
|
138
|
+
const surfaceLines = input.surfaces
|
|
139
|
+
.map((surface) => surfaceReadmeLines[surface])
|
|
140
|
+
.join('\n');
|
|
141
|
+
const verificationCommand = input.verify ? 'bun test\n' : '';
|
|
142
|
+
const verificationStructure = input.verify
|
|
143
|
+
? '- `__tests__/examples.test.ts` - examples-as-tests harness\n'
|
|
144
|
+
: '- Verification files were not generated for this project\n';
|
|
145
|
+
|
|
146
|
+
return `# ${input.name}
|
|
147
|
+
|
|
148
|
+
A Trails project. Trails is an agent-native, contract-first TypeScript framework: author a trail once with typed input, Result output, examples, intent, and meta; surface it through CLI, MCP, HTTP, or future WebSocket.
|
|
149
|
+
|
|
150
|
+
## Getting Started
|
|
151
|
+
|
|
152
|
+
\`\`\`bash
|
|
153
|
+
bun install
|
|
154
|
+
${verificationCommand}bun run warden
|
|
155
|
+
bun run survey
|
|
156
|
+
bun run guide
|
|
157
|
+
\`\`\`
|
|
158
|
+
|
|
159
|
+
## Project Structure
|
|
160
|
+
|
|
161
|
+
- \`src/app.ts\` - the topo that collects this project's trails
|
|
162
|
+
- \`src/trails/\` - trail definitions
|
|
163
|
+
${surfaceLines}
|
|
164
|
+
${verificationStructure}- \`AGENTS.md\` - project guidance for agents working in this app
|
|
128
165
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
166
|
+
## Starter
|
|
167
|
+
|
|
168
|
+
${starterReadmeLines[input.starter]}
|
|
169
|
+
|
|
170
|
+
## Next Steps
|
|
171
|
+
|
|
172
|
+
- Add a trail with \`bun run add\`
|
|
173
|
+
- Run \`bun run warden\` before review
|
|
174
|
+
- Read \`AGENTS.md\` for Trails vocabulary and conventions
|
|
175
|
+
`;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const writeReadme = async (
|
|
179
|
+
input: CreateInput,
|
|
180
|
+
dir: string
|
|
181
|
+
): Promise<Result<string | null, Error>> => {
|
|
182
|
+
const exists = projectPathExists(dir, 'README.md');
|
|
183
|
+
if (exists.isErr()) {
|
|
184
|
+
return exists;
|
|
185
|
+
}
|
|
186
|
+
if (exists.value) {
|
|
187
|
+
return Result.ok(null);
|
|
132
188
|
}
|
|
133
189
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
dir: scaffolded.value.dir,
|
|
141
|
-
name: input.name,
|
|
142
|
-
});
|
|
190
|
+
const written = await writeProjectFile(
|
|
191
|
+
dir,
|
|
192
|
+
'README.md',
|
|
193
|
+
generateReadme(input)
|
|
194
|
+
);
|
|
195
|
+
return written.isErr() ? Result.err(written.error) : Result.ok('README.md');
|
|
143
196
|
};
|
|
144
197
|
|
|
145
198
|
// ---------------------------------------------------------------------------
|
|
146
|
-
//
|
|
199
|
+
// Trail definition
|
|
147
200
|
// ---------------------------------------------------------------------------
|
|
148
201
|
|
|
149
|
-
export const
|
|
202
|
+
export const createTrail = trail('create', {
|
|
203
|
+
composes: ['create.scaffold', 'add.surface', 'add.verify'],
|
|
150
204
|
description: 'Create a new Trails project',
|
|
151
205
|
fields: {
|
|
152
206
|
starter: {
|
|
@@ -157,7 +211,7 @@ export const createRoute = trail('create', {
|
|
|
157
211
|
value: 'hello',
|
|
158
212
|
},
|
|
159
213
|
{
|
|
160
|
-
hint: '4 trails,
|
|
214
|
+
hint: '4 trails, signal, store',
|
|
161
215
|
label: 'Entity CRUD',
|
|
162
216
|
value: 'entity',
|
|
163
217
|
},
|
|
@@ -172,19 +226,84 @@ export const createRoute = trail('create', {
|
|
|
172
226
|
label: 'MCP',
|
|
173
227
|
value: 'mcp',
|
|
174
228
|
},
|
|
229
|
+
{
|
|
230
|
+
hint: 'Hono-powered HTTP endpoints',
|
|
231
|
+
label: 'HTTP',
|
|
232
|
+
value: 'http',
|
|
233
|
+
},
|
|
175
234
|
],
|
|
176
235
|
},
|
|
177
236
|
},
|
|
178
|
-
|
|
237
|
+
implementation: async (input: CreateInput, ctx) => {
|
|
238
|
+
if (!hasCompose(ctx)) {
|
|
239
|
+
return Result.err(new InternalError('create trail requires ctx.compose'));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const scaffolded = await ctx.compose<ScaffoldedProject>(
|
|
243
|
+
'create.scaffold',
|
|
244
|
+
buildScaffoldInput(input)
|
|
245
|
+
);
|
|
246
|
+
if (scaffolded.isErr()) {
|
|
247
|
+
return scaffolded;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const finishCreate = async (): Promise<
|
|
251
|
+
Result<{ created: string[]; dir: string; name: string }, Error>
|
|
252
|
+
> => {
|
|
253
|
+
const surfaceFiles = await collectSurfaceFiles(
|
|
254
|
+
input.surfaces,
|
|
255
|
+
(surface) =>
|
|
256
|
+
ctx.compose<SurfaceResult>(
|
|
257
|
+
'add.surface',
|
|
258
|
+
buildSurfaceInput(scaffolded.value.dir, surface)
|
|
259
|
+
)
|
|
260
|
+
);
|
|
261
|
+
if (surfaceFiles.isErr()) {
|
|
262
|
+
return surfaceFiles;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const verifyFiles = await collectVerifyFiles(input.verify, () =>
|
|
266
|
+
ctx.compose<{ created: string[] }>(
|
|
267
|
+
'add.verify',
|
|
268
|
+
buildVerifyInput(input)
|
|
269
|
+
)
|
|
270
|
+
);
|
|
271
|
+
if (verifyFiles.isErr()) {
|
|
272
|
+
return verifyFiles;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const readmeFile = await writeReadme(input, scaffolded.value.dir);
|
|
276
|
+
if (readmeFile.isErr()) {
|
|
277
|
+
return readmeFile;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return Result.ok({
|
|
281
|
+
created: collectCreatedFiles(
|
|
282
|
+
scaffolded.value.created,
|
|
283
|
+
surfaceFiles.value,
|
|
284
|
+
verifyFiles.value,
|
|
285
|
+
readmeFile.value
|
|
286
|
+
),
|
|
287
|
+
dir: scaffolded.value.dir,
|
|
288
|
+
name: input.name,
|
|
289
|
+
});
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
return finishCreate();
|
|
293
|
+
},
|
|
179
294
|
input: z.object({
|
|
180
295
|
dir: z.string().optional().describe('Parent directory'),
|
|
181
|
-
name: z
|
|
296
|
+
name: z
|
|
297
|
+
.string()
|
|
298
|
+
.regex(PROJECT_NAME_PATTERN, PROJECT_NAME_MESSAGE)
|
|
299
|
+
.describe('Project name'),
|
|
182
300
|
starter: z
|
|
183
301
|
.enum(['hello', 'entity', 'empty'])
|
|
184
302
|
.default('hello')
|
|
185
303
|
.describe('Starter trail'),
|
|
186
304
|
surfaces: z
|
|
187
|
-
.array(z.enum(['cli', 'mcp']))
|
|
305
|
+
.array(z.enum(['cli', 'http', 'mcp']))
|
|
306
|
+
.min(1)
|
|
188
307
|
.default(['cli'])
|
|
189
308
|
.describe('Surfaces'),
|
|
190
309
|
verify: z.boolean().default(true).describe('Include testing + warden'),
|
|
@@ -194,10 +313,5 @@ export const createRoute = trail('create', {
|
|
|
194
313
|
dir: z.string(),
|
|
195
314
|
name: z.string(),
|
|
196
315
|
}),
|
|
197
|
-
|
|
198
|
-
if (!ctx.follow) {
|
|
199
|
-
return Result.err(new Error('create route requires ctx.follow'));
|
|
200
|
-
}
|
|
201
|
-
return await runCreate(ctx.follow, input);
|
|
202
|
-
},
|
|
316
|
+
permit: { scopes: ['project:write'] },
|
|
203
317
|
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { trail } from '@ontrails/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
parseLifecycleTarget,
|
|
6
|
+
setVersionStatusSource,
|
|
7
|
+
withLifecycleApp,
|
|
8
|
+
} from './version-lifecycle-support.js';
|
|
9
|
+
|
|
10
|
+
export const deprecateTrail = trail('deprecate', {
|
|
11
|
+
args: ['target'],
|
|
12
|
+
description: 'Mark a historical trail version deprecated or archived',
|
|
13
|
+
implementation: async (input, ctx) =>
|
|
14
|
+
withLifecycleApp(input, ctx.cwd, async (_app, rootDir) => {
|
|
15
|
+
const target = parseLifecycleTarget(input.target);
|
|
16
|
+
if (target.isErr()) {
|
|
17
|
+
return target;
|
|
18
|
+
}
|
|
19
|
+
return setVersionStatusSource(
|
|
20
|
+
rootDir,
|
|
21
|
+
target.value,
|
|
22
|
+
input.archive ? 'archived' : 'deprecated',
|
|
23
|
+
{
|
|
24
|
+
migration: input.migration,
|
|
25
|
+
note: input.note,
|
|
26
|
+
reason: input.reason,
|
|
27
|
+
successor: input.successor,
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
}),
|
|
31
|
+
input: z.object({
|
|
32
|
+
archive: z
|
|
33
|
+
.boolean()
|
|
34
|
+
.default(false)
|
|
35
|
+
.describe('Archive instead of deprecate'),
|
|
36
|
+
migration: z
|
|
37
|
+
.array(z.string())
|
|
38
|
+
.default([])
|
|
39
|
+
.describe('Migration guidance entries'),
|
|
40
|
+
module: z.string().optional().describe('Path to the app module'),
|
|
41
|
+
note: z.string().optional().describe('Deprecation note'),
|
|
42
|
+
reason: z.string().optional().describe('Archive reason'),
|
|
43
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
44
|
+
successor: z
|
|
45
|
+
.number()
|
|
46
|
+
.int()
|
|
47
|
+
.positive()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe('Successor version'),
|
|
50
|
+
target: z.string().min(1).describe('Historical version target trail.id@N'),
|
|
51
|
+
}),
|
|
52
|
+
intent: 'write',
|
|
53
|
+
output: z.object({
|
|
54
|
+
filePath: z.string(),
|
|
55
|
+
trailId: z.string(),
|
|
56
|
+
updated: z.boolean(),
|
|
57
|
+
}),
|
|
58
|
+
permit: { scopes: ['version:write'] },
|
|
59
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Result, ValidationError, trail } from '@ontrails/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
cleanDevState,
|
|
6
|
+
DEFAULT_TOPO_SNAPSHOT_RETENTION,
|
|
7
|
+
} from './dev-support.js';
|
|
8
|
+
import { resolveTrailRootDir } from './root-dir.js';
|
|
9
|
+
import { createIsolatedExampleInput } from './topo-support.js';
|
|
10
|
+
|
|
11
|
+
export const devCleanTrail = trail('dev.clean', {
|
|
12
|
+
description: 'Prune unpinned topo snapshots and old trace records',
|
|
13
|
+
examples: [
|
|
14
|
+
{
|
|
15
|
+
input: {
|
|
16
|
+
dryRun: true,
|
|
17
|
+
rootDir: createIsolatedExampleInput('dev-clean').rootDir,
|
|
18
|
+
},
|
|
19
|
+
name: 'Preview local cleanup',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
implementation: (input, ctx) => {
|
|
23
|
+
if (input.dryRun !== true && input.yes !== true) {
|
|
24
|
+
return Result.err(
|
|
25
|
+
new ValidationError(
|
|
26
|
+
'Refusing to clean local state without `--yes` or `--dry-run`.'
|
|
27
|
+
)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
|
|
32
|
+
if (rootDirResult.isErr()) {
|
|
33
|
+
return rootDirResult;
|
|
34
|
+
}
|
|
35
|
+
const rootDir = rootDirResult.value;
|
|
36
|
+
return Result.ok(
|
|
37
|
+
cleanDevState({
|
|
38
|
+
dryRun: input.dryRun,
|
|
39
|
+
maxAge: input.traceAgeMs,
|
|
40
|
+
maxRecords: input.traces,
|
|
41
|
+
rootDir,
|
|
42
|
+
snapshotRetention: input.snapshots,
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
},
|
|
46
|
+
input: z.object({
|
|
47
|
+
dryRun: z
|
|
48
|
+
.boolean()
|
|
49
|
+
.default(true)
|
|
50
|
+
.describe('Preview cleanup without changing state'),
|
|
51
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
52
|
+
snapshots: z
|
|
53
|
+
.number()
|
|
54
|
+
.default(DEFAULT_TOPO_SNAPSHOT_RETENTION)
|
|
55
|
+
.describe('Unpinned topo snapshots to retain'),
|
|
56
|
+
traceAgeMs: z
|
|
57
|
+
.number()
|
|
58
|
+
.default(7 * 24 * 60 * 60 * 1000)
|
|
59
|
+
.describe('Maximum retained trace age in milliseconds'),
|
|
60
|
+
traces: z.number().default(10_000).describe('Maximum retained trace count'),
|
|
61
|
+
yes: z.boolean().default(false).describe('Confirm destructive changes'),
|
|
62
|
+
}),
|
|
63
|
+
intent: 'destroy',
|
|
64
|
+
output: z.object({
|
|
65
|
+
dryRun: z.boolean(),
|
|
66
|
+
remaining: z.object({
|
|
67
|
+
pinnedCount: z.number(),
|
|
68
|
+
snapshotCount: z.number(),
|
|
69
|
+
traceCount: z.number(),
|
|
70
|
+
}),
|
|
71
|
+
removed: z.object({
|
|
72
|
+
topoSnapshots: z.number(),
|
|
73
|
+
traceRecords: z.number(),
|
|
74
|
+
}),
|
|
75
|
+
retention: z.object({
|
|
76
|
+
snapshots: z.number(),
|
|
77
|
+
traceAgeMs: z.number(),
|
|
78
|
+
traces: z.number(),
|
|
79
|
+
}),
|
|
80
|
+
}),
|
|
81
|
+
permit: { scopes: ['dev:write'] },
|
|
82
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Result, ValidationError, trail } from '@ontrails/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import { resetDevState } from './dev-support.js';
|
|
5
|
+
import { resolveTrailRootDir } from './root-dir.js';
|
|
6
|
+
import { createIsolatedExampleInput } from './topo-support.js';
|
|
7
|
+
|
|
8
|
+
export const devResetTrail = trail('dev.reset', {
|
|
9
|
+
description: 'Remove local Trails database artifacts',
|
|
10
|
+
examples: [
|
|
11
|
+
{
|
|
12
|
+
input: {
|
|
13
|
+
dryRun: true,
|
|
14
|
+
rootDir: createIsolatedExampleInput('dev-reset').rootDir,
|
|
15
|
+
},
|
|
16
|
+
name: 'Preview local reset',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
implementation: (input, ctx) => {
|
|
20
|
+
if (input.dryRun !== true && input.yes !== true) {
|
|
21
|
+
return Result.err(
|
|
22
|
+
new ValidationError(
|
|
23
|
+
'Refusing to reset local state without `--yes` or `--dry-run`.'
|
|
24
|
+
)
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
|
|
29
|
+
if (rootDirResult.isErr()) {
|
|
30
|
+
return rootDirResult;
|
|
31
|
+
}
|
|
32
|
+
const rootDir = rootDirResult.value;
|
|
33
|
+
return Result.ok(resetDevState({ dryRun: input.dryRun, rootDir }));
|
|
34
|
+
},
|
|
35
|
+
input: z.object({
|
|
36
|
+
dryRun: z
|
|
37
|
+
.boolean()
|
|
38
|
+
.default(true)
|
|
39
|
+
.describe('Preview reset without changing state'),
|
|
40
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
41
|
+
yes: z.boolean().default(false).describe('Confirm destructive changes'),
|
|
42
|
+
}),
|
|
43
|
+
intent: 'destroy',
|
|
44
|
+
output: z.object({
|
|
45
|
+
dryRun: z.boolean(),
|
|
46
|
+
removedCount: z.number(),
|
|
47
|
+
removedFiles: z.array(z.string()),
|
|
48
|
+
}),
|
|
49
|
+
permit: { scopes: ['dev:write'] },
|
|
50
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Result, trail } from '@ontrails/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
buildDevStats,
|
|
6
|
+
DEFAULT_TOPO_SNAPSHOT_RETENTION,
|
|
7
|
+
} from './dev-support.js';
|
|
8
|
+
import { resolveTrailRootDir } from './root-dir.js';
|
|
9
|
+
import { createIsolatedExampleInput } from './topo-support.js';
|
|
10
|
+
|
|
11
|
+
export const devStatsTrail = trail('dev.stats', {
|
|
12
|
+
description: 'Show local Trails workspace state and retention',
|
|
13
|
+
examples: [
|
|
14
|
+
{
|
|
15
|
+
input: { rootDir: createIsolatedExampleInput('dev-stats').rootDir },
|
|
16
|
+
name: 'Show local dev state',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
implementation: (input, ctx) => {
|
|
20
|
+
const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
|
|
21
|
+
if (rootDirResult.isErr()) {
|
|
22
|
+
return rootDirResult;
|
|
23
|
+
}
|
|
24
|
+
const rootDir = rootDirResult.value;
|
|
25
|
+
return Result.ok(
|
|
26
|
+
buildDevStats({
|
|
27
|
+
maxAge: input.traceAgeMs,
|
|
28
|
+
maxRecords: input.traces,
|
|
29
|
+
rootDir,
|
|
30
|
+
snapshotRetention: input.snapshots,
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
input: z.object({
|
|
35
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
36
|
+
snapshots: z
|
|
37
|
+
.number()
|
|
38
|
+
.default(DEFAULT_TOPO_SNAPSHOT_RETENTION)
|
|
39
|
+
.describe('Unpinned topo snapshots to retain'),
|
|
40
|
+
traceAgeMs: z
|
|
41
|
+
.number()
|
|
42
|
+
.default(7 * 24 * 60 * 60 * 1000)
|
|
43
|
+
.describe('Maximum retained trace age in milliseconds'),
|
|
44
|
+
traces: z.number().default(10_000).describe('Maximum retained trace count'),
|
|
45
|
+
}),
|
|
46
|
+
intent: 'read',
|
|
47
|
+
output: z.object({
|
|
48
|
+
db: z.object({
|
|
49
|
+
exists: z.boolean(),
|
|
50
|
+
fileSizeBytes: z.number(),
|
|
51
|
+
path: z.string(),
|
|
52
|
+
}),
|
|
53
|
+
lock: z.object({
|
|
54
|
+
exists: z.boolean(),
|
|
55
|
+
fileSizeBytes: z.number(),
|
|
56
|
+
path: z.string(),
|
|
57
|
+
}),
|
|
58
|
+
retention: z.object({
|
|
59
|
+
snapshots: z.number(),
|
|
60
|
+
traceAgeMs: z.number(),
|
|
61
|
+
traces: z.number(),
|
|
62
|
+
}),
|
|
63
|
+
topo: z.object({
|
|
64
|
+
pinnedCount: z.number(),
|
|
65
|
+
prunableSnapshotCount: z.number(),
|
|
66
|
+
snapshotCount: z.number(),
|
|
67
|
+
}),
|
|
68
|
+
tracing: z.object({
|
|
69
|
+
recordCount: z.number(),
|
|
70
|
+
}),
|
|
71
|
+
}),
|
|
72
|
+
});
|