@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 +9 -1
- package/commands/validate.js +12 -4
- package/errors.js +6 -1
- package/index.d.ts +19 -2
- package/index.js +2 -1
- package/internal/stdin.js +28 -0
- package/package.json +15 -15
- package/version.js +1 -1
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
|
package/commands/validate.js
CHANGED
|
@@ -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 {
|
|
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.
|
|
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.
|
|
46
|
-
"@effected/cli": "^0.
|
|
47
|
-
"@effected/config-file": "^0.
|
|
48
|
-
"@effected/git": "^0.
|
|
49
|
-
"@effected/glob": "^0.
|
|
50
|
-
"@effected/jsonc": "^0.
|
|
51
|
-
"@effected/markdown": "^0.
|
|
52
|
-
"@effected/toml": "^0.
|
|
53
|
-
"@effected/walker": "^0.
|
|
54
|
-
"@effected/yaml": "^0.
|
|
55
|
-
"@okfit/core": "0.
|
|
56
|
-
"@okfit/engine": "0.
|
|
57
|
-
"@okfit/profiles": "0.
|
|
58
|
-
"effect": "4.0.0-rc.
|
|
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"
|