@chrisdudek/yg 5.0.3 → 5.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrisdudek/yg",
3
- "version": "5.0.3",
3
+ "version": "5.1.0",
4
4
  "description": "Architecture rules your AI coding agent can't ignore. It gets the rules for a file before it edits, and every change is checked — by a free local script or an LLM reviewer — before it moves on. Works with Claude Code, Cursor, Copilot, Codex, Cline.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,8 +10,7 @@
10
10
  "dist/**/*.js",
11
11
  "dist/**/*.d.ts",
12
12
  "dist/**/*.wasm",
13
- "dist/**/*.node-types.json",
14
- "graph-schemas/"
13
+ "dist/**/*.node-types.json"
15
14
  ],
16
15
  "engines": {
17
16
  "node": ">=22"
@@ -1,62 +0,0 @@
1
- # yg-architecture.yaml — Schema for architecture constraints
2
- # File: .yggdrasil/yg-architecture.yaml
3
- #
4
- # Defines the project's type system: what kinds of nodes exist, how they can
5
- # relate, and which aspects apply by default. This is the foundation of the
6
- # graph — every node declares a type, and every type must be defined here.
7
- #
8
- # Changes to this file affect the entire graph and should be confirmed with the user.
9
-
10
- node_types:
11
- <type-id>:
12
- description: <string> # required — what this type is for, when to use it.
13
- # absence is a FATAL architecture-invalid error (the whole type system is rejected).
14
-
15
- when: <file-predicate> # optional — per-file classification.
16
- # Types WITH `when` are file-classifying: every file in
17
- # a node's mapping must satisfy the predicate (forward
18
- # check). Types WITHOUT `when` are organizational:
19
- # parent-only nodes — any mapping fires
20
- # type-without-when-with-mapping.
21
- #
22
- # Grammar:
23
- # path: <glob> — minimatch glob on repo-relative POSIX path
24
- # content: <regex> — JavaScript regex against file content
25
- # path + content combined — implicit all_of of both atoms
26
- # all_of: [<predicate>, ...] — every child must satisfy
27
- # any_of: [<predicate>, ...] — at least one child must satisfy
28
- # not: <predicate> — single child negation
29
- #
30
- # See: yg knowledge read working-with-architecture
31
-
32
- enforce: strict # optional — bidirectional enforcement.
33
- # Requires `when`. Every repo file matching the type's
34
- # `when` MUST belong to exactly one node of this type
35
- # (backward scan). A matching file owned by no such node
36
- # emits type-strict-orphan; one owned by a node of a
37
- # different type emits type-strict-misplaced.
38
- # Use only for types where missing the type means missing
39
- # a critical aspect (security, audit, regulatory).
40
-
41
- log_required: <boolean> # optional — default false. Enable (true) on types whose
42
- # changes carry business intent worth capturing — domain
43
- # logic, command handlers, persistence adapters. When true,
44
- # a node of this type demands a fresh log entry before
45
- # `yg check --approve` whenever its mapped source changed
46
- # since the node's last positive closure. Leave omitted
47
- # (false) for types whose changes carry no business decision
48
- # worth forcing an entry for (e.g. config, types, constants).
49
-
50
- aspects: # optional — aspects automatically applied to every
51
- # node of this type (channel 3). Two forms per entry:
52
- - <aspect-id> # bare string — unconditional
53
- - id: <aspect-id> # object form — with per-site applicability filter
54
- status: enforced # optional — explicit status override (channel 3).
55
- # Must satisfy bump rule (bump up OK, downgrade is validator error).
56
- when: <aspect-predicate> # optional — see schemas/yg-aspect.yaml for grammar
57
- # These also cascade to children (channel 4).
58
-
59
- parents: [<type-id>, ...] # optional — allowed parent node types in the hierarchy.
60
-
61
- relations: # optional — allowed relation targets by relation type.
62
- <relation-type>: [<type-id>, ...] # calls | uses | extends | implements | emits | listens
@@ -1,194 +0,0 @@
1
- # yg-aspect.yaml — Schema for cross-cutting aspects
2
- # Each aspect is a directory under .yggdrasil/aspects/ containing this file
3
- # plus any number of .md content files (for LLM aspects) or a check.mjs
4
- # (for deterministic aspects).
5
- #
6
- # Aspect identifier = relative path from aspects/ to the directory
7
- # (e.g. observability/logging). Aspects can be organized in nested
8
- # directories — the directory structure is for organization only,
9
- # there is no automatic parent-child inheritance between aspects.
10
- #
11
- # The .md content files are what the reviewer checks against source code.
12
- # They should state WHAT must be satisfied and WHY.
13
-
14
- name: CrossCuttingRequirementName # required — display name
15
- description: "Short description" # required — shown in yg aspects output and context packages.
16
- # Validator emits description-missing if absent.
17
-
18
- # reviewer: # OPTIONAL — reviewer kind is inferred from rule-file presence:
19
- #
20
- # content.md present → llm
21
- # check.mjs present → deterministic
22
- # neither + implies declared → aggregate
23
- #
24
- # The reviewer: block is only required when you need to declare
25
- # reviewer.tier: for an LLM aspect. When present, an explicit
26
- # reviewer.type must agree with the inferred kind (validator
27
- # error otherwise).
28
- #
29
- # Three kinds:
30
- # llm — aspect ships content.md; an LLM reads it and
31
- # judges the code against the rule.
32
- # deterministic — aspect ships check.mjs; the runner executes it
33
- # locally with graph-aware ctx (files, fs, graph,
34
- # parsers). Language-agnostic. No LLM call, zero
35
- # token cost.
36
- # aggregate — aspect ships neither rule source but declares
37
- # implies:. A named bundle — expands its implied
38
- # aspects onto every node where effective. Has no
39
- # own reviewer and produces no own verdict. An
40
- # aspect with neither rule source and no implies:
41
- # is rejected (aspect-empty).
42
- # type: llm # optional; must be 'llm', 'deterministic', or 'aggregate' if set.
43
- # tier: deep # optional, only when type: llm (or inferred llm).
44
- # If omitted, the aspect uses reviewer.default from yg-config.yaml.
45
- # If present, must reference a key under reviewer.tiers in the config.
46
- # Forbidden when type is 'deterministic' or 'aggregate'.
47
-
48
- status: enforced # optional — aspect-level default. enum: draft | advisory | enforced.
49
- # Absent → 'enforced'.
50
- # draft = reviewer skipped, no verdict, no baseline, no drift.
51
- # advisory = reviewer runs; refused → warning (no block).
52
- # enforced = reviewer runs; refused → error (blocks check).
53
- # This is only the aspect-level default. The effective status on a
54
- # node is max() across cascading channels 1–6; channel 7 (implies)
55
- # carries status_inherit instead. Downgrade attempts are validator
56
- # errors. Advisory and enforced verdicts are recorded in the
57
- # baseline; draft aspects get no verdict.
58
-
59
- # implies: # optional — other aspects included automatically when this
60
- # # aspect is effective on a node. Two forms:
61
- # - simple-aspect-id # bare string — implied unconditionally (when outer aspect passes)
62
- # - id: conditional-aspect-id # object form — imply only when `when` passes on the node
63
- # when: <predicate> # see `when` section below for grammar
64
- # status_inherit: strictest # optional — propagation modifier for this implies edge.
65
- # enum: strictest | own-default.
66
- # Absent → 'strictest' (implied aspect promotes to
67
- # the implier's effective status if higher than the
68
- # implied aspect's own default).
69
- # 'own-default' anchors the implied aspect to its
70
- # own aspect-level default (decouples from implier).
71
- #
72
- # ASYMMETRY NOTE: attach-site entries on channels
73
- # 1–6 (node, ancestor, architecture type, ancestor
74
- # type, flow, port) carry an explicit `status:`
75
- # VALUE. Channel 7 (implies) carries a propagation
76
- # MODIFIER (`status_inherit:`) instead. Implies is
77
- # not a direct attach — the implied aspect's status
78
- # is structurally derived from the implier's
79
- # effective status on the node. The modifier
80
- # selects how to derive; a value-overriding
81
- # `status:` on an implies edge would couple the
82
- # edge to a literal that becomes stale if the
83
- # implied aspect's own default changes.
84
- # Chains expand recursively. Cycles are forbidden — CLI detects.
85
-
86
- # when: <predicate> # optional — applicability filter. If the predicate evaluates
87
- # to false on a node, this aspect is not effective on that node
88
- # regardless of which channel attached it. Combines with
89
- # attach-site `when` declarations via AND.
90
- #
91
- # Grammar:
92
- # when:
93
- # all_of: [<clause>, ...] # AND
94
- # any_of: [<clause>, ...] # OR
95
- # not: <clause> # negation
96
- # <atomic> # top-level atomics imply all_of
97
- #
98
- # Atomic clauses:
99
- # relations:
100
- # <relation-type>: # calls | uses | extends | implements | emits | listens
101
- # target_type: <type-id> # match target node's declared type
102
- # target: <node-path> # match exact node path (relative to model/)
103
- # consumes_port: <port> # match a port consumed on this relation
104
- # descendants: # same as relations but evaluated against any descendant in model/
105
- # relations: {...}
106
- # type: <type-id>
107
- # has_port: <port-name>
108
- # node:
109
- # type: <type-id>
110
- # has_port: <port-name>
111
- # has_mapping: true | false
112
- #
113
- # Example:
114
- # when:
115
- # any_of:
116
- # - relations: { calls: { target_type: service-client } }
117
- # - descendants: { relations: { calls: { target_type: service-client } } }
118
-
119
- # scope: # optional — controls review granularity (applies to both LLM
120
- # and deterministic aspects). Forbidden on aggregate aspects.
121
- # Absent → equivalent to { per: node }.
122
- #
123
- # Fields:
124
- # per: node | file REQUIRED. Default: node.
125
- # node — one review over the whole subject set.
126
- # LLM: one prompt with all subject files.
127
- # Deterministic: one check(ctx) invocation; ctx.files = subject set.
128
- # file — one review per subject file.
129
- # LLM: one prompt per file.
130
- # Deterministic: one check(ctx) invocation per file; ctx.files = [file].
131
- # WARNING: per: file is ONLY for file-local rules — a per-file
132
- # reviewer cannot see sibling files. Rules that need cross-file
133
- # context (e.g. "correlation ID propagates across calls") must
134
- # stay per: node. Before switching an aspect to per: file, verify
135
- # the rule can be judged from that single file alone.
136
- #
137
- # files: <file-predicate> OPTIONAL. Filters which mapped files enter
138
- # the subject set. Subject set = mapped files ∩ filter.
139
- # Absent → all mapped files.
140
- # Empty subject set after filtering → vacuous pass by design (no review,
141
- # no error). An aspect may legitimately exclude every file of a node it
142
- # lands on (e.g. a filter that matches only *.ts files landing on a
143
- # node with only *.py files).
144
- #
145
- # File-predicate grammar — atoms:
146
- # path: "glob" minimatch glob on repo-relative POSIX path
147
- # content: "regex" JavaScript regex tested against file content
148
- # Boolean combinators (same as aspect when:):
149
- # all_of: [...] AND
150
- # any_of: [...] OR
151
- # not: <clause> negation
152
- # Top-level path + content imply all_of.
153
- #
154
- # NOTE: path/content are FILE atoms — they belong here in scope.files.
155
- # The aspect-level when: field uses NODE atoms (node, relations,
156
- # descendants) and filters which NODES the aspect applies to — do not
157
- # mix the two. The CLI will cross-hint you if you use an atom in the
158
- # wrong site.
159
- #
160
- # Cost note: editing scope: (per or files) changes the input hash for
161
- # every pair of this aspect — every node using the aspect needs
162
- # re-verification. Run `yg impact --aspect <id>` before widening or
163
- # narrowing the filter.
164
- #
165
- # Example:
166
- # scope:
167
- # per: file
168
- # files:
169
- # all_of:
170
- # - path: "src/**/*.ts"
171
- # - not: { path: "**/*.test.ts" }
172
-
173
- # references: # optional — supporting files for the LLM reviewer.
174
- # Permitted on LLM aspects ONLY (forbidden on deterministic).
175
- # Each entry is a string (shorthand) OR an object { path, description? }.
176
- #
177
- # Example:
178
- # references:
179
- # - docs/error-codes.md # shorthand
180
- # - path: source/cli/src/errors/codes.ts
181
- # description: "Catalogue of valid error codes; reviewer rejects unknown codes."
182
- #
183
- # Constraints (validated by `yg check`):
184
- # - Path is repo-root-relative.
185
- # - No '..' that escapes the repo root; no leading '/'; no Windows drive letter; no '~'.
186
- # - File must exist at check time and resolve (after symlink follow) to a regular file.
187
- # - No duplicates within one aspect.
188
- #
189
- # Drift semantics: changes to referenced files cascade to all nodes where this
190
- # aspect is effective — same as changes to content.md. Run `yg impact --file <ref>`
191
- # before editing a widely-referenced file.
192
- #
193
- # Size limits: per-tier caps via reviewer.tiers.<tier>.references.* in yg-config.yaml.
194
- # Defaults: 64 KiB per file, 256 KiB total per aspect.
@@ -1,53 +0,0 @@
1
- # yg-config.yaml — Schema for the Yggdrasil project configuration
2
- # Located at .yggdrasil/yg-config.yaml — one per project.
3
- # Edit this after running yg init to describe your project.
4
-
5
- version: "5.0.0" # managed by CLI — do not edit manually. Tracks the CLI version
6
- # that last initialized or upgraded this config.
7
-
8
- quality: # optional — quality thresholds
9
- max_direct_relations: 10 # maximum outgoing relations per node (warning if above)
10
-
11
- parallel: 1 # optional — concurrency limit for the yg check --approve fill (positive integer, default: 1)
12
-
13
- debug: false # optional — when true, appends all command output to .yggdrasil/.debug.log
14
- # Default: false (off). Log is append-only; rotate or delete manually.
15
-
16
- coverage: # optional — scopes the unmapped-files gate. Absent = whole repo required (today's behavior).
17
- required: ["/"] # roots where an uncovered tracked file is an ERROR (blocks). "/" = whole repo.
18
- excluded: [] # roots where an uncovered file is SILENT (no warning).
19
- # Files outside required and excluded are a non-blocking WARNING.
20
- # Subtrees containing their own nested .yggdrasil/ are auto-skipped by every check.
21
-
22
- reviewer: # required — aspect verification during yg check --approve
23
- default: standard # required when more than one tier is configured; optional with exactly one tier.
24
- # Must reference one of the keys under reviewer.tiers.
25
- tiers: # required — named tier configurations, minimum one entry.
26
- standard: # tier name — referenced from aspects via reviewer.tier:
27
- provider: ollama # provider id (one of: ollama, openai, anthropic, google,
28
- # openai-compatible, claude-code, codex, gemini-cli)
29
- consensus: 1 # positive odd integer >= 1 (3+ for majority vote). Per-tier.
30
- config: # provider-specific settings — same fields the provider accepts.
31
- model: "qwen3.5:9b" # model id
32
- endpoint: "http://localhost:11434" # custom endpoint (required for ollama, openai-compatible)
33
- temperature: 0 # reduces variability — keep at 0
34
- # timeout: 300 # Per-call timeout in SECONDS (default 300). Only CLI providers.
35
- # max_prompt_chars: 200000 # optional — assembled reviewer-prompt character cap (positive integer).
36
- # Checked deterministically before the LLM call. Absent = unlimited.
37
- # Exceeding this limit renders a blocking error naming remedies
38
- # (split the node, shorten references, or raise the cap).
39
- # Never participates in verdict identity — tuning it does not
40
- # invalidate recorded baselines.
41
- # Add more tiers as needed (e.g. a `deep` tier with a higher-capability model for critical aspects).
42
- # An aspect references a non-default tier via:
43
- #
44
- # reviewer:
45
- # type: llm
46
- # tier: deep
47
- #
48
- # Tier names match ^[a-zA-Z][a-zA-Z0-9_-]{0,62}$ and `default` is reserved.
49
- # yg-secrets.yaml is a deep-merge overlay over this file (gitignored): it
50
- # mirrors the same shape and overrides any field locally — most often a
51
- # tier's api_key, or pointing a named tier at a different provider/model.
52
- # Only the tier NAME is folded into a verdict hash, so a local override never
53
- # invalidates recorded baselines. Keep credentials out of this committed file.
@@ -1,25 +0,0 @@
1
- # yg-flow.yaml — Schema for end-to-end business flows
2
- # Each flow is a directory under .yggdrasil/flows/ containing this file.
3
- #
4
- # A flow describes a business process — what happens in the world,
5
- # not code call sequences. "User places an order" is a flow.
6
- # "Handler calls service" is a relation between nodes.
7
- #
8
- # Descendants of a declared participant are automatically included —
9
- # listing a parent node covers all its children.
10
-
11
- name: EndToEndProcessName # required — display name
12
- description: "What this business process does" # required — shown in yg flows output and
13
- # context packages. Validator emits description-missing if absent.
14
-
15
- nodes: # required, non-empty — participant nodes (alias: participants)
16
- - orders/order-service # paths relative to model/
17
- - payments/payment-service # each participant (and its descendants) must satisfy
18
- - inventory/inventory-service # any flow-level aspects declared below
19
-
20
- aspects: # optional — aspects propagate to all flow participants (channel 5)
21
- - simple-aspect # bare string
22
- - id: conditional-aspect # object form with per-site applicability filter
23
- status: enforced # optional — explicit status override (channel 5).
24
- # Must satisfy bump rule (bump up OK, downgrade is validator error).
25
- when: <predicate> # optional — see schemas/yg-aspect.yaml for grammar
@@ -1,50 +0,0 @@
1
- # yg-node.yaml — Schema for model nodes
2
- # Every node is a directory under .yggdrasil/model/ containing this file.
3
- # The node's path in the graph = its directory path relative to model/.
4
- # Nodes nest by directory — a node at model/orders/handler/ is a child
5
- # of model/orders/ and inherits its parent's aspects.
6
-
7
- name: ComponentName # required — display name
8
- type: service # required — must match a type defined in yg-architecture.yaml
9
- description: "What this node does" # required — shown in context output and helps agents
10
- # understand purpose. Validator emits description-missing if absent.
11
-
12
- aspects: # optional — aspect identifiers applied directly to this node.
13
- # Two forms per entry:
14
- - simple-aspect # bare string — always effective once attached (channel 1)
15
- - id: conditional-aspect # object form — attach with per-site applicability filter
16
- status: enforced # optional — explicit status override (channel 1).
17
- # Must satisfy bump rule (bump up OK, downgrade is validator error).
18
- when: <predicate> # optional — see schemas/yg-aspect.yaml for grammar
19
- # Aspects cascade to all child nodes.
20
-
21
- ports: # optional — named entry points with required aspects
22
- port-name: # consumers of this node reference ports via consumes
23
- description: "What this port provides" # required
24
- aspects: # required — aspects consumers must satisfy (channel 6)
25
- - simple-aspect # bare string form
26
- - id: conditional-aspect
27
- status: enforced # optional — explicit status override (channel 6).
28
- # Must satisfy bump rule (bump up OK, downgrade is validator error).
29
- when: <predicate> # optional — object form with per-attach applicability filter
30
-
31
- relations: # optional — outgoing dependencies to other nodes
32
- # Load-bearing, not just documentation: a built-in deterministic check
33
- # parses this node's mapped code and REFUSES it (relation-undeclared-dependency,
34
- # always an error) if it depends on another mapped node without a relation here.
35
- # One-directional — a declared relation needs no code behind it. See
36
- # `yg knowledge read ports-and-relations`.
37
- - target: other/module-path # required — node path relative to model/
38
- type: calls # required — calls | uses | extends | implements | emits | listens
39
- consumes: [port-name] # optional — port names consumed from target
40
- # required when target declares ports — otherwise yg check emits a
41
- # BLOCKING ERROR (port-missing-consumes) that fails the architecture gate.
42
- # There is no waiver; resolve by adding consumes or removing the ports.
43
- # Naming a target that declares no ports raises consumes-without-ports.
44
-
45
- mapping: # optional — source files and directories owned by this node
46
- - src/modules/component/ # directory — all files inside are owned (recursive)
47
- - src/modules/component.ts # file — exact match
48
- # paths are relative to repository root
49
- # each source file must have exactly one owner node
50
-