@mrciphersmith/keryx 0.3.44 → 0.3.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -8
- package/dist/cli.js +132183 -128470
- package/dist/core.js +919 -516
- package/package.json +1 -1
- package/src/gdskills/bundled/rules/core/mobx-store-template.mdc +12 -2
- package/src/gdskills/bundled/rules/core/rule-management-workflow.mdc +5 -2
- package/src/gdskills/bundled/skills/core/reviewer-skill-creator/SKILL.detail.md +669 -0
- package/src/gdskills/bundled/skills/core/reviewer-skill-creator/SKILL.md +20 -20
- package/src/gdskills/bundled/skills/platform/agent-entrypoint-distiller/SKILL.md +3 -3
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/review/review-frontend/SKILL.md +7 -7
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +208 -4
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +10 -10
- package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-input.schema.json +7 -0
|
@@ -53,28 +53,28 @@ these go.
|
|
|
53
53
|
|
|
54
54
|
## Bulk import of overlay reviewers
|
|
55
55
|
|
|
56
|
-
When the
|
|
57
|
-
|
|
56
|
+
When the reviewers already exist as skill packages in a tree someone else maintains, import
|
|
57
|
+
them instead of recreating them one by one. Dry-run first, then the same command without it:
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
keryx
|
|
61
|
-
|
|
60
|
+
keryx review import --from <overlay-home> --only 'review-acme-*' --dry-run
|
|
61
|
+
keryx review import --from <overlay-home> --only 'review-acme-*'
|
|
62
|
+
keryx review reviewers # every imported name must be listed here
|
|
63
|
+
keryx skills remove review/<name> # undo one
|
|
62
64
|
```
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
the
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
`keryx review import` is the review-shaped spelling of `keryx skills import --module review`:
|
|
67
|
+
same importer, module implied. A tree of several packages is refused, with the candidate list,
|
|
68
|
+
until `--only <glob>` names the ones you want: `review-orchestrator` dispatches every package in
|
|
69
|
+
module `review`, and one with no path gate is dispatched on every round. Import reviewers only —
|
|
70
|
+
an orchestrator or facade, a deprecated alias, a report generator, or anything with no review
|
|
71
|
+
contract does not belong there. One package directory passed as `--from` needs no `--only`.
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
keryx
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Then `keryx review reviewers`. Until that list shows the names, they are not
|
|
77
|
-
wired.
|
|
73
|
+
Read `SKILL.detail.md` beside this file before running an import. It is the worked example, with
|
|
74
|
+
real output: the refusals, what each run writes, how `flags`, `paths`, `pathsSource`,
|
|
75
|
+
`stackRequires`, `unresolvedRules` and `drift` are derived, rule collisions, undo with
|
|
76
|
+
`keryx skills remove`, refresh, the per-clone note, and the `review-learning.config.json` shape.
|
|
77
|
+
Until `keryx review reviewers` shows the names, they are not wired.
|
|
78
78
|
|
|
79
79
|
---
|
|
80
80
|
|
|
@@ -221,8 +221,8 @@ source file differs from what was imported. It does **not** mean the reviewer is
|
|
|
221
221
|
|
|
222
222
|
Re-read the source, diff it against what the skill encodes, and then decide per
|
|
223
223
|
change: fold it in, or record in the skill why this project deliberately differs.
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
For a reviewer written here, re-run Step 2's command with the same `--name` to re-record the hash once it matches the source again.
|
|
225
|
+
For an imported package, `keryx skills update review/<name>` re-reads its Origin and overwrites the SKILL.md — see `SKILL.detail.md`.
|
|
226
226
|
|
|
227
227
|
A deliberate divergence that is written down is a decision. The same divergence
|
|
228
228
|
undocumented is drift that will be silently "fixed" by whoever refreshes next.
|
|
@@ -239,7 +239,7 @@ undocumented is drift that will be silently "fixed" by whoever refreshes next.
|
|
|
239
239
|
| Change a reviewer keryx ships | NO | edit `src/gdskills/bundled/skills/review/` and open a PR |
|
|
240
240
|
| Update a skill from review findings | NO | `entity-skill-learner`, `keryx skills learn` |
|
|
241
241
|
| Decide which reviewers a round dispatches | NO | `review-orchestrator` |
|
|
242
|
-
| Import a tree of overlay reviewers | YES — `keryx
|
|
242
|
+
| Import a tree of overlay reviewers | YES — `keryx review import --from <dir> --only '<glob>'`, dry-run first (`SKILL.detail.md`) | — |
|
|
243
243
|
| Import a non-review SKILL.md / GitHub URL | NO | `entity-skill-creator` / `keryx skills import` |
|
|
244
244
|
|
|
245
245
|
---
|
|
@@ -32,7 +32,7 @@ keryx rules distill
|
|
|
32
32
|
- `.metaproject/rules/entrypoints/index.md`
|
|
33
33
|
- `.metaproject/rules/entrypoints/*.md`
|
|
34
34
|
- `.metaproject/project-skills/entrypoints/*/SKILL.md`
|
|
35
|
-
-
|
|
35
|
+
- the `.metaproject/index.md` block sits where `agentEntrypoints.root` says: `CLAUDE.local.md` and a regenerated `AGENTS.override.md` under scope `local` (the team files are rewritten without it), the team file itself under scope `shared`
|
|
36
36
|
4. If the command changed root entrypoints, check that only global/personal or
|
|
37
37
|
highest-priority always-on instructions remain there.
|
|
38
38
|
5. Run focused verification:
|
|
@@ -51,7 +51,7 @@ Stop and re-read this skill if you are thinking:
|
|
|
51
51
|
| Rationalization | Rebuttal |
|
|
52
52
|
|---|---|
|
|
53
53
|
| "`keryx rules distill` exited 0, so the split is correct." | Exit 0 says the command ran, not that it split well. It happily writes an empty rule, a rule titled after a heading it could not classify, and two rules holding the same paragraph. Open every file listed in step 3 before reporting anything. |
|
|
54
|
-
| "The root entrypoint is short now, so the job is done." | Shortness is a side effect, not the goal.
|
|
54
|
+
| "The root entrypoint is short now, so the job is done." | Shortness is a side effect, not the goal. An entrypoint set from which no file points at `.metaproject/index.md` any more, or a root that lost an always-on instruction into a rule nothing loads, is a regression that looks like success on a line count. |
|
|
55
55
|
| "This section read like a project rule, so I moved it out of the root." | Global, personal and highest-priority always-on instructions stay in the root by design. If you had to guess which kind a section was, it belongs in the ambiguous list for a human — not silently in a rule file. |
|
|
56
56
|
| "The project has no flow, so I can skip verification." | `keryx flow check` is not conditional either — with no flows it reports `All flows are consistent.` and exits 0. `keryx rules sync` runs every time; skipping it ships a rules tree whose index and files disagree. |
|
|
57
57
|
| "`keryx rules distill` rewrote the root files, so I don't need to read them." | The rewrite is exactly what needs checking. Reading the post-distill `AGENTS.md` and `CLAUDE.md` is the only way to see what was carried out of them. |
|
|
@@ -63,7 +63,7 @@ Before reporting, all of these must hold:
|
|
|
63
63
|
- `keryx rules sync` exits 0.
|
|
64
64
|
- `.metaproject/rules/entrypoints/index.md` exists and names every file written under `.metaproject/rules/entrypoints/`.
|
|
65
65
|
- Every generated rule and project-skill file is non-empty and was read, not just listed.
|
|
66
|
-
- `
|
|
66
|
+
- Claude and Codex still reach `.metaproject/index.md`: the managed block is in `CLAUDE.local.md` and `AGENTS.override.md` (scope `local`; Codex `skip` writes none) or in the shared `CLAUDE.md` / `AGENTS.md` — never in both.
|
|
67
67
|
- `keryx flow check` exits 0. It takes no id and checks every flow package in the project, so a distill that corrupted one reports here even though the distill never touched flows.
|
|
68
68
|
- Every section you could not confidently classify appears in the ambiguous list of the Output Contract. An empty ambiguous list on a large entrypoint is a claim, and it must be a true one.
|
|
69
69
|
|
|
@@ -85,7 +85,7 @@ Present diff preview:
|
|
|
85
85
|
- NEVER remove existing entries without explanation
|
|
86
86
|
- NEVER add entries that duplicate what's already there
|
|
87
87
|
- Keep CLAUDE.md files under 100 lines
|
|
88
|
-
- Prefer project-level for project-specific things
|
|
88
|
+
- Prefer project-level for project-specific things; never write inside the `<!-- keryx:index -->` markers (in `CLAUDE.local.md` by default, `CLAUDE.md` under shared scope) — keryx regenerates that block
|
|
89
89
|
|
|
90
90
|
## Red Flags
|
|
91
91
|
|
|
@@ -181,13 +181,13 @@ Flags:
|
|
|
181
181
|
#### A3. useEffect Misuse
|
|
182
182
|
|
|
183
183
|
- [ ] `useEffect` must not contain business logic
|
|
184
|
-
- [ ] `useEffect` must not
|
|
184
|
+
- [ ] `useEffect` must not fetch data or run business logic through the store: no `store.load()` / `store.loadX()`, and no `init()` of a child store that the parent store owns. The lifecycle bridge `useEffect(() => { store.onMount(); return () => store.onUnmount(); }, [store])` is correct and is not flagged (D6) — it hands the mount event to the store, which decides what to load
|
|
185
185
|
- [ ] `useEffect` must not contain API calls or IO
|
|
186
186
|
- [ ] `useEffect` used to synchronize component state with store state is a sign the component should be an `observer` instead
|
|
187
187
|
- [ ] Every `useEffect` that creates a subscription, timer, or listener MUST return a cleanup function
|
|
188
188
|
|
|
189
189
|
Flags:
|
|
190
|
-
- `useEffect` calls `store.
|
|
190
|
+
- `useEffect` calls `store.loadX()`, or a child store's `init()` that the parent store owns — **major** (`store.onMount()` / `store.onUnmount()` in the bridge form is not this)
|
|
191
191
|
- `useEffect` contains `fetch()` or IO — **major** (API in component)
|
|
192
192
|
- `useEffect` contains business logic / state derivation — **major**
|
|
193
193
|
- `useEffect` creates a subscription/timer/listener with no cleanup return — **major** (memory leak)
|
|
@@ -350,13 +350,13 @@ Flags:
|
|
|
350
350
|
|
|
351
351
|
#### B10. Lifecycle Initialization and Disposal
|
|
352
352
|
|
|
353
|
-
- [ ] `init()` / `onMount()` of a child store is called from the **parent store's** `init()` / `onMount()`,
|
|
353
|
+
- [ ] `init()` / `onMount()` of a **child** store is called from the **parent store's** `init()` / `onMount()`, not from a component `useEffect`. The component's own store is different: its `onMount()` / `onUnmount()` are called from the component through the lifecycle bridge (D6), and that is correct
|
|
354
354
|
- [ ] Components must NOT trigger store data loading via `useEffect` (e.g., `useEffect(() => { store.load() }, [])`)
|
|
355
355
|
- [ ] Parent store's `onUnmount()` / `dispose()` MUST call `childStore.dispose()` to prevent stale-state updates
|
|
356
356
|
- [ ] Store `dispose()` MUST set `this.disposed = true` before canceling async work
|
|
357
357
|
|
|
358
358
|
Flags:
|
|
359
|
-
- Component `useEffect` calls `store.loadX()`, `
|
|
359
|
+
- Component `useEffect` calls `store.loadX()`, or a child store's `init()` / `onMount()` that the parent store owns — **major**. Do not flag the bridge `useEffect(() => { store.onMount(); return () => store.onUnmount(); }, [store])`
|
|
360
360
|
- Parent store does not call `child.dispose()` in `onUnmount()` / `dispose()` — **major**
|
|
361
361
|
- Missing `disposed` flag in store with async operations — **major**
|
|
362
362
|
|
|
@@ -468,7 +468,7 @@ const store = useLocalObservable(() => props.store ?? new ComponentStore(props.s
|
|
|
468
468
|
|
|
469
469
|
#### D6. Store Lifecycle Bridge via useEffect
|
|
470
470
|
|
|
471
|
-
|
|
471
|
+
The component tells its store that it mounted and unmounted; the store decides what that means:
|
|
472
472
|
|
|
473
473
|
```typescript
|
|
474
474
|
useEffect(() => {
|
|
@@ -477,11 +477,11 @@ useEffect(() => {
|
|
|
477
477
|
}, [store]);
|
|
478
478
|
```
|
|
479
479
|
|
|
480
|
-
This
|
|
480
|
+
This is the correct lifecycle bridge — do not flag it under A3 or B10. Those rules target a data fetch or business logic placed in the effect, and a component reaching into a child store the parent store owns; they do not target the lifecycle hooks themselves. What `onMount()` does (load, subscribe, initialise child stores) is store code and is reviewed as store code.
|
|
481
481
|
|
|
482
482
|
Distinction:
|
|
483
483
|
- `useEffect(() => { store.onMount(); return () => store.onUnmount(); }, [store])` — correct lifecycle bridge — **do not flag**
|
|
484
|
-
- `useEffect(() => { store.loadData(); }, [])` — data trigger
|
|
484
|
+
- `useEffect(() => { store.loadData(); }, [])` or `useEffect(() => { store.childStore.init(); }, [])` — data trigger, or a child store's lifecycle the parent store owns, run from the component — **major** (A3 / B10, regardless of project conventions)
|
|
485
485
|
|
|
486
486
|
#### D7. JSX and Naming Conventions
|
|
487
487
|
|
|
@@ -14,16 +14,99 @@ them from the reviewer's prose.
|
|
|
14
14
|
|
|
15
15
|
| Field | Use |
|
|
16
16
|
|---|---|
|
|
17
|
-
| `flags` | A flag the user passed that is in this list selects the reviewer
|
|
17
|
+
| `flags` | The reviewer's selection flags: `metadata.flags` from its frontmatter when it has at least one non-empty entry — then it takes precedence, even when none of its entries turns out to be a flag or it holds only `--all`, and the reviewer has no flags — otherwise the flags its description names. An empty `metadata.flags` (`[]`, `''`) leaves the description's flags in force. Already normalised (lower-case, `--` prefixed), so compare a passed flag with them as they are. A flag the user passed that is in this list selects the reviewer. Whether that selection is also exempt from the path gate depends on `familyFlags`. `--all` selects every project reviewer. |
|
|
18
|
+
| `flagWarnings` | One line per `metadata.flags` entry that was dropped because it is not a flag after normalising, plus one more when that left the reviewer with no flags. A list of only `--all` leaves no flags and no warning. Empty in the ordinary case. A dropped entry selects nothing: do not match a passed flag against the raw spelling in the reviewer's frontmatter. Dispatch the reviewer as its other fields say, and name the warnings once in the report — a reviewer its author meant to be flag-selectable is not. Never a finding against the code. |
|
|
19
|
+
| `familyFlags` | The subset of `flags` that at least one other project reviewer also carries. A passed flag listed here is a **family flag**: it selects this reviewer, and the reviewer stays path-gated. A passed flag that is in `flags` and not here is unique to this reviewer: the selection is **explicit** and never path-gated, like any flag-selected bundled reviewer. |
|
|
18
20
|
| `paths` | Its path triggers for the **path gate**. No file in scope A matches → `Skipped reviewers`, reason `no-matching-paths`. |
|
|
19
|
-
| `pathsSource` | `metadata` (declared `metadata.paths`) or `description` (globs read out of its description). `none` means there is nothing to gate on: **dispatch it** — ambiguity includes. |
|
|
21
|
+
| `pathsSource` | `metadata` (declared `metadata.paths`) or `description` (globs read out of its description, and the literal file paths such as `src/utils/column-zone.ts` listed beside them; a cited `.md` / `.mdc` document and a `.metaproject/` path are not triggers). A description with no glob gives `none` even when it names a file: a literal path counts only beside a glob. `none` means there is nothing to gate on: **dispatch it** — ambiguity includes. |
|
|
20
22
|
| `stackRequires` | Apply stack scoping exactly as for a bundled reviewer carrying `metadata.stack_requires`. |
|
|
21
|
-
| `unresolvedRules` | Rules it cites that the project does not have. Dispatch it anyway, tell it in the prompt which rules are absent, and name them once in the report with the fix (`keryx review import --from <overlay
|
|
23
|
+
| `unresolvedRules` | Rules it cites that the project does not have. Dispatch it anyway, tell it in the prompt which rules are absent, and name them once in the report with the fix (`keryx review import --from <overlay> --only '<glob>'` copies them). Never a finding against the code. |
|
|
24
|
+
| `shadowedRules` | `[{ ref, resolved }]`: rules it cites by one path and must read from another. The project holds its own version of the rule at `resolved` — the reference's slot under `.metaproject/rules/project/`, the whole reference kept (`core/x.mdc` → `.metaproject/rules/project/core/x.mdc`) — which answers the reference before the file `ref` names. In the dispatch prompt, tell the reviewer to read `resolved` in place of `ref` — see "Dispatching a project reviewer" below. Not a defect and not reported as one. |
|
|
25
|
+
| `unresolvedReferences` | `[{ ref, reason }]`: other references in its text that will not hold. `missing` — a backticked `skills/…` or `rules/…` path ending `.md` / `.json` with no file under `.metaproject/`; `non-portable` — a rule cited by an absolute or `~` path, true on one machine at most. Dispatch it anyway, tell it in the prompt which references are absent, and name them once in the report. Never a finding against the code. |
|
|
26
|
+
|
|
27
|
+
`metadata.paths`, `metadata.flags` and `metadata.stack_requires` are read by
|
|
28
|
+
one frontmatter reader that supports a YAML subset (BOM and CRLF accepted,
|
|
29
|
+
trailing `# comments` dropped, `paths` and `flags` as a flow, block or
|
|
30
|
+
comma-separated list, `stack_requires` as a string, only keys directly under
|
|
31
|
+
`metadata:`); a shape outside it reads as not declared.
|
|
32
|
+
`reviewer-skill-creator`'s `SKILL.detail.md` lists the subset. The inventory
|
|
33
|
+
has already applied it — use the fields, not the raw frontmatter.
|
|
22
34
|
|
|
23
35
|
A description saying another entry point dispatches it ("Dispatched by
|
|
24
36
|
vantage-review …") is its author's routing note, not a restriction: this
|
|
25
37
|
orchestrator dispatches it through the fields above.
|
|
26
38
|
|
|
39
|
+
### Family flags — why a shared flag does not lift the path gate
|
|
40
|
+
|
|
41
|
+
An overlay usually gives every reviewer it ships the same umbrella flag beside
|
|
42
|
+
the reviewer's own: eight reviewers all carrying `--acme`, each also carrying
|
|
43
|
+
`--acme-styling`, `--acme-testing` and so on. Read as explicit, `--acme` would
|
|
44
|
+
dispatch all eight against a diff that touches one of their path sets — the
|
|
45
|
+
waste the path gate exists to stop, brought back by a flag.
|
|
46
|
+
|
|
47
|
+
So the two cases are told apart by how many project reviewers carry the flag,
|
|
48
|
+
and the inventory reports the answer per reviewer in `familyFlags` rather than
|
|
49
|
+
leaving the orchestrator to count:
|
|
50
|
+
|
|
51
|
+
| Passed flag | In this reviewer's `familyFlags`? | Result |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| `--acme` | yes (shared) | selected, then path-gated on its `paths` |
|
|
54
|
+
| `--acme-styling` | no (unique to it) | selected explicitly, not path-gated |
|
|
55
|
+
| both | — | the unique flag wins: not path-gated |
|
|
56
|
+
|
|
57
|
+
A family member gated out goes to `Skipped reviewers` with reason
|
|
58
|
+
`no-matching-paths` like any other gated reviewer. `pathsSource: none` still
|
|
59
|
+
means dispatch. An inventory that has no `familyFlags` field comes from a keryx
|
|
60
|
+
that predates it: treat every passed flag as explicit, which is the earlier
|
|
61
|
+
behaviour, and say so once in the report.
|
|
62
|
+
|
|
63
|
+
### Dispatching a project reviewer — by its package, not by its name
|
|
64
|
+
|
|
65
|
+
Each `project` entry carries `path`, the directory its package is registered
|
|
66
|
+
at. Dispatch it with the absolute path of `<path>/SKILL.md` as `skill_path` in
|
|
67
|
+
the reviewer input, and state in the prompt that this file is the reviewer's
|
|
68
|
+
definition and overrides the built-in definition of any agent type with the
|
|
69
|
+
same name. `skill_path` in the reviewer input is set for project reviewers
|
|
70
|
+
only, and it carries that override. A bundled reviewer's input never has it:
|
|
71
|
+
when one runs through the `general-purpose` fallback, the prompt text names its
|
|
72
|
+
skill file and `review_context.review_plan.dispatch_plan` records the path, but nothing is
|
|
73
|
+
being overridden, so the input field stays out. This holds on every branch of "Agent Runtime Compatibility" in
|
|
74
|
+
SKILL.md: when a same-named agent type exists, dispatch it **with** the path
|
|
75
|
+
and the override sentence; when none exists, dispatch `general-purpose` with
|
|
76
|
+
the same two things.
|
|
77
|
+
|
|
78
|
+
The reason is a name collision nobody chose. A runtime can ship an agent type
|
|
79
|
+
whose name equals a project reviewer's — an earlier copy of the same reviewer,
|
|
80
|
+
or an unrelated one. Dispatched by name alone, the round runs that built-in
|
|
81
|
+
prompt and files its findings under the project reviewer's name, and nothing in
|
|
82
|
+
the report shows the project's own text was never read.
|
|
83
|
+
|
|
84
|
+
If `<path>/SKILL.md` does not exist, the reviewer is `BLOCKED`: record the
|
|
85
|
+
missing path, continue with the independent reviewers, and do not run the
|
|
86
|
+
same-named agent type or any other reviewer in its place. A substituted
|
|
87
|
+
reviewer reads as coverage the round did not have.
|
|
88
|
+
|
|
89
|
+
**Shadowed rules go into the same prompt.** For every entry in the reviewer's
|
|
90
|
+
`shadowedRules`, add one line to the dispatch: "where your text names `<ref>`,
|
|
91
|
+
read `<absolute path of resolved>` instead" — for a reviewer citing
|
|
92
|
+
`core/error-handling.mdc`, that is
|
|
93
|
+
`<project root>/.metaproject/rules/project/core/error-handling.mdc`. Take the
|
|
94
|
+
path from `resolved`; do not build it from the rule's filename, because the slot
|
|
95
|
+
keeps the reference's directory and a file lying directly in `rules/project/`
|
|
96
|
+
answers only a literal `project/<name>.mdc` citation. An import keeps an
|
|
97
|
+
overlay's rule in that slot whenever a file already answers the reference with
|
|
98
|
+
other content, or the name is one keryx also ships under `rules/core`, because
|
|
99
|
+
`keryx init`, `keryx update` and the legacy-profile `keryx skills install`
|
|
100
|
+
overwrite `rules/core` with keryx's own rules (the manifest form with
|
|
101
|
+
`--target keryx-shell` skips an existing file it did not record unless
|
|
102
|
+
`--force`; see "Skills present only when installed"). The reviewer's text was written against the overlay and
|
|
103
|
+
still says `core/<name>.mdc`, so followed literally it reads keryx's generic
|
|
104
|
+
rule of the same name and reviews against the wrong standard — silently, since
|
|
105
|
+
both files exist. The inventory computes the project-first order and reports
|
|
106
|
+
it; no loader applies it. This line in the dispatch is the only place it takes
|
|
107
|
+
effect, and a project reviewer run outside this orchestrator reads what its
|
|
108
|
+
text names.
|
|
109
|
+
|
|
27
110
|
### `drift` — the source moved, the reviewer did not
|
|
28
111
|
|
|
29
112
|
A project reviewer built from an external file — a rules file, a review profile,
|
|
@@ -32,7 +115,7 @@ import. `keryx review reviewers` re-reads the source and reports:
|
|
|
32
115
|
|
|
33
116
|
| `drift` | Meaning | What to do this round |
|
|
34
117
|
|---|---|---|
|
|
35
|
-
| `none` | No
|
|
118
|
+
| `none` | No origin recorded; written here. The text row reads `(no recorded origin)` | Nothing |
|
|
36
119
|
| `clean` | Source matches the import | Nothing |
|
|
37
120
|
| `changed` | Source has moved on since import | Dispatch it, and say so in the report |
|
|
38
121
|
| `missing` | Source can no longer be read | Dispatch it, and say so in the report |
|
|
@@ -44,6 +127,127 @@ suppressing it would trade real coverage for tidiness. Record the drift in
|
|
|
44
127
|
profile is due a re-read. Never file it as a finding against the code under
|
|
45
128
|
review: it is a fact about the review, not about the diff.
|
|
46
129
|
|
|
130
|
+
## Skills present only when installed
|
|
131
|
+
|
|
132
|
+
SKILL.md names five skills that the `minimal` and `recommended` profiles do not
|
|
133
|
+
install — they are in the `full` profile only (`src/gdskills/catalog.ts`):
|
|
134
|
+
|
|
135
|
+
| Skill | Reached through |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `review-pr-feedback` | "A round never answers a pull request another skill is answering" |
|
|
138
|
+
| `code-ai-review` | `--code-ai`, `--legacy-profiles` |
|
|
139
|
+
| `code-learned-review` | `--learned`, `--legacy-profiles` |
|
|
140
|
+
| `code-style-review` | `--code-style`, `--legacy-profiles` |
|
|
141
|
+
| `code-mobx-store-review` | `--mobx-store`, `--legacy-profiles` |
|
|
142
|
+
|
|
143
|
+
Whether one is **present** is read from `keryx review reviewers --json`, and
|
|
144
|
+
the answer depends on the inventory's `bundledSource`, which says which tree
|
|
145
|
+
the `bundled` half was read from. Do not infer presence from the flag table,
|
|
146
|
+
which describes what keryx ships, not what this project has.
|
|
147
|
+
|
|
148
|
+
| `bundledSource` | What the `bundled` half is | Is a listed skill installed here? |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `"project"` | this project's `.metaproject/skills/gdskills/review` | yes; one that is not listed is not installed |
|
|
151
|
+
| `"package"` | the review skills the keryx package itself ships | the list cannot say |
|
|
152
|
+
| `"not-found"` | empty — neither tree could be located (the command exits 1) | the list cannot say |
|
|
153
|
+
|
|
154
|
+
`"package"` means the project has no `.metaproject/skills/gdskills/review`
|
|
155
|
+
directory at all — a worktree or clone whose `.metaproject/` holds only data,
|
|
156
|
+
for instance. The inventory then lists every review skill in the package, all
|
|
157
|
+
five above included, whatever profile the project would install, and each
|
|
158
|
+
entry's `path` is relative to the keryx package (`src/gdskills/bundled/…`), not
|
|
159
|
+
to the project. Nothing is installed in this project in that state, so a name
|
|
160
|
+
being in the list is not evidence that it is, and a name missing from it is not
|
|
161
|
+
what `not-installed` means either. Decide each of the five by what can actually
|
|
162
|
+
be dispatched: if the runtime has an agent type of that exact name ("Agent
|
|
163
|
+
Runtime Compatibility" in SKILL.md), the skill is usable and is dispatched as
|
|
164
|
+
any other reviewer; if it does not, record `not-installed` with the command
|
|
165
|
+
below, because the project holds no skill file to hand a `general-purpose`
|
|
166
|
+
agent. Say once in the report that the bundled half came from the package, so
|
|
167
|
+
nobody reads the round's reviewer list as this project's install.
|
|
168
|
+
|
|
169
|
+
`"not-found"` is a failed inventory, not an empty install: report it and do not
|
|
170
|
+
record any reviewer as `not-installed` on its strength.
|
|
171
|
+
|
|
172
|
+
When one is absent:
|
|
173
|
+
|
|
174
|
+
- **A legacy reviewer a flag asked for** is not dispatched and not replaced.
|
|
175
|
+
Record it in `Skipped reviewers` with reason `not-installed` and the command
|
|
176
|
+
below. `--legacy-profiles` and `--all` dispatch the legacy reviewers that are
|
|
177
|
+
installed and record the others the same way. Leave an absent reviewer out of
|
|
178
|
+
the `Optional legacy/profile reviewers` preview: offering a flag that cannot
|
|
179
|
+
run is worse than not offering it.
|
|
180
|
+
- **`review-pr-feedback`** changes nothing about a round. No caller can declare
|
|
181
|
+
that it owns a pull request's reply, so this orchestrator owns the reply as it
|
|
182
|
+
does by default. If the user wants existing pull-request comments interpreted
|
|
183
|
+
and answered, say the skill is not installed and give the command; do not
|
|
184
|
+
improvise its workflow from this file.
|
|
185
|
+
|
|
186
|
+
Adding one. The commands are `keryx skills install` in its two forms
|
|
187
|
+
(`src/commands/skills.ts`):
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# everything the full profile holds, including all five
|
|
191
|
+
keryx skills install --profile full
|
|
192
|
+
|
|
193
|
+
# manifest installer: the generic review module only — review-pr-feedback,
|
|
194
|
+
# code-ai-review, code-learned-review and code-style-review among its skills
|
|
195
|
+
keryx skills install --profile core --target keryx-shell
|
|
196
|
+
|
|
197
|
+
# manifest installer: add the MobX component to a profile — code-mobx-store-review
|
|
198
|
+
keryx skills install --profile <manifest-profile> --with capability:mobx --target keryx-shell
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
What those flags do and do not reach:
|
|
202
|
+
|
|
203
|
+
- `--target keryx-shell` is what writes under `.metaproject/skills/gdskills/`,
|
|
204
|
+
where the inventory looks. Without it the manifest installer targets `claude`
|
|
205
|
+
and writes `.claude/skills/`.
|
|
206
|
+
- `--with <id>` is repeatable and takes a **component** id from
|
|
207
|
+
`install-manifest.json` (`capability:mobx`, `framework:react`, …). It has no
|
|
208
|
+
single-skill form: the smallest unit the manifest installs is a module.
|
|
209
|
+
`review-pr-feedback` and the three `code-*` profile reviewers live in module
|
|
210
|
+
`review-core-skills`, which belongs to no component and arrives through a
|
|
211
|
+
profile that lists it (`core`, `full`).
|
|
212
|
+
- Add `--dry-run` first to read the plan. A file already on disk that the
|
|
213
|
+
manifest installer did not write is left alone unless `--force` is passed.
|
|
214
|
+
|
|
215
|
+
What to expect from the exit code:
|
|
216
|
+
|
|
217
|
+
- **The manifest-installer forms can exit 1 after installing what was missing.**
|
|
218
|
+
`applyInstall` (`src/gdskills/manifest/apply.ts`) writes every planned file
|
|
219
|
+
whose destination is free, skips every planned file that already exists and
|
|
220
|
+
is not recorded in the manifest installer's install-state (or has drifted from
|
|
221
|
+
its recorded hash), and returns `ok: false` when it skipped anything;
|
|
222
|
+
`src/commands/skills.ts` turns that into exit 1. The legacy installer, which
|
|
223
|
+
`keryx init` uses, records no such state, so on a project it set up the output
|
|
224
|
+
is a `Wrote N file(s)` line for the missing skills, then a `Skipped (existing
|
|
225
|
+
file not recorded in install-state…)` list naming the skills already there,
|
|
226
|
+
then exit 1. Running the command again gives the same skip list and the same
|
|
227
|
+
exit code.
|
|
228
|
+
- **Judge the outcome by the inventory, not the exit code.** Run
|
|
229
|
+
`keryx review reviewers --json`: with `bundledSource: "project"`, a skill in
|
|
230
|
+
the `bundled` half is installed. (`"package"` after an install means the
|
|
231
|
+
install did not write under `.metaproject/skills/gdskills/review` — check
|
|
232
|
+
`--target`.) If it is there, continue. If it is not, read the `Skipped` and `Errors` lines
|
|
233
|
+
for the skill's own path before doing anything else. `--force` makes the
|
|
234
|
+
installer overwrite the skipped files with the bundled copies, local edits
|
|
235
|
+
included, so it is the user's call, not a default retry.
|
|
236
|
+
- **`keryx skills install --profile full` does not behave this way.** With no
|
|
237
|
+
manifest flag it takes the legacy route (`installGdskills`,
|
|
238
|
+
`src/gdskills/install.ts`), which copies each bundled skill directory over the
|
|
239
|
+
installed one without consulting install-state, and rewrites the project's
|
|
240
|
+
skills catalog and gdskills module manifest for the `full` profile. It sets
|
|
241
|
+
exit 1 only when `.metaproject/` is absent; a destination it will not write
|
|
242
|
+
through (a symlink, a non-directory) is listed under `Warnings` with exit 0.
|
|
243
|
+
- **The dry-run's `Apply this plan:` hint is not the whole command.** It prints
|
|
244
|
+
only `--profile` and `--target`, so after a dry-run with
|
|
245
|
+
`--with capability:mobx` the hinted command installs the profile without
|
|
246
|
+
`code-mobx-store-review`. Re-type the command you dry-ran, minus `--dry-run`.
|
|
247
|
+
|
|
248
|
+
Run `keryx review reviewers --json` again afterwards and dispatch from what it
|
|
249
|
+
returns.
|
|
250
|
+
|
|
47
251
|
## CLI-engine reviewers — dispatched as a command, not a sub-agent
|
|
48
252
|
|
|
49
253
|
`review-jev-rules` (flow 330), `review-jev-risk` and `review-jev-scenarios`
|
|
@@ -383,7 +383,7 @@ human or a bot has already reviewed pull request `#A`, and someone wants those
|
|
|
383
383
|
comments interpreted, checked against the code, fixed and answered. Under its
|
|
384
384
|
`--fix` mode it dispatches `flow-orchestrator`, which opens a **second** pull
|
|
385
385
|
request `#B` carrying the fix, based on `#A`'s own head branch, and dispatches
|
|
386
|
-
**this** orchestrator on every round against `#B`.
|
|
386
|
+
**this** orchestrator on every round against `#B`. `review-pr-feedback` is present only when installed — it is in the `full` profile only. When it is absent no caller of this kind exists, so the default below holds and this orchestrator owns the reply; do not hand a request to it, say it is not installed and name the install command (`SKILL.detail.md`, "Skills present only when installed").
|
|
387
387
|
|
|
388
388
|
`#B` is its own conversation. Collect and reply on it exactly as always: someone
|
|
389
389
|
reviewing the fix deserves an answer from the run that made the fix, and its
|
|
@@ -1000,7 +1000,7 @@ Three bounds keep it from deleting coverage:
|
|
|
1000
1000
|
|
|
1001
1001
|
- **Only path-triggered reviewers.** A reviewer selected by an explicit flag, or
|
|
1002
1002
|
one whose scope is the whole change rather than a path set — `review-logic`,
|
|
1003
|
-
`review-regression`, `review-verifier` — is never path-gated.
|
|
1003
|
+
`review-regression`, `review-verifier` — is never path-gated. One exception, for project reviewers only: a flag that more than one project reviewer carries is a **family flag** (the inventory lists it in that reviewer's `familyFlags`). It selects the whole family, and each member stays path-gated — `--<family>` over a diff touching one member's paths dispatches that member, not all of them.
|
|
1004
1004
|
- **Scope A only.** Scope B is the blast radius; it is *supposed* to name files the
|
|
1005
1005
|
diff never touched.
|
|
1006
1006
|
- **Ambiguity includes.** If you cannot decide whether a path matches, dispatch.
|
|
@@ -1024,7 +1024,7 @@ keryx review reviewers --json
|
|
|
1024
1024
|
|
|
1025
1025
|
It returns two halves. `bundled` is the installed gdskills review tree — the set
|
|
1026
1026
|
this project's install profile actually produced, which is not everything keryx
|
|
1027
|
-
ships. `project` is every project-skill under module `review`, living at
|
|
1027
|
+
ships — when `bundledSource` is `"project"`; when it is `"package"` the project has no installed review tree and the list is the keryx package's own (`SKILL.detail.md`, "Skills present only when installed"). `project` is every project-skill under module `review`, living at
|
|
1028
1028
|
`.metaproject/project-skills/review/<name>/` so that it sits beside
|
|
1029
1029
|
`.metaproject/skills/gdskills/review/<name>/` and needs no other marking.
|
|
1030
1030
|
|
|
@@ -1044,10 +1044,10 @@ Two things it does NOT get:
|
|
|
1044
1044
|
the origin.
|
|
1045
1045
|
|
|
1046
1046
|
**Select them with the fields the inventory returns, not by reading prose.**
|
|
1047
|
-
Each `project` entry carries `flags` (a passed flag selects it
|
|
1047
|
+
Each `project` entry carries `flags` (a passed flag selects it) and `familyFlags` (the subset of `flags` another project reviewer also carries: a passed flag listed there selects it but leaves it path-gated; a passed flag in `flags` and not in `familyFlags` is unique to it, so it is explicit and ungated), `flagWarnings` (declared `metadata.flags` entries that were dropped as not being flags: they select nothing; name them once in the report),
|
|
1048
1048
|
`paths` + `pathsSource` (the path gate; `none` → dispatch), `stackRequires`
|
|
1049
1049
|
(stack scoping), `unresolvedRules` (dispatch, name the missing rules once in the
|
|
1050
|
-
report) and `drift` (dispatch, name it once in the report). What each means and
|
|
1050
|
+
report), `shadowedRules` (the dispatch prompt names each `resolved` file as the one to read in place of the `ref` the reviewer's own text spells), `unresolvedReferences` (dispatch, name them once in the report) and `drift` (dispatch, name it once in the report). What each means and
|
|
1051
1051
|
what to do with it: `SKILL.detail.md` beside this file, section "Project-local
|
|
1052
1052
|
reviewers". A description saying another entry point dispatches it is its
|
|
1053
1053
|
author's routing note, not a restriction.
|
|
@@ -1085,7 +1085,7 @@ When `review.jev.select` is on, before Wave A/B dispatch: run `keryx review jev-
|
|
|
1085
1085
|
|
|
1086
1086
|
## Legacy/Profile Reviewer Auto-Detection
|
|
1087
1087
|
|
|
1088
|
-
Legacy/profile reviewers are specialized review profiles that predate the review-domain `review-*` naming. They are still valid and must be shown separately from generic and convention reviewers so the user can opt in deliberately.
|
|
1088
|
+
Legacy/profile reviewers are specialized review profiles that predate the review-domain `review-*` naming. They are still valid and must be shown separately from generic and convention reviewers so the user can opt in deliberately. **They are present only when installed:** `code-ai-review`, `code-learned-review`, `code-style-review` and `code-mobx-store-review` are in the `full` profile only, so in any other profile the flags below (`--code-ai`, `--learned`, `--code-style`, `--mobx-store`, `--legacy-profiles`) name reviewers that may not exist. When the inventory's `bundledSource` is `"project"`, a legacy reviewer missing from the `bundled` half of `keryx review reviewers --json` is absent (when it is `"package"` the half is the keryx package's own list and says nothing about this project — decide as `SKILL.detail.md`, "Skills present only when installed", describes): leave it out of the `Optional legacy/profile reviewers` preview, and when a flag asked for it, do not dispatch a substitute — record it in `Skipped reviewers` with reason `not-installed` and the command that adds it (`keryx skills install --profile full`, or the narrower manifest form in `SKILL.detail.md`, "Skills present only when installed"). `--legacy-profiles` and `--all` dispatch the legacy reviewers that are installed and skip the rest the same way.
|
|
1089
1089
|
|
|
1090
1090
|
| Trigger | Reviewers appended |
|
|
1091
1091
|
|---|---|
|
|
@@ -1289,18 +1289,18 @@ Before dispatching a reviewer through a platform-native sub-agent mechanism, ver
|
|
|
1289
1289
|
|
|
1290
1290
|
Runtime rules:
|
|
1291
1291
|
- If the exact reviewer agent type exists, dispatch that reviewer directly.
|
|
1292
|
-
- If the exact reviewer agent type does not exist but `skills/<reviewer>/SKILL.md` exists, dispatch `general-purpose` and include the reviewer name, skill
|
|
1292
|
+
- If the exact reviewer agent type does not exist but `skills/<reviewer>/SKILL.md` exists, dispatch `general-purpose` and include the reviewer name, the path of that skill file, bounded review context, and required `REVIEW_RESULT` schema in the prompt. For a bundled reviewer that path goes in the prompt text and in `dispatch_plan`, not in the input's `skill_path` field, which is for project reviewers only (the project-reviewer rule below).
|
|
1293
1293
|
- If neither the agent type nor the skill file exists, do not silently substitute another reviewer. Mark that reviewer as `BLOCKED`, include the missing agent/skill name, and continue only with independent reviewers.
|
|
1294
|
+
- **A project reviewer is dispatched by its package, on every branch above.** For each entry in the `project` half of `keryx review reviewers --json`, put the absolute path of its registered `<path>/SKILL.md` in the dispatch (`skill_path`) and say in the prompt that this file is the reviewer's definition and overrides the built-in definition of any agent type with the same name. A runtime may ship an agent type called exactly what the project called its reviewer, carrying an older or unrelated prompt; without the path the round runs that prompt and reports it under the project reviewer's name. If the file is missing, mark the reviewer `BLOCKED` and name the missing path — do not fall back to the same-named agent type or to any other reviewer. For every entry in that reviewer's `shadowedRules`, the prompt also says which file to read: "where your text names `<ref>`, read `<resolved>` instead" (the absolute path of `resolved`). The project keeps its own version of that rule beside the one keryx ships under the same name, the reviewer's text still spells the shipped path, and nothing but this sentence in the dispatch makes it read the project's — see `SKILL.detail.md`, "Dispatching a project reviewer".
|
|
1294
1295
|
- Record the chosen runtime per reviewer in `review_context.review_plan.dispatch_plan`.
|
|
1295
|
-
- The user-facing progress line must be explicit: "Running `<reviewer>` via `general-purpose` fallback because native agent type is unavailable."
|
|
1296
|
-
|
|
1297
|
-
Do not use vague fallback messages such as "running through available agent types" without naming which reviewers used fallback and why.
|
|
1296
|
+
- The user-facing progress line must be explicit: "Running `<reviewer>` via `general-purpose` fallback because native agent type is unavailable." Do not use vague fallback messages such as "running through available agent types" without naming which reviewers used fallback and why.
|
|
1298
1297
|
|
|
1299
1298
|
Pass each sub-reviewer a payload matching `.metaproject/skills/gdskills/review/review-orchestrator/reviewer-input.schema.json`:
|
|
1300
1299
|
|
|
1301
1300
|
```yaml
|
|
1302
1301
|
review_context: <bounded context pack>
|
|
1303
1302
|
reviewer: <skill-name>
|
|
1303
|
+
skill_path: <absolute path of <path>/SKILL.md — project reviewers only; omit for a bundled reviewer>
|
|
1304
1304
|
scope_mode: diff | path
|
|
1305
1305
|
context_doc: <path or empty>
|
|
1306
1306
|
issue_url: <url or empty>
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
"reviewer": {
|
|
18
18
|
"type": "string"
|
|
19
19
|
},
|
|
20
|
+
"skill_path": {
|
|
21
|
+
"type": [
|
|
22
|
+
"string",
|
|
23
|
+
"null"
|
|
24
|
+
],
|
|
25
|
+
"description": "Absolute path of the reviewer's registered `<path>/SKILL.md`, set for every reviewer from the `project` half of `keryx review reviewers --json` and omitted for a bundled one. The file is the reviewer's definition: it overrides the built-in definition of a same-named agent type, whichever runtime branch dispatches it. A path that does not exist makes the reviewer BLOCKED, never substituted. Optional, and like the rest of this schema it is read by the dispatching agent — no keryx command validates a dispatch against it."
|
|
26
|
+
},
|
|
20
27
|
"scope_mode": {
|
|
21
28
|
"type": "string",
|
|
22
29
|
"enum": [
|