@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.
- package/CHANGELOG.md +1906 -0
- package/README.md +48 -0
- package/bin/trails.ts +3 -0
- package/package.json +57 -0
- package/src/app.ts +167 -0
- package/src/clack.ts +111 -0
- package/src/cli.ts +308 -0
- package/src/completions.ts +431 -0
- package/src/lifecycle-source-io.ts +33 -0
- package/src/load-app-mirror.ts +202 -0
- package/src/local-state-io.ts +129 -0
- package/src/mcp-app.ts +42 -0
- package/src/mcp-options.ts +92 -0
- package/src/mcp.ts +8 -0
- package/src/project-writes.ts +377 -0
- package/src/regrade/audit.ts +571 -0
- package/src/regrade/config.ts +152 -0
- package/src/regrade/history.ts +636 -0
- package/src/regrade/lifecycle.ts +76 -0
- package/src/regrade/live-api-preserve.ts +123 -0
- package/src/regrade/plan-artifact.ts +515 -0
- package/src/regrade/plan-derivation.ts +301 -0
- package/src/regrade/prepared-run.ts +259 -0
- package/src/regrade/receipt-history.ts +446 -0
- package/src/regrade/source-transaction.ts +185 -0
- package/src/release/bindings.ts +58 -0
- package/src/release/changeset-packages.ts +99 -0
- package/src/release/check.ts +1191 -0
- package/src/release/cli-bundle.ts +575 -0
- package/src/release/config.ts +73 -0
- package/src/release/contract-facts.ts +425 -0
- package/src/release/homebrew.ts +221 -0
- package/src/release/index.ts +180 -0
- package/src/release/lock-roundtrip-smoke.ts +255 -0
- package/src/release/lock-roundtrip-workspace.ts +107 -0
- package/src/release/native-bun-publish.ts +964 -0
- package/src/release/native-bun-registry.ts +848 -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/package-route-facts.ts +146 -0
- package/src/release/packed-artifacts-smoke.ts +236 -0
- package/src/release/policy.ts +1780 -0
- package/src/release/semver.ts +104 -0
- package/src/release/smoke.ts +56 -0
- package/src/release/stable-version-release.ts +80 -0
- package/src/release/wayfinder-dogfood-smoke.ts +762 -0
- package/src/release/zero-line-transition.ts +68 -0
- package/src/retired-topo-command.ts +36 -0
- package/src/run-adapter-check.ts +76 -0
- package/src/run-argv.ts +133 -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-regrade-progress.ts +47 -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-project.ts +52 -0
- package/src/run-watch.ts +381 -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 +816 -0
- package/src/trails/add-trail.ts +141 -0
- package/src/trails/add-verify.ts +252 -0
- package/src/trails/compile.ts +118 -0
- package/src/trails/completions-complete.ts +236 -0
- package/src/trails/completions.ts +47 -0
- package/src/trails/config-explain.ts +43 -0
- package/src/trails/create-adapter.ts +785 -0
- package/src/trails/create-scaffold.ts +1215 -0
- package/src/trails/create-versions.ts +62 -0
- package/src/trails/create.ts +652 -0
- package/src/trails/deprecate.ts +59 -0
- package/src/trails/dev-clean.ts +80 -0
- package/src/trails/dev-reset.ts +48 -0
- package/src/trails/dev-stats.ts +71 -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 +106 -0
- package/src/trails/load-app.ts +1145 -0
- package/src/trails/operator-context.ts +66 -0
- package/src/trails/project-context-output.ts +304 -0
- package/src/trails/project-context.ts +613 -0
- package/src/trails/project.ts +65 -0
- package/src/trails/regrade.ts +4951 -0
- package/src/trails/release-check.ts +113 -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 +592 -0
- package/src/trails/run-examples.ts +149 -0
- package/src/trails/run.ts +496 -0
- package/src/trails/scaffold-json.ts +60 -0
- package/src/trails/scaffold-topo-identity.ts +479 -0
- package/src/trails/survey.ts +990 -0
- 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 +323 -0
- package/src/trails/topo-support.ts +247 -0
- package/src/trails/topo-unpin.ts +61 -0
- package/src/trails/topo.ts +92 -0
- package/src/trails/validate.ts +348 -0
- package/src/trails/version-lifecycle-support.ts +936 -0
- package/src/trails/warden-guide.ts +134 -0
- package/src/trails/warden.ts +598 -0
- package/src/trails/wayfind-diff.ts +716 -0
- package/src/trails/wayfind-outline.ts +876 -0
- package/src/trails/wayfind.ts +1319 -0
- package/src/versions.ts +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Trails CLI
|
|
2
|
+
|
|
3
|
+
Command-line tools for working with Trails projects.
|
|
4
|
+
|
|
5
|
+
Use the canonical [Homebrew installation guide](../../docs/getting-started.md#installation) for installation, updates, and removal. Stable `0.2.0` installation requires the release assets and tap update; that handoff is pending. The formula requires Bun at runtime.
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
These commands target stable `0.2.0`. Run them after that version is published to npm.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bunx @ontrails/trails@0.2.0 create my-app --permit '{"id":"local-dev","scopes":["project:write"]}'
|
|
13
|
+
bunx @ontrails/trails@0.2.0 create my-app --workspace --permit '{"id":"local-dev","scopes":["project:write"]}'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Common workflows:
|
|
17
|
+
|
|
18
|
+
- `trails create` starts a standalone app by default; `--workspace` creates a configured workspace with that app under `apps/<name>`. Generated surfaces live under `bin/`, app source stays side-effect-free, and `--dry-run` returns the complete write plan without touching disk. Workspace creation rejects targets nested beneath an existing configured workspace while allowing a child workspace beneath an ancestor standalone Config. On a rerun, a preserved app entry also rejects when its runtime-selected topo ID can be statically proven to conflict with `workspace.apps`; dynamic, indirect, and post-construction mutation forms remain preserved and are validated by runtime commands such as `trails compile`.
|
|
19
|
+
- `trails add surface` adds another surface entrypoint to an existing project.
|
|
20
|
+
- `trails topo` inspects topo state and manages pins/history.
|
|
21
|
+
- `trails compile` writes the selected app's `trails.lock`. At a configured workspace root, pass `--app <id>`; compile never fans out or writes an aggregate root lock.
|
|
22
|
+
- `trails validate` checks the selected app, or validates every configured app when run at a workspace root without `--app`.
|
|
23
|
+
- `trails wayfind`, `trails wayfind --trails --intent read`, `trails wayfind <id> --contract`, `trails wayfind <id> --deps`, `trails wayfind <id> --impact`, `trails wayfind pattern "wayfind.*"`, `trails wayfind query "release drift"`, and `trails wayfind diff ...` read graph artifacts through Wayfinder. Pass `--app <id>` for one configured app. A workspace-root saved read returns a labeled app-partitioned view and may be partial; workspace diff requires complete views on both roots. `trails wayfind file <file> --outline` remains the operator-owned explicit-source exception and does not accept app selection.
|
|
24
|
+
- `trails schema <command...>` shows accepted CLI routes, aliases, flags, and schemas for an operator command or command namespace.
|
|
25
|
+
- `trails warden` runs Trails governance checks for contract and architecture drift. Configured workspaces derive topo targets from `workspace.apps`; `--app <id>` narrows topo-aware checks while preserving project-wide source facts. Use `--scope-exclude <glob>` or project `warden.scope.exclude` config when local notes, scratch space, or generated state should not be governed by Warden.
|
|
26
|
+
- `trails regrade plan <from> <to> --root-dir <path> --json` writes an active Regrade plan, `trails regrade plan --expand` stages wide-net review candidates in that plan, `trails regrade preview` reruns it without writing, and `trails regrade apply` consumes the plan and writes history. The minimal `from`/`to` seed is the primary workflow: planning deterministically derives morphology proposals, source-observed naming and public-identifier review candidates, review-only filename moves with reference-closure evidence, a namespace census, and current live-topo API preserves into the artifact's `derivation` section. Each derived item records provenance; uncertain forms and file moves remain review inventory until explicitly authored into the plan. A malformed or incompatible `trails.lock` fails planning instead of silently dropping live-API preserves. Use `plan --expand --dry-run` to inspect additional observed candidates without writing the active plan, and add `--dry-run` to `apply` to prove the apply path without mutating source. Use `trails regrade plans` and `trails regrade check` when a workspace has active plans that need inspection before apply. Human CLI runs stream concise phase progress to stderr; `--json` and `--jsonl` keep that channel quiet while returning phase names and timings in the structured result. MCP returns the same lifecycle facts from `trails_plan_regrade`, `trails_check_regrade`, `trails_preview_regrade`, `trails_apply_regrade`, and `trails_adjust_regrade` and forwards progress notifications when the caller supplies a progress token. Lifecycle timings describe the command invocation and are never persisted into plans or history receipts. Apply reuses its command-local preflight evaluation only when exact source bytes plus plan, policy, scope, lock, and tool identities still match. Any stale or unreadable fact fails closed; no prepared handle is written to disk or required for receipt regeneration, and the post-apply completion scan still runs independently.
|
|
27
|
+
- `trails guide` remains available for compatibility; prefer `trails wayfind --source live --module <app-module>` or saved-artifact Wayfinder reads for agent navigation.
|
|
28
|
+
|
|
29
|
+
For configured workspaces, migrate package-manager discovery, root aggregate-lock assumptions, and persistent `warden.apps` lists to static `workspace.apps`. `--root-dir` fixes the project boundary, `--app` selects one stable configured ID, and `--module` only refines that app's live entry. Run and completion ownership are derived live from those configured apps; a root aggregate `trails.lock` is neither created nor consulted as workspace identity.
|
|
30
|
+
|
|
31
|
+
Use `trails config explain --json` to inspect that source-static identity without loading app modules or consulting locks. From a workspace root it reports the configured app catalog; `--app <id>` or an app-root CWD narrows the same Config-authored identity and records how the selection was made.
|
|
32
|
+
|
|
33
|
+
Scaffolding never writes `trails.lock` itself. Install dependencies, then use the normal compile path with the narrow `topo:write` permit:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# From a standalone app
|
|
37
|
+
trails compile --permit '{"id":"local-dev","scopes":["topo:write"]}'
|
|
38
|
+
|
|
39
|
+
# From a configured workspace root
|
|
40
|
+
trails compile --app my-app --permit '{"id":"local-dev","scopes":["topo:write"]}'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The selected app receives a validated lock with its deterministic `scaffold` overlay. The generated README teaches the same standalone or workspace flow.
|
|
44
|
+
|
|
45
|
+
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.
|
|
46
|
+
|
|
47
|
+
See the main Trails documentation for the full framework guide:
|
|
48
|
+
<https://github.com/outfitter-dev/trails>
|
package/bin/trails.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ontrails/trails",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/outfitter-dev/trails.git",
|
|
7
|
+
"directory": "apps/trails"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"trails": "./bin/trails.ts"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"bin/**/*.ts",
|
|
14
|
+
"src/**/*.ts",
|
|
15
|
+
"!src/**/__tests__/**",
|
|
16
|
+
"!src/**/*.test.ts",
|
|
17
|
+
"!src/**/*.test-d.ts",
|
|
18
|
+
"README.md",
|
|
19
|
+
"CHANGELOG.md"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"exports": {
|
|
23
|
+
"./release": "./src/release/index.ts",
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc -b",
|
|
28
|
+
"test": "bun test",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"lint": "oxlint ./src",
|
|
31
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@clack/prompts": "^1.1.0",
|
|
35
|
+
"@manypkg/get-packages": "1.1.3",
|
|
36
|
+
"@ontrails/adapter-kit": "^0.2.0",
|
|
37
|
+
"@ontrails/cli": "^0.2.0",
|
|
38
|
+
"@ontrails/commander": "^0.2.0",
|
|
39
|
+
"@ontrails/config": "^0.2.0",
|
|
40
|
+
"@ontrails/core": "^0.2.0",
|
|
41
|
+
"@ontrails/http": "^0.2.0",
|
|
42
|
+
"@ontrails/mcp": "^0.2.0",
|
|
43
|
+
"@ontrails/observability": "^0.2.0",
|
|
44
|
+
"@ontrails/permits": "^0.2.0",
|
|
45
|
+
"@ontrails/regrade": "^0.2.0",
|
|
46
|
+
"@ontrails/source": "^0.2.0",
|
|
47
|
+
"@ontrails/topography": "^0.2.0",
|
|
48
|
+
"@ontrails/warden": "^0.2.0",
|
|
49
|
+
"commander": "^14.0.3",
|
|
50
|
+
"typescript": "5.9.3",
|
|
51
|
+
"zod": "^4.3.5"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@ontrails/cloudflare": "^0.2.0",
|
|
55
|
+
"@ontrails/testing": "^0.2.0"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/app.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { surfaceOverlay, topo } from '@ontrails/core';
|
|
2
|
+
import {
|
|
3
|
+
wayfindAdaptersTrail,
|
|
4
|
+
wayfindEntitiesTrail,
|
|
5
|
+
wayfindContractTrail,
|
|
6
|
+
wayfindDescribeTrail,
|
|
7
|
+
wayfindErrorsTrail,
|
|
8
|
+
wayfindExamplesTrail,
|
|
9
|
+
wayfindOverlayTrail,
|
|
10
|
+
wayfindTrailheadsTrail,
|
|
11
|
+
wayfindImpactTrail,
|
|
12
|
+
wayfindNearbyTrail,
|
|
13
|
+
wayfindOverviewTrail,
|
|
14
|
+
wayfindResourcesTrail,
|
|
15
|
+
wayfindSearchTrail,
|
|
16
|
+
wayfindSignalsTrail,
|
|
17
|
+
wayfindSurfacesTrail,
|
|
18
|
+
wayfindTrailsTrail,
|
|
19
|
+
wayfindVersionsTrail,
|
|
20
|
+
} from '@ontrails/topography';
|
|
21
|
+
|
|
22
|
+
import * as addSurface from './trails/add-surface.js';
|
|
23
|
+
import * as addTrail from './trails/add-trail.js';
|
|
24
|
+
import * as addVerify from './trails/add-verify.js';
|
|
25
|
+
import * as adapterCheck from './trails/adapter-check.js';
|
|
26
|
+
import * as compile from './trails/compile.js';
|
|
27
|
+
import * as completions from './trails/completions.js';
|
|
28
|
+
import * as completionsComplete from './trails/completions-complete.js';
|
|
29
|
+
import * as configExplain from './trails/config-explain.js';
|
|
30
|
+
import * as create from './trails/create.js';
|
|
31
|
+
import * as createAdapter from './trails/create-adapter.js';
|
|
32
|
+
import * as createScaffold from './trails/create-scaffold.js';
|
|
33
|
+
import * as createVersions from './trails/create-versions.js';
|
|
34
|
+
import * as deprecate from './trails/deprecate.js';
|
|
35
|
+
import * as devClean from './trails/dev-clean.js';
|
|
36
|
+
import * as devReset from './trails/dev-reset.js';
|
|
37
|
+
import * as devStats from './trails/dev-stats.js';
|
|
38
|
+
import * as doctor from './trails/doctor.js';
|
|
39
|
+
import * as draftPromote from './trails/draft-promote.js';
|
|
40
|
+
import * as guide from './trails/guide.js';
|
|
41
|
+
import * as regrade from './trails/regrade.js';
|
|
42
|
+
import * as releaseCheck from './trails/release-check.js';
|
|
43
|
+
import * as releaseSmoke from './trails/release-smoke.js';
|
|
44
|
+
import * as revise from './trails/revise.js';
|
|
45
|
+
import * as run from './trails/run.js';
|
|
46
|
+
import * as runExample from './trails/run-example.js';
|
|
47
|
+
import * as runExamples from './trails/run-examples.js';
|
|
48
|
+
import * as survey from './trails/survey.js';
|
|
49
|
+
import * as topoHistory from './trails/topo-history.js';
|
|
50
|
+
import * as topoPin from './trails/topo-pin.js';
|
|
51
|
+
import * as topoCommand from './trails/topo.js';
|
|
52
|
+
import * as topoUnpin from './trails/topo-unpin.js';
|
|
53
|
+
import * as validate from './trails/validate.js';
|
|
54
|
+
import * as warden from './trails/warden.js';
|
|
55
|
+
import * as wardenGuide from './trails/warden-guide.js';
|
|
56
|
+
import * as wayfind from './trails/wayfind.js';
|
|
57
|
+
import { wayfindOutlineTrail } from './trails/wayfind-outline.js';
|
|
58
|
+
|
|
59
|
+
export const operatorApp = topo(
|
|
60
|
+
'trails',
|
|
61
|
+
run,
|
|
62
|
+
runExamples,
|
|
63
|
+
runExample,
|
|
64
|
+
survey,
|
|
65
|
+
topoCommand,
|
|
66
|
+
compile,
|
|
67
|
+
configExplain,
|
|
68
|
+
topoHistory,
|
|
69
|
+
topoPin,
|
|
70
|
+
topoUnpin,
|
|
71
|
+
validate,
|
|
72
|
+
revise,
|
|
73
|
+
deprecate,
|
|
74
|
+
doctor,
|
|
75
|
+
devStats,
|
|
76
|
+
devClean,
|
|
77
|
+
devReset,
|
|
78
|
+
guide,
|
|
79
|
+
regrade,
|
|
80
|
+
releaseCheck,
|
|
81
|
+
releaseSmoke,
|
|
82
|
+
draftPromote,
|
|
83
|
+
adapterCheck,
|
|
84
|
+
warden,
|
|
85
|
+
wardenGuide,
|
|
86
|
+
create,
|
|
87
|
+
createAdapter,
|
|
88
|
+
createScaffold,
|
|
89
|
+
createVersions,
|
|
90
|
+
addSurface,
|
|
91
|
+
addVerify,
|
|
92
|
+
addTrail,
|
|
93
|
+
completions,
|
|
94
|
+
completionsComplete
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const operatorTrails = Object.fromEntries(
|
|
98
|
+
operatorApp.list().map((trailItem) => [trailItem.id, trailItem])
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const cliWayfinderCommandTrails = {
|
|
102
|
+
wayfindDiffTrail: wayfind.wayfindDiffTrail,
|
|
103
|
+
wayfindFileTrail: wayfind.wayfindFileTrail,
|
|
104
|
+
wayfindPatternTrail: wayfind.wayfindPatternTrail,
|
|
105
|
+
wayfindQueryTrail: wayfind.wayfindQueryTrail,
|
|
106
|
+
wayfindTrail: wayfind.wayfindTrail,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const cliWayfinderSupportTrails = {
|
|
110
|
+
wayfindAdaptersTrail,
|
|
111
|
+
wayfindContractTrail,
|
|
112
|
+
wayfindDescribeTrail,
|
|
113
|
+
wayfindEntitiesTrail,
|
|
114
|
+
wayfindErrorsTrail,
|
|
115
|
+
wayfindExamplesTrail,
|
|
116
|
+
wayfindImpactTrail,
|
|
117
|
+
wayfindNearbyTrail,
|
|
118
|
+
wayfindOutlineTrail,
|
|
119
|
+
wayfindOverlayTrail,
|
|
120
|
+
wayfindOverviewTrail,
|
|
121
|
+
wayfindResourcesTrail,
|
|
122
|
+
wayfindSearchTrail,
|
|
123
|
+
wayfindSignalsTrail,
|
|
124
|
+
wayfindSurfacesTrail,
|
|
125
|
+
wayfindTrailheadsTrail,
|
|
126
|
+
wayfindTrailsTrail,
|
|
127
|
+
wayfindVersionsTrail,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const cliWayfinderTrails = {
|
|
131
|
+
...cliWayfinderCommandTrails,
|
|
132
|
+
...cliWayfinderSupportTrails,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const trailsCliIncludedTrails = [
|
|
136
|
+
...new Set([
|
|
137
|
+
...operatorApp.list().map((trailItem) => trailItem.id),
|
|
138
|
+
...Object.values(cliWayfinderCommandTrails).map(
|
|
139
|
+
(trailItem) => trailItem.id
|
|
140
|
+
),
|
|
141
|
+
]),
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
export const trailsOverlays = [
|
|
145
|
+
surfaceOverlay({
|
|
146
|
+
cli: { diff: 'survey.diff' },
|
|
147
|
+
// Authored, lockable default for the operator MCP `inspect` trailhead.
|
|
148
|
+
// The call-site map in `mcp-options.ts` overrides it at runtime with
|
|
149
|
+
// richer metadata (description, deferred loading) over the same members.
|
|
150
|
+
mcp: {
|
|
151
|
+
inspect: [
|
|
152
|
+
'survey',
|
|
153
|
+
'topo',
|
|
154
|
+
'guide',
|
|
155
|
+
'survey.brief',
|
|
156
|
+
'survey.diff',
|
|
157
|
+
'survey.resource',
|
|
158
|
+
'survey.signal',
|
|
159
|
+
'survey.surfaces',
|
|
160
|
+
'survey.trail',
|
|
161
|
+
'topo.history',
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
export const app = topo('trails', operatorTrails, cliWayfinderTrails);
|
package/src/clack.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clack-backed input resolver for the Trails CLI.
|
|
3
|
+
*
|
|
4
|
+
* This stays at the app layer so @ontrails/cli remains prompt-library agnostic.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Field, InputResolver, ResolveInputOptions } from '@ontrails/cli';
|
|
8
|
+
import { isInteractive } from '@ontrails/cli';
|
|
9
|
+
import * as clack from '@clack/prompts';
|
|
10
|
+
|
|
11
|
+
/** Check whether a field still needs input. */
|
|
12
|
+
const needsInput = (field: Field, current: Record<string, unknown>): boolean =>
|
|
13
|
+
current[field.name] === undefined &&
|
|
14
|
+
field.required &&
|
|
15
|
+
field.default === undefined;
|
|
16
|
+
|
|
17
|
+
/** Build Clack options from field options. */
|
|
18
|
+
const toClackOptions = (field: Field) =>
|
|
19
|
+
field.options?.map((option) => ({
|
|
20
|
+
...(option.hint === undefined ? {} : { hint: option.hint }),
|
|
21
|
+
label: option.label ?? option.value,
|
|
22
|
+
value: option.value,
|
|
23
|
+
})) ?? [];
|
|
24
|
+
|
|
25
|
+
/** Normalize cancelled prompts to `undefined`. */
|
|
26
|
+
const cancelable = async <T>(value: T | symbol): Promise<T | undefined> =>
|
|
27
|
+
await (clack.isCancel(value) ? undefined : value);
|
|
28
|
+
|
|
29
|
+
type FieldResolver = (field: Field) => Promise<unknown>;
|
|
30
|
+
|
|
31
|
+
const fieldResolvers: Record<Field['type'], FieldResolver> = {
|
|
32
|
+
boolean: async (field) =>
|
|
33
|
+
cancelable(
|
|
34
|
+
await clack.confirm({
|
|
35
|
+
initialValue: (field.default as boolean | undefined) ?? false,
|
|
36
|
+
message: field.label,
|
|
37
|
+
})
|
|
38
|
+
),
|
|
39
|
+
enum: async (field) =>
|
|
40
|
+
cancelable(
|
|
41
|
+
await clack.select({
|
|
42
|
+
message: field.label,
|
|
43
|
+
options: toClackOptions(field),
|
|
44
|
+
})
|
|
45
|
+
),
|
|
46
|
+
multiselect: async (field) =>
|
|
47
|
+
cancelable(
|
|
48
|
+
await clack.multiselect({
|
|
49
|
+
initialValues: (field.default as string[] | undefined) ?? [],
|
|
50
|
+
message: field.label,
|
|
51
|
+
options: toClackOptions(field),
|
|
52
|
+
})
|
|
53
|
+
),
|
|
54
|
+
number: async (field) => {
|
|
55
|
+
const raw = await clack.text({ message: field.label });
|
|
56
|
+
return clack.isCancel(raw) ? undefined : Number(raw);
|
|
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
|
+
},
|
|
69
|
+
string: async (field) =>
|
|
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
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/** Resolve a single field value with Clack. */
|
|
85
|
+
const resolveField = (field: Field): Promise<unknown> => {
|
|
86
|
+
const resolver = fieldResolvers[field.type];
|
|
87
|
+
return resolver(field);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/** Fill missing input by prompting with Clack when interactive. */
|
|
91
|
+
export const resolveInputWithClack: InputResolver = async (
|
|
92
|
+
fields,
|
|
93
|
+
provided,
|
|
94
|
+
options?: ResolveInputOptions
|
|
95
|
+
) => {
|
|
96
|
+
if (!isInteractive(options)) {
|
|
97
|
+
return provided;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const resolved: Record<string, unknown> = { ...provided };
|
|
101
|
+
for (const field of fields) {
|
|
102
|
+
if (!needsInput(field, resolved)) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const value = await resolveField(field);
|
|
106
|
+
if (value !== undefined) {
|
|
107
|
+
resolved[field.name] = value;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return resolved;
|
|
111
|
+
};
|