Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions bundle/.gnkit/lib/hook-helpers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,17 @@ export function loadHookConfig(root) {
// graph query tools require a fast incremental refresh. 0 disables the drift gate.
driftRefreshThreshold: 3,
// TASK-CORE compaction migration: nudge the agent to refresh its task-core once context
// reaches contextPressureThreshold of contextWindowTokens. Set contextWindowTokens to your
// model's window (200k default; 1M-context sessions → 1000000). 0 threshold disables.
// reaches contextPressureThreshold of contextWindowTokens. The window is model-specific, so
// prefer the per-machine env var GITNEXUS_CONTEXT_WINDOW=1000000 (wins over this file) rather
// than committing 1000000 to a shared repo where teammates run a 200k model. 0 threshold disables.
contextWindowTokens: 200000,
contextPressureThreshold: 0.9,
};

const cfgPath = path.join(root, CONFIG_FILE);
if (!fs.existsSync(cfgPath)) return cfg;

try {
// Missing file → readFileSync throws → caught below → defaults kept, then env override runs.
const file = JSON.parse(fs.readFileSync(cfgPath, "utf8"));
if (file.mode) cfg.mode = file.mode === "guide" ? "guide" : "enforce";
if (typeof file.readLineThreshold === "number")
Expand All @@ -125,6 +126,12 @@ export function loadHookConfig(root) {
/* keep defaults */
}

// Per-machine env override wins over the (team-shared) config file. The context window is
// model-specific — a 1M-context session and a teammate's 200k model can't share one committed
// value — so set GITNEXUS_CONTEXT_WINDOW=1000000 in your shell rather than in gitnexus-hooks.json.
const envWindow = Number(process.env.GITNEXUS_CONTEXT_WINDOW);
if (Number.isFinite(envWindow) && envWindow > 0) cfg.contextWindowTokens = envWindow;

return cfg;
}

Expand Down
41 changes: 41 additions & 0 deletions lib/kit.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,47 @@ describe("gitnexus-agent-kit", () => {
fs.rmSync(tmp, { recursive: true, force: true });
});

it("GITNEXUS_CONTEXT_WINDOW env overrides the shared config file (per-machine window)", async () => {
const helpers = await import(
new URL("../bundle/.gnkit/lib/hook-helpers.mjs", import.meta.url).href
);
const prev = process.env.GITNEXUS_CONTEXT_WINDOW;
try {
const empty = fs.mkdtempSync(path.join(os.tmpdir(), "gn-win-"));

// No file + no env → default window.
delete process.env.GITNEXUS_CONTEXT_WINDOW;
assert.equal(helpers.loadHookConfig(empty).contextWindowTokens, 200000);

// No file + env → env applies (guards against the no-config early-return path).
process.env.GITNEXUS_CONTEXT_WINDOW = "1000000";
assert.equal(helpers.loadHookConfig(empty).contextWindowTokens, 1000000);

// File says 200k, env says 1M → env wins (model-specific, not committed to a shared repo).
const repo = fs.mkdtempSync(path.join(os.tmpdir(), "gn-win2-"));
fs.mkdirSync(path.join(repo, ".gnkit"), { recursive: true });
fs.writeFileSync(
path.join(repo, ".gnkit/gitnexus-hooks.json"),
JSON.stringify({ contextWindowTokens: 200000 }),
);
assert.equal(helpers.loadHookConfig(repo).contextWindowTokens, 1000000);

// No env → the file value stands.
delete process.env.GITNEXUS_CONTEXT_WINDOW;
assert.equal(helpers.loadHookConfig(repo).contextWindowTokens, 200000);

// Garbage env is ignored (no NaN/0 window).
process.env.GITNEXUS_CONTEXT_WINDOW = "banana";
assert.equal(helpers.loadHookConfig(repo).contextWindowTokens, 200000);

fs.rmSync(empty, { recursive: true, force: true });
fs.rmSync(repo, { recursive: true, force: true });
} finally {
if (prev === undefined) delete process.env.GITNEXUS_CONTEXT_WINDOW;
else process.env.GITNEXUS_CONTEXT_WINDOW = prev;
}
});

it("cypher field access matches Methods (untyped source node)", async () => {
const cypher = await import(
new URL("../bundle/.gnkit/lib/cypher-helpers.mjs", import.meta.url)
Expand Down
Loading