@mrciphersmith/keryx 0.3.21 → 0.3.24
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/dist/cli.js +146334 -144322
- package/dist/core.js +14745 -14647
- package/package.json +2 -2
- package/src/gdgraph/query.ts +102 -2
- package/src/gdgraph/service.ts +8 -2
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +8 -8
- package/src/gdskills/bundled/skills/review/review-orchestrator/templates/review-report.md +11 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.24",
|
|
4
4
|
"description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"typecheck": "tsc --noEmit",
|
|
46
46
|
"typecheck:scripts": "tsc --project tsconfig.scripts.json --noEmit",
|
|
47
47
|
"test": "bun test",
|
|
48
|
-
"test:core": "bun test src/cli src/core src/shell-source-audits.test.ts src/acp/ src/assets/ src/bundle/ src/capability/ src/commands/ src/contracts/ src/ctx/ src/eval/ src/flow/ src/forgetting/ src/gdgraph/ src/gdskills/ src/governance/ src/health/ src/integrations/ src/job/ src/learning/ src/lib/ src/mcp/ src/memory/ src/metrics/ src/retention/ src/review/ src/rules/ src/sac/ src/security/ src/stack/ src/standard/ src/sync/ src/testing/ src/trigger/ src/wiki/",
|
|
48
|
+
"test:core": "bun test src/cli src/core src/impact-evidence/ src/shell-source-audits.test.ts src/acp/ src/assets/ src/bundle/ src/capability/ src/commands/ src/contracts/ src/ctx/ src/eval/ src/flow/ src/forgetting/ src/gdgraph/ src/gdskills/ src/governance/ src/health/ src/integrations/ src/job/ src/learning/ src/lib/ src/mcp/ src/memory/ src/metrics/ src/retention/ src/review/ src/rules/ src/sac/ src/security/ src/stack/ src/standard/ src/sync/ src/testing/ src/trigger/ src/wiki/",
|
|
49
49
|
"test:client:terminal": "bun test src/tui/ src/commands/shell",
|
|
50
50
|
"test:client:streaming": "bun test src/harness/provider/",
|
|
51
51
|
"test:client:cancel-resume": "bun test src/harness/run/ src/harness/resume/ src/harness/session/ src/session/ src/bus/ src/commands/sessions",
|
package/src/gdgraph/query.ts
CHANGED
|
@@ -72,7 +72,20 @@ export function getFilesDescribedBy(graph: GraphData, pageId: string): string[]
|
|
|
72
72
|
return [...files].sort();
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
/**
|
|
76
|
+
* `roots` (A-8, flow 356): files reached OUT OF BAND — never by any `import`
|
|
77
|
+
* statement this graph's builder can see, so they carry no inbound OR
|
|
78
|
+
* outbound edge and would otherwise report as orphans. `src/lib/
|
|
79
|
+
* test-preload.ts` is the motivating case: `bunfig.toml`'s `[test].preload`
|
|
80
|
+
* loads it before any test module runs, which is a real caller this scan
|
|
81
|
+
* simply cannot see (it is not written as an `import`). Reporting it dead is
|
|
82
|
+
* a false positive, not a finding — `bunfigPreloadRoots` below is the one
|
|
83
|
+
* caller (`commands/gdgraph.ts`, `gdgraph/service.ts`) resolves and passes
|
|
84
|
+
* in; a caller that does not know about a project's `bunfig.toml` (e.g. this
|
|
85
|
+
* module's own unit tests, `forgetting/propagation.ts`) omits it and gets
|
|
86
|
+
* the previous, unfiltered behaviour.
|
|
87
|
+
*/
|
|
88
|
+
export function getOrphans(graph: GraphData, roots: ReadonlySet<string> = new Set()): string[] {
|
|
76
89
|
const inbound = new Set(
|
|
77
90
|
graph.edges.filter((edge) => edge.kind !== "unresolved").map((edge) => edge.to),
|
|
78
91
|
);
|
|
@@ -81,10 +94,97 @@ export function getOrphans(graph: GraphData): string[] {
|
|
|
81
94
|
);
|
|
82
95
|
return graph.nodes
|
|
83
96
|
.map((node) => node.path)
|
|
84
|
-
.filter((file) => !inbound.has(file) && !outbound.has(file))
|
|
97
|
+
.filter((file) => !inbound.has(file) && !outbound.has(file) && !roots.has(file))
|
|
85
98
|
.sort();
|
|
86
99
|
}
|
|
87
100
|
|
|
101
|
+
/**
|
|
102
|
+
* `bunfig.toml`'s declared `preload` entries, resolved to graph-relative
|
|
103
|
+
* paths (forward-slash, relative to `projectRoot` — the same shape
|
|
104
|
+
* `GraphNode.path` uses) — the roots {@link getOrphans} excludes (A-8).
|
|
105
|
+
*
|
|
106
|
+
* Handles the two shapes Bun actually reads a `preload` array from: a
|
|
107
|
+
* top-level `preload = […]` (`bun run`/`bunx`) and a table-scoped one
|
|
108
|
+
* (`[test]\npreload = […]`, this repository's own `bunfig.toml`). Not a
|
|
109
|
+
* general TOML parser — narrow on purpose, one array key, matching this
|
|
110
|
+
* repository's own `mcp-servers/compat.ts` precedent of a small reader over
|
|
111
|
+
* a dependency for one config shape. An absent or unreadable `bunfig.toml`
|
|
112
|
+
* yields no roots rather than an error: most projects this graph runs
|
|
113
|
+
* against have none at all.
|
|
114
|
+
*/
|
|
115
|
+
/**
|
|
116
|
+
* Strip `#`-to-end-of-line comments from `text`, leaving string literals
|
|
117
|
+
* (single- or double-quoted, double-quoted honoring a `\"` escape) alone —
|
|
118
|
+
* a `#` inside a string is content, not a comment start. Review round 1,
|
|
119
|
+
* L3: without this, a commented-out `# preload = […]` line still matched
|
|
120
|
+
* {@link bunfigPreloadRoots}'s array pattern, since that pattern searched
|
|
121
|
+
* the raw text with no comment awareness.
|
|
122
|
+
*/
|
|
123
|
+
function stripTomlComments(text: string): string {
|
|
124
|
+
let result = "";
|
|
125
|
+
let quote: '"' | "'" | undefined;
|
|
126
|
+
for (let i = 0; i < text.length; i++) {
|
|
127
|
+
const ch = text[i];
|
|
128
|
+
if (quote !== undefined) {
|
|
129
|
+
result += ch;
|
|
130
|
+
if (ch === "\\" && quote === '"' && i + 1 < text.length) {
|
|
131
|
+
result += text[i + 1];
|
|
132
|
+
i++;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (ch === quote) {
|
|
136
|
+
quote = undefined;
|
|
137
|
+
}
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (ch === '"' || ch === "'") {
|
|
141
|
+
quote = ch;
|
|
142
|
+
result += ch;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (ch === "#") {
|
|
146
|
+
const newlineIndex = text.indexOf("\n", i);
|
|
147
|
+
if (newlineIndex === -1) {
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
i = newlineIndex - 1;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
result += ch;
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function bunfigPreloadRoots(projectRoot: string): Promise<Set<string>> {
|
|
159
|
+
const roots = new Set<string>();
|
|
160
|
+
let text: string;
|
|
161
|
+
try {
|
|
162
|
+
text = await readFile(path.join(projectRoot, "bunfig.toml"), "utf8");
|
|
163
|
+
} catch {
|
|
164
|
+
return roots;
|
|
165
|
+
}
|
|
166
|
+
// Review round 1, L3: strip `#` comments (outside string literals) before
|
|
167
|
+
// matching, and anchor the key to a line start — a commented-out
|
|
168
|
+
// `# preload = […]` line must never contribute a root, and a key that
|
|
169
|
+
// merely ENDS in "preload" mid-line must never match either.
|
|
170
|
+
const stripped = stripTomlComments(text);
|
|
171
|
+
const arrayPattern = /^[ \t]*preload\s*=\s*\[([^\]]*)\]/gm;
|
|
172
|
+
const stringPattern = /"((?:[^"\\]|\\.)*)"|'([^']*)'/g;
|
|
173
|
+
for (const arrayMatch of stripped.matchAll(arrayPattern)) {
|
|
174
|
+
const body = arrayMatch[1] ?? "";
|
|
175
|
+
for (const stringMatch of body.matchAll(stringPattern)) {
|
|
176
|
+
const raw = stringMatch[1] ?? stringMatch[2] ?? "";
|
|
177
|
+
if (raw.length === 0) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const absolute = path.resolve(projectRoot, raw);
|
|
181
|
+
const relative = path.relative(projectRoot, absolute).split(path.sep).join("/");
|
|
182
|
+
roots.add(relative);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return roots;
|
|
186
|
+
}
|
|
187
|
+
|
|
88
188
|
export function getAffected(
|
|
89
189
|
graph: GraphData,
|
|
90
190
|
target: string,
|
package/src/gdgraph/service.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { buildGraph } from "./build";
|
|
9
9
|
import { computeAffected, type AffectedOptions, type AffectedResult } from "./affected";
|
|
10
10
|
import { loadGdgraphConfig } from "./config";
|
|
11
|
-
import { getCycles, getOrphans, loadGraph } from "./query";
|
|
11
|
+
import { bunfigPreloadRoots, getCycles, getOrphans, loadGraph } from "./query";
|
|
12
12
|
import { writeRepomap, type RepomapOptions, type RepomapResult } from "./repomap";
|
|
13
13
|
import type { GraphData } from "./types";
|
|
14
14
|
|
|
@@ -90,7 +90,13 @@ export function createGdgraphService(): GdgraphService {
|
|
|
90
90
|
|
|
91
91
|
async query(cwd, q) {
|
|
92
92
|
const graph = await loadGraph(cwd);
|
|
93
|
-
|
|
93
|
+
if (q === "cycles") {
|
|
94
|
+
return getCycles(graph);
|
|
95
|
+
}
|
|
96
|
+
// A-8 (flow 356): same roots as `commands/gdgraph.ts` — a `bunfig.toml`
|
|
97
|
+
// `preload` entry is a real caller this graph's scan cannot see.
|
|
98
|
+
const roots = await bunfigPreloadRoots(cwd);
|
|
99
|
+
return getOrphans(graph, roots);
|
|
94
100
|
},
|
|
95
101
|
};
|
|
96
102
|
}
|
|
@@ -1587,10 +1587,12 @@ Fix: <concrete>
|
|
|
1587
1587
|
- <what was checked> — <how>
|
|
1588
1588
|
|
|
1589
1589
|
### How this review was run
|
|
1590
|
-
- **
|
|
1590
|
+
- **Workflow:** `review-orchestrator`
|
|
1591
1591
|
- **Scope:** `<base>..<head>`, round N, PR #N
|
|
1592
|
-
- **
|
|
1593
|
-
-
|
|
1592
|
+
- **Models:** <actual models and reviewers, or not recorded>
|
|
1593
|
+
- **Tools:** <tools actually invoked, or not recorded>
|
|
1594
|
+
- **Skills:** <skills actually used, or not recorded>
|
|
1595
|
+
- **Subagents:** <dispatched reviewers and models, or none when confirmed; otherwise not recorded>
|
|
1594
1596
|
- **Not run:** <reviewer — reason>
|
|
1595
1597
|
- **Verification:** <mode>; confirmed N, refuted N, unverifiable N, unverified N
|
|
1596
1598
|
```
|
|
@@ -1663,17 +1665,15 @@ Automation values, names unchanged:
|
|
|
1663
1665
|
|
|
1664
1666
|
Default is do not publish. No resolvable PR number means skip and say so.
|
|
1665
1667
|
|
|
1666
|
-
The comment is the report rendered from `templates/review-report.md`, English, with the domain file chosen above (`templates/pr-comment-frontend.md` / `templates/pr-comment-backend.md`) — never a summary written freehand. It does not use a tool heading, a finding table, or a meta table. It does not carry a co-author line, a `Generated with` trailer, or any sentence that names a vendor or a product as the author.
|
|
1668
|
+
The comment is the report rendered from `templates/review-report.md`, English, with the domain file chosen above (`templates/pr-comment-frontend.md` / `templates/pr-comment-backend.md`) — never a summary written freehand. It does not use a tool heading, a finding table, or a meta table. It does not carry a co-author line, a `Generated with` trailer, or any sentence that names a vendor or a product as the author. Name the workflow and reviewers that actually ran, with models, tools, skills and subagents. Never identify the operator from `gh auth status`, git config or a credential; do not sign the comment as a person, reviewer or model.
|
|
1667
1669
|
|
|
1668
1670
|
The follow-up file path and the metadata rules (real model names, Run vs Not run, no `adaptive` in the model slot) live in that same template. Write the body to a temp file and post with `gh pr comment <n> --body-file <file>`.
|
|
1669
1671
|
|
|
1670
1672
|
**No GitHub write without an approved draft.** Before ANY write — the comment, a thread reply, `keryx review comments reply`, a review — show the user the rendered body and wait for explicit approval of that body.
|
|
1671
1673
|
Picking A or B above chooses *whether* to publish, not *what*; a comment is public and cannot be unsent. With no user to answer (a dispatched run), do not write: hand the rendered body back to the caller.
|
|
1672
1674
|
|
|
1673
|
-
Re-read head and
|
|
1674
|
-
|
|
1675
|
-
Do not post a finding another reviewer already filed on this head; if yours
|
|
1676
|
-
extends theirs, cite theirs.
|
|
1675
|
+
Re-read head and thread before posting. If head moved, re-check findings
|
|
1676
|
+
and name unchecked commits. Do not duplicate findings; cite extended ones.
|
|
1677
1677
|
|
|
1678
1678
|
## Job Context Awareness
|
|
1679
1679
|
|
|
@@ -95,14 +95,19 @@ Do not list a checklist item that was not checked. Do not praise.
|
|
|
95
95
|
|
|
96
96
|
## How this review was run
|
|
97
97
|
|
|
98
|
-
A
|
|
98
|
+
A factual run inventory, not an author or account signature. List only steps
|
|
99
|
+
confirmed by the run record; never infer an operator identity from `gh auth`,
|
|
100
|
+
git config, a credential or a PR author. Use `not recorded` when evidence for
|
|
101
|
+
a tool, skill or model is unavailable; use `none` only when the run proves none.
|
|
99
102
|
|
|
100
103
|
```markdown
|
|
101
104
|
### How this review was run
|
|
102
|
-
- **
|
|
105
|
+
- **Workflow:** `review-orchestrator`; severity and lanes per the repository review rules, else the canonical rubric in this skill.
|
|
103
106
|
- **Scope:** `<base>..<head>`, round N, PR #N. Unchecked commits: <list or none>.
|
|
104
|
-
- **
|
|
105
|
-
-
|
|
107
|
+
- **Models:** <actual model names and the reviewers that ran on each; or not recorded>.
|
|
108
|
+
- **Tools:** <tools actually invoked for this review and their roles; or not recorded>.
|
|
109
|
+
- **Skills:** <skills actually loaded or executed and their roles; or not recorded>.
|
|
110
|
+
- **Subagents:** <dispatched subagents, their reviewer roles and actual models; or none when confirmed, otherwise not recorded>.
|
|
106
111
|
- **Fallback:** <reviewer> via `general-purpose` because <native agent type unavailable | no bundled agent>. Or `none`.
|
|
107
112
|
- **Not run:** <reviewer> — <short reason>. Or `none`.
|
|
108
113
|
- **Selection:** <auto-detected scope | explicit flags | optional groups the user picked>
|
|
@@ -118,8 +123,8 @@ Model rules, unchanged from the old metadata table:
|
|
|
118
123
|
- Name the model that actually ran. Never write `adaptive`, `inherit`,
|
|
119
124
|
`unsupported`, or `current-session` in the model slot.
|
|
120
125
|
- Group reviewers under the model they ran on. One bullet per model.
|
|
121
|
-
- `Model strategy` (`ask` or `adaptive`) goes
|
|
122
|
-
|
|
126
|
+
- `Model strategy` (`ask` or `adaptive`) goes under Selection, never in
|
|
127
|
+
place of an actual model name.
|
|
123
128
|
- Keep **Run** complete. If Not run is long, group it with counts and notable
|
|
124
129
|
names; the full list belongs in the follow-up file.
|
|
125
130
|
|