@okfit/cli 0.6.4 → 0.6.6

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/README.md CHANGED
@@ -8,7 +8,7 @@ The `okfit` command line for [Open Knowledge Format (OKF)](https://github.com/Go
8
8
 
9
9
  ```text
10
10
  okfit [--help] [--version]
11
- okfit validate [path] [--config <file>] [--format human|json] [--skip-provenance] [--help]
11
+ okfit validate [path] [--config <file>] [--format human|json] [--skip-provenance] [--document <bundle-path>] [--help]
12
12
  okfit init [path] [--profile <name>] [--config <file>] [--help]
13
13
  okfit context [path] [--config <file>] [--format human|json] [--help]
14
14
  okfit verify <id> [path] [--config <file>] [--at <iso>] [--dry-run] [--format human|json] [--help]
@@ -69,6 +69,14 @@ every edit-time `validate` call so a git spawn per concept never runs on
69
69
  keystroke-level edits; CI and the MCP `validate_bundle` tool omit it and
70
70
  keep both tiers.
71
71
 
72
+ `--document <bundle-path>` validates one unsaved document: its text is
73
+ read from stdin and stands in for that file (bundle-relative, posix, a
74
+ `.md` path; a file not yet written under an existing directory is walked
75
+ like any other), and nothing is written. A path that is absolute, escapes
76
+ the bundle, is not `.md`, or sits under a directory that does not exist,
77
+ and a terminal stdin, are usage errors (exit `64`). The MCP
78
+ `validate_bundle` tool's `documents` input is the same overlay.
79
+
72
80
  ### `okfit init`
73
81
 
74
82
  Scaffolds a fresh OKF bundle: a thin `.config/okfit.toml`, the
@@ -3,8 +3,9 @@ import { setExitCode } from "../internal/exit.js";
3
3
  import { CLI_VERSION } from "../version.js";
4
4
  import { useColor } from "../internal/tty.js";
5
5
  import { displayRoot, human, summary } from "../render/human.js";
6
+ import { readDocumentText } from "../internal/stdin.js";
6
7
  import { Argument, Command, Flag } from "effect/unstable/cli";
7
- import { JsonEnvelope, Now, collect, forDiagnostics, json, jsonError, provideConfig, resolveProjectConfig, run } from "@okfit/engine";
8
+ import { JsonEnvelope, Now, collect, forDiagnostics, json, jsonError, provideConfig, provideDocuments, resolveProjectConfig, run } from "@okfit/engine";
8
9
  import { Console, Effect, Layer, Option, Path, Schema } from "effect";
9
10
  import { OKF_SPEC_VERSION } from "@okfit/core";
10
11
  import { Git } from "@effected/git";
@@ -19,8 +20,10 @@ const configFlag = Flag.File("config").pipe(Flag.optional, Flag.withDescription(
19
20
  const formatFlag = Flag.Literals("format", ["human", "json"]).pipe(Flag.withDefault("human"), Flag.withDescription("output format: human (default) or json"));
20
21
  /** S-31: skips `Provenance.lint`'s git tier for this invocation, without touching `[lint]`. */
21
22
  const skipProvenanceFlag = Flag.Boolean("skip-provenance").pipe(Flag.withDefault(false), Flag.withDescription("skip the generated-at-drift lint's git tier for this run"));
23
+ /** Spec 4.5: validate one unsaved document, its text read from stdin, in place of the file on disk. */
24
+ const documentFlag = Flag.String("document").pipe(Flag.optional, Flag.withDescription("bundle-relative path of a document whose unsaved text is read from stdin and validated in place of the file on disk"));
22
25
  /**
23
- * `okfit validate [path] [--config <file>] [--format human|json] [--skip-provenance]`.
26
+ * `okfit validate [path] [--config <file>] [--format human|json] [--skip-provenance] [--document <bundle-path>]`.
24
27
  *
25
28
  * Handler order fixed by the contract (§2 `src/commands/validate.ts`):
26
29
  * stat `--config` (K-1) via `provideConfig`, discover (`OkfitConfigFile.discover`),
@@ -36,7 +39,8 @@ const validateCommand = Command.make("validate", {
36
39
  path: pathArg,
37
40
  config: configFlag,
38
41
  format: formatFlag,
39
- skipProvenance: skipProvenanceFlag
42
+ skipProvenance: skipProvenanceFlag,
43
+ document: documentFlag
40
44
  }, (input) => Effect.gen(function* () {
41
45
  const cwd = process.cwd();
42
46
  const discoveryCwd = Option.getOrElse(input.path, () => cwd);
@@ -49,13 +53,17 @@ const validateCommand = Command.make("validate", {
49
53
  explicitConfigPath: input.config,
50
54
  cwd
51
55
  });
56
+ const documents = Option.isSome(input.document) ? [{
57
+ path: input.document.value,
58
+ text: yield* readDocumentText(input.document.value)
59
+ }] : [];
52
60
  const result = yield* run({
53
61
  root: bundleRoot,
54
62
  config: merged,
55
63
  profile,
56
64
  now,
57
65
  skipProvenance: input.skipProvenance
58
- }).pipe(Effect.provide(Layer.mergeAll(Git.layer, GitHistory.layer)));
66
+ }).pipe(provideDocuments(bundleRoot, documents), Effect.provide(Layer.mergeAll(Git.layer, GitHistory.layer)));
59
67
  const diagnostics = collect(result.report.conformance, result.report.lint, result.profileDiagnostics);
60
68
  const code = forDiagnostics(diagnostics);
61
69
  if (input.format === "json") {
package/errors.js CHANGED
@@ -1,4 +1,5 @@
1
- import { ConfigMalformedError, ConfigPathNotFoundError, InitOverwriteError, SyncStagedLogError, VerifyConceptNotFoundError, VerifySelectionError, VerifyUnsupportedFrontmatterError } from "@okfit/engine";
1
+ import { DocumentStdinIsTerminalError } from "./internal/stdin.js";
2
+ import { ConfigMalformedError, ConfigPathNotFoundError, DocumentPathError, InitOverwriteError, SyncStagedLogError, VerifyConceptNotFoundError, VerifySelectionError, VerifyUnsupportedFrontmatterError } from "@okfit/engine";
2
3
  import { ConfigIssueRenderer } from "@effected/cli";
3
4
 
4
5
  //#region src/errors.ts
@@ -45,6 +46,8 @@ const relativeToCwd = (path, cwd) => {
45
46
  * 5b. `VerifySelectionError` (issue #138) renders the same way, one
46
47
  * `error: <message>` line naming why `okfit verify`'s selection was
47
48
  * contradictory, empty, or named an undeclared type.
49
+ * 5c. `DocumentPathError` and `DocumentStdinIsTerminalError` (`--document`)
50
+ * each render as one `error: <message>` line.
48
51
  * 6. Everything else — core's `BundleRootNotFoundError`/`BundleReadError`/
49
52
  * `BundleDepthExceededError`, config-file's other errors, `XdgEnvError`
50
53
  * (the K-13 `HOME`-unset case) — renders as the single line
@@ -67,6 +70,8 @@ const renderFailure = (error) => {
67
70
  if (error instanceof VerifyUnsupportedFrontmatterError) return [`error: ${error.message}`];
68
71
  if (error instanceof SyncStagedLogError) return [`error: ${error.message}`];
69
72
  if (error instanceof VerifySelectionError) return [`error: ${error.message}`];
73
+ if (error instanceof DocumentPathError) return [`error: ${error.message}`];
74
+ if (error instanceof DocumentStdinIsTerminalError) return [`error: ${error.message}`];
70
75
  if (hasTag(error, "ConfigValidationError")) {
71
76
  const validationError = error;
72
77
  return [`error: ${String(validationError)}`, ...ConfigIssueRenderer.render(validationError).map((line) => ` ${line}`)];
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Command } from "effect/unstable/cli";
2
+ import { Runtime, Schema } from "effect";
2
3
  import { ContextEnvelope, RenderedDiagnostic } from "@okfit/engine";
3
- import "effect";
4
4
  //#region src/commands/root.d.ts
5
5
  /**
6
6
  * K-5. No handler: the framework's own default for a command with neither a
@@ -20,7 +20,7 @@ import "effect";
20
20
  *
21
21
  * @public
22
22
  */
23
- export declare const rootCommand: Command.Command<"okfit", {} | {}, {}, import("@okfit/profiles").AgentActorUnconfiguredError | import("@okfit/core").BundleDepthExceededError | import("@okfit/core").BundleReadError | import("@okfit/core").BundleRootNotFoundError | import("@effected/config-file").ConfigCodecError | import("@effected/config-file").ConfigFileReadError | import("@effected/config-file").ConfigFileWriteError | import("@okfit/engine").ConfigMalformedError | import("@okfit/engine").ConfigPathNotFoundError | import("@effected/config-file").ConfigValidationError | import("@effected/markdown").FrontmatterEncodeError | import("@effected/markdown").FrontmatterFormatMismatchError | import("@effected/markdown").FrontmatterValidationError | import("@effected/git").GitCommandError | import("@okfit/profiles").GitHistoryError | import("@okfit/profiles").HumanActorUnresolvedError | import("@okfit/engine").InitOverwriteError | import("@effected/markdown").MarkdownParseError | import("@effected/git").NotARepositoryError | import("effect/PlatformError").PlatformError | import("effect/Schema").SchemaError | import("@okfit/engine").SyncStagedLogError | import("@effected/git").UnknownRefError | import("@okfit/engine").VerifyConceptNotFoundError | import("@okfit/engine").VerifySelectionError | import("@okfit/engine").VerifyUnsupportedFrontmatterError | import("@effected/yaml").YamlParseError, import("@effected/xdg").AppDirs | import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("@okfit/engine").Now | import("effect/Path").Path | import("@effected/xdg").Xdg>;
23
+ export declare const rootCommand: Command.Command<"okfit", {} | {}, {}, import("@okfit/profiles").AgentActorUnconfiguredError | import("@okfit/core").BundleDepthExceededError | import("@okfit/core").BundleReadError | import("@okfit/core").BundleRootNotFoundError | import("@effected/config-file").ConfigCodecError | import("@effected/config-file").ConfigFileReadError | import("@effected/config-file").ConfigFileWriteError | import("@okfit/engine").ConfigMalformedError | import("@okfit/engine").ConfigPathNotFoundError | import("@effected/config-file").ConfigValidationError | import("@okfit/engine").DocumentPathError | DocumentStdinIsTerminalError | import("@effected/markdown").FrontmatterEncodeError | import("@effected/markdown").FrontmatterFormatMismatchError | import("@effected/markdown").FrontmatterValidationError | import("@effected/git").GitCommandError | import("@okfit/profiles").GitHistoryError | import("@okfit/profiles").HumanActorUnresolvedError | import("@okfit/engine").InitOverwriteError | import("@effected/markdown").MarkdownParseError | import("@effected/git").NotARepositoryError | import("effect/PlatformError").PlatformError | import("effect/Schema").SchemaError | import("@okfit/engine").SyncStagedLogError | import("@effected/git").UnknownRefError | import("@okfit/engine").VerifyConceptNotFoundError | import("@okfit/engine").VerifySelectionError | import("@okfit/engine").VerifyUnsupportedFrontmatterError | import("@effected/yaml").YamlParseError, import("@effected/xdg").AppDirs | import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("@okfit/engine").Now | import("effect/Path").Path | import("effect/Stdio").Stdio | import("@effected/xdg").Xdg>;
24
24
  //#endregion
25
25
  //#region src/errors.d.ts
26
26
  /**
@@ -59,6 +59,8 @@ export declare const rootCommand: Command.Command<"okfit", {} | {}, {}, import("
59
59
  * 5b. `VerifySelectionError` (issue #138) renders the same way, one
60
60
  * `error: <message>` line naming why `okfit verify`'s selection was
61
61
  * contradictory, empty, or named an undeclared type.
62
+ * 5c. `DocumentPathError` and `DocumentStdinIsTerminalError` (`--document`)
63
+ * each render as one `error: <message>` line.
62
64
  * 6. Everything else — core's `BundleRootNotFoundError`/`BundleReadError`/
63
65
  * `BundleDepthExceededError`, config-file's other errors, `XdgEnvError`
64
66
  * (the K-13 `HOME`-unset case) — renders as the single line
@@ -70,6 +72,21 @@ export declare const rootCommand: Command.Command<"okfit", {} | {}, {}, import("
70
72
  */
71
73
  export declare const renderFailure: (error: unknown) => ReadonlyArray<string>;
72
74
  //#endregion
75
+ //#region src/internal/stdin.d.ts
76
+ declare const DocumentStdinIsTerminalError_base: Schema.Class<DocumentStdinIsTerminalError, Schema.TaggedStruct<"DocumentStdinIsTerminalError", {
77
+ readonly path: Schema.String;
78
+ }>, import("effect/Cause").YieldableError>;
79
+ /**
80
+ * `--document` reads the draft from stdin, and stdin is a terminal: reading would
81
+ * block forever waiting for input nobody is piping. Exit 64: a usage error.
82
+ *
83
+ * @public
84
+ */
85
+ export declare class DocumentStdinIsTerminalError extends DocumentStdinIsTerminalError_base {
86
+ readonly [Runtime.errorExitCode] = 64;
87
+ get message(): string;
88
+ }
89
+ //#endregion
73
90
  //#region src/render/context.d.ts
74
91
  /**
75
92
  * The `human` format: a short header block, then one line per type (plus
package/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { humanContext } from "./render/context.js";
2
2
  import { CLI_VERSION } from "./version.js";
3
3
  import { human, line, summary } from "./render/human.js";
4
+ import { DocumentStdinIsTerminalError } from "./internal/stdin.js";
4
5
  import { humanVerify } from "./render/verify.js";
5
6
  import { rootCommand } from "./commands/root.js";
6
7
  import { renderFailure } from "./errors.js";
7
8
 
8
- export { CLI_VERSION, human, humanContext, humanVerify, line, renderFailure, rootCommand, summary };
9
+ export { CLI_VERSION, DocumentStdinIsTerminalError, human, humanContext, humanVerify, line, renderFailure, rootCommand, summary };
@@ -0,0 +1,28 @@
1
+ import { Effect, Runtime, Schema, Stdio, Stream } from "effect";
2
+
3
+ //#region src/internal/stdin.ts
4
+ /**
5
+ * `--document` reads the draft from stdin, and stdin is a terminal: reading would
6
+ * block forever waiting for input nobody is piping. Exit 64: a usage error.
7
+ *
8
+ * @public
9
+ */
10
+ var DocumentStdinIsTerminalError = class extends Schema.TaggedError()("DocumentStdinIsTerminalError", { path: Schema.String }) {
11
+ [Runtime.errorExitCode] = 64;
12
+ get message() {
13
+ return `--document reads the document's text from stdin, but stdin is a terminal; pipe it in, for example: okfit validate --document ${this.path} < draft.md`;
14
+ }
15
+ };
16
+ /**
17
+ * The whole of stdin as UTF-8 text, for `okfit validate --document <path>` (spec 4.5).
18
+ *
19
+ * @internal
20
+ */
21
+ const readDocumentText = (path) => Effect.gen(function* () {
22
+ const stdio = yield* Stdio.Stdio;
23
+ if (yield* stdio.stdinIsTerminal) return yield* new DocumentStdinIsTerminalError({ path });
24
+ return yield* stdio.stdin.pipe(Stream.decodeText(), Stream.mkString);
25
+ });
26
+
27
+ //#endregion
28
+ export { DocumentStdinIsTerminalError, readDocumentText };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okfit/cli",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
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,20 +42,20 @@
42
42
  "okfit": "bin/okfit.js"
43
43
  },
44
44
  "dependencies": {
45
- "@effect/platform-node": "4.0.0-rc.116",
46
- "@effected/cli": "^0.6.0",
47
- "@effected/config-file": "^0.11.1",
48
- "@effected/git": "^0.16.0",
49
- "@effected/glob": "^0.7.0",
50
- "@effected/jsonc": "^0.12.0",
51
- "@effected/markdown": "^0.11.0",
52
- "@effected/toml": "^0.8.0",
53
- "@effected/walker": "^0.11.0",
54
- "@effected/yaml": "^0.16.0",
55
- "@okfit/core": "0.7.3",
56
- "@okfit/engine": "0.7.4",
57
- "@okfit/profiles": "0.7.4",
58
- "effect": "4.0.0-rc.116"
45
+ "@effect/platform-node": "4.0.0-rc.117",
46
+ "@effected/cli": "^0.7.0",
47
+ "@effected/config-file": "^0.12.0",
48
+ "@effected/git": "^0.17.0",
49
+ "@effected/glob": "^0.8.0",
50
+ "@effected/jsonc": "^0.13.0",
51
+ "@effected/markdown": "^0.12.0",
52
+ "@effected/toml": "^0.9.0",
53
+ "@effected/walker": "^0.12.0",
54
+ "@effected/yaml": "^0.17.0",
55
+ "@okfit/core": "0.8.0",
56
+ "@okfit/engine": "0.8.0",
57
+ "@okfit/profiles": "0.8.0",
58
+ "effect": "4.0.0-rc.117"
59
59
  },
60
60
  "engines": {
61
61
  "node": ">=24.11.0"
package/version.js CHANGED
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * @public
13
13
  */
14
- const CLI_VERSION = "0.6.4";
14
+ const CLI_VERSION = "0.6.6";
15
15
 
16
16
  //#endregion
17
17
  export { CLI_VERSION };