formatProjectContext function
Renders the context files as a system-prompt section (kimi's wrapper, reduced): each file annotated with its source path, precedence note for deeper-vs-shallower rules. A dropped file renders as a one-line tombstone naming the path on disk (issue #1761 AC1); a truncated file's From-marker carries the original/excerpt sizes and the read-the-full-file instruction follows its excerpt. Empty when nothing was discovered.
Implementation
String formatProjectContext(LoadedProjectContext context) {
if (context.isEmpty) return '';
final buffer = StringBuffer()
..writeln('<project_context>')
..writeln()
..writeln('Project-specific instructions and guidelines:')
..writeln();
for (final drop in context.dropped) {
buffer.writeln(
'<!-- ${drop.path} exists (${_sizeLabel(drop.bytes)}) but did not '
'fit the ${_sizeLabel(context.budgetBytes)} budget — read it with '
'the read tool if its rules apply -->',
);
}
for (final file in context.files) {
if (file.isTruncated) {
buffer.writeln(
'<!-- From: ${file.path} — ${_groupDigits(file.originalBytes!)} '
'bytes, only the first ${_groupDigits(file.excerptBytes!)} shown '
'(${_sizeLabel(context.budgetBytes)} merged budget) -->',
);
buffer
..writeln(file.content.trim())
..writeln();
} else {
buffer
..writeln('<!-- From: ${file.path} -->')
..writeln(file.content.trim())
..writeln();
}
}
buffer
..writeln('</project_context>')
..write(
'When instructions in deeper files conflict with shallower ones, the '
'deeper file takes precedence; user instructions given directly in '
'the conversation take the highest precedence.',
);
return buffer.toString();
}