@okfit/engine 0.2.1 → 0.4.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/graph/run.js ADDED
@@ -0,0 +1,20 @@
1
+ import { Bundle, Graph } from "@okfit/core";
2
+ import { Effect } from "effect";
3
+
4
+ //#region src/graph/run.ts
5
+ /**
6
+ * Load the bundle and build its link graph (D-6, D-26). Pure over the
7
+ * loaded bundle once loading succeeds; `okfit graph` always exits `0`.
8
+ *
9
+ * @public
10
+ */
11
+ const runGraph = (options) => Effect.gen(function* () {
12
+ const bundle = yield* Bundle.load({ root: options.root });
13
+ return {
14
+ bundle,
15
+ graph: Graph.fromBundle(bundle)
16
+ };
17
+ });
18
+
19
+ //#endregion
20
+ export { runGraph };
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BundleLoadError, ConceptId, Diagnostic, DiagnosticRange, DiagnosticSeverity, LoadedBundle, OkfitConfig, OkfitConfigFile, ValidationReport } from "@okfit/core";
1
+ import { BundleLoadError, ConceptId, Diagnostic, DiagnosticRange, DiagnosticSeverity, GraphLink, GraphNode, LinkGraph, LoadedBundle, OkfitConfig, OkfitConfigFile, StaleConcept, ValidationReport } from "@okfit/core";
2
2
  import { Context, Crypto, DateTime, Effect, FileSystem, Layer, Option, Path, PlatformError, Runtime, Schema } from "effect";
3
3
  import { AppDirs, Xdg } from "@effected/xdg";
4
4
  import { ConfigReadError } from "@effected/config-file";
@@ -331,6 +331,25 @@ interface ContextResult {
331
331
  */
332
332
  export declare const runContext: (options: ContextRunOptions) => Effect.Effect<ContextResult, never, FileSystem.FileSystem | Path.Path>;
333
333
  //#endregion
334
+ //#region src/graph/run.d.ts
335
+ /** @public */
336
+ interface GraphRunOptions {
337
+ /** Absolute bundle root; `Bundle.load` never reads cwd (D-8). */
338
+ readonly root: string;
339
+ }
340
+ /** @public */
341
+ interface GraphRunResult {
342
+ readonly bundle: LoadedBundle;
343
+ readonly graph: LinkGraph;
344
+ }
345
+ /**
346
+ * Load the bundle and build its link graph (D-6, D-26). Pure over the
347
+ * loaded bundle once loading succeeds; `okfit graph` always exits `0`.
348
+ *
349
+ * @public
350
+ */
351
+ export declare const runGraph: (options: GraphRunOptions) => Effect.Effect<GraphRunResult, BundleLoadError, FileSystem.FileSystem | Path.Path>;
352
+ //#endregion
334
353
  //#region src/init/scaffold.d.ts
335
354
  /** `.config/okfit.toml`, relative to the project root (C-10). @public */
336
355
  export declare const CONFIG_RELATIVE_PATH = ".config/okfit.toml";
@@ -418,11 +437,55 @@ export declare const files: (options: ScaffoldOptions) => Effect.Effect<Readonly
418
437
  export declare const OkfitPlatform: Layer.Layer<AppDirs | Xdg | NodeServices.NodeServices, import("@effected/xdg").XdgEnvError, never>;
419
438
  //#endregion
420
439
  //#region src/render/context.d.ts
421
- /** One `types[]` entry (M-15). @public */
440
+ /** One enumerated value of a `fields[]` entry. @public */
441
+ export declare const ContextFieldValue: Schema.Struct<{
442
+ readonly name: Schema.String;
443
+ readonly description: Schema.String;
444
+ }>;
445
+ /** @public */
446
+ export type ContextFieldValue = typeof ContextFieldValue.Type;
447
+ /**
448
+ * One `types[].fields[]` entry: a `[types.<Name>.fields.<key>]` declaration
449
+ * flattened for a consumer. `values` keeps the declaration's own order (it
450
+ * is a vocabulary, and the config author chose the order); `kind` is
451
+ * `"path"` or `null`.
452
+ *
453
+ * @public
454
+ */
455
+ export declare const ContextField: Schema.Struct<{
456
+ readonly name: Schema.String;
457
+ readonly description: Schema.String;
458
+ readonly kind: Schema.NullOr<Schema.Literal<"path">>;
459
+ readonly values: Schema.NullOr<Schema.$Array<Schema.Struct<{
460
+ readonly name: Schema.String;
461
+ readonly description: Schema.String;
462
+ }>>>;
463
+ }>;
464
+ /** @public */
465
+ export type ContextField = typeof ContextField.Type;
466
+ /**
467
+ * One `types[]` entry (M-15). `required`, `require_verified` and `fields`
468
+ * carry the constraints `validate` enforces (issue #33): without them an
469
+ * agent learns that `Interface` exists but not that it requires `kind`, nor
470
+ * which values `kind` accepts, until a lint error says so.
471
+ *
472
+ * @public
473
+ */
422
474
  export declare const ContextType: Schema.Struct<{
423
475
  readonly name: Schema.String;
424
476
  readonly description: Schema.NullOr<Schema.String>;
425
477
  readonly guidance: Schema.NullOr<Schema.String>;
478
+ readonly required: Schema.NullOr<Schema.$Array<Schema.String>>;
479
+ readonly require_verified: Schema.NullOr<Schema.Boolean>;
480
+ readonly fields: Schema.$Array<Schema.Struct<{
481
+ readonly name: Schema.String;
482
+ readonly description: Schema.String;
483
+ readonly kind: Schema.NullOr<Schema.Literal<"path">>;
484
+ readonly values: Schema.NullOr<Schema.$Array<Schema.Struct<{
485
+ readonly name: Schema.String;
486
+ readonly description: Schema.String;
487
+ }>>>;
488
+ }>>;
426
489
  }>;
427
490
  /** @public */
428
491
  export type ContextType = typeof ContextType.Type;
@@ -474,6 +537,17 @@ export declare const ContextEnvelope: Schema.Struct<{
474
537
  readonly name: Schema.String;
475
538
  readonly description: Schema.NullOr<Schema.String>;
476
539
  readonly guidance: Schema.NullOr<Schema.String>;
540
+ readonly required: Schema.NullOr<Schema.$Array<Schema.String>>;
541
+ readonly require_verified: Schema.NullOr<Schema.Boolean>;
542
+ readonly fields: Schema.$Array<Schema.Struct<{
543
+ readonly name: Schema.String;
544
+ readonly description: Schema.String;
545
+ readonly kind: Schema.NullOr<Schema.Literal<"path">>;
546
+ readonly values: Schema.NullOr<Schema.$Array<Schema.Struct<{
547
+ readonly name: Schema.String;
548
+ readonly description: Schema.String;
549
+ }>>>;
550
+ }>>;
477
551
  }>>;
478
552
  readonly tags: Schema.$Array<Schema.Struct<{
479
553
  readonly name: Schema.String;
@@ -571,6 +645,79 @@ export declare const tally: (diagnostics: ReadonlyArray<RenderedDiagnostic>) =>
571
645
  */
572
646
  export declare const forDiagnostics: (diagnostics: ReadonlyArray<RenderedDiagnostic>) => 0 | 1 | 2;
573
647
  //#endregion
648
+ //#region src/render/graph.d.ts
649
+ /** One entry of the `nodes` array. @public */
650
+ export declare const GraphNodeEnvelope: Schema.Struct<{
651
+ readonly id: Schema.String;
652
+ readonly kind: Schema.Literals<readonly ["concept", "file", "missing"]>;
653
+ }>;
654
+ /** @public */
655
+ export type GraphNodeEnvelope = typeof GraphNodeEnvelope.Type;
656
+ /** One entry of the `edges` array; `field` is omitted, never `null` (exactOptionalPropertyTypes). @public */
657
+ export declare const GraphEdgeEnvelope: Schema.Struct<{
658
+ readonly from: Schema.String;
659
+ readonly to: Schema.String;
660
+ readonly source: Schema.Literals<readonly ["body", "frontmatter"]>;
661
+ readonly field: Schema.optionalKey<Schema.String>;
662
+ readonly raw: Schema.String;
663
+ }>;
664
+ /** @public */
665
+ export type GraphEdgeEnvelope = typeof GraphEdgeEnvelope.Type;
666
+ /** @public */
667
+ export declare const GraphSummary: Schema.Struct<{
668
+ readonly nodes: Schema.Number;
669
+ readonly edges: Schema.Number;
670
+ }>;
671
+ /** @public */
672
+ export type GraphSummary = typeof GraphSummary.Type;
673
+ /**
674
+ * `okfit graph --format json`'s envelope, schema 1, snake_case — the same
675
+ * convention as `render/json.ts`'s `JsonEnvelope`. Built from
676
+ * `LinkGraph.nodes`/`LinkGraph.edges` (D-26).
677
+ *
678
+ * @public
679
+ */
680
+ export declare const GraphEnvelope: Schema.Struct<{
681
+ readonly schema: Schema.Literal<1>;
682
+ readonly okfit_version: Schema.String;
683
+ readonly producer: Schema.String;
684
+ readonly okf_version: Schema.String;
685
+ readonly root: Schema.String;
686
+ readonly profile: Schema.NullOr<Schema.String>;
687
+ readonly summary: Schema.Struct<{
688
+ readonly nodes: Schema.Number;
689
+ readonly edges: Schema.Number;
690
+ }>;
691
+ readonly nodes: Schema.$Array<Schema.Struct<{
692
+ readonly id: Schema.String;
693
+ readonly kind: Schema.Literals<readonly ["concept", "file", "missing"]>;
694
+ }>>;
695
+ readonly edges: Schema.$Array<Schema.Struct<{
696
+ readonly from: Schema.String;
697
+ readonly to: Schema.String;
698
+ readonly source: Schema.Literals<readonly ["body", "frontmatter"]>;
699
+ readonly field: Schema.optionalKey<Schema.String>;
700
+ readonly raw: Schema.String;
701
+ }>>;
702
+ }>;
703
+ /** @public */
704
+ export type GraphEnvelope = typeof GraphEnvelope.Type;
705
+ /**
706
+ * Build the envelope from a loaded `LinkGraph`'s own `nodes`/`edges`
707
+ * arrays.
708
+ *
709
+ * @public
710
+ */
711
+ export declare const graphEnvelope: (input: {
712
+ readonly okfitVersion: string;
713
+ readonly producer: string;
714
+ readonly okfVersion: string;
715
+ readonly root: string;
716
+ readonly profile: string | null;
717
+ readonly nodes: ReadonlyArray<GraphNode>;
718
+ readonly edges: ReadonlyArray<GraphLink>;
719
+ }) => GraphEnvelope;
720
+ //#endregion
574
721
  //#region src/render/json.d.ts
575
722
  /** One entry of the `diagnostics` array (K-21). @public */
576
723
  export declare const JsonDiagnostic: Schema.Struct<{
@@ -597,13 +744,18 @@ export type JsonSummary = typeof JsonSummary.Type;
597
744
  /**
598
745
  * K-21's success envelope, snake_case. `exit_code` is `0 | 1 | 2` only: an
599
746
  * infrastructure failure never produces this envelope, it produces the
600
- * `JsonErrorEnvelope` (K-22).
747
+ * `JsonErrorEnvelope` (K-22). `okfit_version` names the version of the
748
+ * package that produced the report (J-2), so the CLI and the MCP server
749
+ * legitimately report different numbers over one bundle; `producer` names
750
+ * that package (`okfit` or `@okfit/mcp`) so the difference reads as two
751
+ * producers, not as drift (okfit #75).
601
752
  *
602
753
  * @public
603
754
  */
604
755
  export declare const JsonEnvelope: Schema.Struct<{
605
756
  readonly schema: Schema.Literal<1>;
606
757
  readonly okfit_version: Schema.String;
758
+ readonly producer: Schema.String;
607
759
  readonly okf_version: Schema.String;
608
760
  readonly root: Schema.String;
609
761
  readonly profile: Schema.NullOr<Schema.String>;
@@ -652,6 +804,7 @@ export type JsonErrorEnvelope = typeof JsonErrorEnvelope.Type;
652
804
  */
653
805
  export declare const json: (input: {
654
806
  readonly okfitVersion: string;
807
+ readonly producer: string;
655
808
  readonly okfVersion: string;
656
809
  readonly root: string;
657
810
  readonly profile: string | null;
@@ -662,6 +815,69 @@ export declare const json: (input: {
662
815
  /** K-22. `tag` is the error's `_tag` when it has one, else its constructor name. @public */
663
816
  export declare const jsonError: (error: unknown, okfitVersion: string) => JsonErrorEnvelope;
664
817
  //#endregion
818
+ //#region src/render/stale.d.ts
819
+ /** One entry of the `items` array. @public */
820
+ export declare const StaleItem: Schema.Struct<{
821
+ readonly id: Schema.String;
822
+ readonly stale_after: Schema.String;
823
+ readonly days_past: Schema.Number;
824
+ }>;
825
+ /** @public */
826
+ export type StaleItem = typeof StaleItem.Type;
827
+ /** @public */
828
+ export declare const StaleSummary: Schema.Struct<{
829
+ readonly concepts: Schema.Number;
830
+ readonly stale: Schema.Number;
831
+ }>;
832
+ /** @public */
833
+ export type StaleSummary = typeof StaleSummary.Type;
834
+ /**
835
+ * `okfit stale --format json`'s envelope, schema 1, snake_case — the same
836
+ * convention as `render/json.ts`'s `JsonEnvelope`. `as_of` and each item's
837
+ * `stale_after` are `DateTime.formatIso` strings (mirrors
838
+ * `packages/mcp/src/tools/staleReport.ts`). `stale` is always `0` (the
839
+ * report never fails a run).
840
+ *
841
+ * @public
842
+ */
843
+ export declare const StaleEnvelope: Schema.Struct<{
844
+ readonly schema: Schema.Literal<1>;
845
+ readonly okfit_version: Schema.String;
846
+ readonly producer: Schema.String;
847
+ readonly okf_version: Schema.String;
848
+ readonly root: Schema.String;
849
+ readonly profile: Schema.NullOr<Schema.String>;
850
+ readonly as_of: Schema.String;
851
+ readonly summary: Schema.Struct<{
852
+ readonly concepts: Schema.Number;
853
+ readonly stale: Schema.Number;
854
+ }>;
855
+ readonly items: Schema.$Array<Schema.Struct<{
856
+ readonly id: Schema.String;
857
+ readonly stale_after: Schema.String;
858
+ readonly days_past: Schema.Number;
859
+ }>>;
860
+ }>;
861
+ /** @public */
862
+ export type StaleEnvelope = typeof StaleEnvelope.Type;
863
+ /**
864
+ * Build the envelope. `items` is `Derive.staleReport`'s output verbatim
865
+ * (already sorted by id); this just reshapes it to snake_case and formats
866
+ * the two `DateTime.Utc` fields.
867
+ *
868
+ * @public
869
+ */
870
+ export declare const staleEnvelope: (input: {
871
+ readonly okfitVersion: string;
872
+ readonly producer: string;
873
+ readonly okfVersion: string;
874
+ readonly root: string;
875
+ readonly profile: string | null;
876
+ readonly now: DateTime.Utc;
877
+ readonly concepts: number;
878
+ readonly items: ReadonlyArray<StaleConcept>;
879
+ }) => StaleEnvelope;
880
+ //#endregion
665
881
  //#region src/sync/run.d.ts
666
882
  /** The three families `okfit sync` regenerates (contract §5). @public */
667
883
  type SyncMode = "generated" | "index" | "log";
@@ -831,6 +1047,30 @@ export declare const verifyEnvelope: (input: {
831
1047
  readonly dryRun: boolean;
832
1048
  }) => VerifyEnvelope;
833
1049
  //#endregion
1050
+ //#region src/stale/run.d.ts
1051
+ /** @public */
1052
+ interface StaleRunOptions {
1053
+ /** Absolute bundle root; `Bundle.load` never reads cwd (D-8). */
1054
+ readonly root: string;
1055
+ /** `OKFIT_NOW` or `DateTime.now`, from the caller's own `Now` service (K-47). */
1056
+ readonly now: DateTime.Utc;
1057
+ }
1058
+ /** @public */
1059
+ interface StaleRunResult {
1060
+ readonly bundle: LoadedBundle;
1061
+ /** `Derive.staleReport`'s output verbatim: every stale concept, sorted by id. */
1062
+ readonly items: ReadonlyArray<StaleConcept>;
1063
+ }
1064
+ /**
1065
+ * Load the bundle and derive the stale report against `now` (D-10, D-36:
1066
+ * `Derive.staleReport` is pure and takes `now` as an argument, never a
1067
+ * clock). This is a report, not a check — `okfit stale` always exits `0`;
1068
+ * the `stale` lint rule is where staleness fails a run.
1069
+ *
1070
+ * @public
1071
+ */
1072
+ export declare const runStale: (options: StaleRunOptions) => Effect.Effect<StaleRunResult, BundleLoadError, FileSystem.FileSystem | Path.Path>;
1073
+ //#endregion
834
1074
  //#region src/validate/run.d.ts
835
1075
  declare const Now_base: Context.ServiceClass<Now, "@okfit/engine/Now", DateTime.Utc>;
836
1076
  /**
@@ -869,8 +1109,8 @@ interface RunOptions {
869
1109
  * default severity. Tier 1 (the pure, in-memory body-digest comparison)
870
1110
  * still runs regardless — that is exactly the signal edit time wants,
871
1111
  * at the cost of one sha256 over text already in memory. Defaults to
872
- * `false`; `run` still gates the whole lint call on `severity ===
873
- * "off"`, but no longer on this flag.
1112
+ * `false`; `run` still gates the whole lint call on
1113
+ * `severity === "off"`, but no longer on this flag.
874
1114
  */
875
1115
  readonly skipProvenance?: boolean;
876
1116
  }
@@ -961,5 +1201,5 @@ export declare const runVerify: (options: VerifyOptions) => Effect.Effect<{
961
1201
  fragment: string;
962
1202
  }, import("effect/PlatformError").PlatformError | VerifyConceptNotFoundError | VerifyUnsupportedFrontmatterError | import("@effected/yaml").YamlParseError | import("@okfit/core").BundleLoadError | import("@okfit/profiles").GeneratedByError, FileSystem.FileSystem | import("@effected/git").Git | Path.Path>;
963
1203
  //#endregion
964
- export type { ContextResult, ContextRunOptions, DiagnosticSource, DiscoveredConfig, RenderedDiagnostic, ResolveProjectConfigInput, ResolvedProjectConfig, RunOptions, RunResult, ScaffoldFile, ScaffoldOptions, SyncMode, SyncModeResult, SyncOptions, SyncResult, Tally, VerifyOptions, VerifyResult };
1204
+ export type { ContextResult, ContextRunOptions, DiagnosticSource, DiscoveredConfig, GraphRunOptions, GraphRunResult, RenderedDiagnostic, ResolveProjectConfigInput, ResolvedProjectConfig, RunOptions, RunResult, ScaffoldFile, ScaffoldOptions, StaleRunOptions, StaleRunResult, SyncMode, SyncModeResult, SyncOptions, SyncResult, Tally, VerifyOptions, VerifyResult };
965
1205
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -3,16 +3,20 @@ import { ConfigMalformedError, ConfigPathNotFoundError, InitOverwriteError, Veri
3
3
  import { buildConfigLayer, provideConfig } from "./config/layer.js";
4
4
  import { DEFAULT_PROFILE_NAME, resolveProjectConfig } from "./config/resolve.js";
5
5
  import { runContext } from "./context/run.js";
6
+ import { runGraph } from "./graph/run.js";
6
7
  import { CONFIG_RELATIVE_PATH, SCHEMA_DIRECTIVE, configValue, files, targetPaths } from "./init/scaffold.js";
7
8
  import { OkfitPlatform } from "./platform.js";
8
- import { ContextEnvelope, ContextTag, ContextType, contextEnvelope } from "./render/context.js";
9
+ import { ContextEnvelope, ContextField, ContextFieldValue, ContextTag, ContextType, contextEnvelope } from "./render/context.js";
9
10
  import { forDiagnostics, tally } from "./render/exit.js";
11
+ import { GraphEdgeEnvelope, GraphEnvelope, GraphNodeEnvelope, GraphSummary, graphEnvelope } from "./render/graph.js";
10
12
  import { collect, sort } from "./render/sort.js";
11
13
  import { JsonDiagnostic, JsonEnvelope, JsonErrorEnvelope, JsonSummary, json, jsonError } from "./render/json.js";
14
+ import { StaleEnvelope, StaleItem, StaleSummary, staleEnvelope } from "./render/stale.js";
12
15
  import { SkipReason, runSync } from "./sync/run.js";
13
16
  import { SyncEnvelope, SyncModeEnvelope, syncEnvelope } from "./render/sync.js";
14
17
  import { VerifyEnvelope, verifyEnvelope } from "./render/verify.js";
18
+ import { runStale } from "./stale/run.js";
15
19
  import { Now, run } from "./validate/run.js";
16
20
  import { runVerify } from "./verify/run.js";
17
21
 
18
- export { CONFIG_RELATIVE_PATH, ConfigMalformedError, ConfigPathNotFoundError, ContextEnvelope, ContextTag, ContextType, DEFAULT_PROFILE_NAME, InitOverwriteError, JsonDiagnostic, JsonEnvelope, JsonErrorEnvelope, JsonSummary, Now, OkfitPlatform, SCHEMA_DIRECTIVE, SkipReason, SyncEnvelope, SyncModeEnvelope, VerifyConceptNotFoundError, VerifyEnvelope, VerifyUnsupportedFrontmatterError, buildConfigLayer, collect, configValue, contextEnvelope, files, forDiagnostics, json, jsonError, provideConfig, resolveBundleRoot, resolveProjectConfig, resolveProjectRoot, run, runContext, runSync, runVerify, sort, syncEnvelope, tally, targetPaths, verifyEnvelope };
22
+ export { CONFIG_RELATIVE_PATH, ConfigMalformedError, ConfigPathNotFoundError, ContextEnvelope, ContextField, ContextFieldValue, ContextTag, ContextType, DEFAULT_PROFILE_NAME, GraphEdgeEnvelope, GraphEnvelope, GraphNodeEnvelope, GraphSummary, InitOverwriteError, JsonDiagnostic, JsonEnvelope, JsonErrorEnvelope, JsonSummary, Now, OkfitPlatform, SCHEMA_DIRECTIVE, SkipReason, StaleEnvelope, StaleItem, StaleSummary, SyncEnvelope, SyncModeEnvelope, VerifyConceptNotFoundError, VerifyEnvelope, VerifyUnsupportedFrontmatterError, buildConfigLayer, collect, configValue, contextEnvelope, files, forDiagnostics, graphEnvelope, json, jsonError, provideConfig, resolveBundleRoot, resolveProjectConfig, resolveProjectRoot, run, runContext, runGraph, runStale, runSync, runVerify, sort, staleEnvelope, syncEnvelope, tally, targetPaths, verifyEnvelope };
package/init/scaffold.js CHANGED
@@ -145,10 +145,10 @@ const files = (options) => Effect.gen(function* () {
145
145
  okfVersion: OKF_SPEC_VERSION,
146
146
  subdirectories
147
147
  });
148
- const logText = Derive.renderLogEntry({
148
+ const logText = Derive.renderLog([{
149
149
  date: today,
150
150
  items: [`Initialized the bundle with the ${profileName} profile`]
151
- });
151
+ }]);
152
152
  const directoryFiles = layout.directories.map((directory) => ({
153
153
  path: `${bundleRoot}/${directory.directory}/${layout.root.index}`,
154
154
  contents: `# ${capitalize(directory.directory)}\n`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okfit/engine",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "The okfit engine: platform layer, config discovery, and the validate, verify, sync, init and context programs shared by the okfit CLI and MCP server.",
6
6
  "keywords": [
@@ -35,19 +35,19 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@effect/platform-node": "4.0.0-rc.115",
38
- "@effected/app": "^0.16.0",
39
- "@effected/config-file": "^0.8.0",
40
- "@effected/git": "^0.15.0",
38
+ "@effected/app": "^0.16.1",
39
+ "@effected/config-file": "^0.9.0",
40
+ "@effected/git": "^0.15.1",
41
41
  "@effected/glob": "^0.6.0",
42
42
  "@effected/jsonc": "^0.11.0",
43
43
  "@effected/markdown": "^0.10.0",
44
44
  "@effected/store": "^0.8.0",
45
45
  "@effected/toml": "^0.7.0",
46
- "@effected/walker": "^0.8.0",
47
- "@effected/xdg": "^0.5.0",
48
- "@effected/yaml": "^0.15.0",
49
- "@okfit/core": "0.3.1",
50
- "@okfit/profiles": "0.3.1",
46
+ "@effected/walker": "^0.9.0",
47
+ "@effected/xdg": "^0.5.1",
48
+ "@effected/yaml": "^0.15.1",
49
+ "@okfit/core": "0.4.1",
50
+ "@okfit/profiles": "0.5.0",
51
51
  "effect": "4.0.0-rc.115"
52
52
  },
53
53
  "engines": {
package/render/context.js CHANGED
@@ -1,11 +1,40 @@
1
1
  import { Schema } from "effect";
2
2
 
3
3
  //#region src/render/context.ts
4
- /** One `types[]` entry (M-15). @public */
4
+ /** One enumerated value of a `fields[]` entry. @public */
5
+ const ContextFieldValue = Schema.Struct({
6
+ name: Schema.String,
7
+ description: Schema.String
8
+ });
9
+ /**
10
+ * One `types[].fields[]` entry: a `[types.<Name>.fields.<key>]` declaration
11
+ * flattened for a consumer. `values` keeps the declaration's own order (it
12
+ * is a vocabulary, and the config author chose the order); `kind` is
13
+ * `"path"` or `null`.
14
+ *
15
+ * @public
16
+ */
17
+ const ContextField = Schema.Struct({
18
+ name: Schema.String,
19
+ description: Schema.String,
20
+ kind: Schema.NullOr(Schema.Literal("path")),
21
+ values: Schema.NullOr(Schema.Array(ContextFieldValue))
22
+ });
23
+ /**
24
+ * One `types[]` entry (M-15). `required`, `require_verified` and `fields`
25
+ * carry the constraints `validate` enforces (issue #33): without them an
26
+ * agent learns that `Interface` exists but not that it requires `kind`, nor
27
+ * which values `kind` accepts, until a lint error says so.
28
+ *
29
+ * @public
30
+ */
5
31
  const ContextType = Schema.Struct({
6
32
  name: Schema.String,
7
33
  description: Schema.NullOr(Schema.String),
8
- guidance: Schema.NullOr(Schema.String)
34
+ guidance: Schema.NullOr(Schema.String),
35
+ required: Schema.NullOr(Schema.Array(Schema.String)),
36
+ require_verified: Schema.NullOr(Schema.Boolean),
37
+ fields: Schema.Array(ContextField)
9
38
  });
10
39
  /** One `tags[]` entry (M-15). @public */
11
40
  const ContextTag = Schema.Struct({
@@ -71,7 +100,18 @@ const contextEnvelope = (input) => ({
71
100
  types: Object.entries(input.config.types ?? {}).map(([name, decl]) => ({
72
101
  name,
73
102
  description: decl.description ?? null,
74
- guidance: decl.guidance ?? null
103
+ guidance: decl.guidance ?? null,
104
+ required: decl.required ?? null,
105
+ require_verified: decl.require_verified ?? null,
106
+ fields: Object.entries(decl.fields ?? {}).map(([fieldName, field]) => ({
107
+ name: fieldName,
108
+ description: field.description,
109
+ kind: field.kind ?? null,
110
+ values: field.values === void 0 ? null : Object.entries(field.values).map(([valueName, description]) => ({
111
+ name: valueName,
112
+ description
113
+ }))
114
+ })).toSorted((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)
75
115
  })).toSorted((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0),
76
116
  tags: Object.entries(input.config.tags ?? {}).map(([name, decl]) => ({
77
117
  name,
@@ -80,4 +120,4 @@ const contextEnvelope = (input) => ({
80
120
  });
81
121
 
82
122
  //#endregion
83
- export { ContextEnvelope, ContextTag, ContextType, contextEnvelope };
123
+ export { ContextEnvelope, ContextField, ContextFieldValue, ContextTag, ContextType, contextEnvelope };
@@ -0,0 +1,77 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/render/graph.ts
4
+ /** One entry of the `nodes` array. @public */
5
+ const GraphNodeEnvelope = Schema.Struct({
6
+ id: Schema.String,
7
+ kind: Schema.Literals([
8
+ "concept",
9
+ "file",
10
+ "missing"
11
+ ])
12
+ });
13
+ /** One entry of the `edges` array; `field` is omitted, never `null` (exactOptionalPropertyTypes). @public */
14
+ const GraphEdgeEnvelope = Schema.Struct({
15
+ from: Schema.String,
16
+ to: Schema.String,
17
+ source: Schema.Literals(["body", "frontmatter"]),
18
+ field: Schema.optionalKey(Schema.String),
19
+ raw: Schema.String
20
+ });
21
+ /** @public */
22
+ const GraphSummary = Schema.Struct({
23
+ nodes: Schema.Number,
24
+ edges: Schema.Number
25
+ });
26
+ /**
27
+ * `okfit graph --format json`'s envelope, schema 1, snake_case — the same
28
+ * convention as `render/json.ts`'s `JsonEnvelope`. Built from
29
+ * `LinkGraph.nodes`/`LinkGraph.edges` (D-26).
30
+ *
31
+ * @public
32
+ */
33
+ const GraphEnvelope = Schema.Struct({
34
+ schema: Schema.Literal(1),
35
+ okfit_version: Schema.String,
36
+ producer: Schema.String,
37
+ okf_version: Schema.String,
38
+ root: Schema.String,
39
+ profile: Schema.NullOr(Schema.String),
40
+ summary: GraphSummary,
41
+ nodes: Schema.Array(GraphNodeEnvelope),
42
+ edges: Schema.Array(GraphEdgeEnvelope)
43
+ });
44
+ const toNode = (node) => ({
45
+ id: node.id,
46
+ kind: node.kind
47
+ });
48
+ const toEdge = (edge) => ({
49
+ from: edge.from,
50
+ to: edge.to,
51
+ source: edge.data.source,
52
+ ...edge.data.field === void 0 ? {} : { field: edge.data.field },
53
+ raw: edge.data.raw
54
+ });
55
+ /**
56
+ * Build the envelope from a loaded `LinkGraph`'s own `nodes`/`edges`
57
+ * arrays.
58
+ *
59
+ * @public
60
+ */
61
+ const graphEnvelope = (input) => ({
62
+ schema: 1,
63
+ okfit_version: input.okfitVersion,
64
+ producer: input.producer,
65
+ okf_version: input.okfVersion,
66
+ root: input.root,
67
+ profile: input.profile,
68
+ summary: {
69
+ nodes: input.nodes.length,
70
+ edges: input.edges.length
71
+ },
72
+ nodes: input.nodes.map(toNode),
73
+ edges: input.edges.map(toEdge)
74
+ });
75
+
76
+ //#endregion
77
+ export { GraphEdgeEnvelope, GraphEnvelope, GraphNodeEnvelope, GraphSummary, graphEnvelope };
package/render/json.js CHANGED
@@ -29,13 +29,18 @@ const JsonSummary = Schema.Struct({
29
29
  /**
30
30
  * K-21's success envelope, snake_case. `exit_code` is `0 | 1 | 2` only: an
31
31
  * infrastructure failure never produces this envelope, it produces the
32
- * `JsonErrorEnvelope` (K-22).
32
+ * `JsonErrorEnvelope` (K-22). `okfit_version` names the version of the
33
+ * package that produced the report (J-2), so the CLI and the MCP server
34
+ * legitimately report different numbers over one bundle; `producer` names
35
+ * that package (`okfit` or `@okfit/mcp`) so the difference reads as two
36
+ * producers, not as drift (okfit #75).
33
37
  *
34
38
  * @public
35
39
  */
36
40
  const JsonEnvelope = Schema.Struct({
37
41
  schema: Schema.Literal(1),
38
42
  okfit_version: Schema.String,
43
+ producer: Schema.String,
39
44
  okf_version: Schema.String,
40
45
  root: Schema.String,
41
46
  profile: Schema.NullOr(Schema.String),
@@ -81,6 +86,7 @@ const json = (input) => {
81
86
  return {
82
87
  schema: 1,
83
88
  okfit_version: input.okfitVersion,
89
+ producer: input.producer,
84
90
  okf_version: input.okfVersion,
85
91
  root: input.root,
86
92
  profile: input.profile,
@@ -0,0 +1,62 @@
1
+ import { DateTime, Schema } from "effect";
2
+
3
+ //#region src/render/stale.ts
4
+ /** One entry of the `items` array. @public */
5
+ const StaleItem = Schema.Struct({
6
+ id: Schema.String,
7
+ stale_after: Schema.String,
8
+ days_past: Schema.Number
9
+ });
10
+ /** @public */
11
+ const StaleSummary = Schema.Struct({
12
+ concepts: Schema.Number,
13
+ stale: Schema.Number
14
+ });
15
+ /**
16
+ * `okfit stale --format json`'s envelope, schema 1, snake_case — the same
17
+ * convention as `render/json.ts`'s `JsonEnvelope`. `as_of` and each item's
18
+ * `stale_after` are `DateTime.formatIso` strings (mirrors
19
+ * `packages/mcp/src/tools/staleReport.ts`). `stale` is always `0` (the
20
+ * report never fails a run).
21
+ *
22
+ * @public
23
+ */
24
+ const StaleEnvelope = Schema.Struct({
25
+ schema: Schema.Literal(1),
26
+ okfit_version: Schema.String,
27
+ producer: Schema.String,
28
+ okf_version: Schema.String,
29
+ root: Schema.String,
30
+ profile: Schema.NullOr(Schema.String),
31
+ as_of: Schema.String,
32
+ summary: StaleSummary,
33
+ items: Schema.Array(StaleItem)
34
+ });
35
+ /**
36
+ * Build the envelope. `items` is `Derive.staleReport`'s output verbatim
37
+ * (already sorted by id); this just reshapes it to snake_case and formats
38
+ * the two `DateTime.Utc` fields.
39
+ *
40
+ * @public
41
+ */
42
+ const staleEnvelope = (input) => ({
43
+ schema: 1,
44
+ okfit_version: input.okfitVersion,
45
+ producer: input.producer,
46
+ okf_version: input.okfVersion,
47
+ root: input.root,
48
+ profile: input.profile,
49
+ as_of: DateTime.formatIso(input.now),
50
+ summary: {
51
+ concepts: input.concepts,
52
+ stale: input.items.length
53
+ },
54
+ items: input.items.map((item) => ({
55
+ id: item.id,
56
+ stale_after: DateTime.formatIso(item.staleAfter),
57
+ days_past: item.daysPast
58
+ }))
59
+ });
60
+
61
+ //#endregion
62
+ export { StaleEnvelope, StaleItem, StaleSummary, staleEnvelope };
package/stale/run.js ADDED
@@ -0,0 +1,22 @@
1
+ import { Bundle, Derive } from "@okfit/core";
2
+ import { Effect } from "effect";
3
+
4
+ //#region src/stale/run.ts
5
+ /**
6
+ * Load the bundle and derive the stale report against `now` (D-10, D-36:
7
+ * `Derive.staleReport` is pure and takes `now` as an argument, never a
8
+ * clock). This is a report, not a check — `okfit stale` always exits `0`;
9
+ * the `stale` lint rule is where staleness fails a run.
10
+ *
11
+ * @public
12
+ */
13
+ const runStale = (options) => Effect.gen(function* () {
14
+ const bundle = yield* Bundle.load({ root: options.root });
15
+ return {
16
+ bundle,
17
+ items: Derive.staleReport(bundle, options.now)
18
+ };
19
+ });
20
+
21
+ //#endregion
22
+ export { runStale };