@okfit/cli 0.9.0 → 0.10.1

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/errors.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import { InitBundleDirError } from "./internal/initWizard.js";
2
2
  import { DocumentStdinIsTerminalError } from "./internal/stdin.js";
3
- import { Cancelled, ConfigIssueRenderer, NotInteractive } from "@effected/cli";
3
+ import { ConfigIssueRenderer } from "@effected/cli";
4
4
  import { ConfigMalformedError, ConfigPathNotFoundError, DocumentPathError, InitOverwriteError, NotAPublicationError, PublicationNotFoundError, QueryConceptNotFoundError, QuerySelectionError, QueryUnknownVocabularyError, SyncPublicationConflictError, SyncStagedLogError, VerifyConceptNotFoundError, VerifySelectionError, VerifyUnsupportedFrontmatterError } from "@okfit/engine";
5
5
 
6
6
  //#region src/errors.ts
7
7
  const hasTag = (error, tag) => typeof error === "object" && error !== null && "_tag" in error && error._tag === tag;
8
+ /** Where a defect's report sends the reader. */
9
+ const ISSUE_URL = "https://github.com/spencerbeggs/okfit/issues";
8
10
  /** `path` relative to `cwd` when it is under it, else the absolute path unchanged (K-51). */
9
11
  const relativeToCwd = (path, cwd) => {
10
12
  if (path === cwd) return ".";
@@ -18,14 +20,10 @@ const relativeToCwd = (path, cwd) => {
18
20
  *
19
21
  * Rules, in order:
20
22
  *
21
- * 1. A `ShowHelp` (any `_tag === "ShowHelp"`) renders as `[]` — the empty
22
- * array. `Command.runWith` has already rendered the help document and any
23
- * parse errors before re-failing, so a second rendering here would print
24
- * "Help requested" after the help text. In practice this branch is
25
- * defensive only: `@effected/cli`'s `CliRuntime.main`/`reportFailures`
26
- * already handles a `ShowHelp` before `render` is ever called
27
- * (`main.ts`'s own doc comment), so `renderFailure` never actually sees
28
- * one on the path this package uses.
23
+ * 1. A cancelled or non-interactive run (`details.isCancelled`,
24
+ * `details.isNotInteractive`: a cancelled fallback prompt is a defect in the
25
+ * cause, but not a bug) renders as the kit's own `defaultLines`, unprefixed.
26
+ * `ShowHelp` never reaches `render`: `CliRuntime.main` handles it first.
29
27
  * 2. `ConfigPathNotFoundError` renders as its own `error: <message>` line;
30
28
  * `InitOverwriteError` renders as the K-51 header, one two-space-indented
31
29
  * relativised path per conflict, and the literal `Nothing was written.`.
@@ -56,18 +54,23 @@ const relativeToCwd = (path, cwd) => {
56
54
  * 5d. `QueryUnknownVocabularyError`, `QueryConceptNotFoundError` and
57
55
  * `QuerySelectionError` (`okfit query`) each render as one
58
56
  * `error: <message>` line.
59
- * 6. Everything else — core's `BundleRootNotFoundError`/`BundleReadError`/
57
+ * 6. A defect (`details.isDefect`: a `die`, a thrown exception, a bug) that no
58
+ * rule above claimed renders as `error: ` plus the kit's report for the
59
+ * run without its status marker, then `Please report at <issues URL>`.
60
+ * 7. Every other typed failure — core's `BundleRootNotFoundError`/`BundleReadError`/
60
61
  * `BundleDepthExceededError`, config-file's other errors, `XdgEnvError`
61
62
  * (the K-13 `HOME`-unset case) — renders as the single line
62
63
  * `error: ${String(error)}`. Each of those
63
64
  * classes' own `message` already names the offending path, which is all
64
65
  * K-46 asserts.
65
66
  *
67
+ * `details` is the kit's `FailureDetails`: `isDefect` tells a typed failure
68
+ * from a defect exactly, so no rule here guesses from an error's shape.
69
+ *
66
70
  * @public
67
71
  */
68
- const renderFailure = (error) => {
69
- if (hasTag(error, "ShowHelp")) return [];
70
- if (error instanceof Cancelled || error instanceof NotInteractive) return [error.message];
72
+ const renderFailure = (error, details) => {
73
+ if (details.isCancelled || details.isNotInteractive) return details.defaultLines;
71
74
  if (error instanceof ConfigPathNotFoundError) return [`error: ${error.message}`];
72
75
  if (error instanceof ConfigMalformedError) return [`error: ${error.message}`];
73
76
  if (error instanceof InitOverwriteError) return [
@@ -92,6 +95,14 @@ const renderFailure = (error) => {
92
95
  const validationError = error;
93
96
  return [`error: ${String(validationError)}`, ...ConfigIssueRenderer.render(validationError).map((line) => ` ${line}`)];
94
97
  }
98
+ if (details.isDefect) {
99
+ const [first = String(error), ...rest] = details.lines({ status: false });
100
+ return [
101
+ `error: ${first}`,
102
+ ...rest,
103
+ `Please report at ${ISSUE_URL}`
104
+ ];
105
+ }
95
106
  return [`error: ${String(error)}`];
96
107
  };
97
108
 
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Command } from "effect/cli";
2
- import "@effected/cli";
2
+ import { FailureDetails } from "@effected/cli";
3
3
  import { Runtime, Schema } from "effect";
4
4
  import { ContextEnvelope, RenderedDiagnostic } from "@okfit/engine";
5
5
  //#region src/commands/root.d.ts
@@ -47,14 +47,10 @@ export declare const rootCommand: Command.Command<"okfit", {
47
47
  *
48
48
  * Rules, in order:
49
49
  *
50
- * 1. A `ShowHelp` (any `_tag === "ShowHelp"`) renders as `[]` — the empty
51
- * array. `Command.runWith` has already rendered the help document and any
52
- * parse errors before re-failing, so a second rendering here would print
53
- * "Help requested" after the help text. In practice this branch is
54
- * defensive only: `@effected/cli`'s `CliRuntime.main`/`reportFailures`
55
- * already handles a `ShowHelp` before `render` is ever called
56
- * (`main.ts`'s own doc comment), so `renderFailure` never actually sees
57
- * one on the path this package uses.
50
+ * 1. A cancelled or non-interactive run (`details.isCancelled`,
51
+ * `details.isNotInteractive`: a cancelled fallback prompt is a defect in the
52
+ * cause, but not a bug) renders as the kit's own `defaultLines`, unprefixed.
53
+ * `ShowHelp` never reaches `render`: `CliRuntime.main` handles it first.
58
54
  * 2. `ConfigPathNotFoundError` renders as its own `error: <message>` line;
59
55
  * `InitOverwriteError` renders as the K-51 header, one two-space-indented
60
56
  * relativised path per conflict, and the literal `Nothing was written.`.
@@ -85,16 +81,22 @@ export declare const rootCommand: Command.Command<"okfit", {
85
81
  * 5d. `QueryUnknownVocabularyError`, `QueryConceptNotFoundError` and
86
82
  * `QuerySelectionError` (`okfit query`) each render as one
87
83
  * `error: <message>` line.
88
- * 6. Everything else — core's `BundleRootNotFoundError`/`BundleReadError`/
84
+ * 6. A defect (`details.isDefect`: a `die`, a thrown exception, a bug) that no
85
+ * rule above claimed renders as `error: ` plus the kit's report for the
86
+ * run without its status marker, then `Please report at <issues URL>`.
87
+ * 7. Every other typed failure — core's `BundleRootNotFoundError`/`BundleReadError`/
89
88
  * `BundleDepthExceededError`, config-file's other errors, `XdgEnvError`
90
89
  * (the K-13 `HOME`-unset case) — renders as the single line
91
90
  * `error: ${String(error)}`. Each of those
92
91
  * classes' own `message` already names the offending path, which is all
93
92
  * K-46 asserts.
94
93
  *
94
+ * `details` is the kit's `FailureDetails`: `isDefect` tells a typed failure
95
+ * from a defect exactly, so no rule here guesses from an error's shape.
96
+ *
95
97
  * @public
96
98
  */
97
- export declare const renderFailure: (error: unknown) => ReadonlyArray<string>;
99
+ export declare const renderFailure: (error: unknown, details: FailureDetails) => ReadonlyArray<string>;
98
100
  //#endregion
99
101
  //#region src/internal/initWizard.d.ts
100
102
  declare const InitBundleDirError_base: Schema.Class<InitBundleDirError, Schema.TaggedStruct<"InitBundleDirError", {
@@ -63,7 +63,8 @@ const checkBundleDir = (value) => {
63
63
  /**
64
64
  * The `okfit init` wizard: profile, bundle directory, config location, in
65
65
  * that order. A setting is prompted for only when its flag was not given and
66
- * the run is interactive; otherwise its flag value or default is used
66
+ * the run is interactive (the profile also needs more than one registered: a
67
+ * lone profile is used without a screen); otherwise its flag value or default is used
67
68
  * (`CliUi.prompt`'s `otherwise`, so a non-interactive run never loads Ink).
68
69
  *
69
70
  * A bad `--bundle` flag fails with {@link InitBundleDirError} before any
@@ -86,14 +87,15 @@ const initWizard = (given, defaults) => Effect.gen(function* () {
86
87
  });
87
88
  flagBundle = checked.dir;
88
89
  }
90
+ const profileNames = PROFILE_NAMES;
89
91
  return {
90
- profile: Option.isSome(given.profile) ? given.profile.value : yield* CliUi.prompt(Select.screen({
92
+ profile: Option.isSome(given.profile) ? given.profile.value : profileNames.length === 1 ? defaults.profile : yield* CliUi.prompt(Select.screen({
91
93
  message: "Profile to scaffold with",
92
- choices: PROFILE_NAMES.map((name) => ({
94
+ choices: profileNames.map((name) => ({
93
95
  label: name,
94
96
  value: name
95
97
  })),
96
- initial: Math.max(0, PROFILE_NAMES.indexOf(defaults.profile))
98
+ initial: Math.max(0, profileNames.indexOf(defaults.profile))
97
99
  }), { otherwise: defaults.profile }),
98
100
  bundle: flagBundle ?? (yield* CliUi.prompt(TextInput.screen({
99
101
  message: "Bundle directory (relative to the project root)",
@@ -1,7 +1,5 @@
1
- import { CliColor } from "@effected/cli";
2
- import { CurrentDistribution, distributionSuffix } from "@effected/engine";
1
+ import { distributionSuffix } from "@effected/engine";
3
2
  import { ENGINE_VERSION } from "@okfit/engine";
4
- import { Effect, Layer } from "effect";
5
3
  import { CONFIG_SCHEMA_VERSION, OKF_SPEC_VERSION } from "@okfit/core";
6
4
 
7
5
  //#region src/internal/versionFormatter.ts
@@ -9,28 +7,19 @@ import { CONFIG_SCHEMA_VERSION, OKF_SPEC_VERSION } from "@okfit/core";
9
7
  * `okfit --version`'s full text (okfit #137):
10
8
  * `<name> <version>[ via <distName> <distVersion>] (engine <ENGINE_VERSION>, okf <OKF_SPEC_VERSION>, config-schema <CONFIG_SCHEMA_VERSION>)`.
11
9
  * `GlobalFlag.Version`'s built-in `run` calls `formatter.formatVersion(command.name,
12
- * version)` (`command.name` is always `"okfit"`, `version` is `CLI_VERSION` --
13
- * `cli/GlobalFlag.ts:189-199`), so those two arguments alone are
14
- * exactly `okfit <CLI_VERSION>`; this appends the engine, okf, and
15
- * distribution parts.
10
+ * version)` (`command.name` is always `"okfit"`, `version` is `CLI_VERSION`), so
11
+ * those two arguments alone are exactly `okfit <CLI_VERSION>`; this appends the
12
+ * distribution, engine and okf parts.
16
13
  *
17
- * Built on `@effected/cli`'s `CliColor.formatterLayer` (the kit's one
18
- * colour-decision point, read through the ambient `ConfigProvider`, never
19
- * `process` directly) and `@effected/engine`'s `distributionSuffix` for the
20
- * `via <name> <version>` segment -- the shape `effect-v4-cli`'s
21
- * `recipes.md#version-formatter` teaches. Only `formatVersion` is
22
- * overridden; every other `Formatter` method (help, error rendering) is
23
- * `CliColor.formatterLayer`'s own default, so help/error output stays
24
- * consistent with the rest of the program's colour decision.
25
- *
26
- * `Layer.unwrap` around `Effect.map(CurrentDistribution, ...)` reads the
27
- * distribution once, from the ambient `CurrentDistribution` reference
28
- * `main.ts` provides, rather than threading it in as a constructor argument
29
- * the way the hand-rolled formatter used to.
14
+ * Passed to `CliRuntime.main` as `env.formatter`: `helpOnUsageError: "stderr"`
15
+ * only reroutes help written through a formatter `main` installed, so a layer
16
+ * inside the program would not do. Every method left out keeps the kit's
17
+ * coloured default. `distribution` is the value `main` was given, known before
18
+ * the program runs, so it is read directly rather than from the context.
30
19
  *
31
20
  * @internal
32
21
  */
33
- const versionFormatterLayer = Layer.unwrap(Effect.map(CurrentDistribution, (distribution) => CliColor.formatterLayer({ formatVersion: (name, version) => `${name} ${version}${distributionSuffix(distribution)} (engine ${ENGINE_VERSION}, okf ${OKF_SPEC_VERSION}, config-schema ${CONFIG_SCHEMA_VERSION})` })));
22
+ const versionFormatter = (distribution) => ({ formatVersion: (name, version) => `${name} ${version}${distributionSuffix(distribution)} (engine ${ENGINE_VERSION}, okf ${OKF_SPEC_VERSION}, config-schema ${CONFIG_SCHEMA_VERSION})` });
34
23
 
35
24
  //#endregion
36
- export { versionFormatterLayer };
25
+ export { versionFormatter };
package/main.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CLI_VERSION } from "./version.js";
2
2
  import { rootCommand } from "./commands/root.js";
3
3
  import { renderFailure } from "./errors.js";
4
- import { versionFormatterLayer } from "./internal/versionFormatter.js";
4
+ import { versionFormatter } from "./internal/versionFormatter.js";
5
5
  import { CliAudience, CliRuntime } from "@effected/cli";
6
6
  import { CurrentDistribution } from "@effected/engine";
7
7
  import { Now, OkfitPlatform } from "@okfit/engine";
@@ -46,12 +46,17 @@ const main = (options = {}) => {
46
46
  const program = Effect.gen(function* () {
47
47
  const now = yield* nowEffect;
48
48
  return yield* CliAudience.run(rootCommand, { version: CLI_VERSION }).pipe(Effect.provideService(Now, now));
49
- }).pipe(Effect.provide(versionFormatterLayer), Effect.provideService(CurrentDistribution, distribution));
49
+ }).pipe(Effect.provideService(CurrentDistribution, distribution));
50
50
  NodeRuntime.runMain(CliRuntime.main(program, {
51
51
  platform: OkfitPlatform,
52
52
  exitCode: 3,
53
53
  render: renderFailure,
54
- env: { audienceEnvVar: "OKFIT_AUDIENCE" }
54
+ helpOnUsageError: "stderr",
55
+ env: {
56
+ audienceEnvVar: "OKFIT_AUDIENCE",
57
+ stderrIsTerminal: Effect.sync(() => process.stderr.isTTY === true),
58
+ formatter: versionFormatter(distribution)
59
+ }
55
60
  }));
56
61
  };
57
62
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okfit/cli",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "private": false,
5
5
  "description": "The okfit command line: validate, lint, index, and inspect Open Knowledge Format (OKF) bundles.",
6
6
  "keywords": [
@@ -42,25 +42,25 @@
42
42
  "okfit": "bin/okfit.js"
43
43
  },
44
44
  "dependencies": {
45
- "@effect/platform-node": "^4.0.0",
46
- "@effected/cli": "^0.11.0",
47
- "@effected/config-file": "^0.14.0",
48
- "@effected/engine": "^0.3.0",
45
+ "@effect/platform-node": "^4.0.2",
46
+ "@effected/cli": "^0.14.0",
47
+ "@effected/config-file": "^0.14.2",
48
+ "@effected/engine": "^0.4.0",
49
49
  "@effected/env": "^0.1.0",
50
50
  "@effected/git": "^0.20.0",
51
51
  "@effected/glob": "^0.10.0",
52
- "@effected/jsonc": "^0.15.0",
53
- "@effected/markdown": "^0.15.0",
54
- "@effected/schemastore": "^0.20.0",
55
- "@effected/toml": "^0.11.0",
52
+ "@effected/jsonc": "^0.15.1",
53
+ "@effected/markdown": "^0.15.1",
54
+ "@effected/schemastore": "^0.21.2",
55
+ "@effected/toml": "^0.11.1",
56
56
  "@effected/walker": "^0.15.0",
57
- "@effected/yaml": "^0.19.0",
58
- "@okfit/core": "0.9.0",
59
- "@okfit/engine": "0.12.0",
57
+ "@effected/yaml": "^0.19.1",
58
+ "@okfit/core": "0.9.1",
59
+ "@okfit/engine": "0.13.1",
60
60
  "@okfit/profiles": "0.9.0",
61
- "effect": "^4.0.0",
62
- "ink": "^7.1.1",
63
- "react": "^19.2.0"
61
+ "effect": "^4.0.2",
62
+ "ink": "^8.0.0",
63
+ "react": "^19.3.0"
64
64
  },
65
65
  "engines": {
66
66
  "node": ">=24.11.0"
package/version.js CHANGED
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * @public
13
13
  */
14
- const CLI_VERSION = "0.9.0";
14
+ const CLI_VERSION = "0.10.1";
15
15
 
16
16
  //#endregion
17
17
  export { CLI_VERSION };