This commit is contained in:
2025-09-05 14:38:22 -05:00
commit 4ffef1cc88
8 changed files with 272 additions and 0 deletions

63
README.md Normal file
View File

@@ -0,0 +1,63 @@
# AI Language Note Organizer
This project is a command-line application that uses the Google Gemini API to automatically organize your language class notes. It takes a directory of markdown files, identifies common themes and topics, and generates a single, well-structured markdown file with your notes grouped under relevant headings.
## Features
- **Recursive File Scanning:** Automatically scans through nested directories to find your note files.
- **Flexible Filtering:** Use a simple text pattern to specify which notes to process (e.g., "Japanese" or "Spanish_Class").
- **AI-Powered Organization:** Leverages the Gemini API to intelligently categorize and structure your notes.
- **Non-Destructive:** Your original note files are never modified.
## Installation
1. **Clone the repository:**
```bash
git clone https://github.com/your-username/language-note-organizer.git
cd language-note-organizer
```
2. **Install the dependencies:**
```bash
npm install
```
3. **Set up your environment variables:**
- Create a `.env` file in the root of the project.
- Add your Google Gemini API key to the `.env` file:
```
GEMINI_API_KEY=your_api_key_here
```
## Usage
Run the application from your terminal with the following command:
```bash
node index.js <directory_path> [file_pattern]
```
- `<directory_path>`: The path to the folder containing your note files.
- `[file_pattern]` (Optional): A keyword or pattern to filter the files. Only files with names containing this pattern will be processed.
### Example
Imagine you have a `notes/` directory with the following structure:
```
notes/
├── japanese/
│ ├── 2023-10-26_Japanese_Grammar.md
│ └── 2023-10-27_Japanese_Vocab.md
└── spanish/
└── 2023-11-01_Spanish_Phrases.md
```
To organize only the Japanese notes, you would run:
```bash
node index.js notes/ japanese
```
The script will create a new file named `organized_notes.md` in the project's root directory with the organized content from your Japanese notes.

28
SPEC.md Normal file
View File

@@ -0,0 +1,28 @@
# Project SPEC Sheet: AI Language Note Organizer
## 1. Project Goals
- Develop an application to organize markdown-based language class notes from a specified directory.
- Use an AI to analyze, group, and create headings for related notes.
- Produce a single, organized markdown file for easy reference, without altering the original files.
## 2. Core Functionality
- **File Input:** Accept a directory path and a file name pattern from the user.
- **Note Processing:** Scan for markdown files in the directory that match the specified pattern (e.g., containing "Chinese Class" or "Japanese Class"). Extract content from these files.
- **AI Organization:**
- Use an AI model (Primary: Google Gemini API) to understand the note content.
- The AI will identify themes and topics (e.g., "Verb Conjugation," "Cultural Notes").
- **Output Generation:** Create a new, single markdown file with notes grouped under AI-generated headings.
## 3. Technical Specifications
- **Language:** Node.js
- **AI Service:** Google Gemini API. I'll handle the integration; you'll just need to provide the key when the time comes.
- **User Interface:** A simple Command-Line Interface (CLI).
## 4. Project Outline
1. **Setup:** Initialize a Node.js project.
2. **File Reader:** Build a module to find and read markdown files from a directory that match a user-provided naming pattern.
3. **AI Integration:** Create a module to communicate with the Gemini API.
4. **Note Organizer:** Implement logic to group notes based on the AI's analysis.
5. **Output Writer:** Write the organized notes to a new file.
6. **CLI:** Build the user interface.
7. **Testing:** Write tests to ensure all parts are working correctly.

30
index.js Normal file
View File

@@ -0,0 +1,30 @@
const { processAndOrganizeNotes } = require('./src/noteOrganizer');
const { writeOrganizedNotes } = require('./src/outputWriter');
async function main() {
try {
const args = process.argv.slice(2);
if (args.length < 1 || args.length > 2) {
console.log('Usage: node index.js <directory_path> [file_pattern]');
return;
}
const [dirPath, pattern] = args;
console.log(`Starting to process notes in: ${dirPath}`);
if (pattern) {
console.log(`Filtering by pattern: ${pattern}`);
}
const organizedNotes = await processAndOrganizeNotes(dirPath, pattern);
if (organizedNotes) {
writeOrganizedNotes(__dirname, organizedNotes);
}
} catch (error) {
console.error('An unexpected error occurred:', error);
}
}
main();

22
package.json Normal file
View File

@@ -0,0 +1,22 @@
{
"name": "language_note_app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs",
"dependencies": {
"@google/generative-ai": "^0.24.1",
"dotenv": "^17.2.2"
},
"devDependencies": {
"babel-runtime": "^6.26.0",
"jest": "^30.1.3",
"jest-plugin-fs": "^2.9.0"
}
}

37
src/aiProcessor.js Normal file
View File

@@ -0,0 +1,37 @@
// Module for processing content with Google Gemini
require('dotenv').config();
const API_KEY = process.env.GEMINI_API_KEY;
if (!API_KEY) {
console.error('Error: GEMINI_API_KEY environment variable not set.');
process.exit(1);
}
const { GoogleGenerativeAI } = require('@google/generative-ai');
const genAI = new GoogleGenerativeAI(API_KEY);
/**
* Sends note content to the Gemini API for organization.
* @param {string[]} noteContents - An array of strings, where each string is the content of a note.
* @returns {Promise<string>} - The organized notes as a single string from the AI.
*/
async function organizeNotesWithAI(noteContents) {
const model = genAI.getGenerativeModel({ model: "gemini-2.5-pro"});
const combinedNotes = noteContents.join('\n\n---\n\n');
const prompt = `Please organize the following language notes. Group related topics under clear markdown headings (e.g., ## Grammar, ## Vocabulary, ## Cultural Notes). Here are the notes:\n\n${combinedNotes}`;
try {
const result = await model.generateContent(prompt);
const response = await result.response;
const text = response.text();
return text;
} catch (error) {
console.error('Error communicating with the Gemini API:', error);
return null;
}
}
module.exports = { organizeNotesWithAI };

43
src/fileReader.js Normal file
View File

@@ -0,0 +1,43 @@
// Module for reading and filtering files
const fs = require('fs');
const path = require('path');
/**
* Recursively scans a directory, filters files by a pattern, and reads their content.
* @param {string} dirPath - The path to the directory.
* @param {string} pattern - The keyword pattern to match in the file name.
* @returns {{filePath: string, content: string}[]} - An array of objects, each with the file path and its content.
*/
function getFileContents(dirPath, pattern) {
let fileData = [];
function scan(directory) {
try {
const entries = fs.readdirSync(directory, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(directory, entry.name);
if (entry.isDirectory()) {
scan(fullPath);
} else {
const regex = new RegExp(pattern, 'i');
if (!pattern || regex.test(entry.name)) {
try {
const content = fs.readFileSync(fullPath, 'utf8');
fileData.push({ filePath: fullPath, content });
} catch (readError) {
console.error(`Error reading file ${fullPath}:`, readError);
}
}
}
}
} catch (scanError) {
console.error(`Error scanning directory ${directory}:`, scanError);
}
}
scan(dirPath);
return fileData;
}
module.exports = { getFileContents };

28
src/noteOrganizer.js Normal file
View File

@@ -0,0 +1,28 @@
// Module to organize notes by bridging other modules
const path = require('path');
const { getFileContents } = require('./fileReader');
const { organizeNotesWithAI } = require('./aiProcessor');
/**
* Orchestrates the process of reading, processing, and organizing notes.
* @param {string} dirPath - The path to the directory containing notes.
* @param {string} pattern - The file name pattern to match.
* @returns {Promise<string|null>} - The organized notes as a single markdown string, or null on error.
*/
async function processAndOrganizeNotes(dirPath, pattern) {
const fileData = getFileContents(dirPath, pattern);
if (fileData.length === 0) {
console.log('No matching files found to organize.');
return null;
}
const contextualizedNotes = fileData.map(data => {
const fileName = path.basename(data.filePath);
return `--- Note from ${fileName} ---\n\n${data.content}`;
});
const organizedNotes = await organizeNotesWithAI(contextualizedNotes);
return organizedNotes;
}
module.exports = { processAndOrganizeNotes };

21
src/outputWriter.js Normal file
View File

@@ -0,0 +1,21 @@
// Module for writing the final output to a file
const fs = require('fs');
const path = require('path');
/**
* Writes the organized notes to a markdown file.
* @param {string} outputDir - The directory to write the file in.
* @param {string} content - The markdown content to write.
*/
function writeOrganizedNotes(outputDir, content) {
const outputFilePath = path.join(outputDir, 'organized_notes.md');
try {
fs.writeFileSync(outputFilePath, content, 'utf8');
console.log(`Successfully wrote organized notes to ${outputFilePath}`);
} catch (error) {
console.error(`Error writing to file ${outputFilePath}:`, error);
}
}
module.exports = { writeOrganizedNotes };