@okfit/cli 0.5.3 → 0.6.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/README.md +20 -4
- package/commands/context.js +3 -1
- package/commands/graph.js +5 -2
- package/commands/init.js +2 -2
- package/commands/lint.js +5 -2
- package/commands/stale.js +5 -2
- package/commands/sync.js +33 -18
- package/commands/validate.js +5 -2
- package/commands/verify.js +64 -12
- package/errors.js +9 -1
- package/index.d.ts +15 -6
- package/internal/distribution.js +21 -0
- package/internal/versionFormatter.js +32 -0
- package/main.d.ts +10 -3
- package/main.js +7 -4
- package/package.json +4 -4
- package/render/sync.js +1 -1
- package/render/verify.js +16 -1
- package/version.js +9 -6
package/README.md
CHANGED
|
@@ -14,6 +14,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]
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
`okfit --version` prints `okfit <cli> (engine <engine>, okf <okf>,
|
|
18
|
+
config-schema <schema>)` — for example `okfit 0.5.4 (engine 0.6.0, okf 0.2,
|
|
19
|
+
config-schema 1.0)`, or `okfit 0.5.4 via @okfit/plugin 0.3.7 (engine 0.6.0,
|
|
20
|
+
okf 0.2, config-schema 1.0)` when installed through the meta-package. The
|
|
21
|
+
engine, OKF, and config-schema versions decide what a report says and what
|
|
22
|
+
a config may contain; the CLI version is packaging.
|
|
23
|
+
|
|
17
24
|
`[path]` is the **project root** on every subcommand — the directory discovery
|
|
18
25
|
starts from, and, for `init`, where `.config/okfit.toml` is written.
|
|
19
26
|
It is never the bundle root; the bundle root is `<project root>/<bundle.path>`
|
|
@@ -277,8 +284,10 @@ nothing else (no summary line, no warnings — those still go to stderr):
|
|
|
277
284
|
```json
|
|
278
285
|
{
|
|
279
286
|
"schema": 1,
|
|
280
|
-
"okfit_version": "0.
|
|
287
|
+
"okfit_version": "0.5.4",
|
|
288
|
+
"engine_version": "0.6.0",
|
|
281
289
|
"producer": "okfit",
|
|
290
|
+
"distribution": null,
|
|
282
291
|
"okf_version": "0.2",
|
|
283
292
|
"root": "/abs/path/to/my-repo/okf",
|
|
284
293
|
"profile": "software-project",
|
|
@@ -311,15 +320,21 @@ nothing else (no summary line, no warnings — those still go to stderr):
|
|
|
311
320
|
}
|
|
312
321
|
```
|
|
313
322
|
|
|
314
|
-
`
|
|
315
|
-
|
|
323
|
+
`engine_version` and `okf_version` are the pair to compare across reports:
|
|
324
|
+
the MCP server's `validate_bundle` tool returns this same envelope with the
|
|
325
|
+
same two values over one bundle, and differs only in `okfit_version` (its own
|
|
326
|
+
version) and `producer` (`@okfit/mcp`). `distribution` names the
|
|
327
|
+
meta-package the bin was installed through (`{ "name": "@okfit/plugin",
|
|
328
|
+
"version": "0.3.7" }`) and is `null` for a direct install. `profile` is
|
|
329
|
+
`null` when the config sets `bundle.profile = "none"` or names a profile
|
|
330
|
+
`okfit` does not recognise. `range` is zero-based, exactly as
|
|
316
331
|
`@okfit/core` computed it, and omitted for a range-less diagnostic.
|
|
317
332
|
|
|
318
333
|
An infrastructure failure under `--format json` prints a different, smaller
|
|
319
334
|
envelope to stdout and exits `3`:
|
|
320
335
|
|
|
321
336
|
```json
|
|
322
|
-
{ "schema": 1, "okfit_version": "0.
|
|
337
|
+
{ "schema": 1, "okfit_version": "0.5.4", "engine_version": "0.6.0", "distribution": null, "exit_code": 3, "error": { "tag": "ConfigPathNotFoundError", "message": "config path not found: /abs/ci-config.toml" } }
|
|
323
338
|
```
|
|
324
339
|
|
|
325
340
|
`init` has no `--format`; it is human output only.
|
|
@@ -330,6 +345,7 @@ one above:
|
|
|
330
345
|
```json
|
|
331
346
|
{
|
|
332
347
|
"schema": 1,
|
|
348
|
+
"config_schema_version": "1.0",
|
|
333
349
|
"project_root": "/abs/path/to/my-repo",
|
|
334
350
|
"bundle_root": "/abs/path/to/my-repo/okf",
|
|
335
351
|
"config_path": null,
|
package/commands/context.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Distribution } from "../internal/distribution.js";
|
|
1
2
|
import { setExitCode } from "../internal/exit.js";
|
|
2
3
|
import { humanContext } from "../render/context.js";
|
|
3
4
|
import { CLI_VERSION } from "../version.js";
|
|
@@ -35,6 +36,7 @@ const contextCommand = Command.make("context", {
|
|
|
35
36
|
}, (input) => Effect.gen(function* () {
|
|
36
37
|
const cwd = process.cwd();
|
|
37
38
|
const discoveryCwd = Option.getOrElse(input.path, () => cwd);
|
|
39
|
+
const distribution = yield* Distribution;
|
|
38
40
|
const body = Effect.gen(function* () {
|
|
39
41
|
const { projectRoot, bundleRoot, config: merged, profile, profileName, discovered } = yield* resolveProjectConfig({
|
|
40
42
|
pathArg: input.path,
|
|
@@ -67,7 +69,7 @@ const contextCommand = Command.make("context", {
|
|
|
67
69
|
explicitConfigPath: input.config,
|
|
68
70
|
discoveryCwd
|
|
69
71
|
}));
|
|
70
|
-
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION)))));
|
|
72
|
+
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION, Option.getOrUndefined(distribution))))));
|
|
71
73
|
return yield* body;
|
|
72
74
|
})).pipe(Command.withDescription("Print the resolved project root, bundle root, config path, profile, and type/tag vocabulary without loading the bundle."));
|
|
73
75
|
|
package/commands/graph.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Distribution } from "../internal/distribution.js";
|
|
1
2
|
import { setExitCode } from "../internal/exit.js";
|
|
2
3
|
import { CLI_VERSION } from "../version.js";
|
|
3
4
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
@@ -39,6 +40,7 @@ const graphCommand = Command.make("graph", {
|
|
|
39
40
|
}, (input) => Effect.gen(function* () {
|
|
40
41
|
const cwd = process.cwd();
|
|
41
42
|
const discoveryCwd = Option.getOrElse(input.path, () => cwd);
|
|
43
|
+
const distribution = yield* Distribution;
|
|
42
44
|
const body = Effect.gen(function* () {
|
|
43
45
|
const { bundleRoot, config: merged, profile } = yield* resolveProjectConfig({
|
|
44
46
|
pathArg: input.path,
|
|
@@ -57,7 +59,8 @@ const graphCommand = Command.make("graph", {
|
|
|
57
59
|
onSome: (p) => p.name
|
|
58
60
|
}),
|
|
59
61
|
nodes: result.graph.nodes,
|
|
60
|
-
edges: result.graph.edges
|
|
62
|
+
edges: result.graph.edges,
|
|
63
|
+
...Option.isSome(distribution) ? { distribution: distribution.value } : {}
|
|
61
64
|
});
|
|
62
65
|
yield* Console.log(JSON.stringify(Schema.encodeSync(GraphEnvelope)(envelope)));
|
|
63
66
|
} else if (input.format === "dot") yield* Console.log(result.graph.toGraphViz());
|
|
@@ -67,7 +70,7 @@ const graphCommand = Command.make("graph", {
|
|
|
67
70
|
explicitConfigPath: input.config,
|
|
68
71
|
discoveryCwd
|
|
69
72
|
}));
|
|
70
|
-
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION)))));
|
|
73
|
+
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION, Option.getOrUndefined(distribution))))));
|
|
71
74
|
return yield* body;
|
|
72
75
|
})).pipe(Command.withDescription("Render the bundle's link graph: frontmatter path fields and body links, as Mermaid, DOT, or JSON."));
|
|
73
76
|
|
package/commands/init.js
CHANGED
|
@@ -2,9 +2,9 @@ import { setExitCode } from "../internal/exit.js";
|
|
|
2
2
|
import { useColor } from "../internal/tty.js";
|
|
3
3
|
import { displayRoot, human, summary } from "../render/human.js";
|
|
4
4
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
5
|
-
import { CONFIG_RELATIVE_PATH, InitOverwriteError, Now,
|
|
5
|
+
import { CONFIG_RELATIVE_PATH, InitOverwriteError, Now, collect, configValue, files, forDiagnostics, provideConfig, resolveBundleRoot, resolveProjectRoot, run, targetPaths } from "@okfit/engine";
|
|
6
6
|
import { Console, DateTime, Effect, FileSystem, Layer, Option, Path } from "effect";
|
|
7
|
-
import { OKF_SPEC_VERSION, OkfitConfig, OkfitConfigFile } from "@okfit/core";
|
|
7
|
+
import { OKF_SPEC_VERSION, OkfitConfig, OkfitConfigFile, SCHEMA_DIRECTIVE } from "@okfit/core";
|
|
8
8
|
import { Git } from "@effected/git";
|
|
9
9
|
import { GitHistory, Profiles } from "@okfit/profiles";
|
|
10
10
|
|
package/commands/lint.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Distribution } from "../internal/distribution.js";
|
|
1
2
|
import { setExitCode } from "../internal/exit.js";
|
|
2
3
|
import { CLI_VERSION } from "../version.js";
|
|
3
4
|
import { useColor } from "../internal/tty.js";
|
|
@@ -42,6 +43,7 @@ const lintCommand = Command.make("lint", {
|
|
|
42
43
|
const discoveryCwd = Option.getOrElse(input.path, () => cwd);
|
|
43
44
|
const now = yield* Now;
|
|
44
45
|
const path = yield* Path.Path;
|
|
46
|
+
const distribution = yield* Distribution;
|
|
45
47
|
const body = Effect.gen(function* () {
|
|
46
48
|
const { bundleRoot, config: merged, profile } = yield* resolveProjectConfig({
|
|
47
49
|
pathArg: input.path,
|
|
@@ -69,7 +71,8 @@ const lintCommand = Command.make("lint", {
|
|
|
69
71
|
}),
|
|
70
72
|
exitCode: code,
|
|
71
73
|
concepts: result.bundle.concepts.size,
|
|
72
|
-
diagnostics
|
|
74
|
+
diagnostics,
|
|
75
|
+
...Option.isSome(distribution) ? { distribution: distribution.value } : {}
|
|
73
76
|
});
|
|
74
77
|
yield* Console.log(JSON.stringify(Schema.encodeSync(JsonEnvelope)(envelope)));
|
|
75
78
|
} else {
|
|
@@ -87,7 +90,7 @@ const lintCommand = Command.make("lint", {
|
|
|
87
90
|
explicitConfigPath: input.config,
|
|
88
91
|
discoveryCwd
|
|
89
92
|
}));
|
|
90
|
-
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION)))));
|
|
93
|
+
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION, Option.getOrUndefined(distribution))))));
|
|
91
94
|
return yield* body;
|
|
92
95
|
})).pipe(Command.withDescription("Load config and bundle, run lint checks (and the profile check), and render the diagnostics; never conformance."));
|
|
93
96
|
|
package/commands/stale.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Distribution } from "../internal/distribution.js";
|
|
1
2
|
import { setExitCode } from "../internal/exit.js";
|
|
2
3
|
import { CLI_VERSION } from "../version.js";
|
|
3
4
|
import { displayRoot } from "../render/human.js";
|
|
@@ -35,6 +36,7 @@ const staleCommand = Command.make("stale", {
|
|
|
35
36
|
const discoveryCwd = Option.getOrElse(input.path, () => cwd);
|
|
36
37
|
const now = yield* Now;
|
|
37
38
|
const path = yield* Path.Path;
|
|
39
|
+
const distribution = yield* Distribution;
|
|
38
40
|
const body = Effect.gen(function* () {
|
|
39
41
|
const { bundleRoot, config: merged, profile } = yield* resolveProjectConfig({
|
|
40
42
|
pathArg: input.path,
|
|
@@ -56,7 +58,8 @@ const staleCommand = Command.make("stale", {
|
|
|
56
58
|
}),
|
|
57
59
|
now,
|
|
58
60
|
concepts: result.bundle.concepts.size,
|
|
59
|
-
items: result.items
|
|
61
|
+
items: result.items,
|
|
62
|
+
...Option.isSome(distribution) ? { distribution: distribution.value } : {}
|
|
60
63
|
});
|
|
61
64
|
if (input.format === "json") yield* Console.log(JSON.stringify(Schema.encodeSync(StaleEnvelope)(envelope)));
|
|
62
65
|
else {
|
|
@@ -68,7 +71,7 @@ const staleCommand = Command.make("stale", {
|
|
|
68
71
|
explicitConfigPath: input.config,
|
|
69
72
|
discoveryCwd
|
|
70
73
|
}));
|
|
71
|
-
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION)))));
|
|
74
|
+
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION, Option.getOrUndefined(distribution))))));
|
|
72
75
|
return yield* body;
|
|
73
76
|
})).pipe(Command.withDescription("List every concept whose stale_after instant has passed as of now, with how many whole days past it."));
|
|
74
77
|
|
package/commands/sync.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { Distribution } from "../internal/distribution.js";
|
|
1
2
|
import { setExitCode } from "../internal/exit.js";
|
|
2
3
|
import { CLI_VERSION } from "../version.js";
|
|
3
4
|
import { displayRoot } from "../render/human.js";
|
|
4
5
|
import { humanSync } from "../render/sync.js";
|
|
5
6
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
6
|
-
import { SyncEnvelope, jsonError, provideConfig, resolveProjectConfig, runSync, syncEnvelope } from "@okfit/engine";
|
|
7
|
-
import { Console, Effect, Layer, Option, Path, Schema } from "effect";
|
|
7
|
+
import { Now, SyncEnvelope, jsonError, provideConfig, resolveProjectConfig, runSync, syncEnvelope } from "@okfit/engine";
|
|
8
|
+
import { Console, DateTime, Effect, Layer, Option, Path, Schema } from "effect";
|
|
8
9
|
import { Git } from "@effected/git";
|
|
9
10
|
import { GitHistory } from "@okfit/profiles";
|
|
10
11
|
|
|
@@ -38,23 +39,30 @@ const onlyFlag = Flag.Literals("only", [
|
|
|
38
39
|
]).pipe(Flag.atLeast(0), Flag.withDescription("restrict the run to these modes (repeatable: --only generated --only index); default: all three"));
|
|
39
40
|
const dryRunFlag = Flag.Boolean("dry-run").pipe(Flag.withDefault(false), Flag.withDescription("compute every result and write nothing"));
|
|
40
41
|
const formatFlag = Flag.Literals("format", ["human", "json"]).pipe(Flag.withDefault("human"), Flag.withDescription("output format: human (default) or json"));
|
|
42
|
+
/** #18: log mode's inclusive floor; a malformed value fails at parse time (`ShowHelp` -> exit `64`). */
|
|
43
|
+
const ISO_DATE = Schema.String.pipe(Schema.check(Schema.isPattern(/^\d{4}-\d{2}-\d{2}$/, { message: "Expected a YYYY-MM-DD date" })));
|
|
44
|
+
const sinceFlag = Flag.String("since").pipe(Flag.withSchema(ISO_DATE), Flag.optional, Flag.withDescription("log mode's inclusive floor (YYYY-MM-DD): consider every committed concept dated on or after it; default: the newest date already in log.md"));
|
|
45
|
+
const stagedFlag = Flag.Boolean("staged").pipe(Flag.withDefault(false), Flag.withDescription("pre-commit mode: stamp only the concepts in the git index, with now as generated.at, and re-add what was written; log mode is excluded (default modes: generated, index)"));
|
|
41
46
|
/**
|
|
42
47
|
* `okfit sync [path] [--config <file>] [--only <mode>]... [--dry-run]
|
|
43
|
-
* [--format human|json]`.
|
|
48
|
+
* [--format human|json] [--since <YYYY-MM-DD>] [--staged]`.
|
|
44
49
|
*
|
|
45
50
|
* Handler order fixed by contract §4.3. Steps 1–3 are `context`'s/
|
|
46
51
|
* `validate`'s handler in substance — stat `--config` (K-1) via
|
|
47
52
|
* `provideConfig`, resolve the project and bundle roots through
|
|
48
53
|
* `resolveProjectConfig` — then it diverges: build the `--only` mode
|
|
49
|
-
* set (default: all three,
|
|
50
|
-
* order
|
|
51
|
-
* `
|
|
52
|
-
* `verify.ts:134`'s
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
54
|
+
* set (default: all three modes, or `generated`+`index` under `--staged`;
|
|
55
|
+
* order irrelevant either way — `runSync`'s own fixed order wins, not
|
|
56
|
+
* `--only`'s occurrence order), run `runSync` with BOTH `Git.layer` and
|
|
57
|
+
* `GitHistory.layer` provided (S-16, mirroring `verify.ts:134`'s
|
|
58
|
+
* `Git.layer`-alone provision one layer up: here two layers are needed
|
|
59
|
+
* because `GitHistory.layer` does not re-expose `Git` even though it is
|
|
60
|
+
* built on it), render, and always exit `0`. There is no content tier:
|
|
61
|
+
* every typed failure is exit `3` through `bin.ts`'s existing
|
|
62
|
+
* `reportFailures`, EXCEPT `SyncStagedLogError` (`--staged` combined with
|
|
63
|
+
* `--only log`), which carries its own `[Runtime.errorExitCode] = 64`;
|
|
64
|
+
* an unknown `--only` token never reaches this handler at all — it fails
|
|
65
|
+
* at parse time, exit `64`.
|
|
58
66
|
*
|
|
59
67
|
* @public
|
|
60
68
|
*/
|
|
@@ -63,10 +71,13 @@ const syncCommand = Command.make("sync", {
|
|
|
63
71
|
config: configFlag,
|
|
64
72
|
only: onlyFlag,
|
|
65
73
|
dryRun: dryRunFlag,
|
|
66
|
-
format: formatFlag
|
|
74
|
+
format: formatFlag,
|
|
75
|
+
since: sinceFlag,
|
|
76
|
+
staged: stagedFlag
|
|
67
77
|
}, (input) => Effect.gen(function* () {
|
|
68
78
|
const cwd = process.cwd();
|
|
69
79
|
const discoveryCwd = Option.getOrElse(input.path, () => cwd);
|
|
80
|
+
const distribution = yield* Distribution;
|
|
70
81
|
const body = Effect.gen(function* () {
|
|
71
82
|
const path = yield* Path.Path;
|
|
72
83
|
const resolved = yield* resolveProjectConfig({
|
|
@@ -74,16 +85,19 @@ const syncCommand = Command.make("sync", {
|
|
|
74
85
|
explicitConfigPath: input.config,
|
|
75
86
|
cwd
|
|
76
87
|
});
|
|
77
|
-
const modes = input.only.length
|
|
88
|
+
const modes = input.only.length > 0 ? new Set(input.only) : input.staged ? /* @__PURE__ */ new Set(["generated", "index"]) : /* @__PURE__ */ new Set([
|
|
78
89
|
"generated",
|
|
79
90
|
"index",
|
|
80
91
|
"log"
|
|
81
|
-
])
|
|
92
|
+
]);
|
|
93
|
+
const staged = input.staged ? { at: DateTime.startOf(yield* Now, "second") } : void 0;
|
|
82
94
|
const result = yield* runSync({
|
|
83
95
|
bundleRoot: resolved.bundleRoot,
|
|
84
96
|
config: resolved.config,
|
|
85
97
|
modes,
|
|
86
|
-
dryRun: input.dryRun
|
|
98
|
+
dryRun: input.dryRun,
|
|
99
|
+
...Option.isSome(input.since) ? { logSince: input.since.value } : {},
|
|
100
|
+
...staged === void 0 ? {} : { staged }
|
|
87
101
|
}).pipe(Effect.provide(Layer.mergeAll(Git.layer, GitHistory.layer)));
|
|
88
102
|
const displayPath = displayRoot(cwd, result.bundleRoot, path);
|
|
89
103
|
if (input.format === "json") {
|
|
@@ -91,7 +105,8 @@ const syncCommand = Command.make("sync", {
|
|
|
91
105
|
okfitVersion: CLI_VERSION,
|
|
92
106
|
root: displayPath,
|
|
93
107
|
dryRun: result.dryRun,
|
|
94
|
-
result
|
|
108
|
+
result,
|
|
109
|
+
...Option.isSome(distribution) ? { distribution: distribution.value } : {}
|
|
95
110
|
});
|
|
96
111
|
yield* Console.log(JSON.stringify(Schema.encodeSync(SyncEnvelope)(envelope)));
|
|
97
112
|
} else for (const line of humanSync(result)) yield* Console.log(line);
|
|
@@ -100,7 +115,7 @@ const syncCommand = Command.make("sync", {
|
|
|
100
115
|
explicitConfigPath: input.config,
|
|
101
116
|
discoveryCwd
|
|
102
117
|
}));
|
|
103
|
-
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION)))));
|
|
118
|
+
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION, Option.getOrUndefined(distribution))))));
|
|
104
119
|
return yield* body;
|
|
105
120
|
})).pipe(Command.withDescription("Regenerate the derived-content families that no other command produces: generated.at, index.md, and log.md, all from git history and the bundle's own concepts. Mechanical and agent-runnable; never an attestation and never touches verified."));
|
|
106
121
|
|
package/commands/validate.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Distribution } from "../internal/distribution.js";
|
|
1
2
|
import { setExitCode } from "../internal/exit.js";
|
|
2
3
|
import { CLI_VERSION } from "../version.js";
|
|
3
4
|
import { useColor } from "../internal/tty.js";
|
|
@@ -41,6 +42,7 @@ const validateCommand = Command.make("validate", {
|
|
|
41
42
|
const discoveryCwd = Option.getOrElse(input.path, () => cwd);
|
|
42
43
|
const now = yield* Now;
|
|
43
44
|
const path = yield* Path.Path;
|
|
45
|
+
const distribution = yield* Distribution;
|
|
44
46
|
const body = Effect.gen(function* () {
|
|
45
47
|
const { bundleRoot, config: merged, profile } = yield* resolveProjectConfig({
|
|
46
48
|
pathArg: input.path,
|
|
@@ -68,7 +70,8 @@ const validateCommand = Command.make("validate", {
|
|
|
68
70
|
}),
|
|
69
71
|
exitCode: code,
|
|
70
72
|
concepts: result.bundle.concepts.size,
|
|
71
|
-
diagnostics
|
|
73
|
+
diagnostics,
|
|
74
|
+
...Option.isSome(distribution) ? { distribution: distribution.value } : {}
|
|
72
75
|
});
|
|
73
76
|
yield* Console.log(JSON.stringify(Schema.encodeSync(JsonEnvelope)(envelope)));
|
|
74
77
|
} else {
|
|
@@ -86,7 +89,7 @@ const validateCommand = Command.make("validate", {
|
|
|
86
89
|
explicitConfigPath: input.config,
|
|
87
90
|
discoveryCwd
|
|
88
91
|
}));
|
|
89
|
-
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION)))));
|
|
92
|
+
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION, Option.getOrUndefined(distribution))))));
|
|
90
93
|
return yield* body;
|
|
91
94
|
})).pipe(Command.withDescription("Load config and bundle, run conformance and lint checks, and render the diagnostics."));
|
|
92
95
|
|
package/commands/verify.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
+
import { Distribution } from "../internal/distribution.js";
|
|
1
2
|
import { setExitCode } from "../internal/exit.js";
|
|
2
3
|
import { CLI_VERSION } from "../version.js";
|
|
3
4
|
import { displayRoot } from "../render/human.js";
|
|
4
|
-
import { humanVerify } from "../render/verify.js";
|
|
5
|
+
import { humanVerify, humanVerifyBatch } from "../render/verify.js";
|
|
5
6
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
6
|
-
import { Now, VerifyEnvelope, jsonError, provideConfig, resolveProjectConfig, runVerify, verifyEnvelope } from "@okfit/engine";
|
|
7
|
+
import { Now, VerifyBatchEnvelope, VerifyEnvelope, VerifySelectionError, jsonError, provideConfig, resolveProjectConfig, runVerify, runVerifyBatch, verifyBatchEnvelope, verifyEnvelope } from "@okfit/engine";
|
|
7
8
|
import { Console, DateTime, Effect, Option, Path, Schema } from "effect";
|
|
8
9
|
import { Timestamp } from "@okfit/core";
|
|
9
10
|
import { Git } from "@effected/git";
|
|
10
11
|
|
|
11
12
|
//#region src/commands/verify.ts
|
|
12
|
-
/**
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* V-6: tolerant id, normalised through `ConceptId.normalize`; never
|
|
15
|
+
* `Argument.Path`. Issue #138 makes it optional: `--all`/`--type` select a
|
|
16
|
+
* batch instead of a single concept.
|
|
17
|
+
*/
|
|
18
|
+
const idArg = Argument.String("id").pipe(Argument.optional, Argument.withDescription("concept id to verify, with or without a leading slash or trailing .md; omit with --all or --type"));
|
|
19
|
+
/** Issue #138: attest every unverified concept whose type sets `require_verified`. */
|
|
20
|
+
const allFlag = Flag.Boolean("all").pipe(Flag.withDefault(false), Flag.withDescription("verify every concept whose type sets require_verified and that you have not verified yet; drafts are skipped"));
|
|
21
|
+
/** Issue #138: narrow (or, without --all, define) the batch to these types. */
|
|
22
|
+
const typeFlag = Flag.String("type").pipe(Flag.atLeast(0), Flag.withDescription("verify every unverified concept of this type (repeatable); implies --all's selection rule for the named types"));
|
|
14
23
|
/** K-2: `[path]` is the PROJECT root, byte-identical to validate/init/context's. */
|
|
15
24
|
const pathArg = Argument.Path("path", { pathType: "directory" }).pipe(Argument.optional, Argument.withDescription("project root to start config discovery from (default: current directory); never the bundle root"));
|
|
16
25
|
/** K-1: no `mustExist`; the handler stats it via `provideConfig`. */
|
|
@@ -21,8 +30,8 @@ const atFlag = Flag.String("at").pipe(Flag.optional, Flag.withDescription("ISO 8
|
|
|
21
30
|
const dryRunFlag = Flag.Boolean("dry-run").pipe(Flag.withDefault(false), Flag.withDescription("print the exact fragment a real run would splice in; write nothing"));
|
|
22
31
|
const formatFlag = Flag.Literals("format", ["human", "json"]).pipe(Flag.withDefault("human"), Flag.withDescription("output format: human (default) or json"));
|
|
23
32
|
/**
|
|
24
|
-
* `okfit verify <id> [path] [--
|
|
25
|
-
* [--format human|json]`.
|
|
33
|
+
* `okfit verify [<id>] [path] [--all] [--type <Type>]... [--config <file>]
|
|
34
|
+
* [--at <iso>] [--dry-run] [--format human|json]`.
|
|
26
35
|
*
|
|
27
36
|
* Handler order fixed by contract §2.3. Steps 1–3 are `context`'s handler
|
|
28
37
|
* in substance — stat `--config` (K-1) via `provideConfig`, discover,
|
|
@@ -40,22 +49,64 @@ const verifyCommand = Command.make("verify", {
|
|
|
40
49
|
id: idArg,
|
|
41
50
|
path: pathArg,
|
|
42
51
|
config: configFlag,
|
|
52
|
+
all: allFlag,
|
|
53
|
+
type: typeFlag,
|
|
43
54
|
at: atFlag,
|
|
44
55
|
dryRun: dryRunFlag,
|
|
45
56
|
format: formatFlag
|
|
46
57
|
}, (input) => Effect.gen(function* () {
|
|
47
58
|
const cwd = process.cwd();
|
|
48
|
-
const
|
|
59
|
+
const batch = input.all || input.type.length > 0;
|
|
60
|
+
const effectivePath = batch && Option.isSome(input.id) && Option.isNone(input.path) ? input.id : input.path;
|
|
61
|
+
const discoveryCwd = Option.getOrElse(effectivePath, () => cwd);
|
|
62
|
+
const distribution = yield* Distribution;
|
|
49
63
|
const body = Effect.gen(function* () {
|
|
50
64
|
const path = yield* Path.Path;
|
|
51
65
|
const resolved = yield* resolveProjectConfig({
|
|
52
|
-
pathArg:
|
|
66
|
+
pathArg: effectivePath,
|
|
53
67
|
explicitConfigPath: input.config,
|
|
54
68
|
cwd
|
|
55
69
|
});
|
|
56
70
|
const at = Option.isNone(input.at) ? DateTime.startOf(yield* Now, "second") : yield* Schema.decodeUnknownEffect(Timestamp)(input.at.value);
|
|
71
|
+
if (Option.isSome(input.id) && Option.isSome(input.path) && batch) return yield* new VerifySelectionError({ reason: "id-and-batch" });
|
|
72
|
+
if (Option.isNone(input.id) && !batch) return yield* new VerifySelectionError({ reason: "no-selection" });
|
|
73
|
+
if (batch) {
|
|
74
|
+
const result = yield* runVerifyBatch({
|
|
75
|
+
bundleRoot: resolved.bundleRoot,
|
|
76
|
+
projectRoot: resolved.projectRoot,
|
|
77
|
+
config: resolved.config,
|
|
78
|
+
at,
|
|
79
|
+
dryRun: input.dryRun,
|
|
80
|
+
types: input.type
|
|
81
|
+
});
|
|
82
|
+
const root = displayRoot(cwd, result.bundleRoot, path);
|
|
83
|
+
if (input.format === "json") {
|
|
84
|
+
const envelope = verifyBatchEnvelope({
|
|
85
|
+
okfitVersion: CLI_VERSION,
|
|
86
|
+
by: result.by,
|
|
87
|
+
at: result.at,
|
|
88
|
+
concepts: result.verified.map((entry) => ({
|
|
89
|
+
id: entry.id,
|
|
90
|
+
path: `${root}/${entry.conceptPath}`
|
|
91
|
+
})),
|
|
92
|
+
skipped: result.skipped,
|
|
93
|
+
dryRun: result.dryRun,
|
|
94
|
+
...Option.isSome(distribution) ? { distribution: distribution.value } : {}
|
|
95
|
+
});
|
|
96
|
+
yield* Console.log(JSON.stringify(Schema.encodeSync(VerifyBatchEnvelope)(envelope)));
|
|
97
|
+
} else for (const line of humanVerifyBatch({
|
|
98
|
+
by: result.by,
|
|
99
|
+
at: result.at,
|
|
100
|
+
dryRun: result.dryRun,
|
|
101
|
+
verified: result.verified,
|
|
102
|
+
skipped: result.skipped
|
|
103
|
+
})) yield* Console.log(line);
|
|
104
|
+
setExitCode(0);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (Option.isNone(input.id)) return yield* new VerifySelectionError({ reason: "no-selection" });
|
|
57
108
|
const result = yield* runVerify({
|
|
58
|
-
id: input.id,
|
|
109
|
+
id: input.id.value,
|
|
59
110
|
bundleRoot: resolved.bundleRoot,
|
|
60
111
|
projectRoot: resolved.projectRoot,
|
|
61
112
|
config: resolved.config,
|
|
@@ -70,7 +121,8 @@ const verifyCommand = Command.make("verify", {
|
|
|
70
121
|
path: displayPath,
|
|
71
122
|
by: result.by,
|
|
72
123
|
at: result.at,
|
|
73
|
-
dryRun: result.dryRun
|
|
124
|
+
dryRun: result.dryRun,
|
|
125
|
+
...Option.isSome(distribution) ? { distribution: distribution.value } : {}
|
|
74
126
|
});
|
|
75
127
|
yield* Console.log(JSON.stringify(Schema.encodeSync(VerifyEnvelope)(envelope)));
|
|
76
128
|
} else for (const line of humanVerify({
|
|
@@ -86,9 +138,9 @@ const verifyCommand = Command.make("verify", {
|
|
|
86
138
|
explicitConfigPath: input.config,
|
|
87
139
|
discoveryCwd
|
|
88
140
|
}));
|
|
89
|
-
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION)))));
|
|
141
|
+
if (input.format === "json") return yield* body.pipe(Effect.tapError((error) => Console.log(JSON.stringify(jsonError(error, CLI_VERSION, Option.getOrUndefined(distribution))))));
|
|
90
142
|
return yield* body;
|
|
91
|
-
})).pipe(Command.withDescription("Record a human's attestation that a concept has been reviewed: append one verified entry, { by: human:<id>, at: <now> }, to its frontmatter. The actor is always your own git identity; there is no --by. This is a human-run command: no agent, hook, or MCP tool ever invokes it."));
|
|
143
|
+
})).pipe(Command.withDescription("Record a human's attestation that a concept has been reviewed: append one verified entry, { by: human:<id>, at: <now> }, to its frontmatter. The actor is always your own git identity; there is no --by. Give a concept id, or --all/--type <Type> (repeatable) to attest a batch of unverified concepts at once; exactly one of the two selections is required. This is a human-run command: no agent, hook, or MCP tool ever invokes it."));
|
|
92
144
|
|
|
93
145
|
//#endregion
|
|
94
146
|
export { verifyCommand };
|
package/errors.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConfigMalformedError, ConfigPathNotFoundError, InitOverwriteError, VerifyConceptNotFoundError, VerifyUnsupportedFrontmatterError } from "@okfit/engine";
|
|
1
|
+
import { ConfigMalformedError, ConfigPathNotFoundError, InitOverwriteError, SyncStagedLogError, VerifyConceptNotFoundError, VerifySelectionError, VerifyUnsupportedFrontmatterError } from "@okfit/engine";
|
|
2
2
|
import { ConfigIssueRenderer } from "@effected/cli";
|
|
3
3
|
|
|
4
4
|
//#region src/errors.ts
|
|
@@ -39,6 +39,12 @@ const relativeToCwd = (path, cwd) => {
|
|
|
39
39
|
* each render as their own `error: <message>` line; both messages
|
|
40
40
|
* already name the concept id and what to do about it, and neither
|
|
41
41
|
* carries a filesystem path needing K-51 relativisation.
|
|
42
|
+
* 5a. `SyncStagedLogError` (issue #140) renders the same way, one
|
|
43
|
+
* `error: <message>` line naming why `--staged` and `--only log` cannot
|
|
44
|
+
* combine.
|
|
45
|
+
* 5b. `VerifySelectionError` (issue #138) renders the same way, one
|
|
46
|
+
* `error: <message>` line naming why `okfit verify`'s selection was
|
|
47
|
+
* contradictory, empty, or named an undeclared type.
|
|
42
48
|
* 6. Everything else — core's `BundleRootNotFoundError`/`BundleReadError`/
|
|
43
49
|
* `BundleDepthExceededError`, config-file's other errors, `XdgEnvError`
|
|
44
50
|
* (the K-13 `HOME`-unset case) — renders as the single line
|
|
@@ -59,6 +65,8 @@ const renderFailure = (error) => {
|
|
|
59
65
|
];
|
|
60
66
|
if (error instanceof VerifyConceptNotFoundError) return [`error: ${error.message}`];
|
|
61
67
|
if (error instanceof VerifyUnsupportedFrontmatterError) return [`error: ${error.message}`];
|
|
68
|
+
if (error instanceof SyncStagedLogError) return [`error: ${error.message}`];
|
|
69
|
+
if (error instanceof VerifySelectionError) return [`error: ${error.message}`];
|
|
62
70
|
if (hasTag(error, "ConfigValidationError")) {
|
|
63
71
|
const validationError = error;
|
|
64
72
|
return [`error: ${String(validationError)}`, ...ConfigIssueRenderer.render(validationError).map((line) => ` ${line}`)];
|
package/index.d.ts
CHANGED
|
@@ -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("@effected/git").UnknownRefError | import("@okfit/engine").VerifyConceptNotFoundError | 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("@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>;
|
|
24
24
|
//#endregion
|
|
25
25
|
//#region src/errors.d.ts
|
|
26
26
|
/**
|
|
@@ -53,6 +53,12 @@ export declare const rootCommand: Command.Command<"okfit", {} | {}, {}, import("
|
|
|
53
53
|
* each render as their own `error: <message>` line; both messages
|
|
54
54
|
* already name the concept id and what to do about it, and neither
|
|
55
55
|
* carries a filesystem path needing K-51 relativisation.
|
|
56
|
+
* 5a. `SyncStagedLogError` (issue #140) renders the same way, one
|
|
57
|
+
* `error: <message>` line naming why `--staged` and `--only log` cannot
|
|
58
|
+
* combine.
|
|
59
|
+
* 5b. `VerifySelectionError` (issue #138) renders the same way, one
|
|
60
|
+
* `error: <message>` line naming why `okfit verify`'s selection was
|
|
61
|
+
* contradictory, empty, or named an undeclared type.
|
|
56
62
|
* 6. Everything else — core's `BundleRootNotFoundError`/`BundleReadError`/
|
|
57
63
|
* `BundleDepthExceededError`, config-file's other errors, `XdgEnvError`
|
|
58
64
|
* (the K-13 `HOME`-unset case) — renders as the single line
|
|
@@ -147,11 +153,14 @@ export declare const humanVerify: (input: VerifyLines) => ReadonlyArray<string>;
|
|
|
147
153
|
//#endregion
|
|
148
154
|
//#region src/version.d.ts
|
|
149
155
|
/**
|
|
150
|
-
* The version string reported by `okfit --version
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
156
|
+
* The version string reported by `okfit --version`, alongside `@okfit/engine`'s
|
|
157
|
+
* `ENGINE_VERSION` and `@okfit/core`'s `OKF_SPEC_VERSION` (and, when `main()`
|
|
158
|
+
* was given a distribution, `via <name> <version>`) --
|
|
159
|
+
* `internal/versionFormatter.ts` assembles the full line (okfit #137).
|
|
160
|
+
* `@savvy-web/bundler` replaces `process.env.__PACKAGE_VERSION__` with this
|
|
161
|
+
* package's own version at build time (K-32), so a release can never desync
|
|
162
|
+
* from the printed version. `"0.0.0"` is the unbuilt-source fallback and
|
|
163
|
+
* reads as dev mode.
|
|
155
164
|
*
|
|
156
165
|
* @public
|
|
157
166
|
*/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Context, Option } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/distribution.ts
|
|
4
|
+
/**
|
|
5
|
+
* The meta-package this run's bins were installed through (currently only
|
|
6
|
+
* `@okfit/plugin`), threaded from `main(options)` down to every command
|
|
7
|
+
* that builds a JSON envelope or the custom `--version` formatter (okfit
|
|
8
|
+
* #137). Defaults to `Option.none()` for a direct install of `@okfit/cli`;
|
|
9
|
+
* `main()` provides `Option.some(options.distribution)` exactly once, at
|
|
10
|
+
* the top of the command tree, when the caller supplied one.
|
|
11
|
+
*
|
|
12
|
+
* A `Context.Reference`, not a `Context.Service` (unlike `Now`): it carries
|
|
13
|
+
* its own default, so a command can read it with no explicit provision at
|
|
14
|
+
* all when no distribution was given.
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
const Distribution = Context.Reference("@okfit/cli/Distribution", { defaultValue: () => Option.none() });
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { Distribution };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useColor } from "./tty.js";
|
|
2
|
+
import { CliOutput } from "effect/unstable/cli";
|
|
3
|
+
import { ENGINE_VERSION } from "@okfit/engine";
|
|
4
|
+
import { CONFIG_SCHEMA_VERSION, OKF_SPEC_VERSION } from "@okfit/core";
|
|
5
|
+
|
|
6
|
+
//#region src/internal/versionFormatter.ts
|
|
7
|
+
/**
|
|
8
|
+
* `okfit --version`'s full text (okfit #137):
|
|
9
|
+
* `<name> <version>[ via <distName> <distVersion>] (engine <ENGINE_VERSION>, okf <OKF_SPEC_VERSION>, config-schema <CONFIG_SCHEMA_VERSION>)`.
|
|
10
|
+
* `GlobalFlag.Version`'s built-in `run` calls `formatter.formatVersion(command.name,
|
|
11
|
+
* version)` (`command.name` is always `"okfit"`, `version` is `CLI_VERSION` --
|
|
12
|
+
* `unstable/cli/GlobalFlag.ts:180-186`), so those two arguments alone are
|
|
13
|
+
* exactly `okfit <CLI_VERSION>`; this appends the engine, okf, and
|
|
14
|
+
* distribution parts.
|
|
15
|
+
*
|
|
16
|
+
* Only `formatVersion` is overridden -- every other `Formatter` method
|
|
17
|
+
* (help, error rendering) is `CliOutput.defaultFormatter`'s own, built with
|
|
18
|
+
* the SAME colour decision (`internal/tty.ts#useColor`, "exactly the
|
|
19
|
+
* framework's own colour rule") the rest of this package already uses, so
|
|
20
|
+
* help/error output is byte-identical to the built-in formatter.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
const versionFormatter = (distribution) => ({
|
|
25
|
+
...CliOutput.defaultFormatter({ colors: useColor() }),
|
|
26
|
+
formatVersion: (name, version) => {
|
|
27
|
+
return `${name} ${version}${distribution === void 0 ? "" : ` via ${distribution.name} ${distribution.version}`} (engine ${ENGINE_VERSION}, okf ${OKF_SPEC_VERSION}, config-schema ${CONFIG_SCHEMA_VERSION})`;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
export { versionFormatter };
|
package/main.d.ts
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
|
+
import { Distribution } from "@okfit/engine";
|
|
1
2
|
//#region src/main.d.ts
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* Options `@okfit/plugin`'s bin shims (and only they, today) pass to
|
|
5
|
+
* {@link main}. `distribution` names the meta-package the `okfit` bin was
|
|
6
|
+
* installed through; omitted (or `undefined`) for a direct install of
|
|
7
|
+
* `@okfit/cli` (okfit #137).
|
|
4
8
|
*
|
|
5
|
-
* @
|
|
9
|
+
* @public
|
|
6
10
|
*/
|
|
11
|
+
export interface MainOptions {
|
|
12
|
+
readonly distribution?: Distribution;
|
|
13
|
+
}
|
|
7
14
|
/**
|
|
8
15
|
* Run the okfit CLI. Owns the process: installs the runtime teardown and
|
|
9
16
|
* sets the exit code. `NodeRuntime.runMain` does not return a promise.
|
|
10
17
|
*
|
|
11
18
|
* @public
|
|
12
19
|
*/
|
|
13
|
-
export declare const main: () => void;
|
|
20
|
+
export declare const main: (options?: MainOptions) => void;
|
|
14
21
|
//#endregion
|
|
15
22
|
//# sourceMappingURL=main.d.ts.map
|
package/main.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { Distribution } from "./internal/distribution.js";
|
|
1
2
|
import { CLI_VERSION } from "./version.js";
|
|
2
3
|
import { rootCommand } from "./commands/root.js";
|
|
3
4
|
import { renderFailure } from "./errors.js";
|
|
4
|
-
import {
|
|
5
|
+
import { versionFormatter } from "./internal/versionFormatter.js";
|
|
6
|
+
import { CliOutput, Command } from "effect/unstable/cli";
|
|
5
7
|
import { Now, OkfitPlatform } from "@okfit/engine";
|
|
6
8
|
import { DateTime, Effect, Option } from "effect";
|
|
7
9
|
import { CliLogger, CliRuntime } from "@effected/cli";
|
|
@@ -29,11 +31,12 @@ const nowEffect = Option.fromNullishOr(process.env.OKFIT_NOW).pipe(Option.flatMa
|
|
|
29
31
|
*
|
|
30
32
|
* @public
|
|
31
33
|
*/
|
|
32
|
-
const main = () => {
|
|
34
|
+
const main = (options = {}) => {
|
|
35
|
+
const distribution = Option.fromNullishOr(options.distribution);
|
|
33
36
|
const program = Effect.gen(function* () {
|
|
34
37
|
const now = yield* nowEffect;
|
|
35
|
-
return yield* Command.run(rootCommand, { version: CLI_VERSION }).pipe(Effect.provideService(Now, now), Effect.catchTag("ShowHelp", (help) => Effect.fail(CliRuntime.reported(help, help.errors.length > 0 ? 64 : 0))));
|
|
36
|
-
}).pipe(Effect.provide(OkfitPlatform), CliRuntime.reportFailures({
|
|
38
|
+
return yield* Command.run(rootCommand, { version: CLI_VERSION }).pipe(Effect.provideService(Now, now), Effect.provideService(Distribution, distribution), Effect.catchTag("ShowHelp", (help) => Effect.fail(CliRuntime.reported(help, help.errors.length > 0 ? 64 : 0))));
|
|
39
|
+
}).pipe(Effect.provide(CliOutput.layer(versionFormatter(options.distribution))), Effect.provide(OkfitPlatform), CliRuntime.reportFailures({
|
|
37
40
|
exitCode: 3,
|
|
38
41
|
render: renderFailure
|
|
39
42
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okfit/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The okfit command line: validate, lint, index, and inspect Open Knowledge Format (OKF) bundles.",
|
|
6
6
|
"keywords": [
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"@effected/toml": "^0.7.0",
|
|
53
53
|
"@effected/walker": "^0.9.0",
|
|
54
54
|
"@effected/yaml": "^0.15.1",
|
|
55
|
-
"@okfit/core": "0.
|
|
56
|
-
"@okfit/engine": "0.
|
|
57
|
-
"@okfit/profiles": "0.7.
|
|
55
|
+
"@okfit/core": "0.7.0",
|
|
56
|
+
"@okfit/engine": "0.7.0",
|
|
57
|
+
"@okfit/profiles": "0.7.2",
|
|
58
58
|
"effect": "4.0.0-rc.115"
|
|
59
59
|
},
|
|
60
60
|
"engines": {
|
package/render/sync.js
CHANGED
|
@@ -4,7 +4,7 @@ const REASON_SENTENCE = {
|
|
|
4
4
|
untracked: "not tracked by git",
|
|
5
5
|
dirty: "has uncommitted changes",
|
|
6
6
|
unborn: "the repository has no commits yet",
|
|
7
|
-
"generated-missing": "has no generated block",
|
|
7
|
+
"generated-missing": "has no generated block and actors.agent is not configured; set generated.by by hand or configure actors.agent",
|
|
8
8
|
"generated-unsupported": "generated.at is a shape sync cannot edit; edit it by hand",
|
|
9
9
|
"log-unparseable": "log.md could not be parsed; see okfit validate"
|
|
10
10
|
};
|
package/render/verify.js
CHANGED
|
@@ -24,6 +24,21 @@ const humanVerify = (input) => [
|
|
|
24
24
|
input.dryRun ? `would verify ${input.id} by ${input.by} at ${input.at} (dry run, nothing written)` : `verified ${input.id} by ${input.by} at ${input.at}`,
|
|
25
25
|
...input.dryRun ? ["would write:", ...indentFragment(input.fragment)] : []
|
|
26
26
|
];
|
|
27
|
+
/**
|
|
28
|
+
* V-11-at-batch-scale (#138): one line per skipped candidate, then one line
|
|
29
|
+
* (or, under `--dry-run`, three) per verified concept, then a trailing tally.
|
|
30
|
+
*
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
const humanVerifyBatch = (input) => [
|
|
34
|
+
...input.skipped.map((entry) => entry.reason === "draft" ? `skipped ${entry.id}: draft` : `skipped ${entry.id}: already verified by ${input.by}`),
|
|
35
|
+
...input.verified.flatMap((entry) => input.dryRun ? [
|
|
36
|
+
`would verify ${entry.id} by ${input.by} at ${input.at}`,
|
|
37
|
+
"would write:",
|
|
38
|
+
...indentFragment(entry.fragment)
|
|
39
|
+
] : [`verified ${entry.id} by ${input.by} at ${input.at}`]),
|
|
40
|
+
input.dryRun ? `would verify ${input.verified.length}, skipped ${input.skipped.length} (dry run, nothing written)` : `verified ${input.verified.length}, skipped ${input.skipped.length}`
|
|
41
|
+
];
|
|
27
42
|
|
|
28
43
|
//#endregion
|
|
29
|
-
export { humanVerify };
|
|
44
|
+
export { humanVerify, humanVerifyBatch };
|
package/version.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
//#region src/version.ts
|
|
2
2
|
/**
|
|
3
|
-
* The version string reported by `okfit --version
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* The version string reported by `okfit --version`, alongside `@okfit/engine`'s
|
|
4
|
+
* `ENGINE_VERSION` and `@okfit/core`'s `OKF_SPEC_VERSION` (and, when `main()`
|
|
5
|
+
* was given a distribution, `via <name> <version>`) --
|
|
6
|
+
* `internal/versionFormatter.ts` assembles the full line (okfit #137).
|
|
7
|
+
* `@savvy-web/bundler` replaces `process.env.__PACKAGE_VERSION__` with this
|
|
8
|
+
* package's own version at build time (K-32), so a release can never desync
|
|
9
|
+
* from the printed version. `"0.0.0"` is the unbuilt-source fallback and
|
|
10
|
+
* reads as dev mode.
|
|
8
11
|
*
|
|
9
12
|
* @public
|
|
10
13
|
*/
|
|
11
|
-
const CLI_VERSION = "0.
|
|
14
|
+
const CLI_VERSION = "0.6.0";
|
|
12
15
|
|
|
13
16
|
//#endregion
|
|
14
17
|
export { CLI_VERSION };
|