v1.0
This commit is contained in:
63
README.md
Normal file
63
README.md
Normal 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
28
SPEC.md
Normal 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
30
index.js
Normal 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
22
package.json
Normal 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
37
src/aiProcessor.js
Normal 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
43
src/fileReader.js
Normal 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
28
src/noteOrganizer.js
Normal 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
21
src/outputWriter.js
Normal 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 };
|
||||||
|
|
||||||
Reference in New Issue
Block a user