summaryrefslogtreecommitdiff
path: root/src/system_prompt.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/system_prompt.zig')
-rw-r--r--src/system_prompt.zig89
1 files changed, 89 insertions, 0 deletions
diff --git a/src/system_prompt.zig b/src/system_prompt.zig
index b796394..0b449dc 100644
--- a/src/system_prompt.zig
+++ b/src/system_prompt.zig
@@ -37,6 +37,31 @@ pub const default_seed = "You are a helpful assistant.";
const seed_filename = "SYSTEM.md";
const append_filename = "APPEND_SYSTEM.md";
+const compaction_filename = "COMPACTION.md";
+
+/// Last-resort fallback compaction prompt when no `COMPACTION.md` exists at
+/// any layer. In normal operation the base layer ships a bundled
+/// `agent/COMPACTION.md`, so this is only used if that staged file is
+/// missing or unreadable.
+pub const default_compaction_prompt =
+ \\You are compacting a long conversation to reduce its context size.
+ \\
+ \\You will be given a transcript of an earlier portion of a
+ \\conversation between a user and an AI coding assistant, and possibly a
+ \\previous summary of even older history. Produce a single, dense
+ \\summary that preserves everything needed to continue the work
+ \\coherently:
+ \\
+ \\- the user's goals, decisions, and stated preferences
+ \\- the current state of the task and what remains to be done
+ \\- key facts discovered about the codebase or problem
+ \\- important file paths, identifiers, and code structures
+ \\- unresolved questions, bugs, and their understanding
+ \\
+ \\Write the summary as factual notes, not as a chat reply. Do not
+ \\address the user. Do not include pleasantries. Be comprehensive about
+ \\specifics but omit redundant back-and-forth.
+;
/// A resolved set of system blocks for a fresh session: the seed followed
/// by zero or more appends, already in emission order. All slices
@@ -185,6 +210,39 @@ pub fn reconcileResumeLayers(
}
}
+/// Resolve the compaction system prompt across the three config layers.
+/// Like `SYSTEM.md` (not `APPEND_SYSTEM.md`): the highest layer present
+/// wins (project > user > base); falls back to a built-in default if no
+/// `COMPACTION.md` exists anywhere. The result is owned by `arena`.
+pub fn resolveCompaction(
+ arena: Allocator,
+ io: Io,
+ environ_map: *const std.process.Environ.Map,
+ base_dir: []const u8,
+) ![]const u8 {
+ const user_dir = try userLayerDir(arena, environ_map);
+ const project_dir = try projectLayerDir(arena, io);
+ return resolveCompactionLayers(arena, io, base_dir, user_dir, project_dir);
+}
+
+/// Layer-explicit variant of `resolveCompaction` (see `resolveLayers`).
+pub fn resolveCompactionLayers(
+ arena: Allocator,
+ io: Io,
+ base_dir: ?[]const u8,
+ user_dir: ?[]const u8,
+ project_dir: ?[]const u8,
+) ![]const u8 {
+ var prompt: []const u8 = default_compaction_prompt;
+ for ([_]?[]const u8{ base_dir, user_dir, project_dir }) |maybe_dir| {
+ const dir = maybe_dir orelse continue;
+ if (try readLayerFile(arena, io, dir, compaction_filename)) |text| {
+ prompt = text;
+ }
+ }
+ return prompt;
+}
+
// ---------------------------------------------------------------------------
// Internals
// ---------------------------------------------------------------------------
@@ -539,3 +597,34 @@ test "Resolved.blocks - seed leads appends" {
try testing.expectEqualStrings("U", blocks[2]);
try testing.expectEqualStrings("B", blocks[3]);
}
+
+test "resolveCompactionLayers - highest COMPACTION.md wins, default otherwise" {
+ var tmp = testing.tmpDir(.{ .iterate = true });
+ defer tmp.cleanup();
+
+ var arena_state = std.heap.ArenaAllocator.init(testing.allocator);
+ defer arena_state.deinit();
+ const arena = arena_state.allocator();
+
+ var buf: [std.fs.max_path_bytes]u8 = undefined;
+ const root_len = try tmp.dir.realPathFile(testing.io, ".", &buf);
+ const root = buf[0..root_len];
+
+ // No files anywhere -> default.
+ {
+ const p = try resolveCompactionLayers(arena, testing.io, root, null, null);
+ try testing.expectEqualStrings(default_compaction_prompt, p);
+ }
+
+ // base + project; project wins.
+ try tmp.dir.createDirPath(testing.io, "base");
+ try tmp.dir.createDirPath(testing.io, "project");
+ try writeTmpFile(tmp.dir, "base/COMPACTION.md", "base compaction");
+ try writeTmpFile(tmp.dir, "project/COMPACTION.md", "project compaction");
+ const base_dir = try std.fs.path.join(arena, &.{ root, "base" });
+ const project_dir = try std.fs.path.join(arena, &.{ root, "project" });
+ {
+ const p = try resolveCompactionLayers(arena, testing.io, base_dir, null, project_dir);
+ try testing.expectEqualStrings("project compaction", p);
+ }
+}