@mrciphersmith/keryx 0.2.112 → 0.2.113
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 +202 -13
- package/dist/core.js +22 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -17108,6 +17108,7 @@ async function collectGraphWikiCandidates(cwd, generatedAt, limit, onlyModules)
|
|
|
17108
17108
|
const fileOut = new Map;
|
|
17109
17109
|
const moduleDeps = new Map;
|
|
17110
17110
|
const moduleDependents = new Map;
|
|
17111
|
+
const excludedTestEdges = new Map;
|
|
17111
17112
|
let edges = 0;
|
|
17112
17113
|
let imports = 0;
|
|
17113
17114
|
let unresolved = 0;
|
|
@@ -17128,6 +17129,11 @@ async function collectGraphWikiCandidates(cwd, generatedAt, limit, onlyModules)
|
|
|
17128
17129
|
const fromModule = fileToModule.get(from) ?? moduleNameFromProjectPath(from);
|
|
17129
17130
|
const toModule = fileToModule.get(to) ?? moduleNameFromProjectPath(to);
|
|
17130
17131
|
if (fromModule && toModule && fromModule !== toModule) {
|
|
17132
|
+
if (isTestSourceFile(from)) {
|
|
17133
|
+
recordExcludedTestEdge(excludedTestEdges, fromModule, from);
|
|
17134
|
+
recordExcludedTestEdge(excludedTestEdges, toModule, from);
|
|
17135
|
+
continue;
|
|
17136
|
+
}
|
|
17131
17137
|
bumpNestedCount(moduleDeps, fromModule, toModule);
|
|
17132
17138
|
bumpNestedCount(moduleDependents, toModule, fromModule);
|
|
17133
17139
|
}
|
|
@@ -17187,6 +17193,12 @@ async function collectGraphWikiCandidates(cwd, generatedAt, limit, onlyModules)
|
|
|
17187
17193
|
pushRef("Key files", keyFiles.map((entry) => "- `" + entry.file + "` - imported by " + entry.incoming + ", imports " + entry.outgoing));
|
|
17188
17194
|
pushRef("Depends on", deps.map(([target, count]) => "- `" + target + "` - " + count + " import(s)"));
|
|
17189
17195
|
pushRef("Depended on by", dependents.map(([source, count]) => "- `" + source + "` - " + count + " import(s)"));
|
|
17196
|
+
const dropped = excludedTestEdges.get(moduleName);
|
|
17197
|
+
if (dropped) {
|
|
17198
|
+
pushRef("Dependency basis", [
|
|
17199
|
+
"- Production imports only: " + dropped.count + " import(s) from test file(s) (e.g. `" + dropped.example + "`) excluded from the two sections above in both directions."
|
|
17200
|
+
]);
|
|
17201
|
+
}
|
|
17190
17202
|
pushRef("Entry points", entryFiles.map((file) => "- `" + file + "`"));
|
|
17191
17203
|
if (readme) {
|
|
17192
17204
|
pushRef("Module README", [readme]);
|
|
@@ -17229,6 +17241,13 @@ function bumpNestedCount(map, key, inner) {
|
|
|
17229
17241
|
bucket.set(inner, (bucket.get(inner) ?? 0) + 1);
|
|
17230
17242
|
map.set(key, bucket);
|
|
17231
17243
|
}
|
|
17244
|
+
function isTestSourceFile(filePath) {
|
|
17245
|
+
return TEST_SOURCE_FILE.test(filePath);
|
|
17246
|
+
}
|
|
17247
|
+
function recordExcludedTestEdge(map, moduleName, testFile) {
|
|
17248
|
+
const current = map.get(moduleName);
|
|
17249
|
+
map.set(moduleName, { count: (current?.count ?? 0) + 1, example: current?.example ?? testFile });
|
|
17250
|
+
}
|
|
17232
17251
|
function sumCounts(bucket) {
|
|
17233
17252
|
if (!bucket) {
|
|
17234
17253
|
return 0;
|
|
@@ -17832,7 +17851,7 @@ function compareSemver(a, b) {
|
|
|
17832
17851
|
}
|
|
17833
17852
|
return 0;
|
|
17834
17853
|
}
|
|
17835
|
-
var EXTERNAL_LINK, DEFAULT_COLLECT_LIMIT = 500, MIN_MODULE_FILES = 2, DEFAULT_EVIDENCE_BUDGET_TOKENS = 4000, DEFAULT_EVIDENCE_MAX_ITEMS = 8, WIKI_PAGE_PRODUCERS;
|
|
17854
|
+
var EXTERNAL_LINK, DEFAULT_COLLECT_LIMIT = 500, MIN_MODULE_FILES = 2, DEFAULT_EVIDENCE_BUDGET_TOKENS = 4000, DEFAULT_EVIDENCE_MAX_ITEMS = 8, WIKI_PAGE_PRODUCERS, TEST_SOURCE_FILE;
|
|
17836
17855
|
var init_service3 = __esm(() => {
|
|
17837
17856
|
init_fs();
|
|
17838
17857
|
init_guard2();
|
|
@@ -17853,6 +17872,7 @@ var init_service3 = __esm(() => {
|
|
|
17853
17872
|
path: /^decisions\/sac-[a-z]+-[0-9a-f]+\.md$/
|
|
17854
17873
|
}
|
|
17855
17874
|
];
|
|
17875
|
+
TEST_SOURCE_FILE = /(?:^|\/)[^/]*\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
17856
17876
|
});
|
|
17857
17877
|
|
|
17858
17878
|
// src/gdgraph/wiki-layer.ts
|
|
@@ -22072,10 +22092,13 @@ function isPermissionMode(value) {
|
|
|
22072
22092
|
return PERMISSION_MODES.includes(value);
|
|
22073
22093
|
}
|
|
22074
22094
|
function resolveApprovalDecision(input) {
|
|
22075
|
-
const { mode, risk, destructive, credentials, sacReviewConfirmation } = input;
|
|
22095
|
+
const { mode, risk, destructive, credentials, sacReviewConfirmation, readOnly } = input;
|
|
22076
22096
|
if (risk === "read") {
|
|
22077
22097
|
return "auto";
|
|
22078
22098
|
}
|
|
22099
|
+
if (readOnly) {
|
|
22100
|
+
return "deny";
|
|
22101
|
+
}
|
|
22079
22102
|
if (credentials || sacReviewConfirmation) {
|
|
22080
22103
|
return "ask";
|
|
22081
22104
|
}
|
|
@@ -28191,7 +28214,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
28191
28214
|
continue;
|
|
28192
28215
|
}
|
|
28193
28216
|
executedAny = true;
|
|
28194
|
-
const result = concurrentSpawnResults?.get(call.id) ?? await executeCall(call, toolByName, io.requestApproval, io.permissionMode, io.onAutoApproved, hasInvocationCapacity, reserveInvocation, invocationBudget.maxCalls, signal);
|
|
28217
|
+
const result = concurrentSpawnResults?.get(call.id) ?? await executeCall(call, toolByName, io.requestApproval, io.permissionMode, io.readOnly, io.onAutoApproved, hasInvocationCapacity, reserveInvocation, invocationBudget.maxCalls, signal);
|
|
28195
28218
|
io.onToolResult?.(call.name, result);
|
|
28196
28219
|
const modelOutput = redactSensitiveText(result.output);
|
|
28197
28220
|
const untrusted = result.untrusted === true;
|
|
@@ -28411,7 +28434,7 @@ async function runConcurrentSpawnBatch(spawnCalls, toolByName, io, deps, hasInvo
|
|
|
28411
28434
|
dependsOn: [],
|
|
28412
28435
|
budgetRequest: { reservationId: call.id, maxRuntimeMs: perTaskRuntimeMs }
|
|
28413
28436
|
}));
|
|
28414
|
-
const runOne = (call) => executeCall(call, toolByName, io.requestApproval, io.permissionMode, io.onAutoApproved, hasInvocationCapacity, reserveInvocation);
|
|
28437
|
+
const runOne = (call) => executeCall(call, toolByName, io.requestApproval, io.permissionMode, io.readOnly, io.onAutoApproved, hasInvocationCapacity, reserveInvocation);
|
|
28415
28438
|
const plan = planWaves(tasks, {
|
|
28416
28439
|
maxConcurrency,
|
|
28417
28440
|
parentRemaining: { maxRuntimeMs: perTaskRuntimeMs * tasks.length }
|
|
@@ -28465,7 +28488,7 @@ async function runConcurrentSpawnBatch(spawnCalls, toolByName, io, deps, hasInvo
|
|
|
28465
28488
|
return results;
|
|
28466
28489
|
}
|
|
28467
28490
|
}
|
|
28468
|
-
async function executeCall(call, toolByName, requestApproval, permissionMode, onAutoApproved, hasInvocationCapacity, reserveInvocation, maxToolCalls, signal) {
|
|
28491
|
+
async function executeCall(call, toolByName, requestApproval, permissionMode, readOnly, onAutoApproved, hasInvocationCapacity, reserveInvocation, maxToolCalls, signal) {
|
|
28469
28492
|
const tool = toolByName.get(call.name);
|
|
28470
28493
|
if (tool === undefined) {
|
|
28471
28494
|
return { output: `unknown tool: ${call.name}`, isError: true };
|
|
@@ -28484,12 +28507,23 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, on
|
|
|
28484
28507
|
}
|
|
28485
28508
|
const risk = tool.definition.risk;
|
|
28486
28509
|
const mode = permissionMode?.() ?? DEFAULT_PERMISSION_MODE;
|
|
28510
|
+
const isReadOnly = readOnly?.() ?? false;
|
|
28487
28511
|
if (risk === "shell" || risk === "destructive") {
|
|
28488
28512
|
const command = typeof input.command === "string" ? input.command : "";
|
|
28489
28513
|
const destructive = risk === "destructive" || isDestructiveCommand(command);
|
|
28490
28514
|
const credentials = touchesAgentCredentials(command);
|
|
28491
28515
|
const sacReviewConfirmation = touchesSacConfirmReview(command);
|
|
28492
|
-
const decision = resolveApprovalDecision({
|
|
28516
|
+
const decision = resolveApprovalDecision({
|
|
28517
|
+
mode,
|
|
28518
|
+
risk,
|
|
28519
|
+
destructive,
|
|
28520
|
+
credentials,
|
|
28521
|
+
sacReviewConfirmation,
|
|
28522
|
+
readOnly: isReadOnly
|
|
28523
|
+
});
|
|
28524
|
+
if (decision === "deny") {
|
|
28525
|
+
return { output: `tool "${call.name}" is not permitted while read-only mode (/plan) is on`, isError: true };
|
|
28526
|
+
}
|
|
28493
28527
|
if (decision === "auto") {
|
|
28494
28528
|
onAutoApproved?.(call.name, call.input, { destructive, credentials });
|
|
28495
28529
|
} else {
|
|
@@ -28504,7 +28538,17 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, on
|
|
|
28504
28538
|
}
|
|
28505
28539
|
}
|
|
28506
28540
|
} else if (risk === "delegate") {
|
|
28507
|
-
const decision = resolveApprovalDecision({
|
|
28541
|
+
const decision = resolveApprovalDecision({
|
|
28542
|
+
mode,
|
|
28543
|
+
risk,
|
|
28544
|
+
destructive: false,
|
|
28545
|
+
credentials: false,
|
|
28546
|
+
sacReviewConfirmation: false,
|
|
28547
|
+
readOnly: isReadOnly
|
|
28548
|
+
});
|
|
28549
|
+
if (decision === "deny") {
|
|
28550
|
+
return { output: `tool "${call.name}" is not permitted while read-only mode (/plan) is on`, isError: true };
|
|
28551
|
+
}
|
|
28508
28552
|
if (decision === "auto") {
|
|
28509
28553
|
onAutoApproved?.(call.name, call.input, { destructive: false, credentials: false });
|
|
28510
28554
|
} else {
|
|
@@ -28517,7 +28561,17 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, on
|
|
|
28517
28561
|
} else if (risk === "write") {
|
|
28518
28562
|
const patch = typeof input.patch === "string" ? input.patch : "";
|
|
28519
28563
|
const { destructive, credentials } = classifyPatchRisk(patch);
|
|
28520
|
-
const decision = resolveApprovalDecision({
|
|
28564
|
+
const decision = resolveApprovalDecision({
|
|
28565
|
+
mode,
|
|
28566
|
+
risk,
|
|
28567
|
+
destructive,
|
|
28568
|
+
credentials,
|
|
28569
|
+
sacReviewConfirmation: false,
|
|
28570
|
+
readOnly: isReadOnly
|
|
28571
|
+
});
|
|
28572
|
+
if (decision === "deny") {
|
|
28573
|
+
return { output: `tool "${call.name}" is not permitted while read-only mode (/plan) is on`, isError: true };
|
|
28574
|
+
}
|
|
28521
28575
|
if (decision === "auto") {
|
|
28522
28576
|
onAutoApproved?.(call.name, call.input, { destructive, credentials });
|
|
28523
28577
|
} else {
|
|
@@ -28937,7 +28991,7 @@ var init_quarantine = __esm(() => {
|
|
|
28937
28991
|
},
|
|
28938
28992
|
{
|
|
28939
28993
|
name: "permission-config",
|
|
28940
|
-
test: (t) => /\b(allowed[- ]?tools|disallowed[- ]?tools|permission[- ]?mode|bypass[- ]?permissions|dangerously[- ]?skip)\b/i.test(t)
|
|
28994
|
+
test: (t) => /\b(allowed[- ]?tools|disallowed[- ]?tools|permission[- ]?mode|bypass[- ]?permissions|dangerously[- ]?skip|readonly|read[- ]only[- ]?mode)\b|\/plan\b/i.test(t)
|
|
28941
28995
|
}
|
|
28942
28996
|
];
|
|
28943
28997
|
});
|
|
@@ -48777,7 +48831,7 @@ import path47 from "path";
|
|
|
48777
48831
|
// package.json
|
|
48778
48832
|
var package_default = {
|
|
48779
48833
|
name: "@mrciphersmith/keryx",
|
|
48780
|
-
version: "0.2.
|
|
48834
|
+
version: "0.2.113",
|
|
48781
48835
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
48782
48836
|
private: false,
|
|
48783
48837
|
publishConfig: {
|
|
@@ -88889,6 +88943,8 @@ function classifyBusyDispatch(params) {
|
|
|
88889
88943
|
return "copy";
|
|
88890
88944
|
if (commandName === "/mode")
|
|
88891
88945
|
return "mode";
|
|
88946
|
+
if (commandName === "/plan")
|
|
88947
|
+
return "plan";
|
|
88892
88948
|
if (commandName === "/demote")
|
|
88893
88949
|
return "demote";
|
|
88894
88950
|
if (commandName === "/game")
|
|
@@ -88911,6 +88967,67 @@ function classifyBusyDispatch(params) {
|
|
|
88911
88967
|
return "not-a-command";
|
|
88912
88968
|
}
|
|
88913
88969
|
|
|
88970
|
+
// src/tui/boot-animation.ts
|
|
88971
|
+
var DEFAULT_BOOT_DURATION_MS = 350;
|
|
88972
|
+
var WORDMARK = ["========================", " K E R Y X ", "========================"];
|
|
88973
|
+
var STEPS = ["Reading .metaproject index", "Connecting provider", "Warming graph"];
|
|
88974
|
+
async function playBootAnimation(otui, r, opts) {
|
|
88975
|
+
if (process.env.KERYX_SKIP_BOOT === "1" || opts.skip === true) {
|
|
88976
|
+
return;
|
|
88977
|
+
}
|
|
88978
|
+
const durationMs = opts.durationMs ?? DEFAULT_BOOT_DURATION_MS;
|
|
88979
|
+
const theme = getTheme();
|
|
88980
|
+
const box = new otui.BoxRenderable(r, {
|
|
88981
|
+
id: "boot-animation",
|
|
88982
|
+
position: "absolute",
|
|
88983
|
+
top: 0,
|
|
88984
|
+
left: 0,
|
|
88985
|
+
width: "100%",
|
|
88986
|
+
height: "100%",
|
|
88987
|
+
backgroundColor: theme.bg,
|
|
88988
|
+
flexDirection: "column",
|
|
88989
|
+
alignItems: "center",
|
|
88990
|
+
justifyContent: "center"
|
|
88991
|
+
});
|
|
88992
|
+
r.root.add(box);
|
|
88993
|
+
const logoBox = new otui.BoxRenderable(r, { id: "boot-logo-box", flexDirection: "column", alignItems: "center" });
|
|
88994
|
+
box.add(logoBox);
|
|
88995
|
+
for (const [index, line] of WORDMARK.entries()) {
|
|
88996
|
+
logoBox.add(new otui.TextRenderable(r, { id: `boot-logo-${index}`, content: otui.t`${otui.bold(line)}` }));
|
|
88997
|
+
}
|
|
88998
|
+
const statusText = new otui.TextRenderable(r, {
|
|
88999
|
+
id: "boot-status",
|
|
89000
|
+
content: otui.t`${otui.dim(STEPS[0] ?? "")}`,
|
|
89001
|
+
marginTop: 1
|
|
89002
|
+
});
|
|
89003
|
+
box.add(statusText);
|
|
89004
|
+
box.add(new otui.TextRenderable(r, { id: "boot-hint", content: otui.t`${otui.dim("any key to skip")}`, marginTop: 1 }));
|
|
89005
|
+
await new Promise((resolve) => {
|
|
89006
|
+
let settled = false;
|
|
89007
|
+
let stepIndex = 0;
|
|
89008
|
+
const stepTimer = setInterval(() => {
|
|
89009
|
+
stepIndex = Math.min(stepIndex + 1, STEPS.length - 1);
|
|
89010
|
+
const step = STEPS[stepIndex];
|
|
89011
|
+
if (step !== undefined) {
|
|
89012
|
+
statusText.content = otui.t`${otui.dim(step)}`;
|
|
89013
|
+
}
|
|
89014
|
+
}, Math.max(1, Math.floor(durationMs / STEPS.length)));
|
|
89015
|
+
const finish = () => {
|
|
89016
|
+
if (settled) {
|
|
89017
|
+
return;
|
|
89018
|
+
}
|
|
89019
|
+
settled = true;
|
|
89020
|
+
clearInterval(stepTimer);
|
|
89021
|
+
clearTimeout(doneTimer);
|
|
89022
|
+
unsubscribe();
|
|
89023
|
+
r.root.remove(box);
|
|
89024
|
+
resolve();
|
|
89025
|
+
};
|
|
89026
|
+
const doneTimer = setTimeout(finish, durationMs);
|
|
89027
|
+
const unsubscribe = opts.onKeypress(() => finish());
|
|
89028
|
+
});
|
|
89029
|
+
}
|
|
89030
|
+
|
|
88914
89031
|
// src/tui/workspace-inspector.ts
|
|
88915
89032
|
var WORKSPACE_COMMAND = "/workspace";
|
|
88916
89033
|
var WORKSPACE_FOOTER = [
|
|
@@ -91033,6 +91150,11 @@ var AGENT_SLASH_COMMANDS = [
|
|
|
91033
91150
|
description: "Show or switch the permission mode \u2014 /mode [ask|trust|auto]",
|
|
91034
91151
|
modes: AGENT_ONLY
|
|
91035
91152
|
},
|
|
91153
|
+
{
|
|
91154
|
+
name: "/plan",
|
|
91155
|
+
description: "Toggle read-only mode \u2014 /plan [on|off]",
|
|
91156
|
+
modes: AGENT_ONLY
|
|
91157
|
+
},
|
|
91036
91158
|
{ name: "/clear", description: "New session (alias of /new)", modes: BOTH },
|
|
91037
91159
|
{ name: "/interrupt", description: "Interrupt the running main agent turn", modes: AGENT_ONLY },
|
|
91038
91160
|
{
|
|
@@ -93115,8 +93237,15 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
93115
93237
|
paddingTop: 1
|
|
93116
93238
|
});
|
|
93117
93239
|
rootRow.add(sidebar);
|
|
93118
|
-
const
|
|
93119
|
-
|
|
93240
|
+
const sidebarTopScroll = new otui.ScrollBoxRenderable(r, {
|
|
93241
|
+
id: "sb-top",
|
|
93242
|
+
flexShrink: 1,
|
|
93243
|
+
minHeight: 0,
|
|
93244
|
+
scrollY: true,
|
|
93245
|
+
contentOptions: { flexDirection: "column" }
|
|
93246
|
+
});
|
|
93247
|
+
sidebar.add(sidebarTopScroll);
|
|
93248
|
+
const sidebarTop = sidebarTopScroll.content;
|
|
93120
93249
|
if (opts.versionCheck !== undefined) {
|
|
93121
93250
|
const versionNotice = new otui.TextRenderable(r, {
|
|
93122
93251
|
id: `sb-version-${uid++}`,
|
|
@@ -93620,6 +93749,7 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
93620
93749
|
main,
|
|
93621
93750
|
sidebar,
|
|
93622
93751
|
sidebarTop,
|
|
93752
|
+
sidebarScroll: sidebarTopScroll,
|
|
93623
93753
|
header,
|
|
93624
93754
|
scroll,
|
|
93625
93755
|
transcript,
|
|
@@ -96048,6 +96178,12 @@ function attachBlockIo(io, addBlock, chrome = {}) {
|
|
|
96048
96178
|
};
|
|
96049
96179
|
return io;
|
|
96050
96180
|
}
|
|
96181
|
+
function mountTitlePanel(otui, r, sidebarTop) {
|
|
96182
|
+
sidebarTop.add(new otui.TextRenderable(r, {
|
|
96183
|
+
id: "sb-title",
|
|
96184
|
+
content: otui.t`${otui.bold("keryx")} ${otui.dim(`v${package_default.version}`)}`
|
|
96185
|
+
}));
|
|
96186
|
+
}
|
|
96051
96187
|
function mountCwdPanel(otui, r, sidebarTop, cwd, metadata = resolveSidebarMetadata(cwd)) {
|
|
96052
96188
|
sidebarTop.add(new otui.TextRenderable(r, { id: "sb-cwd-k", content: otui.t`${otui.dim("Directory")}`, marginTop: 1 }));
|
|
96053
96189
|
sidebarTop.add(new otui.TextRenderable(r, {
|
|
@@ -97117,6 +97253,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
97117
97253
|
}
|
|
97118
97254
|
};
|
|
97119
97255
|
r.on("theme_mode", onThemeMode);
|
|
97256
|
+
await playBootAnimation(otui, r, { onKeypress: (handler) => onKeypress4(r, handler) });
|
|
97120
97257
|
const sel = opts.initial ?? await selectProviderModelInTui(otui, r, opts.detected);
|
|
97121
97258
|
if (sel === undefined) {
|
|
97122
97259
|
r.off("theme_mode", onThemeMode);
|
|
@@ -97159,7 +97296,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
97159
97296
|
chrome.stopBusy();
|
|
97160
97297
|
};
|
|
97161
97298
|
const sidebar = chrome.sidebarTop;
|
|
97162
|
-
|
|
97299
|
+
mountTitlePanel(otui, r, sidebar);
|
|
97163
97300
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-model-k", content: otui.t`${otui.dim("Model")}`, marginTop: 1 }));
|
|
97164
97301
|
const sbModelV = new otui.TextRenderable(r, { id: "sb-model-v", content: otui.t`${otui.dim(`${sel.provider}/${sel.model}`)}` });
|
|
97165
97302
|
sidebar.add(sbModelV);
|
|
@@ -97763,6 +97900,8 @@ async function launchTuiAgentShell(opts) {
|
|
|
97763
97900
|
const sessionCwd = opts.session?.cwd ?? process.cwd();
|
|
97764
97901
|
let permissionMode = opts.initialPermissionMode ?? getProjectPermissionMode(sessionCwd) ?? DEFAULT_PERMISSION_MODE;
|
|
97765
97902
|
io.permissionMode = () => permissionMode;
|
|
97903
|
+
let readOnly = false;
|
|
97904
|
+
io.readOnly = () => readOnly;
|
|
97766
97905
|
io.onAutoApproved = (tool, input, meta) => {
|
|
97767
97906
|
const preview = tool === "shell_exec" ? parseShellExecCommand(input) : tool;
|
|
97768
97907
|
const label = meta.destructive ? `\u25C7 auto-approved (${permissionMode}) [destructive]` : `\u25C7 auto-approved (${permissionMode})`;
|
|
@@ -98237,6 +98376,26 @@ Staying in the current session.
|
|
|
98237
98376
|
}
|
|
98238
98377
|
})();
|
|
98239
98378
|
};
|
|
98379
|
+
const runPlanCommand = (line) => {
|
|
98380
|
+
const planArgs = line.trim().split(/\s+/).slice(1).filter((p) => p.length > 0);
|
|
98381
|
+
const wanted = planArgs[0] ?? "";
|
|
98382
|
+
if (wanted.length === 0) {
|
|
98383
|
+
chrome.showToast(`Read-only mode: ${readOnly ? "on" : "off"}`);
|
|
98384
|
+
return;
|
|
98385
|
+
}
|
|
98386
|
+
if (wanted === "on") {
|
|
98387
|
+
readOnly = true;
|
|
98388
|
+
chrome.showToast("Read-only mode: on");
|
|
98389
|
+
return;
|
|
98390
|
+
}
|
|
98391
|
+
if (wanted === "off") {
|
|
98392
|
+
readOnly = false;
|
|
98393
|
+
chrome.showToast(`Read-only mode: off (permission mode stays: ${permissionMode})`);
|
|
98394
|
+
return;
|
|
98395
|
+
}
|
|
98396
|
+
io.onSystem?.(`Usage: /plan [on|off]
|
|
98397
|
+
`);
|
|
98398
|
+
};
|
|
98240
98399
|
const updateModelLabels = () => {
|
|
98241
98400
|
paintSessionHeader();
|
|
98242
98401
|
const label = `${currentSel.provider}/${currentSel.model}`;
|
|
@@ -98730,6 +98889,10 @@ Staying in the current session.
|
|
|
98730
98889
|
runModeCommand(line);
|
|
98731
98890
|
return;
|
|
98732
98891
|
}
|
|
98892
|
+
case "plan": {
|
|
98893
|
+
runPlanCommand(line);
|
|
98894
|
+
return;
|
|
98895
|
+
}
|
|
98733
98896
|
case "session-info": {
|
|
98734
98897
|
showSessionInfo();
|
|
98735
98898
|
return;
|
|
@@ -99044,6 +99207,10 @@ ${formatThemeList(getThemeId())}`);
|
|
|
99044
99207
|
runModeCommand(line);
|
|
99045
99208
|
return;
|
|
99046
99209
|
}
|
|
99210
|
+
if (command.name === "/plan") {
|
|
99211
|
+
runPlanCommand(line);
|
|
99212
|
+
return;
|
|
99213
|
+
}
|
|
99047
99214
|
if (command.name === "/model") {
|
|
99048
99215
|
(async () => {
|
|
99049
99216
|
const detected = opts.redetect !== undefined ? await opts.redetect() : opts.detected;
|
|
@@ -99806,6 +99973,7 @@ var READLINE_AGENT_COMMANDS = [
|
|
|
99806
99973
|
"/flows",
|
|
99807
99974
|
"/theme",
|
|
99808
99975
|
"/mode",
|
|
99976
|
+
"/plan",
|
|
99809
99977
|
"/exit"
|
|
99810
99978
|
];
|
|
99811
99979
|
function readlineAgentHelpText() {
|
|
@@ -100669,6 +100837,8 @@ ${text.trim()}`;
|
|
|
100669
100837
|
const sessionsOn = sessionOpts !== undefined && sessionOpts.enabled !== false;
|
|
100670
100838
|
let permissionMode = initialPermissionMode ?? getProjectPermissionMode(sessionCwd) ?? DEFAULT_PERMISSION_MODE;
|
|
100671
100839
|
agentIo.permissionMode = () => permissionMode;
|
|
100840
|
+
let readOnly = false;
|
|
100841
|
+
agentIo.readOnly = () => readOnly;
|
|
100672
100842
|
agentIo.onAutoApproved = (tool, input, meta) => {
|
|
100673
100843
|
stopSpinner();
|
|
100674
100844
|
const preview = tool === "shell_exec" ? parseShellExecCommand(input) : tool;
|
|
@@ -100952,6 +101122,25 @@ ${GUTTER}${turnSeparator()}
|
|
|
100952
101122
|
}
|
|
100953
101123
|
}
|
|
100954
101124
|
}
|
|
101125
|
+
} else if (command === "/plan") {
|
|
101126
|
+
const planArgs = rest.split(/\s+/).filter((p) => p.length > 0);
|
|
101127
|
+
const wanted = planArgs[0] ?? "";
|
|
101128
|
+
if (wanted.length === 0) {
|
|
101129
|
+
agentIo.onSystem?.(`Read-only mode: ${readOnly ? "on" : "off"}
|
|
101130
|
+
` + `Usage: /plan [on|off]
|
|
101131
|
+
`);
|
|
101132
|
+
} else if (wanted === "on") {
|
|
101133
|
+
readOnly = true;
|
|
101134
|
+
agentIo.onSystem?.(`Read-only mode: on
|
|
101135
|
+
`);
|
|
101136
|
+
} else if (wanted === "off") {
|
|
101137
|
+
readOnly = false;
|
|
101138
|
+
agentIo.onSystem?.(`Read-only mode: off (permission mode stays: ${permissionMode})
|
|
101139
|
+
`);
|
|
101140
|
+
} else {
|
|
101141
|
+
agentIo.onSystem?.(`Usage: /plan [on|off]
|
|
101142
|
+
`);
|
|
101143
|
+
}
|
|
100955
101144
|
} else if (command === "/search-provider") {
|
|
100956
101145
|
const args = parseSearchProviderArgs2(parts.slice(1));
|
|
100957
101146
|
const all = searchProviderController.configurable();
|
package/dist/core.js
CHANGED
|
@@ -9099,6 +9099,7 @@ __export(exports_service3, {
|
|
|
9099
9099
|
collectGraphWikiCandidates: () => collectGraphWikiCandidates,
|
|
9100
9100
|
createGdWikiService: () => createGdWikiService,
|
|
9101
9101
|
extractModuleApi: () => extractModuleApi,
|
|
9102
|
+
isTestSourceFile: () => isTestSourceFile,
|
|
9102
9103
|
moduleNameFromProjectPath: () => moduleNameFromProjectPath,
|
|
9103
9104
|
validModuleNames: () => validModuleNames,
|
|
9104
9105
|
wikiCheckLinks: () => wikiCheckLinks,
|
|
@@ -9472,6 +9473,7 @@ async function collectGraphWikiCandidates(cwd, generatedAt, limit, onlyModules)
|
|
|
9472
9473
|
const fileOut = new Map;
|
|
9473
9474
|
const moduleDeps = new Map;
|
|
9474
9475
|
const moduleDependents = new Map;
|
|
9476
|
+
const excludedTestEdges = new Map;
|
|
9475
9477
|
let edges = 0;
|
|
9476
9478
|
let imports = 0;
|
|
9477
9479
|
let unresolved = 0;
|
|
@@ -9492,6 +9494,11 @@ async function collectGraphWikiCandidates(cwd, generatedAt, limit, onlyModules)
|
|
|
9492
9494
|
const fromModule = fileToModule.get(from) ?? moduleNameFromProjectPath(from);
|
|
9493
9495
|
const toModule = fileToModule.get(to) ?? moduleNameFromProjectPath(to);
|
|
9494
9496
|
if (fromModule && toModule && fromModule !== toModule) {
|
|
9497
|
+
if (isTestSourceFile(from)) {
|
|
9498
|
+
recordExcludedTestEdge(excludedTestEdges, fromModule, from);
|
|
9499
|
+
recordExcludedTestEdge(excludedTestEdges, toModule, from);
|
|
9500
|
+
continue;
|
|
9501
|
+
}
|
|
9495
9502
|
bumpNestedCount(moduleDeps, fromModule, toModule);
|
|
9496
9503
|
bumpNestedCount(moduleDependents, toModule, fromModule);
|
|
9497
9504
|
}
|
|
@@ -9551,6 +9558,12 @@ async function collectGraphWikiCandidates(cwd, generatedAt, limit, onlyModules)
|
|
|
9551
9558
|
pushRef("Key files", keyFiles.map((entry) => "- `" + entry.file + "` - imported by " + entry.incoming + ", imports " + entry.outgoing));
|
|
9552
9559
|
pushRef("Depends on", deps.map(([target, count]) => "- `" + target + "` - " + count + " import(s)"));
|
|
9553
9560
|
pushRef("Depended on by", dependents.map(([source, count]) => "- `" + source + "` - " + count + " import(s)"));
|
|
9561
|
+
const dropped = excludedTestEdges.get(moduleName);
|
|
9562
|
+
if (dropped) {
|
|
9563
|
+
pushRef("Dependency basis", [
|
|
9564
|
+
"- Production imports only: " + dropped.count + " import(s) from test file(s) (e.g. `" + dropped.example + "`) excluded from the two sections above in both directions."
|
|
9565
|
+
]);
|
|
9566
|
+
}
|
|
9554
9567
|
pushRef("Entry points", entryFiles.map((file) => "- `" + file + "`"));
|
|
9555
9568
|
if (readme) {
|
|
9556
9569
|
pushRef("Module README", [readme]);
|
|
@@ -9593,6 +9606,13 @@ function bumpNestedCount(map, key, inner) {
|
|
|
9593
9606
|
bucket.set(inner, (bucket.get(inner) ?? 0) + 1);
|
|
9594
9607
|
map.set(key, bucket);
|
|
9595
9608
|
}
|
|
9609
|
+
function isTestSourceFile(filePath) {
|
|
9610
|
+
return TEST_SOURCE_FILE.test(filePath);
|
|
9611
|
+
}
|
|
9612
|
+
function recordExcludedTestEdge(map, moduleName, testFile) {
|
|
9613
|
+
const current = map.get(moduleName);
|
|
9614
|
+
map.set(moduleName, { count: (current?.count ?? 0) + 1, example: current?.example ?? testFile });
|
|
9615
|
+
}
|
|
9596
9616
|
function sumCounts(bucket) {
|
|
9597
9617
|
if (!bucket) {
|
|
9598
9618
|
return 0;
|
|
@@ -10196,7 +10216,7 @@ function compareSemver(a, b) {
|
|
|
10196
10216
|
}
|
|
10197
10217
|
return 0;
|
|
10198
10218
|
}
|
|
10199
|
-
var EXTERNAL_LINK, DEFAULT_COLLECT_LIMIT = 500, MIN_MODULE_FILES = 2, DEFAULT_EVIDENCE_BUDGET_TOKENS = 4000, DEFAULT_EVIDENCE_MAX_ITEMS = 8, WIKI_PAGE_PRODUCERS;
|
|
10219
|
+
var EXTERNAL_LINK, DEFAULT_COLLECT_LIMIT = 500, MIN_MODULE_FILES = 2, DEFAULT_EVIDENCE_BUDGET_TOKENS = 4000, DEFAULT_EVIDENCE_MAX_ITEMS = 8, WIKI_PAGE_PRODUCERS, TEST_SOURCE_FILE;
|
|
10200
10220
|
var init_service2 = __esm(() => {
|
|
10201
10221
|
init_fs();
|
|
10202
10222
|
init_guard();
|
|
@@ -10217,6 +10237,7 @@ var init_service2 = __esm(() => {
|
|
|
10217
10237
|
path: /^decisions\/sac-[a-z]+-[0-9a-f]+\.md$/
|
|
10218
10238
|
}
|
|
10219
10239
|
];
|
|
10240
|
+
TEST_SOURCE_FILE = /(?:^|\/)[^/]*\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
10220
10241
|
});
|
|
10221
10242
|
|
|
10222
10243
|
// src/gdgraph/wiki-layer.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.113",
|
|
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": {
|