laymos 0.0.8 → 0.0.10

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.
Files changed (60) hide show
  1. package/README.md +259 -142
  2. package/dist/architecture-analysis-schema/module-source-schema.d.ts +4 -0
  3. package/dist/architecture-analysis-schema/module-source-schema.d.ts.map +1 -1
  4. package/dist/architecture-analysis-schema/module-source-schema.js +4 -0
  5. package/dist/change-set-schema/change-set-schema.js +2 -2
  6. package/dist/cli/run.d.ts +1 -1
  7. package/dist/cli/run.d.ts.map +1 -1
  8. package/dist/cli/run.js +2 -0
  9. package/dist/cli/skills.d.ts +6 -0
  10. package/dist/cli/skills.d.ts.map +1 -0
  11. package/dist/cli/skills.js +9 -0
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +2 -2
  15. package/dist/orchestrator/load-changes/index.d.ts +1 -1
  16. package/dist/orchestrator/load-changes/index.d.ts.map +1 -1
  17. package/dist/orchestrator/load-changes/index.js +1 -1
  18. package/dist/orchestrator/load-changes/load-changes.d.ts +4 -3
  19. package/dist/orchestrator/load-changes/load-changes.d.ts.map +1 -1
  20. package/dist/orchestrator/load-changes/load-changes.js +14 -9
  21. package/dist/orchestrator/load-source-files/index.d.ts +1 -1
  22. package/dist/orchestrator/load-source-files/index.d.ts.map +1 -1
  23. package/dist/orchestrator/load-source-files/index.js +1 -1
  24. package/dist/orchestrator/load-source-files/load-source-files.d.ts +20 -4
  25. package/dist/orchestrator/load-source-files/load-source-files.d.ts.map +1 -1
  26. package/dist/orchestrator/load-source-files/load-source-files.js +46 -7
  27. package/dist/services/git/changed-paths.d.ts +1 -0
  28. package/dist/services/git/changed-paths.d.ts.map +1 -1
  29. package/dist/services/git/changed-paths.js +8 -0
  30. package/dist/services/git/git.d.ts +1 -0
  31. package/dist/services/git/git.d.ts.map +1 -1
  32. package/dist/services/git/git.js +5 -1
  33. package/dist/skills-command/index.d.ts +2 -0
  34. package/dist/skills-command/index.d.ts.map +1 -0
  35. package/dist/skills-command/index.js +1 -0
  36. package/dist/skills-command/skills-command.d.ts +21 -0
  37. package/dist/skills-command/skills-command.d.ts.map +1 -0
  38. package/dist/skills-command/skills-command.js +64 -0
  39. package/dist/story/schema/index.d.ts +2 -2
  40. package/dist/story/schema/index.d.ts.map +1 -1
  41. package/dist/story/schema/index.js +1 -1
  42. package/dist/story/schema/story-report-schema.d.ts +264 -132
  43. package/dist/story/schema/story-report-schema.d.ts.map +1 -1
  44. package/dist/story/schema/story-report-schema.js +4 -4
  45. package/dist/story/story.d.ts +3 -1
  46. package/dist/story/story.d.ts.map +1 -1
  47. package/dist/story/story.js +4 -5
  48. package/package.json +18 -11
  49. package/schema.json +60 -55
  50. package/skills/deep-module/SKILL.md +55 -0
  51. package/skills/domain-modeling/ADR-FORMAT.md +47 -0
  52. package/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
  53. package/skills/domain-modeling/SKILL.md +74 -0
  54. package/skills/domain-modeling/agents/openai.yaml +3 -0
  55. package/skills/laymos/SKILL.md +130 -0
  56. package/skills/laymos/references/config.md +96 -0
  57. package/skills/laymos/references/design.md +166 -0
  58. package/skills/laymos/references/graphs.md +85 -0
  59. package/skills/laymos/references/visibility.md +60 -0
  60. package/skills/to-laymos/SKILL.md +60 -0
package/README.md CHANGED
@@ -1,167 +1,284 @@
1
- # Laymos
2
-
3
- Laymos enforces architectural dependency rules. It also provides focused
4
- dependency queries for exploring and understanding a codebase.
5
-
6
- ## Architecture
7
-
8
- Declare Layers, Modules, and LayerGraphs in a plain `laymos.config.json`.
9
-
10
- A **Layer** is a named group of literal project-relative files and folders.
11
- Layers partition the supported files beneath `sourceRoots`: every included
12
- file belongs to exactly one Layer, and no declared Layer scopes may overlap.
13
- Use `ignoredPaths` to remove files or folders from analysis explicitly.
14
-
15
- A **LayerGraph** is a named set of **rules** representing one responsibility
16
- (e.g. core architecture or test boundaries). It is an organizational grouping,
17
- not an enforcement boundary. A project may have no LayerGraphs when no
18
- cross-Layer imports are allowed.
19
-
20
- Each rule maps a layer id to the layer ids it may directly depend on. Rules
21
- are default-deny: any dependency between two layers with no declared path
22
- between them — direct or transitive, across every LayerGraph combined — is a
23
- violation. Permission is transitive, so only direct edges need declaring (if
24
- `app` may depend on `domain` and `domain` may depend on `infra`, `app` may
25
- depend on `infra` without declaring it explicitly). The combined graph must be
26
- acyclic. A layer with no outgoing rule is a valid, intentional leaf.
27
-
28
- A **Module** is a self-contained source boundary backed by a supported source
29
- file or directory. A **Configured Module** is an explicit, disjoint boundary
30
- within one Layer, and every included file belongs to one. Its `kind` is Normal
31
- by default. Marking it `shared` lets peers in the same Layer import it; Layer
32
- Rules still decide cross-Layer access. An Entry Module may depend on other
33
- Modules but cannot be imported by one.
34
-
35
- A Normal or Shared File Module is its own public entry point. A Normal or Shared
36
- Directory Module requires a root `index.ts`. List an exact directory path in
37
- `subpaths` to add another public `index.ts` for tree shaking. Entry Modules
38
- follow their host's file convention and cannot declare Subpaths.
39
-
40
- ```json
41
- {
42
- "$schema": "https://unpkg.com/laymos/schema.json",
43
- "sourceRoots": ["src"],
44
- "ignoredPaths": ["src/generated"],
45
- "layers": {
46
- "app": { "paths": ["src/app"], "description": "Application" },
47
- "domain": { "paths": ["src/domain"], "description": "Domain" },
48
- "infra": { "paths": ["src/infra"] }
49
- },
50
- "modules": {
51
- "src/app": { "kind": "entry" },
52
- "src/domain/orders": {},
53
- "src/domain/catalog": { "kind": "shared", "subpaths": ["events"] },
54
- "src/infra": {}
55
- },
56
- "layerGraphs": {
57
- "architecture": {
58
- "description": "Core layering",
59
- "rules": {
60
- "app": ["domain"],
61
- "domain": ["infra"]
62
- }
63
- }
64
- }
65
- }
66
- ```
1
+ # laymos
67
2
 
68
- No package dependency is required to author or consume this file — the
69
- `$schema` key gives editors autocomplete/validation directly, and any other
70
- tool (including a separate devtools server rendering the project's
71
- architecture) can read it as plain JSON. See
72
- [ADR-0003](docs/adr/0003-json-config-over-typescript.md) for why.
3
+ Enforces architectural dependency rules and explores source dependencies.
73
4
 
74
- ## Library API
5
+ ## Big picture
75
6
 
76
- ```ts
77
- import { Effect } from 'effect';
78
- import { analyzeProject } from 'laymos';
7
+ A project's architecture usually lives in people's heads. laymos moves it into
8
+ a plain `laymos.config.json`: Layers group source paths, Modules draw disjoint
9
+ boundaries inside a Layer, and LayerGraphs say which Layers may depend on
10
+ which. `laymos lint` compares that intent with the real import graph and
11
+ reports every gap. `laymos inspect` answers the reverse question: given a
12
+ file or Module, what does it depend on and who is allowed to reach it.
79
13
 
80
- const analysis = await Effect.runPromise(
81
- analyzeProject('/absolute/project/laymos.config.json'),
82
- );
83
- ```
14
+ The same config can point at a folder of Stories: small executable narratives
15
+ that prove a behavior and keep their proof next to the prose. `laymos stories`
16
+ runs them and reports a verdict per Story.
84
17
 
85
- `analyzeProject` returns `ArchitectureAnalysis`: the decoded Config plus Layer
86
- and Module analysis. `ArchitectureAnalysisSchema` is its runtime and transport
87
- contract; its Maps and Sets support Effect Schema's canonical JSON codec.
18
+ The library entry does everything the CLI does, so other tools can host it.
19
+ [@pkishorez/devtools](../devtools/README.md) serves Architecture Analyses, change sets, and
20
+ Story reports over RPC to its browser UI using the browser-safe schema
21
+ subpaths. Stories capture traces with
22
+ [@pkishorez/effect-tracer](../effect-tracer/README.md) and Flows with
23
+ [@pkishorez/flow](../flow/README.md).
88
24
 
89
- Browser and RPC code should import the contract-only entrypoint:
25
+ Terms are defined in [CONTEXT.md](./CONTEXT.md). Decisions are in
26
+ [docs/adr/](./docs/adr/). The config reference is in
27
+ [docs/config.md](./docs/config.md) and the CLI reference in
28
+ [docs/cli.md](./docs/cli.md).
90
29
 
91
- ```ts
92
- import {
93
- ArchitectureAnalysisSchema,
94
- type ArchitectureAnalysis,
95
- } from 'laymos/architecture-analysis-schema';
30
+ ## Install
31
+
32
+ ```sh
33
+ pnpm add -D laymos
96
34
  ```
97
35
 
98
- This entrypoint contains data schemas and types only. Project analysis remains
99
- available from the Node-oriented root entrypoint.
36
+ Peer dependencies:
37
+
38
+ - `effect` (`4.0.0-rc.112`): every library function returns an Effect, and
39
+ Stories are written as Effects.
40
+
41
+ ## Exports
42
+
43
+ ### `laymos`
44
+
45
+ Node-only. Reads the config, walks the source tree, and runs git.
46
+
47
+ | Export | What it does |
48
+ | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
49
+ | `analyzeProject` | Reads a config path and returns the full Architecture Analysis: config, Layer and Module analysis. |
50
+ | `inspectProject` | Summarizes the whole Project for the `inspect project` view. |
51
+ | `inspectLayer` | Reports one Layer's paths, allowed links, Modules, and violations. |
52
+ | `inspectFile` | Reports one file's Layer, Module, boundary role, and dependencies, optionally recursive. |
53
+ | `inspectModule` | Reports one Configured Module's visibility, shape, entry points, and dependency tree. |
54
+ | `InspectionTargetNotFound` | Error when the inspected file, Layer, or Module does not exist. |
55
+ | `ModuleInspectionCycle` | Error when the inspected Module sits in a dependency cycle. |
56
+ | `loadModuleSource` | Returns the paths and contents of every source file assigned to one Configured Module. |
57
+ | `ModuleSourceNotFound` | Error when the requested Module is not configured. |
58
+ | `ModuleSourceReadError` | Error when one of the Module's files could not be read. |
59
+ | `loadSourceFiles` | Returns the contents of included source files under the given path prefixes. |
60
+ | `SourceFileReadError` | Error when one of those files could not be read. |
61
+ | `loadDocumentation` | Reads the markdown declared by `docsPath` for a Layer, LayerGraph, Module, or Module Graph scope. |
62
+ | `DocumentationScopeNotFound` | Error when the scope names something the config does not declare. |
63
+ | `DocumentationReadError` | Error when the markdown file could not be read. |
64
+ | `getStoryTree` | Loads the Story tree from `storiesPath` without running anything. |
65
+ | `planStories` | Loads and scopes Stories, then returns the total and a Stream of reports as they finish. |
66
+ | `runStories` | Same as `planStories` but returns only the report Stream. |
67
+ | `StoriesError` | Error for a missing `storiesPath`, a bad root, duplicate titles, or an invalid timeout. |
68
+ | `loadBranches` | Lists the git branches of the project's repository. |
69
+ | `loadChangeSet` | Lists paths added or modified against a base ref, default `HEAD`. |
70
+ | `loadFileDiff` | Returns the hunks of one file against a base ref. |
71
+ | `GitError` | Error when the folder is not a repository, the ref is unknown, or git failed. |
72
+ | `ConfigError` | Error when the config could not be read, parsed, decoded, or validated. |
73
+ | `CruiseError` | Error when the source tree could not be walked or parsed. |
74
+ | `ArchitectureAnalysisSchema` | Re-export from `laymos/architecture-analysis-schema`. |
75
+ | `ModuleSourceSnapshotSchema` | Re-export from `laymos/architecture-analysis-schema`. |
76
+ | `DocumentationScopeSchema` | Re-export from `laymos/architecture-analysis-schema`. |
77
+ | `DocumentationSchema` | Re-export from `laymos/architecture-analysis-schema`. |
78
+ | `StoryReportSchema` | Re-export from `laymos/story/schema`. |
79
+ | `StoryTreeSchema` | Re-export from `laymos/story/schema`. |
80
+ | `BranchSchema`, `ChangedPathSchema`, `ChangeSetSchema`, `ChangeStatusSchema`, `DiffHunkSchema`, `DiffLineSchema`, `FileDiffSchema` | Re-exports from `laymos/change-set-schema`. |
81
+
82
+ ### `laymos/architecture-analysis-schema`
83
+
84
+ Browser-safe. Schemas only; no file system access.
85
+
86
+ | Export | What it does |
87
+ | ----------------------------- | -------------------------------------------------------------------------------------------------- |
88
+ | `ArchitectureAnalysisSchema` | The wire contract for an Architecture Analysis; its Maps and Sets encode to JSON. |
89
+ | `LayerAnalysisSchema` | Layer membership, unassigned files, forbidden imports, and Layers without Modules. |
90
+ | `ModuleAnalysisSchema` | Analyzed Modules, Module Graphs, dependencies, and Module violations. |
91
+ | `ProjectConfigSchema` | The decoded config with every key present. |
92
+ | `ProjectConfigInputSchema` | The authoring config, where optional keys fall back to defaults. |
93
+ | `ConfigValidationIssueSchema` | One validation problem with its kind and message. |
94
+ | `ModuleSourceFileSchema` | One source file path with its contents. |
95
+ | `ModuleSourceSnapshotSchema` | The files of one Configured Module. |
96
+ | `DocumentationScopeSchema` | Which entity a documentation request targets: `module`, `module-graph`, `layer`, or `layer-graph`. |
97
+ | `DocumentationSchema` | The resolved markdown for one scope, or its absence. |
98
+
99
+ ### `laymos/change-set-schema`
100
+
101
+ Browser-safe.
102
+
103
+ | Export | What it does |
104
+ | -------------------- | ----------------------------------------------------------------------- |
105
+ | `ChangeStatusSchema` | `added` or `modified`. |
106
+ | `ChangedPathSchema` | One changed path with its status. |
107
+ | `ChangeSetSchema` | Every changed path against a base ref. |
108
+ | `DiffLineSchema` | One line of a diff hunk. |
109
+ | `DiffHunkSchema` | One hunk of a file diff. |
110
+ | `FileDiffSchema` | The hunks of one file. |
111
+ | `BranchSchema` | One local or remote-tracking branch, with `remote` and `current` flags. |
112
+
113
+ ### `laymos/story`
114
+
115
+ For Story files. Runs in Node under the Story runner.
116
+
117
+ | Export | What it does |
118
+ | ---------------- | ----------------------------------------------------------------------------------------------- |
119
+ | `Story.make` | Builds a Story from a title, description, source URL, and questions; `spine` defaults to false. |
120
+ | `Story.question` | Builds one question with its prose answer and its proof Effect. |
121
+ | `Story.group` | Builds a Story Group from a title, description, and children. |
122
+ | `Story.trace` | Runs an Effect under a trace recorder and attaches the captured trace to the report. |
123
+ | `Story.flow` | Runs an Effect with a memory Flow sink and attaches every Journal to the report. |
124
+ | `Story.assert` | Records a named pass or fail that decides the question's verdict. |
125
+ | `StoryContext` | The service the runner injects; `Story.trace`, `Story.flow`, and `Story.assert` use it. |
126
+ | `isStory` | Type guard for a Story value. |
127
+ | `isStoryGroup` | Type guard for a Story Group value. |
128
+
129
+ ### `laymos/story/schema`
130
+
131
+ Browser-safe.
132
+
133
+ | Export | What it does |
134
+ | ----------------------- | ------------------------------------------------------------- |
135
+ | `StoryTreeSchema` | The root Story Group with its nested groups and Story leaves. |
136
+ | `StoryTreeGroupSchema` | One group node of the tree. |
137
+ | `StoryLeafSchema` | One Story with its id, page, source, setup, and questions. |
138
+ | `QuestionLeafSchema` | One question with its slug, answer, and proof snippet. |
139
+ | `StoryPageSchema` | The markdown page attached to a Story or group. |
140
+ | `StorySourceSchema` | The path and contents of a Story file. |
141
+ | `StoryReportSchema` | The verdict and per-question reports of one Story run. |
142
+ | `QuestionReportSchema` | One question's sections, assertions, and verdict. |
143
+ | `QuestionSectionSchema` | A captured trace or Flow Journal section. |
144
+ | `CapturedTraceSchema` | The spans and logs captured by `Story.trace`. |
145
+ | `JournalSchema` | The Flow Journal captured by `Story.flow`. |
146
+ | `StoryAssertionSchema` | One assertion with its description and result. |
147
+ | `StoryVerdictSchema` | `passed`, `failed`, or `errored`. |
148
+ | `slugifyQuestion` | Turns a question string into its URL slug. |
100
149
 
101
- Load the current source snapshot for one Configured Module on demand:
150
+ ### `laymos/skills-command`
151
+
152
+ | Export | What it does |
153
+ | ------------------- | ------------------------------------------------------------------------------- |
154
+ | `makeSkillsCommand` | Builds a `skills` CLI subcommand that lists, prints, or installs skill folders. |
155
+
156
+ ### CLI
157
+
158
+ | Command | What it does |
159
+ | --------------------------------------------------- | ------------------------------------------------------------- |
160
+ | `laymos lint` | Checks every rule and Story page; exits 1 on violations. |
161
+ | `laymos lint layers` | Checks Layer coverage and cross-Layer rules. |
162
+ | `laymos lint modules` | Checks Module coverage, boundaries, dependencies, and cycles. |
163
+ | `laymos inspect project [--json]` | Summarizes the whole architecture. |
164
+ | `laymos inspect layer <name> [--json]` | Shows one Layer and its Modules. |
165
+ | `laymos inspect file <path> [--recursive] [--json]` | Shows a file's Layer, Module, and dependency tree. |
166
+ | `laymos inspect module <path> [--json]` | Shows a Configured Module's identity and dependencies. |
167
+ | `laymos stories [--concurrency <n>]` | Runs every Story and prints each verdict. |
168
+ | `laymos skills [<name>] [--install <dir>]` | Lists, prints, or installs the shipped agent skills. |
169
+
170
+ Details and exit codes are in [docs/cli.md](./docs/cli.md).
171
+
172
+ ## Usage
173
+
174
+ ### Analyze a project from Node
175
+
176
+ Read a config and get the full Architecture Analysis as one value. This is
177
+ what the CLI and the DevTools server both do first.
102
178
 
103
179
  ```ts
104
- import { Effect } from 'effect';
105
- import { loadModuleSource } from 'laymos';
180
+ import { Effect, Schema } from 'effect';
181
+ import { analyzeProject, ArchitectureAnalysisSchema } from 'laymos';
106
182
 
107
- const snapshot = await Effect.runPromise(
108
- loadModuleSource('/absolute/project/laymos.config.json', 'src/domain/orders'),
183
+ const analysis = await analyzeProject('./laymos.config.json').pipe(
184
+ Effect.runPromise,
109
185
  );
186
+
187
+ analysis.config.sourceRoots; // ['src']
188
+ analysis.layerAnalysis.unassignedFiles; // files with no Layer
189
+ analysis.moduleAnalysis.violations; // e.g. { kind: 'boundary', fromFile, toFile, ... }
190
+
191
+ // Send it over the wire: Maps and Sets encode to plain JSON.
192
+ const codec = Schema.toCodecJson(ArchitectureAnalysisSchema);
193
+ const json = Schema.encodeSync(codec)(analysis);
110
194
  ```
111
195
 
112
- The snapshot contains only included supported source files assigned to that
113
- Module by a fresh Architecture Analysis.
196
+ How it works:
114
197
 
115
- Inspect an exact included source file or Configured Module without consuming
116
- the complete Architecture Analysis:
198
+ - `analyzeProject` loads and validates the config, walks `sourceRoots` with
199
+ oxc, and runs Layer and Module analysis.
200
+ - Failures are typed: `ConfigError` for the config, `CruiseError` for the
201
+ source walk.
202
+ - `ArchitectureAnalysisSchema` is the same contract the browser decodes.
203
+
204
+ ### Write a Story
205
+
206
+ A Story file exports Stories built with `Story.make`. Each question holds a
207
+ prose answer and a proof Effect. `Story.assert` decides the verdict.
117
208
 
118
209
  ```ts
119
- import { Effect } from 'effect';
120
- import { inspectFile, inspectModule } from 'laymos';
210
+ import { Effect, Schema } from 'effect';
211
+ import { Story } from 'laymos/story';
212
+ import { EntityESchema } from 'std-toolkit/eschema';
121
213
 
122
- const file = await Effect.runPromise(
123
- inspectFile('/absolute/project/laymos.config.json', 'src/domain/order.ts', {
124
- recursive: true,
125
- }),
126
- );
127
- const module = await Effect.runPromise(
128
- inspectModule('/absolute/project/laymos.config.json', 'src/domain/orders'),
129
- );
214
+ const Task = EntityESchema.make('Task', 'taskId', {
215
+ boardId: Schema.String,
216
+ title: Schema.String,
217
+ })
218
+ .evolve('v2', { assignee: Schema.NullOr(Schema.String) }, (previous) => ({
219
+ ...previous,
220
+ assignee: null,
221
+ }))
222
+ .build();
223
+
224
+ export const appendAVersion = Story.make({
225
+ title: 'Append a version, never edit one',
226
+ description: 'Old rows still read after a new version is added.',
227
+ sourceUrl: import.meta.url,
228
+ questions: [
229
+ Story.question('Do rows written before v2 still read?', {
230
+ answer: 'Yes. The v1 shape is untouched, so a v1 row walks to v2.',
231
+ proof: Effect.gen(function* () {
232
+ const oldest = yield* Task.decode({
233
+ _v: 'v1',
234
+ taskId: 't1',
235
+ boardId: 'work',
236
+ title: 'Write the plan',
237
+ });
238
+ yield* Story.assert('a v1 row arrives at v2', oldest.assignee === null);
239
+ return { oldest };
240
+ }),
241
+ }),
242
+ ],
243
+ });
130
244
  ```
131
245
 
132
- ## CLI
246
+ How it works:
133
247
 
134
- ```sh
135
- laymos [--config <path>] lint
136
- laymos [--config <path>] lint layers
137
- laymos [--config <path>] lint modules
138
- laymos [--config <path>] inspect project [--json]
139
- laymos [--config <path>] inspect layer <layer-name> [--json]
140
- laymos [--config <path>] inspect file <file-path> [--recursive] [--json]
141
- laymos [--config <path>] inspect module <module-path> [--json]
248
+ - `storiesPath/index.ts` default-exports a `Story.group` that lists every
249
+ Story; `laymos stories` imports it with tsx and runs each proof.
250
+ - The runner provides `StoryContext`; `Story.assert` writes into it.
251
+ - Wrap a proof in `Story.trace` or `Story.flow` to attach a trace or a Flow
252
+ Journal to the report.
253
+
254
+ ### Run Stories and stream reports
255
+
256
+ Host the Story runner in your own process and consume reports as they finish.
257
+
258
+ ```ts
259
+ import { Effect, Stream } from 'effect';
260
+ import { getStoryTree, planStories } from 'laymos';
261
+
262
+ const tree = await getStoryTree('./laymos.config.json').pipe(Effect.runPromise);
263
+ tree.groups.map((group) => group.title);
264
+
265
+ const program = Effect.gen(function* () {
266
+ const { total, reports } = yield* planStories('./laymos.config.json', {
267
+ concurrency: 4,
268
+ });
269
+ const collected = yield* Stream.runCollect(reports);
270
+ const failed = [...collected].filter((r) => r.verdict !== 'passed');
271
+ return { total, failed: failed.length };
272
+ });
273
+
274
+ await Effect.runPromise(program);
142
275
  ```
143
276
 
144
- `lint` checks every architectural rule; `lint layers` checks Layer coverage,
145
- configured Module presence, and dependencies, while `lint modules` checks
146
- Module coverage, expected entry points, dependencies, public boundaries, and
147
- cycles. Violations exit with status `1`, while invalid configuration or an
148
- analysis failure exits with status `2`.
149
-
150
- `inspect file` prints the file's Layer, Configured Module, public-boundary role,
151
- and dependencies as a colored path tree. Direct dependencies are yellow; with
152
- `--recursive`, transitive dependencies are gray. Only exact included supported
153
- source files can be inspected. Included files with missing Layer or Module
154
- membership remain inspectable and show a coverage warning.
155
-
156
- `inspect project` summarizes the whole architecture. `inspect layer` accepts an
157
- exact Layer name and reports its paths, allowed Layer links, Modules, Shared
158
- count, and violations. `inspect module` accepts an exact configured Module path
159
- and prints its configured kind, source shape, observed kind, public entry
160
- points, and dependency tree. Add `--json` to any inspect command for stable tool
161
- output. If the selected Module participates in a dependency cycle, inspection
162
- stops and directs the user to `lint modules`.
163
-
164
- The active inspection target is green in both trees. All commands use
165
- `sourceRoots` and `ignoredPaths` from the config. Config paths default to
166
- `./laymos.config.json`, and project-relative paths are resolved from the config
167
- file's directory.
277
+ How it works:
278
+
279
+ - `getStoryTree` loads the tree without running proofs; the DevTools UI uses it
280
+ to render the outline.
281
+ - `planStories` returns the count up front and a `Stream` of `StoryReport`
282
+ so a UI can show progress.
283
+ - Pass `scope` to run one group or Story by id; a `StoriesError` with reason
284
+ `unknown-scope` is returned when it does not match.
@@ -2,6 +2,8 @@ import { Schema } from 'effect';
2
2
  export declare const ModuleSourceFileSchema: Schema.Struct<{
3
3
  readonly path: Schema.String;
4
4
  readonly content: Schema.String;
5
+ readonly binary: Schema.optional<Schema.Boolean>;
6
+ readonly unanalyzed: Schema.optional<Schema.Boolean>;
5
7
  }>;
6
8
  export type ModuleSourceFile = typeof ModuleSourceFileSchema.Type;
7
9
  export declare const ModuleSourceSnapshotSchema: Schema.Struct<{
@@ -10,6 +12,8 @@ export declare const ModuleSourceSnapshotSchema: Schema.Struct<{
10
12
  readonly files: Schema.$Array<Schema.Struct<{
11
13
  readonly path: Schema.String;
12
14
  readonly content: Schema.String;
15
+ readonly binary: Schema.optional<Schema.Boolean>;
16
+ readonly unanalyzed: Schema.optional<Schema.Boolean>;
13
17
  }>>;
14
18
  }>;
15
19
  export type ModuleSourceSnapshot = typeof ModuleSourceSnapshotSchema.Type;
@@ -1 +1 @@
1
- {"version":3,"file":"module-source-schema.d.ts","sourceRoot":"","sources":["../../src/architecture-analysis-schema/module-source-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,eAAO,MAAM,sBAAsB;;;EAGjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,OAAO,sBAAsB,CAAC,IAAI,CAAC;AAElE,eAAO,MAAM,0BAA0B;;;;;;;EAIrC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,OAAO,0BAA0B,CAAC,IAAI,CAAC"}
1
+ {"version":3,"file":"module-source-schema.d.ts","sourceRoot":"","sources":["../../src/architecture-analysis-schema/module-source-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,eAAO,MAAM,sBAAsB;;;;;EAOjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,OAAO,sBAAsB,CAAC,IAAI,CAAC;AAElE,eAAO,MAAM,0BAA0B;;;;;;;;;EAIrC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,OAAO,0BAA0B,CAAC,IAAI,CAAC"}
@@ -1,7 +1,11 @@
1
1
  import { Schema } from 'effect';
2
2
  export const ModuleSourceFileSchema = Schema.Struct({
3
3
  path: Schema.String,
4
+ // Empty for a binary file, whose bytes are never sent.
4
5
  content: Schema.String,
6
+ binary: Schema.optional(Schema.Boolean),
7
+ // An Unanalyzed file: known to git but outside the analysis universe.
8
+ unanalyzed: Schema.optional(Schema.Boolean),
5
9
  });
6
10
  export const ModuleSourceSnapshotSchema = Schema.Struct({
7
11
  modulePath: Schema.String,
@@ -7,14 +7,14 @@ export const ChangedPathSchema = Schema.Struct({
7
7
  uncommitted: Schema.Boolean,
8
8
  }).annotate({
9
9
  title: 'Changed Path',
10
- description: "One path a Base ref changed, relative to the Config's folder, and how it changed. A path may carry both committed work since the Base ref and uncommitted work in the working tree.",
10
+ description: 'One path a Base ref changed, relative to the folder it was measured in, and how it changed. A path may carry both committed work since the Base ref and uncommitted work in the working tree.',
11
11
  });
12
12
  export const ChangeSetSchema = Schema.Struct({
13
13
  baseRef: Schema.String,
14
14
  files: Schema.Array(ChangedPathSchema),
15
15
  }).annotate({
16
16
  title: 'Change Set',
17
- description: "The added and modified paths between a Base ref and the working tree, relative to the Config's folder. It decorates an Architecture Analysis and never alters the analysis universe.",
17
+ description: "The added and modified paths between a Base ref and the working tree, relative to the folder it was measured in: a Laymos Project's Config folder or a Monorepo root. It decorates an analysis and never alters what that analysis covers.",
18
18
  });
19
19
  export const DiffLineSchema = Schema.Struct({
20
20
  kind: Schema.Literals(['context', 'added', 'removed']),
package/dist/cli/run.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { Effect } from 'effect';
2
2
  import { Command } from 'effect/unstable/cli';
3
- export declare const cli: Effect.Effect<void, import("../index.js").ConfigError | import("../index.js").CruiseError | import("../index.js").InspectionTargetNotFound | import("../index.js").ModuleInspectionCycle | import("../index.js").StoriesError | import("effect/unstable/cli/CliError").CliError, Command.Environment>;
3
+ export declare const cli: Effect.Effect<void, import("../index.js").ConfigError | import("../index.js").CruiseError | import("../index.js").InspectionTargetNotFound | import("../index.js").ModuleInspectionCycle | import("effect/PlatformError").PlatformError | import("../index.js").StoriesError | import("effect/unstable/cli/CliError").CliError, Command.Environment>;
4
4
  //# sourceMappingURL=run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,OAAO,EAAQ,MAAM,qBAAqB,CAAC;AAmBpD,eAAO,MAAM,GAAG,uSAOf,CAAC"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,OAAO,EAAQ,MAAM,qBAAqB,CAAC;AAoBpD,eAAO,MAAM,GAAG,sVAQf,CAAC"}
package/dist/cli/run.js CHANGED
@@ -3,6 +3,7 @@ import { Command, Flag } from 'effect/unstable/cli';
3
3
  import { makeInspectCommand } from './inspect/index.js';
4
4
  import { makeLintCommand } from './lint/index.js';
5
5
  import { makeStoriesCommand } from './stories/index.js';
6
+ import { skillsCommand } from './skills.js';
6
7
  const rootCommand = Command.make('laymos', {}, () => Effect.void).pipe(Command.withSharedFlags({
7
8
  config: Flag.string('config').pipe(Flag.withDefault('laymos.config.json'), Flag.withDescription('Config file path. Project paths are relative to its directory.')),
8
9
  }));
@@ -11,4 +12,5 @@ export const cli = rootCommand.pipe(Command.withSubcommands([
11
12
  makeInspectCommand(configPath),
12
13
  makeLintCommand(configPath),
13
14
  makeStoriesCommand(configPath),
15
+ skillsCommand,
14
16
  ]), Command.run({ version: '0.0.1' }));
@@ -0,0 +1,6 @@
1
+ export declare const skillsCommand: import("effect/unstable/cli/Command").Command<"skills", {
2
+ readonly name: import("effect/Option").Option<string>;
3
+ readonly install: import("effect/Option").Option<string>;
4
+ readonly format: "json" | "text";
5
+ }, {}, import("effect/PlatformError").PlatformError, import("effect/FileSystem").FileSystem>;
6
+ //# sourceMappingURL=skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/cli/skills.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,aAAa;;;;4FAIxB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { makeSkillsCommand } from '../skills-command/index.js';
3
+ // Two levels below the package root from both `src/cli/` and `dist/cli/`.
4
+ const SKILLS_ROOT = fileURLToPath(new URL('../../skills/', import.meta.url));
5
+ export const skillsCommand = makeSkillsCommand({
6
+ skillsRoot: SKILLS_ROOT,
7
+ names: ['laymos', 'to-laymos', 'domain-modeling', 'deep-module'],
8
+ cliName: 'laymos',
9
+ });
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { analyzeProject } from './orchestrator/analyze-project/index.js';
2
2
  export { loadModuleSource, ModuleSourceNotFound, ModuleSourceReadError, } from './orchestrator/load-module-source/index.js';
3
3
  export { loadDocumentation, DocumentationReadError, DocumentationScopeNotFound, } from './orchestrator/load-documentation/index.js';
4
- export { loadSourceFiles, SourceFileReadError, } from './orchestrator/load-source-files/index.js';
4
+ export { loadFolderFiles, loadSourceFiles, SourceFileReadError, } from './orchestrator/load-source-files/index.js';
5
5
  export { ArchitectureAnalysisSchema } from './architecture-analysis-schema/index.js';
6
6
  export type { ArchitectureAnalysis } from './architecture-analysis-schema/index.js';
7
7
  export { ModuleSourceSnapshotSchema } from './architecture-analysis-schema/index.js';
@@ -15,7 +15,7 @@ export { CruiseError } from './services/file-cruiser/index.js';
15
15
  export { getStoryTree, planStories, runStories, StoriesError, } from './orchestrator/run-stories/index.js';
16
16
  export { StoryReportSchema, StoryTreeSchema } from './story/schema/index.js';
17
17
  export type { StoryReport, StoryTree } from './story/schema/index.js';
18
- export { loadBranches, loadChangeSet, loadFileDiff, } from './orchestrator/load-changes/index.js';
18
+ export { loadBranches, loadChangeSet, loadFileDiff, loadKnownFiles, } from './orchestrator/load-changes/index.js';
19
19
  export { GitError } from './services/git/index.js';
20
20
  export { BranchSchema, ChangedPathSchema, ChangeSetSchema, ChangeStatusSchema, DiffHunkSchema, DiffLineSchema, FileDiffSchema, } from './change-set-schema/index.js';
21
21
  export type { Branch, ChangedPath, ChangeSet, ChangeStatus, DiffHunk, DiffLine, FileDiff, } from './change-set-schema/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EACL,eAAe,EACf,mBAAmB,GACpB,MAAM,2CAA2C,CAAC;AAEnD,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AAErF,YAAY,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AACrF,YAAY,EACV,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,yCAAyC,CAAC;AACjD,YAAY,EACV,aAAa,EACb,kBAAkB,GACnB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,cAAc,GACf,MAAM,iCAAiC,CAAC;AACzC,YAAY,EACV,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAE/D,OAAO,EACL,YAAY,EACZ,WAAW,EACX,UAAU,EACV,YAAY,GACb,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC7E,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEtE,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,GACb,MAAM,sCAAsC,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,cAAc,GACf,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EACV,MAAM,EACN,WAAW,EACX,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EACL,eAAe,EACf,eAAe,EACf,mBAAmB,GACpB,MAAM,2CAA2C,CAAC;AAEnD,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AAErF,YAAY,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AACrF,YAAY,EACV,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,yCAAyC,CAAC;AACjD,YAAY,EACV,aAAa,EACb,kBAAkB,GACnB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,cAAc,GACf,MAAM,iCAAiC,CAAC;AACzC,YAAY,EACV,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAE/D,OAAO,EACL,YAAY,EACZ,WAAW,EACX,UAAU,EACV,YAAY,GACb,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC7E,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEtE,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,cAAc,GACf,MAAM,sCAAsC,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,cAAc,GACf,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EACV,MAAM,EACN,WAAW,EACX,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,MAAM,8BAA8B,CAAC"}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ export { loadModuleSource, ModuleSourceNotFound, ModuleSourceReadError, } from '
5
5
  // Node consumers use this high-level capability to read documentation at any scope.
6
6
  export { loadDocumentation, DocumentationReadError, DocumentationScopeNotFound, } from './orchestrator/load-documentation/index.js';
7
7
  // Node consumers use this high-level capability to read arbitrary source files by path.
8
- export { loadSourceFiles, SourceFileReadError, } from './orchestrator/load-source-files/index.js';
8
+ export { loadFolderFiles, loadSourceFiles, SourceFileReadError, } from './orchestrator/load-source-files/index.js';
9
9
  // RPC transports use this browser-safe runtime contract for Architecture Analysis.
10
10
  export { ArchitectureAnalysisSchema } from './architecture-analysis-schema/index.js';
11
11
  export { ModuleSourceSnapshotSchema } from './architecture-analysis-schema/index.js';
@@ -20,7 +20,7 @@ export { getStoryTree, planStories, runStories, StoriesError, } from './orchestr
20
20
  // RPC transports use this browser-safe runtime contract for Story reports.
21
21
  export { StoryReportSchema, StoryTreeSchema } from './story/schema/index.js';
22
22
  // Node consumers use this high-level capability to report what a Base ref changed.
23
- export { loadBranches, loadChangeSet, loadFileDiff, } from './orchestrator/load-changes/index.js';
23
+ export { loadBranches, loadChangeSet, loadFileDiff, loadKnownFiles, } from './orchestrator/load-changes/index.js';
24
24
  // Change set callers distinguish a missing repository from a failed git command.
25
25
  export { GitError } from './services/git/index.js';
26
26
  // RPC transports use this browser-safe runtime contract for Change sets.
@@ -1,2 +1,2 @@
1
- export { loadBranches, loadChangeSet, loadFileDiff } from './load-changes.js';
1
+ export { loadBranches, loadChangeSet, loadFileDiff, loadKnownFiles, } from './load-changes.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/load-changes/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/load-changes/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,cAAc,GACf,MAAM,mBAAmB,CAAC"}
@@ -1,2 +1,2 @@
1
1
  // Node consumers use this high-level capability to report what a Base ref changed.
2
- export { loadBranches, loadChangeSet, loadFileDiff } from './load-changes.js';
2
+ export { loadBranches, loadChangeSet, loadFileDiff, loadKnownFiles, } from './load-changes.js';
@@ -1,7 +1,8 @@
1
1
  import { Effect } from 'effect';
2
2
  import type { Branch, ChangeSet, FileDiff } from '../../change-set-schema/index.js';
3
3
  import { type GitError } from '../../services/git/index.js';
4
- export declare function loadChangeSet(configPath: string, baseRef?: string): Effect.Effect<ChangeSet, GitError>;
5
- export declare function loadFileDiff(configPath: string, path: string, baseRef?: string): Effect.Effect<FileDiff, GitError>;
6
- export declare function loadBranches(configPath: string): Effect.Effect<readonly Branch[], GitError>;
4
+ export declare function loadChangeSet(folder: string, baseRef?: string): Effect.Effect<ChangeSet, GitError>;
5
+ export declare function loadFileDiff(folder: string, path: string, baseRef?: string): Effect.Effect<FileDiff, GitError>;
6
+ export declare function loadBranches(folder: string): Effect.Effect<readonly Branch[], GitError>;
7
+ export declare function loadKnownFiles(folder: string): Effect.Effect<readonly string[], GitError>;
7
8
  //# sourceMappingURL=load-changes.d.ts.map