@mrciphersmith/keryx 0.3.22 → 0.3.25
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 +146346 -144351
- 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.25",
|
|
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/ scripts/",
|
|
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
|
}
|