leyline-code 0.8.1__py3-none-any.whl

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 (65) hide show
  1. leyline/__init__.py +3 -0
  2. leyline/adapters/__init__.py +19 -0
  3. leyline/adapters/csharp.py +1026 -0
  4. leyline/adapters/generic.py +844 -0
  5. leyline/adapters/python.py +929 -0
  6. leyline/adapters/typescript.py +1512 -0
  7. leyline/affected.py +509 -0
  8. leyline/agent_skills.py +311 -0
  9. leyline/change.py +500 -0
  10. leyline/channels.py +1782 -0
  11. leyline/cli.py +1029 -0
  12. leyline/cluster.py +149 -0
  13. leyline/context.py +531 -0
  14. leyline/coupling.py +485 -0
  15. leyline/coverage.py +376 -0
  16. leyline/diagrams.py +680 -0
  17. leyline/diff.py +1078 -0
  18. leyline/drift.py +747 -0
  19. leyline/exact.py +410 -0
  20. leyline/explain.py +973 -0
  21. leyline/export.py +673 -0
  22. leyline/fncoupling.py +328 -0
  23. leyline/formats.py +923 -0
  24. leyline/grade.py +207 -0
  25. leyline/incremental.py +931 -0
  26. leyline/indexer.py +3733 -0
  27. leyline/learnings.py +743 -0
  28. leyline/loop.py +584 -0
  29. leyline/model.py +142 -0
  30. leyline/outline.py +1066 -0
  31. leyline/packing.py +86 -0
  32. leyline/patterns.py +478 -0
  33. leyline/pr.py +1249 -0
  34. leyline/props.py +255 -0
  35. leyline/query.py +683 -0
  36. leyline/quick.py +853 -0
  37. leyline/related.py +266 -0
  38. leyline/removal.py +231 -0
  39. leyline/renames.py +231 -0
  40. leyline/rereview.py +375 -0
  41. leyline/roslyn_refs/Program.cs +179 -0
  42. leyline/roslyn_refs/RoslynRefs.csproj +16 -0
  43. leyline/rules.py +171 -0
  44. leyline/schema.sql +178 -0
  45. leyline/scip_pb2.py +97 -0
  46. leyline/server.py +1230 -0
  47. leyline/skills/leyline-adversarial-review/SKILL.md +121 -0
  48. leyline/skills/leyline-ask/SKILL.md +57 -0
  49. leyline/skills/leyline-change-impact/SKILL.md +79 -0
  50. leyline/skills/leyline-explain-flow/SKILL.md +70 -0
  51. leyline/skills/leyline-explore-module/SKILL.md +55 -0
  52. leyline/skills/leyline-pr-review/SKILL.md +77 -0
  53. leyline/skills/leyline-quick-change/SKILL.md +63 -0
  54. leyline/skills/leyline-spec/SKILL.md +179 -0
  55. leyline/skills/leyline-tour/SKILL.md +46 -0
  56. leyline/spec.py +1819 -0
  57. leyline/store.py +560 -0
  58. leyline/tours.py +352 -0
  59. leyline/verdicts.py +390 -0
  60. leyline/viewer/viewer.html +1622 -0
  61. leyline_code-0.8.1.dist-info/METADATA +1325 -0
  62. leyline_code-0.8.1.dist-info/RECORD +65 -0
  63. leyline_code-0.8.1.dist-info/WHEEL +4 -0
  64. leyline_code-0.8.1.dist-info/entry_points.txt +2 -0
  65. leyline_code-0.8.1.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,179 @@
1
+ ---
2
+ name: leyline-spec
3
+ description: Turn a change someone wants into an OpenSpec change folder checked against a Leyline map, and verify it after it is implemented. Use when asked to plan, spec, scope or design a change to a mapped codebase, or to check that an implemented change matches its spec.
4
+ ---
5
+
6
+ # Spec a change with Leyline
7
+
8
+ The person decides the design. You write it down in a form that can be checked, and Leyline checks
9
+ it against the code. The person should finish able to say three things in a sentence each: what
10
+ code will be written, what it will affect, and how they will know it was done.
11
+
12
+ For a change of a function or two and a constant, with no design to agree (a value, a one-line fix), skip the folder:
13
+ use `quick` (`leyline quick "<what>" --about <names>` before, `--done quick-<slug>` after), and write a spec only if its
14
+ answer says the change grew. The leyline-quick-change skill says how. Code someone else already wrote, with no spec, is
15
+ reviewed with the leyline-pr-review skill.
16
+
17
+ It works with the Leyline MCP server or with the `leyline` command alone. Without the server, use the
18
+ command for each tool this skill names:
19
+
20
+ | MCP tool | Command |
21
+ | --- | --- |
22
+ | `map` | `leyline map <repo>` |
23
+ | `plan` (with `test_output`) | `<test command> \| leyline plan <id> --tests -` (`--new-baseline` for `new_baseline`) |
24
+ | `check` (with `test_output`) | `<test command> \| leyline check <id> --tests -` |
25
+ | `overview`, `search`, `expand`, `impact`, `source` | `leyline overview`, `leyline search <text>`, `leyline expand <id>`, `leyline impact <name>`, `leyline source <id>` |
26
+ | `spec_review_facts` | `leyline spec facts <id> --reviewer logic` (or `performance`) |
27
+ | `spec_finding` | `leyline spec finding <id> --reviewer logic --severity medium --claim "..." --evidence <node id> --proposal "..."` |
28
+ | `spec_findings`, `spec_resolve` | `leyline spec findings <id>`, `leyline spec resolve <finding id> accepted\|rejected\|deferred "why"` |
29
+ | `check` (with `coverage_path`) | `... \| leyline check <id> --tests - --coverage .coverage` (per-test coverage: `pytest --cov --cov-context=test`) |
30
+ | `affected_tests` | `leyline affected-tests <id>`: the tests to run for the change, as a command |
31
+ | `drift` (`accept`) | `leyline drift` (`--accept`) |
32
+
33
+ Where this skill says a result field (`next`, `status.blocking`, `page`), the command prints the same
34
+ thing: the `Next:` line, the "Before implementation" list, and the plan itself. If neither the server nor
35
+ the command is there, say so and stop.
36
+
37
+ The path has three Leyline calls, the same three commands the person can type:
38
+
39
+ 1. `map`, only if the code was never mapped (`overview` returns no repos).
40
+ 2. You write the spec, then `plan`; review; the person decides; `plan` again until nothing blocks.
41
+ 3. Someone implements the tasks, then `check`.
42
+
43
+ `plan` and `check` re-map changed code on their own and return `next`. Follow it, or tell the person
44
+ why not.
45
+
46
+ ## Before any code: write the spec
47
+
48
+ 1. **Hear the change.** Restate it in two sentences. If it could mean two designs, ask which.
49
+ 2. **Look before writing.** `overview`, `search`, `expand`, `impact` on what the change touches (the
50
+ leyline-ask skill, for a question about how the code works now).
51
+ Tell the person anything that makes the change bigger or different than they said. Do this before
52
+ drafting: a spec written first and checked second anchors on the wrong shape.
53
+ 3. **Write the OpenSpec folder** at `openspec/changes/<kebab-id>/`:
54
+ - `proposal.md`: `# Change: <title>`, then `## Why`, `## What Changes`, `## Impact`.
55
+ - `specs/<capability>/spec.md`: `## ADDED Requirements` (or MODIFIED, REMOVED), each
56
+ `### Requirement: <name>` with at least one `#### Scenario: <name>` (four `#`) and
57
+ `- **WHEN** ...` / `- **THEN** ...` lines.
58
+ - `tasks.md`: `- [ ] 1.1 <task>` lines.
59
+ - `design.md` only when there is a real decision to record.
60
+ 4. **Follow three conventions**, because the check depends on them:
61
+ - Name code in backticks in tasks: `` `Vehicle.Speed` ``, `` `Queue.Push` ``. New code
62
+ states its home: `` `Owner.NewName` `` for a member; `` `module.new_func` ``,
63
+ `` `path/to/file.py: new_func` `` or `` `new_func` in `file.py` `` for a top-level function. A task
64
+ that names no code (docs) is left for the person to check; it does not block.
65
+ - Start each task with what it does: add, remove, rename, change the signature of. One task, one
66
+ thing a person could tick. The code right after the verb (and any joined to it by "and" or a
67
+ comma) is what the task changes; other code in the sentence, such as `` `SimConfig.QueueSpeed` `` in
68
+ "counting vehicles slower than ...", is context the plan shows as "mentions" and does not count.
69
+ - Name each scenario so a test can carry the same name. Add a task to write that test, quoting
70
+ the scenario's name: `- [ ] 3.1 Add the test "<scenario name>"`.
71
+ - A scenario that states an invariant ("for any amount, the balance is never negative"; always,
72
+ never, for every) gets a property test, not one example: the plan marks it "invariant: a property
73
+ test fits" and names the library. Name the test like the scenario. The shape, for "Balance is
74
+ never negative":
75
+ ```python
76
+ @given(st.integers(min_value=0), st.integers()) # Hypothesis
77
+ def test_balance_is_never_negative(balance, amount): assert apply(balance, amount) >= 0
78
+ ```
79
+ ```ts
80
+ it("Balance is never negative", () => // fast-check
81
+ fc.assert(fc.property(fc.nat(), fc.integer(), (b, a) => apply(b, a) >= 0)));
82
+ ```
83
+ ```csharp
84
+ [Property] public bool Balance_is_never_negative(NonNegativeInt b, int a) => Apply(b.Get, a) >= 0; // FsCheck
85
+ ```
86
+ When it fails, `check` shows the shrunk counterexample: "contradicted: fails for amount=-1".
87
+ - "Remove `X`" is done when `X` is gone and nothing still calls it; "Rename `X` to `Y`" when `X`
88
+ is gone, `Y` is there and nothing still calls `X`. Name the callers' edits in tasks too.
89
+ 5. **Record the tests as they pass now**, before anyone edits code: run the test suite and pass its
90
+ output to `plan` as `test_output` (TAP from vitest or node --test, `pytest -rA`, `go test -v`, `jest
91
+ --verbose`, `dotnet test --logger "console;verbosity=detailed"`, or one PASS or FAIL line per test; several
92
+ runners' output can go in one text), or the
93
+ results as `test_results`. Without it, `check` cannot tell a test the change broke from one that
94
+ already failed. If you cannot run the tests, say so and give the person the command
95
+ (`<test command> | leyline plan <id> --tests -`).
96
+ 6. **Call `plan`** with the folder. Read `status.blocking`. Fix the spec, not the tool: an ambiguous name
97
+ gets its owner, a must-edit with no task gets a task or a sentence in the proposal saying why not,
98
+ a scenario with no test gets a task. Call it again until `status.blocking` is empty or each
99
+ remaining item is a choice the person made.
100
+ Then read the lines under "Must agree with the change" (other ends of a channel the change crosses,
101
+ such as a second program that reads the same pipe) and "Shares a caller or a field with the change,
102
+ and no task names it". Each is code that shares a channel, a caller, a field or a look-alike new
103
+ member with the change. For each, either propose a task (see "Proposing a missing task") or be able
104
+ to say why it is right to leave alone. Do not pass this list to the person undigested.
105
+ "Usually changes with the files the tasks touch" comes from git history, not the map: a doc, schema,
106
+ fixture or test-case folder that changed in most past commits to a file the tasks touch. Treat it the
107
+ same way: a task, or a reason it is not needed this time.
108
+ 7. **Show the person `leyline.md`** (also returned as `page`). It is one page. Do not summarise it
109
+ into something longer. Say which lines you are least sure of.
110
+ 8. **Get it reviewed** (the `leyline-adversarial-review` skill, once for logic and once for
111
+ performance) before anyone implements. The person decides each finding; then call `plan` again.
112
+
113
+ ## After implementation: check
114
+
115
+ 1. Run the tests and call `check` with the folder and their output (`test_output` or `test_results`).
116
+ It re-maps the code itself.
117
+ 2. Report `done_as_agreed` first. Then each task not done, each scenario not proven, and each edit
118
+ outside the spec. For every edit outside the spec, read its source and say whether the spec was
119
+ incomplete or the implementation wandered. The person decides which; do not decide for them. Propose
120
+ the task, or the undo, as in "Proposing a missing task".
121
+ Each task and scenario carries a `verdict` (proven, partial, contradicted, inconclusive, needs a
122
+ person) and `verdict_why`; quote them as they are, and name every item that needs a person.
123
+ 3. Pass on `next`.
124
+
125
+ A check that finds the change done as agreed records what each code name in it means now, in
126
+ `openspec/leyline-anchors.json`. Commit that file with the spec. Later, `drift` says which names in the
127
+ living specs and finished changes have gone, moved or changed signature; `plan` lists those the new
128
+ change touches under "Specs that no longer match code this change touches". Update each such spec with
129
+ the change. Call `drift` with `accept=true` only when the person says the specs and the code agree.
130
+
131
+ `spec_brief`, `spec_verify` and `record_test_run` are the steps inside `plan` and `check`, for when one
132
+ is needed alone. `plan` stores its test run as `before:spec-<id>` and `check` as `after:spec-<id>`.
133
+
134
+ ## Proposing a missing task
135
+
136
+ When `check` lists an edit outside the spec, or `plan` lists a line under "Shares a caller or a field with the
137
+ change, and no task names it" that needs a task:
138
+
139
+ 1. Draft the exact line to add to `tasks.md`, numbered after the tasks around it, in the conventions above: it
140
+ starts with add, remove, rename or "change the signature of" when that is what it does, and names the code in
141
+ backticks with its owner: `` - [ ] 2.4 Change the signature of `Queue.push` to take a priority ``.
142
+ 2. Show the person the line and its evidence: the node, what was edited or what it shares with the change, and
143
+ the source you read. For an edit outside the spec, also propose the other way out: undo the edit, and say what
144
+ undoing it loses.
145
+ 3. Add the line only when the person agrees. If they choose the undo, undo that edit and nothing else.
146
+ 4. Call `plan` again, and `check` again once the code is written.
147
+
148
+ ## OpenSpec's verify
149
+
150
+ If the project uses OpenSpec's expanded profile and `/opsx:verify` is there, you may run it after `check`. Its
151
+ findings are an LLM's opinion, not a verdict. File each CRITICAL or WARNING as a finding with `spec_finding`, so
152
+ it is tracked and the person resolves it like any other:
153
+
154
+ - reviewer `logic` (Leyline has only logic and performance);
155
+ - severity high for CRITICAL, medium for WARNING;
156
+ - the claim in one sentence, starting "/opsx:verify:";
157
+ - as evidence, the node ids it is about (`search`, `expand`). A finding needs at least one node on the map; one
158
+ you cannot tie to a node goes in your report to the person instead, saying so.
159
+
160
+ The two verdicts are separate. Report `check`'s `done_as_agreed` as it is, whatever `/opsx:verify` says, and do
161
+ not drop a `/opsx:verify` finding because `check` passed.
162
+
163
+ ## When the spec changes part-way
164
+
165
+ Edit the spec and call `plan` again. Once the code has changed it answers `baseline: kept`: the
166
+ picture of the code from the first plan stays, so `check` still compares with the code as it was
167
+ before any edit, and test output passed to `plan` then is refused as a baseline. Use `new_baseline`
168
+ only to abandon what was done and start over.
169
+
170
+ A new function that only code named in the spec calls is reported as a helper, not as an edit outside
171
+ the spec. Read it anyway.
172
+
173
+ ## Rules
174
+
175
+ - Never tick a task or call a change done from the agent's own report. `check` reads the code.
176
+ - Never add a task or tick one without the person.
177
+ - A scenario with no test is unproven, however obvious it looks.
178
+ - Keep the spec short. If the brief does not fit on a page, the change is two changes.
179
+ - You do not resolve review findings. The person does.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: leyline-tour
3
+ description: Write a guided tour of a codebase on a Leyline map, or walk someone through an existing tour. Use when asked to onboard someone, explain how a feature works end to end, or say what to read before changing a part of the code.
4
+ ---
5
+
6
+ # Write a tour with Leyline
7
+
8
+ A tour is an ordered list of stops on the map. Each stop points at one thing (a module, a type, a
9
+ function, a flow, a pattern, a saved view) and says what to notice there. Use this when the user
10
+ wants to learn a codebase or a slice of it, or wants something to hand to another person. It needs
11
+ the Leyline MCP server connected. If the `tours` tool is missing, say so and stop.
12
+
13
+ Leyline already writes an orientation tour on every index. Read it first (`tours`, then `tour`):
14
+ often the user wants that, or a narrower tour that starts where it ends. To explain one flow in the
15
+ conversation, with nothing saved, use the leyline-explain-flow skill; for what one module holds, leyline-explore-module.
16
+
17
+ ## Steps
18
+
19
+ 1. **Settle who it is for and what they will do next.** "New to the repo" and "about to change the
20
+ scoring code" need different tours. If the request does not say, ask one question.
21
+ 2. **Find the spine.** A tour follows something: the path of one request, the life of one piece of
22
+ data, or the dependency order of a few modules. Use `overview`, `flows` with `through`, `trace`
23
+ and `patterns` to find it. Pick one spine. Do not mix two.
24
+ 3. **Choose 5 to 9 stops.** For each, call `expand` and read `source`. Only point at code you read.
25
+ Prefer a function to its type and a type to its module: the narrower the stop, the more the
26
+ reader sees. Use a `flow` stop when order matters and a `pattern` stop when a structure does.
27
+ 4. **Write each narrative** in two to four sentences: what this is, why the tour stops here, and
28
+ what to carry to the next stop. Say what you inferred and what the graph states. Name the thing
29
+ the reader should look at in the source panel (a field, a branch, a call).
30
+ 5. **Order them the way you would explain it aloud.** Start where the reader already has footing,
31
+ usually an entry point or a test. End with what to do next or where the edges of the tour are.
32
+ 6. **Call `save_tour`** with a title that says what the tour covers, the audience, and the stops as
33
+ `{"title", "kind", "ref", "narrative"}`. Check `missing` in the result: a stop whose id does not
34
+ exist was dropped.
35
+ 7. **Tell the user** the tour is under the Tour tab after a refresh, and give the stop titles in
36
+ order so they can redirect you before reading it.
37
+
38
+ ## Rules
39
+
40
+ - A tour is a reading order, not a summary. If a stop has nothing to look at, cut it.
41
+ - Do not restate the orientation tour. Link to where it left off.
42
+ - State blind spots: if the path crosses a process, an event or a file, say the link is not in the
43
+ type system and name both sides.
44
+ - Before saving, check every sentence against the source you read. A wrong sentence in a tour does
45
+ more harm than a missing stop, because the reader trusts the order.
46
+ - Keep sentences plain. No stop needs more than four.