Skip to content

Commit d30bb69

Browse files
committed
Feature: Enhanced CLI to support multiple directories and file patterns.
1 parent 3e2c110 commit d30bb69

File tree

3 files changed

+162
-31
lines changed

3 files changed

+162
-31
lines changed

CodeIngest.sln.DotSettings

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_INVOCABLE/@EntryValue">1</s:Int64>
3+
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
4+
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/REMOVE_BLANK_LINES_NEAR_BRACES_IN_CODE/@EntryValue">True</s:Boolean>
5+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
6+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
7+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
8+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
9+
<s:Boolean x:Key="/Default/UserDictionary/Words/=approximator/@EntryIndexedValue">True</s:Boolean>
10+
<s:Boolean x:Key="/Default/UserDictionary/Words/=boid/@EntryIndexedValue">True</s:Boolean>
11+
<s:Boolean x:Key="/Default/UserDictionary/Words/=scroller/@EntryIndexedValue">True</s:Boolean>
12+
<s:Boolean x:Key="/Default/UserDictionary/Words/=sksl/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

Program.cs

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,79 @@
11
namespace CodeIngest;
22

3-
internal static class Program
3+
static class Program
44
{
55
private static void Main(string[] args)
66
{
77
// Getting directory argument from the command line (Report error if none found).
8-
if (args.Length != 2)
8+
if (args.Length < 2 || args.Any(a => a is "-h" or "--help" or "/?"))
99
{
10-
Console.WriteLine("Usage: CodeIngest <directory> <output.cs>");
10+
ShowUsage();
1111
return;
1212
}
13+
14+
var outputFile = new FileInfo(args[^1]);
15+
var patterns = new List<string>();
16+
var directories = new List<DirectoryInfo>();
17+
18+
for (var i = 0; i < args.Length - 1; i++)
19+
{
20+
if (args[i].Contains('*'))
21+
patterns.AddRange(args[i].Split(';', StringSplitOptions.RemoveEmptyEntries));
22+
else
23+
directories.Add(new DirectoryInfo(args[i]));
24+
}
25+
26+
if (directories.Count == 0)
27+
directories.Add(new DirectoryInfo("."));
28+
29+
if (patterns.Count == 0)
30+
patterns.Add("*.cs");
31+
32+
// Recurse directory to find all source files.
33+
var sourceFiles = directories
34+
.Where(d => d.Exists)
35+
.SelectMany(d => patterns.SelectMany(p =>
36+
{
37+
try
38+
{
39+
return d.GetFiles(p, SearchOption.AllDirectories);
40+
}
41+
catch (Exception ex)
42+
{
43+
Console.WriteLine($"Warning: Failed to read directory {d.FullName}: {ex.Message}");
44+
return [];
45+
}
46+
}))
47+
.Where(f => !ShouldSkipFile(f))
48+
.ToDictionary(o => o.Name, o => File.ReadLines(o.FullName));
1349

14-
var sourceDirectory = new DirectoryInfo(args[0]);
15-
16-
// Check directory exists (Report error if not found).
17-
if (!sourceDirectory.Exists)
50+
if (sourceFiles.Count == 0)
1851
{
19-
Console.WriteLine("Directory not found: " + sourceDirectory.FullName);
52+
Console.WriteLine("No matching files found. Check your filters or directory paths.");
2053
return;
2154
}
22-
23-
// Recurse directory to file all .cs files.
24-
var sourceFiles =
25-
sourceDirectory
26-
.GetFiles("*.cs", SearchOption.AllDirectories)
27-
.Where(f => !ShouldSkipFile(f))
28-
.ToDictionary(o => o.FullName, o => File.ReadLines(o.FullName));
29-
55+
3056
// Write header.
31-
var outputFile = new FileInfo(args[1]);
3257
using (var fileStream = outputFile.Open(FileMode.Create))
3358
using (var writer = new StreamWriter(fileStream))
3459
{
3560
{
36-
writer.WriteLine(
37-
"// CodeIngest Source Dump - A CLI tool that merges and processes code files for GPT reviews.");
38-
writer.WriteLine("// Notes: Comments, namespaces, and using statements removed to reduce noise.");
61+
writer.WriteLine("// CodeIngest Source Dump - A CLI tool that merges and processes code files for GPT reviews.");
62+
writer.WriteLine("// Notes: Some code content may have been removed.");
3963

4064
// Combine files into a single output file.
4165
foreach (var kvp in sourceFiles)
4266
{
4367
var lines = kvp.Value.ToList(); // Force evaluation to count
44-
var lineCount = lines.Count;
45-
var padWidth = lineCount.ToString().Length;
68+
var padWidth = lines.Count.ToString().Length;
4669

47-
writer.WriteLine($"// File: {kvp.Key} ({lineCount:N0} lines)");
70+
writer.WriteLine($"// File: {kvp.Key}");
4871

4972
var lineNumber = 1;
5073
foreach (var line in lines)
5174
{
5275
if (ShouldIncludeSourceLine(line))
53-
writer.WriteLine($"{lineNumber.ToString().PadLeft(padWidth)} | {line.Trim()}");
76+
writer.WriteLine($"{lineNumber.ToString().PadLeft(padWidth)}|{GetCodeLine(line).Trim()}");
5477

5578
lineNumber++;
5679
}
@@ -63,8 +86,31 @@ private static void Main(string[] args)
6386
Console.WriteLine($"Processed {sourceFiles.Count:N0} files, producing {outputFile.Length:N0} bytes.");
6487
}
6588

89+
private static void ShowUsage()
90+
{
91+
Console.WriteLine("Usage:");
92+
Console.WriteLine(" CodeIngest [<directory> ...] [*.ext1;*.ext2] <output.code>");
93+
Console.WriteLine();
94+
Console.WriteLine("See:");
95+
Console.WriteLine(" https://github.com/deanthecoder/CodeIngest");
96+
Console.WriteLine();
97+
Console.WriteLine("Examples:");
98+
Console.WriteLine(" CodeIngest MyProject Out.cs");
99+
Console.WriteLine(" CodeIngest Src1 Src2 *.cs;*.txt Dump.txt");
100+
Console.WriteLine(" CodeIngest *.cs;*.cpp SourceDump.code");
101+
Console.WriteLine();
102+
Console.WriteLine("Note:");
103+
Console.WriteLine(" - The output file will be overwritten if it already exists.");
104+
Console.WriteLine(" - If no directory is specified, the current directory is used.");
105+
Console.WriteLine(" - If no file filter is specified, *.cs is used by default.");
106+
}
107+
66108
private static bool ShouldSkipFile(FileInfo f) =>
67-
new[] {"resx", ".g.", ".designer.", "\\obj\\", "/obj/", "\\bin\\", "/bin/", "assemblyinfo.cs", "/.", "\\." }.Any(o => f.FullName.Contains(o, StringComparison.OrdinalIgnoreCase));
109+
new[]
110+
{
111+
"resx", ".g.", ".designer.", "\\obj\\", "/obj/", "\\bin\\", "/bin/", "assemblyinfo.cs", "/.", "\\."
112+
}
113+
.Any(o => f.FullName.Contains(o, StringComparison.OrdinalIgnoreCase));
68114

69115
private static bool ShouldIncludeSourceLine(string s)
70116
{
@@ -80,4 +126,11 @@ private static bool ShouldIncludeSourceLine(string s)
80126
return false;
81127
return true;
82128
}
129+
130+
private static string GetCodeLine(string line)
131+
{
132+
// Strip all comments.
133+
var commentIndex = line.IndexOf("//", StringComparison.Ordinal);
134+
return commentIndex >= 0 ? line[..commentIndex] : line;
135+
}
83136
}

README.md

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,85 @@
1+
[![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/deanthecoder.svg?style=social&label=Follow%20%40deanthecoder)](https://twitter.com/deanthecoder)
12
# CodeIngest
2-
3-
**CodeIngest** is a cross-platform C# CLI tool that recursively scans a directory of `.cs` files, filters out noise (comments, using statements, namespaces), and generates a flattened source dump designed for GPT code review or large-scale source inspection.
3+
**CodeIngest** is a cross-platform C# CLI tool that recursively scans a directory of source files, filters out noise (comments, using statements, namespaces), and generates a flattened source dump designed for GPT code review or large-scale source inspection.
44

55
## Features
6-
7-
- Cross-platform (.NET 6+)
6+
- Cross-platform (.NET)
87
- Strips comments, `using` directives, and `namespace` blocks
9-
- Outputs a single readable `.cs` file with:
8+
- Outputs a single readable output file with the following features:
109
- File headers
1110
- Line numbers
1211
- Cleaned source code
1312
- Skips generated and irrelevant files (e.g. `.designer.cs`, `bin`, `obj`, `.resx`, etc.)
1413

1514
## Usage
15+
```
16+
CodeIngest <directory> [<directory> ...] [*.ext1;*.ext2] <output.code>
17+
```
1618

19+
### Examples
20+
```
21+
CodeIngest MyProject Out.cs
22+
CodeIngest Src1 Src2 *.cs;*.txt Dump.txt
23+
CodeIngest *.cs;*.cpp SourceDump.code
1724
```
18-
CodeIngest.exe <source-folder> <output-file.cs>
25+
26+
## Example Output
27+
```csharp
28+
// CodeIngest Source Dump - A CLI tool that merges and processes code files for GPT reviews.
29+
// Notes: Some code content may have been removed.
30+
// File: GameState.cs
31+
20|public class GameState : IAiGameState
32+
21|{
33+
22|private readonly Vector2[] m_bats;
34+
23|private readonly Vector2 m_ballPosition;
35+
24|private readonly Vector2 m_ballVelocity;
36+
25|private readonly int m_arenaWidth;
37+
26|private readonly int m_arenaHeight;
38+
28|public GameState(Vector2[] bats, Vector2 ballPosition, Vector2 ballVelocity, int arenaWidth, int arenaHeight)
39+
29|{
40+
30|m_bats = bats;
41+
31|m_ballPosition = ballPosition;
42+
32|m_ballVelocity = ballVelocity;
43+
33|m_arenaWidth = arenaWidth;
44+
34|m_arenaHeight = arenaHeight;
45+
35|}
46+
37|public double[] ToInputVector()
47+
38|{
48+
39|var inputVector = new double[Brain.BrainInputCount];
49+
42|inputVector[0] = m_bats[0].Y / m_arenaHeight * 2.0f - 1.0f;
50+
43|inputVector[1] = m_bats[1].Y / m_arenaHeight * 2.0f - 1.0f;
51+
45|inputVector[2] = (m_bats[0].Y - m_ballPosition.Y) / m_arenaHeight * 2.0f;
52+
46|inputVector[3] = (m_bats[1].Y - m_ballPosition.Y) / m_arenaHeight * 2.0f;
53+
49|inputVector[4] = m_ballPosition.X / m_arenaWidth * 2.0f - 1.0f;
54+
50|inputVector[5] = m_ballPosition.Y / m_arenaHeight * 2.0f - 1.0f;
55+
51|inputVector[6] = m_ballVelocity.X.Clamp(-1.0f, 1.0f);
56+
52|inputVector[7] = m_ballVelocity.Y.Clamp(-1.0f, 1.0f);
57+
54|return inputVector;
58+
55|}
59+
56|}
60+
// File: Brain.cs
61+
18|public class Brain : AiBrainBase
62+
19|{
63+
20|public const int BrainInputCount = 8;
64+
22|public Brain() : base(BrainInputCount, [16], 4)
65+
23|{
66+
24|}
67+
26|private Brain(Brain brain) : base(brain)
68+
27|{
69+
28|}
70+
29|public (Direction LeftBat, Direction RightBat) ChooseMoves(IAiGameState state)
71+
30|{
72+
31|var outputs = GetOutputs(state);
73+
33|var leftBatDirection = Direction.Left;
74+
34|var diff = outputs[0] - outputs[1];
75+
35|if (Math.Abs(diff) > 0.2)
76+
36|leftBatDirection = diff > 0 ? Direction.Up : Direction.Down;
77+
38|var rightBatDirection = Direction.Left;
78+
39|diff = outputs[2] - outputs[3];
79+
40|if (Math.Abs(diff) > 0.2)
80+
41|rightBatDirection = diff > 0 ? Direction.Up : Direction.Down;
81+
43|return (leftBatDirection, rightBatDirection);
82+
44|}
83+
46|public override AiBrainBase Clone() => new Brain(this);
84+
47|}
1985
```

0 commit comments

Comments
 (0)